@tiny-fish/cli 0.42.1 → 0.44.0
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/README.md +11 -7
- package/dist/commands/auth.js +11 -1
- package/dist/commands/connect.js +111 -46
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/run.js +5 -2
- package/dist/lib/auth.d.ts +2 -1
- package/dist/lib/auth.js +3 -1
- package/dist/lib/client.d.ts +2 -1
- package/dist/lib/client.js +50 -9
- package/dist/lib/connect-all-auth.js +3 -0
- package/dist/lib/connect-all-uninstall.js +3 -1
- package/dist/lib/connect-all.js +10 -4
- package/dist/lib/connect-clients.d.ts +12 -5
- package/dist/lib/connect-clients.js +48 -23
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.js +2 -0
- package/dist/lib/doctor-checks.js +4 -0
- package/dist/lib/doctor-report.d.ts +2 -0
- package/dist/lib/doctor-report.js +1 -1
- package/dist/lib/harness-detect.d.ts +1 -0
- package/dist/lib/harness-detect.js +1 -0
- package/dist/lib/harness-spec.d.ts +4 -0
- package/dist/lib/harness-spec.js +3 -0
- package/dist/lib/hermes-config.d.ts +4 -0
- package/dist/lib/hermes-config.js +12 -3
- package/dist/lib/hermes-plugin.d.ts +1 -1
- package/dist/lib/hermes-plugin.js +6 -6
- package/dist/lib/mcp-json-config.d.ts +4 -2
- package/dist/lib/mcp-json-config.js +20 -10
- package/dist/lib/omp-config.js +5 -3
- package/dist/lib/output.js +7 -1
- package/dist/lib/registration-detect.js +12 -4
- package/dist/lib/setup-telemetry.d.ts +12 -0
- package/dist/lib/setup-telemetry.js +1 -0
- package/dist/lib/verify.d.ts +1 -1
- package/dist/lib/verify.js +29 -9
- package/package.json +2 -2
package/dist/lib/output.js
CHANGED
|
@@ -41,6 +41,7 @@ export function errLine(line) {
|
|
|
41
41
|
}
|
|
42
42
|
const WARN_ON = '\x1b[33m';
|
|
43
43
|
const WARN_OFF = '\x1b[39m';
|
|
44
|
+
const VAULT_RECONNECT_REQUIRED = 'VAULT_RECONNECT_REQUIRED';
|
|
44
45
|
/** Advisory stderr line, yellow on a terminal and plain everywhere else so piped output stays clean. */
|
|
45
46
|
export function warnLine(line) {
|
|
46
47
|
const text = sanitizeLine(line);
|
|
@@ -57,9 +58,14 @@ export function handleApiError(e) {
|
|
|
57
58
|
const payload = { error: e.message, status: e.status };
|
|
58
59
|
if (e.code)
|
|
59
60
|
payload.code = e.code;
|
|
60
|
-
if (e.
|
|
61
|
+
if (e.code === VAULT_RECONNECT_REQUIRED) {
|
|
62
|
+
payload.hint =
|
|
63
|
+
'Reconnect the provider with `tinyfish vault connection add --provider 1password` or `tinyfish vault connection add --provider bitwarden --client-id <client-id>`. Vault secrets are read from TINYFISH_VAULT_TOKEN, TINYFISH_VAULT_CLIENT_SECRET, and TINYFISH_VAULT_MASTER_PASSWORD.';
|
|
64
|
+
}
|
|
65
|
+
else if (e.status === 401) {
|
|
61
66
|
payload.hint =
|
|
62
67
|
'Clear the TINYFISH_API_KEY environment variable, run `tinyfish auth login`, then try again.';
|
|
68
|
+
}
|
|
63
69
|
err(payload);
|
|
64
70
|
}
|
|
65
71
|
else if (e instanceof Error) {
|
|
@@ -326,9 +326,14 @@ function probeOmp() {
|
|
|
326
326
|
: entry.keyMatchesCliKey
|
|
327
327
|
? { keyMatchesCliKey: true }
|
|
328
328
|
: {};
|
|
329
|
+
let authMode = AuthMode.Unknown;
|
|
330
|
+
if (entry.hasApiKeyHeader)
|
|
331
|
+
authMode = AuthMode.ApiKey;
|
|
332
|
+
else if (entry.keyless)
|
|
333
|
+
authMode = AuthMode.Keyless;
|
|
329
334
|
return {
|
|
330
335
|
registered: Registered.Yes,
|
|
331
|
-
authMode
|
|
336
|
+
authMode,
|
|
332
337
|
...(entry.url ? { registeredUrl: entry.url } : {}),
|
|
333
338
|
...keyVerdict,
|
|
334
339
|
};
|
|
@@ -391,16 +396,19 @@ function probeHermes() {
|
|
|
391
396
|
if (entry.state === 'absent')
|
|
392
397
|
return NOT_REGISTERED;
|
|
393
398
|
// `mcp add` saves a disabled entry on a failed connect, which connect refuses too.
|
|
394
|
-
const
|
|
399
|
+
const details = entry.state === 'enabled' ? {} : { connected: false };
|
|
400
|
+
const registeredUrl = entry.url ? { registeredUrl: entry.url } : {};
|
|
395
401
|
// The .env key is Hermes-wide; the header template ties it here.
|
|
396
402
|
const storedKey = entry.usesKeyHeader ? readHermesKey(home) : undefined;
|
|
397
403
|
if (!storedKey) {
|
|
398
|
-
|
|
404
|
+
const authMode = entry.keyless ? AuthMode.Keyless : AuthMode.OAuth;
|
|
405
|
+
return { registered: Registered.Yes, authMode, ...details, ...registeredUrl };
|
|
399
406
|
}
|
|
400
407
|
return {
|
|
401
408
|
registered: Registered.Yes,
|
|
402
409
|
authMode: AuthMode.ApiKey,
|
|
403
|
-
...
|
|
410
|
+
...details,
|
|
411
|
+
...registeredUrl,
|
|
404
412
|
...(matchesCliKey(storedKey) ? { keyMatchesCliKey: true } : {}),
|
|
405
413
|
};
|
|
406
414
|
}
|
|
@@ -163,6 +163,12 @@ declare const doctorCompletedPayloadSchema: z.ZodObject<{
|
|
|
163
163
|
detected: z.ZodBoolean;
|
|
164
164
|
registered: z.ZodEnum<typeof Registered>;
|
|
165
165
|
auth_mode: z.ZodEnum<typeof AuthMode>;
|
|
166
|
+
recorded_auth_mode: z.ZodOptional<z.ZodEnum<{
|
|
167
|
+
keyless: "keyless";
|
|
168
|
+
"api-key": "api-key";
|
|
169
|
+
oauth: "oauth";
|
|
170
|
+
deferred: "deferred";
|
|
171
|
+
}>>;
|
|
166
172
|
proves_harness_reach: z.ZodBoolean;
|
|
167
173
|
}, z.core.$strip>>;
|
|
168
174
|
duration_ms: z.ZodInt;
|
|
@@ -266,6 +272,12 @@ declare const doctorPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
266
272
|
detected: z.ZodBoolean;
|
|
267
273
|
registered: z.ZodEnum<typeof Registered>;
|
|
268
274
|
auth_mode: z.ZodEnum<typeof AuthMode>;
|
|
275
|
+
recorded_auth_mode: z.ZodOptional<z.ZodEnum<{
|
|
276
|
+
keyless: "keyless";
|
|
277
|
+
"api-key": "api-key";
|
|
278
|
+
oauth: "oauth";
|
|
279
|
+
deferred: "deferred";
|
|
280
|
+
}>>;
|
|
269
281
|
proves_harness_reach: z.ZodBoolean;
|
|
270
282
|
}, z.core.$strip>>;
|
|
271
283
|
duration_ms: z.ZodInt;
|
|
@@ -265,6 +265,7 @@ const doctorCompletedPayloadSchema = z.object({
|
|
|
265
265
|
detected: z.boolean(),
|
|
266
266
|
registered: z.enum(Registered),
|
|
267
267
|
auth_mode: z.enum(AuthMode),
|
|
268
|
+
recorded_auth_mode: z.enum(['api-key', 'keyless', 'oauth', 'deferred']).optional(),
|
|
268
269
|
proves_harness_reach: z.boolean(),
|
|
269
270
|
}))
|
|
270
271
|
.max(16),
|
package/dist/lib/verify.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ export interface VerifyResult {
|
|
|
9
9
|
status?: number;
|
|
10
10
|
}
|
|
11
11
|
/** Reachability check. Verify failure is a warning, never install failure. */
|
|
12
|
-
export declare function verifyMcpHealth(mcpUrl: string): Promise<VerifyResult>;
|
|
12
|
+
export declare function verifyMcpHealth(mcpUrl: string, keyless?: boolean): Promise<VerifyResult>;
|
|
13
13
|
/** Authenticated check — where the CLI holds the key (Cursor, OpenClaw, Hermes). */
|
|
14
14
|
export declare function verifyMcpAuth(apiKey: string, apiBaseUrl?: string): Promise<VerifyResult>;
|
package/dist/lib/verify.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { listRuns } from './client.js';
|
|
2
|
+
import { TINYFISH_ACCESS_MODE_HEADER, TINYFISH_KEYLESS_ACCESS_MODE } from './constants.js';
|
|
2
3
|
import { ApiError } from './output.js';
|
|
4
|
+
import { z } from 'zod';
|
|
3
5
|
const VERIFY_TIMEOUT_MS = 5_000;
|
|
6
|
+
const keylessInitializeSchema = z.object({
|
|
7
|
+
jsonrpc: z.literal('2.0'),
|
|
8
|
+
id: z.literal(1),
|
|
9
|
+
result: z.object({
|
|
10
|
+
capabilities: z.object({}),
|
|
11
|
+
serverInfo: z.object({ name: z.literal('tinyfish') }),
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
4
14
|
// `fetch` collapses every network fault to "fetch failed" and hides the cause on `e.cause`.
|
|
5
15
|
function networkCode(e) {
|
|
6
16
|
const cause = e?.cause;
|
|
@@ -32,24 +42,34 @@ function authCode(e) {
|
|
|
32
42
|
return 'rate limited';
|
|
33
43
|
return e.status >= 500 ? 'TinyFish returned a server error' : `the API returned HTTP ${e.status}`;
|
|
34
44
|
}
|
|
45
|
+
function failedHealth(reason) {
|
|
46
|
+
return { depth: 'health', ok: false, reason, code: reason };
|
|
47
|
+
}
|
|
35
48
|
/** Reachability check. Verify failure is a warning, never install failure. */
|
|
36
|
-
export async function verifyMcpHealth(mcpUrl) {
|
|
49
|
+
export async function verifyMcpHealth(mcpUrl, keyless = false) {
|
|
37
50
|
try {
|
|
38
51
|
const response = await fetch(mcpUrl, {
|
|
39
|
-
method: 'GET',
|
|
52
|
+
method: keyless ? 'POST' : 'GET',
|
|
53
|
+
...(keyless
|
|
54
|
+
? {
|
|
55
|
+
headers: {
|
|
56
|
+
[TINYFISH_ACCESS_MODE_HEADER]: TINYFISH_KEYLESS_ACCESS_MODE,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
Accept: 'application/json, text/event-stream',
|
|
59
|
+
},
|
|
60
|
+
body: '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}',
|
|
61
|
+
}
|
|
62
|
+
: {}),
|
|
40
63
|
// Redirect (captive portal, typo'd --url) is not healthy.
|
|
41
64
|
redirect: 'error',
|
|
42
65
|
signal: AbortSignal.timeout(VERIFY_TIMEOUT_MS),
|
|
43
66
|
});
|
|
44
67
|
// MCP rejects bare GET with 4xx — still proves endpoint routed.
|
|
45
|
-
if (response.status >= 500) {
|
|
46
|
-
return {
|
|
47
|
-
depth: 'health',
|
|
48
|
-
ok: false,
|
|
49
|
-
reason: `endpoint returned HTTP ${response.status}`,
|
|
50
|
-
code: `the endpoint returned HTTP ${response.status}`,
|
|
51
|
-
};
|
|
68
|
+
if (response.status >= 500 || (keyless && response.status !== 200)) {
|
|
69
|
+
return failedHealth(`endpoint returned HTTP ${response.status}`);
|
|
52
70
|
}
|
|
71
|
+
if (keyless && !keylessInitializeSchema.safeParse(await response.json()).success)
|
|
72
|
+
return failedHealth('endpoint returned an invalid MCP initialize response');
|
|
53
73
|
return { depth: 'health', ok: true };
|
|
54
74
|
}
|
|
55
75
|
catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiny-fish/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "TinyFish CLI — run web automations from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"format:check": "oxfmt --check ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@tiny-fish/sdk": "^0.
|
|
38
|
+
"@tiny-fish/sdk": "^0.7.0",
|
|
39
39
|
"commander": "^12.0.0",
|
|
40
40
|
"cross-spawn": "^7.0.6",
|
|
41
41
|
"tldts": "^6.1.86",
|