@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.
Files changed (74) hide show
  1. package/README.md +2 -2
  2. package/dist/commands/create.d.ts +0 -28
  3. package/dist/commands/create.js +0 -89
  4. package/dist/commands/doctor.d.ts +0 -79
  5. package/dist/commands/doctor.js +1 -187
  6. package/dist/commands/login.js +0 -67
  7. package/dist/commands/ping.d.ts +0 -15
  8. package/dist/commands/ping.js +0 -15
  9. package/dist/commands/scraps.d.ts +0 -120
  10. package/dist/commands/scraps.js +10 -724
  11. package/dist/commands/skills.js +0 -22
  12. package/dist/commands/spec.d.ts +0 -85
  13. package/dist/commands/spec.js +0 -67
  14. package/dist/commands/telemetry.js +0 -4
  15. package/dist/commands/token.js +0 -28
  16. package/dist/commands/upgrade.js +0 -22
  17. package/dist/commands/whoami.d.ts +0 -12
  18. package/dist/commands/whoami.js +0 -6
  19. package/dist/index.d.ts +0 -188
  20. package/dist/index.js +0 -349
  21. package/dist/lib/api.d.ts +0 -78
  22. package/dist/lib/api.js +1 -320
  23. package/dist/lib/cdp-pipe.d.ts +0 -72
  24. package/dist/lib/cdp-pipe.js +1 -81
  25. package/dist/lib/chrome-discovery.d.ts +0 -11
  26. package/dist/lib/chrome-discovery.js +0 -19
  27. package/dist/lib/chrome-launch.d.ts +0 -40
  28. package/dist/lib/chrome-launch.js +0 -69
  29. package/dist/lib/config.d.ts +0 -53
  30. package/dist/lib/config.js +0 -55
  31. package/dist/lib/confirm.d.ts +0 -55
  32. package/dist/lib/confirm.js +0 -47
  33. package/dist/lib/docs.d.ts +0 -123
  34. package/dist/lib/docs.js +0 -169
  35. package/dist/lib/errors.d.ts +0 -134
  36. package/dist/lib/errors.js +0 -151
  37. package/dist/lib/format.d.ts +0 -6
  38. package/dist/lib/format.js +0 -6
  39. package/dist/lib/json.d.ts +0 -35
  40. package/dist/lib/json.js +0 -48
  41. package/dist/lib/jwt.d.ts +0 -7
  42. package/dist/lib/jwt.js +0 -7
  43. package/dist/lib/pinch.d.ts +0 -53
  44. package/dist/lib/pinch.js +6 -112
  45. package/dist/lib/pinchAnimation.d.ts +0 -16
  46. package/dist/lib/pinchAnimation.js +8 -29
  47. package/dist/lib/posthog.d.ts +0 -9
  48. package/dist/lib/posthog.js +0 -23
  49. package/dist/lib/prompt.js +1 -20
  50. package/dist/lib/secure-transport.d.ts +0 -7
  51. package/dist/lib/secure-transport.js +0 -24
  52. package/dist/lib/session-capture-guard.d.ts +0 -15
  53. package/dist/lib/session-capture-guard.js +0 -5
  54. package/dist/lib/session-capture.d.ts +0 -125
  55. package/dist/lib/session-capture.js +0 -281
  56. package/dist/lib/skills.d.ts +0 -175
  57. package/dist/lib/skills.js +1 -216
  58. package/dist/lib/skillsNudge.d.ts +0 -17
  59. package/dist/lib/skillsNudge.js +0 -83
  60. package/dist/lib/spinner.d.ts +0 -39
  61. package/dist/lib/spinner.js +0 -40
  62. package/dist/lib/storage-state.d.ts +0 -112
  63. package/dist/lib/storage-state.js +0 -131
  64. package/dist/lib/tips.d.ts +0 -38
  65. package/dist/lib/tips.js +0 -77
  66. package/dist/lib/updateCheckWorker.js +0 -14
  67. package/dist/lib/updateNotifier.d.ts +0 -17
  68. package/dist/lib/updateNotifier.js +0 -53
  69. package/dist/lib/validate.d.ts +0 -8
  70. package/dist/lib/validate.js +0 -8
  71. package/dist/lib/version.d.ts +0 -12
  72. package/dist/lib/version.js +1 -13
  73. package/docs/agent-quickstart.md +2 -2
  74. package/package.json +2 -2
package/dist/lib/pinch.js CHANGED
@@ -1,46 +1,12 @@
1
- /**
2
- * Pinch — Trawl's voxel lobster mascot, rendered as 24-bit ANSI half-block
3
- * art. Distinct from Clawd's 8-bit lane: Pinch is drawn with full 24-bit
4
- * (`\x1b[38;2;r;g;bm` / `\x1b[48;2;r;g;bm`) color blocks, not a fixed palette.
5
- *
6
- * Each grid is packed two rows into one terminal row: the upper row's color
7
- * becomes the half-block's foreground, the lower row's becomes its
8
- * background, using the upper-half-block glyph '▀' (or '▄' when only the
9
- * lower half is filled). '.' cells are transparent — no color escape is
10
- * emitted for that half, so the terminal's own background shows through.
11
- *
12
- * The grids are the founder-approved 2D front avatar (`AVATAR_GRIDS`, rev
13
- * r6f) from trawl_vue `src/modules/trawl/assets/mascot/_src/pinch.model.mjs`,
14
- * ported VERBATIM (no cross-repo import — cli is a standalone npm package).
15
- * Each is 14 cols × 8 rows (the source's 9th all-'.' pad row is dropped) →
16
- * renders in 4 terminal rows, 14 columns wide. This is the mascot "de face":
17
- * antennae on top, 2-wide white eyes + navy pupils, pink cheek blush, a
18
- * U-smile (corners + a 2-wide dropped center), and the signature 2-WIDE
19
- * orange claws on the body flanks (cols 0-1 / 12-13).
20
- *
21
- * History: #133 shrank this to a 9×6 mini (1-wide claws, a single-pixel
22
- * mouth) — #141 reverted to this faithful 14×9 founder art after it read as
23
- * "trop loin du 3D".
24
- *
25
- * State parity (#135): the CLI carries all 9 canonical AVATAR_STATES with the
26
- * source's own names — idle · working · celebrating · thumbsup · confused ·
27
- * wave · heart · sleepy · shocked.
28
- *
29
- * See comes-io/trawl_cli#94, #122, #135, #141.
30
- */
31
- /** Grid-char → RGB, ported from AVCOLORS (pinch.model.mjs, rev r6f). 'C'
32
- * (cyan) is the `working` thinking-blip — not present in any static grid,
33
- * injected per-frame on the antenna tips. */
34
1
  const PALETTE = {
35
- B: [0x29, 0x79, 0xff], // blue — shell
36
- O: [0xfb, 0x92, 0x3c], // orange — antennae / claws
37
- W: [0xff, 0xff, 0xff], // white — eye whites
38
- K: [0x12, 0x30, 0x33], // navy — pupils / mouth / blush-adjacent mouth corner
39
- P: [0xf4, 0x72, 0xb6], // pink — blush / heart-eyes
40
- C: [0x06, 0xb6, 0xd4], // cyan — `working` thinking-blip on the antenna tips
2
+ B: [0x29, 0x79, 0xff],
3
+ O: [0xfb, 0x92, 0x3c],
4
+ W: [0xff, 0xff, 0xff],
5
+ K: [0x12, 0x30, 0x33],
6
+ P: [0xf4, 0x72, 0xb6],
7
+ C: [0x06, 0xb6, 0xd4],
41
8
  };
42
9
  const TRANSPARENT = '.';
43
- /** All 9 canonical states, in the source model's AVATAR_STATES order. */
44
10
  export const PINCH_STATES = [
45
11
  'idle',
46
12
  'working',
@@ -52,21 +18,6 @@ export const PINCH_STATES = [
52
18
  'sleepy',
53
19
  'shocked',
54
20
  ];
55
- /**
56
- * The founder 2D front avatar grids (`AVATAR_GRIDS`, pinch.model.mjs rev
57
- * r6f), ported VERBATIM — rows 0-7 of each 9-row grid (the trailing all-'.'
58
- * pad row 8 is dropped; it only exists to square the SVG canvas). 14 cols,
59
- * 8 rows → 4 terminal lines. Layout:
60
- * rows 0-1 antennae (2 orange, drooped/spread for confused & sleepy)
61
- * row 2 head top (+ claw at cols 0-1/12-13 when RAISED)
62
- * rows 3-4 eyes: 2-wide W whites (row 3) + K pupils (row 4)
63
- * row 5 head mid
64
- * rows 6-7 mouth + blush + the 2-WIDE claws on the flanks
65
- * The claws are 2×2 O blocks OUTSIDE the body silhouette (cols 0-1 / 12-13),
66
- * raised to rows 3-4 for celebrating / shocked (and the right claw for
67
- * wave / thumbsup). The smile is a U: corners K on row 6, a 2-wide center
68
- * KK on row 7.
69
- */
70
21
  const GRIDS = {
71
22
  idle: [
72
23
  '....O....O....',
@@ -78,8 +29,6 @@ const GRIDS = {
78
29
  'OOBBPKBBKPBBOO',
79
30
  'OOBBBBKKBBBBOO',
80
31
  ],
81
- // idle pose + the cyan thinking-blip on the antenna tips (frame-toggled in
82
- // `gridForState`) — the only animated static state.
83
32
  working: [
84
33
  '....O....O....',
85
34
  '....O....O....',
@@ -90,7 +39,6 @@ const GRIDS = {
90
39
  'OOBBPKBBKPBBOO',
91
40
  'OOBBBBKKBBBBOO',
92
41
  ],
93
- // both claws raised to rows 3-4 + a big open mouth (rows 6-7).
94
42
  celebrating: [
95
43
  '....O....O....',
96
44
  '....O....O....',
@@ -101,8 +49,6 @@ const GRIDS = {
101
49
  '..BBPKKKKPBB..',
102
50
  '..BBBKKKKBBB..',
103
51
  ],
104
- // right claw raised (rows 3-4) + a thumb pixel poking up (row 2, col 12);
105
- // left claw resting on the flank (rows 6-7).
106
52
  thumbsup: [
107
53
  '....O....O....',
108
54
  '....O....O....',
@@ -113,8 +59,6 @@ const GRIDS = {
113
59
  'OOBBPKBBKPBB..',
114
60
  'OOBBBBKKBBBB..',
115
61
  ],
116
- // antennae drooped + spread (row 1, cols 3 & 10), flat mouth (blush only, no
117
- // smile K on row 6), claws resting. A '?' is appended by `renderPinch`.
118
62
  confused: [
119
63
  '..............',
120
64
  '...O......O...',
@@ -125,7 +69,6 @@ const GRIDS = {
125
69
  'OOBBPBBBBPBBOO',
126
70
  'OOBBBBKKBBBBOO',
127
71
  ],
128
- // right claw raised (rows 3-4), left claw resting; base face.
129
72
  wave: [
130
73
  '....O....O....',
131
74
  '....O....O....',
@@ -136,8 +79,6 @@ const GRIDS = {
136
79
  'OOBBPKBBKPBB..',
137
80
  'OOBBBBKKBBBB..',
138
81
  ],
139
- // pink heart-eyes (P fills both eye blocks, rows 3-4), claws resting, normal
140
- // smile.
141
82
  heart: [
142
83
  '....O....O....',
143
84
  '....O....O....',
@@ -148,8 +89,6 @@ const GRIDS = {
148
89
  'OOBBPKBBKPBBOO',
149
90
  'OOBBBBKKBBBBOO',
150
91
  ],
151
- // closed eyes (flat K line on row 4, no white), antennae drooped + spread,
152
- // flat mouth, claws resting — like confused but eyes shut and no '?'.
153
92
  sleepy: [
154
93
  '..............',
155
94
  '...O......O...',
@@ -160,9 +99,6 @@ const GRIDS = {
160
99
  'OOBBPBBBBPBBOO',
161
100
  'OOBBBBKKBBBBOO',
162
101
  ],
163
- // pupils up a row (row 3 carries K, row 4 all white), both claws raised
164
- // (rows 3-4), a small round mouth (row 6 only) — startled, not the wide
165
- // celebrating grin.
166
102
  shocked: [
167
103
  '....O....O....',
168
104
  '....O....O....',
@@ -185,19 +121,13 @@ const CAPTIONS = {
185
121
  sleepy: 'Pinch is sleepy…',
186
122
  shocked: 'Pinch is shocked!',
187
123
  };
188
- /** Antenna-tip cells (grid row 0) for the `working` cyan blip: cols 4 & 9. */
189
124
  const ANTENNA_TIP_COLS = [4, 9];
190
- /** Replace the chars at `indices` in `row` with `ch` — never mutates `row`. */
191
125
  function setCells(row, indices, ch) {
192
126
  const chars = row.split('');
193
127
  for (const i of indices)
194
128
  chars[i] = ch;
195
129
  return chars.join('');
196
130
  }
197
- /** Look up the grid for `state`. `frame` only affects `working`: on even
198
- * frames its antenna tips blip cyan (the thinking indicator), on odd frames
199
- * they stay orange — a 2-frame pulse with no layout shift. Every other
200
- * state's grid is static and ignores `frame`. */
201
131
  function gridForState(state, frame) {
202
132
  if (state === 'working' && frame % 2 === 0) {
203
133
  const rows = [...GRIDS.working];
@@ -209,29 +139,17 @@ function gridForState(state, frame) {
209
139
  const RESET = '\x1b[0m';
210
140
  const fgCode = ([r, g, b]) => `\x1b[38;2;${r};${g};${b}m`;
211
141
  const bgCode = ([r, g, b]) => `\x1b[48;2;${r};${g};${b}m`;
212
- /**
213
- * One terminal column: combine an upper (fg-half) + lower (bg-half) grid
214
- * cell into a single half-block character. Either half may be transparent
215
- * independently — no bg/fg escape is emitted for a transparent half, so the
216
- * terminal's own background/foreground shows through it.
217
- */
218
142
  function renderCell(upper, lower) {
219
143
  const upperColor = upper === TRANSPARENT ? null : PALETTE[upper];
220
144
  const lowerColor = lower === TRANSPARENT ? null : PALETTE[lower];
221
145
  if (!upperColor && !lowerColor)
222
146
  return ' ';
223
- // Lower half transparent — draw the upper half-block with fg only, no bg
224
- // (the glyph's own unpainted lower half already shows the terminal bg).
225
147
  if (!lowerColor)
226
148
  return `${fgCode(upperColor)}▀${RESET}`;
227
- // Upper half transparent — draw the LOWER half-block glyph instead, with
228
- // fg set to the lower color and no bg, so the painted half is the bottom.
229
149
  if (!upperColor)
230
150
  return `${fgCode(lowerColor)}▄${RESET}`;
231
151
  return `${fgCode(upperColor)}${bgCode(lowerColor)}▀${RESET}`;
232
152
  }
233
- /** Pack a grid into terminal lines, 2 grid rows per line (upper=fg, lower=bg).
234
- * The 14×8 founder grid is 8 rows → 4 lines. */
235
153
  function renderGrid(rows) {
236
154
  const lines = [];
237
155
  for (let i = 0; i < rows.length; i += 2) {
@@ -245,32 +163,14 @@ function renderGrid(rows) {
245
163
  }
246
164
  return lines;
247
165
  }
248
- /**
249
- * Render Pinch as 24-bit ANSI half-block art for the given state, plus a
250
- * one-line caption. Pure — never touches process.env/stdout; callers must
251
- * gate on `pinchEnabled()` before printing the result.
252
- *
253
- * `frame` only affects `working` (the cyan antenna-tip blip alternates every
254
- * frame); every other state's grid is static.
255
- */
256
166
  export function renderPinch(state, frame = 0) {
257
167
  const grid = gridForState(state, frame);
258
168
  const lines = renderGrid(grid);
259
169
  if (state === 'confused') {
260
- // "beside the art" — a bold '?' to the right of the eye row. The eyes are
261
- // grid rows 2-3 (head-top + eye-whites), which pack into terminal line 1.
262
170
  lines[1] = `${lines[1]} \x1b[1m?${RESET}`;
263
171
  }
264
172
  return [...lines, CAPTIONS[state]].join('\n');
265
173
  }
266
- /**
267
- * #131 — the ART lines only (no caption), optionally COMPACT: trailing and
268
- * leading fully-transparent grid rows are dropped so Pinch takes fewer
269
- * terminal lines (confused/sleepy carry an all-'.' leading row). `frame` is
270
- * passed through to `gridForState` — only `working` reacts to it (the cyan
271
- * antenna blip toggles), so the loop can pulse the tips while the body stays
272
- * still.
273
- */
274
174
  export function renderPinchArt(state, opts = {}) {
275
175
  let grid = gridForState(state, opts.frame ?? 0);
276
176
  if (opts.compact) {
@@ -288,12 +188,6 @@ export function renderPinchArt(state, opts = {}) {
288
188
  lines[1] = `${lines[1]} \x1b[1m?${RESET}`;
289
189
  return lines.join('\n');
290
190
  }
291
- /**
292
- * True when it's safe to print Pinch art: a real color-capable interactive
293
- * terminal. False under NO_COLOR (https://no-color.org — presence, not
294
- * value, disables color output), a non-TTY stdout (piped/redirected output —
295
- * covers --json/--quiet/CI log capture), or TERM=dumb.
296
- */
297
191
  export function pinchEnabled() {
298
192
  if (process.env['NO_COLOR'] !== undefined)
299
193
  return false;
@@ -1,20 +1,4 @@
1
1
  export interface PinchAnimation {
2
- /** Stop the loop and erase the whole animation block, leaving the cursor at
3
- * its top-left so the caller prints its result on a clean line. Idempotent. */
4
2
  stop(): void;
5
3
  }
6
- /**
7
- * #131 / #139 — show Pinch IN PLACE on STDERR while a long operation runs (the
8
- * `create` wizard, `--watch`). Pinch stays a STILL `working` sprite; the ONLY
9
- * animation is the cyan antenna-tip blip pulsing on/off every {@link FRAME_MS}
10
- * (frame 0 = cyan, frame 1 = orange). The body — mouth, claws, eyes — never
11
- * moves (the old loop cycled whole poses, which flickered "in every
12
- * direction"). Redrawn via ANSI cursor moves; `stop()` clears the block.
13
- *
14
- * Contract: the CALLER must have already checked `pinchEnabled()` (TTY /
15
- * !NO_COLOR) and that the command is NOT `--json` — this writes only to
16
- * STDERR, so stdout stays pure regardless, but the animation is human-only.
17
- * The interval is `unref()`'d so it can never keep the process alive on its
18
- * own.
19
- */
20
4
  export declare function startPinchAnimation(statusText: string): PinchAnimation;
@@ -1,32 +1,13 @@
1
1
  import chalk from 'chalk';
2
2
  import { renderPinchArt } from './pinch.js';
3
- /** Frame cadence — a calm, ~2/second pulse. The only thing that moves is the
4
- * cyan antenna blip, so this is intentionally slow: a fast rate on a 2-pixel
5
- * color change reads as a nervous flicker. */
6
3
  const FRAME_MS = 600;
7
- /**
8
- * #131 / #139 — show Pinch IN PLACE on STDERR while a long operation runs (the
9
- * `create` wizard, `--watch`). Pinch stays a STILL `working` sprite; the ONLY
10
- * animation is the cyan antenna-tip blip pulsing on/off every {@link FRAME_MS}
11
- * (frame 0 = cyan, frame 1 = orange). The body — mouth, claws, eyes — never
12
- * moves (the old loop cycled whole poses, which flickered "in every
13
- * direction"). Redrawn via ANSI cursor moves; `stop()` clears the block.
14
- *
15
- * Contract: the CALLER must have already checked `pinchEnabled()` (TTY /
16
- * !NO_COLOR) and that the command is NOT `--json` — this writes only to
17
- * STDERR, so stdout stays pure regardless, but the animation is human-only.
18
- * The interval is `unref()`'d so it can never keep the process alive on its
19
- * own.
20
- */
21
4
  export function startPinchAnimation(statusText) {
22
- // Two frames of the SAME `working` sprite — identical geometry, the antenna
23
- // tips just toggle cyan↔orange. Nothing else changes between frames.
24
5
  const frames = [
25
- renderPinchArt('working', { compact: true, frame: 0 }), // cyan tips
26
- renderPinchArt('working', { compact: true, frame: 1 }), // orange tips
6
+ renderPinchArt('working', { compact: true, frame: 0 }),
7
+ renderPinchArt('working', { compact: true, frame: 1 }),
27
8
  ];
28
9
  const artHeight = frames[0].split('\n').length;
29
- const totalHeight = artHeight + 1; // + the status line below the art
10
+ const totalHeight = artHeight + 1;
30
11
  const out = process.stderr;
31
12
  let i = 0;
32
13
  const block = (frame) => `${frame}\n${chalk.dim(statusText)}\n`;
@@ -34,13 +15,11 @@ export function startPinchAnimation(statusText) {
34
15
  if (n > 0)
35
16
  out.write(`\x1b[${n}A`);
36
17
  };
37
- // Redraw a frame over the previous one — clear each line to EOL first so a
38
- // narrower frame can't leave trailing pixels from a wider one.
39
18
  const redraw = (frame) => {
40
19
  const lines = `${frame}\n${chalk.dim(statusText)}`.split('\n');
41
20
  out.write(lines.map((l) => `\x1b[2K${l}`).join('\n') + '\n');
42
21
  };
43
- out.write('\x1b[?25l'); // hide cursor for a flicker-free redraw
22
+ out.write('\x1b[?25l');
44
23
  out.write(block(frames[0]));
45
24
  const timer = setInterval(() => {
46
25
  i += 1;
@@ -56,11 +35,11 @@ export function startPinchAnimation(statusText) {
56
35
  return;
57
36
  stopped = true;
58
37
  clearInterval(timer);
59
- cursorUp(totalHeight); // back to the top of the block
38
+ cursorUp(totalHeight);
60
39
  for (let l = 0; l < totalHeight; l++)
61
- out.write('\x1b[2K\n'); // erase each line
62
- cursorUp(totalHeight); // back to top so the caller's next print lands here
63
- out.write('\x1b[?25h'); // restore the cursor
40
+ out.write('\x1b[2K\n');
41
+ cursorUp(totalHeight);
42
+ out.write('\x1b[?25h');
64
43
  },
65
44
  };
66
45
  }
@@ -1,14 +1,5 @@
1
1
  export declare const initPostHog: () => void;
2
- /**
3
- * Register the complete set of valid "<parent> <name>" telemetry tokens,
4
- * derived from the actual commander command tree (see resolveCommandName /
5
- * collectCommandNames in src/index.ts). captureCommand refuses any command
6
- * string outside this set — defense in depth so free-form user input (argv
7
- * operands, flag values) can never reach PostHog as the event name, even if a
8
- * future change accidentally reintroduces argv-derived naming.
9
- */
10
2
  export declare const registerAllowedCommands: (names: readonly string[]) => void;
11
3
  export declare const captureCommand: (command: string, props?: Record<string, unknown>) => void;
12
4
  export declare const shutdown: () => Promise<void>;
13
- /** Reset singleton state — for testing only */
14
5
  export declare const _resetForTesting: () => void;
@@ -6,28 +6,17 @@ import { readFileSync } from 'node:fs';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { dirname, resolve } from 'node:path';
8
8
  import config from './config.js';
9
- // public-by-design — cf memory feedback_public_keys_in_config.md
10
9
  const POSTHOG_KEY = 'phc_pGgSLivbq8rDZzXC9trWWJG7sBjNghXeTJjETzBcnEDv';
11
10
  const POSTHOG_HOST = 'https://eu.i.posthog.com';
12
- // Fail fast: a firewalled/offline endpoint must never add tens of seconds of
13
- // trailing latency to a CLI command. Library defaults are requestTimeout 10s x
14
- // 3 retries at a 3s delay (~40s+ worst case per call), and a 30s shutdown
15
- // ceiling on top of that — cap all three low.
16
11
  const REQUEST_TIMEOUT_MS = 3000;
17
12
  const FETCH_RETRY_COUNT = 1;
18
13
  const FETCH_RETRY_DELAY_MS = 250;
19
- // A touch above one request + one retry, so the shutdown-time flush has room
20
- // to complete normally without inheriting the library's 30s default ceiling.
21
14
  const SHUTDOWN_TIMEOUT_MS = 8000;
22
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
16
  const pkg = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf8'));
24
17
  let client = null;
25
18
  let distinctId = null;
26
19
  let allowedCommands = new Set();
27
- /**
28
- * Cross-vendor opt-out (https://consoledonottrack.com) on top of the existing
29
- * config/env gates. Any one of these disables telemetry entirely.
30
- */
31
20
  function isTelemetryDisabled() {
32
21
  return (config.get('telemetry') === false ||
33
22
  process.env['TRAWL_TELEMETRY'] === '0' ||
@@ -39,7 +28,6 @@ export const initPostHog = () => {
39
28
  const isFirstRun = !config.get('telemetryUserId');
40
29
  if (isFirstRun) {
41
30
  config.set('telemetryUserId', `cli_${crypto.randomUUID()}`);
42
- // One-time disclosure — telemetry is on by default and otherwise silent.
43
31
  console.error(chalk.dim('ℹ Trawl CLI collects anonymous usage telemetry (no credentials or command arguments). ' +
44
32
  'Opt out: trawl telemetry off, TRAWL_TELEMETRY=0, or DO_NOT_TRACK=1.'));
45
33
  }
@@ -53,14 +41,6 @@ export const initPostHog = () => {
53
41
  fetchRetryDelay: FETCH_RETRY_DELAY_MS,
54
42
  });
55
43
  };
56
- /**
57
- * Register the complete set of valid "<parent> <name>" telemetry tokens,
58
- * derived from the actual commander command tree (see resolveCommandName /
59
- * collectCommandNames in src/index.ts). captureCommand refuses any command
60
- * string outside this set — defense in depth so free-form user input (argv
61
- * operands, flag values) can never reach PostHog as the event name, even if a
62
- * future change accidentally reintroduces argv-derived naming.
63
- */
64
44
  export const registerAllowedCommands = (names) => {
65
45
  allowedCommands = new Set(names);
66
46
  };
@@ -83,12 +63,9 @@ export const captureCommand = (command, props = {}) => {
83
63
  });
84
64
  };
85
65
  export const shutdown = async () => {
86
- // Explicit ceiling — client.shutdown() otherwise defaults to a 30s internal
87
- // race, which would undercut the whole point of capping request/retry time.
88
66
  if (client)
89
67
  await client.shutdown(SHUTDOWN_TIMEOUT_MS);
90
68
  };
91
- /** Reset singleton state — for testing only */
92
69
  export const _resetForTesting = () => {
93
70
  client = null;
94
71
  distinctId = null;
@@ -12,27 +12,19 @@ export async function promptPassword(prompt) {
12
12
  process.stdin.removeListener('data', onData);
13
13
  };
14
14
  const onData = (chunk) => {
15
- // A stdin chunk is NOT a single keystroke. Terminals deliver a paste as
16
- // one chunk (Windows Terminal always chunk-pastes), and a paste that
17
- // ends in a newline embeds a literal \r mid-chunk. Iterate per code
18
- // point so an embedded Enter finalizes the password instead of being
19
- // appended to it and hanging for a second Enter. (#73)
20
15
  for (const char of chunk) {
21
16
  if (char === '\r' || char === '\n') {
22
17
  restore();
23
18
  process.stderr.write('\n');
24
19
  resolve(password);
25
- return; // ignore anything after the first terminator
20
+ return;
26
21
  }
27
22
  if (char === '') {
28
- // Ctrl+C: restore the terminal and terminate the line before
29
- // exiting so the shell prompt isn't left mid-line. 130 = 128+SIGINT.
30
23
  restore();
31
24
  process.stderr.write('\n');
32
25
  process.exit(130);
33
26
  }
34
27
  if (char === '' || char === '\b') {
35
- // Backspace
36
28
  password = password.slice(0, -1);
37
29
  }
38
30
  else {
@@ -43,17 +35,6 @@ export async function promptPassword(prompt) {
43
35
  process.stdin.on('data', onData);
44
36
  }
45
37
  else {
46
- // Non-TTY fallback (e.g. piped input in CI). If stdin closes (EOF)
47
- // before an answer arrives — e.g. `trawl login < /dev/null` — the
48
- // `question` callback never fires; without a `close` handler the
49
- // promise hangs forever and the process exits 0 without logging in.
50
- // Reject loudly instead. (#68)
51
- //
52
- // No `output` is wired to the interface: readline echoes typed input
53
- // whenever its output is a TTY, so `trawl login < creds` run from a real
54
- // terminal would print the password. Omitting output (and terminal:false)
55
- // guarantees the secret is never echoed. The prompt itself was already
56
- // written to stderr above. (#73)
57
38
  import('readline').then(({ createInterface }) => {
58
39
  const rl = createInterface({ input: process.stdin, terminal: false });
59
40
  let answered = false;
@@ -1,8 +1 @@
1
- /**
2
- * @desc Returns a factual reason string when `apiUrl` is not an
3
- * acceptable transport for a session upload, or null when it is
4
- * (`https:`, or any scheme against a loopback host). An unparseable URL
5
- * returns null — it fails with its own clear error at the actual fetch
6
- * call, not here.
7
- */
8
1
  export declare function assertSecureTransport(apiUrl: string): string | null;
@@ -1,28 +1,4 @@
1
- /**
2
- * Refuses a plaintext API base for the two commands that ship a browser
3
- * session — a bearer-equivalent secret — in the request body
4
- * (trawl_cli#183 review finding 5). Reproduced: `TRAWL_API_URL=http://…
5
- * trawl scraps account session capture <id>` uploaded the full session in
6
- * cleartext with no warning; this command is the first in the CLI to PUT
7
- * that kind of secret, so the fix is scoped HERE rather than to the
8
- * generic `request()` in api.ts — every other command's blast radius
9
- * stays exactly what it was.
10
- *
11
- * Loopback hosts are exempt (by exact hostname only — a plaintext host
12
- * that merely happens to RESOLVE to loopback is not detected here) so
13
- * local/self-hosted dev and the test suite keep working over plain HTTP.
14
- */
15
- // `new URL(...).hostname` returns the IPv6 form WITH its brackets
16
- // (`'[::1]'`, never bare `'::1'`) — both are listed defensively so this
17
- // never regresses silently if that ever changes.
18
1
  const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', '[::1]', 'localhost']);
19
- /**
20
- * @desc Returns a factual reason string when `apiUrl` is not an
21
- * acceptable transport for a session upload, or null when it is
22
- * (`https:`, or any scheme against a loopback host). An unparseable URL
23
- * returns null — it fails with its own clear error at the actual fetch
24
- * call, not here.
25
- */
26
2
  export function assertSecureTransport(apiUrl) {
27
3
  let url;
28
4
  try {
@@ -1,21 +1,6 @@
1
- /**
2
- * The non-interactive guard for `scraps account session capture`
3
- * (trawl_cli#183) — deliberately its OWN function, not a reuse of
4
- * `confirm.ts`'s `isInteractive`. That one returns false under `--json`,
5
- * which is wrong here: `--json` + a real interactive terminal is a valid
6
- * way to run this command (a human still drives the browser; only the
7
- * final result on stdout is machine-shaped). What actually makes this
8
- * command impossible is no TTY on stdin (nobody can press Enter) or, on
9
- * Linux, no display server to open a visible window on.
10
- */
11
1
  export interface NonInteractiveEnv {
12
2
  stdinIsTTY: boolean;
13
3
  platform: NodeJS.Platform;
14
4
  env: NodeJS.ProcessEnv;
15
5
  }
16
- /**
17
- * @desc Returns a factual reason string when this command cannot work in
18
- * the current environment, or null when it can proceed. Never guesses —
19
- * both checks are direct, observable facts (no TTY / no display var).
20
- */
21
6
  export declare function detectNonInteractive({ stdinIsTTY, platform, env }: NonInteractiveEnv): string | null;
@@ -1,8 +1,3 @@
1
- /**
2
- * @desc Returns a factual reason string when this command cannot work in
3
- * the current environment, or null when it can proceed. Never guesses —
4
- * both checks are direct, observable facts (no TTY / no display var).
5
- */
6
1
  export function detectNonInteractive({ stdinIsTTY, platform, env }) {
7
2
  if (!stdinIsTTY) {
8
3
  return 'No interactive terminal attached (stdin is not a TTY) — this command opens a visible Chrome window for a human to log into and needs a terminal to confirm completion. It does not work headless, in CI, or piped.';