@trawlme/cli 3.12.0 → 3.12.1
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/README.md +1 -1
- package/dist/commands/create.d.ts +0 -28
- package/dist/commands/create.js +0 -89
- package/dist/commands/doctor.d.ts +0 -79
- package/dist/commands/doctor.js +1 -187
- package/dist/commands/login.js +0 -67
- package/dist/commands/ping.d.ts +0 -15
- package/dist/commands/ping.js +0 -15
- package/dist/commands/scraps.d.ts +0 -120
- package/dist/commands/scraps.js +10 -724
- package/dist/commands/skills.js +0 -22
- package/dist/commands/spec.d.ts +0 -85
- package/dist/commands/spec.js +0 -67
- package/dist/commands/telemetry.js +0 -4
- package/dist/commands/token.js +0 -28
- package/dist/commands/upgrade.js +0 -22
- package/dist/commands/whoami.d.ts +0 -12
- package/dist/commands/whoami.js +0 -6
- package/dist/index.d.ts +0 -188
- package/dist/index.js +0 -349
- package/dist/lib/api.d.ts +0 -78
- package/dist/lib/api.js +1 -320
- package/dist/lib/cdp-pipe.d.ts +0 -72
- package/dist/lib/cdp-pipe.js +1 -81
- package/dist/lib/chrome-discovery.d.ts +0 -11
- package/dist/lib/chrome-discovery.js +0 -19
- package/dist/lib/chrome-launch.d.ts +0 -40
- package/dist/lib/chrome-launch.js +0 -69
- package/dist/lib/config.d.ts +0 -53
- package/dist/lib/config.js +0 -55
- package/dist/lib/confirm.d.ts +0 -55
- package/dist/lib/confirm.js +0 -47
- package/dist/lib/docs.d.ts +0 -123
- package/dist/lib/docs.js +0 -169
- package/dist/lib/errors.d.ts +0 -134
- package/dist/lib/errors.js +0 -151
- package/dist/lib/format.d.ts +0 -6
- package/dist/lib/format.js +0 -6
- package/dist/lib/json.d.ts +0 -35
- package/dist/lib/json.js +0 -48
- package/dist/lib/jwt.d.ts +0 -7
- package/dist/lib/jwt.js +0 -7
- package/dist/lib/pinch.d.ts +0 -53
- package/dist/lib/pinch.js +6 -112
- package/dist/lib/pinchAnimation.d.ts +0 -16
- package/dist/lib/pinchAnimation.js +8 -29
- package/dist/lib/posthog.d.ts +0 -9
- package/dist/lib/posthog.js +0 -23
- package/dist/lib/prompt.js +1 -20
- package/dist/lib/secure-transport.d.ts +0 -7
- package/dist/lib/secure-transport.js +0 -24
- package/dist/lib/session-capture-guard.d.ts +0 -15
- package/dist/lib/session-capture-guard.js +0 -5
- package/dist/lib/session-capture.d.ts +0 -125
- package/dist/lib/session-capture.js +0 -281
- package/dist/lib/skills.d.ts +0 -175
- package/dist/lib/skills.js +1 -216
- package/dist/lib/skillsNudge.d.ts +0 -17
- package/dist/lib/skillsNudge.js +0 -83
- package/dist/lib/spinner.d.ts +0 -39
- package/dist/lib/spinner.js +0 -40
- package/dist/lib/storage-state.d.ts +0 -112
- package/dist/lib/storage-state.js +0 -131
- package/dist/lib/tips.d.ts +0 -38
- package/dist/lib/tips.js +0 -77
- package/dist/lib/updateCheckWorker.js +0 -14
- package/dist/lib/updateNotifier.d.ts +0 -17
- package/dist/lib/updateNotifier.js +0 -53
- package/dist/lib/validate.d.ts +0 -8
- package/dist/lib/validate.js +0 -8
- package/dist/lib/version.d.ts +0 -12
- package/dist/lib/version.js +1 -13
- package/package.json +2 -2
package/dist/commands/login.js
CHANGED
|
@@ -28,60 +28,11 @@ async function promptEmail() {
|
|
|
28
28
|
rl.close();
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
/** #107 — the shared success-reporting tail for all three login paths (env
|
|
32
|
-
* token, --token flag, email/password). Under --json, prints a single
|
|
33
|
-
* structured result on stdout instead of the chalk lines — never the raw
|
|
34
|
-
* token itself (that's what `trawl token` is for). */
|
|
35
31
|
function reportLoginSuccess(opts, email) {
|
|
36
|
-
// #169 review finding 3 — stderr, both under --json and plain output: this
|
|
37
|
-
// is a warning about what happens AFTER login succeeds, not part of either
|
|
38
|
-
// payload shape, so it must never be silently dropped just because --json
|
|
39
|
-
// is set.
|
|
40
32
|
const overrideVar = getLiveAuthEnvVar();
|
|
41
33
|
if (overrideVar) {
|
|
42
34
|
console.error(chalk.yellow(`⚠ ${overrideVar} is set in your environment — it overrides the token just stored here for every subsequent command until you unset it.`));
|
|
43
35
|
}
|
|
44
|
-
// #184 — `login` is the ONE intentional human setup moment: bootstrap any
|
|
45
|
-
// bundled Claude skill the user doesn't have yet. Gated exactly like
|
|
46
|
-
// every other filesystem-mutating side effect in this CLI
|
|
47
|
-
// (json/TTY/opt-out — see lib/skills.ts's isSkillsActionAllowed), so a
|
|
48
|
-
// CI/agent `TRAWL_TOKEN=x trawl login --json` never writes into
|
|
49
|
-
// ~/.claude/skills on a machine with no Claude Code session to discover
|
|
50
|
-
// them. `isInteractive` is the exact TTY definition login already uses
|
|
51
|
-
// for its own email/password prompt gate (lib/confirm.ts) — stdin AND
|
|
52
|
-
// stdout, not just stdout. `bootstrapped` is `null` ONLY when gated out
|
|
53
|
-
// (json/non-TTY/opted-out) or when every bundled skill is already owned
|
|
54
|
-
// by trawl — the genuine "nothing to do" case, where the lines below
|
|
55
|
-
// naturally never print. It is non-null whenever anything was attempted,
|
|
56
|
-
// whether or not any of it actually landed — see the block below.
|
|
57
|
-
//
|
|
58
|
-
// stderr, same convention as the `overrideVar` warning right above (and
|
|
59
|
-
// for the same reason): this is orthogonal to the `--json` envelope's
|
|
60
|
-
// fixed shape below, so it must never risk landing on stdout — a defense
|
|
61
|
-
// that holds even if `bootstrapSkillsOnLogin`'s own json/TTY gate above it
|
|
62
|
-
// were ever wrong, not just a style match.
|
|
63
|
-
//
|
|
64
|
-
// #184 review (BLOCK + MAJOR) — `bootstrapped` is non-null whenever there
|
|
65
|
-
// was anything to attempt, whether or not any of it actually landed, so
|
|
66
|
-
// both halves below must be checked independently: `installed` prints the
|
|
67
|
-
// success line, `skipped` prints one honest line per skill that was
|
|
68
|
-
// requested but did NOT land (a permission error, or a pre-existing
|
|
69
|
-
// marker-less dir the ownership guard refused to overwrite) and WHY —
|
|
70
|
-
// `reason` is already a relayable, fact-only string (SkillOwnershipRefusalError's
|
|
71
|
-
// `.relayableReason`, or a raw fs error message) with no imperative, so it
|
|
72
|
-
// is safe to print verbatim on this channel. A total failure (installed
|
|
73
|
-
// empty, skipped non-empty) must read as "attempted and failed" — never
|
|
74
|
-
// fall through to silence, which is indistinguishable from "never
|
|
75
|
-
// attempted" (the exact false-success shape this issue exists to
|
|
76
|
-
// prevent). The restart note only applies to what actually landed, so it
|
|
77
|
-
// stays scoped to that branch.
|
|
78
|
-
//
|
|
79
|
-
// #184 defect 2 — `error` is the THIRD case: distinct from both "nothing
|
|
80
|
-
// to do" (bootstrapped is `null`, nothing prints) and "some/all skills
|
|
81
|
-
// failed" (`skipped`, above) — it means bootstrapSkillsOnLogin could not
|
|
82
|
-
// even determine which skills to install (the bundled skills package
|
|
83
|
-
// looks missing/corrupted). Stated as a fact, same convention as every
|
|
84
|
-
// other line here.
|
|
85
36
|
const bootstrapped = bootstrapSkillsOnLogin({ json: opts.json, isTTY: isInteractive(opts) });
|
|
86
37
|
if (bootstrapped) {
|
|
87
38
|
if (bootstrapped.error) {
|
|
@@ -113,17 +64,11 @@ export const login = new Command('login')
|
|
|
113
64
|
.option('-p, --password <password>', 'Password (CI only — visible in process list and shell history)')
|
|
114
65
|
.option('--json', 'Output as JSON')
|
|
115
66
|
.action(async (opts) => {
|
|
116
|
-
// Validate --url but do NOT persist it yet. A failed signin must not
|
|
117
|
-
// brick the config by pointing it at an unreachable/wrong host while the
|
|
118
|
-
// OLD token stays stored (and would then be sent to that new host on the
|
|
119
|
-
// next command). Target this run via `pendingUrl` and persist only once
|
|
120
|
-
// a token has actually been obtained. (#68)
|
|
121
67
|
const pendingUrl = opts.url ? requireUrl(opts.url, '--url') : undefined;
|
|
122
68
|
const persistUrlIfPending = () => {
|
|
123
69
|
if (pendingUrl)
|
|
124
70
|
config.set('apiUrl', pendingUrl);
|
|
125
71
|
};
|
|
126
|
-
// Check environment variable override first
|
|
127
72
|
const envToken = process.env['TRAWL_TOKEN'];
|
|
128
73
|
if (envToken) {
|
|
129
74
|
config.set('token', requireFreshJwt(envToken, 'TRAWL_TOKEN'));
|
|
@@ -131,20 +76,15 @@ export const login = new Command('login')
|
|
|
131
76
|
reportLoginSuccess(opts);
|
|
132
77
|
return;
|
|
133
78
|
}
|
|
134
|
-
// --token flag: direct JWT (CI / retrocompat)
|
|
135
79
|
if (opts.token) {
|
|
136
80
|
config.set('token', requireFreshJwt(opts.token, '--token'));
|
|
137
81
|
persistUrlIfPending();
|
|
138
82
|
reportLoginSuccess(opts);
|
|
139
83
|
return;
|
|
140
84
|
}
|
|
141
|
-
// Email/password flow
|
|
142
85
|
if (opts.password) {
|
|
143
86
|
console.error(chalk.yellow('Warning: passing --password on the command line is insecure.'));
|
|
144
87
|
}
|
|
145
|
-
// #107 — never blocks on a readline prompt under --json or a non-TTY
|
|
146
|
-
// invocation (agent/CI subprocess); refuses with a clear, structured
|
|
147
|
-
// usage error instead, naming the flag (or TRAWL_TOKEN) to pass.
|
|
148
88
|
if (!opts.email) {
|
|
149
89
|
requireInteractive('Email is required — pass -e/--email, --token, or set TRAWL_TOKEN (refusing to block on a prompt, non-interactive).', { json: opts.json });
|
|
150
90
|
}
|
|
@@ -154,7 +94,6 @@ export const login = new Command('login')
|
|
|
154
94
|
const email = opts.email ?? (await promptEmail());
|
|
155
95
|
const password = opts.password ?? (await promptPassword('Password: '));
|
|
156
96
|
const { data, headers } = await api.publicPost('/api/auth/signin', { email, password }, pendingUrl);
|
|
157
|
-
// Try token from response body first, then fall back to Set-Cookie header
|
|
158
97
|
let raw = typeof data === 'string' ? data : data.token;
|
|
159
98
|
if (!raw) {
|
|
160
99
|
const setCookie = headers.get('set-cookie') ?? '';
|
|
@@ -175,12 +114,6 @@ export const logout = new Command('logout')
|
|
|
175
114
|
.option('--json', 'Output as JSON')
|
|
176
115
|
.action((opts) => {
|
|
177
116
|
config.set('token', '');
|
|
178
|
-
// #169 review finding 3 — this is the sharpest version of the gap: an
|
|
179
|
-
// operator runs `logout` SPECIFICALLY to kill access, and if
|
|
180
|
-
// TRAWL_API_KEY/TRAWL_TOKEN is set, access is NOT killed — every
|
|
181
|
-
// subsequent command keeps authenticating with the env credential this
|
|
182
|
-
// command cannot touch. The wording below must not read as a variant of
|
|
183
|
-
// "logged out"; it says plainly that access is still live.
|
|
184
117
|
const overrideVar = getLiveAuthEnvVar();
|
|
185
118
|
if (overrideVar) {
|
|
186
119
|
console.error(chalk.yellow(`⚠ ${overrideVar} is still set in your environment — access is NOT revoked. Every subsequent command will keep authenticating with it until you unset it (or revoke the key in the dashboard).`));
|
package/dist/commands/ping.d.ts
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
/**
|
|
3
|
-
* `GET /api/health` response shape (home.controller.js#health,
|
|
4
|
-
* home.service.js#getHealthStatus) — MCP `trawl_health_ping` parity
|
|
5
|
-
* (`{ok, version, uptime}`). The REST route enriches the payload with
|
|
6
|
-
* `version`/`uptime`/`db`/`memory` ONLY for an admin caller
|
|
7
|
-
* (home.controller.js's `isAdmin` gate); a non-admin JWT gets `{status:'ok'}`
|
|
8
|
-
* alone on a healthy server. A non-2xx response (degraded, `status:503`)
|
|
9
|
-
* throws an ApiError like any other failed request — but a 2xx response is
|
|
10
|
-
* NOT a guarantee that `status === 'ok'` (#106 review F5): a soft-degraded
|
|
11
|
-
* signal (e.g. a partial dependency outage) could still come back as a 200
|
|
12
|
-
* with a non-"ok" `status`. The HTTP call not throwing only means the
|
|
13
|
-
* transport succeeded, never that the reported health is good — so the
|
|
14
|
-
* human-mode `✓ OK` line is gated on the actual field, not assumed from a
|
|
15
|
-
* successful round-trip.
|
|
16
|
-
*/
|
|
17
2
|
export interface PingResponse {
|
|
18
3
|
status: string;
|
|
19
4
|
db?: string;
|
package/dist/commands/ping.js
CHANGED
|
@@ -6,33 +6,18 @@ export const ping = new Command('ping')
|
|
|
6
6
|
.description('Health/version handshake against the Trawl API')
|
|
7
7
|
.option('--json', 'Output as JSON')
|
|
8
8
|
.action(async (opts) => {
|
|
9
|
-
// #148 — the server route is optionalAuth (public, enriched for an admin
|
|
10
|
-
// JWT — see the PingResponse doc above); `api.publicGet` mirrors that:
|
|
11
|
-
// it attaches a stored/env token when one happens to be available but
|
|
12
|
-
// never REQUIRES one, so `ping` works as a zero-config sanity check even
|
|
13
|
-
// with no `trawl login` ever run. `api.get` (the authenticated path)
|
|
14
|
-
// would throw notLoggedInError() locally before this ever reached the
|
|
15
|
-
// network, defeating the whole point of a pre-login handshake.
|
|
16
9
|
const data = await api.publicGet('/api/health');
|
|
17
|
-
// #106 review (kimi) — honest exit code: a soft-degraded 200 (status !== 'ok')
|
|
18
|
-
// must exit non-zero so `trawl ping || handle_degraded` doesn't treat a
|
|
19
|
-
// degraded API as healthy. Applies in BOTH --json and human modes, alongside
|
|
20
|
-
// the raw/decorated output below.
|
|
21
10
|
if (data.status !== 'ok')
|
|
22
11
|
process.exitCode = 1;
|
|
23
12
|
if (opts.json) {
|
|
24
13
|
json(data);
|
|
25
14
|
return;
|
|
26
15
|
}
|
|
27
|
-
// #119 — label it as the API's version, not a bare `v0.4.0` that reads as
|
|
28
|
-
// "the platform is v0.4" (it's the server package version field).
|
|
29
16
|
const versionSuffix = data.version ? ` (api v${data.version})` : '';
|
|
30
17
|
if (data.status === 'ok') {
|
|
31
18
|
console.log(chalk.green('✓ OK') + versionSuffix);
|
|
32
19
|
}
|
|
33
20
|
else {
|
|
34
|
-
// #106 review F5 — never green-light a degraded API just because the
|
|
35
|
-
// HTTP call didn't throw; show the real reported status instead.
|
|
36
21
|
console.log(chalk.yellow(`⚠ ${data.status}`) + versionSuffix);
|
|
37
22
|
}
|
|
38
23
|
});
|
|
@@ -1,138 +1,18 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
/**
|
|
3
|
-
* #108 — surface reorg. `run`/`list`/`get`/`data`/`history`/`run-info`/
|
|
4
|
-
* `trigger` are promoted to top-level verbs (see index.ts's `createProgram`)
|
|
5
|
-
* alongside `create`/`whoami`/`ping` (#114 — `create` replaced `fetch` in
|
|
6
|
-
* that group). Each is built by an exported
|
|
7
|
-
* `attachXCommand(parent, attachOpts)` factory instead of a fixed
|
|
8
|
-
* `scraps.command(...)` chain, so it can be attached TWICE with a single
|
|
9
|
-
* source-of-truth definition: once to `program` (the new canonical
|
|
10
|
-
* top-level path) and once more, hidden, right back onto `scraps` — so every
|
|
11
|
-
* pre-#108 `trawl scraps <verb>` invocation keeps resolving unchanged
|
|
12
|
-
* (`{ hidden: true }` only affects help visibility, never resolution).
|
|
13
|
-
*
|
|
14
|
-
* Future-drift guard: ALL `.option()`/`.argument()`/`.action()` wiring for a
|
|
15
|
-
* promoted verb MUST live INSIDE its `attachXCommand` factory body, never
|
|
16
|
-
* bolted onto one of its two call sites (index.ts's `createProgram` for the
|
|
17
|
-
* top-level attach, this file's own `attachXCommand(scraps, { hidden: true
|
|
18
|
-
* })` call for the legacy one). That's the only thing keeping `trawl run
|
|
19
|
-
* <id>` and `trawl scraps run <id>` identical — a call-site-only tweak to
|
|
20
|
-
* one attachment would silently diverge the two.
|
|
21
|
-
*/
|
|
22
2
|
type AttachOptions = {
|
|
23
3
|
hidden?: boolean;
|
|
24
4
|
};
|
|
25
5
|
export declare const scraps: Command;
|
|
26
|
-
/** The top-of-history snapshot pollRunProgress needs to identify which run
|
|
27
|
-
* it's watching — see captureBeforeRunState.
|
|
28
|
-
*
|
|
29
|
-
* #97 — `captured` discriminates WHY `id` is undefined: `true` means the GET
|
|
30
|
-
* succeeded and the scrap genuinely has no history yet (an honest "never
|
|
31
|
-
* run" signal pollRunProgress can trust immediately); `false` means the GET
|
|
32
|
-
* itself threw, so `id`/`alreadyInFlight` carry NO information at all — the
|
|
33
|
-
* scrap could easily have prior (possibly terminal) history that this
|
|
34
|
-
* lookup simply never saw. Before this field existed, both cases produced
|
|
35
|
-
* the identical `{id: undefined, alreadyInFlight: false}` shape, so
|
|
36
|
-
* pollRunProgress could not tell them apart (see its own #97 comment).
|
|
37
|
-
*/
|
|
38
6
|
interface BeforeRunState {
|
|
39
7
|
id?: string;
|
|
40
8
|
alreadyInFlight: boolean;
|
|
41
9
|
captured: boolean;
|
|
42
10
|
}
|
|
43
|
-
/** The machine-readable outcome `pollRunProgress` prints as the single final
|
|
44
|
-
* NDJSON line under `--json` (#107 review F1). `status` is the same honest
|
|
45
|
-
* terminal string the human-mode "Run finished: <status>" line already
|
|
46
|
-
* shows (`success`/`error`/`empty`/`regression`/…), or `'timeout'` /
|
|
47
|
-
* `'poll_error'` for the two non-terminal exits. */
|
|
48
11
|
export interface PollOutcome {
|
|
49
12
|
runId?: string;
|
|
50
13
|
status: string;
|
|
51
|
-
/** Present only for `status:'poll_error'` — the last poll failure's message. */
|
|
52
14
|
error?: string;
|
|
53
15
|
}
|
|
54
|
-
/**
|
|
55
|
-
* #91 P1 — replaces "await the run to completion, THEN open the activities
|
|
56
|
-
* SSE stream" (which showed NOTHING: the activities SSE
|
|
57
|
-
* (GET /api/scraps/:id/activities/stream) is backed by an in-process
|
|
58
|
-
* EventEmitter with no backlog — trawl_node
|
|
59
|
-
* modules/activities/services/activities.service.js — so by the time a
|
|
60
|
-
* synchronous run has already finished there is nothing left to emit; and
|
|
61
|
-
* the async `trigger` default enqueues the run onto a durable job queue a
|
|
62
|
-
* SEPARATE cron-consumer pod drains, whose in-process emitter never reaches
|
|
63
|
-
* the API pod holding the SSE connection at all).
|
|
64
|
-
*
|
|
65
|
-
* Instead this polls two REST reads that are BOTH Mongo-backed (not
|
|
66
|
-
* in-process), so they work no matter which pod actually executed the run:
|
|
67
|
-
* - GET /api/scraps/:id/activities?history=<hid>&limit=20 — the SAME
|
|
68
|
-
* activities-list endpoint `doctor`'s fetchRunAndFix already calls
|
|
69
|
-
* (src/commands/doctor.ts) — prints each new activity line once the new
|
|
70
|
-
* run's history id is known.
|
|
71
|
-
* - GET /api/scraps/:id — history[0].status/statusDetail, the SAME
|
|
72
|
-
* terminal-status signal `lastStatus()` above already trusts (status:null
|
|
73
|
-
* === in flight, #88 item 1) to know when the run is done.
|
|
74
|
-
*
|
|
75
|
-
* #93 item 1 — dedup-race fix. "Is this the run we're watching?" used to be
|
|
76
|
-
* a single check: `history[0]._id !== beforeHistoryId`. That's wrong when
|
|
77
|
-
* `before.alreadyInFlight` is true (a `trigger` call deduped onto a worker
|
|
78
|
-
* job that was ALREADY pending/running at capture time): the top row IS the
|
|
79
|
-
* run we're watching, but its `_id` never changes, so the old guard never
|
|
80
|
-
* released and the poll ran the full timeout to a false "Timed out". The run
|
|
81
|
-
* we're watching is now EITHER a brand-new id (fresh trigger, the common
|
|
82
|
-
* case) OR the same id that was already in-flight (status:null) at capture
|
|
83
|
-
* (the dedup case) — a same-id row that was already TERMINAL at capture is
|
|
84
|
-
* neither, and must not be latched onto as "done" (it's just the previous
|
|
85
|
-
* run, still sitting there until a genuinely new run supersedes it).
|
|
86
|
-
*
|
|
87
|
-
* #97 — capture-failed fallback. The dedup-race fix above assumes `before`
|
|
88
|
-
* is trustworthy. When `captureBeforeRunState`'s own GET threw,
|
|
89
|
-
* `before.id` is `undefined` — and that is INDISTINGUISHABLE from "the
|
|
90
|
-
* scrap has genuinely never run" (also `id: undefined`), which is exactly
|
|
91
|
-
* the case `isNewRun` above is designed to match on the very first row that
|
|
92
|
-
* ever appears. So on the very first poll, ANY pre-existing history row —
|
|
93
|
-
* even the STALE PREVIOUS run, already terminal — satisfied
|
|
94
|
-
* `last._id !== undefined` and got reported as "the run we just launched"
|
|
95
|
-
* finishing, when it was really just whatever ran before.
|
|
96
|
-
*
|
|
97
|
-
* Fix: `before.captured === false` defers trusting a baseline at all.
|
|
98
|
-
* Instead of comparing against the (unknown) `beforeId` from the start, the
|
|
99
|
-
* FIRST successful poll read is treated as the deferred capture itself —
|
|
100
|
-
* exactly what `captureBeforeRunState` would have returned had its GET
|
|
101
|
-
* succeeded — and only READS from that point on are compared against it,
|
|
102
|
-
* via the exact same `isNewRun` / `isDedupOntoInFlight` logic above. A
|
|
103
|
-
* pre-existing terminal row observed on that first read becomes `beforeId`
|
|
104
|
-
* (not a match for itself), so it correctly falls into "still the stale
|
|
105
|
-
* previous run" below and the poll keeps waiting; a row still in flight
|
|
106
|
-
* becomes the `alreadyInFlight` baseline, exactly like a successful capture
|
|
107
|
-
* would have recorded. Either way this costs at most one extra poll
|
|
108
|
-
* interval, bounded by the same deadline as everything else. The one
|
|
109
|
-
* remaining edge case — capture failed AND the scrap never ran before AND
|
|
110
|
-
* the triggered run already finished by the very first poll — is genuinely
|
|
111
|
-
* undecidable from "stale pre-existing row" with no more information than
|
|
112
|
-
* this function has, so it resolves to the same honest timeout rather than
|
|
113
|
-
* risk reporting a possibly-wrong outcome (never a lie, at worst a timeout
|
|
114
|
-
* telling the caller to check `doctor`).
|
|
115
|
-
*
|
|
116
|
-
* #107 review F1 — before this fix, `run|trigger --json --watch` was
|
|
117
|
-
* outcome-blind: `quiet` suppressed ALL output (including "Run finished:
|
|
118
|
-
* failure" and the timeout notice), a transient poll error was caught and
|
|
119
|
-
* silently retried FOREVER within the deadline, and the process always
|
|
120
|
-
* exited 0 after the poll loop regardless of what the watched run actually
|
|
121
|
-
* did — dead air, then a clean exit code, even for a failed or timed-out
|
|
122
|
-
* run. An agent scripting this CLI had no way to tell success from failure
|
|
123
|
-
* from "we gave up". Fixed by:
|
|
124
|
-
* - emitting exactly ONE final NDJSON line on stdout under `--json` once
|
|
125
|
-
* the watch reaches ANY of its three exits (terminal status, timeout, or
|
|
126
|
-
* a persistent poll error) — `{runId,status}` (+`error` for a poll
|
|
127
|
-
* error) — while every intermediate progress line stays suppressed
|
|
128
|
-
* (unchanged from before);
|
|
129
|
-
* - setting `process.exitCode` non-zero on a genuine run failure, a
|
|
130
|
-
* timeout, or a persistent poll error, and `0` on a real success — in
|
|
131
|
-
* BOTH `--json` and human `--watch` modes (human mode used to exit 0
|
|
132
|
-
* unconditionally, the same bug, just silent instead of dishonest);
|
|
133
|
-
* - giving up after `MAX_CONSECUTIVE_POLL_ERRORS` consecutive failed reads
|
|
134
|
-
* instead of retrying the same dead endpoint for the full 300s.
|
|
135
|
-
*/
|
|
136
16
|
export declare function pollRunProgress(id: string, before: BeforeRunState | undefined, opts?: {
|
|
137
17
|
intervalMs?: number;
|
|
138
18
|
timeoutMs?: number;
|