@tiny-fish/cli 0.30.2-next.257 → 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.
- package/dist/commands/auth.d.ts +1 -1
- package/dist/commands/auth.js +52 -52
- package/dist/commands/batch.d.ts +1 -1
- package/dist/commands/batch.js +54 -46
- package/dist/commands/browser.d.ts +1 -1
- package/dist/commands/browser.js +14 -14
- package/dist/commands/config-claude.d.ts +1 -1
- package/dist/commands/config-claude.js +22 -22
- package/dist/commands/connect.d.ts +3 -3
- package/dist/commands/connect.js +150 -150
- package/dist/commands/doctor.d.ts +4 -4
- package/dist/commands/doctor.js +104 -104
- package/dist/commands/fetch.d.ts +1 -1
- package/dist/commands/fetch.js +24 -27
- package/dist/commands/onboard.d.ts +1 -1
- package/dist/commands/onboard.js +50 -50
- package/dist/commands/profile.d.ts +3 -3
- package/dist/commands/profile.js +45 -45
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.js +78 -65
- package/dist/commands/runs.d.ts +1 -1
- package/dist/commands/runs.js +63 -50
- package/dist/commands/search.d.ts +1 -1
- package/dist/commands/search.js +16 -16
- package/dist/commands/upgrade.d.ts +2 -2
- package/dist/commands/upgrade.js +36 -36
- package/dist/commands/vault.d.ts +1 -1
- package/dist/commands/vault.js +48 -45
- package/dist/commands/wallet.d.ts +1 -1
- package/dist/commands/wallet.js +14 -14
- package/dist/index.js +2 -2
- package/dist/lib/auth.d.ts +5 -5
- package/dist/lib/auth.js +56 -56
- package/dist/lib/batch-store.d.ts +1 -1
- package/dist/lib/batch-store.js +4 -4
- package/dist/lib/chrome-cookies.d.ts +1 -1
- package/dist/lib/chrome-cookies.js +24 -24
- package/dist/lib/claude-config.js +37 -37
- package/dist/lib/cli-install.js +12 -12
- package/dist/lib/client.d.ts +2 -2
- package/dist/lib/client.js +61 -61
- package/dist/lib/connect-all.js +140 -134
- package/dist/lib/connect-auth.d.ts +2 -2
- package/dist/lib/connect-auth.js +15 -15
- package/dist/lib/connect-clients.d.ts +6 -6
- package/dist/lib/connect-clients.js +143 -143
- package/dist/lib/connect-fallback.d.ts +1 -1
- package/dist/lib/connect-fallback.js +38 -38
- package/dist/lib/connect-picker.d.ts +4 -4
- package/dist/lib/connect-picker.js +28 -28
- package/dist/lib/connect-runtime.d.ts +7 -7
- package/dist/lib/connect-runtime.js +61 -61
- package/dist/lib/constants.js +13 -13
- package/dist/lib/cursor-config.d.ts +1 -1
- package/dist/lib/cursor-config.js +30 -30
- package/dist/lib/doctor-report.d.ts +2 -2
- package/dist/lib/doctor-report.js +22 -22
- package/dist/lib/harness-detect.js +39 -39
- package/dist/lib/harness.js +9 -9
- package/dist/lib/hermes-env.js +26 -23
- package/dist/lib/install-root.js +12 -12
- package/dist/lib/notice.d.ts +1 -1
- package/dist/lib/notice.js +12 -12
- package/dist/lib/output.js +14 -14
- package/dist/lib/registration-detect.d.ts +3 -3
- package/dist/lib/registration-detect.js +82 -82
- package/dist/lib/setup-telemetry.d.ts +4 -4
- package/dist/lib/setup-telemetry.js +34 -34
- package/dist/lib/signals.js +1 -1
- package/dist/lib/skill-install.d.ts +3 -3
- package/dist/lib/skill-install.js +40 -40
- package/dist/lib/types.d.ts +8 -8
- package/dist/lib/verify.d.ts +1 -1
- package/dist/lib/verify.js +28 -28
- package/dist/program.d.ts +1 -1
- package/dist/program.js +28 -28
- package/package.json +1 -1
package/dist/commands/run.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { EventType, RunStatus } from
|
|
2
|
-
import { readFile } from
|
|
3
|
-
import { z } from
|
|
4
|
-
import { getApiKey } from
|
|
5
|
-
import { cancelRun, runAsync, runStream, runSync } from
|
|
6
|
-
import { err, errLine, handleApiError, out, outLine } from
|
|
7
|
-
import { BASE_URL } from
|
|
8
|
-
const VALID_BROWSER_PROFILES = [
|
|
9
|
-
const VALID_MODES = [
|
|
10
|
-
const VALID_CURSOR_STYLES = [
|
|
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 ===
|
|
24
|
+
if (typeof error === 'string')
|
|
25
25
|
return error;
|
|
26
|
-
if (typeof error ===
|
|
26
|
+
if (typeof error === 'object' && error !== null) {
|
|
27
27
|
const maybeError = error;
|
|
28
|
-
if (typeof maybeError.message ===
|
|
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:
|
|
40
|
+
status: 'status' in result ? result.status : undefined,
|
|
41
41
|
});
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
|
-
if (expectComplete &&
|
|
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 !==
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
125
|
+
return parseOutputSchema(fileContents, '--output-schema-file');
|
|
123
126
|
}
|
|
124
127
|
export function registerRun(agentCmd) {
|
|
125
128
|
const runCmd = agentCmd
|
|
126
|
-
.command(
|
|
127
|
-
.description(
|
|
128
|
-
.option(
|
|
129
|
-
.option(
|
|
130
|
-
.option(
|
|
131
|
-
.option(
|
|
132
|
-
.option(
|
|
133
|
-
.option(
|
|
134
|
-
.option(
|
|
135
|
-
.option(
|
|
136
|
-
.option(
|
|
137
|
-
.option(
|
|
138
|
-
.option(
|
|
139
|
-
.option(
|
|
140
|
-
.option(
|
|
141
|
-
.option(
|
|
142
|
-
.option(
|
|
143
|
-
.option(
|
|
144
|
-
.option(
|
|
145
|
-
.option(
|
|
146
|
-
.option(
|
|
147
|
-
.option(
|
|
148
|
-
.option(
|
|
149
|
-
.option(
|
|
150
|
-
.option(
|
|
151
|
-
.argument(
|
|
152
|
-
.addHelpText(
|
|
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:
|
|
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({
|
|
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:
|
|
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:
|
|
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,
|
|
200
|
-
const mode = parseChoice(opts.mode, VALID_MODES,
|
|
201
|
-
const cursorStyle = parseChoice(opts.cursorStyle, VALID_CURSOR_STYLES,
|
|
202
|
-
const maxSteps = parsePositiveInteger(opts.maxSteps,
|
|
203
|
-
const maxDurationSeconds = parsePositiveInteger(opts.maxDurationSeconds,
|
|
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,
|
|
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(
|
|
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(
|
|
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) => {
|
|
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 {
|
|
329
|
+
catch {
|
|
330
|
+
/* ignore */
|
|
331
|
+
}
|
|
323
332
|
}
|
|
324
|
-
process.stderr.write(cancelled ?
|
|
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(
|
|
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({
|
|
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) {
|
package/dist/commands/runs.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerRuns(runCmd: Command): void;
|
package/dist/commands/runs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RunStatus } from
|
|
2
|
-
import { getApiKey } from
|
|
3
|
-
import { cancelRun, getRun, getRunSteps, listRuns } from
|
|
4
|
-
import { err, handleApiError, out, outLine } from
|
|
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 ??
|
|
16
|
-
const meta = [step.status, step.duration].filter(Boolean).join(
|
|
17
|
-
return `${String(index + 1).padStart(2,
|
|
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({
|
|
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(
|
|
40
|
+
reject(new Error('watch deadline exceeded'));
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
43
|
if (signal.aborted) {
|
|
42
|
-
reject(new Error(
|
|
44
|
+
reject(new Error('watch aborted'));
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
|
-
const timer = setTimeout(() => reject(new Error(
|
|
47
|
+
const timer = setTimeout(() => reject(new Error('watch deadline exceeded')), remaining);
|
|
46
48
|
const onAbort = () => {
|
|
47
49
|
clearTimeout(timer);
|
|
48
|
-
reject(new Error(
|
|
50
|
+
reject(new Error('watch aborted'));
|
|
49
51
|
};
|
|
50
|
-
signal.addEventListener(
|
|
51
|
-
promise.then((v) => {
|
|
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(
|
|
67
|
+
reject(new Error('aborted'));
|
|
58
68
|
return;
|
|
59
69
|
}
|
|
60
70
|
const timer = setTimeout(() => {
|
|
61
|
-
signal.removeEventListener(
|
|
71
|
+
signal.removeEventListener('abort', onAbort);
|
|
62
72
|
resolve();
|
|
63
73
|
}, ms);
|
|
64
74
|
const onAbort = () => {
|
|
65
75
|
clearTimeout(timer);
|
|
66
|
-
reject(new Error(
|
|
76
|
+
reject(new Error('aborted'));
|
|
67
77
|
};
|
|
68
|
-
signal.addEventListener(
|
|
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(
|
|
75
|
-
.description(
|
|
76
|
-
.option(
|
|
77
|
-
.option(
|
|
78
|
-
.option(
|
|
79
|
-
.option(
|
|
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 &&
|
|
83
|
-
|
|
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(
|
|
123
|
+
outLine('No runs found.');
|
|
111
124
|
return;
|
|
112
125
|
}
|
|
113
|
-
outLine(`${
|
|
114
|
-
outLine(`${
|
|
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(
|
|
133
|
-
.description(
|
|
134
|
-
.option(
|
|
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(
|
|
165
|
-
.description(
|
|
166
|
-
.option(
|
|
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(
|
|
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(
|
|
192
|
-
.description(
|
|
193
|
-
.option(
|
|
194
|
-
.option(
|
|
195
|
-
.option(
|
|
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,
|
|
199
|
-
const intervalMs = parsePositiveInt(opts.interval,
|
|
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(
|
|
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 ===
|
|
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 ===
|
|
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(
|
|
274
|
+
process.off('SIGINT', onSigint);
|
|
262
275
|
}
|
|
263
276
|
});
|
|
264
277
|
// ── run cancel ────────────────────────────────────────────────────────────
|
|
265
278
|
runCmd
|
|
266
|
-
.command(
|
|
267
|
-
.description(
|
|
268
|
-
.option(
|
|
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
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerSearch(program: Command): void;
|
package/dist/commands/search.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { getApiKey } from
|
|
2
|
-
import { searchQuery } from
|
|
3
|
-
import { handleApiError, out, outLine } from
|
|
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(
|
|
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(
|
|
22
|
-
.description(
|
|
21
|
+
.command('search')
|
|
22
|
+
.description('Search commands')
|
|
23
23
|
.enablePositionalOptions();
|
|
24
24
|
searchCmd
|
|
25
|
-
.command(
|
|
26
|
-
.description(
|
|
27
|
-
.argument(
|
|
28
|
-
.option(
|
|
29
|
-
.option(
|
|
30
|
-
.option(
|
|
31
|
-
.option(
|
|
32
|
-
.option(
|
|
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
|
|
2
|
-
import { type UpgradeScope } from
|
|
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. */
|