@hunt-labs/bytekit-cli 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,126 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  This file records only the delta between versions. For how to use the package
9
9
  today, see [README.md](./README.md), which remains the authoritative usage guide.
10
10
 
11
+ ## [0.5.0] - 2026-07-24
12
+
13
+ ### Added
14
+
15
+ - Surface parity round 2 (#2545): every `x-sdk-scope: v0.1` endpoint the SDK
16
+ exposes now has a CLI command, and the high-value request fields are reachable
17
+ by flags. Each new flag follows the omit-when-absent rule — absent → the key is
18
+ left out of the request, so a server default is never materialized.
19
+ - **New command groups:** `usage get | daily | by-endpoint` (billing-period
20
+ usage reporting), `webhook-deliveries list | retry <id>` (inspect and requeue
21
+ deliveries), and `bulk cancel <id>` (stop an in-flight bulk job).
22
+ - **Monitor pause/resume:** `monitors update --status paused` / `--status active`.
23
+ `monitors create`/`update` also gain `--change-threshold` (float), `--notify-on`,
24
+ `--webhook-secret`, and the JSON-object flags `--webhook-headers`, `--options`,
25
+ `--scrape-options`. `--type` is **create-only** (a monitor's type is immutable).
26
+ - **`scrape create` multi-format `--format`:** repeatable AND comma-separated —
27
+ `--format raw_html --format markdown` and `--format raw_html,markdown` both send
28
+ `formats: ["raw_html","markdown"]`. Raw output (`--raw`/`-o`) requires exactly one
29
+ format. New scrape flags: `--cache-ttl`, `--markdown-query`, `--cookies`,
30
+ `--headers`, `--delay-ms`, `--events`, `--token-encoding`, `--custom`,
31
+ `--remove-base64-images` / `--no-remove-base64-images` (tri-state), and the
32
+ `--markdown-*` / `--with-*-summary` family.
33
+ - **`scrape get --raw` / `-o` / `--format`:** retrieve a completed queued scrape's
34
+ body from the shell (a still-queued job prints the id and exits `2`).
35
+ - **`screenshots create`:** `--block-ads`/`--no-block-ads`,
36
+ `--block-cookie-banners`/`--no-block-cookie-banners`, `--scroll`/`--no-scroll`
37
+ (tri-state pairs), `--wait-for-selector`, `--delay-ms`, `--dark-mode`,
38
+ `--device-scale-factor`, `--include-html`, `--country`, `--language`.
39
+ - **`fetch create` / `fetch get`:** `--country`, `--timeout-ms`, `--cache-ttl`
40
+ (both verbs); `--markdown-mode` (create only).
41
+ - **`sitemap create`:** `--strategy`, `--max-depth`, `--max-urls`.
42
+
43
+ ### Fixed
44
+
45
+ - `--json` no longer emits the literal text `undefined` — which is not valid JSON —
46
+ for a response with no body. `JSON.stringify(undefined)` returns the _value_
47
+ `undefined`, so `bytekit monitors delete <id> --json | jq .` failed with a parse
48
+ error on every delete, and `--json -o <file>` on the same response crashed with
49
+ `ERR_INVALID_ARG_TYPE` instead of writing anything. Every `--json` code path now
50
+ serializes through one helper. (#2544)
51
+ - `scrape create --async --raw` (and the bare `-o` form) no longer risks losing the
52
+ `sc_` id it writes. The id went to stdout immediately followed by a hard
53
+ `process.exit(2)`; Node documents `process.stdout` as **asynchronous** when it
54
+ points at a pipe, so anything still queued at that moment is discarded — exactly
55
+ the `sc_id=$(bytekit scrape create … --async --raw)` capture the flag exists for.
56
+ The queued signal is now a soft `process.exitCode`, so the process ends naturally
57
+ once stdout has drained. The exit code is unchanged. (#2544)
58
+ - The per-attempt request timeout now covers the **response body read**, not just the
59
+ time to first byte of headers. The abort budget was armed for `fetch` and cleared the
60
+ moment headers arrived, after which reading the body was unbounded — so a server that
61
+ answered with headers and then stalled its body (including a non-2xx whose error body
62
+ never finished) hung the CLI forever. The budget now spans headers **and** body on both
63
+ transports (`request` and the `/v1/fetch` raw path), and a mid-body abort surfaces as
64
+ the usual `ByteKitConnectionError` with `code: 'timeout'`.
65
+
66
+ The default is **unchanged at 120 s**, and the budget is still **per attempt** — the
67
+ single 429 retry and its backoff sleep still get a fresh full budget. One consequence
68
+ worth planning for: a legitimately large body (a multi-MB `/v1/fetch` document) now
69
+ counts against that same 120 s, where previously only the wait for headers did. (#2546)
70
+
71
+ ### Documented
72
+
73
+ - **Output contract:** with `--json`, a `204 No Content` response (e.g.
74
+ `monitors delete`) renders as JSON `null`. Human mode still prints nothing for
75
+ the same response. (#2544)
76
+ - **Exit codes** now have their own README section. In particular exit code `2` —
77
+ "accepted but still queued", emitted by `scrape create --async` in raw and human
78
+ modes — was previously documented nowhere despite being observable since 0.3.0.
79
+ It is deliberately neither `0` (a content-body success) nor `1` (an error). (#2544)
80
+ - Corrected two false README claims about `scrape` output: a bare `-o` implies
81
+ `--raw` and writes the requested format's **body** (it does not write "the same
82
+ text rendering the terminal would show"), and `--async --raw` prints the **bare
83
+ `sc_` id** on stdout with a note on stderr (it does not print the queued job
84
+ envelope). (#2544)
85
+
86
+ ## [0.4.0] - 2026-07-23
87
+
88
+ ### Removed (**BREAKING**)
89
+
90
+ - The entire `bytekit account api-keys` command group — `create`, `list`, `get`,
91
+ `update`, `reveal`, and `revoke`. Every one of these subcommands was
92
+ structurally unreachable: they target `/v1/account/api-keys*`, which the API
93
+ mounts behind a **Clerk dashboard session** and marks `x-internal` in the
94
+ OpenAPI spec. The CLI's only credential is a bearer `sk_` API key, which can
95
+ never satisfy a session verifier, so all six returned `401 unauthorized` in
96
+ every published version. Running one now exits `1` with
97
+ `error: unknown command 'api-keys'`.
98
+
99
+ Key management was deliberately NOT moved onto bearer auth. The reveal
100
+ operation returns the **live plaintext** of any key on the account, and there
101
+ is no per-key scope or permission model, so accepting API-key auth there would
102
+ turn any leaked key into a self-perpetuating credential factory.
103
+
104
+ **Replacement:** manage API keys from the dashboard at
105
+ [app.bytekit.com](https://app.bytekit.com). (#2536)
106
+
107
+ ## [0.3.1] - 2026-07-22
108
+
109
+ ### Fixed
110
+
111
+ - `bytekit fetch get` / `fetch create` print the fetched content again. **0.3.0's
112
+ claim that fetch "prints the raw body by default, or the `X-Fetch-*` metadata
113
+ as JSON with `--json`" was not true of the shipped artifact**: the CLI replaced
114
+ only `client.request`, but SDK 0.3.0 moved `/v1/fetch` onto `client.requestRaw`,
115
+ so the CLI's raw-body branch was unreachable and every fetch fell through to the
116
+ generic key/value printer — emitting `body: …`, a JSON map of every byte under
117
+ `bytes:`, and `headers: {}`. The CLI now overrides BOTH transport seams and
118
+ renders the SDK's real `FetchResult`. Binary content is byte-exact in every
119
+ mode. (#2535)
120
+ - Fetch commands honor the CLI's single 429 retry again. Riding the
121
+ un-overridden SDK transport also bypassed the retry, the per-attempt 120 s
122
+ timeout, and the connection-error taxonomy; all three now apply to fetch
123
+ exactly as they do to the JSON commands. (#2535)
124
+
125
+ ### Added
126
+
127
+ - `-o, --output <file>` on `fetch get` / `fetch create`: writes the fetched body
128
+ bytes to a file, byte-for-byte (with `--json`, writes the `X-Fetch-*` metadata
129
+ object instead). (#2535)
130
+
11
131
  ## [0.3.0] - 2026-07-21
12
132
 
13
133
  ### Removed (**BREAKING**)
@@ -116,6 +236,7 @@ Additive, lower-risk release — no breaking changes to existing commands.
116
236
  - Initial release of the ByteKit CLI, wrapping the ByteKit SDK as shell
117
237
  subcommands, published under the original `rapidcrawl-cli` name.
118
238
 
239
+ [0.4.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/cli
119
240
  [0.3.0]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/cli
120
241
  [0.2.2]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/cli
121
242
  [0.2.1]: https://github.com/Hunt-Labs-Inc/ByteKit/tree/main/packages/cli
package/README.md CHANGED
@@ -66,6 +66,16 @@ export BYTEKIT_BASE_URL=http://localhost:3001
66
66
  | `--base-url <url>` | `BYTEKIT_BASE_URL` | API base URL (default `https://api.bytekit.com`). |
67
67
  | `--json` | — | Print the raw JSON response instead of the human-readable rendering (for `fetch`, prints the `X-Fetch-*` metadata instead of the body). |
68
68
 
69
+ `--json` always emits a document `JSON.parse` (or `jq`) accepts. A command whose
70
+ API response carries no body — a `204 No Content`, as `monitors delete` returns
71
+ — prints JSON `null`:
72
+
73
+ ```bash
74
+ bytekit monitors delete mon_123abc --json | jq . # → null
75
+ ```
76
+
77
+ Human mode (no `--json`) prints nothing at all for the same response.
78
+
69
79
  ## Commands
70
80
 
71
81
  ### `scrape` — scrape URL content
@@ -77,33 +87,95 @@ bytekit scrape bulk create --file urls.txt --webhook-url https://your.app/hooks/
77
87
  ```
78
88
 
79
89
  `scrape create` also accepts the full `ScrapeRequest` surface, each flag omitted
80
- from the request when absent (a boolean flag never sends `false`):
81
-
82
- | Flag | Effect |
83
- | ------------------------ | ------------------------------------------------------- |
84
- | `--country <cc>` | Route through a country (e.g. `us`, `de`). |
85
- | `--timeout-ms <n>` | Per-request timeout in milliseconds. |
86
- | `--markdown-mode <mode>` | Markdown extraction mode: `article`, `raw`, `llm`. |
87
- | `--async` | Queue the scrape and return a job envelope immediately. |
88
- | `--webhook-url <url>` | Webhook notified when an async scrape completes. |
89
- | `--token-budget <n>` | Cap the markdown returned to `n` tokens. |
90
- | `--clean-markdown` | LLM-clean the markdown (billed 3x). |
91
- | `--include-tags <csv>` | Comma-separated HTML tags to keep. |
92
- | `--exclude-tags <csv>` | Comma-separated HTML tags to drop. |
93
- | `--mobile` | Render with a mobile viewport/user-agent. |
94
-
95
- `--async` combined with `--raw` (or a bare `-o`) prints the queued job envelope
96
- (the `sc_` id + status) rather than a format body, since a queued scrape has no
97
- body yet. On `scrape`, `-o` writes the same text rendering the terminal would
98
- show, so it is legal alongside `--json`.
90
+ from the request when absent (a boolean flag never sends `false`, so a server
91
+ default is never materialized):
92
+
93
+ | Flag | Effect |
94
+ | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
+ | `--format <fmt>` | Output format. **Repeatable and comma-separated** — `--format raw_html --format markdown` and `--format raw_html,markdown` both send `formats: ["raw_html","markdown"]`. Omitted → server default. |
96
+ | `--country <cc>` | Route through a country (e.g. `us`, `de`). |
97
+ | `--timeout-ms <n>` | Per-request timeout in milliseconds. |
98
+ | `--cache-ttl <ttl>` | Cache freshness: `0`, `Nh` (hours), or `Nd` (days) — e.g. `48h`, `2d`, `0`. Omitted → server default. |
99
+ | `--markdown-mode <mode>` | Markdown extraction mode: `article`, `raw`, `llm`. |
100
+ | `--markdown-query <q>` | BM25 query for relevance-ranked markdown filtering. |
101
+ | `--cookies <json>` | Cookies as a JSON array, e.g. `'[{"name":"a","value":"b"}]'`. |
102
+ | `--headers <json>` | Extra request headers as a JSON object, e.g. `'{"X-A":"b"}'`. |
103
+ | `--delay-ms <n>` | Post-load delay before capture, in milliseconds. |
104
+ | `--events <csv>` | Webhook events (comma-separated): `queued`, `completed`, `failed`. |
105
+ | `--token-budget <n>` | Cap the markdown returned to `n` tokens. |
106
+ | `--token-encoding <enc>` | Tokenizer for `--token-budget`: `cl100k_base`, `o200k_base`. |
107
+ | `--custom <json>` | User payload echoed on the envelope, as a JSON object. |
108
+ | `--remove-base64-images` / `--no-remove-base64-images` | Tri-state: strip / preserve base64 `data:` images. Omitted → server default (strip). |
109
+ | `--markdown-links <mode>` | Link rendering: `inline`, `references`, `none`, `text`. |
110
+ | `--markdown-images <mode>` | Image retention: `inline`, `references`, `none`, `text`. |
111
+ | `--with-links-summary` | Append a Links footer to the markdown. |
112
+ | `--with-images-summary` | Append an Images footer to the markdown. |
113
+ | `--markdown-compact` | Collapse excessive whitespace. |
114
+ | `--markdown-filter-images` | Filter low-signal images. |
115
+ | `--markdown-include-media` | Return rich link/image objects and a tables array. |
116
+ | `--markdown-include-warnings` | Include markdown-pipeline and tag-filter warnings. |
117
+ | `--markdown-include-stats` | Include a top-level stats object (chars, tokens, blocks). |
118
+ | `--clean-markdown` | LLM-clean the markdown (billed 3x). |
119
+ | `--include-tags <csv>` | Comma-separated HTML tags to keep. |
120
+ | `--exclude-tags <csv>` | Comma-separated HTML tags to drop. |
121
+ | `--mobile` | Render with a mobile viewport/user-agent. |
122
+ | `--async` | Queue the scrape and return a job envelope immediately. |
123
+ | `--webhook-url <url>` | Webhook notified when an async scrape completes. |
124
+
125
+ `scrape get` accepts the same output flags as `scrape create` —
126
+ `--raw`, `-o <file>`, and `--format <fmt>` — so a job queued with `--async --raw`
127
+ can be finished raw once it completes:
128
+
129
+ ```bash
130
+ sc_id=$(bytekit scrape create --url https://example.com --async --raw) || true
131
+ bytekit scrape get "$sc_id" --raw # prints the body once completed
132
+ bytekit scrape get "$sc_id" --format markdown -o page.md
133
+ ```
134
+
135
+ `--raw`/`-o` require **exactly one** format: with several `--format` values,
136
+ `scrape create --raw` is a usage error (raw mode emits a single body). A
137
+ still-queued `scrape get --raw` prints the bare id and exits [code `2`](#exit-codes),
138
+ same as `scrape create --async --raw`.
139
+
140
+ On `scrape`, a bare `-o <file>` implies `--raw`: the file receives the requested
141
+ format's body (so `-o page.md` holds markdown), not a JSON envelope. Add
142
+ `--json` to write the full envelope to the file instead. `--json` and `--raw`
143
+ together are a usage error.
144
+
145
+ `--async` combined with `--raw` (or a bare `-o`) has no format body to print
146
+ yet, so it writes the bare `sc_` id — and nothing else — to **stdout**, prints a
147
+ human "queued, poll it with…" note to **stderr**, and exits with
148
+ [code `2`](#exit-codes). The `-o` file is deliberately never created: a file
149
+ holding a bare id would masquerade as scraped content. This makes the handle
150
+ directly capturable:
151
+
152
+ ```bash
153
+ sc_id=$(bytekit scrape create --url https://example.com --async --raw) || true
154
+ bytekit scrape get "$sc_id"
155
+ ```
156
+
157
+ `--async --json` is outside that rule — it prints the full queued envelope and
158
+ exits `0`.
99
159
 
100
160
  ### `fetch` — raw, unprocessed response bodies
101
161
 
102
162
  ```bash
103
163
  bytekit fetch create --url https://example.com
164
+ bytekit fetch get --url https://example.com --format markdown
165
+ bytekit fetch get --url https://example.com/logo.png -o logo.png
104
166
  bytekit fetch bulk create --file urls.txt --webhook-url https://your.app/hooks/bytekit
105
167
  ```
106
168
 
169
+ `fetch create` / `fetch get` print the fetched body to stdout verbatim — no JSON
170
+ wrapper, no appended newline, binary-safe. `--json` prints the `X-Fetch-*`
171
+ response metadata instead of the body. `-o <file>` writes the body bytes to a
172
+ file byte-for-byte (with `--json`, the metadata object goes to the file
173
+ instead). `fetch bulk` returns an ordinary JSON job envelope.
174
+
175
+ Both verbs accept `--country <cc>`, `--timeout-ms <n>`, and `--cache-ttl <ttl>`
176
+ (`0`, `Nh`, or `Nd`), each omitted when absent. `--markdown-mode`
177
+ (`article`, `raw`, `llm`) is `fetch create` only.
178
+
107
179
  ### `search` — ranked web search
108
180
 
109
181
  ```bash
@@ -124,6 +196,15 @@ bytekit screenshots get ss_123abc -o shot.png
124
196
  (queue and return a job envelope). Each flag is omitted from the request when
125
197
  absent.
126
198
 
199
+ It also accepts `--wait-for-selector <sel>`, `--delay-ms <n>`,
200
+ `--device-scale-factor` (`1` or `2`), `--dark-mode`, `--include-html`,
201
+ `--country <cc>`, `--language <lang>`, and three **tri-state** blocker pairs:
202
+ `--block-ads` / `--no-block-ads`, `--block-cookie-banners` /
203
+ `--no-block-cookie-banners`, and `--scroll` / `--no-scroll`. Each of these three
204
+ defaults to `true` server-side, so passing neither half omits the key entirely
205
+ (the server default stands) — the `--no-` half sends `false`, the bare flag sends
206
+ `true`.
207
+
127
208
  `-o <file>` downloads the captured image bytes to that file (use `-o -` for
128
209
  stdout). Because `-o` writes BINARY on `screenshots`, it cannot be combined with
129
210
  `--json` (that would emit binary into a JSON pipeline) — pass one or the other.
@@ -136,10 +217,12 @@ image is not ready yet).
136
217
  bytekit bulk create --file urls.txt --webhook-url https://your.app/hooks/bytekit
137
218
  bulk_id=bulk_123abc
138
219
  bytekit bulk get "$bulk_id"
220
+ bytekit bulk cancel "$bulk_id"
139
221
  bytekit bulk screenshots list "$bulk_id"
140
222
  ```
141
223
 
142
224
  `urls.txt` is newline-delimited: one plain URL (or `{"url":"https://..."}`) per line.
225
+ `bulk cancel <id>` stops an in-flight job.
143
226
 
144
227
  ### `monitors` — watch a page for changes
145
228
 
@@ -147,6 +230,8 @@ bytekit bulk screenshots list "$bulk_id"
147
230
  bytekit monitors create --url https://example.com/pricing --interval-type daily \
148
231
  --webhook-url https://your.app/hooks/bytekit
149
232
  bytekit monitors list --limit 25 --status active
233
+ bytekit monitors update mon_123abc --status paused # pause
234
+ bytekit monitors update mon_123abc --status active # resume
150
235
  bytekit monitors captures list mon_123abc
151
236
  bytekit monitors delete mon_123abc
152
237
  ```
@@ -155,26 +240,77 @@ bytekit monitors delete mon_123abc
155
240
  (`active`, `processing`, `paused`, `cancelled`, `suspended`) as query filters,
156
241
  each omitted when absent.
157
242
 
243
+ `monitors update` gains `--status` (`active` | `paused` — pause and resume from
244
+ the shell), `--change-threshold <pct>` (accepts a float), `--notify-on`
245
+ (`change`, `every`), `--webhook-secret <secret>`, and three JSON-object flags:
246
+ `--webhook-headers`, `--options`, and `--scrape-options` (e.g.
247
+ `--options '{"device":"mobile"}'`). `monitors create` accepts the same set plus
248
+ `--type` (`screenshot` | `scrape`). `--type` is **create-only** — a monitor's
249
+ type is immutable, so `monitors update` deliberately has no `--type`. Every flag
250
+ is omitted from the request when absent.
251
+
158
252
  ### `sitemap` — crawl a site's URL graph
159
253
 
160
254
  ```bash
161
- bytekit sitemap create --url https://example.com
255
+ bytekit sitemap create --url https://example.com --strategy deep --max-depth 3 --max-urls 500
162
256
  bytekit sitemap get sm_123abc
163
257
  ```
164
258
 
165
- ### `account` — account details and API keys
259
+ `sitemap create` accepts `--strategy` (`quick`, `standard`, `deep`),
260
+ `--max-depth <n>`, and `--max-urls <n>`, each omitted when absent.
261
+
262
+ ### `account` — account details
166
263
 
167
264
  ```bash
168
265
  bytekit account get
169
- bytekit account api-keys list
170
- bytekit account api-keys create --name "ci-key"
171
- bytekit account api-keys reveal ak_123abc # masked by default
172
- bytekit account api-keys reveal ak_123abc --show # full plaintext
173
266
  ```
174
267
 
175
- `account api-keys reveal` prints a live secret, so it MASKS the key by default
176
- (`sk_live_…2345`). Pass `--show` for the full plaintext, or `--json` for the
177
- full envelope (for scripts).
268
+ API key management is not available from the CLI: it requires a signed-in
269
+ dashboard session, not a bearer API key. Create, rename, reveal, and revoke keys
270
+ at [app.bytekit.com](https://app.bytekit.com).
271
+
272
+ ### `usage` — account usage and billing-period reporting
273
+
274
+ ```bash
275
+ bytekit usage get
276
+ bytekit usage daily --from 2026-01-01 --to 2026-01-31
277
+ bytekit usage by-endpoint --api-key ak_123abc
278
+ ```
279
+
280
+ `usage get` returns the current billing-period summary. `usage daily` and
281
+ `usage by-endpoint` accept `--from <date>`, `--to <date>` (YYYY-MM-DD), and
282
+ `--api-key <ak_id>`, each omitted when absent (default range: last 30 days).
283
+
284
+ ### `webhook-deliveries` — inspect and retry webhook deliveries
285
+
286
+ ```bash
287
+ bytekit webhook-deliveries list --status failed --limit 10
288
+ bytekit webhook-deliveries retry wh_123abc
289
+ ```
290
+
291
+ `list` accepts `--limit <n>`, `--cursor <cursor>`, `--status`
292
+ (`pending`, `delivered`, `failed`, `exhausted`), and `--since <iso>`, each
293
+ omitted when absent. `retry <id>` requeues a failed or exhausted delivery.
294
+
295
+ ## Exit codes
296
+
297
+ | Code | Meaning |
298
+ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
299
+ | `0` | Success. |
300
+ | `1` | Usage error, or an API/connection error. The reason is on stderr, always prefixed `Error: `. |
301
+ | `2` | The scrape was accepted but is still **queued**, so there is no content body to emit — `scrape create --async` with `--raw` or a bare `-o` (which writes the bare `sc_` id to stdout), and the same command in human mode. Not a failure: the job was accepted and billed. Poll it with `bytekit scrape get <id>`. |
302
+
303
+ Code `2` is deliberately distinct from both `0` and `1` so a script can branch
304
+ on "queued" without parsing stderr:
305
+
306
+ ```bash
307
+ sc_id=$(bytekit scrape create --url https://example.com --async --raw)
308
+ case $? in
309
+ 0) echo "content: $sc_id" ;;
310
+ 2) echo "queued as $sc_id — poll it" ;;
311
+ *) exit 1 ;;
312
+ esac
313
+ ```
178
314
 
179
315
  ## Documentation
180
316
 
package/dist/client.d.ts CHANGED
@@ -42,19 +42,28 @@ export declare const DEFAULT_TIMEOUT_MS = 120000;
42
42
  */
43
43
  export declare function parseRetryAfterMs(raw: string | null): number;
44
44
  /**
45
- * Build the CLI's HTTP transport: the SDK's `request` contract, plus a single bounded retry on 429.
46
- *
47
- * Why the CLI owns the `fetch` instead of wrapping the SDK's `request()`: the SDK throws a
48
- * header-less `ByteKitError` and discards the `Response`, so a delegating wrapper can see
49
- * `status === 429` but can never read `Retry-After`. Owning the fetch is the only way to honor the
50
- * server's advertised backoff without editing the SDK.
45
+ * Build the CLI's JSON transport: the SDK's `request` contract, plus the single bounded 429 retry.
51
46
  *
52
47
  * Every SDK resource dereferences `this.client.request` at call time, so installing this as an own
53
- * property of the client (see {@link buildClient}) routes *all* commands through it from one seam.
54
- *
55
- * Retries apply to 429 only. Non-429 statuses and network rejections propagate untouched.
48
+ * property of the client (see {@link buildClient}) routes all JSON commands through it from one
49
+ * seam. `/v1/fetch` does NOT ride this transport — it rides `requestRaw` (see
50
+ * {@link createRetryingRequestRaw}); `/v1/fetch/bulk` is a JSON job envelope and does.
56
51
  */
57
52
  export declare function createRetryingRequest(apiKey: string, baseUrl: string, timeoutMs?: number): ByteKit['request'];
53
+ /**
54
+ * Build the CLI's RAW transport: the SDK's `requestRaw` contract, plus the same bounded 429 retry.
55
+ *
56
+ * `/v1/fetch` returns the fetched content AS the body (HTML, markdown, or true binary) with its
57
+ * metadata in `X-Fetch-*` headers, so the SDK routes it through `requestRaw` rather than the JSON
58
+ * `request`. The CLI must override BOTH seams or fetch commands silently bypass the retry and the
59
+ * CLI's error taxonomy — the #2535 defect, where only `request` was replaced.
60
+ *
61
+ * The returned shape is the SDK's `requestRaw` contract verbatim (`{body, bytes, headers, status}`)
62
+ * so `FetchResource` builds an ordinary `FetchResult` from it and the CLI never forks fetch
63
+ * handling away from the SDK. `bytes` is byte-exact (binary-safe); `body` is the best-effort UTF-8
64
+ * decode, matching the SDK's own semantics.
65
+ */
66
+ export declare function createRetryingRequestRaw(apiKey: string, baseUrl: string, timeoutMs?: number): ByteKit['requestRaw'];
58
67
  /**
59
68
  * Build an authenticated SDK client from global CLI options.
60
69
  *
@@ -63,7 +72,9 @@ export declare function createRetryingRequest(apiKey: string, baseUrl: string, t
63
72
  * baseUrl: --base-url flag → BYTEKIT_BASE_URL → DEFAULT_BASE_URL.
64
73
  * Exits with a descriptive error when no key is available.
65
74
  *
66
- * The returned client's `request` is replaced by the retrying transport, so every command inherits
67
- * the 429 retry from this one seam.
75
+ * The returned client's `request` AND `requestRaw` are both replaced by the retrying transports, so
76
+ * every command inherits the 429 retry — JSON commands via `request`, the `/v1/fetch` content
77
+ * commands via `requestRaw`. Overriding only `request` is the #2535 defect: the SDK's fetch
78
+ * resource rides `requestRaw`, so fetch bypassed the retry and the CLI's error taxonomy entirely.
68
79
  */
69
80
  export declare function buildClient(opts: GlobalOptions): ByteKit;