feat(diagnostics): funnel events throughout setup with persisted install-id
Shared bash + node emitter in setup/lib/diagnostics.{sh,ts} reads/writes data/install-id so every event from a single install shares one distinct_id — bash-side setup_launched/setup_start, node-side auto_started, per-step started/completed, auth_method_chosen, channel_chosen, first_chat_ready/failed, setup_incomplete, setup_aborted, setup_completed. Opt-out via NANOCLAW_NO_DIAGNOSTICS=1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import k from 'kleur';
|
||||
|
||||
import * as setupLog from '../logs.js';
|
||||
import { offerClaudeAssist } from './claude-assist.js';
|
||||
import { emit as phEmit } from './diagnostics.js';
|
||||
import { fitToWidth } from './theme.js';
|
||||
|
||||
export type Fields = Record<string, string>;
|
||||
@@ -186,11 +187,17 @@ export async function runQuietStep(
|
||||
): Promise<StepResult & { rawLog: string; durationMs: number }> {
|
||||
const rawLog = setupLog.stepRawLog(stepName);
|
||||
const start = Date.now();
|
||||
phEmit('step_started', { step: stepName });
|
||||
const result = await runUnderSpinner(labels, () =>
|
||||
spawnStep(stepName, extra, () => {}, rawLog),
|
||||
);
|
||||
const durationMs = Date.now() - start;
|
||||
writeStepEntry(stepName, result, durationMs, rawLog);
|
||||
phEmit('step_completed', {
|
||||
step: stepName,
|
||||
status: outcomeStatus(result),
|
||||
duration_ms: durationMs,
|
||||
});
|
||||
return { ...result, rawLog, durationMs };
|
||||
}
|
||||
|
||||
@@ -209,6 +216,7 @@ export async function runQuietChild(
|
||||
): Promise<QuietChildResult & { rawLog: string; durationMs: number }> {
|
||||
const rawLog = setupLog.stepRawLog(logName);
|
||||
const start = Date.now();
|
||||
phEmit('step_started', { step: logName });
|
||||
const result = await runUnderSpinner(labels, () =>
|
||||
spawnQuiet(cmd, args, rawLog, opts?.env),
|
||||
);
|
||||
@@ -223,9 +231,17 @@ export async function runQuietChild(
|
||||
? 'skipped'
|
||||
: 'success';
|
||||
setupLog.step(logName, status, durationMs, fields, rawLog);
|
||||
phEmit('step_completed', { step: logName, status, duration_ms: durationMs });
|
||||
return { ...result, rawLog, durationMs };
|
||||
}
|
||||
|
||||
/** Collapse a step run into the three-way status used by diagnostics + progression log. */
|
||||
function outcomeStatus(result: StepResult): 'success' | 'skipped' | 'failed' {
|
||||
const rawStatus = result.terminal?.fields.STATUS;
|
||||
if (!result.ok) return 'failed';
|
||||
return rawStatus === 'skipped' ? 'skipped' : 'success';
|
||||
}
|
||||
|
||||
/** Turn a step's terminal-block fields into a concise progression-log entry. */
|
||||
export function writeStepEntry(
|
||||
stepName: string,
|
||||
@@ -318,6 +334,7 @@ export async function fail(
|
||||
rawLogPath?: string,
|
||||
): Promise<never> {
|
||||
setupLog.abort(stepName, msg);
|
||||
phEmit('setup_aborted', { step: stepName, reason: msg });
|
||||
p.log.error(msg);
|
||||
if (hint) p.log.message(k.dim(hint));
|
||||
p.log.message(k.dim('Logs: logs/setup.log · Raw: logs/setup-steps/'));
|
||||
|
||||
Reference in New Issue
Block a user