@tiny-fish/cli 0.26.1-next.223 → 0.26.1-next.226
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
CHANGED
|
@@ -5,7 +5,7 @@ import { HoistedFailureReportedError, installTinyFishCli, SKILL_INSTALL_TIMEOUT_
|
|
|
5
5
|
import { ensureCliAuthenticated } from "../lib/connect-auth.js";
|
|
6
6
|
import { installWebSkill } from "../lib/skill-install.js";
|
|
7
7
|
import { ConnectInterruptedError, ConnectStepError, createConnectTelemetry, finishSetupCommand, requireCommandSupport, runGuarded, settle, spawnStepError, throwIfInterrupted, NON_INTERACTIVE_TIMEOUT_MS, } from "../lib/connect-runtime.js";
|
|
8
|
-
import { cursorInstallDeeplink, cursorMcpPath, writeCursorMcpConfig, } from "../lib/cursor-config.js";
|
|
8
|
+
import { cursorInstallDeeplink, cursorMcpPath, readCursorTinyfishEntry, writeCursorMcpConfig, } from "../lib/cursor-config.js";
|
|
9
9
|
import { runConnectAll } from "../lib/connect-all.js";
|
|
10
10
|
import { AuthMode } from "../lib/harness-detect.js";
|
|
11
11
|
import { detectHumanInitiated } from "../lib/harness.js";
|
|
@@ -67,8 +67,8 @@ async function connectNativeMcpClient(client, options) {
|
|
|
67
67
|
throw new ConnectStepError(`This ${client.displayName} install cannot take a TinyFish API key, and signing in ` +
|
|
68
68
|
`needs a terminal. Update it, then re-run: tinyfish connect ${client.connectClient}`, "harness_too_old");
|
|
69
69
|
}
|
|
70
|
-
throw new
|
|
71
|
-
`terminal. Run: tinyfish connect ${client.connectClient}
|
|
70
|
+
throw new ConnectStepError(`${client.displayName} cannot take a TinyFish API key, and signing in needs a ` +
|
|
71
|
+
`terminal. Run: tinyfish connect ${client.connectClient}`, "key_auth_unsupported");
|
|
72
72
|
}
|
|
73
73
|
telemetry.track("checkpoint", { phase: "prerequisite_ok" });
|
|
74
74
|
state.stage = "registration_cleanup";
|
|
@@ -319,7 +319,36 @@ export async function connectOpenClaw(options) {
|
|
|
319
319
|
}
|
|
320
320
|
/** Writes mcp.json directly — no `cursor mcp add` exists. */
|
|
321
321
|
export async function connectCursor(options) {
|
|
322
|
-
|
|
322
|
+
let fallbackAttemptId;
|
|
323
|
+
const registeredUrl = readCursorTinyfishEntry().url;
|
|
324
|
+
if (registeredUrl) {
|
|
325
|
+
try {
|
|
326
|
+
const registered = new URL(registeredUrl);
|
|
327
|
+
const requested = new URL(options.mcpUrl);
|
|
328
|
+
const registeredId = registered.searchParams.get("connect_attempt_id");
|
|
329
|
+
const registeredSource = registered.searchParams.get("source");
|
|
330
|
+
const registeredClient = registered.searchParams.get("client");
|
|
331
|
+
for (const key of ["source", "client", "connect_attempt_id"]) {
|
|
332
|
+
registered.searchParams.delete(key);
|
|
333
|
+
requested.searchParams.delete(key);
|
|
334
|
+
}
|
|
335
|
+
registered.searchParams.sort();
|
|
336
|
+
requested.searchParams.sort();
|
|
337
|
+
if (registered.toString() === requested.toString() &&
|
|
338
|
+
registeredSource === CONNECT_SOURCE &&
|
|
339
|
+
registeredClient === "cursor" &&
|
|
340
|
+
z.uuid().safeParse(registeredId).success) {
|
|
341
|
+
fallbackAttemptId = registeredId ?? undefined;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// A hand-edited invalid URL is repaired below with a fresh attempt id.
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const telemetry = createConnectTelemetry(options.mcpUrl, "cursor", {
|
|
349
|
+
...options,
|
|
350
|
+
fallbackAttemptId,
|
|
351
|
+
});
|
|
323
352
|
// Never degraded: no sign-in command to be missing. False, not absent, so a `= false`
|
|
324
353
|
// filter still catches Cursor.
|
|
325
354
|
const state = {
|
|
@@ -347,7 +376,7 @@ export async function connectCursor(options) {
|
|
|
347
376
|
const mcpUrl = new URL(options.mcpUrl);
|
|
348
377
|
mcpUrl.searchParams.set("source", CONNECT_SOURCE);
|
|
349
378
|
mcpUrl.searchParams.set("client", "cursor");
|
|
350
|
-
|
|
379
|
+
mcpUrl.searchParams.set("connect_attempt_id", telemetry.attemptId);
|
|
351
380
|
const result = writeCursorMcpConfig(mcpUrl.toString(), resolvedKey);
|
|
352
381
|
if (result.status === "corrupt_skip") {
|
|
353
382
|
throw new ConnectStepError(`Could not update ${cursorMcpPath()}: existing file could not be read or is not valid JSON (${result.error}). ` +
|
|
@@ -3,7 +3,7 @@ export declare const NON_INTERACTIVE_TIMEOUT_MS = 10000;
|
|
|
3
3
|
export type AgentClient = "claude-code" | "codex" | "cursor" | "grok" | "hermes" | "openclaw" | "opencode";
|
|
4
4
|
type ConnectStage = "started" | "checkpoint" | "completed" | "failed" | "aborted" | "post_install_failed";
|
|
5
5
|
type ConnectFailureStage = "prerequisite_check" | "registration_cleanup" | "registration" | "registration_or_authentication" | "client_oauth" | "cli_install" | "skill_install" | "authentication" | "walkthrough_launch";
|
|
6
|
-
export declare const CONNECT_FAILURE_REASONS: readonly ["harness_not_installed", "harness_command_unsupported", "harness_too_old", "command_not_found", "timeout", "spawn_error", "nonzero_exit", "invalid_config", "user_abandoned", "process_terminated", "unexpected_error"];
|
|
6
|
+
export declare const CONNECT_FAILURE_REASONS: readonly ["harness_not_installed", "harness_command_unsupported", "harness_too_old", "key_auth_unsupported", "command_not_found", "timeout", "spawn_error", "nonzero_exit", "invalid_config", "user_abandoned", "process_terminated", "unexpected_error"];
|
|
7
7
|
export type ConnectFailureReason = (typeof CONNECT_FAILURE_REASONS)[number];
|
|
8
8
|
export type ConnectAuthMode = Exclude<AuthMode, AuthMode.Unknown> | "deferred";
|
|
9
9
|
type ConnectCheckpoint = "prerequisite_ok" | "registered" | "oauth_done" | "cli_installed" | "skill_installed" | "authenticated";
|
|
@@ -84,5 +84,6 @@ export declare function requireCommandSupport(client: SupportedCommand): {
|
|
|
84
84
|
export declare function createConnectTelemetry(mcpUrl: string, client: AgentClient, opts?: {
|
|
85
85
|
apiKey?: string;
|
|
86
86
|
attemptId?: string;
|
|
87
|
+
fallbackAttemptId?: string;
|
|
87
88
|
}): ConnectTelemetry;
|
|
88
89
|
export {};
|
|
@@ -12,6 +12,7 @@ export const CONNECT_FAILURE_REASONS = [
|
|
|
12
12
|
"harness_not_installed",
|
|
13
13
|
"harness_command_unsupported",
|
|
14
14
|
"harness_too_old",
|
|
15
|
+
"key_auth_unsupported",
|
|
15
16
|
"command_not_found",
|
|
16
17
|
"timeout",
|
|
17
18
|
"spawn_error",
|
|
@@ -192,7 +193,7 @@ export function createConnectTelemetry(mcpUrl, client, opts) {
|
|
|
192
193
|
const seeded = takeSeedAttemptId();
|
|
193
194
|
// A setup-page id (via --url, the env var, or one parked by login) joins "copied the
|
|
194
195
|
// command" to this install attempt.
|
|
195
|
-
const attemptId = opts?.attemptId ?? seeded ?? randomUUID();
|
|
196
|
+
const attemptId = opts?.attemptId ?? seeded ?? opts?.fallbackAttemptId ?? randomUUID();
|
|
196
197
|
const endpoint = new URL("/api/cli/connect-event", mcpUrl).toString();
|
|
197
198
|
const pending = [];
|
|
198
199
|
let lastTrackAt;
|