@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,6 +1,6 @@
1
- import type { Command } from "commander";
2
- import { type DoctorRepair, type DoctorReport } from "../lib/doctor-report.js";
3
- import { type Harness } from "../lib/harness-detect.js";
1
+ import type { Command } from 'commander';
2
+ import { type DoctorRepair, type DoctorReport } from '../lib/doctor-report.js';
3
+ import { type Harness } from '../lib/harness-detect.js';
4
4
  export interface DoctorOptions {
5
5
  mcpUrl: string;
6
6
  harness?: Harness;
@@ -10,7 +10,7 @@ export interface DoctorOptions {
10
10
  export declare function runDoctor(options: DoctorOptions): Promise<DoctorReport>;
11
11
  export interface RepairOutcome {
12
12
  repair: DoctorRepair;
13
- status: "repaired" | "skipped" | "failed";
13
+ status: 'repaired' | 'skipped' | 'failed';
14
14
  reason?: string;
15
15
  }
16
16
  export declare function applyRepairs(repairs: DoctorRepair[], options: DoctorOptions): Promise<RepairOutcome[]>;
@@ -1,14 +1,14 @@
1
- import spawn from "cross-spawn";
2
- import { apiKeyStatus } from "../lib/auth.js";
3
- import { CLI_VERSION } from "../lib/constants.js";
4
- import { DOCTOR_COULD_NOT_RUN, DOCTOR_SCHEMA_VERSION, doctorReportSchema, exitCodeFor, renderPretty, verdictFor, } from "../lib/doctor-report.js";
5
- import { ALL_HARNESSES, AuthMode, Registered } from "../lib/harness-detect.js";
6
- import { detectHumanInitiated } from "../lib/harness.js";
7
- import { err, errLine, out, outLine } from "../lib/output.js";
8
- import { detectRegistrations } from "../lib/registration-detect.js";
9
- import { sendDoctorCompleted } from "../lib/setup-telemetry.js";
10
- import { verifyMcpAuth, verifyMcpHealth } from "../lib/verify.js";
11
- import { connectClaudeCode, connectCodex, connectCursor, connectGrok, connectHermes, connectOpenClaw, connectOpencode, DEFAULT_MCP_URL, } from "./connect.js";
1
+ import spawn from 'cross-spawn';
2
+ import { apiKeyStatus } from '../lib/auth.js';
3
+ import { CLI_VERSION } from '../lib/constants.js';
4
+ import { DOCTOR_COULD_NOT_RUN, DOCTOR_SCHEMA_VERSION, doctorReportSchema, exitCodeFor, renderPretty, verdictFor, } from '../lib/doctor-report.js';
5
+ import { ALL_HARNESSES, AuthMode, Registered } from '../lib/harness-detect.js';
6
+ import { detectHumanInitiated } from '../lib/harness.js';
7
+ import { err, errLine, out, outLine } from '../lib/output.js';
8
+ import { detectRegistrations } from '../lib/registration-detect.js';
9
+ import { sendDoctorCompleted } from '../lib/setup-telemetry.js';
10
+ import { verifyMcpAuth, verifyMcpHealth } from '../lib/verify.js';
11
+ import { connectClaudeCode, connectCodex, connectCursor, connectGrok, connectHermes, connectOpenClaw, connectOpencode, DEFAULT_MCP_URL, } from './connect.js';
12
12
  // A green auth mode with a red auth call proves nothing, so the call's verdict gates the claim.
13
13
  function provesHarnessReach(status, keyAuth, healthOk, mcpUrl) {
14
14
  // An endpoint doctor could not reach proves nothing about a harness pointed at it.
@@ -26,30 +26,30 @@ function provesHarnessReach(status, keyAuth, healthOk, mcpUrl) {
26
26
  // Reports the version, never judges it: staleness is the server's call via X-TF-Notice.
27
27
  function checkCliVersion() {
28
28
  return {
29
- id: "cli-version",
29
+ id: 'cli-version',
30
30
  title: `CLI ${CLI_VERSION}`,
31
- status: "pass",
31
+ status: 'pass',
32
32
  detail: `${CLI_VERSION}; the server reports staleness via X-TF-Notice`,
33
33
  harness: null,
34
- scope: "info",
34
+ scope: 'info',
35
35
  };
36
36
  }
37
37
  async function checkConnectivity(mcpUrl) {
38
38
  const health = await verifyMcpHealth(mcpUrl);
39
39
  return {
40
- id: "mcp-connectivity",
41
- title: "MCP endpoint reachable",
42
- status: health.ok ? "pass" : "fail",
43
- detail: health.ok ? mcpUrl : (health.code ?? "the endpoint is unreachable"),
40
+ id: 'mcp-connectivity',
41
+ title: 'MCP endpoint reachable',
42
+ status: health.ok ? 'pass' : 'fail',
43
+ detail: health.ok ? mcpUrl : (health.code ?? 'the endpoint is unreachable'),
44
44
  // Harness-scoped: the CLI's own reachability is `cli-auth-call`'s claim.
45
45
  harness: null,
46
- scope: "harness",
46
+ scope: 'harness',
47
47
  };
48
48
  }
49
49
  // Query strings carry the connect attempt id, so they differ on every healthy install.
50
50
  function endpointOf(url) {
51
51
  const parsed = new URL(url);
52
- return `${parsed.origin}${parsed.pathname.replace(/\/$/, "")}`;
52
+ return `${parsed.origin}${parsed.pathname.replace(/\/$/, '')}`;
53
53
  }
54
54
  /** Host alone let `/wrong-path` and plain `http://` through and claim the harness reaches us. */
55
55
  function pointsElsewhere(registeredUrl, mcpUrl) {
@@ -61,7 +61,7 @@ function pointsElsewhere(registeredUrl, mcpUrl) {
61
61
  }
62
62
  catch {
63
63
  // An endpoint that will not parse cannot be shown to match, so it does not.
64
- return "an unparseable endpoint";
64
+ return 'an unparseable endpoint';
65
65
  }
66
66
  }
67
67
  /** Either it reads the CLI's key at runtime or it holds a copy: the CLI's own verdict covers both. */
@@ -74,59 +74,59 @@ function keyedVerdict(status, keyAuth, cliKeyMissing) {
74
74
  if (runsOnCliCredential(status)) {
75
75
  return cliKeyMissing
76
76
  ? {
77
- status: "fail",
78
- detail: "registered, but it runs on the CLI credential, which is missing or unusable",
77
+ status: 'fail',
78
+ detail: 'registered, but it runs on the CLI credential, which is missing or unusable',
79
79
  }
80
80
  : // Reachable with a key only when `--url` skipped verification.
81
81
  {
82
- status: "warn",
83
- detail: "registered, but its CLI credential was not verified against this endpoint",
82
+ status: 'warn',
83
+ detail: 'registered, but its CLI credential was not verified against this endpoint',
84
84
  };
85
85
  }
86
86
  // A bare warn beside exit 0 reads as a pass.
87
- const because = status.keyReason ?? "the CLI cannot verify a key it does not hold";
88
- return { status: "warn", detail: `registered, but ${because}, so its reach is unproven` };
87
+ const because = status.keyReason ?? 'the CLI cannot verify a key it does not hold';
88
+ return { status: 'warn', detail: `registered, but ${because}, so its reach is unproven` };
89
89
  }
90
90
  if (keyAuth.ok) {
91
91
  return {
92
- status: "pass",
93
- detail: "registered, auth mode api-key (key verified against the TinyFish API)",
92
+ status: 'pass',
93
+ detail: 'registered, auth mode api-key (key verified against the TinyFish API)',
94
94
  };
95
95
  }
96
96
  if (keyAuth.status === 401) {
97
- return { status: "fail", detail: "registered, but TinyFish rejects its API key" };
97
+ return { status: 'fail', detail: 'registered, but TinyFish rejects its API key' };
98
98
  }
99
99
  // Only 401 blames the key; connect would loop on the rest.
100
100
  if (keyAuth.status === 403) {
101
- return { status: "warn", detail: "registered, but its API key is not allowed to list runs" };
101
+ return { status: 'warn', detail: 'registered, but its API key is not allowed to list runs' };
102
102
  }
103
- return { status: "warn", detail: "registered, key could not be verified" };
103
+ return { status: 'warn', detail: 'registered, key could not be verified' };
104
104
  }
105
105
  function registrationVerdict(status, mcpUrl, keyAuth, cliKeyMissing) {
106
106
  if (!status.detected)
107
- return { status: "skip", detail: "harness not installed" };
107
+ return { status: 'skip', detail: 'harness not installed' };
108
108
  if (status.registered === Registered.Unknown) {
109
- return { status: "warn", detail: status.reason ?? "could not determine registration" };
109
+ return { status: 'warn', detail: status.reason ?? 'could not determine registration' };
110
110
  }
111
111
  if (status.registered === Registered.Yes) {
112
112
  // A `tinyfish` entry aimed at a dev server is registered and still cannot reach TinyFish.
113
113
  const elsewhere = pointsElsewhere(status.registeredUrl, mcpUrl);
114
114
  if (elsewhere) {
115
115
  return {
116
- status: "fail",
116
+ status: 'fail',
117
117
  detail: `registered, but points at ${elsewhere} rather than ${endpointOf(mcpUrl)}`,
118
118
  };
119
119
  }
120
120
  // Ordered before the key check: the harness's own verdict wins.
121
121
  if (status.connected === false) {
122
122
  return {
123
- status: "warn",
124
- detail: "registered, but the harness reports it cannot reach TinyFish; sign in again there",
123
+ status: 'warn',
124
+ detail: 'registered, but the harness reports it cannot reach TinyFish; sign in again there',
125
125
  };
126
126
  }
127
127
  // Symmetric with provesHarnessReach, which already reads a handshake as decisive.
128
128
  if (status.connected === true) {
129
- return { status: "pass", detail: "registered, and the harness reports it reaches TinyFish" };
129
+ return { status: 'pass', detail: 'registered, and the harness reports it reaches TinyFish' };
130
130
  }
131
131
  // Widening past api-key would warn every healthy oauth install.
132
132
  if (status.authMode === AuthMode.ApiKey)
@@ -134,59 +134,59 @@ function registrationVerdict(status, mcpUrl, keyAuth, cliKeyMissing) {
134
134
  // Recorded, not verified: it never feeds provesHarnessReach.
135
135
  if (status.recordedAuthMode) {
136
136
  return {
137
- status: "pass",
137
+ status: 'pass',
138
138
  detail: `registered, auth mode ${status.recordedAuthMode} ` +
139
- "(recorded at connect; this harness does not report its headers)",
139
+ '(recorded at connect; this harness does not report its headers)',
140
140
  };
141
141
  }
142
142
  // A probe that had to explain itself to reach `yes` is the only thing that explains `unknown`.
143
- const because = status.reason ? ` (${status.reason})` : "";
144
- return { status: "pass", detail: `registered, auth mode ${status.authMode}${because}` };
143
+ const because = status.reason ? ` (${status.reason})` : '';
144
+ return { status: 'pass', detail: `registered, auth mode ${status.authMode}${because}` };
145
145
  }
146
146
  // Connected once and now absent is a broken install; never connected is just not set up.
147
147
  return status.connectedBefore
148
- ? { status: "fail", detail: "connected previously but TinyFish is no longer registered" }
149
- : { status: "warn", detail: "installed but TinyFish was never connected" };
148
+ ? { status: 'fail', detail: 'connected previously but TinyFish is no longer registered' }
149
+ : { status: 'warn', detail: 'installed but TinyFish was never connected' };
150
150
  }
151
151
  function checkRegistration(status, mcpUrl, keyAuth, cliKeyMissing) {
152
152
  return {
153
- id: "harness-registration",
153
+ id: 'harness-registration',
154
154
  title: `${status.harness} registration`,
155
155
  harness: status.harness,
156
- scope: "harness",
156
+ scope: 'harness',
157
157
  ...registrationVerdict(status, mcpUrl, keyAuth, cliKeyMissing),
158
158
  };
159
159
  }
160
160
  function checkCredential() {
161
161
  const resolved = apiKeyStatus();
162
162
  const check = {
163
- id: "cli-credential",
164
- title: "CLI credential",
165
- status: "key" in resolved ? "pass" : "fail",
166
- detail: "key" in resolved ? `key present from ${resolved.source}` : resolved.error,
163
+ id: 'cli-credential',
164
+ title: 'CLI credential',
165
+ status: 'key' in resolved ? 'pass' : 'fail',
166
+ detail: 'key' in resolved ? `key present from ${resolved.source}` : resolved.error,
167
167
  harness: null,
168
- scope: "cli",
168
+ scope: 'cli',
169
169
  };
170
- return "key" in resolved ? { check, key: resolved.key } : { check };
170
+ return 'key' in resolved ? { check, key: resolved.key } : { check };
171
171
  }
172
172
  function checkAuthCall(auth) {
173
173
  if (!auth) {
174
174
  return {
175
- id: "cli-auth-call",
176
- title: "Authenticated call",
177
- status: "skip",
178
- detail: "no credential to test",
175
+ id: 'cli-auth-call',
176
+ title: 'Authenticated call',
177
+ status: 'skip',
178
+ detail: 'no credential to test',
179
179
  harness: null,
180
- scope: "cli",
180
+ scope: 'cli',
181
181
  };
182
182
  }
183
183
  return {
184
- id: "cli-auth-call",
185
- title: "Authenticated call",
186
- status: auth.ok ? "pass" : "fail",
187
- detail: auth.ok ? "listRuns succeeded" : (auth.code ?? "the authenticated call failed"),
184
+ id: 'cli-auth-call',
185
+ title: 'Authenticated call',
186
+ status: auth.ok ? 'pass' : 'fail',
187
+ detail: auth.ok ? 'listRuns succeeded' : (auth.code ?? 'the authenticated call failed'),
188
188
  harness: null,
189
- scope: "cli",
189
+ scope: 'cli',
190
190
  };
191
191
  }
192
192
  // `verifyMcpAuth` hits BASE_URL; a sandbox key 401s against prod.
@@ -208,26 +208,26 @@ function verifyHarnessKey(status, cliAuth, mcpUrl) {
208
208
  return runsOnCliCredential(status) ? cliAuth : undefined;
209
209
  }
210
210
  // Only Cursor's repair is a pure local file write; every other one needs a browser sign-in.
211
- const UNATTENDED_SAFE = new Set(["cursor"]);
211
+ const UNATTENDED_SAFE = new Set(['cursor']);
212
212
  function repairsFor(checks, statuses) {
213
213
  const repairs = [];
214
214
  // A revoked-but-well-formed key passes the credential check and fails the call; both need login.
215
- const credentialBroken = checks.some((c) => (c.id === "cli-credential" || c.id === "cli-auth-call") && c.status === "fail");
215
+ const credentialBroken = checks.some((c) => (c.id === 'cli-credential' || c.id === 'cli-auth-call') && c.status === 'fail');
216
216
  // connect writes the stored key, so replace a dead one first.
217
217
  if (credentialBroken) {
218
218
  repairs.push({
219
- for: "cli-credential-invalid",
220
- action: "auth-login",
219
+ for: 'cli-credential-invalid',
220
+ action: 'auth-login',
221
221
  harness: null,
222
- command: "tinyfish auth login",
222
+ command: 'tinyfish auth login',
223
223
  unattended_safe: false,
224
224
  });
225
225
  }
226
- const authCallPassed = checks.some((c) => c.id === "cli-auth-call" && c.status === "pass");
226
+ const authCallPassed = checks.some((c) => c.id === 'cli-auth-call' && c.status === 'pass');
227
227
  for (const status of statuses) {
228
228
  // `unknown` earns nothing: connect would contradict the check's detail.
229
229
  // A registered entry that still fails earns one too.
230
- const brokenRegistration = checks.some((c) => c.harness === status.harness && c.status === "fail");
230
+ const brokenRegistration = checks.some((c) => c.harness === status.harness && c.status === 'fail');
231
231
  if (!status.detected || (status.registered !== Registered.No && !brokenRegistration))
232
232
  continue;
233
233
  repairs.push({
@@ -236,7 +236,7 @@ function repairsFor(checks, statuses) {
236
236
  : status.connectedBefore
237
237
  ? `${status.harness}-registration-lost`
238
238
  : `${status.harness}-not-connected`,
239
- action: "connect",
239
+ action: 'connect',
240
240
  harness: status.harness,
241
241
  command: `tinyfish connect ${status.harness}`,
242
242
  // A dead well-formed key passes the format check.
@@ -261,7 +261,7 @@ export async function runDoctor(options) {
261
261
  credential.check,
262
262
  checkAuthCall(cliAuthResult),
263
263
  ];
264
- const healthOk = checks.some((c) => c.id === "mcp-connectivity" && c.status === "pass");
264
+ const healthOk = checks.some((c) => c.id === 'mcp-connectivity' && c.status === 'pass');
265
265
  const harnesses = statuses.map((status, i) => ({
266
266
  harness: status.harness,
267
267
  detected: status.detected,
@@ -287,18 +287,18 @@ export async function runDoctor(options) {
287
287
  function runAuthLogin() {
288
288
  const entry = process.argv[1];
289
289
  if (!entry)
290
- throw new Error("Could not locate the TinyFish CLI entry point to re-invoke");
291
- const result = spawn.sync(process.execPath, [entry, "auth", "login", "--source", "doctor"], {
292
- stdio: "inherit",
290
+ throw new Error('Could not locate the TinyFish CLI entry point to re-invoke');
291
+ const result = spawn.sync(process.execPath, [entry, 'auth', 'login', '--source', 'doctor'], {
292
+ stdio: 'inherit',
293
293
  });
294
294
  if (result.error)
295
- throw new Error("Could not run `tinyfish auth login`", { cause: result.error });
295
+ throw new Error('Could not run `tinyfish auth login`', { cause: result.error });
296
296
  if (result.status !== 0) {
297
- throw new Error(`\`tinyfish auth login\` exited ${result.status ?? "unknown"}`);
297
+ throw new Error(`\`tinyfish auth login\` exited ${result.status ?? 'unknown'}`);
298
298
  }
299
299
  }
300
300
  const CONNECTORS = {
301
- "claude-code": (opts) => connectClaudeCode({ ...opts, launch: false }),
301
+ 'claude-code': (opts) => connectClaudeCode({ ...opts, launch: false }),
302
302
  codex: (opts) => connectCodex({ ...opts, launch: false }),
303
303
  cursor: (opts) => connectCursor(opts),
304
304
  grok: (opts) => connectGrok({ ...opts, launch: false }),
@@ -308,7 +308,7 @@ const CONNECTORS = {
308
308
  };
309
309
  /** Dispatches on `action`, never on a null harness: that conflation made the credential fix a no-op. */
310
310
  async function runRepair(repair, mcpUrl) {
311
- if (repair.action === "auth-login")
311
+ if (repair.action === 'auth-login')
312
312
  return runAuthLogin();
313
313
  if (repair.harness)
314
314
  await CONNECTORS[repair.harness]({ mcpUrl });
@@ -320,32 +320,32 @@ export async function applyRepairs(repairs, options) {
320
320
  if (!interactive && !options.yes) {
321
321
  outcomes.push({
322
322
  repair,
323
- status: "skipped",
324
- reason: "no terminal; re-run with --yes to repair unattended",
323
+ status: 'skipped',
324
+ reason: 'no terminal; re-run with --yes to repair unattended',
325
325
  });
326
326
  continue;
327
327
  }
328
328
  if (!interactive && !repair.unattended_safe) {
329
329
  outcomes.push({
330
330
  repair,
331
- status: "skipped",
332
- reason: "needs a browser sign-in, which has no unattended path",
331
+ status: 'skipped',
332
+ reason: 'needs a browser sign-in, which has no unattended path',
333
333
  });
334
334
  continue;
335
335
  }
336
336
  try {
337
337
  await runRepair(repair, options.mcpUrl);
338
- outcomes.push({ repair, status: "repaired" });
338
+ outcomes.push({ repair, status: 'repaired' });
339
339
  }
340
340
  catch (e) {
341
341
  // Connect quotes absolute config paths in its errors, so the raw text stays behind
342
342
  // --debug and the user is told where to find it rather than handed a generic dead end.
343
- if (process.env["TINYFISH_DEBUG"]) {
343
+ if (process.env['TINYFISH_DEBUG']) {
344
344
  errLine(e instanceof Error ? (e.stack ?? e.message) : String(e));
345
345
  }
346
346
  outcomes.push({
347
347
  repair,
348
- status: "failed",
348
+ status: 'failed',
349
349
  reason: `\`${repair.command}\` failed; re-run it directly, or --debug for the raw error`,
350
350
  });
351
351
  }
@@ -360,31 +360,31 @@ function telemetryPayload(report, offered, outcomes, options) {
360
360
  cli_version: report.cli_version,
361
361
  ok_harnesses: report.ok_harnesses,
362
362
  ok_cli: report.ok_cli,
363
- checks_pass: count("pass"),
364
- checks_fail: count("fail"),
365
- checks_warn: count("warn"),
366
- checks_skip: count("skip"),
363
+ checks_pass: count('pass'),
364
+ checks_fail: count('fail'),
365
+ checks_warn: count('warn'),
366
+ checks_skip: count('skip'),
367
367
  // From the pre-repair report; the verdicts above describe the machine after it.
368
368
  repairs_offered: offered.length,
369
369
  repair_actions: [...new Set(offered.map((repair) => repair.action))],
370
- repairs_attempted: outcomes.filter((outcome) => outcome.status !== "skipped").length,
371
- repairs_succeeded: outcomes.filter((outcome) => outcome.status === "repaired").length,
372
- fix_mode: !options.fix ? "none" : options.yes ? "fix_yes" : "fix",
370
+ repairs_attempted: outcomes.filter((outcome) => outcome.status !== 'skipped').length,
371
+ repairs_succeeded: outcomes.filter((outcome) => outcome.status === 'repaired').length,
372
+ fix_mode: !options.fix ? 'none' : options.yes ? 'fix_yes' : 'fix',
373
373
  // --harness scopes detection, so the counts above cover that harness alone.
374
- harness_scope: options.harness ?? "all",
374
+ harness_scope: options.harness ?? 'all',
375
375
  // Same signal cli_connect_stage carries, so agent and human doctor runs split.
376
376
  is_human_initiated: detectHumanInitiated(),
377
377
  };
378
378
  }
379
379
  export function registerDoctor(program) {
380
380
  program
381
- .command("doctor")
382
- .description("Diagnose your TinyFish setup")
383
- .option("--pretty", "Human-readable check list instead of JSON")
384
- .option("--harness <name>", `Restrict to one harness (${ALL_HARNESSES.join(", ")})`)
385
- .option("--url <mcpUrl>", "MCP endpoint override")
386
- .option("--fix", "Repair what can be repaired by re-running tinyfish connect")
387
- .option("--yes", "Allow --fix to run without a terminal (unattended-safe repairs only)")
381
+ .command('doctor')
382
+ .description('Diagnose your TinyFish setup')
383
+ .option('--pretty', 'Human-readable check list instead of JSON')
384
+ .option('--harness <name>', `Restrict to one harness (${ALL_HARNESSES.join(', ')})`)
385
+ .option('--url <mcpUrl>', 'MCP endpoint override')
386
+ .option('--fix', 'Repair what can be repaired by re-running tinyfish connect')
387
+ .option('--yes', 'Allow --fix to run without a terminal (unattended-safe repairs only)')
388
388
  .action(async (options) => {
389
389
  if (options.harness && !ALL_HARNESSES.includes(options.harness)) {
390
390
  err({ error: `Unknown --harness value: ${options.harness}` });
@@ -406,7 +406,7 @@ export function registerDoctor(program) {
406
406
  if (doctorOptions.fix && report.repairs.length > 0) {
407
407
  outcomes = await applyRepairs(report.repairs, doctorOptions);
408
408
  for (const outcome of outcomes) {
409
- const reason = outcome.reason ? ` (${outcome.reason})` : "";
409
+ const reason = outcome.reason ? ` (${outcome.reason})` : '';
410
410
  errLine(`${outcome.status}: ${outcome.repair.command}${reason}`);
411
411
  }
412
412
  // Re-diagnose so the emitted report describes the machine after repair, not before.
@@ -416,10 +416,10 @@ export function registerDoctor(program) {
416
416
  catch (e) {
417
417
  // Exit 2 means doctor itself broke, a different claim from "your setup is broken".
418
418
  // Authored, like every other string a user might paste; raw text is behind --debug.
419
- if (process.env["TINYFISH_DEBUG"]) {
419
+ if (process.env['TINYFISH_DEBUG']) {
420
420
  errLine(e instanceof Error ? (e.stack ?? e.message) : String(e));
421
421
  }
422
- err({ error: "doctor could not complete; re-run with --debug for the raw error" });
422
+ err({ error: 'doctor could not complete; re-run with --debug for the raw error' });
423
423
  process.exitCode = DOCTOR_COULD_NOT_RUN;
424
424
  return;
425
425
  }
@@ -430,6 +430,6 @@ export function registerDoctor(program) {
430
430
  process.exitCode = exitCodeFor(report.checks);
431
431
  // After the exit code is set: telemetry never decides how doctor exits.
432
432
  const resolved = apiKeyStatus();
433
- await sendDoctorCompleted(doctorOptions.mcpUrl, telemetryPayload(report, offered, outcomes, doctorOptions), "key" in resolved ? resolved.key : undefined);
433
+ await sendDoctorCompleted(doctorOptions.mcpUrl, telemetryPayload(report, offered, outcomes, doctorOptions), 'key' in resolved ? resolved.key : undefined);
434
434
  });
435
435
  }
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerFetch(program: Command): void;
@@ -1,7 +1,7 @@
1
- import { InvalidArgumentError } from "commander";
2
- import { getApiKey } from "../lib/auth.js";
3
- import { fetchContentGet } from "../lib/client.js";
4
- import { err, handleApiError, out, outLine } from "../lib/output.js";
1
+ import { InvalidArgumentError } from 'commander';
2
+ import { getApiKey } from '../lib/auth.js';
3
+ import { fetchContentGet } from '../lib/client.js';
4
+ import { err, handleApiError, out, outLine } from '../lib/output.js';
5
5
  const MIN_PER_URL_TIMEOUT_MS = 1;
6
6
  const MAX_PER_URL_TIMEOUT_MS = 110000;
7
7
  function parsePerUrlTimeoutMs(value) {
@@ -16,9 +16,9 @@ function parsePerUrlTimeoutMs(value) {
16
16
  function printPrettyFetch(response) {
17
17
  outLine(`Results: ${response.results.length}`);
18
18
  outLine(`Errors: ${response.errors.length}`);
19
- outLine("");
19
+ outLine('');
20
20
  if (response.results.length > 0) {
21
- outLine("Successful fetches:");
21
+ outLine('Successful fetches:');
22
22
  for (const result of response.results) {
23
23
  outLine(`- ${result.url}`);
24
24
  outLine(` Format: ${result.format}`);
@@ -32,44 +32,41 @@ function printPrettyFetch(response) {
32
32
  outLine(` ETag: ${result.etag}`);
33
33
  if (result.last_modified)
34
34
  outLine(` Last-Modified: ${result.last_modified}`);
35
- outLine("");
35
+ outLine('');
36
36
  }
37
37
  }
38
38
  if (response.errors.length > 0) {
39
- outLine("Errors:");
39
+ outLine('Errors:');
40
40
  for (const error of response.errors) {
41
41
  outLine(`- ${error.url}`);
42
42
  outLine(` ${error.error}`);
43
- outLine("");
43
+ outLine('');
44
44
  }
45
45
  }
46
46
  }
47
47
  export function registerFetch(program) {
48
- const fetchCmd = program
49
- .command("fetch")
50
- .description("Fetch commands")
51
- .enablePositionalOptions();
48
+ const fetchCmd = program.command('fetch').description('Fetch commands').enablePositionalOptions();
52
49
  const contentCmd = fetchCmd
53
- .command("content")
54
- .description("Content extraction commands")
50
+ .command('content')
51
+ .description('Content extraction commands')
55
52
  .enablePositionalOptions();
56
53
  contentCmd
57
- .command("get")
58
- .description("Fetch clean content from one or more URLs")
59
- .argument("<urls...>", "One or more URLs to fetch")
60
- .option("--format <format>", "Output format: markdown, html, or json")
61
- .option("--links", "Include extracted links")
62
- .option("--image-links", "Include extracted image links")
63
- .option("--per-url-timeout-ms <milliseconds>", "Per-URL timeout budget in milliseconds", parsePerUrlTimeoutMs)
64
- .option("--if-none-match <etag>", "ETag validator for a conditional GET (single URL only)")
65
- .option("--if-modified-since <http-date>", "Last-Modified validator for a conditional GET (single URL only)")
66
- .option("--include-etag-and-last-modified", "Include etag / last_modified validators on each result")
67
- .option("--pretty", "Human-readable output")
54
+ .command('get')
55
+ .description('Fetch clean content from one or more URLs')
56
+ .argument('<urls...>', 'One or more URLs to fetch')
57
+ .option('--format <format>', 'Output format: markdown, html, or json')
58
+ .option('--links', 'Include extracted links')
59
+ .option('--image-links', 'Include extracted image links')
60
+ .option('--per-url-timeout-ms <milliseconds>', 'Per-URL timeout budget in milliseconds', parsePerUrlTimeoutMs)
61
+ .option('--if-none-match <etag>', 'ETag validator for a conditional GET (single URL only)')
62
+ .option('--if-modified-since <http-date>', 'Last-Modified validator for a conditional GET (single URL only)')
63
+ .option('--include-etag-and-last-modified', 'Include etag / last_modified validators on each result')
64
+ .option('--pretty', 'Human-readable output')
68
65
  .action(async (urls, opts) => {
69
66
  if ((opts.ifNoneMatch !== undefined || opts.ifModifiedSince !== undefined) &&
70
67
  urls.length > 1) {
71
68
  err({
72
- error: "--if-none-match and --if-modified-since can only be used with a single URL, not a batch",
69
+ error: '--if-none-match and --if-modified-since can only be used with a single URL, not a batch',
73
70
  });
74
71
  process.exit(1);
75
72
  }
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerOnboard(program: Command): void;