@tiny-fish/cli 0.30.2-next.256 → 0.30.2-next.258

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 (77) hide show
  1. package/dist/commands/auth.d.ts +1 -1
  2. package/dist/commands/auth.js +52 -52
  3. package/dist/commands/batch.d.ts +1 -1
  4. package/dist/commands/batch.js +54 -46
  5. package/dist/commands/browser.d.ts +1 -1
  6. package/dist/commands/browser.js +14 -14
  7. package/dist/commands/config-claude.d.ts +1 -1
  8. package/dist/commands/config-claude.js +22 -22
  9. package/dist/commands/connect.d.ts +3 -3
  10. package/dist/commands/connect.js +150 -150
  11. package/dist/commands/doctor.d.ts +4 -4
  12. package/dist/commands/doctor.js +104 -104
  13. package/dist/commands/fetch.d.ts +1 -1
  14. package/dist/commands/fetch.js +24 -27
  15. package/dist/commands/onboard.d.ts +1 -1
  16. package/dist/commands/onboard.js +50 -50
  17. package/dist/commands/profile.d.ts +3 -3
  18. package/dist/commands/profile.js +45 -45
  19. package/dist/commands/run.d.ts +1 -1
  20. package/dist/commands/run.js +78 -65
  21. package/dist/commands/runs.d.ts +1 -1
  22. package/dist/commands/runs.js +63 -50
  23. package/dist/commands/search.d.ts +1 -1
  24. package/dist/commands/search.js +16 -16
  25. package/dist/commands/upgrade.d.ts +2 -2
  26. package/dist/commands/upgrade.js +36 -36
  27. package/dist/commands/vault.d.ts +1 -1
  28. package/dist/commands/vault.js +48 -45
  29. package/dist/commands/wallet.d.ts +1 -1
  30. package/dist/commands/wallet.js +14 -14
  31. package/dist/index.js +2 -2
  32. package/dist/lib/auth.d.ts +5 -5
  33. package/dist/lib/auth.js +56 -56
  34. package/dist/lib/batch-store.d.ts +1 -1
  35. package/dist/lib/batch-store.js +4 -4
  36. package/dist/lib/chrome-cookies.d.ts +1 -1
  37. package/dist/lib/chrome-cookies.js +24 -24
  38. package/dist/lib/claude-config.js +37 -37
  39. package/dist/lib/cli-install.js +12 -12
  40. package/dist/lib/client.d.ts +2 -2
  41. package/dist/lib/client.js +61 -61
  42. package/dist/lib/connect-all.js +140 -134
  43. package/dist/lib/connect-auth.d.ts +2 -2
  44. package/dist/lib/connect-auth.js +15 -15
  45. package/dist/lib/connect-clients.d.ts +6 -6
  46. package/dist/lib/connect-clients.js +143 -143
  47. package/dist/lib/connect-fallback.d.ts +1 -1
  48. package/dist/lib/connect-fallback.js +38 -38
  49. package/dist/lib/connect-picker.d.ts +4 -4
  50. package/dist/lib/connect-picker.js +28 -28
  51. package/dist/lib/connect-runtime.d.ts +7 -7
  52. package/dist/lib/connect-runtime.js +61 -61
  53. package/dist/lib/constants.js +13 -13
  54. package/dist/lib/cursor-config.d.ts +1 -1
  55. package/dist/lib/cursor-config.js +30 -30
  56. package/dist/lib/doctor-report.d.ts +2 -2
  57. package/dist/lib/doctor-report.js +22 -22
  58. package/dist/lib/harness-detect.js +39 -39
  59. package/dist/lib/harness.js +9 -9
  60. package/dist/lib/hermes-env.js +26 -23
  61. package/dist/lib/install-root.js +12 -12
  62. package/dist/lib/notice.d.ts +1 -1
  63. package/dist/lib/notice.js +12 -12
  64. package/dist/lib/output.js +14 -14
  65. package/dist/lib/registration-detect.d.ts +3 -3
  66. package/dist/lib/registration-detect.js +82 -82
  67. package/dist/lib/setup-telemetry.d.ts +4 -4
  68. package/dist/lib/setup-telemetry.js +34 -34
  69. package/dist/lib/signals.js +1 -1
  70. package/dist/lib/skill-install.d.ts +3 -3
  71. package/dist/lib/skill-install.js +40 -40
  72. package/dist/lib/types.d.ts +8 -8
  73. package/dist/lib/verify.d.ts +1 -1
  74. package/dist/lib/verify.js +28 -28
  75. package/dist/program.d.ts +1 -1
  76. package/dist/program.js +28 -28
  77. package/package.json +18 -9
