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
This commit is contained in:
2026-07-12 22:29:24 +02:00
parent 11e5012572
commit b090e26701
19 changed files with 1331 additions and 11 deletions

View File

@@ -0,0 +1,26 @@
import path from "node:path";
import { mergeConfig } from "vitest/config";
import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node";
// Coverage excludes mirror the app-tier pattern (see apps/cms and
// apps/web-next vitest configs): bootstrap glue is excluded, thresholds
// stay inherited from the shared base — never lowered here.
export default mergeConfig(nodeVitestConfig, {
test: {
// Integration suites spawn a real runner process, run real `git
// daemon` clones, and (story task 3) a real package-manager install
// of the vite-kitchen fixture — minutes-scale on a cold cache.
testTimeout: 240_000,
hookTimeout: 60_000,
coverage: {
exclude: [
// Process bootstrap: env → server → signal handlers. Exercised
// end-to-end by the spawn integration suite (tests/), which runs
// it in a child process where v8 in-process coverage cannot see
// it. All logic it calls (config, server) is unit-covered.
"src/main.ts",
],
},
},
resolve: { alias: { "@": path.resolve(__dirname, "./src") } },
});