@trawlme/cli 3.11.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.
Files changed (73) hide show
  1. package/README.md +5 -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 +142 -656
  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 +31 -0
  24. package/dist/lib/cdp-pipe.js +141 -0
  25. package/dist/lib/chrome-discovery.d.ts +1 -0
  26. package/dist/lib/chrome-discovery.js +30 -0
  27. package/dist/lib/chrome-launch.d.ts +8 -0
  28. package/dist/lib/chrome-launch.js +53 -0
  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 +1 -0
  51. package/dist/lib/secure-transport.js +15 -0
  52. package/dist/lib/session-capture-guard.d.ts +6 -0
  53. package/dist/lib/session-capture-guard.js +9 -0
  54. package/dist/lib/session-capture.d.ts +55 -0
  55. package/dist/lib/session-capture.js +319 -0
  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 +55 -0
  63. package/dist/lib/storage-state.js +96 -0
  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/package.json +2 -2
@@ -0,0 +1,96 @@
1
+ export function getTargetHost(targetUrl) {
2
+ return new URL(targetUrl).hostname.toLowerCase();
3
+ }
4
+ export function isCookieDomainInScope(rawDomain, targetHost) {
5
+ const host = targetHost.toLowerCase();
6
+ if (rawDomain.startsWith('.')) {
7
+ const domain = rawDomain.slice(1).toLowerCase();
8
+ return host === domain || host.endsWith(`.${domain}`);
9
+ }
10
+ return host === rawDomain.toLowerCase();
11
+ }
12
+ export function isOriginHostInScope(originHost, targetHost) {
13
+ const h = originHost.toLowerCase();
14
+ const target = targetHost.toLowerCase();
15
+ return h === target || h.endsWith(`.${target}`);
16
+ }
17
+ const CANONICAL_SAME_SITE = {
18
+ strict: 'Strict',
19
+ lax: 'Lax',
20
+ none: 'None',
21
+ };
22
+ function canonicalizeSameSite(value) {
23
+ return CANONICAL_SAME_SITE[value.toLowerCase()] ?? null;
24
+ }
25
+ function normalizeCdpCookie(raw) {
26
+ if (typeof raw.name !== 'string' || raw.name.length === 0)
27
+ return null;
28
+ if (typeof raw.value !== 'string')
29
+ return null;
30
+ if (typeof raw.domain !== 'string' || raw.domain.length === 0)
31
+ return null;
32
+ const cookie = { name: raw.name, value: raw.value, domain: raw.domain };
33
+ if (typeof raw.path === 'string')
34
+ cookie.path = raw.path;
35
+ if (typeof raw.secure === 'boolean')
36
+ cookie.secure = raw.secure;
37
+ if (typeof raw.httpOnly === 'boolean')
38
+ cookie.httpOnly = raw.httpOnly;
39
+ if (typeof raw.sameSite === 'string' && raw.sameSite.length > 0) {
40
+ const canon = canonicalizeSameSite(raw.sameSite);
41
+ if (canon && !(canon === 'None' && cookie.secure !== true)) {
42
+ cookie.sameSite = canon;
43
+ }
44
+ }
45
+ if (typeof raw.expires === 'number' && !Number.isNaN(raw.expires) && raw.expires !== -1) {
46
+ cookie.expires = raw.expires;
47
+ }
48
+ return cookie;
49
+ }
50
+ export function mapCookies(rawCookies, targetUrl) {
51
+ const targetHost = getTargetHost(targetUrl);
52
+ let droppedOutOfScope = 0;
53
+ let droppedInvalid = 0;
54
+ const cookies = [];
55
+ for (const raw of rawCookies) {
56
+ if (!raw || typeof raw.domain !== 'string' || raw.domain.length === 0) {
57
+ droppedInvalid++;
58
+ continue;
59
+ }
60
+ if (!isCookieDomainInScope(raw.domain, targetHost)) {
61
+ droppedOutOfScope++;
62
+ continue;
63
+ }
64
+ const mapped = normalizeCdpCookie(raw);
65
+ if (!mapped) {
66
+ droppedInvalid++;
67
+ continue;
68
+ }
69
+ cookies.push(mapped);
70
+ }
71
+ return { cookies, totalSeen: rawCookies.length, droppedOutOfScope, droppedInvalid };
72
+ }
73
+ export function mapOrigins(rawOrigins, targetUrl) {
74
+ const targetHost = getTargetHost(targetUrl);
75
+ let droppedOutOfScope = 0;
76
+ const origins = [];
77
+ for (const entry of rawOrigins) {
78
+ let host;
79
+ try {
80
+ host = new URL(entry.origin).hostname;
81
+ }
82
+ catch {
83
+ droppedOutOfScope++;
84
+ continue;
85
+ }
86
+ if (!isOriginHostInScope(host, targetHost)) {
87
+ droppedOutOfScope++;
88
+ continue;
89
+ }
90
+ const localStorage = entry.entries
91
+ .filter((kv) => typeof kv?.name === 'string' && typeof kv?.value === 'string')
92
+ .map((kv) => ({ name: kv.name, value: kv.value }));
93
+ origins.push({ origin: entry.origin, localStorage });
94
+ }
95
+ return { origins, totalSeen: rawOrigins.length, droppedOutOfScope };
96
+ }
@@ -1,13 +1,3 @@
1
- /**
2
- * Pure gate: true when the tip should print. Every external signal (json
3
- * flag, TTY-ness, opt-out, persisted timestamp, current time) is a
4
- * parameter rather than read internally, so this is trivially testable
5
- * without mocking process.env/stdout/Date/config. Does NOT cover the
6
- * server-side `invitations.userFacing` flag (#153) — that check requires a
7
- * network round-trip, so it's kept out of this pure/sync gate and only
8
- * consulted once this gate is already true (see
9
- * isReferralProgramUserFacing + maybeShowReferralTip below).
10
- */
11
1
  export declare function isReferralTipDue(opts: {
12
2
  json?: boolean;
13
3
  isTTY: boolean;
@@ -15,34 +5,6 @@ export declare function isReferralTipDue(opts: {
15
5
  lastShownAt: number;
16
6
  now: number;
17
7
  }): boolean;
18
- /**
19
- * Best-effort: print one subtle line inviting the user to refer a builder,
20
- * throttled to once per 7 days via a timestamp persisted in the CLI's
21
- * existing Conf-backed config store (lib/config.ts — same store as
22
- * apiUrl/token/telemetry/updateNotifier), AND gated on the server's
23
- * `invitations.userFacing` flag (#153) once that throttle window is open.
24
- *
25
- * This only gates on throttle/TTY/`--json`/opt-out/server-flag — it does
26
- * NOT know whether the underlying command actually succeeded. Callers must
27
- * only invoke it on a genuine success (see commands/scraps.ts `run <id>`'s
28
- * action, which also checks pollRunProgress's exit code under `--watch`
29
- * before calling this). Any state read/write/network failure (corrupt
30
- * config file, disk error, unreachable server, …) is swallowed silently —
31
- * a broken tip must never break a run.
32
- *
33
- * #153 — the timestamp is now persisted BEFORE the print (#151 originally
34
- * had it after). Two overlapping calls that both read `due=true` before
35
- * either has persisted can still both decide to print (a file-based config
36
- * store can't fully arbitrate that without a lock this feature doesn't
37
- * warrant) — but persist-before-print closes the narrower, guaranteed-
38
- * recurring failure mode: previously, if the print succeeded but the
39
- * subsequent config.set failed (or simply hadn't landed yet when a second
40
- * call read the same stale timestamp), the tip could print again on every
41
- * later run forever, since the "already shown" state never advanced. With
42
- * persist first, a lost race collapses to "tip skipped this time, timestamp
43
- * still unset, next due window free to retry" — never a guaranteed
44
- * repeat print.
45
- */
46
8
  export declare function maybeShowReferralTip(opts?: {
47
9
  json?: boolean;
48
10
  }): Promise<void>;
package/dist/lib/tips.js CHANGED
@@ -1,44 +1,14 @@
1
- /**
2
- * Throttled post-run referral tip (#151) — after a successful `trawl run`,
3
- * occasionally invite the user to refer a builder. Mirrors
4
- * lib/updateNotifier.ts's shape: self-guarded so a broken tip can never turn
5
- * a successful command into a failed one. Async since #153 — printing is
6
- * additionally gated on a server-side flag fetch (see
7
- * isReferralProgramUserFacing below).
8
- */
9
1
  import chalk from 'chalk';
10
2
  import config from './config.js';
11
3
  import { api } from './api.js';
12
- /** Max once per 7 days. */
13
4
  const TIP_THROTTLE_MS = 7 * 24 * 60 * 60 * 1000;
14
- /**
15
- * #153 — checked only once the 7-day throttle window is already open
16
- * (isReferralTipDue === true), so a run that isn't due for the tip anyway
17
- * never pays a network round-trip. ≤2s so a slow/dead server can never
18
- * perceptibly slow a run down.
19
- */
20
5
  const FLAG_CHECK_TIMEOUT_MS = 2_000;
21
6
  const REFERRAL_TIP_TEXT = 'Invite a builder — they get 1,500, you get 3,000 compute → trawl.me → account → Referrals';
22
- /**
23
- * Opted out via env (presence-based, mirrors NO_COLOR / the update
24
- * notifier's TRAWL_NO_UPDATE_NOTIFIER — see lib/updateNotifier.ts) or an
25
- * explicit `false` config value. Absent/undefined config key means enabled.
26
- */
27
7
  function isOptedOut() {
28
8
  if (process.env['TRAWL_NO_TIPS'] !== undefined)
29
9
  return true;
30
10
  return config.get('tips') === false;
31
11
  }
32
- /**
33
- * Pure gate: true when the tip should print. Every external signal (json
34
- * flag, TTY-ness, opt-out, persisted timestamp, current time) is a
35
- * parameter rather than read internally, so this is trivially testable
36
- * without mocking process.env/stdout/Date/config. Does NOT cover the
37
- * server-side `invitations.userFacing` flag (#153) — that check requires a
38
- * network round-trip, so it's kept out of this pure/sync gate and only
39
- * consulted once this gate is already true (see
40
- * isReferralProgramUserFacing + maybeShowReferralTip below).
41
- */
42
12
  export function isReferralTipDue(opts) {
43
13
  if (opts.json)
44
14
  return false;
@@ -48,24 +18,6 @@ export function isReferralTipDue(opts) {
48
18
  return false;
49
19
  return opts.now - opts.lastShownAt >= TIP_THROTTLE_MS;
50
20
  }
51
- /**
52
- * Server-side kill switch (#153) — the web nudge and the digest email
53
- * footer both gate on the same `invitations.userFacing` flag (trawl_node
54
- * modules/auth/controllers/auth.controller.js `getConfig`,
55
- * config/defaults/*.config.js), so if it's ever flipped off for abuse
56
- * mitigation the CLI must stop promising a referral program that isn't
57
- * actually reachable. `api.publicGet` resolves the request's base URL the
58
- * exact same way every other CLI API call does (getApiUrl() in
59
- * lib/config.ts — TRAWL_API_URL env > stored `trawl login --url` >
60
- * default) and never requires a token, matching this endpoint's public/
61
- * unauthenticated contract (mirrors `trawl ping`'s use of the same
62
- * helper for GET /api/health).
63
- *
64
- * ANY failure — network error, timeout (FLAG_CHECK_TIMEOUT_MS), or a
65
- * malformed/unexpected response shape — is indistinguishable from "flag
66
- * off" here: resolves to false, never throws, never retries. A slow or
67
- * broken server must never slow down or break a run.
68
- */
69
21
  async function isReferralProgramUserFacing() {
70
22
  try {
71
23
  const data = await api.publicGet('/api/auth/config', {
@@ -77,34 +29,6 @@ async function isReferralProgramUserFacing() {
77
29
  return false;
78
30
  }
79
31
  }
80
- /**
81
- * Best-effort: print one subtle line inviting the user to refer a builder,
82
- * throttled to once per 7 days via a timestamp persisted in the CLI's
83
- * existing Conf-backed config store (lib/config.ts — same store as
84
- * apiUrl/token/telemetry/updateNotifier), AND gated on the server's
85
- * `invitations.userFacing` flag (#153) once that throttle window is open.
86
- *
87
- * This only gates on throttle/TTY/`--json`/opt-out/server-flag — it does
88
- * NOT know whether the underlying command actually succeeded. Callers must
89
- * only invoke it on a genuine success (see commands/scraps.ts `run <id>`'s
90
- * action, which also checks pollRunProgress's exit code under `--watch`
91
- * before calling this). Any state read/write/network failure (corrupt
92
- * config file, disk error, unreachable server, …) is swallowed silently —
93
- * a broken tip must never break a run.
94
- *
95
- * #153 — the timestamp is now persisted BEFORE the print (#151 originally
96
- * had it after). Two overlapping calls that both read `due=true` before
97
- * either has persisted can still both decide to print (a file-based config
98
- * store can't fully arbitrate that without a lock this feature doesn't
99
- * warrant) — but persist-before-print closes the narrower, guaranteed-
100
- * recurring failure mode: previously, if the print succeeded but the
101
- * subsequent config.set failed (or simply hadn't landed yet when a second
102
- * call read the same stale timestamp), the tip could print again on every
103
- * later run forever, since the "already shown" state never advanced. With
104
- * persist first, a lost race collapses to "tip skipped this time, timestamp
105
- * still unset, next due window free to retry" — never a guaranteed
106
- * repeat print.
107
- */
108
32
  export async function maybeShowReferralTip(opts = {}) {
109
33
  try {
110
34
  const lastShownAt = config.get('referralTipShownAt') || 0;
@@ -124,6 +48,5 @@ export async function maybeShowReferralTip(opts = {}) {
124
48
  console.log(chalk.dim(REFERRAL_TIP_TEXT));
125
49
  }
126
50
  catch {
127
- // silent — #151/#153: a broken tip must never break a run
128
51
  }
129
52
  }
@@ -1,16 +1,3 @@
1
- /**
2
- * Standalone worker script spawned DETACHED by scheduleUpdateCheck()
3
- * (updateNotifier.ts) to refresh the update-check cache in the background —
4
- * this never runs on the CLI's hot path. Invoked as
5
- * `node dist/lib/updateCheckWorker.js <cachePath>`, it queries the npm
6
- * registry for the latest published version, writes
7
- * `{ latest, checkedAt }` to `<cachePath>`, and exits. The NEXT `trawl`
8
- * invocation picks up the refreshed cache — this run never waits on it.
9
- *
10
- * Any failure (npm missing, registry unreachable, cache dir unwritable) exits
11
- * silently (code 0) — a failed background refresh must never surface to the
12
- * user; the cache simply stays stale until the next stale-check fires.
13
- */
14
1
  import { execFile } from 'node:child_process';
15
2
  import { writeFileSync } from 'node:fs';
16
3
  import { PKG_NAME } from './version.js';
@@ -22,7 +9,6 @@ if (cachePath) {
22
9
  writeFileSync(cachePath, JSON.stringify({ latest: String(stdout).trim(), checkedAt: Date.now() }));
23
10
  }
24
11
  catch {
25
- // Best-effort — a write failure just means the next invocation retries.
26
12
  }
27
13
  }
28
14
  process.exit(0);
@@ -1,20 +1,3 @@
1
- /** The cache file lives beside the CLI's own `conf` config file — same
2
- * directory, no extra config-dir resolution logic to duplicate. */
3
1
  export declare function updateCachePath(): string;
4
- /**
5
- * Synchronous, instant — safe to call on every invocation's hot path. Prints
6
- * ONE line to stderr when a cached "latest" version is a valid semver
7
- * strictly greater than the running version, and every gate holds:
8
- * an interactive stderr TTY, no `--json` flag, not a help/version query, and
9
- * not opted out (env or config). Any error (bad cache, etc.) is swallowed —
10
- * this must never throw.
11
- */
12
2
  export declare function maybeNotifyUpdate(argv?: string[]): void;
13
- /**
14
- * Non-blocking background refresh. When the cache is missing or older than
15
- * CHECK_INTERVAL_MS, spawns a fully detached child (updateCheckWorker.ts)
16
- * that queries npm and writes the refreshed cache, then exits — this
17
- * invocation never waits on it (`.unref()`), so the refreshed cache is only
18
- * ever used by the NEXT invocation. Never throws.
19
- */
20
3
  export declare function scheduleUpdateCheck(): void;
@@ -1,19 +1,3 @@
1
- /**
2
- * Passive "update available" notifier (#129) — follows the `update-notifier`
3
- * model: notify from a CACHE, refresh the cache in the BACKGROUND, never
4
- * block or slow down the command it hitches a ride on.
5
- *
6
- * - `maybeNotifyUpdate()` is synchronous and instant — it only ever reads a
7
- * cache file that already exists on disk and prints at most one line to
8
- * stderr. It never talks to the network and never throws.
9
- * - `scheduleUpdateCheck()` refreshes that cache for the NEXT invocation by
10
- * spawning a fully detached, unref'd child process
11
- * (src/lib/updateCheckWorker.ts) — this invocation never awaits it.
12
- *
13
- * Both are called from src/index.ts's `runCli()` finally block, after
14
- * `shutdown()`, wrapped so a notifier failure can never turn a successful
15
- * command into a failed one.
16
- */
17
1
  import { readFileSync } from 'node:fs';
18
2
  import { dirname, join } from 'node:path';
19
3
  import { fileURLToPath } from 'node:url';
@@ -21,15 +5,8 @@ import { spawn } from 'node:child_process';
21
5
  import chalk from 'chalk';
22
6
  import config from './config.js';
23
7
  import { currentVersion } from './version.js';
24
- /** Refresh cadence for the background worker — a cache younger than this is
25
- * left alone; older (or missing) triggers a detached refresh. */
26
8
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
27
- /** Sibling compiled worker script (dist/lib/updateCheckWorker.js next to this
28
- * module's own dist/lib/updateNotifier.js) — resolved relative to THIS
29
- * module so it works regardless of cwd or how the CLI was installed. */
30
9
  const WORKER_PATH = fileURLToPath(new URL('./updateCheckWorker.js', import.meta.url));
31
- /** The cache file lives beside the CLI's own `conf` config file — same
32
- * directory, no extra config-dir resolution logic to duplicate. */
33
10
  export function updateCachePath() {
34
11
  return join(dirname(config.path), 'update-check.json');
35
12
  }
@@ -42,12 +19,9 @@ function readCache() {
42
19
  return { latest: parsed.latest, checkedAt: parsed.checkedAt };
43
20
  }
44
21
  catch {
45
- // Missing file, corrupt JSON, unreadable — treat all the same: no cache.
46
22
  return undefined;
47
23
  }
48
24
  }
49
- /** Opted out via env (presence-based, mirrors NO_COLOR) or an explicit
50
- * `false` config value. Absent/undefined config key means enabled. */
51
25
  function isOptedOut() {
52
26
  if (process.env['TRAWL_NO_UPDATE_NOTIFIER'] !== undefined)
53
27
  return true;
@@ -57,8 +31,6 @@ const SEMVER_RE = /^\d+\.\d+\.\d+$/;
57
31
  function isValidSemver(v) {
58
32
  return SEMVER_RE.test(v);
59
33
  }
60
- /** Plain MAJOR.MINOR.PATCH comparison — no prerelease/build-metadata support,
61
- * consistent with this package's own version scheme (see version.ts). */
62
34
  function isGreaterSemver(a, b) {
63
35
  if (!isValidSemver(a) || !isValidSemver(b))
64
36
  return false;
@@ -72,17 +44,9 @@ function isGreaterSemver(a, b) {
72
44
  }
73
45
  return false;
74
46
  }
75
- /** Best-effort `--json` argv scan — same "argv scan, never trust flag
76
- * values" caveat as index.ts's hasJsonFlag: positional values never equal
77
- * the literal `--json` string. Kept local (not imported from index.ts) so
78
- * this module has no reverse dependency on the entrypoint. */
79
47
  function hasJsonFlag(argv) {
80
48
  return argv.includes('--json');
81
49
  }
82
- /** Same shape as index.ts's isHelpOrVersion (bare invocation, `help`,
83
- * -h/--help/-V/--version) — kept local for the same reason as hasJsonFlag
84
- * above. Belt-and-suspenders: src/index.ts already skips calling this
85
- * function entirely on a help/version invocation. */
86
50
  function isHelpOrVersionInvocation(argv) {
87
51
  if (argv.some((a) => a === '-h' || a === '--help' || a === '-V' || a === '--version'))
88
52
  return true;
@@ -93,14 +57,6 @@ function isHelpOrVersionInvocation(argv) {
93
57
  return true;
94
58
  return false;
95
59
  }
96
- /**
97
- * Synchronous, instant — safe to call on every invocation's hot path. Prints
98
- * ONE line to stderr when a cached "latest" version is a valid semver
99
- * strictly greater than the running version, and every gate holds:
100
- * an interactive stderr TTY, no `--json` flag, not a help/version query, and
101
- * not opted out (env or config). Any error (bad cache, etc.) is swallowed —
102
- * this must never throw.
103
- */
104
60
  export function maybeNotifyUpdate(argv = process.argv) {
105
61
  try {
106
62
  if (!process.stderr.isTTY)
@@ -120,16 +76,8 @@ export function maybeNotifyUpdate(argv = process.argv) {
120
76
  console.error(chalk.dim(`↑ Update available: ${current} → ${cache.latest}. Run \`trawl upgrade\`.`));
121
77
  }
122
78
  catch {
123
- // Never let the notifier turn a successful command into a failure.
124
79
  }
125
80
  }
126
- /**
127
- * Non-blocking background refresh. When the cache is missing or older than
128
- * CHECK_INTERVAL_MS, spawns a fully detached child (updateCheckWorker.ts)
129
- * that queries npm and writes the refreshed cache, then exits — this
130
- * invocation never waits on it (`.unref()`), so the refreshed cache is only
131
- * ever used by the NEXT invocation. Never throws.
132
- */
133
81
  export function scheduleUpdateCheck() {
134
82
  try {
135
83
  if (isOptedOut())
@@ -145,6 +93,5 @@ export function scheduleUpdateCheck() {
145
93
  child.unref();
146
94
  }
147
95
  catch {
148
- // A failed background-refresh spawn must never affect the caller.
149
96
  }
150
97
  }
@@ -2,12 +2,4 @@ export declare function validateObjectId(id: string): void;
2
2
  export declare function requireString(value: unknown, name: string): string;
3
3
  export declare function requireUrl(value: unknown, name: string): string;
4
4
  export declare function requireJwt(value: unknown, name: string): string;
5
- /**
6
- * Like requireJwt, but also rejects a JWT whose `exp` claim has already
7
- * passed — `trawl login --token <expired>` / `TRAWL_TOKEN=<expired>` must
8
- * not print "✓ Logged in" for a token that can't actually authenticate.
9
- * A JWT with an undecodable/opaque payload is let through (we can't prove
10
- * it's expired), mirroring `trawl token`'s advisory-only handling of the
11
- * same case. (#68)
12
- */
13
5
  export declare function requireFreshJwt(value: unknown, name: string): string;
@@ -33,14 +33,6 @@ export function requireJwt(value, name) {
33
33
  }
34
34
  return str;
35
35
  }
36
- /**
37
- * Like requireJwt, but also rejects a JWT whose `exp` claim has already
38
- * passed — `trawl login --token <expired>` / `TRAWL_TOKEN=<expired>` must
39
- * not print "✓ Logged in" for a token that can't actually authenticate.
40
- * A JWT with an undecodable/opaque payload is let through (we can't prove
41
- * it's expired), mirroring `trawl token`'s advisory-only handling of the
42
- * same case. (#68)
43
- */
44
36
  export function requireFreshJwt(value, name) {
45
37
  const jwt = requireJwt(value, name);
46
38
  const exp = decodeExp(jwt);
@@ -1,15 +1,3 @@
1
- /**
2
- * Shared version plumbing — the package name + "what version am I" + "what's
3
- * published" primitives, used by BOTH `trawl upgrade` (src/commands/upgrade.ts)
4
- * and the passive update notifier (src/lib/updateNotifier.ts). Extracted so
5
- * neither duplicates the npm-registry lookup or the package.json read (#129).
6
- */
7
1
  export declare const PKG_NAME = "@trawlme/cli";
8
- /** The version currently RUNNING — read from this package's own package.json.
9
- * (At publish time semantic-release stamps the real version into the tarball's
10
- * package.json, so at runtime this is the installed version; the repo's
11
- * checked-in value is vestigial.) */
12
2
  export declare function currentVersion(): string;
13
- /** Latest published version from the npm registry. Throws with a friendly
14
- * message when npm is missing or the registry is unreachable. */
15
3
  export declare function fetchLatestVersion(): Promise<string>;
@@ -4,24 +4,12 @@ import { readFileSync } from 'node:fs';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { dirname, join } from 'node:path';
6
6
  const execFileP = promisify(execFile);
7
- /**
8
- * Shared version plumbing — the package name + "what version am I" + "what's
9
- * published" primitives, used by BOTH `trawl upgrade` (src/commands/upgrade.ts)
10
- * and the passive update notifier (src/lib/updateNotifier.ts). Extracted so
11
- * neither duplicates the npm-registry lookup or the package.json read (#129).
12
- */
13
7
  export const PKG_NAME = '@trawlme/cli';
14
- /** The version currently RUNNING — read from this package's own package.json.
15
- * (At publish time semantic-release stamps the real version into the tarball's
16
- * package.json, so at runtime this is the installed version; the repo's
17
- * checked-in value is vestigial.) */
18
8
  export function currentVersion() {
19
- const here = dirname(fileURLToPath(import.meta.url)); // dist/lib
9
+ const here = dirname(fileURLToPath(import.meta.url));
20
10
  const pkg = JSON.parse(readFileSync(join(here, '..', '..', 'package.json'), 'utf8'));
21
11
  return pkg.version;
22
12
  }
23
- /** Latest published version from the npm registry. Throws with a friendly
24
- * message when npm is missing or the registry is unreachable. */
25
13
  export async function fetchLatestVersion() {
26
14
  try {
27
15
  const { stdout } = await execFileP('npm', ['view', PKG_NAME, 'version'], { timeout: 30_000 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trawlme/cli",
3
- "version": "3.11.0",
3
+ "version": "3.12.1",
4
4
  "description": "Trawl CLI — manage scraps from the terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "url": "https://github.com/comes-io/trawl_cli/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@trawlme/skills": "1.3.4",
44
+ "@trawlme/skills": "1.4.4",
45
45
  "chalk": "^5.6.2",
46
46
  "commander": "^14.0.3",
47
47
  "conf": "^15.1.0",