@@ -1,10 +1,10 @@
1
- import { createHash } from "node:crypto";
2
- import { z } from "zod";
3
- import { validateKeyFormat } from "./auth.js";
4
- import { BASE_URL, CLI_VERSION } from "./constants.js";
5
- import { ALL_HARNESSES } from "./harness-detect.js";
6
- import { captureNotice } from "./notice.js";
7
- import { errLine } from "./output.js";
1
+ import { createHash } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import { validateKeyFormat } from './auth.js';
4
+ import { BASE_URL, CLI_VERSION } from './constants.js';
5
+ import { ALL_HARNESSES } from './harness-detect.js';
6
+ import { captureNotice } from './notice.js';
7
+ import { errLine } from './output.js';
8
8
  const CONNECT_EVENT_TIMEOUT_MS = 2_000;
9
9
  /**
10
10
  * `TINYFISH_NO_TELEMETRY` suppresses ALL CLI-emitted network analytics
@@ -12,13 +12,13 @@ const CONNECT_EVENT_TIMEOUT_MS = 2_000;
12
12
  * opts out — a privacy switch must not fail closed on `true`.
13
13
  */
14
14
  export function telemetryDisabled() {
15
- const value = (process.env["TINYFISH_NO_TELEMETRY"] ?? "").trim().toLowerCase();
16
- return value !== "" && value !== "0" && value !== "false";
15
+ const value = (process.env['TINYFISH_NO_TELEMETRY'] ?? '').trim().toLowerCase();
16
+ return value !== '' && value !== '0' && value !== 'false';
17
17
  }
18
18
  // TinyFish-owned domains plus loopback are the only origins the API key may be sent to.
19
19
  // `--url` accepts any http(s) URL, so a user-supplied origin must never receive the key.
