@tiny-fish/cli 0.30.2-next.257 → 0.30.2-next.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/auth.d.ts +1 -1
- package/dist/commands/auth.js +52 -52
- package/dist/commands/batch.d.ts +1 -1
- package/dist/commands/batch.js +54 -46
- package/dist/commands/browser.d.ts +1 -1
- package/dist/commands/browser.js +14 -14
- package/dist/commands/config-claude.d.ts +1 -1
- package/dist/commands/config-claude.js +22 -22
- package/dist/commands/connect.d.ts +3 -3
- package/dist/commands/connect.js +150 -150
- package/dist/commands/doctor.d.ts +4 -4
- package/dist/commands/doctor.js +104 -104
- package/dist/commands/fetch.d.ts +1 -1
- package/dist/commands/fetch.js +24 -27
- package/dist/commands/onboard.d.ts +1 -1
- package/dist/commands/onboard.js +50 -50
- package/dist/commands/profile.d.ts +3 -3
- package/dist/commands/profile.js +45 -45
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.js +78 -65
- package/dist/commands/runs.d.ts +1 -1
- package/dist/commands/runs.js +63 -50
- package/dist/commands/search.d.ts +1 -1
- package/dist/commands/search.js +16 -16
- package/dist/commands/upgrade.d.ts +2 -2
- package/dist/commands/upgrade.js +36 -36
- package/dist/commands/vault.d.ts +1 -1
- package/dist/commands/vault.js +48 -45
- package/dist/commands/wallet.d.ts +1 -1
- package/dist/commands/wallet.js +14 -14
- package/dist/index.js +2 -2
- package/dist/lib/auth.d.ts +5 -5
- package/dist/lib/auth.js +56 -56
- package/dist/lib/batch-store.d.ts +1 -1
- package/dist/lib/batch-store.js +4 -4
- package/dist/lib/chrome-cookies.d.ts +1 -1
- package/dist/lib/chrome-cookies.js +24 -24
- package/dist/lib/claude-config.js +37 -37
- package/dist/lib/cli-install.js +12 -12
- package/dist/lib/client.d.ts +2 -2
- package/dist/lib/client.js +61 -61
- package/dist/lib/connect-all.js +140 -134
- package/dist/lib/connect-auth.d.ts +2 -2
- package/dist/lib/connect-auth.js +15 -15
- package/dist/lib/connect-clients.d.ts +6 -6
- package/dist/lib/connect-clients.js +143 -143
- package/dist/lib/connect-fallback.d.ts +1 -1
- package/dist/lib/connect-fallback.js +38 -38
- package/dist/lib/connect-picker.d.ts +4 -4
- package/dist/lib/connect-picker.js +28 -28
- package/dist/lib/connect-runtime.d.ts +7 -7
- package/dist/lib/connect-runtime.js +61 -61
- package/dist/lib/constants.js +13 -13
- package/dist/lib/cursor-config.d.ts +1 -1
- package/dist/lib/cursor-config.js +30 -30
- package/dist/lib/doctor-report.d.ts +2 -2
- package/dist/lib/doctor-report.js +22 -22
- package/dist/lib/harness-detect.js +39 -39
- package/dist/lib/harness.js +9 -9
- package/dist/lib/hermes-env.js +26 -23
- package/dist/lib/install-root.js +12 -12
- package/dist/lib/notice.d.ts +1 -1
- package/dist/lib/notice.js +12 -12
- package/dist/lib/output.js +14 -14
- package/dist/lib/registration-detect.d.ts +3 -3
- package/dist/lib/registration-detect.js +82 -82
- package/dist/lib/setup-telemetry.d.ts +4 -4
- package/dist/lib/setup-telemetry.js +34 -34
- package/dist/lib/signals.js +1 -1
- package/dist/lib/skill-install.d.ts +3 -3
- package/dist/lib/skill-install.js +40 -40
- package/dist/lib/types.d.ts +8 -8
- package/dist/lib/verify.d.ts +1 -1
- package/dist/lib/verify.js +28 -28
- package/dist/program.d.ts +1 -1
- package/dist/program.js +28 -28
- package/package.json +1 -1
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerAuth(program: Command): void;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { spawn } from
|
|
2
|
-
import * as readline from
|
|
3
|
-
import { Option } from
|
|
4
|
-
import { CONNECT_ATTEMPT_ENV, clearConfig, getDashboardUrl, loadConfig, maskKey, savePendingConnectAttempt, saveConfig, validateKeyFormat, } from
|
|
5
|
-
import { TINYFISH_API_KEY_VAR } from
|
|
6
|
-
import { err, errLine, out, outLine } from
|
|
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(
|
|
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(
|
|
29
|
+
process.stdin.setEncoding('utf8');
|
|
30
30
|
const onData = (char) => {
|
|
31
|
-
if (char ===
|
|
31
|
+
if (char === '\r' || char === '\n') {
|
|
32
32
|
process.stdin.setRawMode(false);
|
|
33
33
|
process.stdin.pause();
|
|
34
|
-
process.stdin.removeListener(
|
|
35
|
-
process.stderr.write(
|
|
34
|
+
process.stdin.removeListener('data', onData);
|
|
35
|
+
process.stderr.write('\n');
|
|
36
36
|
resolve(input);
|
|
37
37
|
}
|
|
38
|
-
else if (char ===
|
|
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 ===
|
|
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(
|
|
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(
|
|
64
|
-
process.stdin.on(
|
|
65
|
-
process.stdin.on(
|
|
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(
|
|
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(
|
|
78
|
+
const auth = program.command('auth').description('Manage your TinyFish API key');
|
|
79
79
|
auth
|
|
80
|
-
.command(
|
|
81
|
-
.description(
|
|
82
|
-
.addOption(new Option(
|
|
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 ===
|
|
91
|
-
cmd =
|
|
90
|
+
if (process.platform === 'darwin') {
|
|
91
|
+
cmd = 'open';
|
|
92
92
|
args = [dashboardUrl];
|
|
93
93
|
}
|
|
94
|
-
else if (process.platform ===
|
|
94
|
+
else if (process.platform === 'win32') {
|
|
95
95
|
// "start" treats the first quoted arg as a window title — empty string avoids it
|
|
96
|
-
cmd =
|
|
97
|
-
args = [
|
|
96
|
+
cmd = 'cmd';
|
|
97
|
+
args = ['/c', 'start', '', dashboardUrl];
|
|
98
98
|
}
|
|
99
99
|
else {
|
|
100
|
-
cmd =
|
|
100
|
+
cmd = 'xdg-open';
|
|
101
101
|
args = [dashboardUrl];
|
|
102
102
|
}
|
|
103
|
-
const child = spawn(cmd, args, { stdio:
|
|
103
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
104
104
|
child.unref();
|
|
105
|
-
child.on(
|
|
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(
|
|
111
|
+
const key = await readKeyHidden('Paste your API key: ');
|
|
112
112
|
if (!key) {
|
|
113
|
-
err(
|
|
113
|
+
err('API key cannot be empty');
|
|
114
114
|
process.exit(1);
|
|
115
115
|
}
|
|
116
116
|
if (!validateKeyFormat(key)) {
|
|
117
|
-
err(
|
|
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:
|
|
122
|
+
out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
|
|
123
123
|
});
|
|
124
124
|
auth
|
|
125
|
-
.command(
|
|
126
|
-
.description(
|
|
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(
|
|
131
|
+
err('API key cannot be empty');
|
|
132
132
|
process.exit(1);
|
|
133
133
|
}
|
|
134
134
|
if (!validateKeyFormat(key)) {
|
|
135
|
-
err(
|
|
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:
|
|
140
|
+
out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
|
|
141
141
|
});
|
|
142
142
|
auth
|
|
143
|
-
.command(
|
|
144
|
-
.description(
|
|
145
|
-
.option(
|
|
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:
|
|
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:
|
|
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:
|
|
175
|
+
const result = { source: 'none', key_preview: null, authenticated: false };
|
|
176
176
|
if (opts.pretty) {
|
|
177
|
-
outLine(
|
|
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(
|
|
186
|
-
.description(
|
|
185
|
+
.command('logout')
|
|
186
|
+
.description('Remove stored API key')
|
|
187
187
|
.action(() => {
|
|
188
188
|
if (!clearConfig()) {
|
|
189
|
-
out({ status:
|
|
189
|
+
out({ status: 'ok', message: 'No API key stored' });
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
|
-
out({ status:
|
|
192
|
+
out({ status: 'ok', message: 'API key removed' });
|
|
193
193
|
});
|
|
194
194
|
}
|
package/dist/commands/batch.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerBatch(agentCmd: Command): void;
|
package/dist/commands/batch.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import { randomUUID } from
|
|
3
|
-
import { getApiKey } from
|
|
4
|
-
import { cancelBatchRuns, getBatchRuns, submitBatch } from
|
|
5
|
-
import { findBatch, loadBatches, saveBatch } from
|
|
6
|
-
import { err, handleApiError, out, outLine } from
|
|
7
|
-
import { BASE_URL } from
|
|
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] ===
|
|
34
|
+
return { value, next: line[i] === ',' ? i + 1 : i };
|
|
35
35
|
}
|
|
36
36
|
// Unquoted field — read to next comma
|
|
37
|
-
const commaIdx = line.indexOf(
|
|
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(
|
|
46
|
+
throw new Error('CSV file is empty');
|
|
47
47
|
const header = lines[0].trim();
|
|
48
|
-
if (header !==
|
|
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(
|
|
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(
|
|
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(
|
|
72
|
-
.description(
|
|
71
|
+
.command('batch')
|
|
72
|
+
.description('Batch run operations')
|
|
73
73
|
.enablePositionalOptions();
|
|
74
74
|
// ── batch run ───────────────────────────────────────────────────────────────
|
|
75
75
|
batchCmd
|
|
76
|
-
.command(
|
|
77
|
-
.description(
|
|
78
|
-
.requiredOption(
|
|
79
|
-
.option(
|
|
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,
|
|
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({
|
|
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({
|
|
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(
|
|
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({
|
|
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(
|
|
135
|
-
.description(
|
|
136
|
-
.option(
|
|
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(
|
|
149
|
+
outLine('No batches found.');
|
|
142
150
|
return;
|
|
143
151
|
}
|
|
144
|
-
outLine(`${
|
|
145
|
-
outLine(`${
|
|
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(
|
|
157
|
-
.description(
|
|
158
|
-
.option(
|
|
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,
|
|
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(
|
|
194
|
-
.description(
|
|
195
|
-
.option(
|
|
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 ===
|
|
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(`${
|
|
210
|
-
outLine(`${
|
|
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
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerBrowser(program: Command): void;
|
package/dist/commands/browser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getApiKey } from
|
|
2
|
-
import { browserSessionCreate } from
|
|
3
|
-
import { err, handleApiError, out, outLine } from
|
|
4
|
-
const VALID_BROWSER_PROFILES = [
|
|
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(
|
|
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(
|
|
23
|
-
.description(
|
|
22
|
+
.command('browser')
|
|
23
|
+
.description('Browser commands')
|
|
24
24
|
.enablePositionalOptions();
|
|
25
25
|
const sessionCmd = browserCmd
|
|
26
|
-
.command(
|
|
27
|
-
.description(
|
|
26
|
+
.command('session')
|
|
27
|
+
.description('Manage browser sessions')
|
|
28
28
|
.enablePositionalOptions();
|
|
29
29
|
sessionCmd
|
|
30
|
-
.command(
|
|
31
|
-
.description(
|
|
32
|
-
.option(
|
|
33
|
-
.option(
|
|
34
|
-
.option(
|
|
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
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerConfigureClaude(program: Command): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { err, errLine, out } from
|
|
2
|
-
import { validatedApiKey } from
|
|
3
|
-
import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isTinyfishConfiguredInSettings, isTinyfishConfiguredInClaudeMd, } from
|
|
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(
|
|
31
|
+
errLine('Configuring Claude Code to use TinyFish...\n');
|
|
32
32
|
writeSettingsJson(mergeSettings(settings));
|
|
33
|
-
errLine(
|
|
33
|
+
errLine(' Updated settings.json (permission + hooks)');
|
|
34
34
|
const { content, replaced } = mergeClaudeMd(claudeMd);
|
|
35
35
|
writeClaudeMd(content);
|
|
36
36
|
errLine(replaced
|
|
37
|
-
?
|
|
38
|
-
:
|
|
39
|
-
errLine(
|
|
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(
|
|
43
|
-
errLine(
|
|
44
|
-
errLine(
|
|
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:
|
|
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(
|
|
51
|
-
out({ status:
|
|
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(
|
|
54
|
+
errLine('Removing TinyFish configuration from Claude Code...\n');
|
|
55
55
|
writeSettingsJson(removeFromSettings(settings));
|
|
56
|
-
errLine(
|
|
56
|
+
errLine(' Cleaned settings.json');
|
|
57
57
|
writeClaudeMd(removeFromClaudeMd(claudeMd));
|
|
58
|
-
errLine(
|
|
59
|
-
errLine(
|
|
60
|
-
out({ status:
|
|
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(
|
|
65
|
-
.description(
|
|
66
|
-
.option(
|
|
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
|
|
2
|
-
import { type TakeHoistedCliInstall } from
|
|
3
|
-
import { type AgentClient, type ConnectAuthMode } from
|
|
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;
|