@tiny-fish/cli 0.30.2-next.256 → 0.30.2-next.258

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.
Files changed (77) hide show
  1. package/dist/commands/auth.d.ts +1 -1
  2. package/dist/commands/auth.js +52 -52
  3. package/dist/commands/batch.d.ts +1 -1
  4. package/dist/commands/batch.js +54 -46
  5. package/dist/commands/browser.d.ts +1 -1
  6. package/dist/commands/browser.js +14 -14
  7. package/dist/commands/config-claude.d.ts +1 -1
  8. package/dist/commands/config-claude.js +22 -22
  9. package/dist/commands/connect.d.ts +3 -3
  10. package/dist/commands/connect.js +150 -150
  11. package/dist/commands/doctor.d.ts +4 -4
  12. package/dist/commands/doctor.js +104 -104
  13. package/dist/commands/fetch.d.ts +1 -1
  14. package/dist/commands/fetch.js +24 -27
  15. package/dist/commands/onboard.d.ts +1 -1
  16. package/dist/commands/onboard.js +50 -50
  17. package/dist/commands/profile.d.ts +3 -3
  18. package/dist/commands/profile.js +45 -45
  19. package/dist/commands/run.d.ts +1 -1
  20. package/dist/commands/run.js +78 -65
  21. package/dist/commands/runs.d.ts +1 -1
  22. package/dist/commands/runs.js +63 -50
  23. package/dist/commands/search.d.ts +1 -1
  24. package/dist/commands/search.js +16 -16
  25. package/dist/commands/upgrade.d.ts +2 -2
  26. package/dist/commands/upgrade.js +36 -36
  27. package/dist/commands/vault.d.ts +1 -1
  28. package/dist/commands/vault.js +48 -45
  29. package/dist/commands/wallet.d.ts +1 -1
  30. package/dist/commands/wallet.js +14 -14
  31. package/dist/index.js +2 -2
  32. package/dist/lib/auth.d.ts +5 -5
  33. package/dist/lib/auth.js +56 -56
  34. package/dist/lib/batch-store.d.ts +1 -1
  35. package/dist/lib/batch-store.js +4 -4
  36. package/dist/lib/chrome-cookies.d.ts +1 -1
  37. package/dist/lib/chrome-cookies.js +24 -24
  38. package/dist/lib/claude-config.js +37 -37
  39. package/dist/lib/cli-install.js +12 -12
  40. package/dist/lib/client.d.ts +2 -2
  41. package/dist/lib/client.js +61 -61
  42. package/dist/lib/connect-all.js +140 -134
  43. package/dist/lib/connect-auth.d.ts +2 -2
  44. package/dist/lib/connect-auth.js +15 -15
  45. package/dist/lib/connect-clients.d.ts +6 -6
  46. package/dist/lib/connect-clients.js +143 -143
  47. package/dist/lib/connect-fallback.d.ts +1 -1
  48. package/dist/lib/connect-fallback.js +38 -38
  49. package/dist/lib/connect-picker.d.ts +4 -4
  50. package/dist/lib/connect-picker.js +28 -28
  51. package/dist/lib/connect-runtime.d.ts +7 -7
  52. package/dist/lib/connect-runtime.js +61 -61
  53. package/dist/lib/constants.js +13 -13
  54. package/dist/lib/cursor-config.d.ts +1 -1
  55. package/dist/lib/cursor-config.js +30 -30
  56. package/dist/lib/doctor-report.d.ts +2 -2
  57. package/dist/lib/doctor-report.js +22 -22
  58. package/dist/lib/harness-detect.js +39 -39
  59. package/dist/lib/harness.js +9 -9
  60. package/dist/lib/hermes-env.js +26 -23
  61. package/dist/lib/install-root.js +12 -12
  62. package/dist/lib/notice.d.ts +1 -1
  63. package/dist/lib/notice.js +12 -12
  64. package/dist/lib/output.js +14 -14
  65. package/dist/lib/registration-detect.d.ts +3 -3
  66. package/dist/lib/registration-detect.js +82 -82
  67. package/dist/lib/setup-telemetry.d.ts +4 -4
  68. package/dist/lib/setup-telemetry.js +34 -34
  69. package/dist/lib/signals.js +1 -1
  70. package/dist/lib/skill-install.d.ts +3 -3
  71. package/dist/lib/skill-install.js +40 -40
  72. package/dist/lib/types.d.ts +8 -8
  73. package/dist/lib/verify.d.ts +1 -1
  74. package/dist/lib/verify.js +28 -28
  75. package/dist/program.d.ts +1 -1
  76. package/dist/program.js +28 -28
  77. package/package.json +18 -9
