@tiny-fish/cli 0.31.1-next.271 → 0.32.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/dist/commands/connect.js
CHANGED
|
@@ -665,13 +665,14 @@ function parseMcpUrlOption(options) {
|
|
|
665
665
|
}
|
|
666
666
|
return { mcpUrl, attemptId };
|
|
667
667
|
}
|
|
668
|
-
async function connectAllPath(options, mcpUrl) {
|
|
668
|
+
async function connectAllPath(options, mcpUrl, attemptId) {
|
|
669
669
|
if (options.launch) {
|
|
670
670
|
throw new Error('--launch is not supported with --all. Connect a single client to launch it.');
|
|
671
671
|
}
|
|
672
672
|
const exitCode = await runConnectAll({
|
|
673
673
|
apiKey: options.apiKey,
|
|
674
674
|
mcpUrl,
|
|
675
|
+
attemptId,
|
|
675
676
|
dryRun: options.dryRun ?? false,
|
|
676
677
|
uninstall: options.uninstall ?? false,
|
|
677
678
|
noLaunch: options.skipLaunch === true,
|
|
@@ -681,7 +682,7 @@ async function connectAllPath(options, mcpUrl) {
|
|
|
681
682
|
if (!process.exitCode)
|
|
682
683
|
process.exitCode = exitCode;
|
|
683
684
|
}
|
|
684
|
-
async function connectPickPath(options, mcpUrl) {
|
|
685
|
+
async function connectPickPath(options, mcpUrl, attemptId) {
|
|
685
686
|
if (options.launch) {
|
|
686
687
|
throw new Error('--launch is not supported with --all. Connect a single client to launch it.');
|
|
687
688
|
}
|
|
@@ -692,6 +693,7 @@ async function connectPickPath(options, mcpUrl) {
|
|
|
692
693
|
const exitCode = await runConnectAll({
|
|
693
694
|
apiKey: options.apiKey,
|
|
694
695
|
mcpUrl,
|
|
696
|
+
attemptId,
|
|
695
697
|
dryRun: false,
|
|
696
698
|
uninstall: false,
|
|
697
699
|
pick: humanInitiated,
|
|
@@ -743,14 +745,14 @@ export function registerConnect(program) {
|
|
|
743
745
|
if (client) {
|
|
744
746
|
throw new Error('Pass either a client or --all, not both.');
|
|
745
747
|
}
|
|
746
|
-
await connectAllPath(options, mcpUrl);
|
|
748
|
+
await connectAllPath(options, mcpUrl, attemptId);
|
|
747
749
|
return;
|
|
748
750
|
}
|
|
749
751
|
if (options.dryRun || options.uninstall || options.skipLaunch) {
|
|
750
752
|
throw new Error('--dry-run, --uninstall and --skip-launch are only supported with --all.');
|
|
751
753
|
}
|
|
752
754
|
if (!client) {
|
|
753
|
-
await connectPickPath(options, mcpUrl);
|
|
755
|
+
await connectPickPath(options, mcpUrl, attemptId);
|
|
754
756
|
return;
|
|
755
757
|
}
|
|
756
758
|
await connectSingleClient(client, options, mcpUrl, attemptId);
|
|
@@ -31,6 +31,8 @@ export interface ConnectAllOptions {
|
|
|
31
31
|
pick?: boolean;
|
|
32
32
|
/** Inherit subprocess stdio instead of replaying it only on failure. */
|
|
33
33
|
verbose?: boolean;
|
|
34
|
+
/** Setup-page id riding in on --url; joins "copied the command" to this run. */
|
|
35
|
+
attemptId?: string;
|
|
34
36
|
}
|
|
35
37
|
export declare function runConnectAll(opts: ConnectAllOptions): Promise<number>;
|
|
36
38
|
export declare const FIRST_TASK_PROMPT: string;
|
package/dist/lib/connect-all.js
CHANGED
|
@@ -14,7 +14,7 @@ import { commandOnPath, detectInstalledHarnesses, HARNESS_COMMANDS, HARNESS_DISP
|
|
|
14
14
|
import { detectHumanInitiated } from './harness.js';
|
|
15
15
|
import { emitNotice } from './notice.js';
|
|
16
16
|
import { errLine, outLine, setErrIndent } from './output.js';
|
|
17
|
-
import { sendSetupCompleted } from './setup-telemetry.js';
|
|
17
|
+
import { sendSetupCompleted, sendSetupStarted, } from './setup-telemetry.js';
|
|
18
18
|
import { verifyMcpAuth, verifyMcpHealth } from './verify.js';
|
|
19
19
|
const STEP_INDENT = ' ';
|
|
20
20
|
// Derived: a native add carries the key, or the CLI holds it itself.
|
|
@@ -315,6 +315,12 @@ async function finishRun(results, opts, isTTY) {
|
|
|
315
315
|
}
|
|
316
316
|
export async function runConnectAll(opts) {
|
|
317
317
|
const isTTY = detectHumanInitiated();
|
|
318
|
+
// Ahead of detection and the hoisted install; either can end the process silently.
|
|
319
|
+
// Not on the picker path: an explicit cancel is not a setup.
|
|
320
|
+
if (!opts.uninstall && !opts.dryRun && !opts.pick) {
|
|
321
|
+
// validatedApiKey falls back to env/config; opts.apiKey alone splits the run's two ends.
|
|
322
|
+
await sendSetupStarted(opts.mcpUrl, opts.attemptId, validatedApiKey(opts.apiKey));
|
|
323
|
+
}
|
|
318
324
|
// Open interface holds stdin raw; hermes' input() then wedges.
|
|
319
325
|
const prompt = isTTY ? createStdinPrompt() : undefined;
|
|
320
326
|
const detections = detectInstalledHarnesses();
|
|
@@ -372,7 +378,7 @@ export async function runConnectAll(opts) {
|
|
|
372
378
|
verify_depth: r.verifyDepth ?? null,
|
|
373
379
|
verify_ok: r.verifyOk ?? null,
|
|
374
380
|
}));
|
|
375
|
-
await sendSetupCompleted(opts.mcpUrl, harnessTelemetry, validatedApiKey(opts.apiKey), mode);
|
|
381
|
+
await sendSetupCompleted(opts.mcpUrl, harnessTelemetry, validatedApiKey(opts.apiKey), mode, opts.attemptId);
|
|
376
382
|
// After the summary and before the launch, which is where the user is reading.
|
|
377
383
|
emitNotice();
|
|
378
384
|
}
|
|
@@ -40,6 +40,8 @@ declare const harnessResultSchema: z.ZodObject<{
|
|
|
40
40
|
}>>;
|
|
41
41
|
verify_ok: z.ZodNullable<z.ZodBoolean>;
|
|
42
42
|
}, z.core.$strip>;
|
|
43
|
+
/** First act of a `--all` run; everything upstream of it stays invisible. */
|
|
44
|
+
export declare function sendSetupStarted(mcpUrl: string, attemptId?: string, apiKey?: string): Promise<void>;
|
|
43
45
|
export declare const setupCompletedPayloadSchema: z.ZodObject<{
|
|
44
46
|
harnesses: z.ZodArray<z.ZodObject<{
|
|
45
47
|
harness: z.ZodEnum<{
|
|
@@ -65,6 +67,7 @@ export declare const setupCompletedPayloadSchema: z.ZodObject<{
|
|
|
65
67
|
pick: "pick";
|
|
66
68
|
pick_numeric: "pick_numeric";
|
|
67
69
|
}>>;
|
|
70
|
+
connect_attempt_id: z.ZodOptional<z.ZodUUID>;
|
|
68
71
|
}, z.core.$strip>;
|
|
69
72
|
export type SetupCompletedPayload = z.infer<typeof setupCompletedPayloadSchema>;
|
|
70
73
|
export type HarnessResult = z.infer<typeof harnessResultSchema>;
|
|
@@ -74,7 +77,7 @@ export type UpgradeOutcome = 'updated' | 'failed' | 'interrupted';
|
|
|
74
77
|
export type UpgradeScope = 'both' | 'cli' | 'skill';
|
|
75
78
|
/** `toVersion` is null when the run was interrupted or the installed version could not be read. */
|
|
76
79
|
export declare function sendUpgradeCompleted(fromVersion: string, toVersion: string | null, outcome: UpgradeOutcome, scope: UpgradeScope, apiKey?: string): Promise<void>;
|
|
77
|
-
export declare function sendSetupCompleted(mcpUrl: string, harnesses: HarnessResult[], apiKey?: string, mode?: SetupMode): Promise<void>;
|
|
80
|
+
export declare function sendSetupCompleted(mcpUrl: string, harnesses: HarnessResult[], apiKey?: string, mode?: SetupMode, attemptId?: string): Promise<void>;
|
|
78
81
|
export declare const DOCTOR_HARNESS_SCOPES: readonly ["all", "claude-code", "codex", "cursor", "grok", "hermes", "openclaw", "opencode"];
|
|
79
82
|
export declare const doctorCompletedPayloadSchema: z.ZodObject<{
|
|
80
83
|
schema_version: z.ZodInt;
|
|
@@ -101,6 +101,25 @@ const harnessResultSchema = z.object({
|
|
|
101
101
|
verify_depth: z.enum(['health', 'health+auth']).nullable(),
|
|
102
102
|
verify_ok: z.boolean().nullable(),
|
|
103
103
|
});
|
|
104
|
+
/** First act of a `--all` run; everything upstream of it stays invisible. */
|
|
105
|
+
export async function sendSetupStarted(mcpUrl, attemptId, apiKey) {
|
|
106
|
+
if (telemetryDisabled())
|
|
107
|
+
return;
|
|
108
|
+
// Route 400s a bad id and postConnectEvent never retries 4xx, losing the whole event.
|
|
109
|
+
const stamped = attemptId && z.uuid().safeParse(attemptId).success ? attemptId : undefined;
|
|
110
|
+
await postConnectEvent({
|
|
111
|
+
endpoint: new URL('/api/cli/connect-event', mcpUrl).toString(),
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
event: 'setup_started',
|
|
114
|
+
cli_version: CLI_VERSION,
|
|
115
|
+
runtime_platform: process.platform,
|
|
116
|
+
...(stamped ? { connect_attempt_id: stamped } : {}),
|
|
117
|
+
}),
|
|
118
|
+
attempts: 1,
|
|
119
|
+
label: 'setup',
|
|
120
|
+
apiKey,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
104
123
|
export const setupCompletedPayloadSchema = z.object({
|
|
105
124
|
harnesses: z
|
|
106
125
|
.array(harnessResultSchema)
|
|
@@ -111,6 +130,7 @@ export const setupCompletedPayloadSchema = z.object({
|
|
|
111
130
|
cli_version: z.string(),
|
|
112
131
|
// Segments picker runs from --all runs; the server field shipped first.
|
|
113
132
|
mode: z.enum(['all', 'pick', 'pick_numeric']).optional(),
|
|
133
|
+
connect_attempt_id: z.uuid().optional(),
|
|
114
134
|
});
|
|
115
135
|
/** `toVersion` is null when the run was interrupted or the installed version could not be read. */
|
|
116
136
|
export async function sendUpgradeCompleted(fromVersion, toVersion, outcome, scope, apiKey) {
|
|
@@ -128,7 +148,7 @@ export async function sendUpgradeCompleted(fromVersion, toVersion, outcome, scop
|
|
|
128
148
|
apiKey,
|
|
129
149
|
});
|
|
130
150
|
}
|
|
131
|
-
export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode) {
|
|
151
|
+
export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode, attemptId) {
|
|
132
152
|
// Ahead of the parse so an opt-out run prints no telemetry debug.
|
|
133
153
|
if (telemetryDisabled())
|
|
134
154
|
return;
|
|
@@ -140,6 +160,10 @@ export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode) {
|
|
|
140
160
|
harnesses,
|
|
141
161
|
cli_version: CLI_VERSION,
|
|
142
162
|
...(mode ? { mode } : {}),
|
|
163
|
+
// Guarded here, not only at the route: a parse throw drops the whole event, not one field.
|
|
164
|
+
...(attemptId && z.uuid().safeParse(attemptId).success
|
|
165
|
+
? { connect_attempt_id: attemptId }
|
|
166
|
+
: {}),
|
|
143
167
|
});
|
|
144
168
|
endpoint = new URL('/api/cli/connect-event', mcpUrl).toString();
|
|
145
169
|
body = JSON.stringify({ event: 'setup_completed', ...payload });
|