@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
@@ -1,54 +1,20 @@
1
1
  import chalk from 'chalk';
2
2
  import { ApiError, AuthError, NetworkError } from './api.js';
3
- /**
4
- * Thrown for CLI usage / input-validation failures (bad flag value, malformed
5
- * JSON, invalid ObjectId, missing required prompt input, …). Distinguished
6
- * from ApiError/NetworkError so the top-level handler can map it to its own
7
- * exit code (2) instead of the generic uniform 1 every other bug collapses
8
- * into. (#71)
9
- */
10
3
  export class UsageError extends Error {
11
4
  constructor(message) {
12
5
  super(message);
13
6
  this.name = 'UsageError';
14
7
  }
15
8
  }
16
- /**
17
- * Thrown for a business-logic REFUSAL the server explicitly reported back
18
- * (e.g. a tier-ceiling override the registry cap rejected) — distinct from
19
- * an arbitrary unmapped bug. Before this, `reportTierRefusal` routed a bare
20
- * `new Error(message)` through here, which fell through to the generic
21
- * `kind:"unknown"` bucket — indistinguishable from a genuine crash, even
22
- * though the README sells `kind` as the machine discriminant an agent
23
- * branches on. Same exit code (1: a business-logic refusal, not a usage
24
- * error) as before — only the `kind` differs. (#107 review F3)
25
- */
26
9
  export class RefusalError extends Error {
27
10
  constructor(message) {
28
11
  super(message);
29
12
  this.name = 'RefusalError';
30
13
  }
31
14
  }
32
- /**
33
- * Commander prefixes every one of its own usage-error messages with the
34
- * literal `"error: "` (see `missingArgument`/`unknownOption`/`unknownCommand`
35
- * etc. in commander's `command.js`) — harmless for its own plain default
36
- * line, but redundant once this CLI reuses that message itself: (1) baked
37
- * into a `--json` envelope's `message` field, a human-facing "error: " prefix
38
- * is dead weight for a machine parser that already reads `kind:"usage"`; (2)
39
- * reformatted through the app's own `chalk.red('✗ ' + message)` convention it
40
- * would double up as "✗ error: unknown option …". Idempotent — a message
41
- * that never had the prefix passes through unchanged. (#149 items 1/2)
42
- */
43
15
  export function stripCommanderErrorPrefix(message) {
44
16
  return message.startsWith('error: ') ? message.slice('error: '.length) : message;
45
17
  }
