@trawlme/cli 3.11.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 +5 -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 +142 -656
- 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 +31 -0
- package/dist/lib/cdp-pipe.js +141 -0
- package/dist/lib/chrome-discovery.d.ts +1 -0
- package/dist/lib/chrome-discovery.js +30 -0
- package/dist/lib/chrome-launch.d.ts +8 -0
- package/dist/lib/chrome-launch.js +53 -0
- 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 +1 -0
- package/dist/lib/secure-transport.js +15 -0
- package/dist/lib/session-capture-guard.d.ts +6 -0
- package/dist/lib/session-capture-guard.js +9 -0
- package/dist/lib/session-capture.d.ts +55 -0
- package/dist/lib/session-capture.js +319 -0
- 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 +55 -0
- package/dist/lib/storage-state.js +96 -0
- 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/skills.js
CHANGED
|
@@ -11,8 +11,6 @@ function pickSkills(arg) {
|
|
|
11
11
|
if (!arg || arg === 'all')
|
|
12
12
|
return all;
|
|
13
13
|
if (!all.includes(arg)) {
|
|
14
|
-
// Usage error (exit 2), not a generic bug (exit 1) — the caller typed a
|
|
15
|
-
// skill name that doesn't exist. (#86 finding 3)
|
|
16
14
|
throw new UsageError(`Unknown skill "${arg}". Available: ${all.join(', ') || '(none)'}`);
|
|
17
15
|
}
|
|
18
16
|
return [arg];
|
|
@@ -39,19 +37,6 @@ skills
|
|
|
39
37
|
for (const name of bundled) {
|
|
40
38
|
const userInstalled = isSkillInstalled(name, 'user');
|
|
41
39
|
const localInstalled = isSkillInstalled(name, 'local');
|
|
42
|
-
// #91 — same EISDIR class as autoUpdateInstalledSkills/installSkill: an
|
|
43
|
-
// unreadable `.version` marker on one skill must not crash the whole
|
|
44
|
-
// listing before the other skills are shown.
|
|
45
|
-
//
|
|
46
|
-
// #93 item 3 — the two scope reads must be guarded INDEPENDENTLY. A
|
|
47
|
-
// single try/catch wrapped around `getInstalledVersion(name, 'user') ??
|
|
48
|
-
// getInstalledVersion(name, 'local')` still throws the whole expression
|
|
49
|
-
// the moment the 'user' read throws (e.g. a corrupt/EISDIR `.version`
|
|
50
|
-
// marker) — `??` never gets a chance to evaluate the 'local' fallback,
|
|
51
|
-
// so a healthy local install gets masked as "not installed" too. Every
|
|
52
|
-
// other marker read in this codebase (installSkill, removeOrphanedSkills,
|
|
53
|
-
// autoUpdateInstalledSkills — all in lib/skills.ts) already guards each
|
|
54
|
-
// scope on its own; this was the one chained exception.
|
|
55
40
|
let installedVersion;
|
|
56
41
|
try {
|
|
57
42
|
installedVersion = getInstalledVersion(name, 'user');
|
|
@@ -155,13 +140,6 @@ skills
|
|
|
155
140
|
}
|
|
156
141
|
console.log(chalk.green(`✓ Updated "${name}"`) + chalk.dim(` at ${dest}`));
|
|
157
142
|
}
|
|
158
|
-
// #86 review — same orphan sweep as the startup auto-sync: an explicit
|
|
159
|
-
// `skills update` must also drop CLI-owned dirs whose skill was renamed
|
|
160
|
-
// or removed upstream (e.g. 1.0.0's `trawl` → 1.3.1's `trawl-cli`),
|
|
161
|
-
// instead of leaving a stale ghost teaching outdated usage. Only the
|
|
162
|
-
// scope being updated is swept; marker-less dirs are never touched.
|
|
163
|
-
// removeOrphanedSkills prints its own honest stderr line per removal
|
|
164
|
-
// (stderr, so it's safe under --json too).
|
|
165
143
|
const orphansRemoved = removeOrphanedSkills(scope);
|
|
166
144
|
if (opts.json)
|
|
167
145
|
json({ updated: results, orphansRemoved });
|
package/dist/commands/spec.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { type DocsUrls } from '../lib/docs.js';
|
|
3
|
-
/**
|
|
4
|
-
* `trawl spec --json` (#170) — a versioned, machine-readable description of
|
|
5
|
-
* the command tree, so an agent can learn the CLI's surface without parsing
|
|
6
|
-
* ~250 lines of README prose. Everything below is DERIVED at runtime from
|
|
7
|
-
* the live commander `Command` tree (`buildSpec`) — never a hand-maintained
|
|
8
|
-
* file, which would be a second source of truth that silently drifts. See
|
|
9
|
-
* `tests/contracts/` for the README-vs-spec cross-check that exists
|
|
10
|
-
* specifically to catch that drift.
|
|
11
|
-
*/
|
|
12
3
|
export interface CliSpecArgument {
|
|
13
4
|
name: string;
|
|
14
5
|
required: boolean;
|
|
@@ -18,106 +9,30 @@ export interface CliSpecOption {
|
|
|
18
9
|
long: string;
|
|
19
10
|
short?: string;
|
|
20
11
|
description: string;
|
|
21
|
-
/**
|
|
22
|
-
* commander's `Option#required`/`Option#mandatory` are a false-friend pair
|
|
23
|
-
* that an earlier draft of this file collapsed into one `required` key —
|
|
24
|
-
* DON'T "simplify" it back. `Option#required` means "if this option is
|
|
25
|
-
* used, a value must follow" (true for any `<value>` flag, e.g. `--limit
|
|
26
|
-
* <n>`, whether or not the option itself is optional to pass). `mandatory`
|
|
27
|
-
* means "the user MUST pass this option at all" (only true for
|
|
28
|
-
* `.requiredOption()`). Naming the value-arity field `required` (as this
|
|
29
|
-
* did before) reads as "you must specify this option" — wrong for ~30 of
|
|
30
|
-
* the ~34 options that tripped it. See commander's option.js:15/19.
|
|
31
|
-
*/
|
|
32
12
|
mandatory: boolean;
|
|
33
13
|
valueRequired: boolean;
|
|
34
14
|
negatable: boolean;
|
|
35
15
|
default?: unknown;
|
|
36
16
|
}
|
|
37
17
|
export interface CliSpecCommand {
|
|
38
|
-
/** Full path from the program root, e.g. "scraps account session set". */
|
|
39
18
|
name: string;
|
|
40
19
|
description: string;
|
|
41
20
|
hidden: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* True when this node has no subcommands of its own — i.e. it is an
|
|
44
|
-
* executable leaf (`.action()` actually runs something), not a pure
|
|
45
|
-
* namespace/group node like `scraps`/`skills`/`telemetry`/`scraps account`.
|
|
46
|
-
* An agent that emits one tool per spec entry needs this to skip group
|
|
47
|
-
* nodes: invoking one directly (e.g. `trawl scraps`) exits non-zero with
|
|
48
|
-
* usage text on stderr and ZERO bytes on stdout — a guaranteed-failing
|
|
49
|
-
* tool, not a --json envelope. Derived the same way
|
|
50
|
-
* `tests/contracts/readme-commands.test.ts` independently re-derives it
|
|
51
|
-
* (its own `leafNames()` walk existed ONLY because this field didn't) —
|
|
52
|
-
* that test now consumes THIS field instead of re-walking the tree itself.
|
|
53
|
-
*/
|
|
54
21
|
leaf: boolean;
|
|
55
22
|
aliases: string[];
|
|
56
23
|
arguments: CliSpecArgument[];
|
|
57
24
|
options: CliSpecOption[];
|
|
58
|
-
/**
|
|
59
|
-
* #185 — a deep link into a specific guide when one is mapped for this
|
|
60
|
-
* command (docs.ts's `COMMAND_DOC_PATHS`, e.g. every `scraps account *`
|
|
61
|
-
* command -> the account-sessions guide). Omitted (never a guessed/generic
|
|
62
|
-
* link) whenever no guide is mapped, the top-level `docsUrl` itself
|
|
63
|
-
* couldn't be resolved, OR `docsUrl` resolved from rung 1 (a server-
|
|
64
|
-
* declared `externalDocs.url`) rather than rung 2 (this CLI's own known-host
|
|
65
|
-
* derivation) — appending our guide slug onto a third party's own docs root
|
|
66
|
-
* is exactly the confidently-wrong-404 defect this omission closes. See
|
|
67
|
-
* `resolveCommandDocsUrl` / `DocsUrls.docsUrlIsDerived`.
|
|
68
|
-
*/
|
|
69
25
|
docs?: string;
|
|
70
26
|
}
|
|
71
27
|
export interface CliSpec {
|
|
72
28
|
specVersion: 1;
|
|
73
29
|
cliVersion: string;
|
|
74
30
|
commands: CliSpecCommand[];
|
|
75
|
-
/** "0".."5" → a short kind label. Same constant `classifyError` reads
|
|
76
|
-
* (errors.ts's EXIT_CODE_LABELS) — never a second copy. Code `1` is a
|
|
77
|
-
* SHARED bucket (several `kind`s land there, see `kindExitCodes` below
|
|
78
|
-
* for the honest reverse mapping) — this label names the generic/unmapped
|
|
79
|
-
* case, not an exhaustive claim that `1` means only that. */
|
|
80
31
|
exitCodes: Record<string, string>;
|
|
81
|
-
/** Every `kind` a --json error envelope can carry — the classifyError
|
|
82
|
-
* kinds PLUS the hand-built ones (`in_progress`/`run_failed`/
|
|
83
|
-
* `upgrade_failed`). Same constant (errors.ts's ENVELOPE_KINDS) — never a
|
|
84
|
-
* second copy, and never `ERROR_KINDS` (that one omits the hand-built
|
|
85
|
-
* kinds by design — see its doc comment). */
|
|
86
32
|
errorKinds: string[];
|
|
87
|
-
/**
|
|
88
|
-
* #170 review F7 — the inverse of `exitCodes`: `kind -> exitCode`, one
|
|
89
|
-
* entry per `errorKinds` member. `exitCodes` alone can't tell an agent
|
|
90
|
-
* which of the several kinds sharing code `1` (`api`/`refused`/`unknown`/
|
|
91
|
-
* `in_progress`/`run_failed`/`upgrade_failed`) it actually got — this
|
|
92
|
-
* field answers that directly instead of requiring the reverse lookup to
|
|
93
|
-
* be reconstructed by hand. Same source constant as `errorKinds`
|
|
94
|
-
* (errors.ts's KIND_EXIT_CODES) — never a second copy.
|
|
95
|
-
*/
|
|
96
33
|
kindExitCodes: Record<string, number>;
|
|
97
|
-
/**
|
|
98
|
-
* #185 — where the human-readable guides live, resolved via docs.ts's
|
|
99
|
-
* ladder (server `externalDocs.url` > derive from the API base's known
|
|
100
|
-
* `trawl.me` host > omit). Absent (never a guessed URL) when neither rung
|
|
101
|
-
* resolves — e.g. a self-hosted install with no `externalDocs` declared.
|
|
102
|
-
*/
|
|
103
34
|
docsUrl?: string;
|
|
104
|
-
/** #185 — the llms.txt entry point alongside `docsUrl`, when resolvable.
|
|
105
|
-
* See docs.ts's module doc comment for why this never rides along with a
|
|
106
|
-
* server-declared `docsUrl` it wasn't itself derived from. */
|
|
107
35
|
llmsUrl?: string;
|
|
108
36
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Build the full spec from a live, already-constructed program (e.g.
|
|
111
|
-
* `createProgram()`'s return value). Includes every node in the tree —
|
|
112
|
-
* hidden legacy aliases (`scraps list`, …) included, flagged via `hidden`,
|
|
113
|
-
* so a consumer that wants only the advertised surface can filter on it.
|
|
114
|
-
*
|
|
115
|
-
* #185 — `docs` is an already-RESOLVED `DocsUrls` (docs.ts's ladder run to
|
|
116
|
-
* completion), never computed in here: `buildSpec` stays pure/synchronous
|
|
117
|
-
* (no network, no config read) exactly as before, so every existing caller
|
|
118
|
-
* (the README-vs-spec contract test, the synthetic-tree unit tests) keeps
|
|
119
|
-
* working unchanged by simply omitting the parameter. Only `spec.ts`'s own
|
|
120
|
-
* action resolves rung 1 (a bounded fetch) before calling this.
|
|
121
|
-
*/
|
|
122
37
|
export declare function buildSpec(program: Command, docs?: DocsUrls): CliSpec;
|
|
123
38
|
export declare const spec: Command;
|
package/dist/commands/spec.js
CHANGED
|
@@ -37,18 +37,6 @@ function buildCommandEntry(cmd, name, hidden, docs = {}) {
|
|
|
37
37
|
entry.docs = docsLink;
|
|
38
38
|
return entry;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Walk `cmd.commands` recursively, collecting one entry per node under its
|
|
42
|
-
* FULL path name. `hidden` is read via commander's own
|
|
43
|
-
* `Help#visibleCommands()` — the same idiom this codebase already uses
|
|
44
|
-
* (scraps.test.ts's #108 describe block) — rather than reaching for the
|
|
45
|
-
* private, untyped `_hidden` field directly. The resolved `docs` object,
|
|
46
|
-
* when present, threads through so every entry can carry its own per-command
|
|
47
|
-
* `docs` deep link (#185) off the SAME resolved root — never a second lookup
|
|
48
|
-
* per node, and never flattened to a bare string (see docs.ts's
|
|
49
|
-
* `DocsUrls.docsUrlIsDerived` — the rung provenance must survive the walk
|
|
50
|
-
* unchanged so `resolveCommandDocsUrl` can still gate on it at each leaf).
|
|
51
|
-
*/
|
|
52
40
|
function walk(cmd, prefix, out, docs = {}) {
|
|
53
41
|
const visible = new Set(new Help().visibleCommands(cmd));
|
|
54
42
|
for (const sub of cmd.commands) {
|
|
@@ -57,19 +45,6 @@ function walk(cmd, prefix, out, docs = {}) {
|
|
|
57
45
|
walk(sub, name, out, docs);
|
|
58
46
|
}
|
|
59
47
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Build the full spec from a live, already-constructed program (e.g.
|
|
62
|
-
* `createProgram()`'s return value). Includes every node in the tree —
|
|
63
|
-
* hidden legacy aliases (`scraps list`, …) included, flagged via `hidden`,
|
|
64
|
-
* so a consumer that wants only the advertised surface can filter on it.
|
|
65
|
-
*
|
|
66
|
-
* #185 — `docs` is an already-RESOLVED `DocsUrls` (docs.ts's ladder run to
|
|
67
|
-
* completion), never computed in here: `buildSpec` stays pure/synchronous
|
|
68
|
-
* (no network, no config read) exactly as before, so every existing caller
|
|
69
|
-
* (the README-vs-spec contract test, the synthetic-tree unit tests) keeps
|
|
70
|
-
* working unchanged by simply omitting the parameter. Only `spec.ts`'s own
|
|
71
|
-
* action resolves rung 1 (a bounded fetch) before calling this.
|
|
72
|
-
*/
|
|
73
48
|
export function buildSpec(program, docs = {}) {
|
|
74
49
|
const commands = [];
|
|
75
50
|
walk(program, '', commands, docs);
|
|
@@ -87,40 +62,7 @@ export function buildSpec(program, docs = {}) {
|
|
|
87
62
|
spec.llmsUrl = docs.llmsUrl;
|
|
88
63
|
return spec;
|
|
89
64
|
}
|
|
90
|
-
/**
|
|
91
|
-
* #185 — bounds the SOCKET, not just the promise, so a slow/dead server can
|
|
92
|
-
* never perceptibly slow `spec --json` down. Mirrors lib/tips.ts's
|
|
93
|
-
* FLAG_CHECK_TIMEOUT_MS for the same reason: this is the one surface allowed
|
|
94
|
-
* a network call at all, and only because it's a deliberate, once-per-
|
|
95
|
-
* invocation agent probe.
|
|
96
|
-
*
|
|
97
|
-
* This is a REAL wall-clock ceiling, not merely a promise-level one: an
|
|
98
|
-
* earlier version of this fetch used `api.publicGet` (Node's global
|
|
99
|
-
* `fetch()` + `AbortSignal.timeout()`), which bounds the promise but not the
|
|
100
|
-
* OS-level connection — against a routable-but-silently-dropping host (a
|
|
101
|
-
* dropped SYN, no RST; a realistic firewalled/air-gapped shape, the very
|
|
102
|
-
* audience this feature cites) the promise rejected at ~2005ms while the
|
|
103
|
-
* PROCESS didn't exit until ~10.5s later, waiting out Node's own internal
|
|
104
|
-
* connect-timeout. `api.probeJson` (see its own doc comment in lib/api.ts)
|
|
105
|
-
* fixes this by managing the raw socket directly — an independent timer
|
|
106
|
-
* that `req.destroy()`s the connection, never relying on the idle-based
|
|
107
|
-
* `http.request` `timeout` option. Re-measured after the fix, same
|
|
108
|
-
* black-holed host, via the exact repro command (`time env
|
|
109
|
-
* TRAWL_API_URL=https://192.0.2.1 node dist/index.js spec --json`): total
|
|
110
|
-
* process time ~2.43s/~2.47s across two runs (the 2000ms bound plus Node
|
|
111
|
-
* startup and spec-tree construction), not ~10.68s.
|
|
112
|
-
*/
|
|
113
65
|
const EXTERNAL_DOCS_FETCH_TIMEOUT_MS = 2_000;
|
|
114
|
-
/**
|
|
115
|
-
* Rung 1 — read `externalDocs.url` off the OpenAPI document the configured
|
|
116
|
-
* API base itself serves at `/api/spec.json`. Unset (null) server-side
|
|
117
|
-
* today, so this resolves to `null` in practice; forward-looking for when
|
|
118
|
-
* it isn't. ANY failure — network error, timeout, a malformed/unexpected
|
|
119
|
-
* response shape — is indistinguishable from "not declared" here: resolves
|
|
120
|
-
* to `null`, never throws (api.probeJson's own contract — see its doc
|
|
121
|
-
* comment). `spec --json` must stay a pure, reliable, side-effect-free probe
|
|
122
|
-
* even against an unreachable or ancient server.
|
|
123
|
-
*/
|
|
124
66
|
async function fetchExternalDocsUrl() {
|
|
125
67
|
const data = await api.probeJson('/api/spec.json', {
|
|
126
68
|
timeoutMs: EXTERNAL_DOCS_FETCH_TIMEOUT_MS,
|
|
@@ -132,17 +74,8 @@ export const spec = new Command('spec')
|
|
|
132
74
|
.description('Print a versioned, machine-readable description of the command tree')
|
|
133
75
|
.option('--json', 'Output as JSON')
|
|
134
76
|
.action(async (opts) => {
|
|
135
|
-
// `spec` is registered as a direct child of the program root
|
|
136
|
-
// (index.ts's createProgram), so `.parent` IS that root by the time this
|
|
137
|
-
// action ever runs — commander sets it in `addCommand()`. Falling back
|
|
138
|
-
// to `spec` itself only matters for an isolated unit invocation (e.g. a
|
|
139
|
-
// test driving `spec.parseAsync()` directly, unattached to a program).
|
|
140
77
|
const root = spec.parent ?? spec;
|
|
141
78
|
const apiBaseUrl = getApiUrl();
|
|
142
|
-
// #185 — rung 1 (the server fetch) only runs under --json: the
|
|
143
|
-
// plain-text branch below never reads docsUrl at all, so paying a
|
|
144
|
-
// network round-trip for it would be pure waste. Rungs 2/3 (sync,
|
|
145
|
-
// no network) still apply either way via the fallback below.
|
|
146
79
|
const externalDocsUrl = opts.json ? await fetchExternalDocsUrl() : null;
|
|
147
80
|
const docs = resolveDocsUrls({ apiBaseUrl, externalDocsUrl });
|
|
148
81
|
const cliSpec = buildSpec(root, docs);
|
|
@@ -10,7 +10,6 @@ telemetry
|
|
|
10
10
|
.option('--json', 'Output as JSON')
|
|
11
11
|
.action((opts) => {
|
|
12
12
|
config.set('telemetry', true);
|
|
13
|
-
// Ensure a telemetryUserId is created on opt-in
|
|
14
13
|
initPostHog();
|
|
15
14
|
if (opts.json) {
|
|
16
15
|
json({ telemetry: true });
|
|
@@ -41,9 +40,6 @@ telemetry
|
|
|
41
40
|
const userId = config.get('telemetryUserId') || '(not yet generated)';
|
|
42
41
|
const envOverride = process.env['TRAWL_TELEMETRY'] === '0';
|
|
43
42
|
const doNotTrack = process.env['DO_NOT_TRACK'] === '1';
|
|
44
|
-
// The effective on/off state factoring in both env overrides — matches
|
|
45
|
-
// the human-mode "State:" line's own precedence (DO_NOT_TRACK >
|
|
46
|
-
// TRAWL_TELEMETRY > config).
|
|
47
43
|
const effectiveEnabled = enabled && !envOverride && !doNotTrack;
|
|
48
44
|
if (opts.json) {
|
|
49
45
|
json({ enabled: effectiveEnabled, configEnabled: enabled, telemetryUserId: config.get('telemetryUserId') || null, envOverride, doNotTrack });
|
package/dist/commands/token.js
CHANGED
|
@@ -9,35 +9,16 @@ export const token = new Command('token')
|
|
|
9
9
|
.description('Print the stored credential — a scoped API key or a session JWT (for MCP Bearer auth)')
|
|
10
10
|
.option('--json', 'Output as JSON ({token, exp, expiresAt, mode}) instead of the raw credential')
|
|
11
11
|
.action((opts) => {
|
|
12
|
-
// getToken() resolves TRAWL_API_KEY env first, then TRAWL_TOKEN env, then
|
|
13
|
-
// the stored config token (see config.ts) — matching every other token
|
|
14
|
-
// consumer in the CLI instead of reading the config store directly. (#86
|
|
15
|
-
// finding 1, #169)
|
|
16
12
|
const stored = getToken();
|
|
17
13
|
if (!stored) {
|
|
18
|
-
// Auth-classified (ApiError 401 → exit 3, kind:"auth"), not a generic
|
|
19
|
-
// exit 1 — an agent scripting `trawl token` needs to tell "not logged
|
|
20
|
-
// in" apart from an arbitrary bug. (#86 finding 1)
|
|
21
14
|
process.exitCode = reportError(notLoggedInError(), { json: opts.json });
|
|
22
15
|
return;
|
|
23
16
|
}
|
|
24
|
-
// #169 review finding 2 — a scoped API key is not a JWT: it has no `exp`
|
|
25
|
-
// claim to decode (decodeExp() would return null for one anyway, since
|
|
26
|
-
// it isn't dot-segmented), and unlike a session JWT it does NOT expire
|
|
27
|
-
// on its own — it stays live until revoked in the dashboard. Branching
|
|
28
|
-
// on getAuthMode() explicitly (rather than silently relying on
|
|
29
|
-
// decodeExp()'s null-for-non-JWT behaviour) means the distinct advisory
|
|
30
|
-
// below is a deliberate case, not an accident of what decodeExp() happens
|
|
31
|
-
// to return — and it replaces the old "(could not decode expiry — verify
|
|
32
|
-
// the token manually)" fallback, which was written for a malformed JWT
|
|
33
|
-
// and, read against a key, wrongly implied something was wrong with it.
|
|
34
17
|
if (getAuthMode(stored) === 'apiKey') {
|
|
35
18
|
if (opts.json) {
|
|
36
19
|
json({ token: stored, exp: null, expiresAt: null, mode: 'apiKey' });
|
|
37
20
|
return;
|
|
38
21
|
}
|
|
39
|
-
// Print the raw key first (so it can be piped / copied) — same
|
|
40
|
-
// stdout-only-the-credential contract `$(trawl token)` relies on.
|
|
41
22
|
console.log(stored);
|
|
42
23
|
console.error(chalk.dim(' This is a scoped API key (trawl_*) — it does not expire on its own. Revoke it in the dashboard to end its access.'));
|
|
43
24
|
return;
|
|
@@ -45,9 +26,6 @@ export const token = new Command('token')
|
|
|
45
26
|
const exp = decodeExp(stored);
|
|
46
27
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
47
28
|
if (exp !== null && exp < nowSeconds) {
|
|
48
|
-
// Decoded entirely client-side (no HTTP call made) — AuthError, not a
|
|
49
|
-
// fabricated ApiError(401): the server never actually said this. (#88
|
|
50
|
-
// item 4)
|
|
51
29
|
process.exitCode = reportError(new AuthError('Session token expired. Run: trawl login to refresh.'), { json: opts.json });
|
|
52
30
|
return;
|
|
53
31
|
}
|
|
@@ -60,10 +38,8 @@ export const token = new Command('token')
|
|
|
60
38
|
});
|
|
61
39
|
return;
|
|
62
40
|
}
|
|
63
|
-
// Print the raw token first (so it can be piped / copied)
|
|
64
41
|
console.log(stored);
|
|
65
42
|
if (exp === null) {
|
|
66
|
-
// Could not decode expiry (malformed/opaque JWT) — advisory only, do not block piping
|
|
67
43
|
console.error(chalk.dim(' (could not decode expiry — verify the token manually)'));
|
|
68
44
|
}
|
|
69
45
|
else {
|
|
@@ -71,10 +47,6 @@ export const token = new Command('token')
|
|
|
71
47
|
const daysLeft = secsLeft / 86400;
|
|
72
48
|
if (daysLeft < 1) {
|
|
73
49
|
const hoursLeft = Math.floor(secsLeft / 3600);
|
|
74
|
-
// stderr, not stdout — this is an advisory, not the payload. `trawl
|
|
75
|
-
// token` exists so callers can capture the raw JWT via
|
|
76
|
-
// `$(trawl token)` for `Authorization: Bearer …`; anything printed
|
|
77
|
-
// to stdout after the token corrupts that capture. (#68)
|
|
78
50
|
console.error(chalk.yellow(`⚠ Token expiring in ${hoursLeft}h. Run: trawl login to refresh.`));
|
|
79
51
|
}
|
|
80
52
|
else {
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -21,21 +21,12 @@ export const upgrade = new Command('upgrade')
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
if (opts.check) {
|
|
24
|
-
// #170 review F10 — set BEFORE the --json early return. Before this,
|
|
25
|
-
// `trawl upgrade --check` exited 1 but `trawl upgrade --check --json`
|
|
26
|
-
// (identical state) exited 0 — the exit code was set below the --json
|
|
27
|
-
// `return`, so a --json caller never saw it. README documents this gate
|
|
28
|
-
// as "exit 1 if so" with no --json carve-out; a CI step gating on the
|
|
29
|
-
// exit code alone silently never fired under --json.
|
|
30
24
|
process.exitCode = 1;
|
|
31
25
|
if (opts.json)
|
|
32
26
|
return json({ package: PKG_NAME, current, latest, upToDate: false, upgraded: false });
|
|
33
27
|
console.log(`${chalk.yellow('↑')} Update available: ${chalk.bold(current)} → ${chalk.bold(latest)}. Run ${chalk.cyan('trawl upgrade')} to install.`);
|
|
34
28
|
return;
|
|
35
29
|
}
|
|
36
|
-
// Install the latest globally. This shells out to the same npm the user
|
|
37
|
-
// installed the CLI with; a permission error (EACCES on a system-owned
|
|
38
|
-
// global prefix) is surfaced with the manual command rather than swallowed.
|
|
39
30
|
if (!opts.json)
|
|
40
31
|
console.log(chalk.dim(`Upgrading ${PKG_NAME}: ${current} → ${latest}…`));
|
|
41
32
|
try {
|
|
@@ -49,19 +40,6 @@ export const upgrade = new Command('upgrade')
|
|
|
49
40
|
? 'permission denied on the global npm prefix — retry with sudo, or use a Node version manager.'
|
|
50
41
|
: (e.stderr?.trim() || e.message);
|
|
51
42
|
if (opts.json) {
|
|
52
|
-
// `upgrade_failed` is intentionally NOT in errors.ts's RETRY_POLICY
|
|
53
|
-
// (that map is the exhaustive set of kinds classifyError can
|
|
54
|
-
// produce — ERROR_KINDS derives from its keys) — retryFieldsFor is
|
|
55
|
-
// total, so an unmapped kind still gets the honest conservative
|
|
56
|
-
// default (`retryable:false`) instead of silently omitting the
|
|
57
|
-
// field. It IS registered in ENVELOPE_KINDS, the superset
|
|
58
|
-
// `spec --json`'s errorKinds actually publishes — see that
|
|
59
|
-
// constant's doc comment.
|
|
60
|
-
//
|
|
61
|
-
// #170 review F3 — typed as ErrorEnvelope so the compiler enforces
|
|
62
|
-
// `retryable` here too (this was one of the two emitters a
|
|
63
|
-
// mutation-test proved `tsc --noEmit` never actually guarded before
|
|
64
|
-
// this annotation).
|
|
65
43
|
const envelope = {
|
|
66
44
|
message: `upgrade failed: ${hint}`,
|
|
67
45
|
kind: 'upgrade_failed',
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
/**
|
|
3
|
-
* `GET /api/users/me` response shape (users.account.controller.js#me).
|
|
4
|
-
*
|
|
5
|
-
* Chosen over the MCP `trawl_whoami` tool's own payload
|
|
6
|
-
* (userId/organizationId/organizationName/scrapIdsAllowlist/mode) because
|
|
7
|
-
* that shape lives entirely in the MCP per-call auth-bridge context
|
|
8
|
-
* (developers.mcp.js) — there is no equivalent REST route exposing
|
|
9
|
-
* organization/scope/mode for a session-JWT caller today. `/api/users/me`
|
|
10
|
-
* is the closest REST parity match: real identity, already used by every
|
|
11
|
-
* other trawl_node client. `roles` is the nearest analogue to "scopes";
|
|
12
|
-
* there is currently no "plan" field on this endpoint.
|
|
13
|
-
*/
|
|
14
2
|
export interface WhoamiResponse {
|
|
15
3
|
id?: string;
|
|
16
4
|
email?: string;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -7,12 +7,6 @@ export const whoami = new Command('whoami')
|
|
|
7
7
|
.description("Show the authenticated user's identity")
|
|
8
8
|
.option('--json', 'Output as JSON')
|
|
9
9
|
.action(async (opts) => {
|
|
10
|
-
// #169 — JWT-only route (see WhoamiResponse's own doc comment above for
|
|
11
|
-
// why it stays that way). Under a scoped API key, a real request here
|
|
12
|
-
// would 401 and surface the generic "Session expired or invalid" text —
|
|
13
|
-
// wrong twice over: this route never accepts keys at all, and no
|
|
14
|
-
// session ever "expired". Detect it client-side, before any HTTP call,
|
|
15
|
-
// and say so plainly instead.
|
|
16
10
|
if (getAuthMode() === 'apiKey') {
|
|
17
11
|
throw apiKeyUnsupportedError('trawl whoami');
|
|
18
12
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,203 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
/**
|
|
4
|
-
* Derive a safe telemetry event name from a *resolved* commander Command —
|
|
5
|
-
* NEVER from raw argv. Flag values (e.g. the string after --password/--email/
|
|
6
|
-
* --url) don't start with '-' and would otherwise survive an argv filter and
|
|
7
|
-
* leak to PostHog (#67). Falls back to 'unknown' when no command resolved
|
|
8
|
-
* (e.g. an error thrown before any action ran).
|
|
9
|
-
*
|
|
10
|
-
* #88 item 9 — walks the FULL parent chain, not just the immediate parent:
|
|
11
|
-
* the old one-level join (`${parent.name()} ${own.name()}`) resolved a
|
|
12
|
-
* 4-deep command like `scraps account session set` down to just "session
|
|
13
|
-
* set", silently dropping "scraps account". The root program node (the
|
|
14
|
-
* 'trawl' Command itself, which has no `.parent`) is excluded from the
|
|
15
|
-
* chain — matching the pre-existing convention that a direct child of the
|
|
16
|
-
* root (e.g. `scraps list`, `telemetry on`) is named relative to its
|
|
17
|
-
* immediate group, never prefixed with the program name.
|
|
18
|
-
*
|
|
19
|
-
* #108 note: promoting a verb to a top-level command (see `createProgram`
|
|
20
|
-
* below) renamed ITS resolved telemetry name from `scraps <verb>` to
|
|
21
|
-
* `<verb>` — the canonical top-level attach and the legacy hidden
|
|
22
|
-
* `scraps <verb>` attach are two separate Command instances (scraps.ts's
|
|
23
|
-
* double-attach factories), each with its own parent chain, so they
|
|
24
|
-
* resolve to two different names here even though they run the same
|
|
25
|
-
* handler. Intentional (the canonical command IS now `<verb>`), not a bug.
|
|
26
|
-
*/
|
|
27
3
|
export declare function resolveCommandName(actionCommand: Command | undefined): string;
|
|
28
|
-
/**
|
|
29
|
-
* Walk the full command tree and produce every valid resolveCommandName()
|
|
30
|
-
* token — the allowlist registered with posthog.ts so captureCommand can
|
|
31
|
-
* never be handed a free-form string.
|
|
32
|
-
*/
|
|
33
4
|
export declare function collectCommandNames(root: Command): string[];
|
|
34
5
|
export declare function createProgram(): Command;
|
|
35
|
-
/**
|
|
36
|
-
* #185 — see the `addHelpText('afterAll', …)` call above for why this is
|
|
37
|
-
* unconditional (no TTY/--json gate) and why a thrown error must never
|
|
38
|
-
* escape: commander calls this synchronously while already writing help
|
|
39
|
-
* output, so a throw here would crash a `--help` invocation, the one code
|
|
40
|
-
* path this whole feature is not allowed to touch (see docs.ts's own
|
|
41
|
-
* `docsFooterLine` for the "never a guessed URL" contract this composes).
|
|
42
|
-
*/
|
|
43
6
|
export declare function docsFooterText(): string;
|
|
44
|
-
/**
|
|
45
|
-
* True when this module is the process entrypoint (not merely imported by a test).
|
|
46
|
-
*
|
|
47
|
-
* npm installs the global bin as a SYMLINK (`bin/trawl` -> `dist/index.js`). For an
|
|
48
|
-
* ESM entrypoint Node realpaths the module URL (`import.meta.url` = the real
|
|
49
|
-
* `dist/index.js`) but leaves `process.argv[1]` as the symlink path — so a raw
|
|
50
|
-
* `moduleUrl === pathToFileURL(argv1).href` compare is FALSE for the normal global
|
|
51
|
-
* invocation, the guard never fires, and `runCli()` never runs (silent no-op, #103).
|
|
52
|
-
* Canonicalise both sides with `realpathSync` before comparing. A genuine entrypoint
|
|
53
|
-
* was just loaded by Node, so both realpath calls resolve; the catch only trips for a
|
|
54
|
-
* non-file module URL (exotic loaders) — correctly "not the entrypoint".
|
|
55
|
-
*/
|
|
56
7
|
export declare function isEntryPoint(argv1: string | undefined, moduleUrl: string): boolean;
|
|
57
|
-
/**
|
|
58
|
-
* True when the invocation is a pure `--help`/`--version` query, a bare
|
|
59
|
-
* `trawl` with no subcommand (commander prints top-level help and exits), or
|
|
60
|
-
* `trawl help [command]`. None of these should trigger the skills auto-sync
|
|
61
|
-
* (a filesystem-mutating startup side effect) — a user running `trawl
|
|
62
|
-
* --version` (or just `trawl`) never expects it to rewrite their skills
|
|
63
|
-
* dirs. (#73, extended #86 finding 7 for the bare-invocation + `help`
|
|
64
|
-
* subcommand cases)
|
|
65
|
-
*/
|
|
66
8
|
export declare function isHelpOrVersion(argv: string[]): boolean;
|
|
67
|
-
/**
|
|
68
|
-
* True for a bare `trawl` invocation — no subcommand, no flags at all. This
|
|
69
|
-
* is the CLI's "first thing a new user sees" moment (commander prints its
|
|
70
|
-
* own top-level help right after), distinct from `isHelpOrVersion` which is
|
|
71
|
-
* intentionally broader (also matches `--help`/`--version`/`trawl help`
|
|
72
|
-
* anywhere in argv) — the Pinch wave banner (#94) only wants the narrowest
|
|
73
|
-
* case so it never shows up ahead of e.g. `trawl scraps --help`.
|
|
74
|
-
*/
|
|
75
9
|
export declare function isBareInvocation(argv: string[]): boolean;
|
|
76
|
-
/**
|
|
77
|
-
* True for `trawl spec` (with or without `--json`) — deliberately NOT folded
|
|
78
|
-
* into `isHelpOrVersion` above: that predicate's name and doc comment are
|
|
79
|
-
* about help/version queries specifically, and `spec` is a real, data-
|
|
80
|
-
* bearing command (see `isHelpOrVersion`'s own #170 test case), not a help
|
|
81
|
-
* query. This is its own narrow predicate for a DIFFERENT reason: `spec
|
|
82
|
-
* --json` is documented (docs/agent-quickstart.md) as the first call an AI
|
|
83
|
-
* agent makes against this CLI, so it must not mutate anything the caller
|
|
84
|
-
* did not ask it to — same requirement `--help`/`--version` already got
|
|
85
|
-
* from `isHelpOrVersion` (#73), extended here to cover `spec` too.
|
|
86
|
-
*
|
|
87
|
-
* Scope, stated honestly rather than aspirationally: this guard suppresses
|
|
88
|
-
* the skills auto-sync (an `rmSync(recursive)` + `cpSync` over the user's
|
|
89
|
-
* `~/.claude/skills`) and the update notifier (a config write plus a
|
|
90
|
-
* DETACHED CHILD that queries the npm registry — fatal in an
|
|
91
|
-
* egress-restricted sandbox). It does NOT suppress `initPostHog()`, which
|
|
92
|
-
* still creates the `conf` config file and mints a persistent
|
|
93
|
-
* `telemetryUserId` on first run, exactly as it does for every other
|
|
94
|
-
* command. That one is deliberate: `spec` is the signal that tells us
|
|
95
|
-
* whether agents are actually adopting the CLI, so it stays measured, and
|
|
96
|
-
* the caller's own controls (`TRAWL_TELEMETRY=0`, `DO_NOT_TRACK=1`) are the
|
|
97
|
-
* opt-out. That same first run also has `initPostHog()` write a one-time
|
|
98
|
-
* telemetry disclosure line (`ℹ Trawl CLI collects anonymous usage
|
|
99
|
-
* telemetry…`) to STDERR, ahead of the JSON payload — this guard does not
|
|
100
|
-
* suppress that either. `stdout` stays pure JSON either way, so this only
|
|
101
|
-
* bites a caller that merges the two streams (e.g. `2>&1`); whether a `spec`
|
|
102
|
-
* probe specifically should suppress the notice is a product call,
|
|
103
|
-
* deliberately not taken here. Do not upgrade this paragraph back to "no
|
|
104
|
-
* filesystem side effect" without also gating telemetry — a comment that
|
|
105
|
-
* overstates its own invariant is worse than no comment, because the next
|
|
106
|
-
* reader trusts it.
|
|
107
|
-
*
|
|
108
|
-
* #185 — `spec --json` (not plain `spec`) now DOES make its own bounded
|
|
109
|
-
* (2s), swallowed-on-failure GET for `externalDocs.url` (see spec.ts's
|
|
110
|
-
* `fetchExternalDocsUrl`) — this is not a regression of the "no mutation"
|
|
111
|
-
* invariant above (a GET mutates nothing, and any failure — including no
|
|
112
|
-
* egress at all — degrades to the sync host-derivation fallback, never a
|
|
113
|
-
* thrown error), just a second, narrower kind of side effect this guard was
|
|
114
|
-
* never meant to suppress in the first place.
|
|
115
|
-
*
|
|
116
|
-
* Before this, `trawl spec --json` silently re-synced
|
|
117
|
-
* `~/.claude/skills` (and `./.claude/skills`) and printed `trawl: re-synced
|
|
118
|
-
* skill …` lines to stderr BEFORE the JSON payload — an agent's very first
|
|
119
|
-
* probe of the CLI mutated the user's filesystem and polluted the channel
|
|
120
|
-
* it was about to parse.
|
|
121
|
-
*
|
|
122
|
-
* Matches `spec` as the FIRST positional token, skipping any leading
|
|
123
|
-
* `-`-prefixed tokens first (a global flag ahead of the subcommand, e.g.
|
|
124
|
-
* `trawl --debug spec --json`) — the same argv-scan discipline `hasJsonFlag`
|
|
125
|
-
* below already applies. #170 review F4 — before this, a bare
|
|
126
|
-
* `argv.slice(2)[0] === 'spec'` check was defeated by ANY leading flag:
|
|
127
|
-
* `trawl --debug spec --json` read `--debug` as the first positional, missed
|
|
128
|
-
* the match entirely, and fell through to the normal startup path — running
|
|
129
|
-
* the filesystem-mutating skills auto-sync during what's documented
|
|
130
|
-
* (docs/agent-quickstart.md) as an agent's read-only first probe of the CLI.
|
|
131
|
-
* Still never a bare substring search: e.g. `trawl scraps create --title
|
|
132
|
-
* spec` (the literal string "spec" as a FLAG VALUE, not a leading flag) never
|
|
133
|
-
* matches, since `scraps` — the first non-`-`-prefixed token — isn't `spec`.
|
|
134
|
-
*/
|
|
135
10
|
export declare function isSpecQuery(argv: string[]): boolean;
|
|
136
|
-
/** Best-effort scan for a `--json` flag in raw argv, used only when parsing
|
|
137
|
-
* itself failed before any command's own `.opts()` could be resolved (a
|
|
138
|
-
* commander usage error — unknown option/command, missing required arg). Same
|
|
139
|
-
* "argv scan, never trust flag values" caveat as isHelpOrVersion: positional
|
|
140
|
-
* values are never mistaken for `--json` since they don't equal the literal
|
|
141
|
-
* string. (#86 finding 3)
|
|
142
|
-
*
|
|
143
|
-
* #88 item 5 — only scans tokens BEFORE the first bare `--`. Commander treats
|
|
144
|
-
* `--` as "end of options": everything after it is a positional operand, not
|
|
145
|
-
* a flag, even if the literal text is `--json`. `scraps list -- --json`
|
|
146
|
-
* passes `--json` as an (excess) positional argument, not the flag — an
|
|
147
|
-
* unscoped `argv.includes('--json')` would still match it and wrongly emit a
|
|
148
|
-
* JSON envelope for what is actually a plain usage error with no --json
|
|
149
|
-
* requested at all.
|
|
150
|
-
*/
|
|
151
11
|
export declare function hasJsonFlag(argv: string[]): boolean;
|
|
152
|
-
/**
|
|
153
|
-
* Commander's default (no exitOverride) calls `process.exit()` directly for
|
|
154
|
-
* a usage error (unknown option/command, missing required arg) or a
|
|
155
|
-
* --help/--version/`help` query — bypassing runCli's try/catch/finally
|
|
156
|
-
* entirely, so the telemetry shutdown() flush below never runs and a usage
|
|
157
|
-
* error exits 1 (the generic bug bucket) instead of its own distinct code.
|
|
158
|
-
* `program.exitOverride()` on the root command alone does NOT fix this for
|
|
159
|
-
* subcommands added via `addCommand()` (login/scraps/skills/telemetry/token
|
|
160
|
-
* are each built as standalone Command instances in their own module and
|
|
161
|
-
* only ever copy inherited settings — including exitOverride — from a parent
|
|
162
|
-
* at `.command()` construction time, which for these root-level modules never
|
|
163
|
-
* happens). Every node in the tree needs its own exitOverride() call, so this
|
|
164
|
-
* walks the whole tree and installs it everywhere. (#86 finding 3)
|
|
165
|
-
*/
|
|
166
12
|
export declare function applyExitOverride(cmd: Command): void;
|
|
167
|
-
/**
|
|
168
|
-
* #149 item 1 — commander's own usage-error line (unknown option, missing
|
|
169
|
-
* required arg, unknown command, …) is written via `Command#error()` ->
|
|
170
|
-
* `outputError()` straight to `process.stderr` BEFORE the exitOverride throw
|
|
171
|
-
* below is ever caught — in commander's own plain, uncolored `"error: …"`
|
|
172
|
-
* format, a visibly different convention from every other error this CLI
|
|
173
|
-
* prints (`reportError`'s `chalk.red('✗ ' + message)`). `configureOutput`'s
|
|
174
|
-
* `outputError` hook is exactly the one commander documents for reformatting
|
|
175
|
-
* the error text itself (`writeErr` also carries unrelated help output, e.g.
|
|
176
|
-
* `showHelpAfterError` — never enabled in this CLI, but out of scope to
|
|
177
|
-
* touch here). Overriding it on every node in the tree (same walk shape as
|
|
178
|
-
* applyExitOverride, for the same reason: addCommand()-attached subtrees
|
|
179
|
-
* don't otherwise inherit a parent's configureOutput) reformats that one line
|
|
180
|
-
* through the same red ✗ convention, stripping the redundant "error: " prefix
|
|
181
|
-
* first (stripCommanderErrorPrefix — the same prefix also leaks verbatim
|
|
182
|
-
* into the --json envelope's `message` field, see runCli's catch below).
|
|
183
|
-
*/
|
|
184
13
|
export declare function applyOutputConfiguration(cmd: Command): void;
|
|
185
|
-
/**
|
|
186
|
-
* #88 item 6 — best-effort command-name recovery for a commander parse error
|
|
187
|
-
* that happened BEFORE any action ran, so `currentCommand` (the preAction
|
|
188
|
-
* hook's resolved command) is still undefined — e.g. an unknown option on an
|
|
189
|
-
* otherwise-valid subcommand, or excess arguments. Without this, EVERY such
|
|
190
|
-
* failure previously collapsed to resolveCommandName(undefined) === 'unknown'
|
|
191
|
-
* — and since 'unknown' was never itself allowlisted at the
|
|
192
|
-
* registerAllowedCommands call site (see runCli below), that capture was
|
|
193
|
-
* silently dropped by captureCommand's allowlist check: a dead branch that
|
|
194
|
-
* looked like it reported telemetry but never actually did.
|
|
195
|
-
*
|
|
196
|
-
* Matches ONLY a name already present in `allowedNames` (the real command
|
|
197
|
-
* tree, from collectCommandNames) — never invents one from raw argv text.
|
|
198
|
-
* Checks the two-token form first (`scraps boom`) since most usage errors
|
|
199
|
-
* happen on a nested leaf command; falls back to the single top-level token,
|
|
200
|
-
* then to 'unknown' (now itself allowlisted, so that capture fires too).
|
|
201
|
-
*/
|
|
202
14
|
export declare function bestEffortCommandName(argv: string[], allowedNames: readonly string[]): string;
|
|
203
15
|
export declare function runCli(argv?: string[]): Promise<void>;
|