Files
agentic-dev/apps/runner/tests/runner-session.ts
Danijel Martinek ec7bf948af feat(runner): install stage with progress events
Install stage: package-manager detection (lockfile beats the
packageManager field, npm default — vite-kitchen's shape), install run
inside the clone with staged status heartbeats, and every failure —
including install-before-clone — mapped to the named install-failed
cause with a bounded output tail. The spawned-runner suite now runs the
full clone → install pipeline on the daemon-served vite-kitchen (real
npm registry install) and greps the child's entire stdout+stderr plus
the clone's .git/config for the PAT and workspace token. Shared test
doubles extracted (exec.mock.ts, tests/runner-session.ts) to keep the
suites duplication-free.

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

54 lines
1.5 KiB
TypeScript

import { mkdtemp, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { createLogger } from "@/log";
import { startRunnerServer, type RunnerServer } from "@/server";
import {
connectProtocolClient,
handshake,
type ProtocolClient,
} from "./protocol-client";
/**
* An in-process runner server plus one already-handshaken client, with
* log capture (for the credential-leak assertions) and one-call
* teardown. In-process so v8 coverage sees the server; the spawn suite
* covers the real-child-process shape.
*/
export interface RunnerSession {
client: ProtocolClient;
workspaceDir: string;
/** Every log line the runner emitted — greppable for leaks. */
logLines: string[];
close: () => Promise<void>;
}
export async function startRunnerSession(options: {
token: string;
tmpPrefix: string;
heartbeatMs?: number;
}): Promise<RunnerSession> {
const workspaceDir = await mkdtemp(path.join(os.tmpdir(), options.tmpPrefix));
const logLines: string[] = [];
const server: RunnerServer = await startRunnerServer({
host: "127.0.0.1",
port: 0,
token: options.token,
workspaceDir,
heartbeatMs: options.heartbeatMs ?? 100,
log: createLogger((line) => logLines.push(line)),
});
const client = await connectProtocolClient(server.port, options.token);
await handshake(client);
return {
client,
workspaceDir,
logLines,
close: async () => {
client.close();
await server.close();
await rm(workspaceDir, { recursive: true, force: true });
},
};
}