@tiny-fish/cli 0.19.1-next.179 → 0.19.1-next.181

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.
@@ -35,6 +35,7 @@ async function connectNativeMcpClient(client, options) {
35
35
  await runGuarded(state, telemetry, () => {
36
36
  telemetry.track("started");
37
37
  const { optionalSupported } = requireCommandSupport(client);
38
+ state.harnessDegraded = !!client.loginArgs && !optionalSupported;
38
39
  telemetry.track("checkpoint", { phase: "prerequisite_ok" });
39
40
  state.stage = "registration_cleanup";
40
41
  for (const removal of client.removals)
@@ -47,7 +48,7 @@ async function connectNativeMcpClient(client, options) {
47
48
  const storedKey = validatedApiKey(options.apiKey);
48
49
  const useKeyAuth = client.headerAuthSupported === true && !!storedKey;
49
50
  // Undefining `loginArgs` reuses the deferred-auth path codex/hermes already take.
50
- const loginDegraded = !!client.loginArgs && !optionalSupported;
51
+ const loginDegraded = state.harnessDegraded === true;
51
52
  const loginArgs = loginDegraded ? undefined : client.loginArgs;
52
53
  // A probed client authenticates inside `mcp add` too, so a failure there is either.
53
54
  state.stage =
@@ -98,9 +99,15 @@ async function connectNativeMcpClient(client, options) {
98
99
  }
99
100
  telemetry.track("checkpoint", { phase: "oauth_done" });
100
101
  }
102
+ const signInDeferred = !useKeyAuth && !addSignedIn && !pendingLogin;
103
+ const authMode = useKeyAuth
104
+ ? "api-key"
105
+ : signInDeferred
106
+ ? "deferred"
107
+ : "oauth";
101
108
  // Registration/OAuth make MCP work; later steps are cosmetic and must not fail the attempt.
102
- settle(state, telemetry, "completed");
103
- runPostInstallSteps(client, options, state, telemetry, !useKeyAuth && !addSignedIn && !pendingLogin);
109
+ settle(state, telemetry, "completed", { authMode });
110
+ runPostInstallSteps(client, options, state, telemetry, signInDeferred);
104
111
  });
105
112
  }
106
113
  function connectedLine(client, signInDeferred) {
@@ -163,7 +170,12 @@ export async function connectOpencode(options) {
163
170
  }
164
171
  export async function connectOpenClaw(options) {
165
172
  const telemetry = createConnectTelemetry(options.mcpUrl, "openclaw", options);
166
- const state = { stage: "prerequisite_check", settled: false };
173
+ // Skill install, no MCP server — never degraded, and false keeps it inside a `= false` filter.
174
+ const state = {
175
+ stage: "prerequisite_check",
176
+ settled: false,
177
+ harnessDegraded: false,
178
+ };
167
179
  await runGuarded(state, telemetry, () => {
168
180
  telemetry.track("started");
169
181
  requireCommandSupport(OPENCLAW);
@@ -230,7 +242,13 @@ export async function connectOpenClaw(options) {
230
242
  /** Writes mcp.json directly — no `cursor mcp add` exists. */
231
243
  export async function connectCursor(options) {
232
244
  const telemetry = createConnectTelemetry(options.mcpUrl, "cursor", options);
233
- const state = { stage: "prerequisite_check", settled: false };
245
+ // Never degraded: no sign-in command to be missing. False, not absent, so a `= false`
246
+ // filter still catches Cursor.
247
+ const state = {
248
+ stage: "prerequisite_check",
249
+ settled: false,
250
+ harnessDegraded: false,
251
+ };
234
252
  await runGuarded(state, telemetry, async () => {
235
253
  telemetry.track("started");
236
254
  telemetry.track("checkpoint", { phase: "prerequisite_ok" });
@@ -279,7 +297,7 @@ export async function connectCursor(options) {
279
297
  else {
280
298
  errLine("TinyFish is connected. Reload the Cursor window, then approve/sign in under Settings → MCP.");
281
299
  }
282
- settle(state, telemetry, "completed");
300
+ settle(state, telemetry, "completed", { authMode: resolvedKey ? "api-key" : "deferred" });
283
301
  });
284
302
  }
285
303
  /** Best-effort: false when no handler/open fails — caller falls back to reload copy. */
@@ -13,7 +13,8 @@ const CLAUDE_CODE_KEYED_FALLBACK_NOTE = "Using your stored TinyFish API key: thi
13
13
  "(`claude mcp login`), so no browser sign-in is needed.";
14
14
  const CLAUDE_CODE_DEFERRED_AUTH_NOTE = "TinyFish is registered in Claude Code but not signed in: this Claude Code predates " +
15
15
  "one-command sign-in (`claude mcp login`). Open Claude Code and run `/mcp` to sign in to " +
16
- "TinyFish. `claude update` gets you one-command sign-in.";
16
+ "TinyFish. `claude update` gets you one-command sign-in. Already have a TinyFish API key? " +
17
+ "Run `tinyfish connect claude-code --api-key <key>` instead.";
17
18
  const CODEX_OAUTH_RESOURCE_UNAVAILABLE_MESSAGE = "Could not confirm this Codex installation supports one-command MCP authentication: `codex " +
18
19
  "mcp add --help` did not list `--oauth-resource`. Update Codex and retry, or add TinyFish " +
19
20
  "manually with `codex mcp add`." +
@@ -1,8 +1,10 @@
1
+ import { type AuthMode } from "./registration-detect.js";
1
2
  export declare const NON_INTERACTIVE_TIMEOUT_MS = 10000;
2
3
  export type AgentClient = "claude-code" | "codex" | "cursor" | "hermes" | "openclaw" | "opencode";
3
4
  type ConnectStage = "started" | "checkpoint" | "completed" | "failed" | "aborted" | "post_install_failed";
4
5
  type ConnectFailureStage = "prerequisite_check" | "registration_cleanup" | "registration" | "registration_or_authentication" | "client_oauth" | "cli_install" | "skill_install" | "authentication" | "walkthrough_launch";
5
6
  type ConnectFailureReason = "harness_not_installed" | "harness_command_unsupported" | "harness_too_old";
7
+ export type ConnectAuthMode = Extract<AuthMode, "oauth" | "api-key"> | "deferred";
6
8
  type ConnectCheckpoint = "prerequisite_ok" | "registered" | "oauth_done" | "cli_installed" | "skill_installed" | "authenticated";
7
9
  /** Ctrl+C/SIGTERM killed a setup child — abandonment, not error. */
8
10
  export declare class ConnectInterruptedError extends Error {
@@ -15,12 +17,17 @@ export declare function commandNotFound(error: unknown): boolean;
15
17
  export interface ConnectRunState {
16
18
  stage: ConnectFailureStage;
17
19
  settled: boolean;
20
+ /** On state, not per-call: otherwise degraded installs never appear to fail. */
21
+ harnessDegraded?: boolean;
18
22
  }
19
23
  interface ConnectStageDetail {
20
24
  failedStage?: ConnectFailureStage;
21
25
  phase?: ConnectCheckpoint;
22
26
  failureReason?: ConnectFailureReason;
23
27
  harnessVersion?: string;
28
+ authMode?: ConnectAuthMode;
29
+ /** Orthogonal to `authMode`: a stored key wins over `mcp login` on a healthy client too. */
30
+ harnessDegraded?: boolean;
24
31
  }
25
32
  export interface ConnectTelemetry {
26
33
  attemptId: string;
@@ -36,7 +36,7 @@ export function settle(state, telemetry, stage, detail) {
36
36
  if (state.settled)
37
37
  return;
38
38
  state.settled = true;
39
- telemetry.track(stage, detail);
39
+ telemetry.track(stage, { harnessDegraded: state.harnessDegraded, ...detail });
40
40
  }
41
41
  // Raw-mode children still surface via throwIfInterrupted; cooked-mode stages
42
42
  // (npm/npx/OAuth waits) land here.
@@ -165,6 +165,8 @@ export function createConnectTelemetry(mcpUrl, client, opts) {
165
165
  phase: detail?.phase,
166
166
  failure_reason: detail?.failureReason,
167
167
  harness_version: detail?.harnessVersion,
168
+ auth_mode: detail?.authMode,
169
+ harness_degraded: detail?.harnessDegraded,
168
170
  runtime_platform: process.platform,
169
171
  node_version: process.version,
170
172
  cli_version: CLI_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.19.1-next.179",
3
+ "version": "0.19.1-next.181",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {