mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-28 08:49:38 +02:00
fix(telemetry): emit install_first_run from notice path and derive flagsPresent from commander
This commit is contained in:
parent
3414d19916
commit
2532d4db56
6 changed files with 75 additions and 79 deletions
|
|
@ -1,29 +1,13 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
beginCommandSpan,
|
||||
completeCommandSpan,
|
||||
extractFlagsPresent,
|
||||
resetCommandSpan,
|
||||
} from './command-hook.js';
|
||||
import { beginCommandSpan, completeCommandSpan, resetCommandSpan } from './command-hook.js';
|
||||
|
||||
describe('telemetry command hook', () => {
|
||||
it('extracts only flag names, never flag values', () => {
|
||||
expect(
|
||||
extractFlagsPresent(['--project-dir', '/Users/alice/private', '--json', '--limit=5', '-v', 'status']),
|
||||
).toEqual({
|
||||
'project-dir': true,
|
||||
json: true,
|
||||
limit: true,
|
||||
v: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('builds a completed command event from a span', () => {
|
||||
resetCommandSpan();
|
||||
beginCommandSpan({
|
||||
commandPath: ['ktx', 'status'],
|
||||
argv: ['--project-dir', '/tmp/private', 'status', '--json'],
|
||||
flagsPresent: { projectDir: true, json: true },
|
||||
projectDir: '/tmp/private',
|
||||
hasProject: true,
|
||||
attachProjectGroup: true,
|
||||
|
|
@ -39,10 +23,7 @@ describe('telemetry command hook', () => {
|
|||
commandPath: ['ktx', 'status'],
|
||||
durationMs: 25,
|
||||
outcome: 'ok',
|
||||
flagsPresent: {
|
||||
'project-dir': true,
|
||||
json: true,
|
||||
},
|
||||
flagsPresent: { projectDir: true, json: true },
|
||||
hasProject: true,
|
||||
projectDir: '/tmp/private',
|
||||
projectGroupAttached: true,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export type CommandOutcome = 'ok' | 'error' | 'aborted';
|
|||
|
||||
interface CommandSpan {
|
||||
commandPath: string[];
|
||||
argv: string[];
|
||||
flagsPresent: Record<string, boolean>;
|
||||
projectDir?: string;
|
||||
hasProject: boolean;
|
||||
attachProjectGroup: boolean;
|
||||
|
|
@ -24,29 +24,6 @@ export interface CompletedCommandSpan {
|
|||
|
||||
let activeCommandSpan: CommandSpan | undefined;
|
||||
|
||||
/** @internal */
|
||||
export function extractFlagsPresent(argv: string[]): Record<string, boolean> {
|
||||
const flags: Record<string, boolean> = {};
|
||||
|
||||
for (const arg of argv) {
|
||||
if (arg.startsWith('--') && arg.length > 2) {
|
||||
const [name] = arg.slice(2).split('=', 1);
|
||||
if (name) {
|
||||
flags[name] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('-') && arg.length > 1) {
|
||||
for (const shortFlag of arg.slice(1)) {
|
||||
flags[shortFlag] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
export function beginCommandSpan(input: CommandSpan): void {
|
||||
activeCommandSpan = input;
|
||||
}
|
||||
|
|
@ -69,7 +46,7 @@ export function completeCommandSpan(input: {
|
|||
durationMs: Math.max(0, input.completedAt - span.startedAt),
|
||||
outcome: input.outcome,
|
||||
...(errorClass ? { errorClass } : {}),
|
||||
flagsPresent: extractFlagsPresent(span.argv),
|
||||
flagsPresent: span.flagsPresent,
|
||||
hasProject: span.hasProject,
|
||||
projectDir: span.projectDir,
|
||||
projectGroupAttached: span.attachProjectGroup,
|
||||
|
|
|
|||
|
|
@ -20,12 +20,30 @@ import { buildProjectStackSnapshotFields } from './project-snapshot.js';
|
|||
export { beginCommandSpan, completeCommandSpan, shutdownTelemetryEmitter };
|
||||
export type { CommandOutcome, CompletedCommandSpan };
|
||||
|
||||
export async function showTelemetryNoticeIfNeeded(io: KtxCliIo): Promise<void> {
|
||||
await loadTelemetryIdentity({
|
||||
export async function showTelemetryNoticeIfNeeded(io: KtxCliIo, packageInfo: KtxCliPackageInfo): Promise<void> {
|
||||
const identity = await loadTelemetryIdentity({
|
||||
stdoutIsTTY: io.stdout.isTTY === true,
|
||||
stderr: io.stderr,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (!identity.enabled || !identity.createdFile || !identity.installId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await trackTelemetryEvent({
|
||||
event: buildTelemetryEvent(
|
||||
'install_first_run',
|
||||
buildCommonEnvelope({
|
||||
cliVersion: packageInfo.version,
|
||||
isCi: Boolean(process.env.CI),
|
||||
}),
|
||||
{},
|
||||
),
|
||||
distinctId: identity.installId,
|
||||
env: process.env,
|
||||
stderr: io.stderr,
|
||||
});
|
||||
}
|
||||
|
||||
type TelemetryEventFields<Name extends TelemetryEventName> = Omit<
|
||||
|
|
@ -46,30 +64,6 @@ export function mcpTelemetrySampleRate(): 0.1 {
|
|||
return MCP_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
async function emitInstallFirstRunIfNeeded(input: {
|
||||
identity: Awaited<ReturnType<typeof loadTelemetryIdentity>>;
|
||||
packageInfo: KtxCliPackageInfo;
|
||||
io: KtxCliIo;
|
||||
}): Promise<void> {
|
||||
if (!input.identity.enabled || !input.identity.createdFile || !input.identity.installId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await trackTelemetryEvent({
|
||||
event: buildTelemetryEvent(
|
||||
'install_first_run',
|
||||
buildCommonEnvelope({
|
||||
cliVersion: input.packageInfo.version,
|
||||
isCi: Boolean(process.env.CI),
|
||||
}),
|
||||
{},
|
||||
),
|
||||
distinctId: input.identity.installId,
|
||||
env: process.env,
|
||||
stderr: input.io.stderr,
|
||||
});
|
||||
}
|
||||
|
||||
export async function emitTelemetryEvent<Name extends TelemetryEventName>(input: {
|
||||
name: Name;
|
||||
fields: TelemetryEventFields<Name>;
|
||||
|
|
@ -91,7 +85,6 @@ export async function emitTelemetryEvent<Name extends TelemetryEventName>(input:
|
|||
name: '@kaelio/ktx',
|
||||
version: process.env.npm_package_version ?? '0.0.0',
|
||||
};
|
||||
await emitInstallFirstRunIfNeeded({ identity, packageInfo, io: input.io });
|
||||
|
||||
const projectId = input.projectDir ? computeTelemetryProjectId(identity.installId, input.projectDir) : undefined;
|
||||
await trackTelemetryEvent({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue