@tiny-fish/cli 0.16.1-next.163 → 0.16.1-next.165
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.js +42 -20
- package/package.json +1 -1
package/dist/commands/connect.js
CHANGED
|
@@ -18,10 +18,15 @@ const SKILL_INSTALL_TIMEOUT_MS = 120_000;
|
|
|
18
18
|
const HERMES_SEED_TIMEOUT_MS = 120_000;
|
|
19
19
|
// An old install is only one reason a flag can go unseen, so no message asserts the cause.
|
|
20
20
|
const SUPPORT_CHECK_DEBUG_HINT = " Set TINYFISH_DEBUG=1 and retry to print the help output TinyFish read.";
|
|
21
|
-
const
|
|
22
|
-
"--help` did not list it. Update Claude Code
|
|
23
|
-
"from /mcp." +
|
|
21
|
+
const MCP_ADD_UNAVAILABLE_MESSAGE = "Could not confirm this Claude Code installation exposes `claude mcp add`: `claude mcp " +
|
|
22
|
+
"--help` did not list it. Update Claude Code with `claude update` and retry." +
|
|
24
23
|
SUPPORT_CHECK_DEBUG_HINT;
|
|
24
|
+
// `claude mcp login` only exists upstream from Claude Code 2.1.186; older installs still register.
|
|
25
|
+
const CLAUDE_CODE_KEYED_FALLBACK_NOTE = "Using your stored TinyFish API key: this Claude Code predates one-command sign-in " +
|
|
26
|
+
"(`claude mcp login`), so no browser sign-in is needed.";
|
|
27
|
+
const CLAUDE_CODE_DEFERRED_AUTH_NOTE = "TinyFish is registered in Claude Code but not signed in: this Claude Code predates " +
|
|
28
|
+
"one-command sign-in (`claude mcp login`). Open Claude Code and run `/mcp` to sign in to " +
|
|
29
|
+
"TinyFish. `claude update` gets you one-command sign-in.";
|
|
25
30
|
const CODEX_OAUTH_RESOURCE_UNAVAILABLE_MESSAGE = "Could not confirm this Codex installation supports one-command MCP authentication: `codex " +
|
|
26
31
|
"mcp add --help` did not list `--oauth-resource`. Update Codex and retry, or add TinyFish " +
|
|
27
32
|
"manually with `codex mcp add`." +
|
|
@@ -141,11 +146,16 @@ const CLAUDE_CODE = {
|
|
|
141
146
|
displayName: "Claude Code",
|
|
142
147
|
supportCheck: {
|
|
143
148
|
args: ["mcp", "--help"],
|
|
144
|
-
patterns: [/^\s*
|
|
145
|
-
|
|
149
|
+
patterns: [/^\s*add(?:\s|\[)/m],
|
|
150
|
+
optionalPatterns: [/^\s*login(?:\s|\[)/m],
|
|
151
|
+
unavailableMessage: MCP_ADD_UNAVAILABLE_MESSAGE,
|
|
146
152
|
},
|
|
147
153
|
headerAuthSupported: true,
|
|
148
154
|
loginArgs: ["mcp", "login", "tinyfish"],
|
|
155
|
+
degradedAuthNotes: {
|
|
156
|
+
keyed: CLAUDE_CODE_KEYED_FALLBACK_NOTE,
|
|
157
|
+
deferred: CLAUDE_CODE_DEFERRED_AUTH_NOTE,
|
|
158
|
+
},
|
|
149
159
|
// Project scope is shared in .mcp.json; a user setup command must not rewrite it.
|
|
150
160
|
removals: [
|
|
151
161
|
{ args: ["mcp", "remove", "tinyfish", "--scope", "user"], label: "user TinyFish registration" },
|
|
@@ -336,14 +346,18 @@ function requireCommandSupport(client) {
|
|
|
336
346
|
}
|
|
337
347
|
// Belt and braces with the colour env: a client that ignores NO_COLOR still has to match.
|
|
338
348
|
const output = sanitizeLine(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
349
|
+
const listed = (patterns) => patterns.every((pattern) => pattern.test(output));
|
|
350
|
+
const essential = listed(client.supportCheck.patterns);
|
|
351
|
+
const optionalSupported = listed(client.supportCheck.optionalPatterns ?? []);
|
|
352
|
+
if ((!essential || !optionalSupported) && process.env["TINYFISH_DEBUG"]) {
|
|
353
|
+
errLine(`${client.command} ${client.supportCheck.args.join(" ")} printed:\n${output.trim()}`);
|
|
354
|
+
}
|
|
355
|
+
if (!essential) {
|
|
343
356
|
throw new PrerequisiteError(client.supportCheck.unavailableMessage, "harness_too_old", {
|
|
344
357
|
harnessVersion: probeHarnessVersion(client.command),
|
|
345
358
|
});
|
|
346
359
|
}
|
|
360
|
+
return { optionalSupported };
|
|
347
361
|
}
|
|
348
362
|
/** Best-effort; the server rejects non-printable characters and >32 chars. */
|
|
349
363
|
function probeHarnessVersion(command) {
|
|
@@ -583,7 +597,7 @@ async function connectNativeMcpClient(client, options) {
|
|
|
583
597
|
const state = { stage: "prerequisite_check", settled: false };
|
|
584
598
|
await runGuarded(state, telemetry, () => {
|
|
585
599
|
telemetry.track("started");
|
|
586
|
-
requireCommandSupport(client);
|
|
600
|
+
const { optionalSupported } = requireCommandSupport(client);
|
|
587
601
|
telemetry.track("checkpoint", { phase: "prerequisite_ok" });
|
|
588
602
|
state.stage = "registration_cleanup";
|
|
589
603
|
for (const removal of client.removals)
|
|
@@ -595,7 +609,10 @@ async function connectNativeMcpClient(client, options) {
|
|
|
595
609
|
// Prod MCP accepts X-API-Key, so a stored key replaces the browser OAuth hop.
|
|
596
610
|
const storedKey = validatedApiKey(options.apiKey);
|
|
597
611
|
const useKeyAuth = client.headerAuthSupported === true && !!storedKey;
|
|
598
|
-
|
|
612
|
+
// Undefining `loginArgs` reuses the deferred-auth path codex/hermes already take.
|
|
613
|
+
const loginDegraded = !!client.loginArgs && !optionalSupported;
|
|
614
|
+
const loginArgs = loginDegraded ? undefined : client.loginArgs;
|
|
615
|
+
state.stage = loginArgs ? "registration" : "registration_or_authentication";
|
|
599
616
|
errLine(`Adding TinyFish to ${client.displayName}...`);
|
|
600
617
|
const addResult = spawn.sync(client.command, [
|
|
601
618
|
...client.addArgs(mcpUrl.toString(), options.mcpUrl),
|
|
@@ -612,13 +629,18 @@ async function connectNativeMcpClient(client, options) {
|
|
|
612
629
|
});
|
|
613
630
|
}
|
|
614
631
|
telemetry.track("checkpoint", { phase: "registered" });
|
|
632
|
+
const degradedNotes = loginDegraded ? client.degradedAuthNotes : undefined;
|
|
615
633
|
if (useKeyAuth) {
|
|
616
|
-
errLine("Using your stored TinyFish API key — no browser sign-in needed.");
|
|
634
|
+
errLine(degradedNotes?.keyed ?? "Using your stored TinyFish API key — no browser sign-in needed.");
|
|
635
|
+
}
|
|
636
|
+
else if (degradedNotes) {
|
|
637
|
+
// Printed here, not with the closing line, so a `--launch` run sees it before the handover.
|
|
638
|
+
errLine(degradedNotes.deferred);
|
|
617
639
|
}
|
|
618
|
-
if (
|
|
640
|
+
if (loginArgs && !useKeyAuth) {
|
|
619
641
|
state.stage = "client_oauth";
|
|
620
642
|
errLine("Signing in to TinyFish...");
|
|
621
|
-
const loginResult = spawn.sync(client.command,
|
|
643
|
+
const loginResult = spawn.sync(client.command, loginArgs, {
|
|
622
644
|
stdio: "inherit",
|
|
623
645
|
});
|
|
624
646
|
if (loginResult.error || loginResult.status !== 0) {
|
|
@@ -631,19 +653,19 @@ async function connectNativeMcpClient(client, options) {
|
|
|
631
653
|
}
|
|
632
654
|
// Registration/OAuth make MCP work; later steps are cosmetic and must not fail the attempt.
|
|
633
655
|
settle(state, telemetry, "completed");
|
|
634
|
-
runPostInstallSteps(client, options, state, telemetry, useKeyAuth);
|
|
656
|
+
runPostInstallSteps(client, options, state, telemetry, !loginArgs && !useKeyAuth);
|
|
635
657
|
});
|
|
636
658
|
}
|
|
637
|
-
function connectedLine(client,
|
|
638
|
-
//
|
|
639
|
-
if (
|
|
659
|
+
function connectedLine(client, signInDeferred) {
|
|
660
|
+
// The CLI never observes deferred auth, so don't claim "connected".
|
|
661
|
+
if (signInDeferred) {
|
|
640
662
|
return (`TinyFish is configured in ${client.displayName}. ${client.displayName} will ask you ` +
|
|
641
663
|
"to sign in to TinyFish the first time you use it.");
|
|
642
664
|
}
|
|
643
665
|
return `TinyFish is connected. Open ${client.displayName} to start using it.`;
|
|
644
666
|
}
|
|
645
667
|
// Runs after `completed` settled: failures warn and report post_install_failed, exit 0.
|
|
646
|
-
function runPostInstallSteps(client, options, state, telemetry,
|
|
668
|
+
function runPostInstallSteps(client, options, state, telemetry, signInDeferred) {
|
|
647
669
|
try {
|
|
648
670
|
state.stage = "cli_install";
|
|
649
671
|
installTinyFishCli();
|
|
@@ -661,7 +683,7 @@ function runPostInstallSteps(client, options, state, telemetry, useKeyAuth) {
|
|
|
661
683
|
if (client.postConnectNote)
|
|
662
684
|
errLine(client.postConnectNote);
|
|
663
685
|
if (!options.launch) {
|
|
664
|
-
errLine(connectedLine(client,
|
|
686
|
+
errLine(connectedLine(client, signInDeferred));
|
|
665
687
|
return;
|
|
666
688
|
}
|
|
667
689
|
state.stage = "walkthrough_launch";
|