@tiny-fish/cli 0.1.0 → 0.1.2-next.15
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.js +3 -2
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.js +71 -29
- package/dist/commands/runs.d.ts +1 -1
- package/dist/commands/runs.js +33 -9
- package/dist/index.js +7 -2
- package/dist/lib/client.d.ts +2 -1
- package/dist/lib/client.js +11 -4
- package/dist/lib/output.d.ts +1 -0
- package/dist/lib/output.js +3 -0
- package/dist/lib/types.d.ts +15 -9
- package/package.json +6 -3
package/dist/commands/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import * as readline from "readline";
|
|
3
3
|
import { DASHBOARD_URL, clearConfig, loadConfig, maskKey, saveConfig, validateKeyFormat, } from "../lib/auth.js";
|
|
4
|
-
import { err, out, outLine } from "../lib/output.js";
|
|
4
|
+
import { err, errLine, out, outLine } from "../lib/output.js";
|
|
5
5
|
/**
|
|
6
6
|
* Read a key from stdin without echoing it to the terminal.
|
|
7
7
|
* Uses setRawMode on TTY so characters are never written to the screen.
|
|
@@ -71,7 +71,7 @@ export function registerAuth(program) {
|
|
|
71
71
|
.command("login")
|
|
72
72
|
.description("Open the API keys page and save your key interactively")
|
|
73
73
|
.action(async () => {
|
|
74
|
-
|
|
74
|
+
errLine(`Opening ${DASHBOARD_URL} in your browser...`);
|
|
75
75
|
try {
|
|
76
76
|
// Use spawn with an explicit args array — no shell, no interpolation
|
|
77
77
|
let cmd;
|
|
@@ -166,6 +166,7 @@ export function registerAuth(program) {
|
|
|
166
166
|
else {
|
|
167
167
|
out(result);
|
|
168
168
|
}
|
|
169
|
+
process.exit(1);
|
|
169
170
|
});
|
|
170
171
|
auth
|
|
171
172
|
.command("logout")
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
export declare function registerRun(
|
|
2
|
+
export declare function registerRun(agentCmd: Command): Command;
|
package/dist/commands/run.js
CHANGED
|
@@ -1,46 +1,64 @@
|
|
|
1
1
|
import { getApiKey } from "../lib/auth.js";
|
|
2
|
-
import { runAsync, runStream, runSync } from "../lib/client.js";
|
|
3
|
-
import { err, handleApiError, out, outLine } from "../lib/output.js";
|
|
2
|
+
import { cancelRun, runAsync, runStream, runSync } from "../lib/client.js";
|
|
3
|
+
import { err, errLine, handleApiError, out, outLine } from "../lib/output.js";
|
|
4
4
|
import { RunStatus, StreamEventType } from "../lib/types.js";
|
|
5
|
+
import { BASE_URL } from "../lib/constants.js";
|
|
5
6
|
const RUN_TIMEOUT_MS = 20 * 60 * 1000; // 20 minutes
|
|
6
7
|
function checkRunResult(result, expectComplete = true) {
|
|
7
8
|
if (result.error) {
|
|
8
|
-
err({ error: result.error,
|
|
9
|
+
err({ error: result.error, run_id: result.run_id, status: result.status });
|
|
9
10
|
process.exit(1);
|
|
10
11
|
}
|
|
11
12
|
if (expectComplete && result.status && result.status !== RunStatus.COMPLETED) {
|
|
12
|
-
err({ error: `Run ${result.status}`,
|
|
13
|
+
err({ error: `Run ${result.status}`, run_id: result.run_id, status: result.status });
|
|
13
14
|
process.exit(1);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
|
-
export function registerRun(
|
|
17
|
-
|
|
18
|
-
.command("run
|
|
19
|
-
.description("Run a browser automation")
|
|
20
|
-
.
|
|
17
|
+
export function registerRun(agentCmd) {
|
|
18
|
+
const runCmd = agentCmd
|
|
19
|
+
.command("run")
|
|
20
|
+
.description("Run a browser automation or manage runs")
|
|
21
|
+
.option("--url <url>", "Target URL for the automation")
|
|
21
22
|
.option("--sync", "Wait for result without streaming steps")
|
|
22
23
|
.option("--async", "Submit without waiting for result")
|
|
23
24
|
.option("--pretty", "Human-readable output")
|
|
25
|
+
.argument("[goal]", "Goal to automate")
|
|
24
26
|
.action(async (goal, opts) => {
|
|
27
|
+
// If no goal is provided and no subcommand matched, show help
|
|
28
|
+
if (!goal) {
|
|
29
|
+
runCmd.help();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!opts.url) {
|
|
33
|
+
err({ error: "required option '--url <url>' not specified" });
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
25
36
|
if (opts.sync && opts.async) {
|
|
26
37
|
err({ error: "--sync and --async are mutually exclusive" });
|
|
27
38
|
process.exit(1);
|
|
28
39
|
}
|
|
29
|
-
//
|
|
40
|
+
// Normalise URL: accept bare hostnames by prepending https://.
|
|
41
|
+
// The server requires a valid URL — it does not auto-prefix.
|
|
42
|
+
let normalizedUrl = opts.url;
|
|
30
43
|
try {
|
|
31
44
|
new URL(opts.url);
|
|
32
45
|
}
|
|
33
46
|
catch {
|
|
34
47
|
try {
|
|
35
48
|
new URL(`https://${opts.url}`);
|
|
49
|
+
normalizedUrl = `https://${opts.url}`;
|
|
36
50
|
}
|
|
37
51
|
catch {
|
|
38
|
-
err({ error:
|
|
52
|
+
err({ error: `Invalid --url "${opts.url}". Provide a valid URL, e.g. https://example.com or example.com` });
|
|
39
53
|
process.exit(1);
|
|
40
54
|
}
|
|
41
55
|
}
|
|
56
|
+
if (!goal.trim()) {
|
|
57
|
+
err({ error: 'Goal cannot be empty' });
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
42
60
|
const apiKey = getApiKey();
|
|
43
|
-
const req = { goal, url:
|
|
61
|
+
const req = { goal, url: normalizedUrl };
|
|
44
62
|
if (opts.async) {
|
|
45
63
|
let result;
|
|
46
64
|
try {
|
|
@@ -50,16 +68,18 @@ export function registerRun(program) {
|
|
|
50
68
|
handleApiError(e);
|
|
51
69
|
}
|
|
52
70
|
checkRunResult(result, false); // async = PENDING is expected
|
|
71
|
+
const { run_id: _asyncRunId, ...asyncRest } = result; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
72
|
+
const asyncResultWithUrl = { run_id: result.run_id, run_url: `${BASE_URL}/runs/${result.run_id}`, ...asyncRest };
|
|
53
73
|
if (opts.pretty) {
|
|
54
|
-
outLine(`Run submitted\nID: ${
|
|
74
|
+
outLine(`Run submitted\nID: ${asyncResultWithUrl.run_id}\nURL: ${asyncResultWithUrl.run_url}`);
|
|
55
75
|
}
|
|
56
76
|
else {
|
|
57
|
-
out(
|
|
77
|
+
out(asyncResultWithUrl);
|
|
58
78
|
}
|
|
59
79
|
return;
|
|
60
80
|
}
|
|
61
81
|
if (opts.sync) {
|
|
62
|
-
|
|
82
|
+
errLine("Waiting for result...");
|
|
63
83
|
let result;
|
|
64
84
|
try {
|
|
65
85
|
result = await runSync(req, apiKey, AbortSignal.timeout(RUN_TIMEOUT_MS));
|
|
@@ -68,17 +88,20 @@ export function registerRun(program) {
|
|
|
68
88
|
handleApiError(e);
|
|
69
89
|
}
|
|
70
90
|
checkRunResult(result);
|
|
91
|
+
const { run_id: _syncRunId, ...syncRest } = result; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
92
|
+
const syncResultWithUrl = { run_id: result.run_id, run_url: `${BASE_URL}/runs/${result.run_id}`, ...syncRest };
|
|
71
93
|
if (opts.pretty) {
|
|
72
|
-
outLine(`Status: ${
|
|
94
|
+
outLine(`Status: ${syncResultWithUrl.status}\n\n${JSON.stringify(syncResultWithUrl.result ?? {}, null, 2)}`);
|
|
73
95
|
}
|
|
74
96
|
else {
|
|
75
|
-
out(
|
|
97
|
+
out(syncResultWithUrl);
|
|
76
98
|
}
|
|
77
99
|
return;
|
|
78
100
|
}
|
|
79
101
|
// Default: SSE stream — 20 min timeout + Ctrl+C cancellation
|
|
80
102
|
const controller = new AbortController();
|
|
81
103
|
let siginted = false;
|
|
104
|
+
let capturedRunId = null;
|
|
82
105
|
const timeout = setTimeout(() => controller.abort(), RUN_TIMEOUT_MS);
|
|
83
106
|
const onSigint = () => {
|
|
84
107
|
siginted = true;
|
|
@@ -88,18 +111,26 @@ export function registerRun(program) {
|
|
|
88
111
|
let streamFailed = false;
|
|
89
112
|
try {
|
|
90
113
|
for await (const event of runStream(req, apiKey, controller.signal)) {
|
|
91
|
-
if (!handleStreamEvent(event, opts.pretty)) {
|
|
114
|
+
if (!handleStreamEvent(event, opts.pretty, (id) => { capturedRunId = id; })) {
|
|
92
115
|
streamFailed = true;
|
|
93
116
|
break;
|
|
94
117
|
}
|
|
95
118
|
}
|
|
96
119
|
}
|
|
97
120
|
catch (e) {
|
|
98
|
-
if (controller.signal.aborted) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
121
|
+
if (controller.signal.aborted && siginted) {
|
|
122
|
+
let cancelled = false;
|
|
123
|
+
if (capturedRunId) {
|
|
124
|
+
try {
|
|
125
|
+
await cancelRun(capturedRunId, apiKey);
|
|
126
|
+
cancelled = true;
|
|
127
|
+
}
|
|
128
|
+
catch { /* ignore */ }
|
|
102
129
|
}
|
|
130
|
+
process.stderr.write(cancelled ? "\nRun cancelled.\n" : "\n");
|
|
131
|
+
process.exit(130);
|
|
132
|
+
}
|
|
133
|
+
if (controller.signal.aborted) {
|
|
103
134
|
err({ error: "Run timed out after 20 minutes" });
|
|
104
135
|
process.exit(1);
|
|
105
136
|
}
|
|
@@ -112,34 +143,39 @@ export function registerRun(program) {
|
|
|
112
143
|
if (streamFailed)
|
|
113
144
|
process.exit(1);
|
|
114
145
|
});
|
|
146
|
+
return runCmd;
|
|
115
147
|
}
|
|
116
148
|
/**
|
|
117
149
|
* Handle one SSE event from the stream.
|
|
118
150
|
* Returns false on terminal error (caller should exit 1); true otherwise.
|
|
119
151
|
*/
|
|
120
|
-
function handleStreamEvent(event, pretty) {
|
|
152
|
+
function handleStreamEvent(event, pretty, onRunId) {
|
|
121
153
|
if (event.type === StreamEventType.ERROR) {
|
|
122
|
-
err({ error: event.error,
|
|
154
|
+
err({ error: event.error, run_id: event.run_id, status: StreamEventType.ERROR });
|
|
123
155
|
return false;
|
|
124
156
|
}
|
|
125
157
|
if (event.type === StreamEventType.COMPLETE && event.status !== RunStatus.COMPLETED) {
|
|
126
|
-
err({ error: event.error ?? `Run ${event.status}`,
|
|
158
|
+
err({ error: event.error ?? `Run ${event.status}`, run_id: event.run_id, status: event.status });
|
|
127
159
|
return false;
|
|
128
160
|
}
|
|
129
161
|
if (pretty) {
|
|
130
162
|
switch (event.type) {
|
|
131
163
|
case StreamEventType.STARTED:
|
|
164
|
+
if (onRunId && event.run_id)
|
|
165
|
+
onRunId(event.run_id);
|
|
132
166
|
outLine(`▶ Run started`);
|
|
133
167
|
break;
|
|
134
168
|
case StreamEventType.STREAMING_URL:
|
|
135
|
-
outLine(`🔗 Live view: ${event.
|
|
169
|
+
outLine(`🔗 Live view: ${event.streaming_url}`);
|
|
136
170
|
break;
|
|
137
171
|
case StreamEventType.PROGRESS:
|
|
138
172
|
outLine(`• ${event.purpose}`);
|
|
139
173
|
break;
|
|
140
|
-
case StreamEventType.COMPLETE:
|
|
141
|
-
|
|
174
|
+
case StreamEventType.COMPLETE: {
|
|
175
|
+
const runUrl = `${BASE_URL}/runs/${event.run_id}`;
|
|
176
|
+
outLine(`✓ Completed\n\n${JSON.stringify(event.result ?? {}, null, 2)}\n\nView run: ${runUrl}`);
|
|
142
177
|
break;
|
|
178
|
+
}
|
|
143
179
|
case StreamEventType.HEARTBEAT:
|
|
144
180
|
// silently skip — keep-alive noise
|
|
145
181
|
break;
|
|
@@ -147,7 +183,13 @@ function handleStreamEvent(event, pretty) {
|
|
|
147
183
|
}
|
|
148
184
|
else {
|
|
149
185
|
// Raw JSON: emit all events (STREAMING_URL and HEARTBEAT are useful for agents)
|
|
150
|
-
|
|
186
|
+
if (event.type === StreamEventType.COMPLETE) {
|
|
187
|
+
const { run_id: _eventRunId, ...eventRest } = event; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
188
|
+
out({ run_id: event.run_id, run_url: `${BASE_URL}/runs/${event.run_id}`, ...eventRest });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
out(event);
|
|
192
|
+
}
|
|
151
193
|
}
|
|
152
194
|
return true;
|
|
153
195
|
}
|
package/dist/commands/runs.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
export declare function registerRuns(
|
|
2
|
+
export declare function registerRuns(runCmd: Command): void;
|
package/dist/commands/runs.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { getApiKey } from "../lib/auth.js";
|
|
2
|
-
import { getRun, listRuns } from "../lib/client.js";
|
|
2
|
+
import { cancelRun, getRun, listRuns } from "../lib/client.js";
|
|
3
3
|
import { err, handleApiError, out, outLine } from "../lib/output.js";
|
|
4
4
|
import { RunStatus } from "../lib/types.js";
|
|
5
5
|
const VALID_STATUSES = Object.values(RunStatus);
|
|
6
|
-
export function registerRuns(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
.description("Manage past automation runs");
|
|
10
|
-
// ── runs list ──────────────────────────────────────────────────────────────
|
|
11
|
-
runs
|
|
6
|
+
export function registerRuns(runCmd) {
|
|
7
|
+
// ── run list ───────────────────────────────────────────────────────────────
|
|
8
|
+
runCmd
|
|
12
9
|
.command("list")
|
|
13
10
|
.description("List your automation runs")
|
|
14
11
|
.option("--status <status>", "Filter by status (PENDING|RUNNING|COMPLETED|FAILED|CANCELLED)")
|
|
@@ -65,8 +62,8 @@ export function registerRuns(program) {
|
|
|
65
62
|
handleApiError(e);
|
|
66
63
|
}
|
|
67
64
|
});
|
|
68
|
-
// ──
|
|
69
|
-
|
|
65
|
+
// ── run get ────────────────────────────────────────────────────────────────
|
|
66
|
+
runCmd
|
|
70
67
|
.command("get <run_id>")
|
|
71
68
|
.description("Get a run by ID")
|
|
72
69
|
.option("--pretty", "Human-readable output")
|
|
@@ -98,4 +95,31 @@ export function registerRuns(program) {
|
|
|
98
95
|
handleApiError(e);
|
|
99
96
|
}
|
|
100
97
|
});
|
|
98
|
+
// ── run cancel ────────────────────────────────────────────────────────────
|
|
99
|
+
runCmd
|
|
100
|
+
.command("cancel <run_id>")
|
|
101
|
+
.description("Cancel a run by ID")
|
|
102
|
+
.option("--pretty", "Human-readable output")
|
|
103
|
+
.action(async (runId, opts) => {
|
|
104
|
+
const apiKey = getApiKey();
|
|
105
|
+
try {
|
|
106
|
+
const result = await cancelRun(runId, apiKey);
|
|
107
|
+
if (opts.pretty) {
|
|
108
|
+
if (result.status === RunStatus.CANCELLED) {
|
|
109
|
+
outLine(result.message
|
|
110
|
+
? `Run ${result.run_id} cancelled (${result.message}).`
|
|
111
|
+
: `Run ${result.run_id} cancelled.`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
outLine(`Run ${result.run_id} already finished (${result.status}).`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
out(result);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
handleApiError(e);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
101
125
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ program
|
|
|
12
12
|
.version(version, "-V, --version", "Show version")
|
|
13
13
|
.helpOption("-h, --help", "Show help")
|
|
14
14
|
.addHelpCommand(false)
|
|
15
|
+
.enablePositionalOptions()
|
|
15
16
|
.option("--debug", "Print HTTP requests and responses to stderr (or set TINYFISH_DEBUG=1)")
|
|
16
17
|
.hook("preAction", (cmd) => {
|
|
17
18
|
if (cmd.opts().debug) {
|
|
@@ -19,8 +20,12 @@ program
|
|
|
19
20
|
}
|
|
20
21
|
});
|
|
21
22
|
registerAuth(program);
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const agentCmd = program
|
|
24
|
+
.command("agent")
|
|
25
|
+
.description("Agent automation commands")
|
|
26
|
+
.enablePositionalOptions();
|
|
27
|
+
const runCmd = registerRun(agentCmd);
|
|
28
|
+
registerRuns(runCmd);
|
|
24
29
|
// Await parseAsync so async command handlers complete before the process exits
|
|
25
30
|
program.parseAsync(process.argv).catch((e) => {
|
|
26
31
|
process.stderr.write(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }) + "\n");
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ListRunsOptions, ListRunsResponse, RunApiResponse, RunRequest, RunResult, StreamEvent } from "./types.js";
|
|
1
|
+
import type { CancelRunResponse, ListRunsOptions, ListRunsResponse, RunApiResponse, RunRequest, RunResult, StreamEvent } from "./types.js";
|
|
2
2
|
export declare function runSync(req: RunRequest, apiKey: string, signal?: AbortSignal): Promise<RunResult>;
|
|
3
3
|
export declare function runAsync(req: RunRequest, apiKey: string): Promise<RunResult>;
|
|
4
4
|
export declare function runStream(req: RunRequest, apiKey: string, signal?: AbortSignal): AsyncGenerator<StreamEvent>;
|
|
5
5
|
export declare function listRuns(opts: ListRunsOptions, apiKey: string): Promise<ListRunsResponse>;
|
|
6
6
|
export declare function getRun(runId: string, apiKey: string): Promise<RunApiResponse>;
|
|
7
|
+
export declare function cancelRun(runId: string, apiKey: string): Promise<CancelRunResponse>;
|
package/dist/lib/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BASE_URL } from "./constants.js";
|
|
2
2
|
import { ApiError } from "./output.js";
|
|
3
|
+
const API_TIMEOUT_MS = 30_000;
|
|
3
4
|
// ── Debug logging ─────────────────────────────────────────────────────────────
|
|
4
5
|
const DEBUG_ENABLED = /^(1|true)$/i.test(process.env["TINYFISH_DEBUG"] ?? "");
|
|
5
6
|
function debugLog(msg) {
|
|
@@ -18,7 +19,9 @@ function headers(apiKey) {
|
|
|
18
19
|
async function throwIfError(res) {
|
|
19
20
|
if (!res.ok) {
|
|
20
21
|
const json = await res.json().catch(() => ({}));
|
|
21
|
-
const message = json.
|
|
22
|
+
const message = json.error?.message ??
|
|
23
|
+
json.detail ??
|
|
24
|
+
`HTTP ${res.status}`;
|
|
22
25
|
throw new ApiError(res.status, message);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
@@ -71,7 +74,7 @@ export async function runSync(req, apiKey, signal) {
|
|
|
71
74
|
}
|
|
72
75
|
export async function runAsync(req, apiKey) {
|
|
73
76
|
// Async just submits the job — 30 s is plenty to get an ACK
|
|
74
|
-
const res = await postJson("/v1/automation/run-async", req, apiKey, AbortSignal.timeout(
|
|
77
|
+
const res = await postJson("/v1/automation/run-async", req, apiKey, AbortSignal.timeout(API_TIMEOUT_MS));
|
|
75
78
|
return res.json();
|
|
76
79
|
}
|
|
77
80
|
export async function* runStream(req, apiKey, signal) {
|
|
@@ -109,8 +112,12 @@ export async function listRuns(opts, apiKey) {
|
|
|
109
112
|
if (opts.cursor)
|
|
110
113
|
params.set("cursor", opts.cursor);
|
|
111
114
|
const qs = params.size ? `?${params}` : "";
|
|
112
|
-
return getJson(`/v1/runs${qs}`, apiKey, AbortSignal.timeout(
|
|
115
|
+
return getJson(`/v1/runs${qs}`, apiKey, AbortSignal.timeout(API_TIMEOUT_MS));
|
|
113
116
|
}
|
|
114
117
|
export async function getRun(runId, apiKey) {
|
|
115
|
-
return getJson(`/v1/runs/${encodeURIComponent(runId)}`, apiKey, AbortSignal.timeout(
|
|
118
|
+
return getJson(`/v1/runs/${encodeURIComponent(runId)}`, apiKey, AbortSignal.timeout(API_TIMEOUT_MS));
|
|
119
|
+
}
|
|
120
|
+
export async function cancelRun(runId, apiKey) {
|
|
121
|
+
const res = await postJson(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {}, apiKey, AbortSignal.timeout(API_TIMEOUT_MS));
|
|
122
|
+
return res.json();
|
|
116
123
|
}
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export declare class ApiError extends Error {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function out(data: unknown): void;
|
|
7
7
|
export declare function outLine(line: string): void;
|
|
8
|
+
export declare function errLine(line: string): void;
|
|
8
9
|
export declare function err(data: unknown): void;
|
|
9
10
|
export declare function handleApiError(e: unknown): never;
|
package/dist/lib/output.js
CHANGED
|
@@ -14,6 +14,9 @@ export function out(data) {
|
|
|
14
14
|
export function outLine(line) {
|
|
15
15
|
process.stdout.write(line + "\n");
|
|
16
16
|
}
|
|
17
|
+
export function errLine(line) {
|
|
18
|
+
process.stderr.write(line + "\n");
|
|
19
|
+
}
|
|
17
20
|
export function err(data) {
|
|
18
21
|
const payload = typeof data === "string" ? { error: data } : data;
|
|
19
22
|
process.stderr.write(JSON.stringify(payload) + "\n");
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -20,41 +20,41 @@ export declare const StreamEventType: {
|
|
|
20
20
|
};
|
|
21
21
|
export type StreamEventType = (typeof StreamEventType)[keyof typeof StreamEventType];
|
|
22
22
|
export interface RunResult {
|
|
23
|
-
|
|
23
|
+
run_id: string;
|
|
24
24
|
status: RunStatus;
|
|
25
|
-
|
|
25
|
+
result?: unknown;
|
|
26
26
|
error?: string;
|
|
27
27
|
}
|
|
28
28
|
/** Discriminated union of all real SSE event shapes from the API */
|
|
29
29
|
export type StreamEvent = {
|
|
30
30
|
type: typeof StreamEventType.STARTED;
|
|
31
|
-
|
|
31
|
+
run_id: string;
|
|
32
32
|
timestamp: string;
|
|
33
33
|
} | {
|
|
34
34
|
type: typeof StreamEventType.STREAMING_URL;
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
run_id: string;
|
|
36
|
+
streaming_url: string;
|
|
37
37
|
timestamp: string;
|
|
38
38
|
} | {
|
|
39
39
|
type: typeof StreamEventType.HEARTBEAT;
|
|
40
40
|
timestamp: string;
|
|
41
41
|
} | {
|
|
42
42
|
type: typeof StreamEventType.PROGRESS;
|
|
43
|
-
|
|
43
|
+
run_id: string;
|
|
44
44
|
purpose: string;
|
|
45
45
|
timestamp: string;
|
|
46
46
|
} | {
|
|
47
47
|
type: typeof StreamEventType.COMPLETE;
|
|
48
|
-
|
|
48
|
+
run_id: string;
|
|
49
49
|
status: RunStatus;
|
|
50
50
|
timestamp: string;
|
|
51
|
-
|
|
51
|
+
result?: unknown;
|
|
52
52
|
error?: string;
|
|
53
53
|
help_url?: string;
|
|
54
54
|
help_message?: string;
|
|
55
55
|
} | {
|
|
56
56
|
type: typeof StreamEventType.ERROR;
|
|
57
|
-
|
|
57
|
+
run_id?: string;
|
|
58
58
|
error: string;
|
|
59
59
|
timestamp: string;
|
|
60
60
|
};
|
|
@@ -94,3 +94,9 @@ export interface ListRunsOptions {
|
|
|
94
94
|
limit?: number;
|
|
95
95
|
cursor?: string;
|
|
96
96
|
}
|
|
97
|
+
export interface CancelRunResponse {
|
|
98
|
+
run_id: string;
|
|
99
|
+
status: RunStatus | string;
|
|
100
|
+
cancelled_at: string;
|
|
101
|
+
message: string | null;
|
|
102
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiny-fish/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2-next.15",
|
|
4
4
|
"description": "TinyFish CLI — run web automations from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsc",
|
|
16
|
-
"prepack": "npm run build",
|
|
17
16
|
"test": "vitest --run",
|
|
18
17
|
"test:file": "vitest --run",
|
|
19
18
|
"test:watch": "vitest",
|
|
20
19
|
"test:integration": "vitest --run --config vitest.integration.config.ts",
|
|
21
20
|
"lint": "eslint src tests",
|
|
22
21
|
"format": "prettier --write src tests",
|
|
23
|
-
"type-check": "tsc --noEmit --project tsconfig.all.json"
|
|
22
|
+
"type-check": "tsc --noEmit --project tsconfig.all.json",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"commander": "^12.0.0",
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=24.0.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"registry": "https://registry.npmjs.org/"
|
|
40
43
|
}
|
|
41
44
|
}
|