@@ -1,13 +1,13 @@
1
- import { EventType, RunStatus } from "@tiny-fish/sdk";
2
- import { readFile } from "node:fs/promises";
3
- import { z } from "zod";
4
- import { getApiKey } from "../lib/auth.js";
5
- import { cancelRun, runAsync, runStream, runSync } from "../lib/client.js";
6
- import { err, errLine, handleApiError, out, outLine } from "../lib/output.js";
7
- import { BASE_URL } from "../lib/constants.js";
8
- const VALID_BROWSER_PROFILES = ["lite", "stealth"];
9
- const VALID_MODES = ["default", "strict"];
10
- const VALID_CURSOR_STYLES = ["fish", "standard", "green-pointer"];
1
+ import { EventType, RunStatus, } from '@tiny-fish/sdk';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { z } from 'zod';
4
+ import { getApiKey } from '../lib/auth.js';
5
+ import { cancelRun, runAsync, runStream, runSync } from '../lib/client.js';
6
+ import { err, errLine, handleApiError, out, outLine } from '../lib/output.js';
7
+ import { BASE_URL } from '../lib/constants.js';
8
+ const VALID_BROWSER_PROFILES = ['lite', 'stealth'];
9
+ const VALID_MODES = ['default', 'strict'];
10
+ const VALID_CURSOR_STYLES = ['fish', 'standard', 'green-pointer'];
11
11
  const MAX_STEPS_LIMIT = 500;
