@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.
@@ -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 MCP_LOGIN_UNAVAILABLE_MESSAGE = "Could not confirm this Claude Code installation exposes `claude mcp login`: `claude mcp " +
22
- "--help` did not list it. Update Claude Code and retry, or authenticate TinyFish manually " +
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*login(?:\s|\[)/m],
145
- unavailableMessage: MCP_LOGIN_UNAVAILABLE_MESSAGE,
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
- if (!client.supportCheck.patterns.every((pattern) => pattern.test(output))) {
340
- if (process.env["TINYFISH_DEBUG"]) {
341
- errLine(`${client.command} ${client.supportCheck.args.join(" ")} printed:\n${output.trim()}`);
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
- state.stage = client.loginArgs ? "registration" : "registration_or_authentication";
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 (client.loginArgs && !useKeyAuth) {
640
+ if (loginArgs && !useKeyAuth) {
619
641
  state.stage = "client_oauth";
620
642
  errLine("Signing in to TinyFish...");
621
- const loginResult = spawn.sync(client.command, client.loginArgs, {
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, useKeyAuth) {
638
- // Deferred OAuth (codex/hermes): the CLI never observes auth, so don't claim "connected".
639
- if (!client.loginArgs && !useKeyAuth) {
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, useKeyAuth) {
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, useKeyAuth));
686
+ errLine(connectedLine(client, signInDeferred));
665
687
  return;
666
688
  }
667
689
  state.stage = "walkthrough_launch";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.16.1-next.163",
3
+ "version": "0.16.1-next.165",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {