@trawlme/cli 3.12.0 → 3.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/create.d.ts +0 -28
- package/dist/commands/create.js +0 -89
- package/dist/commands/doctor.d.ts +0 -79
- package/dist/commands/doctor.js +1 -187
- package/dist/commands/login.js +0 -67
- package/dist/commands/ping.d.ts +0 -15
- package/dist/commands/ping.js +0 -15
- package/dist/commands/scraps.d.ts +0 -120
- package/dist/commands/scraps.js +10 -724
- package/dist/commands/skills.js +0 -22
- package/dist/commands/spec.d.ts +0 -85
- package/dist/commands/spec.js +0 -67
- package/dist/commands/telemetry.js +0 -4
- package/dist/commands/token.js +0 -28
- package/dist/commands/upgrade.js +0 -22
- package/dist/commands/whoami.d.ts +0 -12
- package/dist/commands/whoami.js +0 -6
- package/dist/index.d.ts +0 -188
- package/dist/index.js +0 -349
- package/dist/lib/api.d.ts +0 -78
- package/dist/lib/api.js +1 -320
- package/dist/lib/cdp-pipe.d.ts +0 -72
- package/dist/lib/cdp-pipe.js +1 -81
- package/dist/lib/chrome-discovery.d.ts +0 -11
- package/dist/lib/chrome-discovery.js +0 -19
- package/dist/lib/chrome-launch.d.ts +0 -40
- package/dist/lib/chrome-launch.js +0 -69
- package/dist/lib/config.d.ts +0 -53
- package/dist/lib/config.js +0 -55
- package/dist/lib/confirm.d.ts +0 -55
- package/dist/lib/confirm.js +0 -47
- package/dist/lib/docs.d.ts +0 -123
- package/dist/lib/docs.js +0 -169
- package/dist/lib/errors.d.ts +0 -134
- package/dist/lib/errors.js +0 -151
- package/dist/lib/format.d.ts +0 -6
- package/dist/lib/format.js +0 -6
- package/dist/lib/json.d.ts +0 -35
- package/dist/lib/json.js +0 -48
- package/dist/lib/jwt.d.ts +0 -7
- package/dist/lib/jwt.js +0 -7
- package/dist/lib/pinch.d.ts +0 -53
- package/dist/lib/pinch.js +6 -112
- package/dist/lib/pinchAnimation.d.ts +0 -16
- package/dist/lib/pinchAnimation.js +8 -29
- package/dist/lib/posthog.d.ts +0 -9
- package/dist/lib/posthog.js +0 -23
- package/dist/lib/prompt.js +1 -20
- package/dist/lib/secure-transport.d.ts +0 -7
- package/dist/lib/secure-transport.js +0 -24
- package/dist/lib/session-capture-guard.d.ts +0 -15
- package/dist/lib/session-capture-guard.js +0 -5
- package/dist/lib/session-capture.d.ts +0 -125
- package/dist/lib/session-capture.js +0 -281
- package/dist/lib/skills.d.ts +0 -175
- package/dist/lib/skills.js +1 -216
- package/dist/lib/skillsNudge.d.ts +0 -17
- package/dist/lib/skillsNudge.js +0 -83
- package/dist/lib/spinner.d.ts +0 -39
- package/dist/lib/spinner.js +0 -40
- package/dist/lib/storage-state.d.ts +0 -112
- package/dist/lib/storage-state.js +0 -131
- package/dist/lib/tips.d.ts +0 -38
- package/dist/lib/tips.js +0 -77
- package/dist/lib/updateCheckWorker.js +0 -14
- package/dist/lib/updateNotifier.d.ts +0 -17
- package/dist/lib/updateNotifier.js +0 -53
- package/dist/lib/validate.d.ts +0 -8
- package/dist/lib/validate.js +0 -8
- package/dist/lib/version.d.ts +0 -12
- package/dist/lib/version.js +1 -13
- package/package.json +2 -2
package/dist/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],
|
|
36
|
-
O: [0xfb, 0x92, 0x3c],
|
|
37
|
-
W: [0xff, 0xff, 0xff],
|
|
38
|
-
K: [0x12, 0x30, 0x33],
|
|
39
|
-
P: [0xf4, 0x72, 0xb6],
|
|
40
|
-
C: [0x06, 0xb6, 0xd4],
|
|
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 }),
|
|
26
|
-
renderPinchArt('working', { compact: true, frame: 1 }),
|
|
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;
|
|
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');
|
|
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);
|
|
38
|
+
cursorUp(totalHeight);
|
|
60
39
|
for (let l = 0; l < totalHeight; l++)
|
|
61
|
-
out.write('\x1b[2K\n');
|
|
62
|
-
cursorUp(totalHeight);
|
|
63
|
-
out.write('\x1b[?25h');
|
|
40
|
+
out.write('\x1b[2K\n');
|
|
41
|
+
cursorUp(totalHeight);
|
|
42
|
+
out.write('\x1b[?25h');
|
|
64
43
|
},
|
|
65
44
|
};
|
|
66
45
|
}
|
package/dist/lib/posthog.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/posthog.js
CHANGED
|
@@ -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;
|
package/dist/lib/prompt.js
CHANGED
|
@@ -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;
|
|
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.';
|