12
12
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
13
  function parseSessionId(value) {
@@ -21,11 +21,11 @@ function parseSessionId(value) {
21
21
  }
22
22
  const outputSchemaFieldSchema = z.object({}).catchall(z.unknown());
23
23
  function extractErrorMessage(error) {
24
- if (typeof error === "string")
24
+ if (typeof error === 'string')
25
25
  return error;
26
- if (typeof error === "object" && error !== null) {
26
+ if (typeof error === 'object' && error !== null) {
27
27
  const maybeError = error;
28
- if (typeof maybeError.message === "string") {
28
+ if (typeof maybeError.message === 'string') {
29
29
  return maybeError.message;
30
30
  }
31
31
  }
@@ -37,11 +37,14 @@ function checkRunResult(result, expectComplete = true) {
37
37
  err({
38
38
  error: errorMessage,
39
39
  run_id: result.run_id,
40
- status: "status" in result ? result.status : undefined,
40
+ status: 'status' in result ? result.status : undefined,
41
41
  });
42
42
  process.exit(1);
43
43
  }
44
- if (expectComplete && "status" in result && result.status && result.status !== RunStatus.COMPLETED) {
44
+ if (expectComplete &&
45
+ 'status' in result &&
46
+ result.status &&
47
+ result.status !== RunStatus.COMPLETED) {
45
48
  err({ error: `Run ${result.status}`, run_id: result.run_id, status: result.status });
46
49
  process.exit(1);
47
50
  }
@@ -67,7 +70,7 @@ function parseChoice(value, valid, flag) {
67
70
  return undefined;
68
71
  if (valid.includes(value))
69
72
  return value;
70
- err({ error: `Invalid ${flag} "${value}". Must be one of: ${valid.join(", ")}` });
73
+ err({ error: `Invalid ${flag} "${value}". Must be one of: ${valid.join(', ')}` });
71
74
  process.exit(1);
72
75
  }
73
76
  function parsePositiveInteger(value, flag, max) {
@@ -95,7 +98,7 @@ function parseHttpsUrl(value, flag) {
95
98
  err({ error: `Invalid ${flag} "${value}". Provide a valid URL.` });
96
99
  process.exit(1);
97
100
  }
98
- if (parsed.protocol !== "https:") {
101
+ if (parsed.protocol !== 'https:') {
99
102
  err({ error: `${flag} must use HTTPS.` });
100
103
  process.exit(1);
101
104
  }
@@ -103,53 +106,53 @@ function parseHttpsUrl(value, flag) {
103
106
  }
104
107
  async function loadOutputSchema(opts) {
105
108
  if (opts.outputSchema !== undefined && opts.outputSchemaFile !== undefined) {
106
- err({ error: "--output-schema and --output-schema-file are mutually exclusive" });
109
+ err({ error: '--output-schema and --output-schema-file are mutually exclusive' });
107
110
  process.exit(1);
108
111
  }
109
112
  if (opts.outputSchema !== undefined) {
110
- return parseOutputSchema(opts.outputSchema, "--output-schema");
113
+ return parseOutputSchema(opts.outputSchema, '--output-schema');
111
114
  }
112
115
  if (opts.outputSchemaFile === undefined)
113
116
  return undefined;
114
117
  let fileContents;
115
118
  try {
116
- fileContents = await readFile(opts.outputSchemaFile, "utf8");
119
+ fileContents = await readFile(opts.outputSchemaFile, 'utf8');
117
120
  }
118
121
  catch {
119
122
  err({ error: `Unable to read --output-schema-file "${opts.outputSchemaFile}"` });
120
123
  process.exit(1);
121
124
  }
122
- return parseOutputSchema(fileContents, "--output-schema-file");
125
+ return parseOutputSchema(fileContents, '--output-schema-file');
123
126
  }
124
127
  export function registerRun(agentCmd) {
125
128
  const runCmd = agentCmd
126
- .command("run")
127
- .description("Run a browser automation or manage runs")
128
- .option("--url <url>", "Target URL for the automation")
129
- .option("--sync", "Wait for result without streaming steps")
130
- .option("--async", "Submit without waiting for result")
131
- .option("--output-schema <json>", "Inline JSON Schema object for structured output")
132
- .option("--output-schema-file <path>", "Path to a JSON file containing the output schema")
133
- .option("--browser-profile <profile>", "Browser profile for execution (lite|stealth)")
134
- .option("--max-steps <n>", `Maximum tool-call steps before stopping (1-${MAX_STEPS_LIMIT})`)
135
- .option("--max-duration-seconds <n>", "Maximum wall-clock seconds before stopping")
136
- .option("--mode <mode>", "Agent behavior mode (default|strict)")
137
- .option("--cursor-style <style>", "Cursor style (fish|standard|green-pointer)")
138
- .option("--capture-elements", "Capture page element metadata (gated capability; returns 403 if not enabled for your account)")
139
- .option("--capture-snapshots", "Capture page snapshots")
140
- .option("--capture-screenshots", "Capture screenshots")
141
- .option("--no-capture-screenshots", "Disable screenshot capture")
142
- .option("--capture-recording", "Capture a browser recording (gated capability; returns 403 if not enabled for your account)")
143
- .option("--capture-html", "Capture HTML snapshots")
144
- .option("--webhook-url <url>", "HTTPS webhook URL for run lifecycle events")
145
- .option("--use-profile", "Use an attached browser profile")
146
- .option("--profile-id <id>", "Browser Context Profile ID to start from (requires --use-profile)")
147
- .option("--session-id <uuid>", "Caller-provided UUID for idempotency / parallel-safe --sync")
148
- .option("--use-vault", "Consume vault credentials in this run")
149
- .option("--credential-item-id <id>", "Scope vault to a specific credential item ID (repeat for multiple; get IDs from `vault item list`; requires --use-vault)", (v, acc) => [...acc, v], [])
150
- .option("--pretty", "Human-readable output")
151
- .argument("[goal]", "Goal to automate")
152
- .addHelpText("after", "\nToken scope note:\n CLI runs use your personal API key. MCP runs use the MCP token.\n These are different namespaces; getting an MCP run ID with the CLI returns 404 by design.\n")
129
+ .command('run')
130
+ .description('Run a browser automation or manage runs')
131
+ .option('--url <url>', 'Target URL for the automation')
132
+ .option('--sync', 'Wait for result without streaming steps')
133
+ .option('--async', 'Submit without waiting for result')
134
+ .option('--output-schema <json>', 'Inline JSON Schema object for structured output')
135
+ .option('--output-schema-file <path>', 'Path to a JSON file containing the output schema')
136
+ .option('--browser-profile <profile>', 'Browser profile for execution (lite|stealth)')
137
+ .option('--max-steps <n>', `Maximum tool-call steps before stopping (1-${MAX_STEPS_LIMIT})`)
138
+ .option('--max-duration-seconds <n>', 'Maximum wall-clock seconds before stopping')
139
+ .option('--mode <mode>', 'Agent behavior mode (default|strict)')
140
+ .option('--cursor-style <style>', 'Cursor style (fish|standard|green-pointer)')
141
+ .option('--capture-elements', 'Capture page element metadata (gated capability; returns 403 if not enabled for your account)')
142
+ .option('--capture-snapshots', 'Capture page snapshots')
143
+ .option('--capture-screenshots', 'Capture screenshots')
144
+ .option('--no-capture-screenshots', 'Disable screenshot capture')
145
+ .option('--capture-recording', 'Capture a browser recording (gated capability; returns 403 if not enabled for your account)')
146
+ .option('--capture-html', 'Capture HTML snapshots')
147
+ .option('--webhook-url <url>', 'HTTPS webhook URL for run lifecycle events')
148
+ .option('--use-profile', 'Use an attached browser profile')
149
+ .option('--profile-id <id>', 'Browser Context Profile ID to start from (requires --use-profile)')
150
+ .option('--session-id <uuid>', 'Caller-provided UUID for idempotency / parallel-safe --sync')
151
+ .option('--use-vault', 'Consume vault credentials in this run')
152
+ .option('--credential-item-id <id>', 'Scope vault to a specific credential item ID (repeat for multiple; get IDs from `vault item list`; requires --use-vault)', (v, acc) => [...acc, v], [])
153
+ .option('--pretty', 'Human-readable output')
154
+ .argument('[goal]', 'Goal to automate')
155
+ .addHelpText('after', '\nToken scope note:\n CLI runs use your personal API key. MCP runs use the MCP token.\n These are different namespaces; getting an MCP run ID with the CLI returns 404 by design.\n')
153
156
  .action(async (goal, opts) => {
154
157
  // If no goal is provided and no subcommand matched, show help
155
158
  if (!goal) {
@@ -161,7 +164,7 @@ export function registerRun(agentCmd) {
161
164
  process.exit(1);
162
165
  }
163
166
  if (opts.sync && opts.async) {
164
- err({ error: "--sync and --async are mutually exclusive" });
167
+ err({ error: '--sync and --async are mutually exclusive' });
165
168
  process.exit(1);
166
169
  }
167
170
  // Normalise URL: accept bare hostnames by prepending https://.
@@ -176,7 +179,9 @@ export function registerRun(agentCmd) {
176
179
  normalizedUrl = `https://${opts.url}`;
177
180
  }
178
181
  catch {
179
- err({ error: `Invalid --url "${opts.url}". Provide a valid URL, e.g. https://example.com or example.com` });
182
+ err({
183
+ error: `Invalid --url "${opts.url}". Provide a valid URL, e.g. https://example.com or example.com`,
184
+ });
180
185
  process.exit(1);
181
186
  }
182
187
  }
@@ -187,22 +192,22 @@ export function registerRun(agentCmd) {
187
192
  // The server rejects credential_item_ids without use_vault (400); fail fast here.
188
193
  const hasCredentialItemIds = opts.credentialItemId.length > 0;
189
194
  if (hasCredentialItemIds && !opts.useVault) {
190
- err({ error: "--credential-item-id requires --use-vault" });
195
+ err({ error: '--credential-item-id requires --use-vault' });
191
196
  process.exit(1);
192
197
  }
193
198
  // The server rejects profile_id without use_profile (400); fail fast here.
194
199
  if (opts.profileId !== undefined && !opts.useProfile) {
195
- err({ error: "--profile-id requires --use-profile" });
200
+ err({ error: '--profile-id requires --use-profile' });
196
201
  process.exit(1);
197
202
  }
198
203
  const outputSchema = await loadOutputSchema(opts);
199
- const browserProfile = parseChoice(opts.browserProfile, VALID_BROWSER_PROFILES, "--browser-profile");
200
- const mode = parseChoice(opts.mode, VALID_MODES, "--mode");
201
- const cursorStyle = parseChoice(opts.cursorStyle, VALID_CURSOR_STYLES, "--cursor-style");
202
- const maxSteps = parsePositiveInteger(opts.maxSteps, "--max-steps", MAX_STEPS_LIMIT);
203
- const maxDurationSeconds = parsePositiveInteger(opts.maxDurationSeconds, "--max-duration-seconds");
204
+ const browserProfile = parseChoice(opts.browserProfile, VALID_BROWSER_PROFILES, '--browser-profile');
205
+ const mode = parseChoice(opts.mode, VALID_MODES, '--mode');
206
+ const cursorStyle = parseChoice(opts.cursorStyle, VALID_CURSOR_STYLES, '--cursor-style');
207
+ const maxSteps = parsePositiveInteger(opts.maxSteps, '--max-steps', MAX_STEPS_LIMIT);
208
+ const maxDurationSeconds = parsePositiveInteger(opts.maxDurationSeconds, '--max-duration-seconds');
204
209
  const sessionId = parseSessionId(opts.sessionId);
205
- const webhookUrl = parseHttpsUrl(opts.webhookUrl, "--webhook-url");
210
+ const webhookUrl = parseHttpsUrl(opts.webhookUrl, '--webhook-url');
206
211
  const agentConfig = mode !== undefined ||
207
212
  cursorStyle !== undefined ||
208
213
  maxSteps !== undefined ||
@@ -269,7 +274,7 @@ export function registerRun(agentCmd) {
269
274
  return;
270
275
  }
271
276
  if (opts.sync) {
272
- errLine("Waiting for result...");
277
+ errLine('Waiting for result...');
273
278
  let result;
274
279
  try {
275
280
  result = await runSync(req, apiKey);
@@ -298,11 +303,13 @@ export function registerRun(agentCmd) {
298
303
  siginted = true;
299
304
  controller.abort();
300
305
  };
301
- process.once("SIGINT", onSigint);
306
+ process.once('SIGINT', onSigint);
302
307
  let streamFailed = false;
303
308
  try {
304
309
  for await (const event of runStream(req, apiKey, controller.signal)) {
305
- if (!handleStreamEvent(event, opts.pretty, (id) => { capturedRunId = id; })) {
310
+ if (!handleStreamEvent(event, opts.pretty, (id) => {
311
+ capturedRunId = id;
312
+ })) {
306
313
  streamFailed = true;
307
314
  break;
308
315
  }
@@ -319,15 +326,17 @@ export function registerRun(agentCmd) {
319
326
  await cancelRun(capturedRunId, apiKey);
320
327
  cancelled = true;
321
328
  }
322
- catch { /* ignore */ }
329
+ catch {
330
+ /* ignore */
331
+ }
323
332
  }
324
- process.stderr.write(cancelled ? "\nRun cancelled.\n" : "\n");
333
+ process.stderr.write(cancelled ? '\nRun cancelled.\n' : '\n');
325
334
  process.exit(130);
326
335
  }
327
336
  handleApiError(e);
328
337
  }
329
338
  finally {
330
- process.removeListener("SIGINT", onSigint);
339
+ process.removeListener('SIGINT', onSigint);
331
340
  }
332
341
  if (streamFailed)
333
342
  process.exit(1);
@@ -340,7 +349,11 @@ export function registerRun(agentCmd) {
340
349
  */
341
350
  function handleStreamEvent(event, pretty, onRunId) {
342
351
  if (event.type === EventType.COMPLETE && event.status !== RunStatus.COMPLETED) {
343
- err({ error: event.error?.message ?? `Run ${event.status}`, run_id: event.run_id, status: event.status });
352
+ err({
353
+ error: event.error?.message ?? `Run ${event.status}`,
354
+ run_id: event.run_id,
355
+ status: event.status,
356
+ });
344
357
  return false;
345
358
  }
346
359
  if (pretty) {
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerRuns(runCmd: Command): void;
@@ -1,7 +1,7 @@
1
- import { RunStatus } from "@tiny-fish/sdk";
2
- import { getApiKey } from "../lib/auth.js";
3
- import { cancelRun, getRun, getRunSteps, listRuns } from "../lib/client.js";
4
- import { err, handleApiError, out, outLine } from "../lib/output.js";
1
+ import { RunStatus } from '@tiny-fish/sdk';
2
+ import { getApiKey } from '../lib/auth.js';
3
+ import { cancelRun, getRun, getRunSteps, listRuns } from '../lib/client.js';
4
+ import { err, handleApiError, out, outLine } from '../lib/output.js';
5
5
  const VALID_STATUSES = Object.values(RunStatus);
6
6
  const DEFAULT_WATCH_TIMEOUT_MS = 1_800_000;
7
7
  const DEFAULT_WATCH_INTERVAL_MS = 3000;
@@ -12,16 +12,18 @@ const TERMINAL_STATUSES = [
12
12
  RunStatus.CANCELLED,
13
13
  ];
14
14
  function formatStep(step, index) {
15
- const action = step.action ?? "(no action)";
16
- const meta = [step.status, step.duration].filter(Boolean).join(", ");
17
- return `${String(index + 1).padStart(2, " ")}. ${action}${meta ? ` (${meta})` : ""}`;
15
+ const action = step.action ?? '(no action)';
16
+ const meta = [step.status, step.duration].filter(Boolean).join(', ');
17
+ return `${String(index + 1).padStart(2, ' ')}. ${action}${meta ? ` (${meta})` : ''}`;
18
18
  }
19
19
  function parsePositiveInt(value, flag, defaultMs, minMs = 1) {
20
20
  if (value === undefined)
21
21
  return defaultMs;
22
22
  // Reject "5000ms", "1e3", "+5", and similar — only whole-digit input.
23
23
  if (!/^\d+$/.test(value)) {
24
- err({ error: `Invalid ${flag} "${value}". Must be a whole-number integer in milliseconds (>= ${minMs}).` });
24
+ err({
25
+ error: `Invalid ${flag} "${value}". Must be a whole-number integer in milliseconds (>= ${minMs}).`,
26
+ });
25
27
  process.exit(1);
26
28
  }
27
29
  const parsed = Number(value);
@@ -35,52 +37,63 @@ function withDeadline(promise, deadline, signal) {
35
37
  return new Promise((resolve, reject) => {
36
38
  const remaining = deadline - Date.now();
37
39
  if (remaining <= 0) {
38
- reject(new Error("watch deadline exceeded"));
40
+ reject(new Error('watch deadline exceeded'));
39
41
  return;
40
42
  }
41
43
  if (signal.aborted) {
42
- reject(new Error("watch aborted"));
44
+ reject(new Error('watch aborted'));
43
45
  return;
44
46
  }
45
- const timer = setTimeout(() => reject(new Error("watch deadline exceeded")), remaining);
47
+ const timer = setTimeout(() => reject(new Error('watch deadline exceeded')), remaining);
46
48
  const onAbort = () => {
47
49
  clearTimeout(timer);
48
- reject(new Error("watch aborted"));
50
+ reject(new Error('watch aborted'));
49
51
  };
50
- signal.addEventListener("abort", onAbort, { once: true });
51
- promise.then((v) => { clearTimeout(timer); signal.removeEventListener("abort", onAbort); resolve(v); }, (e) => { clearTimeout(timer); signal.removeEventListener("abort", onAbort); reject(e); });
52
+ signal.addEventListener('abort', onAbort, { once: true });
53
+ promise.then((v) => {
54
+ clearTimeout(timer);
55
+ signal.removeEventListener('abort', onAbort);
56
+ resolve(v);
57
+ }, (e) => {
58
+ clearTimeout(timer);
59
+ signal.removeEventListener('abort', onAbort);
60
+ reject(e);
61
+ });
52
62
  });
53
63
  }
54
64
  function sleep(ms, signal) {
55
65
  return new Promise((resolve, reject) => {
56
66
  if (signal.aborted) {
57
- reject(new Error("aborted"));
67
+ reject(new Error('aborted'));
58
68
  return;
59
69
  }
60
70
  const timer = setTimeout(() => {
61
- signal.removeEventListener("abort", onAbort);
71
+ signal.removeEventListener('abort', onAbort);
62
72
  resolve();
63
73
  }, ms);
64
74
  const onAbort = () => {
65
75
  clearTimeout(timer);
66
- reject(new Error("aborted"));
76
+ reject(new Error('aborted'));
67
77
  };
68
- signal.addEventListener("abort", onAbort, { once: true });
78
+ signal.addEventListener('abort', onAbort, { once: true });
69
79
  });
70
80
  }
71
81
  export function registerRuns(runCmd) {
72
82
  // ── run list ───────────────────────────────────────────────────────────────
73
83
  runCmd
74
- .command("list")
75
- .description("List your automation runs")
76
- .option("--status <status>", "Filter by status (PENDING|RUNNING|COMPLETED|FAILED|CANCELLED)")
77
- .option("--limit <n>", "Max number of runs to return (default: 20)")
78
- .option("--cursor <cursor>", "Pagination cursor from a previous response")
79
- .option("--pretty", "Human-readable output")
84
+ .command('list')
85
+ .description('List your automation runs')
86
+ .option('--status <status>', 'Filter by status (PENDING|RUNNING|COMPLETED|FAILED|CANCELLED)')
87
+ .option('--limit <n>', 'Max number of runs to return (default: 20)')
88
+ .option('--cursor <cursor>', 'Pagination cursor from a previous response')
89
+ .option('--pretty', 'Human-readable output')
80
90
  .action(async (opts) => {
81
91
  // Validate --status
82
- if (opts.status != null && !VALID_STATUSES.includes(opts.status)) {
83
- err({ error: `Invalid --status "${opts.status}". Must be one of: ${VALID_STATUSES.join(", ")}` });
92
+ if (opts.status != null &&
93
+ !VALID_STATUSES.includes(opts.status)) {
94
+ err({
95
+ error: `Invalid --status "${opts.status}". Must be one of: ${VALID_STATUSES.join(', ')}`,
96
+ });
84
97
  process.exit(1);
85
98
  }
86
99
  // Validate --limit
@@ -107,11 +120,11 @@ export function registerRuns(runCmd) {
107
120
  const body = await listRuns(filter, apiKey);
108
121
  if (opts.pretty) {
109
122
  if (body.data.length === 0) {
110
- outLine("No runs found.");
123
+ outLine('No runs found.');
111
124
  return;
112
125
  }
113
- outLine(`${"RUN ID".padEnd(36)} ${"STATUS".padEnd(10)} ${"CREATED AT".padEnd(24)} GOAL`);
114
- outLine(`${"-".repeat(36)} ${"-".repeat(10)} ${"-".repeat(24)} ${"-".repeat(30)}`);
126
+ outLine(`${'RUN ID'.padEnd(36)} ${'STATUS'.padEnd(10)} ${'CREATED AT'.padEnd(24)} GOAL`);
127
+ outLine(`${'-'.repeat(36)} ${'-'.repeat(10)} ${'-'.repeat(24)} ${'-'.repeat(30)}`);
115
128
  for (const run of body.data) {
116
129
  outLine(`${run.run_id.padEnd(36)} ${run.status.padEnd(10)} ${run.created_at.padEnd(24)} ${run.goal}`);
117
130
  }
@@ -129,9 +142,9 @@ export function registerRuns(runCmd) {
129
142
  });
130
143
  // ── run get ────────────────────────────────────────────────────────────────
131
144
  runCmd
132
- .command("get <run_id>")
133
- .description("Get a run by ID")
134
- .option("--pretty", "Human-readable output")
145
+ .command('get <run_id>')
146
+ .description('Get a run by ID')
147
+ .option('--pretty', 'Human-readable output')
135
148
  .action(async (runId, opts) => {
136
149
  const apiKey = getApiKey();
137
150
  try {
@@ -161,16 +174,16 @@ export function registerRuns(runCmd) {
161
174
  }
162
175
  });
163
176
  runCmd
164
- .command("steps <run_id>")
165
- .description("Print the step-by-step trace for a run")
166
- .option("--pretty", "Human-readable output")
177
+ .command('steps <run_id>')
178
+ .description('Print the step-by-step trace for a run')
179
+ .option('--pretty', 'Human-readable output')
167
180
  .action(async (runId, opts) => {
168
181
  const apiKey = getApiKey();
169
182
  try {
170
183
  const result = await getRunSteps(runId, apiKey);
171
184
  if (opts.pretty) {
172
185
  if (result.steps.length === 0) {
173
- outLine("No steps found.");
186
+ outLine('No steps found.');
174
187
  return;
175
188
  }
176
189
  outLine(`Run ID: ${result.run_id}`);
@@ -188,21 +201,21 @@ export function registerRuns(runCmd) {
188
201
  }
189
202
  });
190
203
  runCmd
191
- .command("watch <run_id>")
192
- .description("Poll a run and print new steps as they arrive (exits when run reaches a terminal status)")
193
- .option("--pretty", "Human-readable output")
194
- .option("--timeout <ms>", `Stop polling after this many milliseconds (default: ${DEFAULT_WATCH_TIMEOUT_MS})`)
195
- .option("--interval <ms>", `Poll interval (default: ${DEFAULT_WATCH_INTERVAL_MS}, min: ${MIN_WATCH_INTERVAL_MS})`)
204
+ .command('watch <run_id>')
205
+ .description('Poll a run and print new steps as they arrive (exits when run reaches a terminal status)')
206
+ .option('--pretty', 'Human-readable output')
207
+ .option('--timeout <ms>', `Stop polling after this many milliseconds (default: ${DEFAULT_WATCH_TIMEOUT_MS})`)
208
+ .option('--interval <ms>', `Poll interval (default: ${DEFAULT_WATCH_INTERVAL_MS}, min: ${MIN_WATCH_INTERVAL_MS})`)
196
209
  .action(async (runId, opts) => {
197
210
  const apiKey = getApiKey();
198
- const timeoutMs = parsePositiveInt(opts.timeout, "--timeout", DEFAULT_WATCH_TIMEOUT_MS);
199
- const intervalMs = parsePositiveInt(opts.interval, "--interval", DEFAULT_WATCH_INTERVAL_MS, MIN_WATCH_INTERVAL_MS);
211
+ const timeoutMs = parsePositiveInt(opts.timeout, '--timeout', DEFAULT_WATCH_TIMEOUT_MS);
212
+ const intervalMs = parsePositiveInt(opts.interval, '--interval', DEFAULT_WATCH_INTERVAL_MS, MIN_WATCH_INTERVAL_MS);
200
213
  const controller = new AbortController();
201
214
  const onSigint = () => {
202
215
  controller.abort();
203
216
  process.exit(130);
204
217
  };
205
- process.once("SIGINT", onSigint);
218
+ process.once('SIGINT', onSigint);
206
219
  const deadline = Date.now() + timeoutMs;
207
220
  const seen = new Set();
208
221
  try {
@@ -248,24 +261,24 @@ export function registerRuns(runCmd) {
248
261
  }
249
262
  }
250
263
  catch (e) {
251
- if (e instanceof Error && e.message === "watch deadline exceeded") {
264
+ if (e instanceof Error && e.message === 'watch deadline exceeded') {
252
265
  err({ error: `Watch timed out after ${timeoutMs}ms.` });
253
266
  process.exit(1);
254
267
  }
255
- if (e instanceof Error && e.message === "watch aborted") {
268
+ if (e instanceof Error && e.message === 'watch aborted') {
256
269
  return; // SIGINT handler already triggered process.exit(130)
257
270
  }
258
271
  handleApiError(e);
259
272
  }
260
273
  finally {
261
- process.off("SIGINT", onSigint);
274
+ process.off('SIGINT', onSigint);
262
275
  }
263
276
  });
264
277
  // ── run cancel ────────────────────────────────────────────────────────────
265
278
  runCmd
266
- .command("cancel <run_id>")
267
- .description("Cancel a run by ID")
268
- .option("--pretty", "Human-readable output")
279
+ .command('cancel <run_id>')
280
+ .description('Cancel a run by ID')
281
+ .option('--pretty', 'Human-readable output')
269
282
  .action(async (runId, opts) => {
270
283
  const apiKey = getApiKey();
271
284
  try {
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerSearch(program: Command): void;
@@ -1,35 +1,35 @@
1
- import { getApiKey } from "../lib/auth.js";
2
- import { searchQuery } from "../lib/client.js";
3
- import { handleApiError, out, outLine } from "../lib/output.js";
1
+ import { getApiKey } from '../lib/auth.js';
2
+ import { searchQuery } from '../lib/client.js';
3
+ import { handleApiError, out, outLine } from '../lib/output.js';
4
4
  function printPrettySearch(response) {
5
5
  outLine(`Query: ${response.query}`);
6
6
  outLine(`Total results: ${response.total_results}`);
7
- outLine("");
7
+ outLine('');
8
8
  if (response.results.length === 0) {
9
- outLine("No results.");
9
+ outLine('No results.');
10
10
  return;
11
11
  }
12
12
  for (const result of response.results) {
13
13
  outLine(`${result.position}. ${result.title}`);
14
14
  outLine(` ${result.url}`);
15
15
  outLine(` ${result.snippet}`);
16
- outLine("");
16
+ outLine('');
17
17
  }
18
18
  }
19
19
  export function registerSearch(program) {
20
20
  const searchCmd = program
21
- .command("search")
22
- .description("Search commands")
21
+ .command('search')
22
+ .description('Search commands')
23
23
  .enablePositionalOptions();
24
24
  searchCmd
25
- .command("query")
26
- .description("Query TinyFish Search")
27
- .argument("<query>", "Search query")
28
- .option("--location <value>", "Search location hint")
29
- .option("--language <value>", "Search language hint")
30
- .option("--include-domains <value>", "Comma-separated list of domains to restrict results to")
31
- .option("--exclude-domains <value>", "Comma-separated list of domains to exclude from results")
32
- .option("--pretty", "Human-readable output")
25
+ .command('query')
26
+ .description('Query TinyFish Search')
27
+ .argument('<query>', 'Search query')
28
+ .option('--location <value>', 'Search location hint')
29
+ .option('--language <value>', 'Search language hint')
30
+ .option('--include-domains <value>', 'Comma-separated list of domains to restrict results to')
31
+ .option('--exclude-domains <value>', 'Comma-separated list of domains to exclude from results')
32
+ .option('--pretty', 'Human-readable output')
33
33
  .action(async (query, opts) => {
34
34
  const apiKey = getApiKey();
35
35
  try {
@@ -1,5 +1,5 @@
1
- import { Command } from "commander";
2
- import { type UpgradeScope } from "../lib/setup-telemetry.js";
1
+ import { Command } from 'commander';
2
+ import { type UpgradeScope } from '../lib/setup-telemetry.js';
3
3
  /** Returns the version now installed, so the caller reports exactly what the user was told. */
4
4
  export declare function runUpgrade(verbose?: boolean, scope?: UpgradeScope): string | null;
5
5
  /** `runUpgrade` plus its outcome telemetry, which must never change what the upgrade does. */