20
- const TRUSTED_TELEMETRY_HOST_SUFFIXES = [".tinyfish.ai", ".tinyfish.io"];
21
- const TRUSTED_TELEMETRY_LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
20
+ const TRUSTED_TELEMETRY_HOST_SUFFIXES = ['.tinyfish.ai', '.tinyfish.io'];
21
+ const TRUSTED_TELEMETRY_LOOPBACK_HOSTS = ['localhost', '127.0.0.1', '[::1]'];
22
22
  export function isTrustedTelemetryOrigin(endpoint) {
23
23
  let url;
24
24
  try {
@@ -32,15 +32,15 @@ export function isTrustedTelemetryOrigin(endpoint) {
32
32
  // key is never sent over cleartext.
33
33
  if (TRUSTED_TELEMETRY_LOOPBACK_HOSTS.includes(host))
34
34
  return true;
35
- if (url.protocol !== "https:")
35
+ if (url.protocol !== 'https:')
36
36
  return false;
37
37
  return TRUSTED_TELEMETRY_HOST_SUFFIXES.some((suffix) => host === suffix.slice(1) || host.endsWith(suffix));
38
38
  }
39
39
  /** SHA-256 lookup key only, and only to trusted origins — the raw key never rides telemetry. */
40
40
  export function telemetryHeaders(endpoint, apiKey) {
41
- const headers = { "content-type": "application/json" };
41
+ const headers = { 'content-type': 'application/json' };
42
42
  if (apiKey && validateKeyFormat(apiKey) && isTrustedTelemetryOrigin(endpoint)) {
43
- headers["x-api-key-lookup"] = createHash("sha256").update(apiKey).digest("hex");
43
+ headers['x-api-key-lookup'] = createHash('sha256').update(apiKey).digest('hex');
44
44
  }
45
45
  return headers;
46
46
  }
@@ -56,7 +56,7 @@ export async function postConnectEvent(options) {
56
56
  return;
57
57
  // Swallows its own write failure so callers can treat this function as total.
58
58
  const debug = (line) => {
59
- if (!process.env["TINYFISH_DEBUG"])
59
+ if (!process.env['TINYFISH_DEBUG'])
60
60
  return;
61
61
  try {
62
62
  errLine(`${label} telemetry ${line}`);
@@ -70,10 +70,10 @@ export async function postConnectEvent(options) {
70
70
  for (let attempt = 0; attempt < attempts; attempt++) {
71
71
  try {
72
72
  const response = await fetch(endpoint, {
73
- method: "POST",
73
+ method: 'POST',
74
74
  headers,
75
75
  body,
76
- redirect: "error",
76
+ redirect: 'error',
77
77
  signal: AbortSignal.timeout(CONNECT_EVENT_TIMEOUT_MS),
78
78
  });
79
79
  // Before the ok branch so a rejected event still yields a notice; gated on origin so a
@@ -98,7 +98,7 @@ const harnessResultSchema = z.object({
98
98
  harness: z.enum(ALL_HARNESSES),
99
99
  detected: z.boolean(),
100
100
  installed: z.boolean(),
101
- verify_depth: z.enum(["health", "health+auth"]).nullable(),
101
+ verify_depth: z.enum(['health', 'health+auth']).nullable(),
102
102
  verify_ok: z.boolean().nullable(),
103
103
  });
104
104
  export const setupCompletedPayloadSchema = z.object({
@@ -106,25 +106,25 @@ export const setupCompletedPayloadSchema = z.object({
106
106
  .array(harnessResultSchema)
107
107
  .length(ALL_HARNESSES.length)
108
108
  .refine((results) => new Set(results.map((r) => r.harness)).size === ALL_HARNESSES.length, {
109
- message: "expected one result per harness",
109
+ message: 'expected one result per harness',
110
110
  }),
111
111
  cli_version: z.string(),
112
112
  // Segments picker runs from --all runs; the server field shipped first.
113
- mode: z.enum(["all", "pick", "pick_numeric"]).optional(),
113
+ mode: z.enum(['all', 'pick', 'pick_numeric']).optional(),
114
114
  });
115
115
  /** `toVersion` is null when the run was interrupted or the installed version could not be read. */
116
116
  export async function sendUpgradeCompleted(fromVersion, toVersion, outcome, scope, apiKey) {
117
117
  await postConnectEvent({
118
- endpoint: new URL("/api/cli/connect-event", BASE_URL).toString(),
118
+ endpoint: new URL('/api/cli/connect-event', BASE_URL).toString(),
119
119
  body: JSON.stringify({
120
- event: "upgrade_completed",
120
+ event: 'upgrade_completed',
121
121
  from_version: fromVersion,
122
122
  to_version: toVersion,
123
123
  outcome,
124
124
  scope,
125
125
  }),
126
126
  attempts: 1,
127
- label: "upgrade",
127
+ label: 'upgrade',
128
128
  apiKey,
129
129
  });
130
130
  }
@@ -141,18 +141,18 @@ export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode) {
141
141
  cli_version: CLI_VERSION,
142
142
  ...(mode ? { mode } : {}),
143
143
  });
144
- endpoint = new URL("/api/cli/connect-event", mcpUrl).toString();
145
- body = JSON.stringify({ event: "setup_completed", ...payload });
144
+ endpoint = new URL('/api/cli/connect-event', mcpUrl).toString();
145
+ body = JSON.stringify({ event: 'setup_completed', ...payload });
146
146
  }
147
147
  catch (e) {
148
- if (process.env["TINYFISH_DEBUG"]) {
148
+ if (process.env['TINYFISH_DEBUG']) {
149
149
  errLine(`setup telemetry skipped: ${e instanceof Error ? e.message : String(e)}`);
150
150
  }
151
151
  return;
152
152
  }
153
- await postConnectEvent({ endpoint, body, attempts: 2, label: "setup", apiKey });
153
+ await postConnectEvent({ endpoint, body, attempts: 2, label: 'setup', apiKey });
154
154
  }
155
- export const DOCTOR_HARNESS_SCOPES = ["all", ...ALL_HARNESSES];
155
+ export const DOCTOR_HARNESS_SCOPES = ['all', ...ALL_HARNESSES];
156
156
  // Aggregate only: per-harness rows and check `detail` strings stay local, so nothing
157
157
  // user-authored or credential-shaped can ride a doctor run to PostHog.
158
158
  // Caps mirror the route's schema: a payload the server would 400 is dropped here instead,
@@ -168,10 +168,10 @@ export const doctorCompletedPayloadSchema = z.object({
168
168
  checks_skip: z.int().nonnegative().max(256),
169
169
  /** Offered by the pre-repair report; the verdicts describe the machine after it. */
170
170
  repairs_offered: z.int().nonnegative().max(256),
171
- repair_actions: z.array(z.enum(["connect", "auth-login"])).max(8),
171
+ repair_actions: z.array(z.enum(['connect', 'auth-login'])).max(8),
172
172
  repairs_attempted: z.int().nonnegative().max(256),
173
173
  repairs_succeeded: z.int().nonnegative().max(256),
174
- fix_mode: z.enum(["none", "fix", "fix_yes"]),
174
+ fix_mode: z.enum(['none', 'fix', 'fix_yes']),
175
175
  /** "all" unless --harness scoped the run; the counts above cover only that scope. */
176
176
  harness_scope: z.enum(DOCTOR_HARNESS_SCOPES),
177
177
  is_human_initiated: z.boolean(),
@@ -184,15 +184,15 @@ export async function sendDoctorCompleted(mcpUrl, payload, apiKey) {
184
184
  try {
185
185
  // Parse inside try — a schema violation must not fail a diagnosis that already ran.
186
186
  const parsed = doctorCompletedPayloadSchema.parse(payload);
187
- endpoint = new URL("/api/cli/connect-event", mcpUrl).toString();
188
- body = JSON.stringify({ event: "doctor_completed", ...parsed });
187
+ endpoint = new URL('/api/cli/connect-event', mcpUrl).toString();
188
+ body = JSON.stringify({ event: 'doctor_completed', ...parsed });
189
189
  }
190
190
  catch (e) {
191
- if (process.env["TINYFISH_DEBUG"]) {
191
+ if (process.env['TINYFISH_DEBUG']) {
192
192
  errLine(`doctor telemetry skipped: ${e instanceof Error ? e.message : String(e)}`);
193
193
  }
194
194
  return;
195
195
  }
196
196
  // One attempt: doctor just probed this origin, so a stale keep-alive socket is not the risk.
197
- await postConnectEvent({ endpoint, body, attempts: 1, label: "doctor", apiKey });
197
+ await postConnectEvent({ endpoint, body, attempts: 1, label: 'doctor', apiKey });
198
198
  }
@@ -7,7 +7,7 @@ export const SIGNAL_EXIT_CODES = { SIGHUP: 129, SIGINT: 130, SIGTERM: 143 };
7
7
  */
8
8
  export function installSignalGuard(onSignal) {
9
9
  let handling = false;
10
- const handlers = ["SIGINT", "SIGTERM", "SIGHUP"].map((signal) => {
10
+ const handlers = ['SIGINT', 'SIGTERM', 'SIGHUP'].map((signal) => {
11
11
  const handler = () => {
12
12
  const exit = () => process.exit(SIGNAL_EXIT_CODES[signal]);
13
13
  if (handling)
@@ -1,9 +1,9 @@
1
- import { type InstallOptions } from "./cli-install.js";
2
- import { type SkillAgent } from "./connect-clients.js";
1
+ import { type InstallOptions } from './cli-install.js';
2
+ import { type SkillAgent } from './connect-clients.js';
3
3
  export declare function installWebSkill(client: {
4
4
  skillAgent?: SkillAgent;
5
5
  displayName: string;
6
- }, { verbose }: Pick<InstallOptions, "verbose">): void;
6
+ }, { verbose }: Pick<InstallOptions, 'verbose'>): void;
7
7
  /**
8
8
  * Refresh the skill we installed, for the harnesses we installed it in.
9
9
  *
@@ -1,29 +1,29 @@
1
- import * as path from "node:path";
2
- import spawn from "cross-spawn";
3
- import { z } from "zod";
4
- import { loadConfig } from "./auth.js";
5
- import { captureStdio, capturedOutput, replay, SKILL_INSTALL_TIMEOUT_MS, STEP_MAX_BUFFER, } from "./cli-install.js";
6
- import { CURSOR_SKILL_TARGET, NATIVE_MCP_CLIENTS, OPENCLAW_SKILL_INSTALL_ARGS, } from "./connect-clients.js";
7
- import { commandNotFound, spawnStepError, throwIfInterrupted } from "./connect-runtime.js";
8
- import { CLI_AGENT_IDENTITY } from "./constants.js";
9
- import { errLine } from "./output.js";
1
+ import * as path from 'node:path';
2
+ import spawn from 'cross-spawn';
3
+ import { z } from 'zod';
4
+ import { loadConfig } from './auth.js';
5
+ import { captureStdio, capturedOutput, replay, SKILL_INSTALL_TIMEOUT_MS, STEP_MAX_BUFFER, } from './cli-install.js';
6
+ import { CURSOR_SKILL_TARGET, NATIVE_MCP_CLIENTS, OPENCLAW_SKILL_INSTALL_ARGS, } from './connect-clients.js';
7
+ import { commandNotFound, spawnStepError, throwIfInterrupted } from './connect-runtime.js';
8
+ import { CLI_AGENT_IDENTITY } from './constants.js';
9
+ import { errLine } from './output.js';
10
10
  // Supports Hermes without node:util.styleText, so the installer still runs on Node 20.11.
11
- const SKILLS_CLI_PACKAGE = "skills@1.5.15";
12
- const TINYFISH_WEB_SKILL_SOURCE = "tinyfish-io/tinyfish-cookbook";
13
- const TINYFISH_WEB_SKILL = "use-tinyfish";
11
+ const SKILLS_CLI_PACKAGE = 'skills@1.5.15';
12
+ const TINYFISH_WEB_SKILL_SOURCE = 'tinyfish-io/tinyfish-cookbook';
13
+ const TINYFISH_WEB_SKILL = 'use-tinyfish';
14
14
  /** `skills add` is an unconditional overwrite, so it doubles as the refresh path. */
15
15
  function skillAddArgs(skillAgents) {
16
16
  return [
17
- "-y",
17
+ '-y',
18
18
  SKILLS_CLI_PACKAGE,
19
- "add",
19
+ 'add',
20
20
  TINYFISH_WEB_SKILL_SOURCE,
21
- "--skill",
21
+ '--skill',
22
22
  TINYFISH_WEB_SKILL,
23
- "--global",
24
- "--agent",
23
+ '--global',
24
+ '--agent',
25
25
  ...skillAgents,
26
- "--yes",
26
+ '--yes',
27
27
  ];
28
28
  }
29
29
  // npx resolves from PATH, which a global npm install may not have exported yet.
@@ -32,7 +32,7 @@ function skillSpawnEnv() {
32
32
  ...process.env,
33
33
  // `skills` gates its logo art on agent detection.
34
34
  AI_AGENT: CLI_AGENT_IDENTITY,
35
- PATH: `${path.dirname(process.execPath)}${path.delimiter}${process.env.PATH ?? ""}`,
35
+ PATH: `${path.dirname(process.execPath)}${path.delimiter}${process.env.PATH ?? ''}`,
36
36
  };
37
37
  }
38
38
  // The `skills` update flow exits 0 even when it fails, so status alone proves nothing.
@@ -47,13 +47,13 @@ const SKILL_UNCHECKABLE_PATTERN = /cannot be checked automatically/;
47
47
  const skillListSchema = z.array(z.object({ name: z.string() }));
48
48
  // `add` exits 0 on per-skill failure; the piped list is the verdict.
49
49
  function failedListVerdict() {
50
- const list = spawn.sync("npx", ["-y", SKILLS_CLI_PACKAGE, "list", "--global", "--json"], {
51
- encoding: "utf8",
50
+ const list = spawn.sync('npx', ['-y', SKILLS_CLI_PACKAGE, 'list', '--global', '--json'], {
51
+ encoding: 'utf8',
52
52
  env: skillSpawnEnv(),
53
53
  maxBuffer: STEP_MAX_BUFFER,
54
54
  timeout: SKILL_INSTALL_TIMEOUT_MS,
55
55
  });
56
- const verified = !list.error && list.status === 0 && skillListed(list.stdout ?? "");
56
+ const verified = !list.error && list.status === 0 && skillListed(list.stdout ?? '');
57
57
  return verified ? null : list;
58
58
  }
59
59
  // Entry presence is the whole verdict: `agents` names the harnesses `skills` detects,
@@ -74,7 +74,7 @@ export function installWebSkill(client, { verbose }) {
74
74
  if (!client.skillAgent)
75
75
  return;
76
76
  errLine(`Installing the TinyFish web skill in ${client.displayName}...`);
77
- const add = spawn.sync("npx", skillAddArgs([client.skillAgent]), {
77
+ const add = spawn.sync('npx', skillAddArgs([client.skillAgent]), {
78
78
  ...captureStdio(verbose),
79
79
  env: skillSpawnEnv(),
80
80
  });
@@ -103,9 +103,9 @@ export function updateWebSkill({ verbose }) {
103
103
  const skillAgents = [
104
104
  ...NATIVE_MCP_CLIENTS.filter((client) => connected[client.connectClient]).map((client) => client.skillAgent),
105
105
  // Cursor is not a NativeMcpClient — it writes its own MCP config.
106
- ...(connected["cursor"] ? [CURSOR_SKILL_TARGET.skillAgent] : []),
106
+ ...(connected['cursor'] ? [CURSOR_SKILL_TARGET.skillAgent] : []),
107
107
  ].filter((agent) => agent !== undefined);
108
- const hasOpenClaw = Boolean(connected["openclaw"]);
108
+ const hasOpenClaw = Boolean(connected['openclaw']);
109
109
  // Installs predating PF-3169 recorded nothing; ask `skills` to refresh what it tracks. A
110
110
  // skill-less recorded install (Grok) may still have one from before, so refresh anyway
111
111
  // — but there having been none is then expected, not an upgrade failure.
@@ -124,26 +124,26 @@ export function updateWebSkill({ verbose }) {
124
124
  }
125
125
  function reinstallOpenClawSkill(verbose) {
126
126
  if (verbose)
127
- errLine("Refreshing the TinyFish skill in OpenClaw...");
128
- const result = spawn.sync("openclaw", OPENCLAW_SKILL_INSTALL_ARGS, captureStdio(verbose));
127
+ errLine('Refreshing the TinyFish skill in OpenClaw...');
128
+ const result = spawn.sync('openclaw', OPENCLAW_SKILL_INSTALL_ARGS, captureStdio(verbose));
129
129
  // OpenClaw may have been removed since connect; a stale context entry must not fail an
130
130
  // upgrade that otherwise succeeded.
131
131
  if (commandNotFound(result.error)) {
132
- errLine("OpenClaw is not on PATH, so its TinyFish skill was left unchanged.");
132
+ errLine('OpenClaw is not on PATH, so its TinyFish skill was left unchanged.');
133
133
  return false;
134
134
  }
135
135
  if (result.error || result.status !== 0) {
136
136
  throwIfInterrupted(result);
137
137
  replay(capturedOutput(result));
138
- throw new Error("Could not refresh the TinyFish skill in OpenClaw", { cause: result.error });
138
+ throw new Error('Could not refresh the TinyFish skill in OpenClaw', { cause: result.error });
139
139
  }
140
140
  return true;
141
141
  }
142
142
  function reinstallWebSkill(skillAgents, verbose) {
143
143
  if (verbose)
144
- errLine(`Refreshing the TinyFish web skill for ${skillAgents.join(", ")}...`);
145
- const result = spawn.sync("npx", skillAddArgs(skillAgents), {
146
- encoding: "utf8",
144
+ errLine(`Refreshing the TinyFish web skill for ${skillAgents.join(', ')}...`);
145
+ const result = spawn.sync('npx', skillAddArgs(skillAgents), {
146
+ encoding: 'utf8',
147
147
  env: skillSpawnEnv(),
148
148
  maxBuffer: STEP_MAX_BUFFER,
149
149
  timeout: SKILL_INSTALL_TIMEOUT_MS,
@@ -158,7 +158,7 @@ function reinstallWebSkill(skillAgents, verbose) {
158
158
  if (verbose || failed)
159
159
  replay(output);
160
160
  if (failed) {
161
- throw new Error("Could not refresh the TinyFish web skill", {
161
+ throw new Error('Could not refresh the TinyFish web skill', {
162
162
  cause: (failedList ?? result).error,
163
163
  });
164
164
  }
@@ -166,9 +166,9 @@ function reinstallWebSkill(skillAgents, verbose) {
166
166
  /** Fallback when no skill-bearing harness is recorded: refresh whatever `skills` tracks. */
167
167
  function refreshWebSkillByHash(verbose, untrackedIsExpected = false) {
168
168
  if (verbose)
169
- errLine("Refreshing the TinyFish web skill...");
170
- const result = spawn.sync("npx", ["-y", SKILLS_CLI_PACKAGE, "update", TINYFISH_WEB_SKILL, "--global", "--yes"], {
171
- encoding: "utf8",
169
+ errLine('Refreshing the TinyFish web skill...');
170
+ const result = spawn.sync('npx', ['-y', SKILLS_CLI_PACKAGE, 'update', TINYFISH_WEB_SKILL, '--global', '--yes'], {
171
+ encoding: 'utf8',
172
172
  env: skillSpawnEnv(),
173
173
  maxBuffer: STEP_MAX_BUFFER,
174
174
  timeout: SKILL_INSTALL_TIMEOUT_MS,
@@ -182,15 +182,15 @@ function refreshWebSkillByHash(verbose, untrackedIsExpected = false) {
182
182
  if (verbose || failed || unchecked)
183
183
  replay(output);
184
184
  if (failed)
185
- throw new Error("Could not refresh the TinyFish web skill", { cause: result.error });
185
+ throw new Error('Could not refresh the TinyFish web skill', { cause: result.error });
186
186
  if (unchecked) {
187
- errLine("Run `tinyfish connect <client>` to reinstall the skill and restore update tracking.");
187
+ errLine('Run `tinyfish connect <client>` to reinstall the skill and restore update tracking.');
188
188
  if (untrackedIsExpected)
189
189
  return false;
190
- throw new Error("The TinyFish web skill cannot be checked for updates");
190
+ throw new Error('The TinyFish web skill cannot be checked for updates');
191
191
  }
192
192
  if (SKILL_NOT_INSTALLED_PATTERN.test(output)) {
193
- errLine("No use-tinyfish skill installed. Run `tinyfish connect <client>` to add it.");
193
+ errLine('No use-tinyfish skill installed. Run `tinyfish connect <client>` to add it.');
194
194
  return false;
195
195
  }
196
196
  return !SKILL_ALREADY_CURRENT_PATTERN.test(output);
@@ -1,10 +1,10 @@
1
- import type { AgentRunParams, BrowserProfile, Run, RunStatus } from "@tiny-fish/sdk";
1
+ import type { AgentRunParams, BrowserProfile, Run, RunStatus } from '@tiny-fish/sdk';
2
2
  export type OutputSchema = Record<string, unknown>;
3
3
  export interface CliAgentRunParams extends AgentRunParams {
4
4
  output_schema?: OutputSchema;
5
5
  agent_config?: {
6
- mode?: "default" | "strict";
7
- cursor_style?: "fish" | "standard" | "green-pointer";
6
+ mode?: 'default' | 'strict';
7
+ cursor_style?: 'fish' | 'standard' | 'green-pointer';
8
8
  max_steps?: number;
9
9
  max_duration_seconds?: number;
10
10
  };
@@ -21,7 +21,7 @@ export interface CliAgentRunParams extends AgentRunParams {
21
21
  use_vault?: boolean;
22
22
  credential_item_ids?: string[];
23
23
  }
24
- export type VaultProvider = "1password" | "bitwarden";
24
+ export type VaultProvider = '1password' | 'bitwarden';
25
25
  export interface VaultConnection {
26
26
  id: string;
27
27
  provider: VaultProvider;
@@ -31,7 +31,7 @@ export interface VaultConnection {
31
31
  export interface VaultFieldMetadata {
32
32
  fieldId: string;
33
33
  label: string;
34
- type: "STRING" | "CONCEALED" | "OTP";
34
+ type: 'STRING' | 'CONCEALED' | 'OTP';
35
35
  }
36
36
  export interface VaultItem {
37
37
  itemId: string;
@@ -43,10 +43,10 @@ export interface VaultItem {
43
43
  hasTotp: boolean;
44
44
  }
45
45
  export type VaultConnectRequest = {
46
- provider: "1password";
46
+ provider: '1password';
47
47
  token: string;
48
48
  } | {
49
- provider: "bitwarden";
49
+ provider: 'bitwarden';
50
50
  clientId: string;
51
51
  clientSecret: string;
52
52
  masterPassword: string;
@@ -93,7 +93,7 @@ export interface RunStepsResponse {
93
93
  }
94
94
  export interface CliBrowserSessionCreateParams {
95
95
  url?: string;
96
- browser_profile?: "lite" | "stealth";
96
+ browser_profile?: 'lite' | 'stealth';
97
97
  }
98
98
  export interface CancelRunResponse {
99
99
  run_id: string;
@@ -1,4 +1,4 @@
1
- export type VerifyDepth = "health" | "health+auth";
1
+ export type VerifyDepth = 'health' | 'health+auth';
2
2
  export interface VerifyResult {
3
3
  depth: VerifyDepth;
4
4
  ok: boolean;
@@ -1,62 +1,62 @@
1
- import { listRuns } from "./client.js";
2
- import { ApiError } from "./output.js";
1
+ import { listRuns } from './client.js';
2
+ import { ApiError } from './output.js';
3
3
  const VERIFY_TIMEOUT_MS = 5_000;
4
4
  // `fetch` collapses every network fault to "fetch failed" and hides the cause on `e.cause`.
5
5
  function networkCode(e) {
6
6
  const cause = e?.cause;
7
7
  switch (cause?.code) {
8
- case "ECONNREFUSED":
9
- return "connection refused";
10
- case "ENOTFOUND":
11
- return "host not found";
12
- case "ETIMEDOUT":
13
- return "connection timed out";
14
- case "CERT_HAS_EXPIRED":
15
- case "UNABLE_TO_VERIFY_LEAF_SIGNATURE":
16
- case "DEPTH_ZERO_SELF_SIGNED_CERT":
17
- return "TLS verification failed";
8
+ case 'ECONNREFUSED':
9
+ return 'connection refused';
10
+ case 'ENOTFOUND':
11
+ return 'host not found';
12
+ case 'ETIMEDOUT':
13
+ return 'connection timed out';
14
+ case 'CERT_HAS_EXPIRED':
15
+ case 'UNABLE_TO_VERIFY_LEAF_SIGNATURE':
16
+ case 'DEPTH_ZERO_SELF_SIGNED_CERT':
17
+ return 'TLS verification failed';
18
18
  default:
19
- return e instanceof Error && e.name === "TimeoutError"
20
- ? "request timed out"
21
- : "could not reach the endpoint";
19
+ return e instanceof Error && e.name === 'TimeoutError'
20
+ ? 'request timed out'
21
+ : 'could not reach the endpoint';
22
22
  }
23
23
  }
24
24
  function authCode(e) {
25
25
  if (!(e instanceof ApiError))
26
- return "the authenticated call failed";
26
+ return 'the authenticated call failed';
27
27
  if (e.status === 401)
28
- return "the API key is invalid or expired";
28
+ return 'the API key is invalid or expired';
29
29
  if (e.status === 403)
30
- return "the API key is not allowed to do this";
30
+ return 'the API key is not allowed to do this';
31
31
  if (e.status === 429)
32
- return "rate limited";
33
- return e.status >= 500 ? "TinyFish returned a server error" : `the API returned HTTP ${e.status}`;
32
+ return 'rate limited';
33
+ return e.status >= 500 ? 'TinyFish returned a server error' : `the API returned HTTP ${e.status}`;
34
34
  }
35
35
  /** Reachability check. Verify failure is a warning, never install failure. */
36
36
  export async function verifyMcpHealth(mcpUrl) {
37
37
  try {
38
38
  const response = await fetch(mcpUrl, {
39
- method: "GET",
39
+ method: 'GET',
40
40
  // Redirect (captive portal, typo'd --url) is not healthy.
41
- redirect: "error",
41
+ redirect: 'error',
42
42
  signal: AbortSignal.timeout(VERIFY_TIMEOUT_MS),
43
43
  });
44
44
  // MCP rejects bare GET with 4xx — still proves endpoint routed.
45
45
  if (response.status >= 500) {
46
46
  return {
47
- depth: "health",
47
+ depth: 'health',
48
48
  ok: false,
49
49
  reason: `endpoint returned HTTP ${response.status}`,
50
50
  code: `the endpoint returned HTTP ${response.status}`,
51
51
  };
52
52
  }
53
- return { depth: "health", ok: true };
53
+ return { depth: 'health', ok: true };
54
54
  }
55
55
  catch (e) {
56
56
  return {
57
- depth: "health",
57
+ depth: 'health',
58
58
  ok: false,
59
- reason: e instanceof Error ? e.message : "network error reaching MCP endpoint",
59
+ reason: e instanceof Error ? e.message : 'network error reaching MCP endpoint',
60
60
  code: networkCode(e),
61
61
  };
62
62
  }
@@ -66,11 +66,11 @@ export async function verifyMcpAuth(apiKey) {
66
66
  try {
67
67
  // Bound it: the SDK default is 600s, and a warning-only check must never hang an install.
68
68
  await listRuns({ limit: 1 }, apiKey, VERIFY_TIMEOUT_MS);
69
- return { depth: "health+auth", ok: true };
69
+ return { depth: 'health+auth', ok: true };
70
70
  }
71
71
  catch (e) {
72
72
  return {
73
- depth: "health+auth",
73
+ depth: 'health+auth',
74
74
  ok: false,
75
75
  reason: e instanceof Error ? e.message : String(e),
76
76
  code: authCode(e),
package/dist/program.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  /** Split from index.ts so tests can drive the real program instead of hand-assembling one. */
3
3
  export declare function buildProgram(): {
4
4
  program: Command;
package/dist/program.js CHANGED
@@ -1,36 +1,36 @@
1
- import { createRequire } from "module";
2
- import { Command } from "commander";
3
- import { registerAuth } from "./commands/auth.js";
4
- import { registerBatch } from "./commands/batch.js";
5
- import { registerFetch } from "./commands/fetch.js";
6
- import { registerBrowser } from "./commands/browser.js";
7
- import { registerProfile } from "./commands/profile.js";
8
- import { registerVault } from "./commands/vault.js";
9
- import { registerWallet } from "./commands/wallet.js";
10
- import { registerRun } from "./commands/run.js";
11
- import { registerRuns } from "./commands/runs.js";
12
- import { registerConfigureClaude } from "./commands/config-claude.js";
13
- import { registerSearch } from "./commands/search.js";
14
- import { registerConnect } from "./commands/connect.js";
15
- import { registerDoctor } from "./commands/doctor.js";
16
- import { registerUpgrade } from "./commands/upgrade.js";
17
- import { registerOnboard } from "./commands/onboard.js";
18
- import { installNoticeEmit } from "./lib/notice.js";
1
+ import { createRequire } from 'module';
2
+ import { Command } from 'commander';
3
+ import { registerAuth } from './commands/auth.js';
4
+ import { registerBatch } from './commands/batch.js';
5
+ import { registerFetch } from './commands/fetch.js';
6
+ import { registerBrowser } from './commands/browser.js';
7
+ import { registerProfile } from './commands/profile.js';
8
+ import { registerVault } from './commands/vault.js';
9
+ import { registerWallet } from './commands/wallet.js';
10
+ import { registerRun } from './commands/run.js';
11
+ import { registerRuns } from './commands/runs.js';
12
+ import { registerConfigureClaude } from './commands/config-claude.js';
13
+ import { registerSearch } from './commands/search.js';
14
+ import { registerConnect } from './commands/connect.js';
15
+ import { registerDoctor } from './commands/doctor.js';
16
+ import { registerUpgrade } from './commands/upgrade.js';
17
+ import { registerOnboard } from './commands/onboard.js';
18
+ import { installNoticeEmit } from './lib/notice.js';
19
19
  /** Split from index.ts so tests can drive the real program instead of hand-assembling one. */
20
20
  export function buildProgram() {
21
- const { version } = createRequire(import.meta.url)("../package.json");
21
+ const { version } = createRequire(import.meta.url)('../package.json');
22
22
  const program = new Command();
23
23
  program
24
- .name("tinyfish")
25
- .description("TinyFish CLI — run web automations from your terminal or agent.")
26
- .version(version, "-V, --version", "Show version")
27
- .helpOption("-h, --help", "Show help")
24
+ .name('tinyfish')
25
+ .description('TinyFish CLI — run web automations from your terminal or agent.')
26
+ .version(version, '-V, --version', 'Show version')
27
+ .helpOption('-h, --help', 'Show help')
28
28
  .addHelpCommand(false)
29
29
  .enablePositionalOptions()
30
- .option("--debug", "Print HTTP requests and responses to stderr (or set TINYFISH_DEBUG=1)")
31
- .hook("preAction", (cmd) => {
30
+ .option('--debug', 'Print HTTP requests and responses to stderr (or set TINYFISH_DEBUG=1)')
31
+ .hook('preAction', (cmd) => {
32
32
  if (cmd.opts().debug) {
33
- process.env["TINYFISH_DEBUG"] = "1";
33
+ process.env['TINYFISH_DEBUG'] = '1';
34
34
  }
35
35
  });
36
36
  registerAuth(program);
@@ -44,8 +44,8 @@ export function buildProgram() {
44
44
  registerVault(program);
45
45
  registerWallet(program);
46
46
  const agentCmd = program
47
- .command("agent")
48
- .description("Agent automation commands")
47
+ .command('agent')
48
+ .description('Agent automation commands')
49
49
  .enablePositionalOptions();
50
50
  const runCmd = registerRun(agentCmd);
51
51
  registerRuns(runCmd);