@hunt-labs/bytekit-cli 0.9.3 → 0.9.4

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
@@ -10,6 +10,18 @@ today, see [README.md](./README.md), which remains the authoritative usage guide
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.9.4] - 2026-09-10
14
+
15
+ ### Added
16
+
17
+ - `--cursor`, `--limit` and `--status` on the three bulk recovery commands (#3717):
18
+ `bulk screenshots list <id>`, `scrape bulk get <id>` and `fetch bulk get <id>`.
19
+ They forward the cursor pagination the API already exposed, so a job whose webhook
20
+ manifest came back incomplete can be enumerated page by page from the CLI.
21
+ `--limit` is bounded 1-500 and `--status` accepts only the documented result
22
+ statuses; both are rejected locally before a request is made. No new command and no
23
+ `--all` flag — the existing commands gained options, nothing else moved.
24
+
13
25
  ## [0.9.3] - 2026-09-03
14
26
 
15
27
  ### Changed
package/README.md CHANGED
@@ -276,8 +276,10 @@ bytekit bulk screenshots list "$bulk_id"
276
276
  `urls.txt` is newline-delimited: one plain URL (or `{"url":"https://..."}`) per
277
277
  line. A line whose only key is `url` is sent as a bare URL string; any line with
278
278
  additional keys switches that entry to the per-item shape. `bulk cancel <id>`
279
- stops an in-flight job; `bulk get <id>` and `bulk screenshots list <id>` take no
280
- flags.
279
+ stops an in-flight job. Recover another result page with `--cursor <cursor>`,
280
+ bound it with `--limit <n>`, or filter it with `--status <status>` on
281
+ `bulk screenshots list <id>`. The same recovery options are available on
282
+ `scrape bulk get <id>` and `fetch bulk get <id>`.
281
283
 
282
284
  The three bulk-job creators — `bulk create`, `scrape bulk create` and
283
285
  `fetch bulk create` — share one flag set:
@@ -2,19 +2,34 @@ import { Command } from 'commander';
2
2
  import { buildClient } from '../client.js';
3
3
  import { printResult, handleError } from '../output.js';
4
4
  import { parseBulkLines, toBulkItems } from '../lib/url-lines.js';
5
- import { parseJsonFlag } from '../lib/parse-flags.js';
5
+ import { parseEnum, parseIntFlag, parseJsonFlag } from '../lib/parse-flags.js';
6
+ const BULK_RESULT_STATUSES = ['pending', 'processing', 'completed', 'failed', 'cancelled'];
7
+ // Mirrors the `limit` query schema (`minimum: 1, maximum: 500`) on this job's recovery route in
8
+ // docs/api/openapi.yaml, so the range the `--limit` help text advertises is enforced locally.
9
+ const PAGE_LIMIT_BOUNDS = { min: 1, max: 500 };
6
10
  export function makeBulkCommand(getGlobal) {
7
11
  const cmd = new Command('bulk').description('Bulk screenshot operations');
8
12
  const screenshotsCmd = new Command('screenshots').description('Bulk screenshot sub-resources');
9
13
  screenshotsCmd
10
14
  .command('list <id>')
11
- .description('List screenshots in a bulk job')
12
- .action(async (id) => {
15
+ .description('List items in a mixed bulk job')
16
+ .option('--cursor <cursor>', 'Continue from a cursor returned by the preceding page')
17
+ .option('--limit <n>', 'Maximum items per type to return (1-500)', (v) => parseIntFlag('--limit', v, PAGE_LIMIT_BOUNDS))
18
+ .option('--status <status>', `Filter items by status: ${BULK_RESULT_STATUSES.join(', ')}`, (v) => parseEnum('--status', BULK_RESULT_STATUSES, v))
19
+ .action(async (id, opts) => {
13
20
  const g = getGlobal();
14
21
  try {
15
22
  // Inside the try: buildClient() throws on a missing key (#2686 item 4) and the throw must reach handleError(). See its doc comment in client.ts.
16
23
  const client = buildClient(g);
17
- const result = await client.bulk.screenshots.list(id);
24
+ const bulkId = id;
25
+ const pageParams = {
26
+ ...(opts.cursor !== undefined ? { cursor: opts.cursor } : {}),
27
+ ...(opts.limit !== undefined ? { limit: opts.limit } : {}),
28
+ ...(opts.status !== undefined ? { status: opts.status } : {}),
29
+ };
30
+ const result = Object.keys(pageParams).length === 0
31
+ ? await client.bulk.screenshots.list(bulkId)
32
+ : await client.bulk.screenshots.list(bulkId, pageParams);
18
33
  printResult(result, g.json);
19
34
  }
20
35
  catch (err) {
@@ -2,7 +2,11 @@ import { Command } from 'commander';
2
2
  import { buildClient } from '../client.js';
3
3
  import { printResult, emitFetchResult, handleError } from '../output.js';
4
4
  import { parseBulkLines } from '../lib/url-lines.js';
5
- import { parseIntFlag, parseCacheTtl, parseJsonFlag } from '../lib/parse-flags.js';
5
+ import { parseIntFlag, parseCacheTtl, parseJsonFlag, parseEnum } from '../lib/parse-flags.js';
6
+ const BULK_RESULT_STATUSES = ['pending', 'processing', 'completed', 'failed', 'cancelled'];
7
+ // Mirrors the `limit` query schema (`minimum: 1, maximum: 500`) on this job's recovery route in
8
+ // docs/api/openapi.yaml, so the range the `--limit` help text advertises is enforced locally.
9
+ const PAGE_LIMIT_BOUNDS = { min: 1, max: 500 };
6
10
  export function makeFetchCommand(getGlobal) {
7
11
  const cmd = new Command('fetch').description('Fetch URL content operations');
8
12
  const bulkCmd = new Command('bulk').description('Bulk fetch operations');
@@ -44,12 +48,23 @@ export function makeFetchCommand(getGlobal) {
44
48
  bulkCmd
45
49
  .command('get <id>')
46
50
  .description('Get a bulk fetch job')
47
- .action(async (id) => {
51
+ .option('--cursor <cursor>', 'Continue from a cursor returned by the preceding page')
52
+ .option('--limit <n>', 'Maximum items to return (1-500)', (v) => parseIntFlag('--limit', v, PAGE_LIMIT_BOUNDS))
53
+ .option('--status <status>', `Filter items by status: ${BULK_RESULT_STATUSES.join(', ')}`, (v) => parseEnum('--status', BULK_RESULT_STATUSES, v))
54
+ .action(async (id, opts) => {
48
55
  const g = getGlobal();
49
56
  try {
50
57
  // Inside the try: buildClient() throws on a missing key (#2686 item 4) and the throw must reach handleError(). See its doc comment in client.ts.
51
58
  const client = buildClient(g);
52
- const result = await client.fetch.bulk.get(id);
59
+ const bulkId = id;
60
+ const pageParams = {
61
+ ...(opts.cursor !== undefined ? { cursor: opts.cursor } : {}),
62
+ ...(opts.limit !== undefined ? { limit: opts.limit } : {}),
63
+ ...(opts.status !== undefined ? { status: opts.status } : {}),
64
+ };
65
+ const result = Object.keys(pageParams).length === 0
66
+ ? await client.fetch.bulk.get(bulkId)
67
+ : await client.fetch.bulk.get(bulkId, pageParams);
53
68
  printResult(result, g.json);
54
69
  }
55
70
  catch (err) {
@@ -11,6 +11,10 @@ const SCRAPE_EVENTS = ['queued', 'completed', 'failed'];
11
11
  const TOKEN_ENCODINGS = ['cl100k_base', 'o200k_base'];
12
12
  /** The `markdown_links` / `markdown_images` render-mode enum. */
13
13
  const MARKDOWN_RENDER_MODES = ['inline', 'references', 'none', 'text'];
14
+ const BULK_RESULT_STATUSES = ['pending', 'processing', 'completed', 'failed', 'cancelled'];
15
+ // Mirrors the `limit` query schema (`minimum: 1, maximum: 500`) on this job's recovery route in
16
+ // docs/api/openapi.yaml, so the range the `--limit` help text advertises is enforced locally.
17
+ const PAGE_LIMIT_BOUNDS = { min: 1, max: 500 };
14
18
  /**
15
19
  * Exit code when a queued (202 async) envelope reaches a non-JSON output mode.
16
20
  *
@@ -83,12 +87,23 @@ export function makeScrapeCommand(getGlobal) {
83
87
  bulkCmd
84
88
  .command('get <id>')
85
89
  .description('Get a bulk scrape job')
86
- .action(async (id) => {
90
+ .option('--cursor <cursor>', 'Continue from a cursor returned by the preceding page')
91
+ .option('--limit <n>', 'Maximum items to return (1-500)', (v) => parseIntFlag('--limit', v, PAGE_LIMIT_BOUNDS))
92
+ .option('--status <status>', `Filter items by status: ${BULK_RESULT_STATUSES.join(', ')}`, (v) => parseEnum('--status', BULK_RESULT_STATUSES, v))
93
+ .action(async (id, opts) => {
87
94
  const g = getGlobal();
88
95
  try {
89
96
  // Inside the try: buildClient() throws on a missing key (#2686 item 4) and the throw must reach handleError(). See its doc comment in client.ts.
90
97
  const client = buildClient(g);
91
- const result = await client.scrape.bulk.get(id);
98
+ const bulkId = id;
99
+ const pageParams = {
100
+ ...(opts.cursor !== undefined ? { cursor: opts.cursor } : {}),
101
+ ...(opts.limit !== undefined ? { limit: opts.limit } : {}),
102
+ ...(opts.status !== undefined ? { status: opts.status } : {}),
103
+ };
104
+ const result = Object.keys(pageParams).length === 0
105
+ ? await client.scrape.bulk.get(bulkId)
106
+ : await client.scrape.bulk.get(bulkId, pageParams);
92
107
  printResult(result, g.json);
93
108
  }
94
109
  catch (err) {
@@ -8,8 +8,16 @@
8
8
  * Parse a whole-number flag value. The strict `/^\d+$/` test deterministically rejects `abc`, ``,
9
9
  * `1.5`, `-5`, `1e2`, so an off-spec value fails at parse time with an actionable message instead
10
10
  * of a server 422 or a NaN on the wire.
11
+ *
12
+ * `bounds` extends that guarantee to flags whose spec schema declares a range (#3717): a
13
+ * well-formed integer outside `[min, max]` is just as certain to 422 as `abc` is, so it is
14
+ * rejected here rather than spending a round trip to be told so. Pass it wherever the flag's help
15
+ * text states a range; omit it where the spec declares none.
11
16
  */
12
- export declare function parseIntFlag(flag: string, value: string): number;
17
+ export declare function parseIntFlag(flag: string, value: string, bounds?: {
18
+ min: number;
19
+ max: number;
20
+ }): number;
13
21
  /** Reject an off-spec enum value at parse time with the allowed set in the message. */
14
22
  export declare function parseEnum<T extends string>(flag: string, allowed: readonly T[], value: string): T;
15
23
  /** Split a comma-separated flag value into a trimmed, non-empty string array. */
@@ -9,12 +9,21 @@ import { InvalidArgumentError } from 'commander';
9
9
  * Parse a whole-number flag value. The strict `/^\d+$/` test deterministically rejects `abc`, ``,
10
10
  * `1.5`, `-5`, `1e2`, so an off-spec value fails at parse time with an actionable message instead
11
11
  * of a server 422 or a NaN on the wire.
12
+ *
13
+ * `bounds` extends that guarantee to flags whose spec schema declares a range (#3717): a
14
+ * well-formed integer outside `[min, max]` is just as certain to 422 as `abc` is, so it is
15
+ * rejected here rather than spending a round trip to be told so. Pass it wherever the flag's help
16
+ * text states a range; omit it where the spec declares none.
12
17
  */
13
- export function parseIntFlag(flag, value) {
18
+ export function parseIntFlag(flag, value, bounds) {
14
19
  if (!/^\d+$/.test(value)) {
15
20
  throw new InvalidArgumentError(`${flag} must be a whole number.`);
16
21
  }
17
- return Number(value);
22
+ const parsed = Number(value);
23
+ if (bounds && (parsed < bounds.min || parsed > bounds.max)) {
24
+ throw new InvalidArgumentError(`${flag} must be between ${bounds.min} and ${bounds.max}.`);
25
+ }
26
+ return parsed;
18
27
  }
19
28
  /** Reject an off-spec enum value at parse time with the allowed set in the message. */
20
29
  export function parseEnum(flag, allowed, value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hunt-labs/bytekit-cli",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "ByteKit CLI — wraps @hunt-labs/bytekit-sdk as shell subcommands",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "commander": "^12.0.0",
44
- "@hunt-labs/bytekit-sdk": "^0.9.2"
44
+ "@hunt-labs/bytekit-sdk": "^0.10.0"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc",