@tiny-fish/cli 0.52.5-next.437 → 0.52.5-next.438

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.
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
+ import { Option } from 'commander';
3
4
  import { saveConnectConfigDir, validateKeyFormat, validatedApiKey } from '../lib/auth.js';
4
5
  import { runConnectAll } from '../lib/connect-all.js';
5
6
  import { ALL_HARNESSES } from '../lib/harness/harness-detect.js';
@@ -10,6 +11,8 @@ import { z } from 'zod';
10
11
  import { gateApiKey } from '../lib/connect-preflight.js';
11
12
  import { DEFAULT_MCP_URL } from '../lib/mcp-endpoint.js';
12
13
  import { connectHarness } from '../lib/connect-harness.js';
14
+ // The route 400s anything else, losing the event; drop it instead.
15
+ const TRIGGER_FORMAT = /^[a-z0-9-]{1,32}$/;
13
16
  /** Cline reads CLINE_DIR itself, so setting it steers the add, the remove and our own probe. */
14
17
  function applyConfigDir(client, configDir) {
15
18
  if (client !== 'cline') {
@@ -60,6 +63,7 @@ async function connectManyPath(options, mcpUrl, attemptId) {
60
63
  apiKey: options.apiKey,
61
64
  mcpUrl,
62
65
  attemptId,
66
+ trigger: options.trigger,
63
67
  dryRun: options.dryRun ?? false,
64
68
  uninstall: options.uninstall ?? false,
65
69
  noLaunch: options.skipLaunch === true,
@@ -80,6 +84,7 @@ async function connectSingleClient(client, options, mcpUrl, attemptId) {
80
84
  launch: options.launch ?? false,
81
85
  configDir: options.configDir ? applyConfigDir(client, options.configDir) : undefined,
82
86
  attemptId,
87
+ trigger: options.trigger,
83
88
  // A key that the install cannot use leaves only a browser hop no agent can finish.
84
89
  keyAuthOnly: !detectHumanInitiated() && !!validatedApiKey(options.apiKey),
85
90
  authTimeoutMs: detectHumanInitiated() ? undefined : 90_000,
@@ -117,10 +122,13 @@ export function registerConnect(program) {
117
122
  .option('--url <mcpUrl>', 'MCP endpoint override')
118
123
  .option('--verbose', 'Print the full npm and skills output instead of only on failure')
119
124
  .option('--config-dir <path>', 'Cline only: config dir of a relocated install (~/.cline)')
125
+ .addOption(new Option('--trigger <trigger>', 'What prompted this connect, for attribution').hideHelp())
120
126
  .action(async (client, options) => {
121
127
  if (options.apiKey && !validateKeyFormat(options.apiKey)) {
122
128
  throw new Error('Invalid --api-key value');
123
129
  }
130
+ if (options.trigger && !TRIGGER_FORMAT.test(options.trigger))
131
+ options.trigger = undefined;
124
132
  const { mcpUrl, attemptId } = parseMcpUrlOption(options);
125
133
  if (options.all) {
126
134
  if (client) {
@@ -81,6 +81,7 @@ export declare function connectConfigFileHarness(harness: keyof typeof CONFIG_FI
81
81
  mcpUrl: string;
82
82
  launch?: boolean;
83
83
  attemptId?: string;
84
+ trigger?: string;
84
85
  verbose?: boolean;
85
86
  authTimeoutMs?: number;
86
87
  onPostInstallFailed?: () => void;
@@ -33,6 +33,8 @@ export interface ConnectAllOptions {
33
33
  verbose?: boolean;
34
34
  /** Setup-page id riding in on --url; joins "copied the command" to this run. */
35
35
  attemptId?: string;
36
+ /** What prompted the connect (`--trigger`), stamped on every event. */
37
+ trigger?: string;
36
38
  }
37
39
  /** The harness has never created its dir, so connecting would build one it does not own. */
38
40
  export declare function needsFirstRun(detection: HarnessDetection): boolean;
@@ -94,6 +94,7 @@ async function processHarness(detection, opts, prompt, oauthBudget, hoistedCliIn
94
94
  mcpUrl: opts.mcpUrl,
95
95
  launch: false,
96
96
  verbose: opts.verbose,
97
+ trigger: opts.trigger,
97
98
  keyAuthOnly: keyed && !isTTY,
98
99
  authTimeoutMs: headlessAuth ? OAUTH_SIGN_IN_TIMEOUT_MS : undefined,
99
100
  hoistedCliInstall,
@@ -379,7 +380,7 @@ export async function runConnectAll(opts) {
379
380
  verify_depth: r.verifyDepth ?? null,
380
381
  verify_ok: r.verifyOk ?? null,
381
382
  }));
382
- await sendSetupCompleted(opts.mcpUrl, harnessTelemetry, validatedApiKey(opts.apiKey), mode, opts.attemptId);
383
+ await sendSetupCompleted(opts.mcpUrl, harnessTelemetry, validatedApiKey(opts.apiKey), mode, opts.attemptId, opts.trigger);
383
384
  // After the summary and before the launch, which is where the user is reading.
384
385
  emitNotice();
385
386
  }
@@ -108,5 +108,6 @@ export declare function createConnectTelemetry(mcpUrl: string, client: AgentClie
108
108
  apiKey?: string;
109
109
  attemptId?: string;
110
110
  fallbackAttemptId?: string;
111
+ trigger?: string;
111
112
  }): ConnectTelemetry;
112
113
  export {};
@@ -334,6 +334,7 @@ export function createConnectTelemetry(mcpUrl, client, opts) {
334
334
  // command" to this install attempt.
335
335
  const attemptId = opts?.attemptId ?? seeded ?? opts?.fallbackAttemptId ?? randomUUID();
336
336
  const endpoint = new URL('/api/cli/connect-event', mcpUrl).toString();
337
+ const trigger = opts?.trigger;
337
338
  const pending = [];
338
339
  let lastTrackAt;
339
340
  let harnessVersion;
@@ -378,6 +379,7 @@ export function createConnectTelemetry(mcpUrl, client, opts) {
378
379
  calling_harness: detectHarness() ?? 'none',
379
380
  cli_invocation_id: CLI_INVOCATION_ID,
380
381
  runtime_env: detectRuntimeEnv(),
382
+ connect_trigger: trigger,
381
383
  });
382
384
  }
383
385
  return {
@@ -6,6 +6,8 @@ export interface NativeConnectOptions {
6
6
  mcpUrl: string;
7
7
  launch: boolean;
8
8
  attemptId?: string;
9
+ /** What prompted the connect (`--trigger`), stamped on every event. */
10
+ trigger?: string;
9
11
  /** Headless: refuse rather than fall back to an unfinishable browser sign-in. */
10
12
  keyAuthOnly?: boolean;
11
13
  /** Headless --all: bounds sign-in spawns and bars interactive CLI login. */
@@ -97,7 +97,7 @@ export type UpgradeOutcome = 'updated' | 'failed' | 'interrupted';
97
97
  export type UpgradeScope = 'both' | 'cli' | 'skill';
98
98
  /** `toVersion` is null when the run was interrupted or the installed version could not be read. */
99
99
  export declare function sendUpgradeCompleted(fromVersion: string, toVersion: string | null, outcome: UpgradeOutcome, scope: UpgradeScope, apiKey?: string): Promise<void>;
100
- export declare function sendSetupCompleted(mcpUrl: string, harnesses: HarnessResult[], apiKey?: string, mode?: SetupMode, attemptId?: string): Promise<void>;
100
+ export declare function sendSetupCompleted(mcpUrl: string, harnesses: HarnessResult[], apiKey?: string, mode?: SetupMode, attemptId?: string, trigger?: string): Promise<void>;
101
101
  export declare const DOCTOR_ERROR_CLASSES: readonly ["command_not_found", "timeout", "spawn_error", "nonzero_exit", "unexpected_error"];
102
102
  export type DoctorErrorClass = (typeof DOCTOR_ERROR_CLASSES)[number];
103
103
  /** Derived, not read: the catch is generic, so only the code is trustworthy. */
@@ -183,7 +183,7 @@ export async function sendUpgradeCompleted(fromVersion, toVersion, outcome, scop
183
183
  apiKey,
184
184
  });
185
185
  }
186
- export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode, attemptId) {
186
+ export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode, attemptId, trigger) {
187
187
  // Ahead of the parse so an opt-out run prints no telemetry debug.
188
188
  if (telemetryDisabled())
189
189
  return;
@@ -204,6 +204,7 @@ export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode, attemp
204
204
  ...payload,
205
205
  cli_invocation_id: CLI_INVOCATION_ID,
206
206
  runtime_env: detectRuntimeEnv(),
207
+ ...(trigger ? { connect_trigger: trigger } : {}),
207
208
  });
208
209
  }
209
210
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.52.5-next.437",
3
+ "version": "0.52.5-next.438",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {