diff --git a/AGENTS.md b/AGENTS.md index 8a30113..5fc8025 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,14 +1,23 @@ -# fetch +# fetch development guide -Guidance for AI agents working in this repo. Keep this file current when behavior or workflow changes. +Repository-specific guidance for AI agents. Update this file only when the development workflow, architecture, or non-obvious implementation constraints change. User-facing behavior belongs in `README.md`, `docs/`, and tests. -## Snapshot +## Workflow -`fetch` is a Rust terminal-native API client (`cargo` binary: `fetch`) for HTTP requests, streaming, WebSockets, gRPC/reflection/protobuf, DNS/TLS inspection, timing, auth (Basic/Digest/Bearer/AWS SigV4), rich response formatting (JSON/XML/YAML/HTML/CSS/CSV/Markdown/msgpack/protobuf/SSE/NDJSON/images), and self-update/session workflows. +- Inspect nearby code and tests before editing; preserve unrelated working-tree changes. +- Prefer existing abstractions and patterns over parallel implementations. +- Add or update tests for behavior changes. Update docs when flags, defaults, output, configuration, or examples change. +- During development, run the narrowest relevant test first, for example: -## Commands +```bash +cargo test --locked --all-features --test http request_construction_and_data_sources +cargo test --locked --all-features image:: +cargo test --locked --all-features --test install +``` + +## Validation -Rust code changes: +For Rust changes, run before completion: ```bash cargo fmt @@ -16,15 +25,7 @@ cargo clippy --locked --all-targets --all-features -- -D warnings cargo test --locked --all-features --lib --bins ``` -Then run the narrowest relevant test, e.g.: - -```bash -cargo test --locked --all-features --test http request_construction_and_data_sources -cargo test --locked --all-features image:: -cargo test --locked --all-features --test install -``` - -Full CI-equivalent before PRs, shared transport/request/response changes, or unclear scope: +Run the full CI-equivalent suite before PRs and for shared transport/request/response changes or unclear scope: ```bash cargo fmt --check @@ -33,109 +34,57 @@ cargo test --locked --all-features --lib --bins cargo test --locked --all-features --test cli --test formatting --test grpc --test har --test http --test install --test network --test terminal --test update --test websocket -- --test-threads=2 ``` -The integration test suite uses `TcpListener::bind("127.0.0.1:0")` and -`UdpSocket::bind("127.0.0.1:0")` for all test servers. CI uses two test threads to -speed up the suite without overloading timing-sensitive socket tests. If transient -"Connection refused" or connection-reset errors occur from port reuse races, use -`-- --test-threads=1` to diagnose sequentially. The `TestServer` and `H3TestServer` -use `mpsc` channels instead of polling for request notification. - -Docs-only changes: skip Cargo unless examples/generated CLI output changed; format changed docs only: +For docs-only changes, skip Cargo unless examples or generated CLI output changed: ```bash -prettier -w README.md docs/**/*.md AGENTS.md +prettier -w README.md "docs/**/*.md" "**/AGENTS.md" ``` -Release/package/update validation only: +For release/package/update validation only: ```bash cargo build --release --locked ``` -## Architecture Map - -| Area | Files | Notes | -| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Entry | `src/main.rs`, `src/app.rs`, `src/cli.rs` | `main` runs the Tokio runtime on a spawned thread without overriding the platform default stack size; this avoids Windows' small process-main stack while removing the custom 16 MiB stack. Do not move stack-heavy entry futures back to the process main thread. | -| Core IO | `src/core.rs`, `src/output`, `src/fileutil.rs` | Central printer/color/format/stdout policy, atomic writes, `~/` expansion, cross-platform locks. Prefer `core::write_stdout`, `core::stdio`, `core::color_enabled`, `core::format_enabled`; avoid direct `print!`/`println!` and ad-hoc terminal checks. | -| Config | `src/config` | INI with host overlays. Config-backed options belong in `config_options!`; duplicate host sections are errors. Metadata commands parse config best-effort only. | -| HTTP | `src/http`, `src/http/response`, `src/http/transport`, `src/net.rs` | Request building/execution, response orchestration, retries, proxies, TLS, Unix sockets, timing. Transport code owns DNS/TCP/TLS/QUIC setup; reuse `src/net.rs` dialing/proxy helpers. | -| DNS | `src/dns` | Custom UDP/TCP/TLS/QUIC/DoH resolvers, inspection, HTTPS/SVCB, EDNS/truncation fallback. Reuse `custom.rs`, `wire.rs`, `doh.rs`; inspection orchestration/rendering stays under `src/dns/inspect*`. | -| TLS | `src/tls` | Shared rustls config, client auth, min/max TLS, ECH, inspection. Inspection orchestration in `inspect.rs`; cert/DER/render helpers stay split. | -| Formatting | `src/format`, `src/format/content_type.rs`, `src/image` | MIME-to-formatter policy, streaming formatters, built-in image defaults; external image adapters only for `--image external`/config and must be bounded. | -| gRPC/protobuf | `src/grpc`, `src/proto` | Framing/status/reflection, local schema/discovery/conversion/JSON streams. Reuse standard gRPC headers/status/framed-body helpers. | -| WebSocket | `src/websocket` | Interactive and non-interactive message loops; custom dialer for DNS/proxy/TLS. | -| Auth/session/update | `src/auth`, `src/session.rs`, `src/update`, `src/skill.rs`, `install.sh` | Auth helpers; locked cookie sessions; HTTPS self-update; offline embedded Agent Skill installation with modification detection. | -| Tests | `tests/`, `tests/support/` | Integration tests run the compiled binary; support code is split by domain. `run_fetch` isolates HTTP/3 cache by default. `TestServer`/`H3TestServer` use `mpsc` channel notification (not polling). `wait_for_requests` blocks via `recv_timeout` on the notification channel. | - -Request flow: CLI parse → config merge → request build (gRPC may load/reflect schema and frame protobuf) → transport execute → response format/output/pager/clipboard. - -## Invariants & Implementation Notes - -### Transport, DNS, TLS, HTTP/3 - -- Direct HTTPS can opportunistically discover HTTP/3 via HTTPS/SVCB `h3`; proxy, Unix socket, explicit `--http`, gRPC, and WebSocket paths bypass auto-H3. Explicit `--http 1|2|3` forces a version, not a cap. -- Auto-H3 must not delay normal TCP/TLS: start SVCB discovery in parallel, race QUIC only if a usable candidate appears before TCP/TLS wins, and share the started `--connect-timeout` budget across branches. Use `net::race_staggered`/`AbortOnDropJoin` for per-address races. -- HTTP/3 alternatives are cached as bounded sharded JSON under `http3//.json`, scoped by normalized origin + resolver key, storing alternative authorities (not IPs), expiring by SVCB TTL/Alt-Svc `ma` with a hard cap. Never use for proxy, Unix socket, IP literal, non-HTTPS, explicit `--http`, gRPC, or WebSocket. Do not learn Alt-Svc from `--insecure` responses. -- Preserve reqwest-like safe retries for HTTP/2/3 protocol NACKs (`REFUSED_STREAM`, `GOAWAY(NO_ERROR)`, HTTP/3 connection timeout) only when the request body is replayable. -- Custom/direct DNS is scoped per request URL and redirect target. A/AAAA run concurrently and may proceed once one family succeeds; preserve successful records if the other fails, resolver order in diagnostics, IPv6 scope IDs, and Happy Eyeballs staggering. -- `--dns-server` schemes: bare/`udp://IP[:PORT]`, `tcp://`, `tls://`/`dot://` (853), `quic://`/`doq://` (853). TLS/QUIC resolver hostnames resolve via system DNS and provide SNI/verification. -- UDP DNS advertises EDNS(0), randomizes IDs, falls back to TCP on truncation using the remaining timeout, and uses a 5s receive timeout when no request/connect timeout exists. DoH uses RFC8484 POST first, JSON fallback, shared `DohClient` for concurrent families, main transport pooling, and a 1 MiB response cap. -- Timeout handling lives in `src/duration.rs`; use `TimeoutBudget` for HTTP/WebSocket/DNS/TLS, cap sleeps/work to remaining budget, and preserve the original request-timeout diagnostic. -- TLS versions are only 1.2/1.3. `--tls` aliases min TLS; prefer `--min-tls`/`--max-tls` in docs. rustls uses `aws-lc-rs` with post-quantum preference enabled. -- ECH: flags `--ech auto|on|off`, `--ech-config`, `--ech-grease`, `--ech-hard-fail`; ECH comes from HTTPS/SVCB key 5 or explicit config, requires TLS 1.3, and is fetched even when remote-resolving proxies skip A/AAAA. DNS inspection prints `ECH=`. -- Request/DoH HTTPS/`wss://` use rustls platform verification while preserving request-path CA, mTLS, `--insecure`, and TLS min/max. HTTPS proxy TLS is separate and must not inherit origin CA/client auth/`--insecure`. - -### Requests, bodies, retries, curl - -- Request `Content-Length` inference is centralized in `src/http/mod.rs` and only runs when neither `Content-Length` nor `Transfer-Encoding` was supplied. -- Body-producing flags (`--data`, `--json`, `--xml`, `--form`, `--multipart`, `--edit`) infer `POST` unless `--method` is explicit; explicit methods (including GET with body) win. -- Retryable uploads use replayable body descriptors, not one universal `Vec`: file/multipart sources reopen for retries/307/308; stdin streams once and errors if replay is required. Multipart boundaries are stable; multipart part headers/content type/length are resolved when built. -- Digest auth retries use bounded cleanup for 401 bodies and retry through a fresh client before dropping an abandoned challenge body; malformed/unsupported challenges fail before replay checks. -- `--basic`/`--digest` preserve exact bytes around the first colon; do not trim spaces. -- `--from-curl` should no-op only defaults/presentation flags. Unsupported semantic flags (`-n`, `--netrc`, `-f`, `--fail`, `-N`, `--no-buffer`, `--proto-default`, `--proto-redir`, etc.) must diagnose clearly. Single `-d @file`/`@-` uses native streaming; composite data/materialized `--data-urlencode @file` caps at 16 MiB. -- Schemeless URLs default to HTTPS for hostnames and HTTP for `localhost`/IP literals; dry-run shows normalized absolute URL and HTTPS plaintext failures suggest `http://`. -- Default request `Accept` is `application/json, */*;q=0.5`. `--sort-headers` sorts displayed headers only. - -### Response/output/formatting - -- Response handling split: `stdout.rs` terminal/pager policy; `stream.rs` decoded streaming, shared sink copy, formatter callback driver, trailers/byte counts/clipboard/broken-pipe handling; `formatters.rs` buffered/streaming body formatting; `metadata.rs` timing/clipboard/status metadata. -- SSE, NDJSON, and gRPC formatted stdout streaming share the `stream.rs` callback driver. Keep per-format parsing in callbacks; NDJSON pending records cap at `MAX_BUFFERED_RESPONSE_BYTES`. -- Binary-looking bodies are not written to terminal stdout unless forced with `--output -`, for both buffered fallback and raw streaming (`--format off`). -- `--compress auto|br|gzip|zstd|off` controls negotiation/decoding (`brotli` aliases `br`). Output files receive decoded bodies by default; document `--compress off` for byte-for-byte compressed downloads. -- Auto-compressed SSE retries without `Accept-Encoding` only for safe methods (`GET`/`HEAD`); unsafe methods warn and keep the original response. -- Pager: `--pager auto|on|off`; `NO_PAGER` disables auto fallback; `$PAGER` is shell-split but launched directly; `$LESS` suppresses fallback flags. Images/output files bypass pager. -- `--copy` tees decoded stdout/output-file bodies to platform clipboard commands, skips >1 MiB, and bounds stdin/write/wait, killing hung backends with a warning. -- Content type policy belongs in `src/format/content_type.rs`; update README/docs when user-visible formats or MIME behavior change. -- `--article` is an explicit buffered HTML/Markdown transformation: Legible extracts readable HTML, htmd converts it to Markdown, and existing Markdown passes through directly. `src/format/article.rs` adds scalar YAML frontmatter (all available article details for HTML, only the final URL for Markdown). It uses the final response URL for relative HTML links, caps decoded responses at 16 MiB and HTML DOM elements at 500,000, writes raw Markdown to files/clipboard, and only applies terminal Markdown styling afterward. - -### gRPC/protobuf - -- `--grpc-list`/`--grpc-describe` use reflection or local descriptors; `--grpc` auto-reflects when no local schema is supplied. Plaintext loopback h2c is gRPC-only. -- gRPC requests advertise `grpc-accept-encoding: gzip`; compressed response frames use `grpc-encoding` and unsupported encodings report by name. -- Reflection framed-body reads are bounded by decoded bytes and message count. Client/bidi streaming should stream incremental JSON into framed protobuf without materializing full input; stdin uses the shared incremental parser, whitespace skipping, `framing::MAX_MESSAGE_SIZE`, and Windows pipe peeking. -- `application/grpc+proto` formatted responses stream complete frames immediately while preserving trailers. - -### WebSocket - -- Requires HTTP/1.1 upgrade; reject explicit `--http 2|3`. Output-file/clipboard/retry flags are invalid because the path streams through the message loop. -- Interactive prompt default is controlled by `--ws-interactive auto|on|off`. -- Non-interactive stdin connects before reading piped input, sends lines concurrently with receive, preserves empty text lines, closes send half at EOF, and continues receiving. `--ws-message-mode auto|text|binary`; `auto` sends invalid UTF-8 as binary, `binary` streams raw chunks. -- Text output locks stdout, flushes per message, and treats broken pipe as normal. Incoming binary writes raw bytes only to non-terminal stdout; terminals use a guard. Incoming frames/messages are capped at 16 MiB. -- URL userinfo becomes Basic auth on the stripped URL unless explicit auth headers/options override. `wss://` honors request TLS options; `ws://` rejects TLS flags. DNS/proxy/connect timeout behavior mirrors HTTP, with SOCKS5H resolving remotely and plain SOCKS5 locally. Sessions send/persist cookies on successful handshakes. - -### Inspection, sessions, update, platform - -- `--inspect-dns` resolves without HTTP, shows common records/backend/duration/TTLs, retries truncated UDP over TCP, and exits non-zero with a warning if fallback cannot complete a record type. -- `--inspect-tls --http 3` uses QUIC/TLS with `h3`; inspection honors `--dns-server`. Verified chain rendering may append/replace trusted roots for expiry display; `--insecure` shows raw peer chain. -- Session saves lock per session, reload latest JSON, merge only local cookie changes, atomically replace, and warn on bounded lock wait. Update locks use `fileutil::FileLock`; background checks are nonblocking. -- Self-update metadata/artifact/checksum/redirect URLs require HTTPS except internal test overrides; update networking must not inherit origin-specific TLS/version/Unix-socket config, but may keep proxy/DNS/timeouts/verbosity/custom CA. Artifacts stream with SHA-256; tar extracts streaming, zip uses temp archive; Unix replacement preserves atomic parent-dir sync. -- `install.sh` verifies `.sha256`, validates a same-directory staged binary, and atomically renames it over the destination; completion install is opt-in (`--completions` or `FETCH_INSTALL_COMPLETIONS=1`) and must not auto-edit shell startup files by default. -- Agent Skill files live under `skills/fetch/` and are embedded by `src/skill.rs`. User/project installs support generic `.agents/skills/fetch` plus distinct Codex, Claude, Gemini, and Pi directories, record `.fetch-skill.json` in each copy, use atomic file helpers and a parent-directory operation lock, revalidate before writes/deletions, refuse modified installs without `--force`, reject unrelated CLI options rather than silently ignoring them, and leave no lock/directory artifacts when uninstall targets are all missing. Never add network downloads or agent-config edits to skill installation. -- Ctrl-C/SIGINT exits 130, including streaming modes. Output downloads keep `*.download` temps under a drop guard. -- Rust is pinned to 1.97.1 (`rust-toolchain.toml`); keep `Cargo.toml` rust-version and CI aligned. Windows config search prefers XDG/HOME paths before AppData; Windows mTLS fixtures use RSA certs. -- GitHub Actions run fmt/clippy/unit/integration. Release builds archive names for self-updater, Linux GNU uses `cargo-zigbuild` with glibc 2.28 floor, Windows uses static MSVC CRT, archives get SHA-256 sidecars, `FETCH_VERSION` comes from release tag/manual version (local: matching `v*`, then `git describe`, then `v0.0.0-dev`), and `vcs.modified` ignores untracked files. - -## Docs - -README is high-level; detailed docs are under `docs/`. Keep docs and generated CLI output aligned with code. The `--edit` workflow accepts `VISUAL`/`EDITOR` values with flags and preserves executable paths containing spaces even when unquoted. +Report the checks run and any checks not run. + +## Architecture + +| Area | Main code | Integration tests | +| --------------------- | -------------------------------------------------------------- | ------------------------------------------ | +| Entry and CLI | `src/main.rs`, `src/app.rs`, `src/cli.rs` | `tests/cli.rs` | +| Config and core IO | `src/config/`, `src/core.rs`, `src/output/`, `src/fileutil.rs` | `tests/cli.rs`, `tests/terminal.rs` | +| HTTP and transport | `src/http/`, `src/net.rs` | `tests/http.rs`, `tests/network.rs` | +| DNS and TLS | `src/dns/`, `src/tls/` | `tests/network.rs` | +| Formatting and images | `src/format/`, `src/image/` | `tests/formatting.rs`, `tests/terminal.rs` | +| gRPC and protobuf | `src/grpc/`, `src/proto/` | `tests/grpc.rs` | +| WebSocket | `src/websocket/` | `tests/websocket.rs` | +| Auth and sessions | `src/auth/`, `src/session.rs` | `tests/http.rs` | +| Update and skills | `src/update/`, `src/skill.rs`, `install.sh`, `skills/fetch/` | `tests/update.rs`, `tests/install.rs` | + +Request flow: CLI parse → config merge → request construction → transport → response formatting/output. + +Read the nearest scoped guide before changing these areas: + +- `src/http/AGENTS.md` +- `src/dns/AGENTS.md` +- `src/grpc/AGENTS.md` and `src/proto/AGENTS.md` +- `src/websocket/AGENTS.md` +- `src/update/AGENTS.md` +- `tests/AGENTS.md` + +## Cross-cutting rules + +- Preserve the spawned Tokio runtime in `src/main.rs`; stack-heavy entry futures must not move back to the process main thread. +- Use `core::write_stdout`, `core::stdio`, `core::color_enabled`, and `core::format_enabled`; avoid direct `print!`/`println!` and ad-hoc terminal checks. +- Use `TimeoutBudget` from `src/duration.rs` for timeout-aware HTTP, WebSocket, DNS, and TLS work. Cap sleeps and nested operations to the remaining budget. +- Reuse dialing, proxy, and address-racing helpers in `src/net.rs`. Do not create subsystem-specific copies. +- Bound all externally controlled buffering and preserve streaming where the surrounding path streams. +- Preserve request-body replayability semantics across authentication retries, transport retries, and 307/308 redirects; stdin is not replayable. +- Keep HTTPS proxy TLS configuration separate from origin TLS configuration. +- Use atomic file helpers and cross-platform locks for persistent state. Downloads must retain their temporary-file drop guards. +- Config-backed options belong in `config_options!`. Metadata commands parse config best-effort. +- Content-type-to-formatter policy belongs in `src/format/content_type.rs`. +- Skill installation remains offline and embedded; do not add network downloads or agent-configuration edits. +- Ctrl-C/SIGINT exits with status 130, including streaming modes. +- Rust is pinned in `rust-toolchain.toml`; keep it aligned with `Cargo.toml` and CI. diff --git a/src/dns/AGENTS.md b/src/dns/AGENTS.md new file mode 100644 index 0000000..6e57d89 --- /dev/null +++ b/src/dns/AGENTS.md @@ -0,0 +1,12 @@ +# DNS development notes + +Applies to `src/dns/` and its submodules. + +- Custom DNS is scoped to each request URL and redirect target. +- Resolve A and AAAA concurrently. Allow progress when either family succeeds while preserving successful records, resolver diagnostic order, IPv6 scope IDs, and Happy Eyeballs staggering. +- UDP queries advertise EDNS(0), randomize IDs, and fall back to TCP on truncation using the remaining timeout budget. +- DoH uses RFC 8484 POST first and its existing JSON fallback. Share the `DohClient` across concurrent address-family lookups and keep response bodies bounded. +- TLS/QUIC resolver hostnames use system DNS for bootstrap and retain SNI and certificate verification. +- ECH discovery can be required even when a remote-resolving proxy bypasses A/AAAA lookup; do not couple those decisions. +- DNS inspection must report partial fallback failures as failures rather than silently presenting incomplete success. +- Reuse `custom.rs`, `wire.rs`, and `doh.rs`; keep inspection orchestration and rendering under `inspect.rs` and its helpers. diff --git a/src/grpc/AGENTS.md b/src/grpc/AGENTS.md new file mode 100644 index 0000000..28af864 --- /dev/null +++ b/src/grpc/AGENTS.md @@ -0,0 +1,10 @@ +# gRPC development notes + +Applies to `src/grpc/` and its submodules. Protobuf conversion and schema work must also follow `../proto/AGENTS.md`. + +- Reuse the standard gRPC framing, header, status, and compression helpers. +- Reflection and descriptor reads must be bounded by decoded bytes and message count. +- Client and bidirectional streaming input must convert incremental JSON directly to framed protobuf; do not materialize the complete input. +- Enforce `framing::MAX_MESSAGE_SIZE` on incremental and buffered paths. +- Preserve complete-frame streaming and trailers for formatted `application/grpc+proto` responses. +- Keep plaintext loopback h2c support restricted to the gRPC path. diff --git a/src/http/AGENTS.md b/src/http/AGENTS.md new file mode 100644 index 0000000..eedd73e --- /dev/null +++ b/src/http/AGENTS.md @@ -0,0 +1,16 @@ +# HTTP development notes + +Applies to `src/http/` and its submodules. + +- Transport code owns DNS/TCP/TLS/QUIC setup. Reuse `src/net.rs` dialing, proxy, timeout, and address-racing helpers. +- Automatic HTTP/3 discovery applies only to eligible direct HTTPS requests. It must not delay normal TCP/TLS and must share the already-started connect-timeout budget. +- Keep HTTP/3 cache entries bounded, origin/resolver scoped, authority-based, and expiring. Never learn Alt-Svc from insecure responses. +- Retry protocol NACKs only when safe and when the request body is replayable. +- Body descriptors must preserve replay behavior: files and multipart sources reopen; stdin streams once and errors if replay becomes necessary. +- `Content-Length` inference stays centralized in `src/http/mod.rs` and runs only when neither `Content-Length` nor `Transfer-Encoding` was supplied. +- Digest authentication validates challenges before replay checks and cleans up abandoned 401 bodies with bounded work. +- Keep request/DoH/WebSocket origin TLS settings separate from HTTPS proxy TLS settings. +- Response responsibilities remain split between `response/stdout.rs`, `response/stream.rs`, `response/formatters.rs`, and `response/metadata.rs`. +- SSE, NDJSON, and gRPC stdout streaming use the shared callback driver. Keep parsing format-specific and buffering bounded. +- Do not emit binary-looking bodies to terminal stdout unless the user explicitly forces stdout output. +- Put MIME selection policy in `src/format/content_type.rs`; update user documentation for visible format or MIME changes. diff --git a/src/proto/AGENTS.md b/src/proto/AGENTS.md new file mode 100644 index 0000000..9599d1b --- /dev/null +++ b/src/proto/AGENTS.md @@ -0,0 +1,9 @@ +# Protobuf development notes + +Applies to `src/proto/` and its submodules. Transport-level gRPC work must also follow `../grpc/AGENTS.md`. + +- Reuse shared descriptor discovery, schema, conversion, and JSON-stream parsing helpers. +- Reflection and descriptor processing must remain bounded by decoded bytes and message count. +- Streaming JSON conversion must process values incrementally rather than materializing all input. +- Enforce gRPC framing message-size limits before allocating or encoding a frame. +- Preserve local-schema and reflection behavior across unary, client-streaming, and bidirectional paths. diff --git a/src/update/AGENTS.md b/src/update/AGENTS.md new file mode 100644 index 0000000..23a09f1 --- /dev/null +++ b/src/update/AGENTS.md @@ -0,0 +1,11 @@ +# Update development notes + +Applies to `src/update/` and its submodules. Installer behavior is also covered by `tests/install.rs` and `tests/update.rs`. + +- Metadata, artifact, checksum, and redirect URLs require HTTPS except for explicit internal test overrides. +- Update networking must not inherit origin-specific TLS versions, client authentication, insecure mode, or Unix-socket settings. It may retain proxy, DNS, timeout, verbosity, and custom-CA settings. +- Stream artifacts while hashing them; do not buffer complete downloads in memory. +- Preserve archive path validation and the existing streaming tar / temporary zip behavior. +- Use `fileutil::FileLock`, atomic replacement, and parent-directory synchronization where supported. +- Background checks remain nonblocking. +- Keep installer completion setup opt-in and never edit shell startup files automatically. diff --git a/src/websocket/AGENTS.md b/src/websocket/AGENTS.md new file mode 100644 index 0000000..40e9720 --- /dev/null +++ b/src/websocket/AGENTS.md @@ -0,0 +1,12 @@ +# WebSocket development notes + +Applies to `src/websocket/` and its submodules. + +- WebSocket handshakes require HTTP/1.1. Keep explicit HTTP/2 and HTTP/3 rejection at validation boundaries. +- Reuse HTTP networking semantics for DNS, proxies, TLS, and connect-timeout budgeting; SOCKS5H resolves remotely while SOCKS5 resolves locally. +- Non-interactive mode connects before consuming piped stdin, sends and receives concurrently, closes only the send half at EOF, and continues receiving. +- Preserve empty text lines and automatic binary handling for invalid UTF-8. +- Keep incoming frames and messages bounded. Never write incoming binary data to terminal stdout. +- Lock and flush text stdout per message; treat broken pipes as normal termination. +- Keep origin TLS options on `wss://`; reject TLS options for `ws://`. +- Preserve session cookie loading and persistence around successful handshakes. diff --git a/tests/AGENTS.md b/tests/AGENTS.md new file mode 100644 index 0000000..a24edc7 --- /dev/null +++ b/tests/AGENTS.md @@ -0,0 +1,10 @@ +# Integration test development notes + +Applies to `tests/` and `tests/support/`. + +- Integration tests execute the compiled `fetch` binary. Put reusable servers and fixtures in the appropriate `tests/support/` module. +- Bind TCP and UDP test servers to `127.0.0.1:0`; do not reserve fixed ports. +- Use `mpsc` request notifications and `recv_timeout` rather than polling server state. +- `run_fetch` isolates the HTTP/3 cache by default; preserve that isolation in new helpers. +- CI runs integration tests with two test threads. If a socket test fails transiently with connection refusal or reset, diagnose with `-- --test-threads=1` before weakening assertions. +- Keep waits bounded and diagnostics useful. Do not add arbitrary sleeps when an event or channel can signal readiness.