@trawlme/cli 3.4.2 → 3.5.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 +6 -2
- package/dist/commands/create.js +1 -1
- package/dist/commands/ping.js +8 -1
- package/dist/commands/scraps.js +1 -1
- package/dist/index.js +23 -1
- package/dist/lib/api.d.ts +1 -0
- package/dist/lib/api.js +42 -0
- package/dist/lib/pinch.d.ts +25 -23
- package/dist/lib/pinch.js +128 -108
- package/docs/agent-quickstart.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Command-line client for [Trawl](https://trawl.me) — manage your scraps from the terminal.
|
|
8
8
|
|
|
9
|
-
>
|
|
9
|
+
> 📌 **Versioned with semantic-release** — breaking CLI-surface changes always ship as a new major. Pin a major (`@trawlme/cli@^3`) if you depend on the CLI in CI.
|
|
10
10
|
|
|
11
11
|
> 🤖 **Driving this CLI from an AI agent?** See [docs/agent-quickstart.md](docs/agent-quickstart.md) for the minimal surface (`create`, `--json`, `TRAWL_TOKEN`, exit codes) — this README is the full human/CI guide.
|
|
12
12
|
|
|
@@ -48,7 +48,7 @@ All commands accept a global `--debug` flag to show full error stack traces on f
|
|
|
48
48
|
```
|
|
49
49
|
trawl create <url> --prompt <goal> [--no-autofix] [--json] # url also accepted as --url <url> (#116)
|
|
50
50
|
Create a persistent, self-healing scrap from a URL + a goal (AI-generated)
|
|
51
|
-
trawl run <id> [--watch] [--json] Run a scrap
|
|
51
|
+
trawl run <id> [--watch] [--json] Run a scrap (blocks until finished)
|
|
52
52
|
trawl list|ls [--json] [--status <success|failure|never|running|regression>] [--limit <n>] [--page <n>]
|
|
53
53
|
trawl get <id> [--json] Get scrap details
|
|
54
54
|
trawl data <id> [--json] [--fresh] [--errors] Get scrap data (last persisted run — read-only, no quota; --fresh to launch one)
|
|
@@ -59,6 +59,8 @@ trawl whoami [--json] Show the authenticated user's ide
|
|
|
59
59
|
trawl ping [--json] Health/version handshake against the Trawl API
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
(`trawl create` AI-generates and persists a new scrap from a URL + a goal — distinct from `trawl scraps create`, which is raw manual scrap entry.)
|
|
63
|
+
|
|
62
64
|
> **No breaking change:** every verb above is also still reachable under its pre-reorg path, `trawl scraps <verb>` (e.g. `trawl scraps list`, `trawl scraps run <id>`) — kept as a hidden alias so scripts written before the surface reorg keep working. `trawl --help` only shows the top-level form above; `trawl scraps --help` only shows the remaining scrap-management commands below.
|
|
63
65
|
|
|
64
66
|
`create`/`whoami`/`ping` are fully non-interactive — all three read auth only from `TRAWL_TOKEN`/the stored login token, never prompt. `trawl create` runs the AI wizard server-side (`POST /api/ai/wizard`): generate scrap code from `--prompt` via LLM, persist the scrap, trigger its FIRST run, and auto-fix on failure (default on — `--no-autofix` disables it, sending `autoFix:false`). On a successful first run (human mode) it prints a small **data sample** (item count + first-item fields + one truncated value) as proof of value — best-effort, silent if the sample can't be fetched — and points `Next step` at `trawl data <id>` (the data), with `trawl get <id>` as the secondary detail view. `--json` skips the sample fetch and prints the raw wizard payload verbatim. `success` is an honest outcome of that first run, not "did the HTTP call succeed" — a failed first run is still a 200 response (the scrap was still created; auto-fix, when enabled, retries in the background), and the CLI exits 1 in that case (both human and `--json` modes) even though `--json` always prints the raw payload verbatim. The call legitimately takes 30–250s+ server-side (AI generation + a real run), same long-run timeout as `run`/`data --fresh`/`trigger --wait` below. `trawl whoami`/`trawl ping` mirror the MCP `trawl_whoami`/`trawl_health_ping` tools as closely as the REST surface allows (`GET /api/users/me` / `GET /api/health`) — `ping`'s `--json` payload is admin-enriched (version/uptime/db) and just `{"status":"ok"}` for anyone else.
|
|
@@ -243,6 +245,8 @@ This project uses `legacy-peer-deps` (see `.npmrc`) due to a transitive peer-dep
|
|
|
243
245
|
|
|
244
246
|
Issues and PRs are welcome — please open an issue first for non-trivial changes so we can align on direction.
|
|
245
247
|
|
|
248
|
+
Releases: a merge to `main` publishes to the npm **`next`** dist-tag (candidate channel — try it with `npm i -g @trawlme/cli@next`). The **`latest`** tag is promoted manually after QA, so a merge never ships to default installs directly.
|
|
249
|
+
|
|
246
250
|
## License
|
|
247
251
|
|
|
248
252
|
MIT — see [LICENSE](./LICENSE).
|
package/dist/commands/create.js
CHANGED
|
@@ -88,7 +88,7 @@ async function printDataSample(historyId) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
export const create = new Command('create')
|
|
91
|
-
.description('Create a persistent, self-healing scrap from a URL + a goal (AI-generated)')
|
|
91
|
+
.description('Create a persistent, self-healing scrap from a URL + a goal (AI-generated) — distinct from `trawl scraps create`, which is raw manual entry')
|
|
92
92
|
.argument('[url]', 'Target public URL (http/https)')
|
|
93
93
|
.requiredOption('--prompt <goal>', 'What to extract/scrape, in plain language (required)')
|
|
94
94
|
.option('--url <url>', 'Target public URL — alias of the positional argument')
|
package/dist/commands/ping.js
CHANGED
|
@@ -6,7 +6,14 @@ export const ping = new Command('ping')
|
|
|
6
6
|
.description('Health/version handshake against the Trawl API')
|
|
7
7
|
.option('--json', 'Output as JSON')
|
|
8
8
|
.action(async (opts) => {
|
|
9
|
-
|
|
9
|
+
// #148 — the server route is optionalAuth (public, enriched for an admin
|
|
10
|
+
// JWT — see the PingResponse doc above); `api.publicGet` mirrors that:
|
|
11
|
+
// it attaches a stored/env token when one happens to be available but
|
|
12
|
+
// never REQUIRES one, so `ping` works as a zero-config sanity check even
|
|
13
|
+
// with no `trawl login` ever run. `api.get` (the authenticated path)
|
|
14
|
+
// would throw notLoggedInError() locally before this ever reached the
|
|
15
|
+
// network, defeating the whole point of a pre-login handshake.
|
|
16
|
+
const data = await api.publicGet('/api/health');
|
|
10
17
|
// #106 review (kimi) — honest exit code: a soft-degraded 200 (status !== 'ok')
|
|
11
18
|
// must exit non-zero so `trawl ping || handle_degraded` doesn't treat a
|
|
12
19
|
// degraded API as healthy. Applies in BOTH --json and human modes, alongside
|
package/dist/commands/scraps.js
CHANGED
|
@@ -710,7 +710,7 @@ scraps
|
|
|
710
710
|
export function attachRunCommand(parent, attachOpts = {}) {
|
|
711
711
|
return parent
|
|
712
712
|
.command('run <id>', attachOpts)
|
|
713
|
-
.description('Run a scrap')
|
|
713
|
+
.description('Run a scrap (blocks until finished)')
|
|
714
714
|
.option('-w, --watch', 'Show progress after launching (polls — see `trawl trigger --watch`, #91)')
|
|
715
715
|
.option('--json', 'Output the raw launch payload as JSON')
|
|
716
716
|
.action(async (id, opts) => {
|
package/dist/index.js
CHANGED
|
@@ -293,7 +293,29 @@ export async function runCli(argv = process.argv) {
|
|
|
293
293
|
}
|
|
294
294
|
});
|
|
295
295
|
try {
|
|
296
|
-
|
|
296
|
+
// #148 — a bare `trawl` (no args at all) reads as "let me look around",
|
|
297
|
+
// not a failure: it should behave exactly like `trawl --help` (same
|
|
298
|
+
// text, stdout, exit 0). Left to commander's own default, a program with
|
|
299
|
+
// subcommands and no root action handler treats zero args as "probably
|
|
300
|
+
// missing subcommand" and calls `this.help({ error: true })` internally
|
|
301
|
+
// (see node_modules/commander/lib/command.js `_parseCommand`) — which
|
|
302
|
+
// writes the SAME help text to stderr and throws a CommanderError with
|
|
303
|
+
// exitCode 1 (caught below, bucketed into HELP_OR_VERSION_CODES since its
|
|
304
|
+
// code is 'commander.help', so only the exit code carries the mistake
|
|
305
|
+
// through — text already went to the wrong stream by then). Intercepting
|
|
306
|
+
// here, before parseAsync ever runs, avoids fighting that internal
|
|
307
|
+
// decision entirely and can't affect a genuine unknown top-level command
|
|
308
|
+
// (`trawl frobnicate`) — that path only runs when `isBareInvocation` is
|
|
309
|
+
// false, so it still reaches `this.unknownCommand()` unchanged (stderr,
|
|
310
|
+
// exit 2, code 'commander.unknownCommand' — never in
|
|
311
|
+
// HELP_OR_VERSION_CODES).
|
|
312
|
+
if (isBareInvocation(argv)) {
|
|
313
|
+
program.outputHelp();
|
|
314
|
+
process.exitCode = 0;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
await program.parseAsync(argv);
|
|
318
|
+
}
|
|
297
319
|
}
|
|
298
320
|
catch (err) {
|
|
299
321
|
const { debug } = program.opts();
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export interface RequestOptions {
|
|
|
60
60
|
}
|
|
61
61
|
export declare const api: {
|
|
62
62
|
get: <T>(path: string, opts?: RequestOptions) => Promise<T>;
|
|
63
|
+
publicGet: <T>(path: string, opts?: RequestOptions) => Promise<T>;
|
|
63
64
|
getText: (path: string, opts?: RequestOptions) => Promise<string>;
|
|
64
65
|
post: <T>(path: string, body?: unknown, opts?: RequestOptions) => Promise<T>;
|
|
65
66
|
put: <T>(path: string, body?: unknown, opts?: RequestOptions) => Promise<T>;
|
package/dist/lib/api.js
CHANGED
|
@@ -314,6 +314,47 @@ async function publicPost(path, body, baseUrlOverride, reqOpts = {}) {
|
|
|
314
314
|
throw new Error('Invalid JSON in server response');
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Unauthenticated GET — for endpoints the server itself treats as public/
|
|
319
|
+
* optional-auth (e.g. `GET /api/health`, home.route.js's `optionalAuth`
|
|
320
|
+
* middleware: enriches the response for an admin JWT, passes through
|
|
321
|
+
* otherwise). Unlike `request()`, this NEVER throws `notLoggedInError()` for
|
|
322
|
+
* a missing token — that guard exists for endpoints that genuinely require
|
|
323
|
+
* auth, and applying it here defeated the whole point of a pre-login
|
|
324
|
+
* sanity-check (#148). The token is still attached as a best-effort Cookie
|
|
325
|
+
* when one happens to be stored/env-set, so an already-authenticated caller
|
|
326
|
+
* still gets the admin-enriched payload — it's just never REQUIRED.
|
|
327
|
+
* `throwIfError(res, true)` mirrors `publicPost`'s "public endpoint" 401
|
|
328
|
+
* wording, even though this route's optionalAuth middleware never actually
|
|
329
|
+
* rejects a request for lacking/invalid credentials.
|
|
330
|
+
*/
|
|
331
|
+
async function publicGet(path, reqOpts = {}) {
|
|
332
|
+
const token = getToken();
|
|
333
|
+
const url = `${getApiUrl()}${path}`;
|
|
334
|
+
const timeoutMs = getTimeoutMs(reqOpts.timeoutMs);
|
|
335
|
+
const res = await safeFetch(url, {
|
|
336
|
+
headers: {
|
|
337
|
+
'User-Agent': USER_AGENT,
|
|
338
|
+
...(token ? { Cookie: `TOKEN=${token}` } : {}),
|
|
339
|
+
},
|
|
340
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
341
|
+
}, timeoutMs);
|
|
342
|
+
await throwIfError(res, true);
|
|
343
|
+
const text = await res.text();
|
|
344
|
+
try {
|
|
345
|
+
if (!text)
|
|
346
|
+
return {};
|
|
347
|
+
const parsed = JSON.parse(text);
|
|
348
|
+
// Unwrap API envelope { type, message, data: T }
|
|
349
|
+
if (parsed !== null && typeof parsed === 'object' && 'data' in parsed) {
|
|
350
|
+
return parsed.data;
|
|
351
|
+
}
|
|
352
|
+
return parsed;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
throw new Error('Invalid JSON in server response');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
317
358
|
async function getText(path, reqOpts = {}) {
|
|
318
359
|
const token = getToken();
|
|
319
360
|
if (!token)
|
|
@@ -332,6 +373,7 @@ async function getText(path, reqOpts = {}) {
|
|
|
332
373
|
}
|
|
333
374
|
export const api = {
|
|
334
375
|
get: (path, opts) => request(path, {}, opts),
|
|
376
|
+
publicGet: (path, opts) => publicGet(path, opts),
|
|
335
377
|
getText: (path, opts) => getText(path, opts),
|
|
336
378
|
post: (path, body, opts) => request(path, {
|
|
337
379
|
method: 'POST',
|
package/dist/lib/pinch.d.ts
CHANGED
|
@@ -3,29 +3,30 @@
|
|
|
3
3
|
* art. Distinct from Clawd's 8-bit lane: Pinch is drawn with full 24-bit
|
|
4
4
|
* (`\x1b[38;2;r;g;bm` / `\x1b[48;2;r;g;bm`) color blocks, not a fixed palette.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* row
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* terminal rows, 9 columns wide. '.' cells are transparent — no color
|
|
12
|
-
* escape is emitted for that half, so the terminal's own background shows
|
|
13
|
-
* through.
|
|
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.
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
* `src/modules/trawl/assets/mascot/_src/pinch.model.mjs
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* CLI shipped 4 states and called `working` "thinking", a name not in the
|
|
25
|
-
* source). Each is a 9×6 mini redraw preserving that state's distinguishing
|
|
26
|
-
* feature.
|
|
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".
|
|
27
24
|
*
|
|
28
|
-
*
|
|
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.
|
|
29
30
|
*/
|
|
30
31
|
export type PinchState = 'idle' | 'working' | 'celebrating' | 'thumbsup' | 'confused' | 'wave' | 'heart' | 'sleepy' | 'shocked';
|
|
31
32
|
/** All 9 canonical states, in the source model's AVATAR_STATES order. */
|
|
@@ -42,9 +43,10 @@ export declare function renderPinch(state: PinchState, frame?: number): string;
|
|
|
42
43
|
/**
|
|
43
44
|
* #131 — the ART lines only (no caption), optionally COMPACT: trailing and
|
|
44
45
|
* leading fully-transparent grid rows are dropped so Pinch takes fewer
|
|
45
|
-
* terminal lines (
|
|
46
|
-
* through to `gridForState` — only `working` reacts to it (the cyan
|
|
47
|
-
* blip toggles), so the loop can pulse the tips while the body stays
|
|
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.
|
|
48
50
|
*/
|
|
49
51
|
export declare function renderPinchArt(state: PinchState, opts?: {
|
|
50
52
|
compact?: boolean;
|
package/dist/lib/pinch.js
CHANGED
|
@@ -3,29 +3,30 @@
|
|
|
3
3
|
* art. Distinct from Clawd's 8-bit lane: Pinch is drawn with full 24-bit
|
|
4
4
|
* (`\x1b[38;2;r;g;bm` / `\x1b[48;2;r;g;bm`) color blocks, not a fixed palette.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* row
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* terminal rows, 9 columns wide. '.' cells are transparent — no color
|
|
12
|
-
* escape is emitted for that half, so the terminal's own background shows
|
|
13
|
-
* through.
|
|
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.
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
* `src/modules/trawl/assets/mascot/_src/pinch.model.mjs
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* CLI shipped 4 states and called `working` "thinking", a name not in the
|
|
25
|
-
* source). Each is a 9×6 mini redraw preserving that state's distinguishing
|
|
26
|
-
* feature.
|
|
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".
|
|
27
24
|
*
|
|
28
|
-
*
|
|
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.
|
|
29
30
|
*/
|
|
30
31
|
/** Grid-char → RGB, ported from AVCOLORS (pinch.model.mjs, rev r6f). 'C'
|
|
31
32
|
* (cyan) is the `working` thinking-blip — not present in any static grid,
|
|
@@ -52,107 +53,125 @@ export const PINCH_STATES = [
|
|
|
52
53
|
'shocked',
|
|
53
54
|
];
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* row
|
|
61
|
-
*
|
|
62
|
-
* row
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* dips to row 5 — never a flat single-row bar (#137).
|
|
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
69
|
*/
|
|
70
70
|
const GRIDS = {
|
|
71
|
-
// calm resting face, both claws down, normal smile.
|
|
72
71
|
idle: [
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
72
|
+
'....O....O....',
|
|
73
|
+
'....O....O....',
|
|
74
|
+
'..BBBBBBBBBB..',
|
|
75
|
+
'..BWWBBBBWWB..',
|
|
76
|
+
'..BWKBBBBKWB..',
|
|
77
|
+
'..BBBBBBBBBB..',
|
|
78
|
+
'OOBBPKBBKPBBOO',
|
|
79
|
+
'OOBBBBKKBBBBOO',
|
|
79
80
|
],
|
|
80
81
|
// idle pose + the cyan thinking-blip on the antenna tips (frame-toggled in
|
|
81
82
|
// `gridForState`) — the only animated static state.
|
|
82
83
|
working: [
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
84
|
+
'....O....O....',
|
|
85
|
+
'....O....O....',
|
|
86
|
+
'..BBBBBBBBBB..',
|
|
87
|
+
'..BWWBBBBWWB..',
|
|
88
|
+
'..BWKBBBBKWB..',
|
|
89
|
+
'..BBBBBBBBBB..',
|
|
90
|
+
'OOBBPKBBKPBBOO',
|
|
91
|
+
'OOBBBBKKBBBBOO',
|
|
89
92
|
],
|
|
90
|
-
// both claws raised
|
|
93
|
+
// both claws raised to rows 3-4 + a big open mouth (rows 6-7).
|
|
91
94
|
celebrating: [
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
97
|
-
'
|
|
95
|
+
'....O....O....',
|
|
96
|
+
'....O....O....',
|
|
97
|
+
'..BBBBBBBBBB..',
|
|
98
|
+
'OOBWWBBBBWWBOO',
|
|
99
|
+
'OOBWKBBBBKWBOO',
|
|
100
|
+
'..BBBBBBBBBB..',
|
|
101
|
+
'..BBPKKKKPBB..',
|
|
102
|
+
'..BBBKKKKBBB..',
|
|
98
103
|
],
|
|
99
|
-
// right claw raised (rows
|
|
100
|
-
//
|
|
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).
|
|
101
106
|
thumbsup: [
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
'.
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
-
'
|
|
107
|
+
'....O....O....',
|
|
108
|
+
'....O....O....',
|
|
109
|
+
'..BBBBBBBBBBO.',
|
|
110
|
+
'..BWWBBBBWWBOO',
|
|
111
|
+
'..BWKBBBBKWBOO',
|
|
112
|
+
'..BBBBBBBBBB..',
|
|
113
|
+
'OOBBPKBBKPBB..',
|
|
114
|
+
'OOBBBBKKBBBB..',
|
|
108
115
|
],
|
|
109
|
-
// antennae drooped + spread,
|
|
110
|
-
//
|
|
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`.
|
|
111
118
|
confused: [
|
|
112
|
-
'
|
|
113
|
-
'
|
|
114
|
-
'
|
|
115
|
-
'
|
|
116
|
-
'
|
|
117
|
-
'
|
|
119
|
+
'..............',
|
|
120
|
+
'...O......O...',
|
|
121
|
+
'..BBBBBBBBBB..',
|
|
122
|
+
'..BWWBBBBWWB..',
|
|
123
|
+
'..BWKBBBBKWB..',
|
|
124
|
+
'..BBBBBBBBBB..',
|
|
125
|
+
'OOBBPBBBBPBBOO',
|
|
126
|
+
'OOBBBBKKBBBBOO',
|
|
118
127
|
],
|
|
119
|
-
// right claw raised (rows
|
|
128
|
+
// right claw raised (rows 3-4), left claw resting; base face.
|
|
120
129
|
wave: [
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
124
|
-
'
|
|
125
|
-
'
|
|
126
|
-
'
|
|
130
|
+
'....O....O....',
|
|
131
|
+
'....O....O....',
|
|
132
|
+
'..BBBBBBBBBB..',
|
|
133
|
+
'..BWWBBBBWWBOO',
|
|
134
|
+
'..BWKBBBBKWBOO',
|
|
135
|
+
'..BBBBBBBBBB..',
|
|
136
|
+
'OOBBPKBBKPBB..',
|
|
137
|
+
'OOBBBBKKBBBB..',
|
|
127
138
|
],
|
|
128
|
-
// pink heart-eyes (P
|
|
139
|
+
// pink heart-eyes (P fills both eye blocks, rows 3-4), claws resting, normal
|
|
140
|
+
// smile.
|
|
129
141
|
heart: [
|
|
130
|
-
'
|
|
131
|
-
'
|
|
132
|
-
'
|
|
133
|
-
'
|
|
134
|
-
'
|
|
135
|
-
'
|
|
142
|
+
'....O....O....',
|
|
143
|
+
'....O....O....',
|
|
144
|
+
'..BBBBBBBBBB..',
|
|
145
|
+
'..BPPBBBBPPB..',
|
|
146
|
+
'..BPPBBBBPPB..',
|
|
147
|
+
'..BBBBBBBBBB..',
|
|
148
|
+
'OOBBPKBBKPBBOO',
|
|
149
|
+
'OOBBBBKKBBBBOO',
|
|
136
150
|
],
|
|
137
|
-
// closed eyes (flat K line, no white), antennae drooped + spread,
|
|
138
|
-
// mouth, claws resting — like confused but eyes shut and no '?'.
|
|
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 '?'.
|
|
139
153
|
sleepy: [
|
|
140
|
-
'
|
|
141
|
-
'
|
|
142
|
-
'
|
|
143
|
-
'
|
|
144
|
-
'
|
|
145
|
-
'
|
|
154
|
+
'..............',
|
|
155
|
+
'...O......O...',
|
|
156
|
+
'..BBBBBBBBBB..',
|
|
157
|
+
'..BWWBBBBWWB..',
|
|
158
|
+
'..BKKBBBBKKB..',
|
|
159
|
+
'..BBBBBBBBBB..',
|
|
160
|
+
'OOBBPBBBBPBBOO',
|
|
161
|
+
'OOBBBBKKBBBBOO',
|
|
146
162
|
],
|
|
147
|
-
//
|
|
148
|
-
//
|
|
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.
|
|
149
166
|
shocked: [
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
167
|
+
'....O....O....',
|
|
168
|
+
'....O....O....',
|
|
169
|
+
'..BBBBBBBBBB..',
|
|
170
|
+
'OOBWKBBBBKWBOO',
|
|
171
|
+
'OOBWWBBBBWWBOO',
|
|
172
|
+
'..BBBBBBBBBB..',
|
|
173
|
+
'..BBPBKKBPBB..',
|
|
174
|
+
'..BBBBBBBBBB..',
|
|
156
175
|
],
|
|
157
176
|
};
|
|
158
177
|
const CAPTIONS = {
|
|
@@ -166,8 +185,8 @@ const CAPTIONS = {
|
|
|
166
185
|
sleepy: 'Pinch is sleepy…',
|
|
167
186
|
shocked: 'Pinch is shocked!',
|
|
168
187
|
};
|
|
169
|
-
/** Antenna-tip cells (row 0) for the `working` cyan blip: cols
|
|
170
|
-
const ANTENNA_TIP_COLS = [
|
|
188
|
+
/** Antenna-tip cells (grid row 0) for the `working` cyan blip: cols 4 & 9. */
|
|
189
|
+
const ANTENNA_TIP_COLS = [4, 9];
|
|
171
190
|
/** Replace the chars at `indices` in `row` with `ch` — never mutates `row`. */
|
|
172
191
|
function setCells(row, indices, ch) {
|
|
173
192
|
const chars = row.split('');
|
|
@@ -212,7 +231,7 @@ function renderCell(upper, lower) {
|
|
|
212
231
|
return `${fgCode(upperColor)}${bgCode(lowerColor)}▀${RESET}`;
|
|
213
232
|
}
|
|
214
233
|
/** Pack a grid into terminal lines, 2 grid rows per line (upper=fg, lower=bg).
|
|
215
|
-
* The
|
|
234
|
+
* The 14×8 founder grid is 8 rows → 4 lines. */
|
|
216
235
|
function renderGrid(rows) {
|
|
217
236
|
const lines = [];
|
|
218
237
|
for (let i = 0; i < rows.length; i += 2) {
|
|
@@ -238,8 +257,8 @@ export function renderPinch(state, frame = 0) {
|
|
|
238
257
|
const grid = gridForState(state, frame);
|
|
239
258
|
const lines = renderGrid(grid);
|
|
240
259
|
if (state === 'confused') {
|
|
241
|
-
// "beside the art" — a bold '?' to the right of the eye row.
|
|
242
|
-
//
|
|
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.
|
|
243
262
|
lines[1] = `${lines[1]} \x1b[1m?${RESET}`;
|
|
244
263
|
}
|
|
245
264
|
return [...lines, CAPTIONS[state]].join('\n');
|
|
@@ -247,9 +266,10 @@ export function renderPinch(state, frame = 0) {
|
|
|
247
266
|
/**
|
|
248
267
|
* #131 — the ART lines only (no caption), optionally COMPACT: trailing and
|
|
249
268
|
* leading fully-transparent grid rows are dropped so Pinch takes fewer
|
|
250
|
-
* terminal lines (
|
|
251
|
-
* through to `gridForState` — only `working` reacts to it (the cyan
|
|
252
|
-
* blip toggles), so the loop can pulse the tips while the body stays
|
|
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.
|
|
253
273
|
*/
|
|
254
274
|
export function renderPinchArt(state, opts = {}) {
|
|
255
275
|
let grid = gridForState(state, opts.frame ?? 0);
|
package/docs/agent-quickstart.md
CHANGED
|
@@ -27,7 +27,7 @@ first-class on every one, and none of them ever blocks on a prompt (see
|
|
|
27
27
|
```
|
|
28
28
|
trawl create <url> --prompt <goal> [--no-autofix] [--json] # url also accepted as --url <url> (#116)
|
|
29
29
|
Create a persistent, self-healing scrap from a URL + a goal (AI-generated)
|
|
30
|
-
trawl run <id> [--watch] [--json] Run a scrap
|
|
30
|
+
trawl run <id> [--watch] [--json] Run a scrap (blocks until finished)
|
|
31
31
|
trawl list|ls [--json] [--status <s>] [--limit <n>] [--page <n>] List all scraps
|
|
32
32
|
trawl get <id> [--json] Get scrap details
|
|
33
33
|
trawl data <id> [--json] [--fresh] [--errors] Get scrap data (last persisted run, or --fresh to launch one)
|