@tiny-fish/cli 0.30.2-next.257 → 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 +1 -1
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerAuth(program: Command): void;
@@ -1,9 +1,9 @@
1
- import { spawn } from "child_process";
2
- import * as readline from "readline";
3
- import { Option } from "commander";
4
- import { CONNECT_ATTEMPT_ENV, clearConfig, getDashboardUrl, loadConfig, maskKey, savePendingConnectAttempt, saveConfig, validateKeyFormat, } from "../lib/auth.js";
5
- import { TINYFISH_API_KEY_VAR } from "../lib/constants.js";
6
- import { err, errLine, out, outLine } from "../lib/output.js";
1
+ import { spawn } from 'child_process';
2
+ import * as readline from 'readline';
3
+ import { Option } from 'commander';
4
+ import { CONNECT_ATTEMPT_ENV, clearConfig, getDashboardUrl, loadConfig, maskKey, savePendingConnectAttempt, saveConfig, validateKeyFormat, } from '../lib/auth.js';
5
+ import { TINYFISH_API_KEY_VAR } from '../lib/constants.js';
6
+ import { err, errLine, out, outLine } from '../lib/output.js';
7
7
  /**
8
8
  * Read a key from stdin without echoing it to the terminal.
9
9
  * Uses setRawMode on TTY so characters are never written to the screen.
@@ -15,7 +15,7 @@ async function readKeyHidden(prompt) {
15
15
  // Non-interactive (piped): read stdin line as-is — no echo suppression needed
16
16
  const rl = readline.createInterface({ input: process.stdin, terminal: false });
17
17
  return new Promise((resolve) => {
18
- rl.once("line", (line) => {
18
+ rl.once('line', (line) => {
19
19
  rl.close();
20
20
  resolve(line.trim());
21
21
  });
@@ -23,24 +23,24 @@ async function readKeyHidden(prompt) {
23
23
  }
24
24
  // Interactive TTY: use raw mode so keystrokes are never echoed
25
25
  return new Promise((resolve) => {
26
- let input = "";
26
+ let input = '';
27
27
  process.stdin.setRawMode(true);
28
28
  process.stdin.resume();
29
- process.stdin.setEncoding("utf8");
29
+ process.stdin.setEncoding('utf8');
30
30
  const onData = (char) => {
31
- if (char === "\r" || char === "\n") {
31
+ if (char === '\r' || char === '\n') {
32
32
  process.stdin.setRawMode(false);
33
33
  process.stdin.pause();
34
- process.stdin.removeListener("data", onData);
35
- process.stderr.write("\n");
34
+ process.stdin.removeListener('data', onData);
35
+ process.stderr.write('\n');
36
36
  resolve(input);
37
37
  }
38
- else if (char === "\u0003") {
38
+ else if (char === '\u0003') {
39
39
  // Ctrl+C — restore terminal state before exiting
40
40
  process.stdin.setRawMode(false);
41
41
  process.exit(130);
42
42
  }
43
- else if (char === "\u007f" || char === "\b") {
43
+ else if (char === '\u007f' || char === '\b') {
44
44
  // Backspace / Delete
45
45
  input = input.slice(0, -1);
46
46
  }
@@ -48,7 +48,7 @@ async function readKeyHidden(prompt) {
48
48
  input += char;
49
49
  }
50
50
  };
51
- process.stdin.on("data", onData);
51
+ process.stdin.on('data', onData);
52
52
  });
53
53
  }
54
54
  /**
@@ -59,13 +59,13 @@ async function readKeyFromStdinOrPrompt() {
59
59
  if (!process.stdin.isTTY) {
60
60
  // Piped input: echo $KEY | tinyfish auth set
61
61
  return new Promise((resolve) => {
62
- let data = "";
63
- process.stdin.setEncoding("utf8");
64
- process.stdin.on("data", (chunk) => (data += chunk));
65
- process.stdin.on("end", () => resolve(data.trim()));
62
+ let data = '';
63
+ process.stdin.setEncoding('utf8');
64
+ process.stdin.on('data', (chunk) => (data += chunk));
65
+ process.stdin.on('end', () => resolve(data.trim()));
66
66
  });
67
67
  }
68
- return readKeyHidden("Paste your API key: ");
68
+ return readKeyHidden('Paste your API key: ');
69
69
  }
70
70
  // Sign-in and the connect that follows are separate copied commands, so a page-minted id
71
71
  // handed to this process has to outlive it.
@@ -75,11 +75,11 @@ function parkSeedAttempt() {
75
75
  savePendingConnectAttempt(seed);
76
76
  }
77
77
  export function registerAuth(program) {
78
- const auth = program.command("auth").description("Manage your TinyFish API key");
78
+ const auth = program.command('auth').description('Manage your TinyFish API key');
79
79
  auth
80
- .command("login")
81
- .description("Open the API keys page and save your key interactively")
82
- .addOption(new Option("--source <source>", "API keys page source attribution").default("cli").hideHelp())
80
+ .command('login')
81
+ .description('Open the API keys page and save your key interactively')
82
+ .addOption(new Option('--source <source>', 'API keys page source attribution').default('cli').hideHelp())
83
83
  .action(async (options) => {
84
84
  const dashboardUrl = getDashboardUrl(options.source);
85
85
  errLine(`Opening ${dashboardUrl} in your browser...`);
@@ -87,67 +87,67 @@ export function registerAuth(program) {
87
87
  // Use spawn with an explicit args array — no shell, no interpolation
88
88
  let cmd;
89
89
  let args;
90
- if (process.platform === "darwin") {
91
- cmd = "open";
90
+ if (process.platform === 'darwin') {
91
+ cmd = 'open';
92
92
  args = [dashboardUrl];
93
93
  }
94
- else if (process.platform === "win32") {
94
+ else if (process.platform === 'win32') {
95
95
  // "start" treats the first quoted arg as a window title — empty string avoids it
96
- cmd = "cmd";
97
- args = ["/c", "start", "", dashboardUrl];
96
+ cmd = 'cmd';
97
+ args = ['/c', 'start', '', dashboardUrl];
98
98
  }
99
99
  else {
100
- cmd = "xdg-open";
100
+ cmd = 'xdg-open';
101
101
  args = [dashboardUrl];
102
102
  }
103
- const child = spawn(cmd, args, { stdio: "ignore", detached: true });
103
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
104
104
  child.unref();
105
- child.on("error", () => { }); // headless — ignore
105
+ child.on('error', () => { }); // headless — ignore
106
106
  }
107
107
  catch {
108
108
  // headless — ignore
109
109
  }
110
110
  process.stderr.write(`If the browser didn't open, visit: ${dashboardUrl}\n\n`);
111
- const key = await readKeyHidden("Paste your API key: ");
111
+ const key = await readKeyHidden('Paste your API key: ');
112
112
  if (!key) {
113
- err("API key cannot be empty");
113
+ err('API key cannot be empty');
114
114
  process.exit(1);
115
115
  }
116
116
  if (!validateKeyFormat(key)) {
117
- err("Invalid key format. Expected sk-tinyfish-... or sk-mino-...");
117
+ err('Invalid key format. Expected sk-tinyfish-... or sk-mino-...');
118
118
  process.exit(1);
119
119
  }
120
120
  saveConfig(key);
121
121
  parkSeedAttempt();
122
- out({ status: "ok", message: "API key saved", key_preview: maskKey(key) });
122
+ out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
123
123
  });
124
124
  auth
125
- .command("set")
126
- .description("Save an API key. Pass via stdin: echo $KEY | tinyfish auth set (safe for CI/CD)")
125
+ .command('set')
126
+ .description('Save an API key. Pass via stdin: echo $KEY | tinyfish auth set (safe for CI/CD)')
127
127
  .action(async () => {
128
128
  // Read from stdin pipe (CI/CD) or hidden TTY prompt (interactive)
129
129
  const key = await readKeyFromStdinOrPrompt();
130
130
  if (!key) {
131
- err("API key cannot be empty");
131
+ err('API key cannot be empty');
132
132
  process.exit(1);
133
133
  }
134
134
  if (!validateKeyFormat(key)) {
135
- err("Invalid key format. Expected sk-tinyfish-... or sk-mino-...");
135
+ err('Invalid key format. Expected sk-tinyfish-... or sk-mino-...');
136
136
  process.exit(1);
137
137
  }
138
138
  saveConfig(key);
139
139
  parkSeedAttempt();
140
- out({ status: "ok", message: "API key saved", key_preview: maskKey(key) });
140
+ out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
141
141
  });
142
142
  auth
143
- .command("status")
144
- .description("Show which API key is active and its source")
145
- .option("--pretty", "Human-readable output")
143
+ .command('status')
144
+ .description('Show which API key is active and its source')
145
+ .option('--pretty', 'Human-readable output')
146
146
  .action((opts) => {
147
147
  const envKey = process.env[TINYFISH_API_KEY_VAR];
148
148
  if (envKey) {
149
149
  const authenticated = validateKeyFormat(envKey);
150
- const result = { source: "env", key_preview: maskKey(envKey), authenticated };
150
+ const result = { source: 'env', key_preview: maskKey(envKey), authenticated };
151
151
  if (opts.pretty) {
152
152
  outLine(`Source: environment variable (TINYFISH_API_KEY)\nKey: ${maskKey(envKey)}`);
153
153
  }
@@ -160,7 +160,7 @@ export function registerAuth(program) {
160
160
  if (config.api_key) {
161
161
  const authenticated = validateKeyFormat(config.api_key);
162
162
  const result = {
163
- source: "config",
163
+ source: 'config',
164
164
  key_preview: maskKey(config.api_key),
165
165
  authenticated,
166
166
  };
@@ -172,9 +172,9 @@ export function registerAuth(program) {
172
172
  }
173
173
  return;
174
174
  }
175
- const result = { source: "none", key_preview: null, authenticated: false };
175
+ const result = { source: 'none', key_preview: null, authenticated: false };
176
176
  if (opts.pretty) {
177
- outLine("No API key configured. Run: tinyfish auth login");
177
+ outLine('No API key configured. Run: tinyfish auth login');
178
178
  }
179
179
  else {
180
180
  out(result);
@@ -182,13 +182,13 @@ export function registerAuth(program) {
182
182
  process.exit(1);
183
183
  });
184
184
  auth
185
- .command("logout")
186
- .description("Remove stored API key")
185
+ .command('logout')
186
+ .description('Remove stored API key')
187
187
  .action(() => {
188
188
  if (!clearConfig()) {
189
- out({ status: "ok", message: "No API key stored" });
189
+ out({ status: 'ok', message: 'No API key stored' });
190
190
  return;
191
191
  }
192
- out({ status: "ok", message: "API key removed" });
192
+ out({ status: 'ok', message: 'API key removed' });
193
193
  });
194
194
  }
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerBatch(agentCmd: Command): void;
@@ -1,10 +1,10 @@
1
- import * as fs from "fs";
2
- import { randomUUID } from "crypto";
3
- import { getApiKey } from "../lib/auth.js";
4
- import { cancelBatchRuns, getBatchRuns, submitBatch } from "../lib/client.js";
5
- import { findBatch, loadBatches, saveBatch } from "../lib/batch-store.js";
6
- import { err, handleApiError, out, outLine } from "../lib/output.js";
7
- import { BASE_URL } from "../lib/constants.js";
1
+ import * as fs from 'fs';
2
+ import { randomUUID } from 'crypto';
3
+ import { getApiKey } from '../lib/auth.js';
4
+ import { cancelBatchRuns, getBatchRuns, submitBatch } from '../lib/client.js';
5
+ import { findBatch, loadBatches, saveBatch } from '../lib/batch-store.js';
6
+ import { err, handleApiError, out, outLine } from '../lib/output.js';
7
+ import { BASE_URL } from '../lib/constants.js';
8
8
  // ── CSV parsing ───────────────────────────────────────────────────────────────
9
9
  /**
10
10
  * Parse one RFC 4180 field from `line` starting at `pos`.
@@ -14,7 +14,7 @@ import { BASE_URL } from "../lib/constants.js";
14
14
  function parseField(line, pos, rowNum) {
15
15
  if (line[pos] === '"') {
16
16
  // Quoted field — read until closing unescaped quote
17
- let value = "";
17
+ let value = '';
18
18
  let i = pos + 1;
19
19
  while (i < line.length) {
20
20
  if (line[i] === '"') {
@@ -31,10 +31,10 @@ function parseField(line, pos, rowNum) {
31
31
  if (i > line.length)
32
32
  throw new Error(`Row ${rowNum}: unclosed quote in field`);
33
33
  // Skip optional comma after closing quote
34
- return { value, next: line[i] === "," ? i + 1 : i };
34
+ return { value, next: line[i] === ',' ? i + 1 : i };
35
35
  }
36
36
  // Unquoted field — read to next comma
37
- const commaIdx = line.indexOf(",", pos);
37
+ const commaIdx = line.indexOf(',', pos);
38
38
  if (commaIdx === -1)
39
39
  return { value: line.slice(pos).trim(), next: line.length };
40
40
  return { value: line.slice(pos, commaIdx).trim(), next: commaIdx + 1 };
@@ -43,18 +43,18 @@ function parseField(line, pos, rowNum) {
43
43
  function parseCsv(content) {
44
44
  const lines = content.split(/\r?\n/).filter((l) => l.trim().length > 0);
45
45
  if (lines.length === 0)
46
- throw new Error("CSV file is empty");
46
+ throw new Error('CSV file is empty');
47
47
  const header = lines[0].trim();
48
- if (header !== "url,goal") {
48
+ if (header !== 'url,goal') {
49
49
  throw new Error(`Invalid CSV header: "${header}". Expected: "url,goal"`);
50
50
  }
51
51
  const dataLines = lines.slice(1);
52
52
  if (dataLines.length === 0)
53
- throw new Error("CSV file has no data rows");
53
+ throw new Error('CSV file has no data rows');
54
54
  return dataLines.map((line, idx) => {
55
55
  const rowNum = idx + 2;
56
56
  const urlField = parseField(line, 0, rowNum);
57
- if (urlField.next >= line.length && line.indexOf(",") === -1) {
57
+ if (urlField.next >= line.length && line.indexOf(',') === -1) {
58
58
  throw new Error(`Row ${rowNum}: missing comma separator — "${line}"`);
59
59
  }
60
60
  const goalField = parseField(line, urlField.next, rowNum);
@@ -68,20 +68,20 @@ function parseCsv(content) {
68
68
  // ── register ──────────────────────────────────────────────────────────────────
69
69
  export function registerBatch(agentCmd) {
70
70
  const batchCmd = agentCmd
71
- .command("batch")
72
- .description("Batch run operations")
71
+ .command('batch')
72
+ .description('Batch run operations')
73
73
  .enablePositionalOptions();
74
74
  // ── batch run ───────────────────────────────────────────────────────────────
75
75
  batchCmd
76
- .command("run")
77
- .description("Submit a batch of runs from a CSV file (columns: url,goal)")
78
- .requiredOption("--input <file>", "Path to CSV file with columns: url,goal")
79
- .option("--pretty", "Human-readable output")
76
+ .command('run')
77
+ .description('Submit a batch of runs from a CSV file (columns: url,goal)')
78
+ .requiredOption('--input <file>', 'Path to CSV file with columns: url,goal')
79
+ .option('--pretty', 'Human-readable output')
80
80
  .action(async (opts) => {
81
81
  // Read CSV file
82
82
  let csvContent;
83
83
  try {
84
- csvContent = fs.readFileSync(opts.input, "utf8");
84
+ csvContent = fs.readFileSync(opts.input, 'utf8');
85
85
  }
86
86
  catch {
87
87
  err({ error: `Cannot read file: ${opts.input}` });
@@ -93,7 +93,10 @@ export function registerBatch(agentCmd) {
93
93
  runs = parseCsv(csvContent);
94
94
  }
95
95
  catch (e) {
96
- err({ error: String(e instanceof Error ? e.message : e), recommended_action: "Fix the CSV file and try again" });
96
+ err({
97
+ error: String(e instanceof Error ? e.message : e),
98
+ recommended_action: 'Fix the CSV file and try again',
99
+ });
97
100
  process.exit(1);
98
101
  }
99
102
  const apiKey = getApiKey();
@@ -103,7 +106,10 @@ export function registerBatch(agentCmd) {
103
106
  handleApiError(response.error);
104
107
  const runIds = response.run_ids;
105
108
  if (!runIds || runIds.length === 0) {
106
- err({ error: "Batch submission returned no run IDs", recommended_action: "Check the API key and try again" });
109
+ err({
110
+ error: 'Batch submission returned no run IDs',
111
+ recommended_action: 'Check the API key and try again',
112
+ });
107
113
  process.exit(1);
108
114
  }
109
115
  const submitted = runIds.length;
@@ -119,30 +125,32 @@ export function registerBatch(agentCmd) {
119
125
  saveBatch(batch);
120
126
  const resultsUrl = `${BASE_URL}/runs`;
121
127
  if (opts.pretty) {
122
- outLine("Batch submitted");
128
+ outLine('Batch submitted');
123
129
  outLine(`ID: ${batchId}`);
124
130
  outLine(`Total: ${runs.length}`);
125
131
  outLine(`Submitted: ${submitted}`);
126
132
  outLine(`Runs URL: ${resultsUrl}`);
127
133
  }
128
134
  else {
129
- out({ data: { batch_id: batchId, total: runs.length, submitted, results_url: resultsUrl } });
135
+ out({
136
+ data: { batch_id: batchId, total: runs.length, submitted, results_url: resultsUrl },
137
+ });
130
138
  }
131
139
  });
132
140
  // ── batch list ──────────────────────────────────────────────────────────────
133
141
  batchCmd
134
- .command("list")
135
- .description("List locally tracked batches")
136
- .option("--pretty", "Human-readable output")
142
+ .command('list')
143
+ .description('List locally tracked batches')
144
+ .option('--pretty', 'Human-readable output')
137
145
  .action((opts) => {
138
146
  const batches = loadBatches();
139
147
  if (opts.pretty) {
140
148
  if (batches.length === 0) {
141
- outLine("No batches found.");
149
+ outLine('No batches found.');
142
150
  return;
143
151
  }
144
- outLine(`${"BATCH ID".padEnd(38)} ${"TOTAL".padEnd(6)} ${"SUBMITTED".padEnd(9)} CREATED AT`);
145
- outLine(`${"-".repeat(38)} ${"-".repeat(6)} ${"-".repeat(9)} ${"-".repeat(24)}`);
152
+ outLine(`${'BATCH ID'.padEnd(38)} ${'TOTAL'.padEnd(6)} ${'SUBMITTED'.padEnd(9)} CREATED AT`);
153
+ outLine(`${'-'.repeat(38)} ${'-'.repeat(6)} ${'-'.repeat(9)} ${'-'.repeat(24)}`);
146
154
  for (const b of batches) {
147
155
  outLine(`${b.batch_id.padEnd(38)} ${String(b.total).padEnd(6)} ${String(b.submitted).padEnd(9)} ${b.created_at}`);
148
156
  }
@@ -153,9 +161,9 @@ export function registerBatch(agentCmd) {
153
161
  });
154
162
  // ── batch get ───────────────────────────────────────────────────────────────
155
163
  batchCmd
156
- .command("get <batch_id>")
157
- .description("Get run results for a batch")
158
- .option("--pretty", "Human-readable output")
164
+ .command('get <batch_id>')
165
+ .description('Get run results for a batch')
166
+ .option('--pretty', 'Human-readable output')
159
167
  .action(async (batchId, opts) => {
160
168
  const batch = findBatch(batchId);
161
169
  if (!batch) {
@@ -172,12 +180,12 @@ export function registerBatch(agentCmd) {
172
180
  outLine(`Not found: ${response.not_found.length}`);
173
181
  }
174
182
  for (const run of response.data) {
175
- outLine("");
183
+ outLine('');
176
184
  outLine(`Run: ${run.run_id}`);
177
- outLine(`Status: ${run.status}${run.num_of_steps != null ? ` (${run.num_of_steps} steps)` : ""}`);
185
+ outLine(`Status: ${run.status}${run.num_of_steps != null ? ` (${run.num_of_steps} steps)` : ''}`);
178
186
  outLine(`Goal: ${run.goal}`);
179
187
  if (run.result != null) {
180
- outLine(`Result: ${JSON.stringify(run.result, null, 2).replace(/\n/g, "\n ")}`);
188
+ outLine(`Result: ${JSON.stringify(run.result, null, 2).replace(/\n/g, '\n ')}`);
181
189
  }
182
190
  else if (run.error) {
183
191
  outLine(`Error: ${JSON.stringify(run.error)}`);
@@ -190,9 +198,9 @@ export function registerBatch(agentCmd) {
190
198
  });
191
199
  // ── batch cancel ─────────────────────────────────────────────────────────────
192
200
  batchCmd
193
- .command("cancel <batch_id>")
194
- .description("Cancel all runs in a batch")
195
- .option("--pretty", "Human-readable output")
201
+ .command('cancel <batch_id>')
202
+ .description('Cancel all runs in a batch')
203
+ .option('--pretty', 'Human-readable output')
196
204
  .action(async (batchId, opts) => {
197
205
  const batch = findBatch(batchId);
198
206
  if (!batch) {
@@ -202,18 +210,18 @@ export function registerBatch(agentCmd) {
202
210
  const apiKey = getApiKey();
203
211
  const response = await cancelBatchRuns(batch.run_ids, apiKey).catch(handleApiError);
204
212
  if (opts.pretty) {
205
- const cancelled = response.results.filter((r) => r.status === "CANCELLED").length;
213
+ const cancelled = response.results.filter((r) => r.status === 'CANCELLED').length;
206
214
  outLine(`Batch ${batchId}: ${cancelled}/${response.results.length} run(s) cancelled.`);
207
215
  if (response.results.length > 0) {
208
- outLine("");
209
- outLine(`${"RUN ID".padEnd(36)} ${"STATUS".padEnd(10)} CANCELLED AT`);
210
- outLine(`${"-".repeat(36)} ${"-".repeat(10)} ${"-".repeat(24)}`);
216
+ outLine('');
217
+ outLine(`${'RUN ID'.padEnd(36)} ${'STATUS'.padEnd(10)} CANCELLED AT`);
218
+ outLine(`${'-'.repeat(36)} ${'-'.repeat(10)} ${'-'.repeat(24)}`);
211
219
  for (const r of response.results) {
212
- outLine(`${r.run_id.padEnd(36)} ${r.status.padEnd(10)} ${r.cancelled_at ?? "-"}`);
220
+ outLine(`${r.run_id.padEnd(36)} ${r.status.padEnd(10)} ${r.cancelled_at ?? '-'}`);
213
221
  }
214
222
  }
215
223
  if (response.not_found && response.not_found.length > 0) {
216
- outLine(`\nNot found: ${response.not_found.join(", ")}`);
224
+ outLine(`\nNot found: ${response.not_found.join(', ')}`);
217
225
  }
218
226
  }
219
227
  else {
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerBrowser(program: Command): void;
@@ -1,7 +1,7 @@
1
- import { getApiKey } from "../lib/auth.js";
2
- import { browserSessionCreate } from "../lib/client.js";
3
- import { err, handleApiError, out, outLine } from "../lib/output.js";
4
- const VALID_BROWSER_PROFILES = ["lite", "stealth"];
1
+ import { getApiKey } from '../lib/auth.js';
2
+ import { browserSessionCreate } from '../lib/client.js';
3
+ import { err, handleApiError, out, outLine } from '../lib/output.js';
4
+ const VALID_BROWSER_PROFILES = ['lite', 'stealth'];
5
5
  function parseBrowserProfile(value) {
6
6
  if (value === undefined)
7
7
  return undefined;
@@ -12,26 +12,26 @@ function parseBrowserProfile(value) {
12
12
  process.exit(1);
13
13
  }
14
14
  function printPrettyBrowserSession(session) {
15
- outLine("Browser session created");
15
+ outLine('Browser session created');
16
16
  outLine(`Session ID: ${session.session_id}`);
17
17
  outLine(`CDP URL: ${session.cdp_url}`);
18
18
  outLine(`Base URL: ${session.base_url}`);
19
19
  }
20
20
  export function registerBrowser(program) {
21
21
  const browserCmd = program
22
- .command("browser")
23
- .description("Browser commands")
22
+ .command('browser')
23
+ .description('Browser commands')
24
24
  .enablePositionalOptions();
25
25
  const sessionCmd = browserCmd
26
- .command("session")
27
- .description("Manage browser sessions")
26
+ .command('session')
27
+ .description('Manage browser sessions')
28
28
  .enablePositionalOptions();
29
29
  sessionCmd
30
- .command("create")
31
- .description("Create a remote browser session")
32
- .option("--url <value>", "Open a URL after session creation")
33
- .option("--browser-profile <profile>", "Browser profile for execution (lite|stealth)")
34
- .option("--pretty", "Human-readable output")
30
+ .command('create')
31
+ .description('Create a remote browser session')
32
+ .option('--url <value>', 'Open a URL after session creation')
33
+ .option('--browser-profile <profile>', 'Browser profile for execution (lite|stealth)')
34
+ .option('--pretty', 'Human-readable output')
35
35
  .action(async (opts) => {
36
36
  const apiKey = getApiKey();
37
37
  const browserProfile = parseBrowserProfile(opts.browserProfile);
@@ -1,2 +1,2 @@
1
- import { Command } from "commander";
1
+ import { Command } from 'commander';
2
2
  export declare function registerConfigureClaude(program: Command): void;
@@ -1,6 +1,6 @@
1
- import { err, errLine, out } from "../lib/output.js";
2
- import { validatedApiKey } from "../lib/auth.js";
3
- import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isTinyfishConfiguredInSettings, isTinyfishConfiguredInClaudeMd, } from "../lib/claude-config.js";
1
+ import { err, errLine, out } from '../lib/output.js';
2
+ import { validatedApiKey } from '../lib/auth.js';
3
+ import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isTinyfishConfiguredInSettings, isTinyfishConfiguredInClaudeMd, } from '../lib/claude-config.js';
4
4
  function loadExistingConfig() {
5
5
  let settings;
6
6
  try {
@@ -28,42 +28,42 @@ function isSignedIn() {
28
28
  return !!validatedApiKey();
29
29
  }
30
30
  function install(settings, claudeMd) {
31
- errLine("Configuring Claude Code to use TinyFish...\n");
31
+ errLine('Configuring Claude Code to use TinyFish...\n');
32
32
  writeSettingsJson(mergeSettings(settings));
33
- errLine(" Updated settings.json (permission + hooks)");
33
+ errLine(' Updated settings.json (permission + hooks)');
34
34
  const { content, replaced } = mergeClaudeMd(claudeMd);
35
35
  writeClaudeMd(content);
36
36
  errLine(replaced
37
- ? " Updated CLAUDE.md (replaced existing TinyFish block)"
38
- : " Updated CLAUDE.md (added TinyFish instructions)");
39
- errLine("\nClaude Code is now configured to use TinyFish for web search and fetch! 🚀");
37
+ ? ' Updated CLAUDE.md (replaced existing TinyFish block)'
38
+ : ' Updated CLAUDE.md (added TinyFish instructions)');
39
+ errLine('\nClaude Code is now configured to use TinyFish for web search and fetch! 🚀');
40
40
  const signedIn = isSignedIn();
41
41
  if (!signedIn) {
42
- errLine("\nNote: You are not signed in to TinyFish yet. The configuration is in place,");
43
- errLine("but search and fetch will not work until you sign in. Run:\n");
44
- errLine(" tinyfish auth login");
42
+ errLine('\nNote: You are not signed in to TinyFish yet. The configuration is in place,');
43
+ errLine('but search and fetch will not work until you sign in. Run:\n');
44
+ errLine(' tinyfish auth login');
45
45
  }
46
- out({ status: "ok", action: "configured", signed_in: signedIn });
46
+ out({ status: 'ok', action: 'configured', signed_in: signedIn });
47
47
  }
48
48
  function remove(settings, claudeMd) {
49
49
  if (!isTinyfishConfiguredInSettings(settings) && !isTinyfishConfiguredInClaudeMd(claudeMd)) {
50
- errLine("TinyFish is not configured in Claude Code. Nothing to remove.");
51
- out({ status: "ok", action: "noop" });
50
+ errLine('TinyFish is not configured in Claude Code. Nothing to remove.');
51
+ out({ status: 'ok', action: 'noop' });
52
52
  return;
53
53
  }
54
- errLine("Removing TinyFish configuration from Claude Code...\n");
54
+ errLine('Removing TinyFish configuration from Claude Code...\n');
55
55
  writeSettingsJson(removeFromSettings(settings));
56
- errLine(" Cleaned settings.json");
56
+ errLine(' Cleaned settings.json');
57
57
  writeClaudeMd(removeFromClaudeMd(claudeMd));
58
- errLine(" Cleaned CLAUDE.md");
59
- errLine("\nTinyFish configuration removed from Claude Code.");
60
- out({ status: "ok", action: "removed" });
58
+ errLine(' Cleaned CLAUDE.md');
59
+ errLine('\nTinyFish configuration removed from Claude Code.');
60
+ out({ status: 'ok', action: 'removed' });
61
61
  }
62
62
  export function registerConfigureClaude(program) {
63
63
  program
64
- .command("config-claude")
65
- .description("Configure Claude Code to use TinyFish for web search and fetch")
66
- .option("--remove", "Remove TinyFish configuration from Claude Code")
64
+ .command('config-claude')
65
+ .description('Configure Claude Code to use TinyFish for web search and fetch')
66
+ .option('--remove', 'Remove TinyFish configuration from Claude Code')
67
67
  .action(async (opts) => {
68
68
  const { settings, claudeMd } = loadExistingConfig();
69
69
  if (opts.remove) {
@@ -1,6 +1,6 @@
1
- import { Command } from "commander";
2
- import { type TakeHoistedCliInstall } from "../lib/cli-install.js";
3
- import { type AgentClient, type ConnectAuthMode } from "../lib/connect-runtime.js";
1
+ import { Command } from 'commander';
2
+ import { type TakeHoistedCliInstall } from '../lib/cli-install.js';
3
+ import { type AgentClient, type ConnectAuthMode } from '../lib/connect-runtime.js';
4
4
  export declare const DEFAULT_MCP_URL = "https://agent.tinyfish.ai/mcp";
5
5
  export interface NativeConnectOptions {
6
6
  apiKey?: string;