@tiny-fish/cli 0.39.1-next.309 → 0.39.1-next.311
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/connect.d.ts +2 -14
- package/dist/commands/connect.js +23 -56
- package/dist/commands/profile.d.ts +0 -3
- package/dist/commands/profile.js +7 -22
- package/dist/commands/run.js +20 -40
- package/dist/commands/runs.js +24 -58
- package/dist/lib/auth.d.ts +1 -3
- package/dist/lib/auth.js +1 -1
- package/dist/lib/claude-config.d.ts +0 -4
- package/dist/lib/claude-config.js +4 -4
- package/dist/lib/cli-install.d.ts +0 -1
- package/dist/lib/cli-install.js +0 -1
- package/dist/lib/client.js +82 -147
- package/dist/lib/connect-all-auth.js +1 -1
- package/dist/lib/connect-all.js +8 -27
- package/dist/lib/connect-clients.d.ts +3 -6
- package/dist/lib/connect-fallback.d.ts +2 -1
- package/dist/lib/connect-picker.d.ts +3 -2
- package/dist/lib/connect-runtime.js +4 -6
- package/dist/lib/harness-spec.d.ts +5 -34
- package/dist/lib/harness-spec.js +1 -30
- package/dist/lib/hermes-env.d.ts +0 -1
- package/dist/lib/hermes-env.js +1 -3
- package/dist/lib/hermes-plugin.js +1 -9
- package/dist/lib/output.d.ts +2 -0
- package/dist/lib/output.js +9 -0
- package/dist/lib/registration-detect.d.ts +0 -1
- package/dist/lib/registration-detect.js +36 -84
- package/dist/lib/setup-telemetry.d.ts +3 -7
- package/dist/lib/setup-telemetry.js +5 -5
- package/dist/lib/skill-install.js +1 -9
- package/dist/lib/types.d.ts +4 -3
- package/dist/program.js +2 -3
- package/package.json +1 -2
package/dist/lib/client.js
CHANGED
|
@@ -108,6 +108,9 @@ function createSdkClient(apiKey, timeout, baseUrl) {
|
|
|
108
108
|
...(timeout !== undefined ? { timeout } : {}),
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
+
function sdk(apiKey, call) {
|
|
112
|
+
return call(createSdkClient(apiKey)).catch(rethrowSdkError);
|
|
113
|
+
}
|
|
111
114
|
function rethrowSdkError(error) {
|
|
112
115
|
if (error instanceof APIStatusError) {
|
|
113
116
|
throw new ApiError(error.statusCode, error.message);
|
|
@@ -136,14 +139,8 @@ const batchGetResponseSchema = z.object({
|
|
|
136
139
|
data: z.array(runSchema),
|
|
137
140
|
not_found: z.array(z.string()).nullable(),
|
|
138
141
|
});
|
|
139
|
-
const batchCancelResultSchema = z.object({
|
|
140
|
-
run_id: z.string(),
|
|
141
|
-
status: z.string(),
|
|
142
|
-
cancelled_at: z.string().nullable(),
|
|
143
|
-
message: z.string().nullable(),
|
|
144
|
-
});
|
|
145
142
|
const batchCancelResponseSchema = z.object({
|
|
146
|
-
results: z.array(
|
|
143
|
+
results: z.array(cancelRunResponseSchema),
|
|
147
144
|
not_found: z.array(z.string()).nullable(),
|
|
148
145
|
});
|
|
149
146
|
const runStepSchema = z.object({
|
|
@@ -219,27 +216,21 @@ async function* parseSseStream(stream) {
|
|
|
219
216
|
reader.releaseLock();
|
|
220
217
|
}
|
|
221
218
|
}
|
|
222
|
-
export
|
|
223
|
-
|
|
224
|
-
const response = await
|
|
219
|
+
export function runSync(req, apiKey) {
|
|
220
|
+
return sdk(apiKey, async (client) => {
|
|
221
|
+
const response = await client.post('/v1/automation/run', {
|
|
225
222
|
json: req,
|
|
226
223
|
});
|
|
227
224
|
return parseWithSchema(agentRunResponseSchema, response, 'Invalid agent run response');
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
rethrowSdkError(error);
|
|
231
|
-
}
|
|
225
|
+
});
|
|
232
226
|
}
|
|
233
|
-
export
|
|
234
|
-
|
|
235
|
-
const response = await
|
|
227
|
+
export function runAsync(req, apiKey) {
|
|
228
|
+
return sdk(apiKey, async (client) => {
|
|
229
|
+
const response = await client.post('/v1/automation/run-async', {
|
|
236
230
|
json: req,
|
|
237
231
|
});
|
|
238
232
|
return parseWithSchema(agentRunAsyncResponseSchema, response, 'Invalid async run response');
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
rethrowSdkError(error);
|
|
242
|
-
}
|
|
233
|
+
});
|
|
243
234
|
}
|
|
244
235
|
export async function* runStream(req, apiKey, signal) {
|
|
245
236
|
let stream = null;
|
|
@@ -268,110 +259,75 @@ export async function* runStream(req, apiKey, signal) {
|
|
|
268
259
|
}
|
|
269
260
|
}
|
|
270
261
|
}
|
|
271
|
-
export
|
|
272
|
-
|
|
273
|
-
return await createSdkClient(apiKey, timeout, baseUrl).runs.list(opts);
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
rethrowSdkError(error);
|
|
277
|
-
}
|
|
262
|
+
export function listRuns(opts, apiKey, timeout, baseUrl) {
|
|
263
|
+
return createSdkClient(apiKey, timeout, baseUrl).runs.list(opts).catch(rethrowSdkError);
|
|
278
264
|
}
|
|
279
|
-
export
|
|
280
|
-
|
|
281
|
-
return await createSdkClient(apiKey).runs.get(runId);
|
|
282
|
-
}
|
|
283
|
-
catch (error) {
|
|
284
|
-
rethrowSdkError(error);
|
|
285
|
-
}
|
|
265
|
+
export function getRun(runId, apiKey) {
|
|
266
|
+
return sdk(apiKey, (client) => client.runs.get(runId));
|
|
286
267
|
}
|
|
287
|
-
export
|
|
288
|
-
|
|
268
|
+
export function getRunSteps(runId, apiKey) {
|
|
269
|
+
return sdk(apiKey, async (client) => {
|
|
289
270
|
// The public GET /v1/runs/:id route returns the base run payload plus `steps`;
|
|
290
271
|
// the installed SDK runSchema only models the base run, so steps need this local schema.
|
|
291
|
-
const response = await
|
|
272
|
+
const response = await client.get(`/v1/runs/${encodeURIComponent(runId)}`, {
|
|
273
|
+
params: { screenshots: 'none' },
|
|
274
|
+
});
|
|
292
275
|
return parseWithSchema(runStepsResponseSchema, response, 'Invalid run steps response');
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
rethrowSdkError(error);
|
|
296
|
-
}
|
|
276
|
+
});
|
|
297
277
|
}
|
|
298
|
-
export
|
|
299
|
-
|
|
300
|
-
const response = await
|
|
278
|
+
export function searchQuery(params, apiKey) {
|
|
279
|
+
return sdk(apiKey, async (client) => {
|
|
280
|
+
const response = await client.search.query(params);
|
|
301
281
|
return searchQueryResponseSchema.parse(response);
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
rethrowSdkError(error);
|
|
305
|
-
}
|
|
282
|
+
});
|
|
306
283
|
}
|
|
307
|
-
export
|
|
308
|
-
|
|
309
|
-
const response = await
|
|
284
|
+
export function fetchContentGet(params, apiKey) {
|
|
285
|
+
return sdk(apiKey, async (client) => {
|
|
286
|
+
const response = await client.fetch.getContents(params);
|
|
310
287
|
return fetchResponseSchema.parse(response);
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
rethrowSdkError(error);
|
|
314
|
-
}
|
|
288
|
+
});
|
|
315
289
|
}
|
|
316
|
-
export
|
|
317
|
-
|
|
318
|
-
const response = await
|
|
290
|
+
export function browserSessionCreate(params, apiKey) {
|
|
291
|
+
return sdk(apiKey, async (client) => {
|
|
292
|
+
const response = await client.post('/v1/browser', { json: params });
|
|
319
293
|
return browserSessionSchema.parse(response);
|
|
320
|
-
}
|
|
321
|
-
catch (error) {
|
|
322
|
-
rethrowSdkError(error);
|
|
323
|
-
}
|
|
294
|
+
});
|
|
324
295
|
}
|
|
325
|
-
export
|
|
326
|
-
|
|
327
|
-
const response = await
|
|
296
|
+
export function cancelRun(runId, apiKey) {
|
|
297
|
+
return sdk(apiKey, async (client) => {
|
|
298
|
+
const response = await client.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
299
|
+
json: {},
|
|
300
|
+
});
|
|
328
301
|
return parseWithSchema(cancelRunResponseSchema, response, 'Invalid cancel run response');
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
rethrowSdkError(error);
|
|
332
|
-
}
|
|
302
|
+
});
|
|
333
303
|
}
|
|
334
304
|
// ── Wallet ──────────────────────────────────────────────────────────────────
|
|
335
|
-
export
|
|
336
|
-
|
|
337
|
-
return await createSdkClient(apiKey).wallet.get();
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
rethrowSdkError(error);
|
|
341
|
-
}
|
|
305
|
+
export function getWallet(apiKey) {
|
|
306
|
+
return sdk(apiKey, (client) => client.wallet.get());
|
|
342
307
|
}
|
|
343
|
-
export
|
|
344
|
-
|
|
345
|
-
const response = await
|
|
308
|
+
export function submitBatch(req, apiKey) {
|
|
309
|
+
return sdk(apiKey, async (client) => {
|
|
310
|
+
const response = await client.post('/v1/automation/run-batch', {
|
|
346
311
|
json: req,
|
|
347
312
|
});
|
|
348
313
|
return parseWithSchema(batchRunResponseSchema, response, 'Invalid batch run response');
|
|
349
|
-
}
|
|
350
|
-
catch (error) {
|
|
351
|
-
rethrowSdkError(error);
|
|
352
|
-
}
|
|
314
|
+
});
|
|
353
315
|
}
|
|
354
|
-
export
|
|
355
|
-
|
|
356
|
-
const response = await
|
|
316
|
+
export function getBatchRuns(runIds, apiKey) {
|
|
317
|
+
return sdk(apiKey, async (client) => {
|
|
318
|
+
const response = await client.post('/v1/runs/batch', {
|
|
357
319
|
json: { run_ids: runIds },
|
|
358
320
|
});
|
|
359
321
|
return parseWithSchema(batchGetResponseSchema, response, 'Invalid batch get response');
|
|
360
|
-
}
|
|
361
|
-
catch (error) {
|
|
362
|
-
rethrowSdkError(error);
|
|
363
|
-
}
|
|
322
|
+
});
|
|
364
323
|
}
|
|
365
|
-
export
|
|
366
|
-
|
|
367
|
-
const response = await
|
|
324
|
+
export function cancelBatchRuns(runIds, apiKey) {
|
|
325
|
+
return sdk(apiKey, async (client) => {
|
|
326
|
+
const response = await client.post('/v1/runs/batch/cancel', {
|
|
368
327
|
json: { run_ids: runIds },
|
|
369
328
|
});
|
|
370
329
|
return parseWithSchema(batchCancelResponseSchema, response, 'Invalid batch cancel response');
|
|
371
|
-
}
|
|
372
|
-
catch (error) {
|
|
373
|
-
rethrowSdkError(error);
|
|
374
|
-
}
|
|
330
|
+
});
|
|
375
331
|
}
|
|
376
332
|
const profileCreateResponseSchema = z.object({
|
|
377
333
|
id: z.string(),
|
|
@@ -384,25 +340,19 @@ const profileUploadResponseSchema = z.object({
|
|
|
384
340
|
domains_updated: z.array(z.string()),
|
|
385
341
|
domains_failed: z.array(z.string()).optional(),
|
|
386
342
|
});
|
|
387
|
-
export
|
|
388
|
-
|
|
389
|
-
const response = await
|
|
343
|
+
export function profileCreate(name, apiKey) {
|
|
344
|
+
return sdk(apiKey, async (client) => {
|
|
345
|
+
const response = await client.post('/v1/profiles', {
|
|
390
346
|
json: { name },
|
|
391
347
|
});
|
|
392
348
|
return parseWithSchema(profileCreateResponseSchema, response, 'Invalid profile create response');
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
rethrowSdkError(error);
|
|
396
|
-
}
|
|
349
|
+
});
|
|
397
350
|
}
|
|
398
|
-
export
|
|
399
|
-
|
|
400
|
-
const response = await
|
|
351
|
+
export function profileUpload(profileId, cookies, apiKey) {
|
|
352
|
+
return sdk(apiKey, async (client) => {
|
|
353
|
+
const response = await client.post(`/v1/profiles/${encodeURIComponent(profileId)}/upload`, { json: { cookies } });
|
|
401
354
|
return parseWithSchema(profileUploadResponseSchema, response, 'Invalid profile upload response');
|
|
402
|
-
}
|
|
403
|
-
catch (error) {
|
|
404
|
-
rethrowSdkError(error);
|
|
405
|
-
}
|
|
355
|
+
});
|
|
406
356
|
}
|
|
407
357
|
// ── Vault ───────────────────────────────────────────────────────────────────
|
|
408
358
|
// /v1/vault/* returns camelCase fields (unlike the snake_case rest of the API).
|
|
@@ -451,52 +401,37 @@ const vaultItemsSyncResponseSchema = z.object({
|
|
|
451
401
|
removed: z.number(),
|
|
452
402
|
}),
|
|
453
403
|
});
|
|
454
|
-
export
|
|
455
|
-
|
|
456
|
-
const response = await
|
|
404
|
+
export function vaultConnections(apiKey) {
|
|
405
|
+
return sdk(apiKey, async (client) => {
|
|
406
|
+
const response = await client.get('/v1/vault/connections');
|
|
457
407
|
return parseWithSchema(vaultConnectionsListResponseSchema, response, 'Invalid vault connections response');
|
|
458
|
-
}
|
|
459
|
-
catch (error) {
|
|
460
|
-
rethrowSdkError(error);
|
|
461
|
-
}
|
|
408
|
+
});
|
|
462
409
|
}
|
|
463
|
-
export
|
|
464
|
-
|
|
465
|
-
const response = await
|
|
410
|
+
export function vaultConnect(req, apiKey) {
|
|
411
|
+
return sdk(apiKey, async (client) => {
|
|
412
|
+
const response = await client.post('/v1/vault/connections', {
|
|
466
413
|
json: req,
|
|
467
414
|
});
|
|
468
415
|
return parseWithSchema(vaultConnectResponseSchema, response, 'Invalid vault connect response');
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
rethrowSdkError(error);
|
|
472
|
-
}
|
|
416
|
+
});
|
|
473
417
|
}
|
|
474
|
-
export
|
|
475
|
-
|
|
476
|
-
const response = await
|
|
418
|
+
export function vaultDisconnect(connectionId, apiKey) {
|
|
419
|
+
return sdk(apiKey, async (client) => {
|
|
420
|
+
const response = await client.del(`/v1/vault/connections/${encodeURIComponent(connectionId)}`);
|
|
477
421
|
return parseWithSchema(vaultDisconnectResponseSchema, response, 'Invalid vault disconnect response');
|
|
478
|
-
}
|
|
479
|
-
catch (error) {
|
|
480
|
-
rethrowSdkError(error);
|
|
481
|
-
}
|
|
422
|
+
});
|
|
482
423
|
}
|
|
483
|
-
export
|
|
484
|
-
|
|
485
|
-
const response = await
|
|
424
|
+
export function vaultItems(apiKey) {
|
|
425
|
+
return sdk(apiKey, async (client) => {
|
|
426
|
+
const response = await client.get('/v1/vault/items');
|
|
486
427
|
return parseWithSchema(vaultItemsListResponseSchema, response, 'Invalid vault items response');
|
|
487
|
-
}
|
|
488
|
-
catch (error) {
|
|
489
|
-
rethrowSdkError(error);
|
|
490
|
-
}
|
|
428
|
+
});
|
|
491
429
|
}
|
|
492
|
-
export
|
|
493
|
-
|
|
494
|
-
const response = await
|
|
430
|
+
export function vaultSync(apiKey) {
|
|
431
|
+
return sdk(apiKey, async (client) => {
|
|
432
|
+
const response = await client.post('/v1/vault/items/sync', {
|
|
495
433
|
json: {},
|
|
496
434
|
});
|
|
497
435
|
return parseWithSchema(vaultItemsSyncResponseSchema, response, 'Invalid vault sync response');
|
|
498
|
-
}
|
|
499
|
-
catch (error) {
|
|
500
|
-
rethrowSdkError(error);
|
|
501
|
-
}
|
|
436
|
+
});
|
|
502
437
|
}
|
|
@@ -19,7 +19,7 @@ const NO_BROWSER_REASONS = new Set([
|
|
|
19
19
|
// Derived: these block `connect` on a browser sign-in when keyless.
|
|
20
20
|
const BROWSER_SIGN_IN = new Set(ALL_HARNESSES.filter((harness) => {
|
|
21
21
|
const native = NATIVE_BY_HARNESS.get(harness);
|
|
22
|
-
return !!native?.loginArgs
|
|
22
|
+
return !!native?.loginArgs;
|
|
23
23
|
}));
|
|
24
24
|
// Derived: these refuse a keyless install, so no sign-in helps.
|
|
25
25
|
const KEY_REQUIRED = new Set(ALL_HARNESSES.filter((harness) => !!NATIVE_BY_HARNESS.get(harness)?.keyRequired));
|
package/dist/lib/connect-all.js
CHANGED
|
@@ -7,7 +7,6 @@ import { installTinyFishCli, } from './cli-install.js';
|
|
|
7
7
|
import { authGate, mapConnectError, OAUTH_SIGN_IN_TIMEOUT_MS } from './connect-all-auth.js';
|
|
8
8
|
import { actionable, computeExitCode, reloadHint, renderSummary } from './connect-all-summary.js';
|
|
9
9
|
import { planText, uninstallHarness, uninstallPlanText } from './connect-all-uninstall.js';
|
|
10
|
-
import { NATIVE_BY_HARNESS } from './connect-clients.js';
|
|
11
10
|
import { createStdinPrompt, runCliFallback } from './connect-fallback.js';
|
|
12
11
|
import { gateApiKey } from './connect-preflight.js';
|
|
13
12
|
import { ConnectInterruptedError } from './connect-runtime.js';
|
|
@@ -19,21 +18,6 @@ import { errLine, outLine, setErrIndent } from './output.js';
|
|
|
19
18
|
import { sendSetupCompleted, sendSetupStarted, } from './setup-telemetry.js';
|
|
20
19
|
import { verifyMcpAuth, verifyMcpHealth } from './verify.js';
|
|
21
20
|
const STEP_INDENT = ' ';
|
|
22
|
-
// Derived: a native add carries the key, or the CLI holds it itself.
|
|
23
|
-
const KEY_AUTH_CAPABLE = new Set(ALL_HARNESSES.filter((harness) => !!NATIVE_BY_HARNESS.get(harness)?.keyAuth || harnessSpec(harness).keyHeldByCli));
|
|
24
|
-
async function runHarnessConnect(harness, opts, extras) {
|
|
25
|
-
return connectHarness(harness, {
|
|
26
|
-
apiKey: opts.apiKey,
|
|
27
|
-
mcpUrl: opts.mcpUrl,
|
|
28
|
-
launch: false,
|
|
29
|
-
keyAuthOnly: extras.keyAuthOnly,
|
|
30
|
-
authTimeoutMs: extras.authTimeoutMs,
|
|
31
|
-
hoistedCliInstall: extras.hoistedCliInstall,
|
|
32
|
-
onPostInstallFailed: extras.onPostInstallFailed,
|
|
33
|
-
verbose: opts.verbose,
|
|
34
|
-
deferOutro: extras.deferOutro,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
21
|
/** Undefined means Ctrl+C: the caller stops the whole run. */
|
|
38
22
|
function runHoistedCliInstall(verbose) {
|
|
39
23
|
const startedAt = Date.now();
|
|
@@ -90,8 +74,8 @@ async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliIn
|
|
|
90
74
|
return settled;
|
|
91
75
|
const isTTY = detectHumanInitiated();
|
|
92
76
|
const apiKey = validatedApiKey(opts.apiKey);
|
|
93
|
-
//
|
|
94
|
-
const keyed = !!apiKey
|
|
77
|
+
// All harnesses carry a key; one that cannot fails harness_too_old.
|
|
78
|
+
const keyed = !!apiKey;
|
|
95
79
|
const fixCommand = `tinyfish connect ${harness}`;
|
|
96
80
|
const gate = await authGate(base, oauthBudget, fixCommand, { prompt, isTTY, keyed });
|
|
97
81
|
if (gate.result)
|
|
@@ -103,7 +87,11 @@ async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliIn
|
|
|
103
87
|
let postInstallFailed = false;
|
|
104
88
|
try {
|
|
105
89
|
// A keyed install too old for key auth fails, no browser.
|
|
106
|
-
authMode = await
|
|
90
|
+
authMode = await connectHarness(harness, {
|
|
91
|
+
apiKey: opts.apiKey,
|
|
92
|
+
mcpUrl: opts.mcpUrl,
|
|
93
|
+
launch: false,
|
|
94
|
+
verbose: opts.verbose,
|
|
107
95
|
keyAuthOnly: keyed && !isTTY,
|
|
108
96
|
authTimeoutMs: headlessAuth ? OAUTH_SIGN_IN_TIMEOUT_MS : undefined,
|
|
109
97
|
hoistedCliInstall,
|
|
@@ -337,10 +325,6 @@ export async function runConnectAll(opts) {
|
|
|
337
325
|
const prompt = isTTY ? createStdinPrompt() : undefined;
|
|
338
326
|
const detections = detectInstalledHarnesses();
|
|
339
327
|
const gateKey = validatedApiKey(opts.apiKey);
|
|
340
|
-
// Keyed installs first: working tools land before sign-ins can block.
|
|
341
|
-
if (gateKey) {
|
|
342
|
-
detections.sort((a, b) => Number(KEY_AUTH_CAPABLE.has(b.harness)) - Number(KEY_AUTH_CAPABLE.has(a.harness)));
|
|
343
|
-
}
|
|
344
328
|
const pick = await resolvePick(detections, opts, prompt);
|
|
345
329
|
if (pick.cancelled) {
|
|
346
330
|
// Before summary and telemetry: an explicit nothing is not a setup.
|
|
@@ -349,10 +333,7 @@ export async function runConnectAll(opts) {
|
|
|
349
333
|
}
|
|
350
334
|
const { deselected, mode } = pick;
|
|
351
335
|
// Mirrors processHarness's gate: headless, only keyless CLI-login harnesses skip.
|
|
352
|
-
const willAttempt = (harness) => !deselected.has(harness) &&
|
|
353
|
-
(isTTY ||
|
|
354
|
-
!harnessSpec(harness).signInViaCliLogin ||
|
|
355
|
-
(!!gateKey && KEY_AUTH_CAPABLE.has(harness)));
|
|
336
|
+
const willAttempt = (harness) => !deselected.has(harness) && (isTTY || !harnessSpec(harness).signInViaCliLogin || !!gateKey);
|
|
356
337
|
const hoisted = hoistCliIfNeeded(detections, opts, willAttempt);
|
|
357
338
|
if (hoisted === 'interrupted') {
|
|
358
339
|
errLine('Setup interrupted — run the command again to finish.');
|
|
@@ -18,7 +18,7 @@ export declare const CURSOR_SKILL_TARGET: {
|
|
|
18
18
|
readonly displayName: string;
|
|
19
19
|
};
|
|
20
20
|
/** The harness reads the key from its own store. */
|
|
21
|
-
|
|
21
|
+
interface SeededInstall {
|
|
22
22
|
/** Pins the add child at the home the key landed in. */
|
|
23
23
|
env: typeof process.env;
|
|
24
24
|
/** Answers prompts `mcp add` still asks once the key is seeded. */
|
|
@@ -45,8 +45,6 @@ interface BaseMcpClient extends SupportedCommand, Pick<HarnessSpec, 'skillAgent'
|
|
|
45
45
|
seedKey?: (apiKey: string) => SeededInstall;
|
|
46
46
|
};
|
|
47
47
|
loginArgs?: string[];
|
|
48
|
-
/** Keyless `mcp add` itself blocks on the browser OAuth. */
|
|
49
|
-
oauthInAdd?: boolean;
|
|
50
48
|
removals: {
|
|
51
49
|
args: string[];
|
|
52
50
|
label: string;
|
|
@@ -69,17 +67,16 @@ interface BaseMcpClient extends SupportedCommand, Pick<HarnessSpec, 'skillAgent'
|
|
|
69
67
|
};
|
|
70
68
|
}
|
|
71
69
|
/** Keyless installs use this argv; the harness signs itself in. */
|
|
72
|
-
|
|
70
|
+
interface OauthCapableMcpClient extends BaseMcpClient {
|
|
73
71
|
keyRequired?: false;
|
|
74
72
|
addArgs: (mcpUrl: string) => string[];
|
|
75
73
|
}
|
|
76
74
|
/** No sign-in exists here, so connect refuses a keyless install (Hermes). */
|
|
77
|
-
|
|
75
|
+
interface KeyRequiredMcpClient extends BaseMcpClient {
|
|
78
76
|
keyRequired: true;
|
|
79
77
|
keyAuth: NonNullable<BaseMcpClient['keyAuth']>;
|
|
80
78
|
/** Restoring any of these three would restore the OAuth fallback. */
|
|
81
79
|
addArgs?: never;
|
|
82
|
-
oauthInAdd?: never;
|
|
83
80
|
loginArgs?: never;
|
|
84
81
|
}
|
|
85
82
|
export type NativeMcpClient = OauthCapableMcpClient | KeyRequiredMcpClient;
|
|
@@ -4,7 +4,7 @@ export declare const SEARCH_RETRY = "tinyfish search query --pretty -- \"<your q
|
|
|
4
4
|
export declare const DEFAULT_VERIFICATION_QUERY = "today's top Hacker News story";
|
|
5
5
|
export declare const MALFORMED_ENV_KEY_MESSAGE = "TINYFISH_API_KEY is set but malformed; it will override any stored key. Fix or unset it, then re-run.";
|
|
6
6
|
export declare const MALFORMED_STORED_KEY_MESSAGE = "The stored TinyFish API key is malformed. Run: tinyfish auth login";
|
|
7
|
-
|
|
7
|
+
interface CliFallbackHooks {
|
|
8
8
|
onStepStart?: (stage: ConnectFailureStage) => void;
|
|
9
9
|
onStepDone?: (phase: ConnectCheckpoint) => void;
|
|
10
10
|
}
|
|
@@ -22,3 +22,4 @@ export interface CliFallbackOptions {
|
|
|
22
22
|
export declare function createStdinPrompt(): Prompt;
|
|
23
23
|
/** Steps print retry lines and return outcomes; only interrupts throw. */
|
|
24
24
|
export declare function runCliFallback(opts: CliFallbackOptions): Promise<CliFallbackOutcome>;
|
|
25
|
+
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
2
|
import { type Harness, type HarnessDetection } from './harness-detect.js';
|
|
3
|
-
|
|
3
|
+
type PickSelection = Harness[] | 'cancel';
|
|
4
4
|
export interface PickResult {
|
|
5
5
|
selection: PickSelection;
|
|
6
6
|
mode: 'pick' | 'pick_numeric';
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
type PickerStdin = Readable & {
|
|
9
9
|
setRawMode?: (raw: boolean) => unknown;
|
|
10
10
|
};
|
|
11
11
|
export interface PickerIo {
|
|
@@ -14,3 +14,4 @@ export interface PickerIo {
|
|
|
14
14
|
prompt?: (question: string) => Promise<string>;
|
|
15
15
|
}
|
|
16
16
|
export declare function pickHarnesses(detections: HarnessDetection[], connectedBefore: ReadonlySet<string>, io?: PickerIo): Promise<PickResult>;
|
|
17
|
+
export {};
|
|
@@ -49,8 +49,6 @@ export function stageDurationOf(error) {
|
|
|
49
49
|
const value = error?.stageDurationMs;
|
|
50
50
|
return typeof value === 'number' ? value : undefined;
|
|
51
51
|
}
|
|
52
|
-
class PrerequisiteError extends ConnectStepError {
|
|
53
|
-
}
|
|
54
52
|
/** A bounded sign-in wait elapsed — unfinished, not broken. */
|
|
55
53
|
export class SignInTimeoutError extends ConnectStepError {
|
|
56
54
|
constructor(message, cause) {
|
|
@@ -190,7 +188,7 @@ function throwProbeFailure(client, result) {
|
|
|
190
188
|
`${probeTimeoutMs(client) / 1000}s. Retry.`, 'timeout', { cause: result.error });
|
|
191
189
|
}
|
|
192
190
|
// It ran and exited non-zero, so the version is still answerable.
|
|
193
|
-
throw new
|
|
191
|
+
throw new ConnectStepError(client.supportCheck.unavailableMessage, 'harness_command_unsupported', { cause: result.error, harnessVersion: probeHarnessVersion(client) });
|
|
194
192
|
}
|
|
195
193
|
function supportProbeOutput(client) {
|
|
196
194
|
const result = spawn.sync(client.command, client.supportCheck.args, {
|
|
@@ -201,7 +199,7 @@ function supportProbeOutput(client) {
|
|
|
201
199
|
});
|
|
202
200
|
// 127 is the shell's not-found code; a wrapper shim with no real binary behind it exits that.
|
|
203
201
|
if (commandNotFound(result.error) || result.status === 127) {
|
|
204
|
-
throw new
|
|
202
|
+
throw new ConnectStepError(`${client.displayName} is not installed or not available on PATH.`, 'harness_not_installed', { cause: result.error });
|
|
205
203
|
}
|
|
206
204
|
if (result.error || result.status !== 0) {
|
|
207
205
|
throwProbeFailure(client, result);
|
|
@@ -218,7 +216,7 @@ function requireEssential(client, output) {
|
|
|
218
216
|
if (client.supportCheck.patterns.every((pattern) => pattern.test(output)))
|
|
219
217
|
return;
|
|
220
218
|
debugProbeOutput(client, output);
|
|
221
|
-
throw new
|
|
219
|
+
throw new ConnectStepError(client.supportCheck.unavailableMessage, 'harness_too_old', {
|
|
222
220
|
harnessVersion: probeHarnessVersion(client),
|
|
223
221
|
});
|
|
224
222
|
}
|
|
@@ -235,7 +233,7 @@ function resolveVariant(client, output) {
|
|
|
235
233
|
return undefined;
|
|
236
234
|
// Unacknowledged policy warnings can block the install; fail before it does.
|
|
237
235
|
debugProbeOutput(client, output);
|
|
238
|
-
throw new
|
|
236
|
+
throw new ConnectStepError(`${client.displayName} lists \`${unknown}\`, which this TinyFish CLI does not recognise. ` +
|
|
239
237
|
'Upgrade the TinyFish CLI with `tinyfish upgrade` and retry.', 'harness_too_old', { harnessVersion: probeHarnessVersion(client) });
|
|
240
238
|
}
|
|
241
239
|
/** Refresh paths need the advertised alternative without connect's version probe. */
|
|
@@ -67,17 +67,17 @@ export interface HarnessSpec {
|
|
|
67
67
|
/** Printed after a successful connect, on both the launch and non-launch paths. */
|
|
68
68
|
postConnectNote?: string;
|
|
69
69
|
/** Sign-in is the CLI's own interactive `tinyfish auth login`. */
|
|
70
|
-
signInViaCliLogin
|
|
70
|
+
signInViaCliLogin?: true;
|
|
71
71
|
/** Stored-key auth verification works against this harness's config. */
|
|
72
|
-
canVerifyAuth
|
|
72
|
+
canVerifyAuth?: true;
|
|
73
73
|
/** The CLI holds or writes the key; no harness `mcp add` carries it. */
|
|
74
|
-
keyHeldByCli
|
|
74
|
+
keyHeldByCli?: true;
|
|
75
75
|
/** Connect writes the MCP config file; no harness binary is spawned. */
|
|
76
|
-
cliWritesConfig
|
|
76
|
+
cliWritesConfig?: true;
|
|
77
77
|
/** Binary resolves the config path; absent binary, nothing reads it. */
|
|
78
78
|
configPathFromBinary?: true;
|
|
79
79
|
/** `mcp add` succeeds unauthenticated; OAuth lands at first tool use. */
|
|
80
|
-
authDeferredAtInstall
|
|
80
|
+
authDeferredAtInstall?: true;
|
|
81
81
|
}
|
|
82
82
|
/** One entry per harness; every per-harness list derives from it. */
|
|
83
83
|
export declare const HARNESS_SPECS: {
|
|
@@ -110,11 +110,6 @@ export declare const HARNESS_SPECS: {
|
|
|
110
110
|
args: string[];
|
|
111
111
|
label: string;
|
|
112
112
|
}[];
|
|
113
|
-
signInViaCliLogin: false;
|
|
114
|
-
canVerifyAuth: false;
|
|
115
|
-
keyHeldByCli: false;
|
|
116
|
-
cliWritesConfig: false;
|
|
117
|
-
authDeferredAtInstall: false;
|
|
118
113
|
};
|
|
119
114
|
codex: {
|
|
120
115
|
command: string;
|
|
@@ -136,10 +131,6 @@ export declare const HARNESS_SPECS: {
|
|
|
136
131
|
deferred: string;
|
|
137
132
|
};
|
|
138
133
|
loginArgs: string[];
|
|
139
|
-
signInViaCliLogin: false;
|
|
140
|
-
canVerifyAuth: false;
|
|
141
|
-
keyHeldByCli: false;
|
|
142
|
-
cliWritesConfig: false;
|
|
143
134
|
authDeferredAtInstall: true;
|
|
144
135
|
};
|
|
145
136
|
cursor: {
|
|
@@ -148,11 +139,9 @@ export declare const HARNESS_SPECS: {
|
|
|
148
139
|
configDir: string;
|
|
149
140
|
reloadAction: string;
|
|
150
141
|
skillAgent: "cursor";
|
|
151
|
-
signInViaCliLogin: false;
|
|
152
142
|
canVerifyAuth: true;
|
|
153
143
|
keyHeldByCli: true;
|
|
154
144
|
cliWritesConfig: true;
|
|
155
|
-
authDeferredAtInstall: false;
|
|
156
145
|
};
|
|
157
146
|
grok: {
|
|
158
147
|
command: string;
|
|
@@ -175,11 +164,6 @@ export declare const HARNESS_SPECS: {
|
|
|
175
164
|
viaEnv: true;
|
|
176
165
|
};
|
|
177
166
|
signInHint: string;
|
|
178
|
-
signInViaCliLogin: false;
|
|
179
|
-
canVerifyAuth: false;
|
|
180
|
-
keyHeldByCli: false;
|
|
181
|
-
cliWritesConfig: false;
|
|
182
|
-
authDeferredAtInstall: false;
|
|
183
167
|
};
|
|
184
168
|
hermes: {
|
|
185
169
|
command: string;
|
|
@@ -201,11 +185,7 @@ export declare const HARNESS_SPECS: {
|
|
|
201
185
|
label: string;
|
|
202
186
|
};
|
|
203
187
|
postConnectNote: string;
|
|
204
|
-
signInViaCliLogin: false;
|
|
205
|
-
canVerifyAuth: false;
|
|
206
188
|
keyHeldByCli: true;
|
|
207
|
-
cliWritesConfig: false;
|
|
208
|
-
authDeferredAtInstall: false;
|
|
209
189
|
};
|
|
210
190
|
omp: {
|
|
211
191
|
command: string;
|
|
@@ -213,11 +193,9 @@ export declare const HARNESS_SPECS: {
|
|
|
213
193
|
configDir: string;
|
|
214
194
|
reloadAction: string;
|
|
215
195
|
configPathFromBinary: true;
|
|
216
|
-
signInViaCliLogin: false;
|
|
217
196
|
canVerifyAuth: true;
|
|
218
197
|
keyHeldByCli: true;
|
|
219
198
|
cliWritesConfig: true;
|
|
220
|
-
authDeferredAtInstall: false;
|
|
221
199
|
};
|
|
222
200
|
openclaw: {
|
|
223
201
|
command: string;
|
|
@@ -235,8 +213,6 @@ export declare const HARNESS_SPECS: {
|
|
|
235
213
|
signInViaCliLogin: true;
|
|
236
214
|
canVerifyAuth: true;
|
|
237
215
|
keyHeldByCli: true;
|
|
238
|
-
cliWritesConfig: false;
|
|
239
|
-
authDeferredAtInstall: false;
|
|
240
216
|
};
|
|
241
217
|
opencode: {
|
|
242
218
|
command: string;
|
|
@@ -259,11 +235,6 @@ export declare const HARNESS_SPECS: {
|
|
|
259
235
|
loginArgs: string[];
|
|
260
236
|
removals: never[];
|
|
261
237
|
postConnectNote: string;
|
|
262
|
-
signInViaCliLogin: false;
|
|
263
|
-
canVerifyAuth: false;
|
|
264
|
-
keyHeldByCli: false;
|
|
265
|
-
cliWritesConfig: false;
|
|
266
|
-
authDeferredAtInstall: false;
|
|
267
238
|
};
|
|
268
239
|
};
|
|
269
240
|
/** Derived from spec keys; one entry extends every union. */
|