import { type CliFailure } from "./hosting/cli-failure"; import { type ErrorCode, type Origin } from "./hosting/error-catalog"; export declare const TELEMETRY_OPT_OUT_ENV = "CLUSTLY_NO_TELEMETRY"; export declare const SPOOL_DIR_NAME = "spool"; export declare const INSTALL_ID_FILENAME = "install-id"; export declare const SPOOL_MAX_FILES = 200; export declare const WHAT_MAX_CHARS = 500; /** The wire's own bound on `command` (events.schema.ts `SHORT`). Clamped here as well as chosen * here: a value the dispatcher never produces must still not cost the whole batch a 400. */ export declare const COMMAND_MAX_CHARS = 32; /** * int4 max. The wire's own bound (`events.schema.ts DURATION_MAX_MS`), clamped here as well as * checked there: `duration_ms` is the only field a long-lived command can grow without limit * (25 days of `clustly run`), and an event the schema refuses costs the whole batch a 400. */ export declare const DURATION_MAX_MS = 2147483647; export type TrailEventName = "cli.command_started" | "cli.command_finished"; export interface TrailEvent { id: string; event: TrailEventName; at: string; session_id: string; install_id: string; key_prefix: string | null; command: string; cli_version: string; node_version: string; platform: string; ci: boolean; json: boolean; exit_code?: number; duration_ms?: number; error_code?: string | null; origin?: Origin | null; what?: string | null; } export interface TelemetryDeps { home: string; env: Record; now: () => number; cliVersion: string; /** Launch the drain child for this spool dir; the real one spawns node detached (cli.ts). */ spawnDrain: (spoolDir: string) => void; } export declare function telemetryEnabled(env: Record): boolean; export declare function noteFailureForTrail(failure: CliFailure): void; export declare function takeNotedFailure(): CliFailure | undefined; /** * Whether the caller asked for the machine envelope. Read from argv rather than from a parsed * flag on purpose: a usage error IS a parse that failed, and it still has to answer in the * shape the caller asked for. Exact-token match, and nothing after a `--` terminator counts โ€” * a positional that merely CONTAINS "--json" never did. * * Lives here rather than in commands/failure.ts because failure.ts already imports this module * (`noteFailureForTrail`); the reverse would be a cycle. */ export declare function jsonRequested(argv?: readonly string[]): boolean; /** * Codes whose `what` ENUMERATES NAMES out of the builder's own project โ€” the secret names * clustly.yaml declares, the binaries their code spawns, the credential a scanner matched. For * these the instance text is replaced wholesale by the catalog's meaning: the code already tells * us what broke, and the list is the builder's business. */ export declare const WHAT_WITHHELD_CODES: readonly ErrorCode[]; /** * The privacy policy for the one free-text field an event carries (spec ยง4.1). Pure, exported * for its own tests. * * Order is load-bearing. ANSI first, so an escape sequence cannot hide a path from the pattern. * URLs next, because they are the one construct that legitimately CONTAINS slashes โ€” reducing * them first means the path patterns that follow never have to reason about them. Relative paths * BEFORE absolute ones: the absolute pattern happily matches the `/x/y` inside `../x/y` and would * leave the `..` behind. Then quoted spans (which swallow whatever the path rules left inside * them), then the cap. */ export declare function redactForTrail(code: ErrorCode, what: string): string; export declare class CommandTrail { private readonly deps; private readonly command; private readonly argv; readonly sessionId: `${string}-${string}-${string}-${string}-${string}`; private startedAt; private readonly spoolDir; constructor(deps: TelemetryDeps, command: string, argv: readonly string[]); start(): void; finish(exitCode: number): void; private identity; /** One file per event; oldest evicted past the cap; every failure swallowed. */ private spool; }