@trawlme/cli 3.12.0 → 3.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/commands/create.d.ts +0 -28
- package/dist/commands/create.js +0 -89
- package/dist/commands/doctor.d.ts +0 -79
- package/dist/commands/doctor.js +1 -187
- package/dist/commands/login.js +0 -67
- package/dist/commands/ping.d.ts +0 -15
- package/dist/commands/ping.js +0 -15
- package/dist/commands/scraps.d.ts +0 -120
- package/dist/commands/scraps.js +10 -724
- package/dist/commands/skills.js +0 -22
- package/dist/commands/spec.d.ts +0 -85
- package/dist/commands/spec.js +0 -67
- package/dist/commands/telemetry.js +0 -4
- package/dist/commands/token.js +0 -28
- package/dist/commands/upgrade.js +0 -22
- package/dist/commands/whoami.d.ts +0 -12
- package/dist/commands/whoami.js +0 -6
- package/dist/index.d.ts +0 -188
- package/dist/index.js +0 -349
- package/dist/lib/api.d.ts +0 -78
- package/dist/lib/api.js +1 -320
- package/dist/lib/cdp-pipe.d.ts +0 -72
- package/dist/lib/cdp-pipe.js +1 -81
- package/dist/lib/chrome-discovery.d.ts +0 -11
- package/dist/lib/chrome-discovery.js +0 -19
- package/dist/lib/chrome-launch.d.ts +0 -40
- package/dist/lib/chrome-launch.js +0 -69
- package/dist/lib/config.d.ts +0 -53
- package/dist/lib/config.js +0 -55
- package/dist/lib/confirm.d.ts +0 -55
- package/dist/lib/confirm.js +0 -47
- package/dist/lib/docs.d.ts +0 -123
- package/dist/lib/docs.js +0 -169
- package/dist/lib/errors.d.ts +0 -134
- package/dist/lib/errors.js +0 -151
- package/dist/lib/format.d.ts +0 -6
- package/dist/lib/format.js +0 -6
- package/dist/lib/json.d.ts +0 -35
- package/dist/lib/json.js +0 -48
- package/dist/lib/jwt.d.ts +0 -7
- package/dist/lib/jwt.js +0 -7
- package/dist/lib/pinch.d.ts +0 -53
- package/dist/lib/pinch.js +6 -112
- package/dist/lib/pinchAnimation.d.ts +0 -16
- package/dist/lib/pinchAnimation.js +8 -29
- package/dist/lib/posthog.d.ts +0 -9
- package/dist/lib/posthog.js +0 -23
- package/dist/lib/prompt.js +1 -20
- package/dist/lib/secure-transport.d.ts +0 -7
- package/dist/lib/secure-transport.js +0 -24
- package/dist/lib/session-capture-guard.d.ts +0 -15
- package/dist/lib/session-capture-guard.js +0 -5
- package/dist/lib/session-capture.d.ts +0 -125
- package/dist/lib/session-capture.js +0 -281
- package/dist/lib/skills.d.ts +0 -175
- package/dist/lib/skills.js +1 -216
- package/dist/lib/skillsNudge.d.ts +0 -17
- package/dist/lib/skillsNudge.js +0 -83
- package/dist/lib/spinner.d.ts +0 -39
- package/dist/lib/spinner.js +0 -40
- package/dist/lib/storage-state.d.ts +0 -112
- package/dist/lib/storage-state.js +0 -131
- package/dist/lib/tips.d.ts +0 -38
- package/dist/lib/tips.js +0 -77
- package/dist/lib/updateCheckWorker.js +0 -14
- package/dist/lib/updateNotifier.d.ts +0 -17
- package/dist/lib/updateNotifier.js +0 -53
- package/dist/lib/validate.d.ts +0 -8
- package/dist/lib/validate.js +0 -8
- package/dist/lib/version.d.ts +0 -12
- package/dist/lib/version.js +1 -13
- package/docs/agent-quickstart.md +2 -2
- package/package.json +2 -2
package/dist/lib/errors.js
CHANGED
|
@@ -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) {
|
package/dist/lib/format.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/format.js
CHANGED
|
@@ -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 '—';
|
package/dist/lib/json.d.ts
CHANGED
|
@@ -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('.');
|
package/dist/lib/pinch.d.ts
CHANGED
|
@@ -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;
|