@trawlme/cli 3.12.0 → 3.12.2
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 +2 -2
- 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/docs/agent-quickstart.md +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -24,30 +24,6 @@ import { getApiUrl } from './lib/config.js';
|
|
|
24
24
|
import { resolveDocsUrls, docsFooterLine } from './lib/docs.js';
|
|
25
25
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
26
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
27
|
-
/**
|
|
28
|
-
* Derive a safe telemetry event name from a *resolved* commander Command —
|
|
29
|
-
* NEVER from raw argv. Flag values (e.g. the string after --password/--email/
|
|
30
|
-
* --url) don't start with '-' and would otherwise survive an argv filter and
|
|
31
|
-
* leak to PostHog (#67). Falls back to 'unknown' when no command resolved
|
|
32
|
-
* (e.g. an error thrown before any action ran).
|
|
33
|
-
*
|
|
34
|
-
* #88 item 9 — walks the FULL parent chain, not just the immediate parent:
|
|
35
|
-
* the old one-level join (`${parent.name()} ${own.name()}`) resolved a
|
|
36
|
-
* 4-deep command like `scraps account session set` down to just "session
|
|
37
|
-
* set", silently dropping "scraps account". The root program node (the
|
|
38
|
-
* 'trawl' Command itself, which has no `.parent`) is excluded from the
|
|
39
|
-
* chain — matching the pre-existing convention that a direct child of the
|
|
40
|
-
* root (e.g. `scraps list`, `telemetry on`) is named relative to its
|
|
41
|
-
* immediate group, never prefixed with the program name.
|
|
42
|
-
*
|
|
43
|
-
* #108 note: promoting a verb to a top-level command (see `createProgram`
|
|
44
|
-
* below) renamed ITS resolved telemetry name from `scraps <verb>` to
|
|
45
|
-
* `<verb>` — the canonical top-level attach and the legacy hidden
|
|
46
|
-
* `scraps <verb>` attach are two separate Command instances (scraps.ts's
|
|
47
|
-
* double-attach factories), each with its own parent chain, so they
|
|
48
|
-
* resolve to two different names here even though they run the same
|
|
49
|
-
* handler. Intentional (the canonical command IS now `<verb>`), not a bug.
|
|
50
|
-
*/
|
|
51
27
|
export function resolveCommandName(actionCommand) {
|
|
52
28
|
if (!actionCommand)
|
|
53
29
|
return 'unknown';
|
|
@@ -57,16 +33,8 @@ export function resolveCommandName(actionCommand) {
|
|
|
57
33
|
chain.unshift(current.name());
|
|
58
34
|
current = current.parent;
|
|
59
35
|
}
|
|
60
|
-
// `current` is now the root (no parent) — its name is excluded by design.
|
|
61
|
-
// If the chain came up empty, actionCommand itself IS the root (no parent
|
|
62
|
-
// at all) — fall back to its own name so the function never returns ''.
|
|
63
36
|
return chain.length > 0 ? chain.join(' ') : actionCommand.name();
|
|
64
37
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Walk the full command tree and produce every valid resolveCommandName()
|
|
67
|
-
* token — the allowlist registered with posthog.ts so captureCommand can
|
|
68
|
-
* never be handed a free-form string.
|
|
69
|
-
*/
|
|
70
38
|
export function collectCommandNames(root) {
|
|
71
39
|
const names = [];
|
|
72
40
|
const walk = (cmd) => {
|
|
@@ -78,16 +46,6 @@ export function collectCommandNames(root) {
|
|
|
78
46
|
walk(root);
|
|
79
47
|
return names;
|
|
80
48
|
}
|
|
81
|
-
/**
|
|
82
|
-
* #108 — surface reorg into two `trawl --help` tiers. Core verbs are
|
|
83
|
-
* agent+human, `--json` first-class, non-interactive; Management is the
|
|
84
|
-
* existing human/CI surface, kept but grouped so top-level help reads
|
|
85
|
-
* simple. Commander v14's native per-command help group (`.commandsGroup()`
|
|
86
|
-
* sets the default a subsequently-registered command inherits via
|
|
87
|
-
* `.helpGroup()`) drives the section headings — group ORDER in the printed
|
|
88
|
-
* help follows first-seen insertion order into `program.commands`, so every
|
|
89
|
-
* Core command is registered below before any Management one.
|
|
90
|
-
*/
|
|
91
49
|
const CORE_GROUP = 'Core commands (agent + human):';
|
|
92
50
|
const MANAGEMENT_GROUP = 'Management commands (human/CI):';
|
|
93
51
|
export function createProgram() {
|
|
@@ -96,28 +54,6 @@ export function createProgram() {
|
|
|
96
54
|
.description('Trawl CLI — manage scraps from the terminal')
|
|
97
55
|
.version(pkg.version)
|
|
98
56
|
.option('--debug', 'Show full error stack traces');
|
|
99
|
-
// Core verbs (#108) — promoted/listed first, registration order now
|
|
100
|
-
// follows the guided flow a new user (or agent) actually walks: create it
|
|
101
|
-
// → list/get to see it → run/trigger to execute it → data to read the
|
|
102
|
-
// result → history/run-info for deeper diagnostics → whoami/token/ping as
|
|
103
|
-
// standalone identity/credential/health utilities (#149 item 6 — the
|
|
104
|
-
// pre-#149 order had `run` wedged between `create` and `list`, and
|
|
105
|
-
// `trigger` stranded after `run-info`, both ahead of ever having listed or
|
|
106
|
-
// inspected anything). `list`/`get`/`run`/`data`/`history`/`run-info`/
|
|
107
|
-
// `trigger` are built via scraps.ts's exported attachXCommand() factories —
|
|
108
|
-
// the SAME definition also stays wired (hidden) under `scraps` there, so
|
|
109
|
-
// every pre-#108 `trawl scraps <verb>` invocation keeps resolving (no
|
|
110
|
-
// breaking change).
|
|
111
|
-
//
|
|
112
|
-
// #114 — `create` replaced `fetch` in this slot: `POST /api/ai/wizard`
|
|
113
|
-
// (AI-generate + persist + first-run + autofix), a distinct command from
|
|
114
|
-
// the still-untouched `trawl scraps create` (raw-script management verb).
|
|
115
|
-
//
|
|
116
|
-
// #149 item 5 — `token` promoted from Management into Core: it satisfies
|
|
117
|
-
// every criterion the CORE_GROUP heading itself claims (agent + human,
|
|
118
|
-
// `--json` first-class, never interactive) at least as well as `whoami`/
|
|
119
|
-
// `ping` do, and exists specifically to feed an agent's MCP Bearer auth
|
|
120
|
-
// header (`$(trawl token)`) — a Management/human-CI bucket undersells that.
|
|
121
57
|
program.commandsGroup(CORE_GROUP);
|
|
122
58
|
program.addCommand(create);
|
|
123
59
|
attachListCommand(program);
|
|
@@ -130,13 +66,7 @@ export function createProgram() {
|
|
|
130
66
|
program.addCommand(whoami);
|
|
131
67
|
program.addCommand(token);
|
|
132
68
|
program.addCommand(ping);
|
|
133
|
-
// #170 — `spec` last: it describes the whole tree above it, so it reads
|
|
134
|
-
// naturally as the final "and here's the full machine-readable map" entry
|
|
135
|
-
// rather than being wedged between identity/credential/health utilities.
|
|
136
69
|
program.addCommand(spec);
|
|
137
|
-
// Management (#108) — human/CI surface, grouped below. `scraps` still
|
|
138
|
-
// holds every pre-#108 management command (create/update/delete/banner/
|
|
139
|
-
// watch/account.*/session.*/doctor/autofix/snapshot) exactly as before.
|
|
140
70
|
program.commandsGroup(MANAGEMENT_GROUP);
|
|
141
71
|
program.addCommand(scraps);
|
|
142
72
|
program.addCommand(skills);
|
|
@@ -144,28 +74,9 @@ export function createProgram() {
|
|
|
144
74
|
program.addCommand(logout);
|
|
145
75
|
program.addCommand(telemetry);
|
|
146
76
|
program.addCommand(upgrade);
|
|
147
|
-
// #185 — one dim line pointing at the docs, appended after EVERY `--help`
|
|
148
|
-
// in the tree (registering 'afterAll' on the root fires for a subcommand's
|
|
149
|
-
// own `--help` too — commander emits the afterAllHelp event on every
|
|
150
|
-
// ancestor of whichever command's outputHelp() ran, root included).
|
|
151
|
-
// Gated the way lib/tips.ts gates its own footer-shaped output: a pure,
|
|
152
|
-
// synchronous helper (docsFooterText, exported for unit testing) wrapped
|
|
153
|
-
// in try/catch so a broken footer can never turn a clean `--help` into a
|
|
154
|
-
// crash — but deliberately WITHOUT tips.ts's TTY/--json gate: `--help`
|
|
155
|
-
// itself never emits a --json payload (nothing here can pollute that
|
|
156
|
-
// channel), and a docs line inside `--help | less` is exactly the kind of
|
|
157
|
-
// thing worth keeping, unlike a promotional nudge.
|
|
158
77
|
program.addHelpText('afterAll', docsFooterText);
|
|
159
78
|
return program;
|
|
160
79
|
}
|
|
161
|
-
/**
|
|
162
|
-
* #185 — see the `addHelpText('afterAll', …)` call above for why this is
|
|
163
|
-
* unconditional (no TTY/--json gate) and why a thrown error must never
|
|
164
|
-
* escape: commander calls this synchronously while already writing help
|
|
165
|
-
* output, so a throw here would crash a `--help` invocation, the one code
|
|
166
|
-
* path this whole feature is not allowed to touch (see docs.ts's own
|
|
167
|
-
* `docsFooterLine` for the "never a guessed URL" contract this composes).
|
|
168
|
-
*/
|
|
169
80
|
export function docsFooterText() {
|
|
170
81
|
try {
|
|
171
82
|
const line = docsFooterLine(resolveDocsUrls({ apiBaseUrl: getApiUrl() }).docsUrl);
|
|
@@ -175,18 +86,6 @@ export function docsFooterText() {
|
|
|
175
86
|
return '';
|
|
176
87
|
}
|
|
177
88
|
}
|
|
178
|
-
/**
|
|
179
|
-
* True when this module is the process entrypoint (not merely imported by a test).
|
|
180
|
-
*
|
|
181
|
-
* npm installs the global bin as a SYMLINK (`bin/trawl` -> `dist/index.js`). For an
|
|
182
|
-
* ESM entrypoint Node realpaths the module URL (`import.meta.url` = the real
|
|
183
|
-
* `dist/index.js`) but leaves `process.argv[1]` as the symlink path — so a raw
|
|
184
|
-
* `moduleUrl === pathToFileURL(argv1).href` compare is FALSE for the normal global
|
|
185
|
-
* invocation, the guard never fires, and `runCli()` never runs (silent no-op, #103).
|
|
186
|
-
* Canonicalise both sides with `realpathSync` before comparing. A genuine entrypoint
|
|
187
|
-
* was just loaded by Node, so both realpath calls resolve; the catch only trips for a
|
|
188
|
-
* non-file module URL (exotic loaders) — correctly "not the entrypoint".
|
|
189
|
-
*/
|
|
190
89
|
export function isEntryPoint(argv1, moduleUrl) {
|
|
191
90
|
if (argv1 === undefined)
|
|
192
91
|
return false;
|
|
@@ -197,15 +96,6 @@ export function isEntryPoint(argv1, moduleUrl) {
|
|
|
197
96
|
return false;
|
|
198
97
|
}
|
|
199
98
|
}
|
|
200
|
-
/**
|
|
201
|
-
* True when the invocation is a pure `--help`/`--version` query, a bare
|
|
202
|
-
* `trawl` with no subcommand (commander prints top-level help and exits), or
|
|
203
|
-
* `trawl help [command]`. None of these should trigger the skills auto-sync
|
|
204
|
-
* (a filesystem-mutating startup side effect) — a user running `trawl
|
|
205
|
-
* --version` (or just `trawl`) never expects it to rewrite their skills
|
|
206
|
-
* dirs. (#73, extended #86 finding 7 for the bare-invocation + `help`
|
|
207
|
-
* subcommand cases)
|
|
208
|
-
*/
|
|
209
99
|
export function isHelpOrVersion(argv) {
|
|
210
100
|
if (argv.some((a) => a === '-h' || a === '--help' || a === '-V' || a === '--version'))
|
|
211
101
|
return true;
|
|
@@ -216,76 +106,9 @@ export function isHelpOrVersion(argv) {
|
|
|
216
106
|
return true;
|
|
217
107
|
return false;
|
|
218
108
|
}
|
|
219
|
-
/**
|
|
220
|
-
* True for a bare `trawl` invocation — no subcommand, no flags at all. This
|
|
221
|
-
* is the CLI's "first thing a new user sees" moment (commander prints its
|
|
222
|
-
* own top-level help right after), distinct from `isHelpOrVersion` which is
|
|
223
|
-
* intentionally broader (also matches `--help`/`--version`/`trawl help`
|
|
224
|
-
* anywhere in argv) — the Pinch wave banner (#94) only wants the narrowest
|
|
225
|
-
* case so it never shows up ahead of e.g. `trawl scraps --help`.
|
|
226
|
-
*/
|
|
227
109
|
export function isBareInvocation(argv) {
|
|
228
110
|
return argv.slice(2).length === 0;
|
|
229
111
|
}
|
|
230
|
-
/**
|
|
231
|
-
* True for `trawl spec` (with or without `--json`) — deliberately NOT folded
|
|
232
|
-
* into `isHelpOrVersion` above: that predicate's name and doc comment are
|
|
233
|
-
* about help/version queries specifically, and `spec` is a real, data-
|
|
234
|
-
* bearing command (see `isHelpOrVersion`'s own #170 test case), not a help
|
|
235
|
-
* query. This is its own narrow predicate for a DIFFERENT reason: `spec
|
|
236
|
-
* --json` is documented (docs/agent-quickstart.md) as the first call an AI
|
|
237
|
-
* agent makes against this CLI, so it must not mutate anything the caller
|
|
238
|
-
* did not ask it to — same requirement `--help`/`--version` already got
|
|
239
|
-
* from `isHelpOrVersion` (#73), extended here to cover `spec` too.
|
|
240
|
-
*
|
|
241
|
-
* Scope, stated honestly rather than aspirationally: this guard suppresses
|
|
242
|
-
* the skills auto-sync (an `rmSync(recursive)` + `cpSync` over the user's
|
|
243
|
-
* `~/.claude/skills`) and the update notifier (a config write plus a
|
|
244
|
-
* DETACHED CHILD that queries the npm registry — fatal in an
|
|
245
|
-
* egress-restricted sandbox). It does NOT suppress `initPostHog()`, which
|
|
246
|
-
* still creates the `conf` config file and mints a persistent
|
|
247
|
-
* `telemetryUserId` on first run, exactly as it does for every other
|
|
248
|
-
* command. That one is deliberate: `spec` is the signal that tells us
|
|
249
|
-
* whether agents are actually adopting the CLI, so it stays measured, and
|
|
250
|
-
* the caller's own controls (`TRAWL_TELEMETRY=0`, `DO_NOT_TRACK=1`) are the
|
|
251
|
-
* opt-out. That same first run also has `initPostHog()` write a one-time
|
|
252
|
-
* telemetry disclosure line (`ℹ Trawl CLI collects anonymous usage
|
|
253
|
-
* telemetry…`) to STDERR, ahead of the JSON payload — this guard does not
|
|
254
|
-
* suppress that either. `stdout` stays pure JSON either way, so this only
|
|
255
|
-
* bites a caller that merges the two streams (e.g. `2>&1`); whether a `spec`
|
|
256
|
-
* probe specifically should suppress the notice is a product call,
|
|
257
|
-
* deliberately not taken here. Do not upgrade this paragraph back to "no
|
|
258
|
-
* filesystem side effect" without also gating telemetry — a comment that
|
|
259
|
-
* overstates its own invariant is worse than no comment, because the next
|
|
260
|
-
* reader trusts it.
|
|
261
|
-
*
|
|
262
|
-
* #185 — `spec --json` (not plain `spec`) now DOES make its own bounded
|
|
263
|
-
* (2s), swallowed-on-failure GET for `externalDocs.url` (see spec.ts's
|
|
264
|
-
* `fetchExternalDocsUrl`) — this is not a regression of the "no mutation"
|
|
265
|
-
* invariant above (a GET mutates nothing, and any failure — including no
|
|
266
|
-
* egress at all — degrades to the sync host-derivation fallback, never a
|
|
267
|
-
* thrown error), just a second, narrower kind of side effect this guard was
|
|
268
|
-
* never meant to suppress in the first place.
|
|
269
|
-
*
|
|
270
|
-
* Before this, `trawl spec --json` silently re-synced
|
|
271
|
-
* `~/.claude/skills` (and `./.claude/skills`) and printed `trawl: re-synced
|
|
272
|
-
* skill …` lines to stderr BEFORE the JSON payload — an agent's very first
|
|
273
|
-
* probe of the CLI mutated the user's filesystem and polluted the channel
|
|
274
|
-
* it was about to parse.
|
|
275
|
-
*
|
|
276
|
-
* Matches `spec` as the FIRST positional token, skipping any leading
|
|
277
|
-
* `-`-prefixed tokens first (a global flag ahead of the subcommand, e.g.
|
|
278
|
-
* `trawl --debug spec --json`) — the same argv-scan discipline `hasJsonFlag`
|
|
279
|
-
* below already applies. #170 review F4 — before this, a bare
|
|
280
|
-
* `argv.slice(2)[0] === 'spec'` check was defeated by ANY leading flag:
|
|
281
|
-
* `trawl --debug spec --json` read `--debug` as the first positional, missed
|
|
282
|
-
* the match entirely, and fell through to the normal startup path — running
|
|
283
|
-
* the filesystem-mutating skills auto-sync during what's documented
|
|
284
|
-
* (docs/agent-quickstart.md) as an agent's read-only first probe of the CLI.
|
|
285
|
-
* Still never a bare substring search: e.g. `trawl scraps create --title
|
|
286
|
-
* spec` (the literal string "spec" as a FLAG VALUE, not a leading flag) never
|
|
287
|
-
* matches, since `scraps` — the first non-`-`-prefixed token — isn't `spec`.
|
|
288
|
-
*/
|
|
289
112
|
export function isSpecQuery(argv) {
|
|
290
113
|
const args = argv.slice(2);
|
|
291
114
|
let i = 0;
|
|
@@ -293,62 +116,16 @@ export function isSpecQuery(argv) {
|
|
|
293
116
|
i++;
|
|
294
117
|
return args[i] === 'spec';
|
|
295
118
|
}
|
|
296
|
-
/** Best-effort scan for a `--json` flag in raw argv, used only when parsing
|
|
297
|
-
* itself failed before any command's own `.opts()` could be resolved (a
|
|
298
|
-
* commander usage error — unknown option/command, missing required arg). Same
|
|
299
|
-
* "argv scan, never trust flag values" caveat as isHelpOrVersion: positional
|
|
300
|
-
* values are never mistaken for `--json` since they don't equal the literal
|
|
301
|
-
* string. (#86 finding 3)
|
|
302
|
-
*
|
|
303
|
-
* #88 item 5 — only scans tokens BEFORE the first bare `--`. Commander treats
|
|
304
|
-
* `--` as "end of options": everything after it is a positional operand, not
|
|
305
|
-
* a flag, even if the literal text is `--json`. `scraps list -- --json`
|
|
306
|
-
* passes `--json` as an (excess) positional argument, not the flag — an
|
|
307
|
-
* unscoped `argv.includes('--json')` would still match it and wrongly emit a
|
|
308
|
-
* JSON envelope for what is actually a plain usage error with no --json
|
|
309
|
-
* requested at all.
|
|
310
|
-
*/
|
|
311
119
|
export function hasJsonFlag(argv) {
|
|
312
120
|
const dashDashIdx = argv.indexOf('--');
|
|
313
121
|
const scanned = dashDashIdx === -1 ? argv : argv.slice(0, dashDashIdx);
|
|
314
122
|
return scanned.includes('--json');
|
|
315
123
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Commander's default (no exitOverride) calls `process.exit()` directly for
|
|
318
|
-
* a usage error (unknown option/command, missing required arg) or a
|
|
319
|
-
* --help/--version/`help` query — bypassing runCli's try/catch/finally
|
|
320
|
-
* entirely, so the telemetry shutdown() flush below never runs and a usage
|
|
321
|
-
* error exits 1 (the generic bug bucket) instead of its own distinct code.
|
|
322
|
-
* `program.exitOverride()` on the root command alone does NOT fix this for
|
|
323
|
-
* subcommands added via `addCommand()` (login/scraps/skills/telemetry/token
|
|
324
|
-
* are each built as standalone Command instances in their own module and
|
|
325
|
-
* only ever copy inherited settings — including exitOverride — from a parent
|
|
326
|
-
* at `.command()` construction time, which for these root-level modules never
|
|
327
|
-
* happens). Every node in the tree needs its own exitOverride() call, so this
|
|
328
|
-
* walks the whole tree and installs it everywhere. (#86 finding 3)
|
|
329
|
-
*/
|
|
330
124
|
export function applyExitOverride(cmd) {
|
|
331
125
|
cmd.exitOverride();
|
|
332
126
|
for (const sub of cmd.commands)
|
|
333
127
|
applyExitOverride(sub);
|
|
334
128
|
}
|
|
335
|
-
/**
|
|
336
|
-
* #149 item 1 — commander's own usage-error line (unknown option, missing
|
|
337
|
-
* required arg, unknown command, …) is written via `Command#error()` ->
|
|
338
|
-
* `outputError()` straight to `process.stderr` BEFORE the exitOverride throw
|
|
339
|
-
* below is ever caught — in commander's own plain, uncolored `"error: …"`
|
|
340
|
-
* format, a visibly different convention from every other error this CLI
|
|
341
|
-
* prints (`reportError`'s `chalk.red('✗ ' + message)`). `configureOutput`'s
|
|
342
|
-
* `outputError` hook is exactly the one commander documents for reformatting
|
|
343
|
-
* the error text itself (`writeErr` also carries unrelated help output, e.g.
|
|
344
|
-
* `showHelpAfterError` — never enabled in this CLI, but out of scope to
|
|
345
|
-
* touch here). Overriding it on every node in the tree (same walk shape as
|
|
346
|
-
* applyExitOverride, for the same reason: addCommand()-attached subtrees
|
|
347
|
-
* don't otherwise inherit a parent's configureOutput) reformats that one line
|
|
348
|
-
* through the same red ✗ convention, stripping the redundant "error: " prefix
|
|
349
|
-
* first (stripCommanderErrorPrefix — the same prefix also leaks verbatim
|
|
350
|
-
* into the --json envelope's `message` field, see runCli's catch below).
|
|
351
|
-
*/
|
|
352
129
|
export function applyOutputConfiguration(cmd) {
|
|
353
130
|
cmd.configureOutput({
|
|
354
131
|
outputError: (str, write) => {
|
|
@@ -359,27 +136,7 @@ export function applyOutputConfiguration(cmd) {
|
|
|
359
136
|
for (const sub of cmd.commands)
|
|
360
137
|
applyOutputConfiguration(sub);
|
|
361
138
|
}
|
|
362
|
-
/** Commander's own codes for a successful --help/--version/`help` query —
|
|
363
|
-
* these already printed their own output (to stdout) via commander itself;
|
|
364
|
-
* runCli's catch must treat them as a clean exit, not an error. (#86 finding 3) */
|
|
365
139
|
const HELP_OR_VERSION_CODES = new Set(['commander.helpDisplayed', 'commander.help', 'commander.version']);
|
|
366
|
-
/**
|
|
367
|
-
* #88 item 6 — best-effort command-name recovery for a commander parse error
|
|
368
|
-
* that happened BEFORE any action ran, so `currentCommand` (the preAction
|
|
369
|
-
* hook's resolved command) is still undefined — e.g. an unknown option on an
|
|
370
|
-
* otherwise-valid subcommand, or excess arguments. Without this, EVERY such
|
|
371
|
-
* failure previously collapsed to resolveCommandName(undefined) === 'unknown'
|
|
372
|
-
* — and since 'unknown' was never itself allowlisted at the
|
|
373
|
-
* registerAllowedCommands call site (see runCli below), that capture was
|
|
374
|
-
* silently dropped by captureCommand's allowlist check: a dead branch that
|
|
375
|
-
* looked like it reported telemetry but never actually did.
|
|
376
|
-
*
|
|
377
|
-
* Matches ONLY a name already present in `allowedNames` (the real command
|
|
378
|
-
* tree, from collectCommandNames) — never invents one from raw argv text.
|
|
379
|
-
* Checks the two-token form first (`scraps boom`) since most usage errors
|
|
380
|
-
* happen on a nested leaf command; falls back to the single top-level token,
|
|
381
|
-
* then to 'unknown' (now itself allowlisted, so that capture fires too).
|
|
382
|
-
*/
|
|
383
140
|
export function bestEffortCommandName(argv, allowedNames) {
|
|
384
141
|
const a2 = argv[2];
|
|
385
142
|
const a3 = argv[3];
|
|
@@ -395,31 +152,16 @@ export function bestEffortCommandName(argv, allowedNames) {
|
|
|
395
152
|
export async function runCli(argv = process.argv) {
|
|
396
153
|
if (!isHelpOrVersion(argv) && !isSpecQuery(argv))
|
|
397
154
|
autoUpdateInstalledSkills();
|
|
398
|
-
// Pinch wave banner (#94) — bare `trawl` only, guarded so it never shows
|
|
399
|
-
// under NO_COLOR/non-TTY/piped output (pinchEnabled() covers all three).
|
|
400
155
|
if (isBareInvocation(argv) && pinchEnabled()) {
|
|
401
156
|
console.log(renderPinch('wave'));
|
|
402
157
|
console.log();
|
|
403
158
|
}
|
|
404
159
|
initPostHog();
|
|
405
160
|
const program = createProgram();
|
|
406
|
-
// Must run before parseAsync — installs on every node in the tree,
|
|
407
|
-
// including subcommands added via addCommand() that don't otherwise
|
|
408
|
-
// inherit it. (#86 finding 3)
|
|
409
161
|
applyExitOverride(program);
|
|
410
|
-
// Same tree-walk requirement as applyExitOverride, same reason — installs
|
|
411
|
-
// the unified red ✗ error formatting on every node. (#149 item 1)
|
|
412
162
|
applyOutputConfiguration(program);
|
|
413
163
|
const commandNames = collectCommandNames(program);
|
|
414
|
-
// #88 item 6 — 'unknown' is a legitimate resolveCommandName() output (the
|
|
415
|
-
// fallback for "no command resolved at all"), not free-form user input —
|
|
416
|
-
// it must be explicitly allowlisted here or every capture that falls back
|
|
417
|
-
// to it is silently dropped by captureCommand's allowlist check (a dead
|
|
418
|
-
// branch that looks like it reports telemetry but never does).
|
|
419
164
|
registerAllowedCommands([...commandNames, 'unknown']);
|
|
420
|
-
// Track start times + the currently-resolved command per instance, so the
|
|
421
|
-
// catch handler below can derive the exact same safe name the success path
|
|
422
|
-
// uses — it must never re-derive anything from argv.
|
|
423
165
|
const startTimes = new WeakMap();
|
|
424
166
|
let currentCommand;
|
|
425
167
|
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
@@ -429,8 +171,6 @@ export async function runCli(argv = process.argv) {
|
|
|
429
171
|
program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
430
172
|
const start = startTimes.get(actionCommand);
|
|
431
173
|
if (start !== undefined) {
|
|
432
|
-
// Actions can fail without throwing (process.exitCode set directly) —
|
|
433
|
-
// report the real outcome instead of hardcoding success.
|
|
434
174
|
const exitCode = typeof process.exitCode === 'number' ? process.exitCode : 0;
|
|
435
175
|
void captureCommand(resolveCommandName(actionCommand), {
|
|
436
176
|
duration_ms: Date.now() - start,
|
|
@@ -439,22 +179,6 @@ export async function runCli(argv = process.argv) {
|
|
|
439
179
|
}
|
|
440
180
|
});
|
|
441
181
|
try {
|
|
442
|
-
// #148 — a bare `trawl` (no args at all) reads as "let me look around",
|
|
443
|
-
// not a failure: it should behave exactly like `trawl --help` (same
|
|
444
|
-
// text, stdout, exit 0). Left to commander's own default, a program with
|
|
445
|
-
// subcommands and no root action handler treats zero args as "probably
|
|
446
|
-
// missing subcommand" and calls `this.help({ error: true })` internally
|
|
447
|
-
// (see node_modules/commander/lib/command.js `_parseCommand`) — which
|
|
448
|
-
// writes the SAME help text to stderr and throws a CommanderError with
|
|
449
|
-
// exitCode 1 (caught below, bucketed into HELP_OR_VERSION_CODES since its
|
|
450
|
-
// code is 'commander.help', so only the exit code carries the mistake
|
|
451
|
-
// through — text already went to the wrong stream by then). Intercepting
|
|
452
|
-
// here, before parseAsync ever runs, avoids fighting that internal
|
|
453
|
-
// decision entirely and can't affect a genuine unknown top-level command
|
|
454
|
-
// (`trawl frobnicate`) — that path only runs when `isBareInvocation` is
|
|
455
|
-
// false, so it still reaches `this.unknownCommand()` unchanged (stderr,
|
|
456
|
-
// exit 2, code 'commander.unknownCommand' — never in
|
|
457
|
-
// HELP_OR_VERSION_CODES).
|
|
458
182
|
if (isBareInvocation(argv)) {
|
|
459
183
|
program.outputHelp();
|
|
460
184
|
process.exitCode = 0;
|
|
@@ -467,40 +191,13 @@ export async function runCli(argv = process.argv) {
|
|
|
467
191
|
const { debug } = program.opts();
|
|
468
192
|
const isDebug = Boolean(debug || process.env['DEBUG']);
|
|
469
193
|
if (err instanceof CommanderError) {
|
|
470
|
-
// Commander's own parse-time errors (exitOverride, #86 finding 3) —
|
|
471
|
-
// a distinct family from our ApiError/NetworkError/UsageError/generic
|
|
472
|
-
// Error taxonomy, so classifyError/reportError don't apply here.
|
|
473
194
|
if (HELP_OR_VERSION_CODES.has(err.code)) {
|
|
474
|
-
// --help / --version / `trawl help` already printed their own
|
|
475
|
-
// output via commander itself — nothing else to print, just adopt
|
|
476
|
-
// commander's suggested exit code (0) and fall through to the
|
|
477
|
-
// shared shutdown() flush below.
|
|
478
195
|
process.exitCode = err.exitCode;
|
|
479
196
|
}
|
|
480
197
|
else {
|
|
481
|
-
// A usage error (unknown option/command, missing required arg, …) —
|
|
482
|
-
// commander already wrote its own human-readable line to stderr via
|
|
483
|
-
// Command#error() (now reformatted through the same red ✗ convention
|
|
484
|
-
// by applyOutputConfiguration, #149 item 1), so this never duplicates
|
|
485
|
-
// it. Force exit code 2 (usage) regardless of whichever code
|
|
486
|
-
// commander suggests (it defaults these to 1), and add the --json
|
|
487
|
-
// machine envelope when resolvable — parsing failed before any
|
|
488
|
-
// command's own --json flag could be read off `currentCommand`
|
|
489
|
-
// (preAction never fired), so scan raw argv instead. (#86 finding 3)
|
|
490
198
|
if (isDebug)
|
|
491
199
|
console.error(err);
|
|
492
200
|
if (hasJsonFlag(argv)) {
|
|
493
|
-
// #149 item 2 — err.message still carries commander's own literal
|
|
494
|
-
// "error: " prefix (set on the CommanderError itself, independent
|
|
495
|
-
// of the outputError override above, which only reformats what
|
|
496
|
-
// gets WRITTEN to stderr) — strip it so the envelope's `message`
|
|
497
|
-
// stays clean for a machine parser.
|
|
498
|
-
//
|
|
499
|
-
// #170 review F3 — typed as ErrorEnvelope (not a bare object
|
|
500
|
-
// literal) so the compiler enforces `retryable` being non-optional
|
|
501
|
-
// here too. Mutation-proven: `tsc --noEmit` stayed at exit 0 with
|
|
502
|
-
// `...retryFieldsFor('usage')` removed entirely before this
|
|
503
|
-
// annotation existed.
|
|
504
201
|
const envelope = {
|
|
505
202
|
message: stripCommanderErrorPrefix(err.message),
|
|
506
203
|
kind: 'usage',
|
|
@@ -509,11 +206,6 @@ export async function runCli(argv = process.argv) {
|
|
|
509
206
|
console.log(JSON.stringify({ error: envelope }));
|
|
510
207
|
}
|
|
511
208
|
process.exitCode = 2;
|
|
512
|
-
// #88 item 6 — currentCommand is still undefined here whenever the
|
|
513
|
-
// parse error happened before preAction fired (the common case for a
|
|
514
|
-
// usage error — commander validates flags/arity before invoking the
|
|
515
|
-
// action). Try a best-effort match against the real command tree
|
|
516
|
-
// instead of collapsing straight to 'unknown'.
|
|
517
209
|
const commandName = currentCommand
|
|
518
210
|
? resolveCommandName(currentCommand)
|
|
519
211
|
: bestEffortCommandName(argv, commandNames);
|
|
@@ -521,73 +213,33 @@ export async function runCli(argv = process.argv) {
|
|
|
521
213
|
}
|
|
522
214
|
}
|
|
523
215
|
else {
|
|
524
|
-
// Map the error to a distinct exit code + machine envelope instead of a
|
|
525
|
-
// uniform 1 — agents driving this CLI unattended need to tell
|
|
526
|
-
// auth-expired (3) from not-found (4) from network-down (5) from a bad
|
|
527
|
-
// flag (2) apart from an arbitrary bug (1). (#71)
|
|
528
216
|
const { exitCode } = classifyError(err);
|
|
529
|
-
// Capture error telemetry from the resolved command only — never argv.
|
|
530
217
|
void captureCommand(resolveCommandName(currentCommand), {
|
|
531
218
|
exit_code: exitCode,
|
|
532
219
|
error: err.name,
|
|
533
220
|
});
|
|
534
|
-
// A --json subcommand must keep stdout pure JSON even on failure — read
|
|
535
|
-
// the resolved command's own --json flag (never argv) so the error
|
|
536
|
-
// envelope lands on the same channel the success path would have used.
|
|
537
221
|
const wantsJson = Boolean(currentCommand?.opts()?.json);
|
|
538
222
|
if (isDebug)
|
|
539
223
|
console.error(err);
|
|
540
|
-
// reportError is the single formatting path (#86 finding 9) — prints
|
|
541
|
-
// EITHER the --json envelope (stdout) OR the human "✗ message" line
|
|
542
|
-
// (stderr), never both; `quiet` skips the human line when the raw
|
|
543
|
-
// stack was already dumped above under --debug.
|
|
544
224
|
process.exitCode = reportError(err, { json: wantsJson, quiet: isDebug });
|
|
545
|
-
// Pinch confused frame (#94) — never under --json (stdout must stay
|
|
546
|
-
// pure JSON) and guarded by pinchEnabled() (NO_COLOR/non-TTY/piped).
|
|
547
|
-
// Printed to stderr, alongside reportError's own human line above.
|
|
548
225
|
if (!wantsJson && pinchEnabled()) {
|
|
549
226
|
console.error(renderPinch('confused'));
|
|
550
227
|
}
|
|
551
228
|
}
|
|
552
229
|
}
|
|
553
230
|
finally {
|
|
554
|
-
// Flush + close telemetry before the process exits. A `process.on('exit')`
|
|
555
|
-
// handler cannot reliably run async work, so this must happen here.
|
|
556
231
|
await shutdown();
|
|
557
|
-
// Passive "update available" notifier (#129) — never on a help/version
|
|
558
|
-
// query, and each half individually guarded so a notifier bug can never
|
|
559
|
-
// turn a successful command into a failure or change its exit code.
|
|
560
|
-
// #170 review F5 — never on a `spec` query either: `maybeNotifyUpdate`/
|
|
561
|
-
// `scheduleUpdateCheck` write to disk (`Library/Preferences/…/config.json`,
|
|
562
|
-
// `update-check.json`) and the latter spawns a DETACHED child that queries
|
|
563
|
-
// the npm registry — real filesystem + network side effects `isSpecQuery`'s
|
|
564
|
-
// own doc comment already promises `spec --json` never has. Before this,
|
|
565
|
-
// `spec`'s only protection was skipping the skills auto-sync above; this
|
|
566
|
-
// finally block ran unconditionally regardless.
|
|
567
232
|
if (!isHelpOrVersion(argv) && !isSpecQuery(argv)) {
|
|
568
233
|
try {
|
|
569
234
|
maybeNotifyUpdate();
|
|
570
235
|
}
|
|
571
236
|
catch {
|
|
572
|
-
// swallow — see updateNotifier.ts, this is already self-guarded too.
|
|
573
237
|
}
|
|
574
238
|
try {
|
|
575
239
|
scheduleUpdateCheck();
|
|
576
240
|
}
|
|
577
241
|
catch {
|
|
578
|
-
// swallow — background refresh must never affect this invocation.
|
|
579
242
|
}
|
|
580
|
-
// #184 — "elsewhere: suggest, never install" half of the skills
|
|
581
|
-
// bootstrap story (lib/skills.ts's bootstrapSkillsOnLogin is the
|
|
582
|
-
// "install on login" half). Only on a clean exit — mirrors the
|
|
583
|
-
// referral tip's own success-only precedent (lib/tips.ts, called only
|
|
584
|
-
// after a run genuinely succeeds) and sidesteps a whole class of
|
|
585
|
-
// parse-error edge cases where `currentCommand` never resolved (so
|
|
586
|
-
// its own `--json` flag can't be read reliably here either). Running
|
|
587
|
-
// AFTER the command completed also means a `login` that just
|
|
588
|
-
// bootstrapped is already reflected in this nudge's own "any skill
|
|
589
|
-
// installed?" read — no separate command-name exclusion needed, and
|
|
590
|
-
// no double-message risk with the line `login` may have just printed.
|
|
591
243
|
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
592
244
|
try {
|
|
593
245
|
maybeSuggestSkillsInstall({
|
|
@@ -595,7 +247,6 @@ export async function runCli(argv = process.argv) {
|
|
|
595
247
|
});
|
|
596
248
|
}
|
|
597
249
|
catch {
|
|
598
|
-
// swallow — see skillsNudge.ts, this is already self-guarded too.
|
|
599
250
|
}
|
|
600
251
|
}
|
|
601
252
|
}
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -1,102 +1,24 @@
|
|
|
1
1
|
export declare class ApiError extends Error {
|
|
2
2
|
status: number;
|
|
3
3
|
next?: string[] | undefined;
|
|
4
|
-
/**
|
|
5
|
-
* `next` is an OPTIONAL per-instance override of the envelope's default
|
|
6
|
-
* `next` steps (errors.ts's frozen `RETRY_POLICY`) — set only at the two
|
|
7
|
-
* apiKey-mode 401 call sites below (via authNextSteps()), where the
|
|
8
|
-
* generic "trawl login --token <jwt>" default is inert until a live
|
|
9
|
-
* TRAWL_API_KEY/TRAWL_TOKEN is unset first (#169 review). Absent for every
|
|
10
|
-
* other error, so classifyError falls back to the frozen default exactly
|
|
11
|
-
* as before.
|
|
12
|
-
*/
|
|
13
4
|
constructor(status: number, message: string, next?: string[] | undefined);
|
|
14
5
|
}
|
|
15
|
-
/**
|
|
16
|
-
* A fetch-level failure — the request never got a response at all (DNS,
|
|
17
|
-
* connection refused, timeout, TLS, …). Distinguished from ApiError (which
|
|
18
|
-
* always carries a real HTTP status) so the top-level handler can map it to
|
|
19
|
-
* its own exit code instead of the generic uniform 1. (#71 findings 4/58)
|
|
20
|
-
*/
|
|
21
6
|
export declare class NetworkError extends Error {
|
|
22
7
|
constructor(message: string);
|
|
23
8
|
}
|
|
24
|
-
/**
|
|
25
|
-
* A LOCAL auth failure — no token available, or a locally-decoded token
|
|
26
|
-
* that's provably expired, discovered entirely client-side before any HTTP
|
|
27
|
-
* call was ever made. Distinguished from ApiError(401) (a real server-issued
|
|
28
|
-
* 401 response) so the --json envelope never claims `status:401` for
|
|
29
|
-
* something the server never said — that would be a fabricated fact,
|
|
30
|
-
* indistinguishable from an actual server round-trip to a machine consumer.
|
|
31
|
-
* Both classify to the same exit code (3) / kind "auth" in classifyError
|
|
32
|
-
* (errors.ts); only the envelope's `status` field differs (present for
|
|
33
|
-
* ApiError, absent here). (#88 item 4)
|
|
34
|
-
*/
|
|
35
9
|
export declare class AuthError extends Error {
|
|
36
10
|
next?: string[] | undefined;
|
|
37
|
-
/** See ApiError's `next` for what this overrides and why. */
|
|
38
11
|
constructor(message: string, next?: string[] | undefined);
|
|
39
12
|
}
|
|
40
|
-
/**
|
|
41
|
-
* The single "no token available" error — every call site in this file that
|
|
42
|
-
* needs a token (request/upload/getText/stream) used to throw its own copy
|
|
43
|
-
* of `new Error('Not logged in. Run: trawl login')`, which fell through
|
|
44
|
-
* classifyError's generic branch (exit 1, kind:"unknown") — indistinguishable
|
|
45
|
-
* from an arbitrary bug. Auth-classifying it puts it on the exact same
|
|
46
|
-
* exit-3 / kind:"auth" path a real 401 response already takes — but as an
|
|
47
|
-
* AuthError (no HTTP call happened here), never a fabricated ApiError(401).
|
|
48
|
-
* `trawl token` (src/commands/token.ts) reuses this too, so "no token" means
|
|
49
|
-
* the same thing everywhere it can be observed. (#86 findings 1/2, #88 item 4)
|
|
50
|
-
*/
|
|
51
13
|
export declare function notLoggedInError(): AuthError;
|
|
52
|
-
/**
|
|
53
|
-
* The "this route is JWT-only" error — thrown client-side, before any HTTP
|
|
54
|
-
* call, by a command that only works with a session JWT (today: `whoami`,
|
|
55
|
-
* see its own doc comment for why `GET /api/users/me` was descoped rather
|
|
56
|
-
* than relocated) when the resolved credential is a scoped API key instead.
|
|
57
|
-
* Distinct from notLoggedInError(): there IS a credential here, it's just
|
|
58
|
-
* the wrong shape for this one route. Left unhandled, that route would 401
|
|
59
|
-
* and surface the generic `notLoggedInError`-adjacent message ("Session
|
|
60
|
-
* expired or invalid. Run: trawl login") — wrong twice over under a key: the
|
|
61
|
-
* route never accepts keys at all (no session ever "expired"), and
|
|
62
|
-
* re-running `trawl login` genuinely IS the fix here, just not because
|
|
63
|
-
* anything expired. Classifies to the SAME exit 3 / kind:"auth" as every
|
|
64
|
-
* other auth failure (#169) — reusing the existing `auth` kind rather than
|
|
65
|
-
* minting a new one, since the taxonomy question is still "not
|
|
66
|
-
* authenticated the way this route needs," never a new category.
|
|
67
|
-
*
|
|
68
|
-
* Always called already knowing authMode is 'apiKey' (see whoami.ts) — the
|
|
69
|
-
* message and `next` both route through loginRemedyText()/authNextSteps()
|
|
70
|
-
* so this never emits the same inert "Run: trawl login" that finding 2
|
|
71
|
-
* corrects everywhere else. (#169 review round 2 — finding 2)
|
|
72
|
-
*/
|
|
73
14
|
export declare function apiKeyUnsupportedError(command: string): AuthError;
|
|
74
|
-
/**
|
|
75
|
-
* #91 P0 — some endpoints legitimately run 30–250s server-side: a scrap
|
|
76
|
-
* execute (worker-puppeteer navigation + antibot tier escalation + AI-fix
|
|
77
|
-
* dry-run retries). The generic 30s default was aborting those mid-flight
|
|
78
|
-
* and surfacing a fabricated `NetworkError timed out` (exit 5) for a request
|
|
79
|
-
* that was always going to succeed given enough time. Passed as the per-call
|
|
80
|
-
* `{timeoutMs}` override at exactly the 4 call sites that hit those
|
|
81
|
-
* endpoints: `scraps run` / `data --fresh` (GET /api/scraps/load/:id) and
|
|
82
|
-
* `scraps trigger --wait` (POST /api/scraps/worker/:id, synchronous branch
|
|
83
|
-
* only — the default async `?wait=false` POST returns almost immediately and
|
|
84
|
-
* keeps the 30s default) — all three in src/commands/scraps.ts — plus (#114)
|
|
85
|
-
* `create` (POST /api/ai/wizard, src/commands/create.ts), whose AI-generation
|
|
86
|
-
* + scrap creation + first run + autofix pipeline runs the same 30–250s+
|
|
87
|
-
* server-side. 300s leaves margin over the ~250s worst case without being
|
|
88
|
-
* unboundedly long.
|
|
89
|
-
*/
|
|
90
15
|
export declare const LONG_RUN_TIMEOUT_MS = 300000;
|
|
91
16
|
export interface RequestOptions {
|
|
92
|
-
/** Per-call timeout override in ms (e.g. LONG_RUN_TIMEOUT_MS). Ignored — env wins — when TRAWL_TIMEOUT is set; see getTimeoutMs. */
|
|
93
17
|
timeoutMs?: number;
|
|
94
18
|
}
|
|
95
19
|
export declare const api: {
|
|
96
20
|
get: <T>(path: string, opts?: RequestOptions) => Promise<T>;
|
|
97
21
|
publicGet: <T>(path: string, opts?: RequestOptions) => Promise<T>;
|
|
98
|
-
/** See `probeJson`'s own doc comment — hard-bounded, probe-only, never for
|
|
99
|
-
* a command's real work. Today's one caller: spec.ts's fetchExternalDocsUrl. */
|
|
100
22
|
probeJson: <T>(path: string, opts: {
|
|
101
23
|
timeoutMs: number;
|
|
102
24
|
}) => Promise<T | null>;
|