@founderos/runner 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Spawn `claude` CLI for a claimed runner job. Streams stream-json from
3
+ * stdout, parses each line into a typed event, and emits events via the
4
+ * caller's `onEvent` callback. The caller is responsible for batching and
5
+ * shipping events back to the cloud (see api.ts appendEvents).
6
+ *
7
+ * Why we don't use `claude --output-format=stream-json` for everything:
8
+ * `claude --print -` accepts the prompt on stdin and writes mixed stdout —
9
+ * stream-json events when `--output-format=stream-json` is set, plain text
10
+ * otherwise. We always set stream-json so parsing is uniform. Lines that
11
+ * fail JSON.parse are surfaced as `stdout_line` raw text events (so noisy
12
+ * stderr output from claude's setup phase is still visible to ops).
13
+ *
14
+ * Result extraction: the LAST `claude_result` event (kind `result` in
15
+ * stream-json terms) carries the cost + sessionId. We snapshot it so the
16
+ * caller can read it after the child exits.
17
+ */
18
+ import { spawn } from "node:child_process";
19
+ import { type RunnerEvent } from "./api.js";
20
+ export interface SpawnArgs {
21
+ binary: string;
22
+ prompt: string;
23
+ sessionId?: string | null;
24
+ model?: string | null;
25
+ maxTurns?: number | null;
26
+ /** Base64 contents of the agent's append-system-prompt file. */
27
+ instructionsBase64?: string | null;
28
+ /** Hard timeout — if claude doesn't exit by then, we SIGTERM then SIGKILL. */
29
+ timeoutSec: number;
30
+ /** Working directory for the spawned claude. Defaults to cwd. */
31
+ cwd?: string;
32
+ /** Optional --add-dir entries the cloud suggested. */
33
+ addDirs?: string[];
34
+ }
35
+ export interface SpawnResult {
36
+ exitCode: number;
37
+ signal: string | null;
38
+ elapsedSec: number;
39
+ /** True iff we killed the process due to the timeout. */
40
+ timedOut: boolean;
41
+ /** Captured `result` event from stream-json — null if claude never emitted one. */
42
+ finalResult: {
43
+ sessionId?: string | null;
44
+ costUsd?: number | null;
45
+ raw: Record<string, unknown>;
46
+ } | null;
47
+ }
48
+ /**
49
+ * Build the claude CLI argv. Kept pure so it's unit-testable without
50
+ * actually spawning a child.
51
+ */
52
+ export declare function buildClaudeArgs(args: {
53
+ sessionId?: string | null;
54
+ model?: string | null;
55
+ maxTurns?: number | null;
56
+ instructionsFilePath?: string | null;
57
+ addDirs?: string[];
58
+ }): string[];
59
+ /**
60
+ * Parse one line of claude stream-json into a RunnerEvent. The mapping
61
+ * follows ADR-011 § "Event taxonomy":
62
+ * - `assistant` / `user` messages → claude_message
63
+ * - `tool_use` blocks → claude_tool_use
64
+ * - `tool_result` blocks → claude_tool_result
65
+ * - `result` (final stats) → claude_result
66
+ * - anything else → stdout_line (raw)
67
+ */
68
+ export declare function parseStreamJsonLine(line: string): RunnerEvent | null;
69
+ /**
70
+ * Materialize the base64 instructions blob to a tempfile so claude can
71
+ * `--append-system-prompt-file` it. Returns the path; caller is responsible
72
+ * for cleanup (we surface the cleanup function to keep this composable).
73
+ */
74
+ export declare function materializeInstructions(base64: string): Promise<{
75
+ path: string;
76
+ cleanup: () => Promise<void>;
77
+ }>;
78
+ /**
79
+ * Run claude end-to-end. Streams events, enforces timeout, returns the
80
+ * captured final result. Tests can substitute `spawnImpl` to run a mock
81
+ * binary that prints scripted stream-json.
82
+ */
83
+ export declare function runClaude(args: SpawnArgs, hooks: {
84
+ onEvent: (evt: RunnerEvent) => void | Promise<void>;
85
+ }, spawnImpl?: typeof spawn): Promise<SpawnResult>;
86
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAO9D,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,mFAAmF;IACnF,WAAW,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;CAC1G;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GAAG,MAAM,EAAE,CAkBX;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAmEpE;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAezD;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE;IACL,OAAO,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,EACD,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAwHtB"}
package/dist/spawn.js ADDED
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Spawn `claude` CLI for a claimed runner job. Streams stream-json from
3
+ * stdout, parses each line into a typed event, and emits events via the
4
+ * caller's `onEvent` callback. The caller is responsible for batching and
5
+ * shipping events back to the cloud (see api.ts appendEvents).
6
+ *
7
+ * Why we don't use `claude --output-format=stream-json` for everything:
8
+ * `claude --print -` accepts the prompt on stdin and writes mixed stdout —
9
+ * stream-json events when `--output-format=stream-json` is set, plain text
10
+ * otherwise. We always set stream-json so parsing is uniform. Lines that
11
+ * fail JSON.parse are surfaced as `stdout_line` raw text events (so noisy
12
+ * stderr output from claude's setup phase is still visible to ops).
13
+ *
14
+ * Result extraction: the LAST `claude_result` event (kind `result` in
15
+ * stream-json terms) carries the cost + sessionId. We snapshot it so the
16
+ * caller can read it after the child exits.
17
+ */
18
+ import { spawn } from "node:child_process";
19
+ import { setTimeout as sleep } from "node:timers/promises";
20
+ import { writeFile, mkdtemp, rm } from "node:fs/promises";
21
+ import path from "node:path";
22
+ import os from "node:os";
23
+ import { performance } from "node:perf_hooks";
24
+ import { makeEventId } from "./api.js";
25
+ /**
26
+ * Build the claude CLI argv. Kept pure so it's unit-testable without
27
+ * actually spawning a child.
28
+ */
29
+ export function buildClaudeArgs(args) {
30
+ const argv = ["--print", "-", "--output-format", "stream-json", "--verbose"];
31
+ if (args.sessionId) {
32
+ argv.push("--resume", args.sessionId);
33
+ }
34
+ if (args.model) {
35
+ argv.push("--model", args.model);
36
+ }
37
+ if (typeof args.maxTurns === "number" && args.maxTurns > 0) {
38
+ argv.push("--max-turns", String(args.maxTurns));
39
+ }
40
+ if (args.instructionsFilePath) {
41
+ argv.push("--append-system-prompt-file", args.instructionsFilePath);
42
+ }
43
+ for (const dir of args.addDirs ?? []) {
44
+ argv.push("--add-dir", dir);
45
+ }
46
+ return argv;
47
+ }
48
+ /**
49
+ * Parse one line of claude stream-json into a RunnerEvent. The mapping
50
+ * follows ADR-011 § "Event taxonomy":
51
+ * - `assistant` / `user` messages → claude_message
52
+ * - `tool_use` blocks → claude_tool_use
53
+ * - `tool_result` blocks → claude_tool_result
54
+ * - `result` (final stats) → claude_result
55
+ * - anything else → stdout_line (raw)
56
+ */
57
+ export function parseStreamJsonLine(line) {
58
+ const trimmed = line.trim();
59
+ if (!trimmed)
60
+ return null;
61
+ // Try strict JSON first.
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(trimmed);
65
+ }
66
+ catch {
67
+ return {
68
+ eventId: makeEventId(),
69
+ kind: "stdout_line",
70
+ ts: new Date().toISOString(),
71
+ payload: trimmed,
72
+ };
73
+ }
74
+ if (!parsed || typeof parsed !== "object") {
75
+ return {
76
+ eventId: makeEventId(),
77
+ kind: "stdout_line",
78
+ ts: new Date().toISOString(),
79
+ payload: trimmed,
80
+ };
81
+ }
82
+ const obj = parsed;
83
+ const type = typeof obj.type === "string" ? obj.type : "";
84
+ if (type === "result") {
85
+ return {
86
+ eventId: makeEventId(),
87
+ kind: "claude_result",
88
+ ts: new Date().toISOString(),
89
+ payload: obj,
90
+ };
91
+ }
92
+ if (type === "assistant" || type === "user" || type === "system") {
93
+ return {
94
+ eventId: makeEventId(),
95
+ kind: "claude_message",
96
+ ts: new Date().toISOString(),
97
+ payload: obj,
98
+ };
99
+ }
100
+ if (type === "tool_use") {
101
+ return {
102
+ eventId: makeEventId(),
103
+ kind: "claude_tool_use",
104
+ ts: new Date().toISOString(),
105
+ payload: obj,
106
+ };
107
+ }
108
+ if (type === "tool_result") {
109
+ return {
110
+ eventId: makeEventId(),
111
+ kind: "claude_tool_result",
112
+ ts: new Date().toISOString(),
113
+ payload: obj,
114
+ };
115
+ }
116
+ return {
117
+ eventId: makeEventId(),
118
+ kind: "stdout_line",
119
+ ts: new Date().toISOString(),
120
+ payload: obj,
121
+ };
122
+ }
123
+ /**
124
+ * Materialize the base64 instructions blob to a tempfile so claude can
125
+ * `--append-system-prompt-file` it. Returns the path; caller is responsible
126
+ * for cleanup (we surface the cleanup function to keep this composable).
127
+ */
128
+ export async function materializeInstructions(base64) {
129
+ const dir = await mkdtemp(path.join(os.tmpdir(), "founderos-runner-"));
130
+ const filePath = path.join(dir, "instructions.md");
131
+ const buf = Buffer.from(base64, "base64");
132
+ await writeFile(filePath, buf);
133
+ return {
134
+ path: filePath,
135
+ cleanup: async () => {
136
+ try {
137
+ await rm(dir, { recursive: true, force: true });
138
+ }
139
+ catch {
140
+ /* best-effort */
141
+ }
142
+ },
143
+ };
144
+ }
145
+ /**
146
+ * Run claude end-to-end. Streams events, enforces timeout, returns the
147
+ * captured final result. Tests can substitute `spawnImpl` to run a mock
148
+ * binary that prints scripted stream-json.
149
+ */
150
+ export async function runClaude(args, hooks, spawnImpl = spawn) {
151
+ let cleanup = null;
152
+ let instructionsPath = null;
153
+ if (args.instructionsBase64) {
154
+ const m = await materializeInstructions(args.instructionsBase64);
155
+ instructionsPath = m.path;
156
+ cleanup = m.cleanup;
157
+ }
158
+ const argv = buildClaudeArgs({
159
+ sessionId: args.sessionId,
160
+ model: args.model,
161
+ maxTurns: args.maxTurns,
162
+ instructionsFilePath: instructionsPath,
163
+ addDirs: args.addDirs,
164
+ });
165
+ const startedAt = performance.now();
166
+ const child = spawnImpl(args.binary, argv, {
167
+ cwd: args.cwd,
168
+ stdio: ["pipe", "pipe", "pipe"],
169
+ env: process.env,
170
+ });
171
+ let timedOut = false;
172
+ let finalResult = null;
173
+ // Hard kill after 1.5x timeout if SIGTERM didn't land.
174
+ const termTimer = setTimeout(() => {
175
+ timedOut = true;
176
+ if (!child.killed)
177
+ child.kill("SIGTERM");
178
+ }, args.timeoutSec * 1000);
179
+ const killTimer = setTimeout(() => {
180
+ if (!child.killed)
181
+ child.kill("SIGKILL");
182
+ }, args.timeoutSec * 1000 * 1.5);
183
+ // stdin: write the prompt then close.
184
+ if (child.stdin) {
185
+ child.stdin.write(args.prompt);
186
+ child.stdin.end();
187
+ }
188
+ // Line-buffer stdout. claude emits one JSON object per line in
189
+ // stream-json mode; partial lines accumulate until the next \n.
190
+ let stdoutBuf = "";
191
+ child.stdout?.setEncoding("utf-8");
192
+ child.stdout?.on("data", (chunk) => {
193
+ stdoutBuf += chunk;
194
+ let nl;
195
+ while ((nl = stdoutBuf.indexOf("\n")) >= 0) {
196
+ const line = stdoutBuf.slice(0, nl);
197
+ stdoutBuf = stdoutBuf.slice(nl + 1);
198
+ const evt = parseStreamJsonLine(line);
199
+ if (!evt)
200
+ continue;
201
+ if (evt.kind === "claude_result" && typeof evt.payload === "object" && evt.payload) {
202
+ const p = evt.payload;
203
+ finalResult = {
204
+ sessionId: typeof p.session_id === "string" ? p.session_id : null,
205
+ costUsd: typeof p.total_cost_usd === "number" ? p.total_cost_usd : null,
206
+ raw: p,
207
+ };
208
+ }
209
+ void hooks.onEvent(evt);
210
+ }
211
+ });
212
+ // stderr: surface as raw stderr_line events. Don't try to parse —
213
+ // claude's stderr is human-readable diagnostics, not stream-json.
214
+ let stderrBuf = "";
215
+ child.stderr?.setEncoding("utf-8");
216
+ child.stderr?.on("data", (chunk) => {
217
+ stderrBuf += chunk;
218
+ let nl;
219
+ while ((nl = stderrBuf.indexOf("\n")) >= 0) {
220
+ const line = stderrBuf.slice(0, nl);
221
+ stderrBuf = stderrBuf.slice(nl + 1);
222
+ if (!line.trim())
223
+ continue;
224
+ void hooks.onEvent({
225
+ eventId: makeEventId(),
226
+ kind: "stderr_line",
227
+ ts: new Date().toISOString(),
228
+ payload: line,
229
+ });
230
+ }
231
+ });
232
+ const exit = await new Promise((resolve) => {
233
+ child.once("exit", (code, signal) => resolve({ code: code ?? -1, signal }));
234
+ child.once("error", () => resolve({ code: -1, signal: null }));
235
+ });
236
+ // Drain any trailing partial line.
237
+ if (stdoutBuf.trim()) {
238
+ const evt = parseStreamJsonLine(stdoutBuf);
239
+ if (evt)
240
+ await hooks.onEvent(evt);
241
+ }
242
+ if (stderrBuf.trim()) {
243
+ await hooks.onEvent({
244
+ eventId: makeEventId(),
245
+ kind: "stderr_line",
246
+ ts: new Date().toISOString(),
247
+ payload: stderrBuf,
248
+ });
249
+ }
250
+ clearTimeout(termTimer);
251
+ clearTimeout(killTimer);
252
+ if (cleanup)
253
+ await cleanup();
254
+ // Tiny sleep to let any straggler stream events flush through onEvent.
255
+ await sleep(10);
256
+ return {
257
+ exitCode: exit.code,
258
+ signal: exit.signal,
259
+ elapsedSec: (performance.now() - startedAt) / 1000,
260
+ timedOut,
261
+ finalResult,
262
+ };
263
+ }
264
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const RUNNER_VERSION = "0.1.1";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,UAAU,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Sourced from package.json at install time. Bumping this should match a
2
+ // `npm version` so the runner identifies itself accurately to the cloud
3
+ // (used in `cliVersion` field of /jobs/:id/complete).
4
+ export const RUNNER_VERSION = "0.1.1";
5
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,sDAAsD;AACtD,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@founderos/runner",
3
+ "version": "0.1.1",
4
+ "description": "FounderOS local runner — polls cloud, spawns claude CLI under your Pro session, streams events back. ADR-011.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/founderos-ai/founderos",
7
+ "bugs": {
8
+ "url": "https://github.com/founderos-ai/founderos/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/founderos-ai/founderos",
13
+ "directory": "packages/runner"
14
+ },
15
+ "type": "module",
16
+ "bin": {
17
+ "founderos-runner": "./dist/cli.js"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "clean": "rm -rf dist",
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest"
39
+ },
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^24.6.0",
45
+ "typescript": "^5.7.3",
46
+ "vitest": "^3.2.4"
47
+ }
48
+ }