@tiny-fish/cli 0.32.1-next.275 → 0.33.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.
@@ -342,9 +342,12 @@ export async function applyRepairs(repairs, options) {
342
342
  }
343
343
  return outcomes;
344
344
  }
345
- /** Aggregate counts only; check `detail` text and per-harness rows never leave the machine. */
346
- function telemetryPayload(report, offered, outcomes, options) {
345
+ /** Mirrors the route's cap; over it the payload would 400 and never be retried. */
346
+ const MAX_DURATION_MS = 3_600_000;
347
+ /** Enumerated fields only; check `detail` text never leaves the machine. */
348
+ function telemetryPayload(report, offered, outcomes, options, durationMs) {
347
349
  const count = (status) => report.checks.filter((check) => check.status === status).length;
350
+ const failed = report.checks.filter((check) => check.status === 'fail');
348
351
  return {
349
352
  schema_version: report.schema_version,
350
353
  cli_version: report.cli_version,
@@ -364,6 +367,12 @@ function telemetryPayload(report, offered, outcomes, options) {
364
367
  harness_scope: options.harness ?? 'all',
365
368
  // Same signal cli_connect_stage carries, so agent and human doctor runs split.
366
369
  is_human_initiated: detectHumanInitiated(),
370
+ // Two flat arrays rather than one of pairs: PostHog groups by element, not by field.
371
+ failed_check_ids: [...new Set(failed.map((check) => check.id))],
372
+ failed_harnesses: [...new Set(failed.map((check) => check.harness).filter((h) => h !== null))],
373
+ harnesses: report.harnesses,
374
+ // Clamped, not validated: a --fix parked at a prompt must not fail the parse and drop the run.
375
+ duration_ms: Math.min(durationMs, MAX_DURATION_MS),
367
376
  };
368
377
  }
369
378
  export function registerDoctor(program) {
@@ -390,6 +399,7 @@ export function registerDoctor(program) {
390
399
  let report;
391
400
  let offered;
392
401
  let outcomes = [];
402
+ const startedAt = performance.now();
393
403
  try {
394
404
  report = await runDoctor(doctorOptions);
395
405
  offered = report.repairs;
@@ -420,6 +430,6 @@ export function registerDoctor(program) {
420
430
  process.exitCode = exitCodeFor(report.checks);
421
431
  // After the exit code is set: telemetry never decides how doctor exits.
422
432
  const resolved = apiKeyStatus();
423
- await sendDoctorCompleted(doctorOptions.mcpUrl, telemetryPayload(report, offered, outcomes, doctorOptions), 'key' in resolved ? resolved.key : undefined);
433
+ await sendDoctorCompleted(doctorOptions.mcpUrl, telemetryPayload(report, offered, outcomes, doctorOptions, Math.round(performance.now() - startedAt)), 'key' in resolved ? resolved.key : undefined);
424
434
  });
425
435
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { AuthMode, Registered } from './harness-detect.js';
2
3
  /**
3
4
  * `TINYFISH_NO_TELEMETRY` suppresses ALL CLI-emitted network analytics
4
5
  * (setup_completed, connect-event, update check). Any value except `0`/`false`
@@ -111,6 +112,32 @@ export declare const doctorCompletedPayloadSchema: z.ZodObject<{
111
112
  all: "all";
112
113
  }>;
113
114
  is_human_initiated: z.ZodBoolean;
115
+ failed_check_ids: z.ZodArray<z.ZodString>;
116
+ failed_harnesses: z.ZodArray<z.ZodEnum<{
117
+ openclaw: "openclaw";
118
+ grok: "grok";
119
+ cursor: "cursor";
120
+ codex: "codex";
121
+ hermes: "hermes";
122
+ opencode: "opencode";
123
+ "claude-code": "claude-code";
124
+ }>>;
125
+ harnesses: z.ZodArray<z.ZodObject<{
126
+ harness: z.ZodEnum<{
127
+ openclaw: "openclaw";
128
+ grok: "grok";
129
+ cursor: "cursor";
130
+ codex: "codex";
131
+ hermes: "hermes";
132
+ opencode: "opencode";
133
+ "claude-code": "claude-code";
134
+ }>;
135
+ detected: z.ZodBoolean;
136
+ registered: z.ZodEnum<typeof Registered>;
137
+ auth_mode: z.ZodEnum<typeof AuthMode>;
138
+ proves_harness_reach: z.ZodBoolean;
139
+ }, z.core.$strip>>;
140
+ duration_ms: z.ZodInt;
114
141
  }, z.core.$strip>;
115
142
  export type DoctorCompletedPayload = z.infer<typeof doctorCompletedPayloadSchema>;
116
143
  export declare function sendDoctorCompleted(mcpUrl: string, payload: DoctorCompletedPayload, apiKey?: string): Promise<void>;
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { z } from 'zod';
3
3
  import { validateKeyFormat } from './auth.js';
4
4
  import { BASE_URL, CLI_INVOCATION_ID, CLI_VERSION } from './constants.js';
5
- import { ALL_HARNESSES } from './harness-detect.js';
5
+ import { ALL_HARNESSES, AuthMode, Registered } from './harness-detect.js';
6
6
  import { detectHarness } from './harness.js';
7
7
  import { captureNotice } from './notice.js';
8
8
  import { errLine } from './output.js';
@@ -184,8 +184,8 @@ export async function sendSetupCompleted(mcpUrl, harnesses, apiKey, mode, attemp
184
184
  await postConnectEvent({ endpoint, body, attempts: 2, label: 'setup', apiKey });
185
185
  }
186
186
  export const DOCTOR_HARNESS_SCOPES = ['all', ...ALL_HARNESSES];
187
- // Aggregate only: per-harness rows and check `detail` strings stay local, so nothing
188
- // user-authored or credential-shaped can ride a doctor run to PostHog.
187
+ // Enumerated only: check ids, harness names and auth modes are authored here, so nothing
188
+ // user-authored or credential-shaped can ride a doctor run to PostHog. `detail` never leaves.
189
189
  // Caps mirror the route's schema: a payload the server would 400 is dropped here instead,
190
190
  // because postConnectEvent never retries a 4xx and the event would vanish silently.
191
191
  export const doctorCompletedPayloadSchema = z.object({
@@ -206,6 +206,19 @@ export const doctorCompletedPayloadSchema = z.object({
206
206
  /** "all" unless --harness scoped the run; the counts above cover only that scope. */
207
207
  harness_scope: z.enum(DOCTOR_HARNESS_SCOPES),
208
208
  is_human_initiated: z.boolean(),
209
+ /** Flat and distinct: PostHog cannot group by array elements of an object. */
210
+ failed_check_ids: z.array(z.string().regex(/^[a-z0-9-]{1,64}$/)).max(32),
211
+ failed_harnesses: z.array(z.enum(ALL_HARNESSES)).max(16),
212
+ harnesses: z
213
+ .array(z.object({
214
+ harness: z.enum(ALL_HARNESSES),
215
+ detected: z.boolean(),
216
+ registered: z.enum(Registered),
217
+ auth_mode: z.enum(AuthMode),
218
+ proves_harness_reach: z.boolean(),
219
+ }))
220
+ .max(16),
221
+ duration_ms: z.int().nonnegative().max(3_600_000),
209
222
  });
210
223
  export async function sendDoctorCompleted(mcpUrl, payload, apiKey) {
211
224
  if (telemetryDisabled())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.32.1-next.275",
3
+ "version": "0.33.0",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {