Files
agentic-dev/apps/runner/src/log.ts
Danijel Martinek b090e26701 feat(runner): WS protocol server with handshake
apps/runner scaffold (app-tier, walking-skeleton story 04): WS server
speaking @repo/core-runner-protocol. Every inbound/outbound frame is
envelope-wrapped and zod-parsed; hello/ready handshake gates on the
workspace-scoped token (constant-time compare, redacted token on
rejection replies); named error events for version/schema/auth
rejections. Config via env only (token never argv); port announced on
stdout for the story-06 provisioner. Runtime deps: ws (the standard
Node WS server; ADR-022 traces do not apply to app-tier) and zod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 22:50:41 +02:00

35 lines
1.1 KiB
TypeScript

/**
* Minimal JSON-lines logger for the runner process.
*
* The runner logs to stdout so the provisioner (story 06) and the
* integration tests can read a structured stream. The sink is injectable
* so in-process tests can capture every emitted line and assert
* credential-leak absence (spec §15): nothing in this module — and by
* convention nothing passed to it — may ever contain a secret. Callers
* log event names and safe fields only, never raw protocol payloads.
*/
export type LogSink = (line: string) => void;
export interface Logger {
info(event: string, fields?: Record<string, unknown>): void;
error(event: string, fields?: Record<string, unknown>): void;
}
const stdoutSink: LogSink = (line) => {
process.stdout.write(`${line}\n`);
};
export function createLogger(sink: LogSink = stdoutSink): Logger {
const emit = (
level: "info" | "error",
event: string,
fields?: Record<string, unknown>,
) => {
sink(JSON.stringify({ level, event, ...fields }));
};
return {
info: (event, fields) => emit("info", event, fields),
error: (event, fields) => emit("error", event, fields),
};
}