46
- /**
47
- * The exit-code taxonomy (#71 findings 13/14/60), named once so
48
- * `classifyError` below and `spec.ts`'s `buildSpec()` (#170) read the SAME
49
- * numbers instead of `spec.ts` hand-listing its own copy — exactly the kind
50
- * of second source of truth `tests/contracts/` exists to catch drifting.
51
- */
52
18
  export const EXIT_CODES = Object.freeze({
53
19
  SUCCESS: 0,
54
20
  UNKNOWN: 1,
@@ -57,9 +23,6 @@ export const EXIT_CODES = Object.freeze({
57
23
  NOT_FOUND: 4,
58
24
  NETWORK: 5,
59
25
  });
60
- /** `trawl spec --json`'s `exitCodes` field — a short label per code. Code
61
- * `1` is shared by several `kind`s (`api`, `refused`, `unknown`); the label
62
- * names the generic/unmapped bucket it represents, not an exhaustive list. */
63
26
  export const EXIT_CODE_LABELS = Object.freeze({
64
27
  [EXIT_CODES.SUCCESS]: 'success',
65
28
  [EXIT_CODES.UNKNOWN]: 'unknown',
@@ -68,26 +31,6 @@ export const EXIT_CODE_LABELS = Object.freeze({
68
31
  [EXIT_CODES.NOT_FOUND]: 'not_found',
69
32
  [EXIT_CODES.NETWORK]: 'network',
70
33
  });
71
- /**
72
- * #170 — ONE frozen map, keyed by the existing `kind`, driving the
73
- * envelope's new `retryable`/`next` fields. This is also the exhaustive set
74
- * of `kind`s `classifyError` can produce — `ERROR_KINDS` below derives its
75
- * list from these keys rather than hand-listing them a second time, and
76
- * `spec.ts`'s `buildSpec()` reads `ERROR_KINDS`, never its own copy.
77
- *
78
- * `network` is the one kind worth retrying unchanged. `auth` isn't
79
- * retryable but has an honest next step (`trawl login --token <jwt>`).
80
- * Everything else (`usage`/`not_found`/`api`/`refused`/`unknown`) is neither
81
- * — retrying a bad flag, a missing resource, or an unmapped bug with no new
82
- * information just repeats the same failure.
83
- *
84
- * #170 review F8 — `next` must be an EXECUTABLE next command, not just a verb
85
- * name: bare `trawl login` still blocks on an interactive prompt (email then
86
- * password) — for a non-interactive caller (stdin closed, the exact
87
- * situation an auth failure implies) it fails immediately with its OWN usage
88
- * error instead of the login the agent was told to run. `trawl login --token
89
- * <jwt>` is the one form of `login` that never prompts.
90
- */
91
34
  export const RETRY_POLICY = Object.freeze({
92
35
  auth: { retryable: false, next: ['trawl login --token <jwt>'] },
93
36
  network: { retryable: true },
@@ -97,60 +40,13 @@ export const RETRY_POLICY = Object.freeze({
97
40
  refused: { retryable: false },
98
41
  unknown: { retryable: false },
99
42
  });
100
- /** Every `kind` string `classifyError` can produce — derived from
101
- * `RETRY_POLICY`'s keys (see its doc comment), never a second hand list. */
102
43
  export const ERROR_KINDS = Object.freeze(Object.keys(RETRY_POLICY));
103
- /**
104
- * Every `kind` a real `--json` error envelope can carry — NOT the same set
105
- * as `ERROR_KINDS`. `ERROR_KINDS` is deliberately narrow (exactly what
106
- * `classifyError` produces, see its own doc comment — `RETRY_POLICY` must
107
- * keep meaning that, `retryFieldsFor` relies on it), but three more kinds
108
- * reach a real envelope from hand-built emitters that never go through
109
- * `classifyError` at all:
110
- * - `in_progress` — `src/commands/scraps.ts`, `data <id>`'s `reportDataState`
111
- * call for a run still in flight
112
- * - `run_failed` — same file, same helper, for a last run that failed
113
- * - `upgrade_failed` — `src/commands/upgrade.ts`, when `npm install -g` itself fails
114
- *
115
- * `spec.ts`'s `errorKinds` field publishes THIS constant, never
116
- * `ERROR_KINDS` — an agent that builds its allow-list from the published
117
- * spec must not reject a perfectly valid `{"error":{"kind":"in_progress",…}}`
118
- * envelope just because it was hand-built instead of classified. Built as a
119
- * union (spread `ERROR_KINDS` + list the hand-built kinds) rather than a
120
- * second hand-copy of the first group, so the classifyError kinds are
121
- * PROVABLY a subset — see errors.test.ts's exhaustiveness check.
122
- *
123
- * Adding a new hand-built `kind:` literal anywhere in `src/` means
124
- * registering it here too, or the published spec will lie about it exactly
125
- * like this constant exists to prevent.
126
- */
127
44
  export const ENVELOPE_KINDS = Object.freeze([
128
45
  ...ERROR_KINDS,
129
46
  'in_progress',
130
47
  'run_failed',
131
48
  'upgrade_failed',
132
49
  ]);
133
- /**
134
- * #170 review F7 — `spec --json`'s flat `exitCodes` map (`EXIT_CODE_LABELS`)
135
- * publishes ONE label per code, which is honest about code `1` being a
136
- * generic/unmapped bucket but says nothing about which `kind`s actually land
137
- * there. An agent building an `exitCode -> kind` table off `exitCodes` alone
138
- * reads `"1":"unknown"` and treats every other kind sharing that code
139
- * (`api`/`refused`/`in_progress`/`run_failed`/`upgrade_failed`) as an
140
- * unmapped bug it should give up on — instead of honouring the very
141
- * `retryable`/`next` fields those envelopes correctly carry.
142
- *
143
- * This is the inverse direction, `kind -> exitCode`, one entry per
144
- * `ENVELOPE_KINDS` member — additive (published as a SIBLING field,
145
- * `kindExitCodes`, never replacing `exitCodes`) and keyed off the same
146
- * `EXIT_CODES` constants every real call site already uses, so a future exit
147
- * code change here can't silently drift from `classifyError`/
148
- * `reportDataState`/`upgrade.ts`'s actual behaviour without also changing the
149
- * single source those all draw from. errors.test.ts asserts every entry here
150
- * against classifyError's REAL returned exitCode for that kind — the closest
151
- * an exhaustive hand-map can get to "derived", short of `classifyError`
152
- * itself being rewritten to loop over a shared table (out of scope here).
153
- */
154
50
  export const KIND_EXIT_CODES = Object.freeze({
155
51
  auth: EXIT_CODES.AUTH,
156
52
  network: EXIT_CODES.NETWORK,
@@ -159,51 +55,18 @@ export const KIND_EXIT_CODES = Object.freeze({
159
55
  api: EXIT_CODES.UNKNOWN,
160
56
  refused: EXIT_CODES.UNKNOWN,
161
57
  unknown: EXIT_CODES.UNKNOWN,
162
- // Hand-built kinds (never routed through classifyError) — in_progress/
163
- // run_failed both set via reportDataState's literal `1` (scraps.ts),
164
- // upgrade_failed via its own literal `process.exitCode = 1` (upgrade.ts).
165
58
  in_progress: EXIT_CODES.UNKNOWN,
166
59
  run_failed: EXIT_CODES.UNKNOWN,
167
60
  upgrade_failed: EXIT_CODES.UNKNOWN,
168
61
  });
169
- /**
170
- * Look up the frozen default `retryable`/`next` for a `kind`. Total (never
171
- * throws) — a `kind` outside `RETRY_POLICY` (e.g. `scraps data`'s own
172
- * `run_failed`/`in_progress` states, which aren't part of `classifyError`'s
173
- * taxonomy) falls back to the conservative "not retryable, nothing to
174
- * suggest" default. Callers with a genuine kind-is-wrong override (an
175
- * `ApiError` 429, `scraps data`'s `in_progress` refusal) pass their own
176
- * `retryable`/`next` instead of trusting this lookup — see classifyError and
177
- * scraps.ts's `reportDataState`.
178
- */
179
62
  export function retryFieldsFor(kind) {
180
63
  const policy = RETRY_POLICY[kind];
181
64
  if (!policy)
182
65
  return { retryable: false };
183
66
  return policy.next ? { retryable: policy.retryable, next: [...policy.next] } : { retryable: policy.retryable };
184
67
  }
185
- /**
186
- * Central status → exit-code map (#71 findings 13/14/60). Agents driving this
187
- * CLI unattended need to tell "you're not logged in" (3) from "that id
188
- * doesn't exist" (4) from "the network/API is unreachable" (5) from "you
189
- * passed a bad flag" (2) — a uniform exit 1 collapses all of these into one
190
- * undifferentiable signal.
191
- */
192
68
  export function classifyError(err) {
193
69
  const message = err instanceof Error ? err.message : String(err);
194
- // #88 item 4 — a LOCAL auth failure (no token, or a locally-decoded expired
195
- // token) never made an HTTP call, so its envelope must never carry
196
- // `status:401` — that would claim a server response that never happened.
197
- // Same exit code / kind as a real server 401 (ApiError below); only the
198
- // envelope shape differs.
199
- //
200
- // #169 review round 2, finding 2 — `err.next` is only ever set (in api.ts,
201
- // via authNextSteps()) for an apiKey-mode failure, where the frozen
202
- // `next` default ("trawl login --token <jwt>") is inert until a live
203
- // TRAWL_API_KEY/TRAWL_TOKEN is unset first. Spread AFTER retryFieldsFor so
204
- // it overrides that default's `next` key; every other AuthError/ApiError
205
- // 401 (jwt mode, notLoggedInError — no credential at all) leaves `err.next`
206
- // undefined and keeps the unmodified frozen default, unchanged from before.
207
70
  if (err instanceof AuthError) {
208
71
  return {
209
72
  exitCode: EXIT_CODES.AUTH,
@@ -226,10 +89,6 @@ export function classifyError(err) {
226
89
  if (err.status === 404) {
227
90
  return { exitCode: EXIT_CODES.NOT_FOUND, envelope: { message, status: 404, kind: 'not_found', ...retryFieldsFor('not_found') } };
228
91
  }
229
- // #170 — a 429 (rate-limited / quota-exhausted) is the one `api`-kind
230
- // response worth retrying, even though the frozen map's default for
231
- // `api` is not retryable. Genuinely kind-is-wrong override, not a new
232
- // kind — `kind` stays `"api"`.
233
92
  const retry = err.status === 429 ? { retryable: true } : retryFieldsFor('api');
234
93
  return { exitCode: EXIT_CODES.UNKNOWN, envelope: { message, status: err.status, kind: 'api', ...retry } };
235
94
  }
@@ -244,16 +103,6 @@ export function classifyError(err) {
244
103
  }
245
104
  return { exitCode: EXIT_CODES.UNKNOWN, envelope: { message, kind: 'unknown', ...retryFieldsFor('unknown') } };
246
105
  }
247
- /**
248
- * Print a classified error to the correct stream and return its exit code.
249
- * stdout is reserved for payload — under --json the error itself IS the
250
- * payload (`{"error":{message,status,kind,retryable,next?}}`); otherwise the
251
- * human-readable line goes to stderr, never stdout. (#71 findings 13/14/60)
252
- *
253
- * `quiet` skips the human-readable stderr line (used when the caller already
254
- * printed a fuller diagnostic, e.g. a raw stack trace under --debug) while
255
- * still emitting the --json payload when requested.
256
- */
257
106
  export function reportError(err, opts = {}) {
258
107
  const { exitCode, envelope } = classifyError(err);
259
108
  if (opts.json) {
@@ -1,9 +1,3 @@
1
1
  export declare function table(rows: Record<string, unknown>[], columns: string[]): void;
2
2
  export declare function json(data: unknown): void;
3
- /**
4
- * One date format across the whole CLI: `DD/MM/YY HH:mm` (local time).
5
- * #119 — `list` mixed `DD/MM/YY` (last-run col) with `M/D/YYYY,
6
- * h:mm:ss AM/PM` (toLocaleString), ambiguous day/month side by side.
7
- * Returns `—` for a missing/invalid date.
8
- */
9
3
  export declare function formatDate(value: string | number | Date | null | undefined): string;
@@ -23,12 +23,6 @@ export function table(rows, columns) {
23
23
  export function json(data) {
24
24
  console.log(JSON.stringify(data, null, 2));
25
25
  }
26
- /**
27
- * One date format across the whole CLI: `DD/MM/YY HH:mm` (local time).
28
- * #119 — `list` mixed `DD/MM/YY` (last-run col) with `M/D/YYYY,
29
- * h:mm:ss AM/PM` (toLocaleString), ambiguous day/month side by side.
30
- * Returns `—` for a missing/invalid date.
31
- */
32
26
  export function formatDate(value) {
33
27
  if (value === null || value === undefined)
34
28
  return '—';
@@ -1,37 +1,2 @@
1
- /**
2
- * Shared, tolerant JSON parsing for server response bodies (#159).
3
- *
4
- * The Trawl API is known to occasionally emit a raw, unescaped control
5
- * character (U+0000–U+001F — a literal newline, NUL, BEL, …) inside a JSON
6
- * string value, most often nested inside `scrap.history[0].data` (a
7
- * worker-envelope blob embedded as a string field of the outer response —
8
- * the exact server-side emission point is not yet pinned down; tracked in
9
- * comes-io/trawl_node#1768). That is invalid per RFC 8259 — our own QA
10
- * tooling already works around it by parsing raw API responses with
11
- * Python's `json.loads(text, strict=False)` — but native `JSON.parse`
12
- * has no equivalent lenient mode: it throws outright, and until this fix
13
- * every one of `api.ts`'s response-parsing call sites (`request`, `upload`,
14
- * `publicGet`, `publicPost`) turned that into a bare "Invalid JSON in server
15
- * response" error, discarding the ENTIRE response — even a successful
16
- * create/run whose scrap was already persisted server-side. Exactly the
17
- * "payload-dependent, not a constant break" shape reported in #159: it only
18
- * fires when the specific scraped content/log text happens to carry a raw
19
- * control byte.
20
- *
21
- * `parseServerJson` recovers from that ONE known shape — nothing else. It
22
- * tries a normal strict `JSON.parse` first (the common case, zero extra
23
- * cost) and only falls back to a sanitizing re-parse when that throws.
24
- * Genuinely malformed JSON (truncated body, stray token, …) still throws
25
- * after the fallback also fails — this is a targeted recovery, not a
26
- * general-purpose lenient parser.
27
- */
28
1
  export declare function escapeRawControlCharsInJsonStrings(text: string): string;
29
- /**
30
- * Parse a server response body, tolerating the known #159 quirk above. Never
31
- * silently drops data: a byte that reaches here as a real control character
32
- * comes back out as the same character (via the escape → JSON.parse
33
- * unescape round trip) once parsed, and JSON.stringify guarantees it is
34
- * re-escaped correctly on the way back out through the shared `--json`
35
- * output seam (`lib/format.ts#json`).
36
- */
37
2
  export declare function parseServerJson(text: string): unknown;
package/dist/lib/json.js CHANGED
@@ -1,40 +1,3 @@
1
- /**
2
- * Shared, tolerant JSON parsing for server response bodies (#159).
3
- *
4
- * The Trawl API is known to occasionally emit a raw, unescaped control
5
- * character (U+0000–U+001F — a literal newline, NUL, BEL, …) inside a JSON
6
- * string value, most often nested inside `scrap.history[0].data` (a
7
- * worker-envelope blob embedded as a string field of the outer response —
8
- * the exact server-side emission point is not yet pinned down; tracked in
9
- * comes-io/trawl_node#1768). That is invalid per RFC 8259 — our own QA
10
- * tooling already works around it by parsing raw API responses with
11
- * Python's `json.loads(text, strict=False)` — but native `JSON.parse`
12
- * has no equivalent lenient mode: it throws outright, and until this fix
13
- * every one of `api.ts`'s response-parsing call sites (`request`, `upload`,
14
- * `publicGet`, `publicPost`) turned that into a bare "Invalid JSON in server
15
- * response" error, discarding the ENTIRE response — even a successful
16
- * create/run whose scrap was already persisted server-side. Exactly the
17
- * "payload-dependent, not a constant break" shape reported in #159: it only
18
- * fires when the specific scraped content/log text happens to carry a raw
19
- * control byte.
20
- *
21
- * `parseServerJson` recovers from that ONE known shape — nothing else. It
22
- * tries a normal strict `JSON.parse` first (the common case, zero extra
23
- * cost) and only falls back to a sanitizing re-parse when that throws.
24
- * Genuinely malformed JSON (truncated body, stray token, …) still throws
25
- * after the fallback also fails — this is a targeted recovery, not a
26
- * general-purpose lenient parser.
27
- */
28
- /**
29
- * Escape any raw control character (U+0000–U+001F) found INSIDE a JSON
30
- * string literal, leaving everything else — including legitimate raw
31
- * whitespace between tokens (space/tab/CR/LF are valid there per RFC 8259) —
32
- * untouched. A small state machine tracks whether the scan is currently
33
- * inside a `"…"` string and whether the previous character was an unescaped
34
- * backslash; it does not otherwise validate structure, so text that is
35
- * invalid JSON for any OTHER reason still fails the subsequent `JSON.parse`
36
- * call unchanged.
37
- */
38
1
  const NAMED_CONTROL_CHAR_ESCAPES = {
39
2
  '\n': '\\n',
40
3
  '\r': '\\r',
@@ -71,9 +34,6 @@ export function escapeRawControlCharsInJsonStrings(text) {
71
34
  continue;
72
35
  }
73
36
  if (code <= 0x1f) {
74
- // Raw control character inside a string literal — recover the same
75
- // way JSON.stringify would have escaped it on the way out, instead of
76
- // losing the whole response to a SyntaxError.
77
37
  out += NAMED_CONTROL_CHAR_ESCAPES[ch] ?? `\\u${code.toString(16).padStart(4, '0')}`;
78
38
  continue;
79
39
  }
@@ -81,14 +41,6 @@ export function escapeRawControlCharsInJsonStrings(text) {
81
41
  }
82
42
  return out;
83
43
  }
84
- /**
85
- * Parse a server response body, tolerating the known #159 quirk above. Never
86
- * silently drops data: a byte that reaches here as a real control character
87
- * comes back out as the same character (via the escape → JSON.parse
88
- * unescape round trip) once parsed, and JSON.stringify guarantees it is
89
- * re-escaped correctly on the way back out through the shared `--json`
90
- * output seam (`lib/format.ts#json`).
91
- */
92
44
  export function parseServerJson(text) {
93
45
  try {
94
46
  return JSON.parse(text);
package/dist/lib/jwt.d.ts CHANGED
@@ -1,8 +1 @@
1
- /**
2
- * Decode the `exp` claim from a JWT (middle segment, base64url-encoded JSON).
3
- * Returns null if the payload cannot be decoded or has no `exp` field.
4
- *
5
- * Shared between `trawl token` (expiry advisory) and `trawl login` (reject
6
- * already-expired tokens instead of silently storing them). (#68)
7
- */
8
1
  export declare function decodeExp(jwt: string): number | null;
package/dist/lib/jwt.js CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * Decode the `exp` claim from a JWT (middle segment, base64url-encoded JSON).
3
- * Returns null if the payload cannot be decoded or has no `exp` field.
4
- *
5
- * Shared between `trawl token` (expiry advisory) and `trawl login` (reject
6
- * already-expired tokens instead of silently storing them). (#68)
7
- */
8
1
  export function decodeExp(jwt) {
9
2
  try {
10
3
  const parts = jwt.split('.');
@@ -1,61 +1,8 @@
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
1
  export type PinchState = 'idle' | 'working' | 'celebrating' | 'thumbsup' | 'confused' | 'wave' | 'heart' | 'sleepy' | 'shocked';
32
- /** All 9 canonical states, in the source model's AVATAR_STATES order. */
33
2
  export declare const PINCH_STATES: readonly PinchState[];
34
- /**
35
- * Render Pinch as 24-bit ANSI half-block art for the given state, plus a
36
- * one-line caption. Pure — never touches process.env/stdout; callers must
37
- * gate on `pinchEnabled()` before printing the result.
38
- *
39
- * `frame` only affects `working` (the cyan antenna-tip blip alternates every
40
- * frame); every other state's grid is static.
41
- */
42
3
  export declare function renderPinch(state: PinchState, frame?: number): string;
43
- /**
44
- * #131 — the ART lines only (no caption), optionally COMPACT: trailing and
45
- * leading fully-transparent grid rows are dropped so Pinch takes fewer
46
- * terminal lines (confused/sleepy carry an all-'.' leading row). `frame` is
47
- * passed through to `gridForState` — only `working` reacts to it (the cyan
48
- * antenna blip toggles), so the loop can pulse the tips while the body stays
49
- * still.
50
- */
51
4
  export declare function renderPinchArt(state: PinchState, opts?: {
52
5
  compact?: boolean;
53
6
  frame?: number;
54
7
  }): string;
55
- /**
56
- * True when it's safe to print Pinch art: a real color-capable interactive
57
- * terminal. False under NO_COLOR (https://no-color.org — presence, not
58
- * value, disables color output), a non-TTY stdout (piped/redirected output —
59
- * covers --json/--quiet/CI log capture), or TERM=dumb.
60
- */
61
8
  export declare function pinchEnabled(): boolean;