@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.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/api.d.ts +137 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +148 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.d.ts +28 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +182 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +68 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +42 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +237 -0
- package/dist/main.js.map +1 -0
- package/dist/spawn.d.ts +86 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +264 -0
- package/dist/spawn.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +48 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `founderos-runner` CLI entry. Supports a single command:
|
|
4
|
+
*
|
|
5
|
+
* founderos-runner start [--token=...] [--server-url=...] [--claude-bin=...]
|
|
6
|
+
* [--timeout-sec=N] [--log-level=info]
|
|
7
|
+
* founderos-runner --version
|
|
8
|
+
* founderos-runner --help
|
|
9
|
+
*
|
|
10
|
+
* Flags override the matching FOUNDEROS_* environment variables. Both
|
|
11
|
+
* forms are supported because Windows PowerShell users find env-var
|
|
12
|
+
* setup awkward (`$env:FOUNDEROS_RUNNER_TOKEN="..."` vs `--token=...`),
|
|
13
|
+
* and macOS/Linux users in shell scripts often prefer env vars.
|
|
14
|
+
*/
|
|
15
|
+
import { realpathSync } from "node:fs";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { loadConfig, RunnerConfigError } from "./config.js";
|
|
18
|
+
import { runRunnerLoop } from "./main.js";
|
|
19
|
+
import { RUNNER_VERSION } from "./version.js";
|
|
20
|
+
function printHelp() {
|
|
21
|
+
console.log(`founderos-runner v${RUNNER_VERSION}
|
|
22
|
+
|
|
23
|
+
USAGE
|
|
24
|
+
founderos-runner start [flags]
|
|
25
|
+
founderos-runner --version
|
|
26
|
+
founderos-runner --help
|
|
27
|
+
|
|
28
|
+
FLAGS
|
|
29
|
+
--token=<fos_...> Runner token (overrides FOUNDEROS_RUNNER_TOKEN)
|
|
30
|
+
--server-url=<url> Cloud base URL (overrides FOUNDEROS_RUNNER_URL)
|
|
31
|
+
--claude-bin=<path> Path to claude CLI (overrides FOUNDEROS_CLAUDE_BIN)
|
|
32
|
+
--timeout-sec=<n> Per-job timeout 1..3600 (overrides FOUNDEROS_RUNNER_TIMEOUT_SEC)
|
|
33
|
+
--log-level=<level> debug | info | warn | error (overrides FOUNDEROS_RUNNER_LOG_LEVEL)
|
|
34
|
+
|
|
35
|
+
ENVIRONMENT (used when matching flag is omitted)
|
|
36
|
+
FOUNDEROS_RUNNER_URL Cloud base URL (https://founderos.fly.dev)
|
|
37
|
+
FOUNDEROS_RUNNER_TOKEN Bearer token (fos_<32 chars>)
|
|
38
|
+
FOUNDEROS_CLAUDE_BIN Path to claude CLI [claude]
|
|
39
|
+
FOUNDEROS_RUNNER_TIMEOUT_SEC Per-job timeout [600]
|
|
40
|
+
FOUNDEROS_RUNNER_LOG_LEVEL debug | info | warn | error [info]
|
|
41
|
+
|
|
42
|
+
EXAMPLES
|
|
43
|
+
# Flags (recommended on Windows PowerShell)
|
|
44
|
+
npx @founderos/runner start --token=fos_xxx --server-url=https://founderos.fly.dev
|
|
45
|
+
|
|
46
|
+
# macOS/Linux — env vars
|
|
47
|
+
export FOUNDEROS_RUNNER_TOKEN=fos_xxx
|
|
48
|
+
export FOUNDEROS_RUNNER_URL=https://founderos.fly.dev
|
|
49
|
+
founderos-runner start
|
|
50
|
+
|
|
51
|
+
The runner reads jobs queued by the FounderOS cloud, spawns claude under
|
|
52
|
+
your local subscription, and streams results back. See ADR-011 for the
|
|
53
|
+
full architecture.`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse `--key=value` and `--key value` flag forms. Returns parsed
|
|
57
|
+
* overrides + any unknown args (which we treat as errors today).
|
|
58
|
+
*
|
|
59
|
+
* Argv shape: caller has already stripped node + script + the "start"
|
|
60
|
+
* subcommand. Only flag tokens remain.
|
|
61
|
+
*/
|
|
62
|
+
export function parseFlags(argv) {
|
|
63
|
+
const overrides = {};
|
|
64
|
+
const unknown = [];
|
|
65
|
+
for (let i = 0; i < argv.length; i++) {
|
|
66
|
+
const tok = argv[i];
|
|
67
|
+
if (!tok.startsWith("--")) {
|
|
68
|
+
unknown.push(tok);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const eqIdx = tok.indexOf("=");
|
|
72
|
+
let key;
|
|
73
|
+
let value;
|
|
74
|
+
if (eqIdx >= 0) {
|
|
75
|
+
key = tok.slice(2, eqIdx);
|
|
76
|
+
value = tok.slice(eqIdx + 1);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
key = tok.slice(2);
|
|
80
|
+
const next = argv[i + 1];
|
|
81
|
+
if (next != null && !next.startsWith("--")) {
|
|
82
|
+
value = next;
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (value == null || value === "") {
|
|
87
|
+
unknown.push(tok);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
switch (key) {
|
|
91
|
+
case "token":
|
|
92
|
+
overrides.token = value;
|
|
93
|
+
break;
|
|
94
|
+
case "server-url":
|
|
95
|
+
overrides.serverUrl = value;
|
|
96
|
+
break;
|
|
97
|
+
case "claude-bin":
|
|
98
|
+
overrides.claudeBin = value;
|
|
99
|
+
break;
|
|
100
|
+
case "timeout-sec": {
|
|
101
|
+
const n = Number(value);
|
|
102
|
+
if (Number.isFinite(n))
|
|
103
|
+
overrides.defaultTimeoutSec = n;
|
|
104
|
+
else
|
|
105
|
+
unknown.push(tok);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case "log-level":
|
|
109
|
+
overrides.logLevel = value;
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
unknown.push(tok);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { overrides, unknown };
|
|
116
|
+
}
|
|
117
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
118
|
+
const cmd = argv[0] ?? "start";
|
|
119
|
+
if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
120
|
+
printHelp();
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
124
|
+
console.log(RUNNER_VERSION);
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
if (cmd !== "start") {
|
|
128
|
+
console.error(`unknown command: ${cmd}`);
|
|
129
|
+
printHelp();
|
|
130
|
+
return 64;
|
|
131
|
+
}
|
|
132
|
+
const { overrides, unknown } = parseFlags(argv.slice(1));
|
|
133
|
+
if (unknown.length > 0) {
|
|
134
|
+
console.error(`unknown or malformed flag(s): ${unknown.join(" ")}`);
|
|
135
|
+
printHelp();
|
|
136
|
+
return 64;
|
|
137
|
+
}
|
|
138
|
+
let config;
|
|
139
|
+
try {
|
|
140
|
+
config = loadConfig(process.env, overrides);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
if (err instanceof RunnerConfigError) {
|
|
144
|
+
console.error(`config error: ${err.message}`);
|
|
145
|
+
return 78; // EX_CONFIG
|
|
146
|
+
}
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
await runRunnerLoop({ config });
|
|
150
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Detect whether this module is being invoked directly (i.e. via the
|
|
154
|
+
* `bin` shim or `node dist/cli.js`) versus imported by tests.
|
|
155
|
+
*
|
|
156
|
+
* Naive equality `import.meta.url === \`file://\${process.argv[1]}\`` breaks
|
|
157
|
+
* under npm bin shims: argv[1] is the symlink path
|
|
158
|
+
* (`node_modules/.bin/founderos-runner`, no `.js` extension), while
|
|
159
|
+
* `import.meta.url` resolves to the symlink target. They never match.
|
|
160
|
+
* The fix: resolve argv[1] through `realpathSync` and compare against
|
|
161
|
+
* the URL converted to a filesystem path. Both sides end up as the
|
|
162
|
+
* same canonical path. Wrapped in try/catch because argv[1] can be
|
|
163
|
+
* undefined or non-existent in unusual harnesses.
|
|
164
|
+
*/
|
|
165
|
+
function isDirectInvocation() {
|
|
166
|
+
try {
|
|
167
|
+
const invokedPath = process.argv[1];
|
|
168
|
+
if (!invokedPath)
|
|
169
|
+
return false;
|
|
170
|
+
return realpathSync(invokedPath) === fileURLToPath(import.meta.url);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (isDirectInvocation()) {
|
|
177
|
+
main().then((code) => process.exit(code), (err) => {
|
|
178
|
+
console.error("fatal:", err);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner config loader. v0.1 — env-only. TOML file support deferred to v0.2.
|
|
3
|
+
*
|
|
4
|
+
* Required (no fallbacks; runner refuses to start without):
|
|
5
|
+
* FOUNDEROS_RUNNER_URL — cloud base URL, e.g. https://founderos.fly.dev
|
|
6
|
+
* FOUNDEROS_RUNNER_TOKEN — bearer token issued by the cloud, fos_<32>
|
|
7
|
+
*
|
|
8
|
+
* Optional:
|
|
9
|
+
* FOUNDEROS_CLAUDE_BIN — path to claude CLI (default: "claude" on PATH)
|
|
10
|
+
* FOUNDEROS_RUNNER_TIMEOUT_SEC — per-job ceiling, default 600
|
|
11
|
+
* FOUNDEROS_RUNNER_LOG_LEVEL — debug | info | warn | error (default: info)
|
|
12
|
+
*/
|
|
13
|
+
export interface RunnerConfig {
|
|
14
|
+
serverUrl: string;
|
|
15
|
+
token: string;
|
|
16
|
+
claudeBin: string;
|
|
17
|
+
defaultTimeoutSec: number;
|
|
18
|
+
logLevel: "debug" | "info" | "warn" | "error";
|
|
19
|
+
}
|
|
20
|
+
export declare class RunnerConfigError extends Error {
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Optional overrides for `loadConfig`. Used by the CLI to thread parsed
|
|
25
|
+
* `--token` / `--server-url` flags through. When a key is non-empty, it
|
|
26
|
+
* takes precedence over the matching env var. When omitted or empty,
|
|
27
|
+
* the env var is consulted.
|
|
28
|
+
*
|
|
29
|
+
* v0.1.1 (2026-05-07) — added so Windows PowerShell users don't have
|
|
30
|
+
* to fight `$env:FOUNDEROS_RUNNER_TOKEN="..."` to start the runner;
|
|
31
|
+
* `npx @founderos/runner start --token=fos_... --server-url=...`
|
|
32
|
+
* works inline.
|
|
33
|
+
*/
|
|
34
|
+
export interface RunnerConfigOverrides {
|
|
35
|
+
serverUrl?: string;
|
|
36
|
+
token?: string;
|
|
37
|
+
claudeBin?: string;
|
|
38
|
+
defaultTimeoutSec?: number;
|
|
39
|
+
logLevel?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read config from process.env, with optional flag-based overrides.
|
|
43
|
+
* Throws RunnerConfigError with a hint when a required var is missing
|
|
44
|
+
* or malformed — caller prints to stderr and exits.
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv, overrides?: RunnerConfigOverrides): RunnerConfig;
|
|
47
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C;AAED,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,SAAS,GAAE,qBAA0B,GACpC,YAAY,CA0Dd"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner config loader. v0.1 — env-only. TOML file support deferred to v0.2.
|
|
3
|
+
*
|
|
4
|
+
* Required (no fallbacks; runner refuses to start without):
|
|
5
|
+
* FOUNDEROS_RUNNER_URL — cloud base URL, e.g. https://founderos.fly.dev
|
|
6
|
+
* FOUNDEROS_RUNNER_TOKEN — bearer token issued by the cloud, fos_<32>
|
|
7
|
+
*
|
|
8
|
+
* Optional:
|
|
9
|
+
* FOUNDEROS_CLAUDE_BIN — path to claude CLI (default: "claude" on PATH)
|
|
10
|
+
* FOUNDEROS_RUNNER_TIMEOUT_SEC — per-job ceiling, default 600
|
|
11
|
+
* FOUNDEROS_RUNNER_LOG_LEVEL — debug | info | warn | error (default: info)
|
|
12
|
+
*/
|
|
13
|
+
const TOKEN_FORMAT = /^fos_[A-Za-z0-9]{32}$/;
|
|
14
|
+
export class RunnerConfigError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "RunnerConfigError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read config from process.env, with optional flag-based overrides.
|
|
22
|
+
* Throws RunnerConfigError with a hint when a required var is missing
|
|
23
|
+
* or malformed — caller prints to stderr and exits.
|
|
24
|
+
*/
|
|
25
|
+
export function loadConfig(env = process.env, overrides = {}) {
|
|
26
|
+
const serverUrl = (overrides.serverUrl ?? env.FOUNDEROS_RUNNER_URL ?? "").trim();
|
|
27
|
+
if (!serverUrl) {
|
|
28
|
+
throw new RunnerConfigError("FOUNDEROS_RUNNER_URL is required. Example: export FOUNDEROS_RUNNER_URL=https://founderos.fly.dev");
|
|
29
|
+
}
|
|
30
|
+
// Reject anything that isn't a valid http/https URL — keeps log/argv
|
|
31
|
+
// injection vectors out of the request path.
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = new URL(serverUrl);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new RunnerConfigError(`FOUNDEROS_RUNNER_URL is not a valid URL: ${serverUrl}`);
|
|
38
|
+
}
|
|
39
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
40
|
+
throw new RunnerConfigError(`FOUNDEROS_RUNNER_URL must be http(s); got ${parsed.protocol}`);
|
|
41
|
+
}
|
|
42
|
+
const token = (overrides.token ?? env.FOUNDEROS_RUNNER_TOKEN ?? "").trim();
|
|
43
|
+
if (!TOKEN_FORMAT.test(token)) {
|
|
44
|
+
throw new RunnerConfigError("FOUNDEROS_RUNNER_TOKEN is required and must match fos_<32 alphanumeric>. Issue one from the FounderOS dashboard.");
|
|
45
|
+
}
|
|
46
|
+
const claudeBin = (overrides.claudeBin ?? env.FOUNDEROS_CLAUDE_BIN ?? "claude").trim();
|
|
47
|
+
const rawTimeout = overrides.defaultTimeoutSec != null
|
|
48
|
+
? String(overrides.defaultTimeoutSec)
|
|
49
|
+
: (env.FOUNDEROS_RUNNER_TIMEOUT_SEC ?? "").trim();
|
|
50
|
+
const defaultTimeoutSec = rawTimeout ? Number(rawTimeout) : 600;
|
|
51
|
+
if (!Number.isFinite(defaultTimeoutSec) || defaultTimeoutSec < 1 || defaultTimeoutSec > 3600) {
|
|
52
|
+
throw new RunnerConfigError(`FOUNDEROS_RUNNER_TIMEOUT_SEC must be 1..3600 seconds; got ${rawTimeout || "(empty)"}`);
|
|
53
|
+
}
|
|
54
|
+
const rawLevel = (overrides.logLevel ?? env.FOUNDEROS_RUNNER_LOG_LEVEL ?? "info").trim().toLowerCase();
|
|
55
|
+
if (rawLevel !== "debug" && rawLevel !== "info" && rawLevel !== "warn" && rawLevel !== "error") {
|
|
56
|
+
throw new RunnerConfigError(`FOUNDEROS_RUNNER_LOG_LEVEL must be debug|info|warn|error; got ${rawLevel}`);
|
|
57
|
+
}
|
|
58
|
+
// Trim trailing slash so route concatenation is predictable.
|
|
59
|
+
const normalizedUrl = serverUrl.replace(/\/+$/, "");
|
|
60
|
+
return {
|
|
61
|
+
serverUrl: normalizedUrl,
|
|
62
|
+
token,
|
|
63
|
+
claudeBin,
|
|
64
|
+
defaultTimeoutSec,
|
|
65
|
+
logLevel: rawLevel,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAU7C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAqBD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,MAAyB,OAAO,CAAC,GAAG,EACpC,YAAmC,EAAE;IAErC,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,SAAS,IAAI,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACjF,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CACzB,kGAAkG,CACnG,CAAC;IACJ,CAAC;IACD,qEAAqE;IACrE,6CAA6C;IAC7C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,iBAAiB,CAAC,4CAA4C,SAAS,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChE,MAAM,IAAI,iBAAiB,CACzB,6CAA6C,MAAM,CAAC,QAAQ,EAAE,CAC/D,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,SAAS,CAAC,KAAK,IAAI,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3E,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,iBAAiB,CACzB,kHAAkH,CACnH,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,SAAS,IAAI,GAAG,CAAC,oBAAoB,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAEvF,MAAM,UAAU,GACd,SAAS,CAAC,iBAAiB,IAAI,IAAI;QACjC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,CAAC;QACrC,CAAC,CAAC,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,iBAAiB,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,iBAAiB,GAAG,CAAC,IAAI,iBAAiB,GAAG,IAAI,EAAE,CAAC;QAC7F,MAAM,IAAI,iBAAiB,CACzB,6DAA6D,UAAU,IAAI,SAAS,EAAE,CACvF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,QAAQ,IAAI,GAAG,CAAC,0BAA0B,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvG,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC/F,MAAM,IAAI,iBAAiB,CACzB,iEAAiE,QAAQ,EAAE,CAC5E,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEpD,OAAO;QACL,SAAS,EAAE,aAAa;QACxB,KAAK;QACL,SAAS;QACT,iBAAiB;QACjB,QAAQ,EAAE,QAAQ;KACnB,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API. Most consumers use the CLI; programmatic access is available
|
|
3
|
+
* for embedded test harnesses or wrappers.
|
|
4
|
+
*/
|
|
5
|
+
export { loadConfig, RunnerConfigError, type RunnerConfig, type RunnerConfigOverrides, } from "./config.js";
|
|
6
|
+
export { RunnerApiClient, ApiError, type RunnerEvent, type JobPayload, type JobDescriptor } from "./api.js";
|
|
7
|
+
export { runClaude, parseStreamJsonLine, buildClaudeArgs } from "./spawn.js";
|
|
8
|
+
export { runRunnerLoop, consoleLogger, type RunnerLogger, type RunnerLoopOptions } from "./main.js";
|
|
9
|
+
export { RUNNER_VERSION } from "./version.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API. Most consumers use the CLI; programmatic access is available
|
|
3
|
+
* for embedded test harnesses or wrappers.
|
|
4
|
+
*/
|
|
5
|
+
export { loadConfig, RunnerConfigError, } from "./config.js";
|
|
6
|
+
export { RunnerApiClient, ApiError } from "./api.js";
|
|
7
|
+
export { runClaude, parseStreamJsonLine, buildClaudeArgs } from "./spawn.js";
|
|
8
|
+
export { runRunnerLoop, consoleLogger } from "./main.js";
|
|
9
|
+
export { RUNNER_VERSION } from "./version.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,UAAU,EACV,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAyD,MAAM,UAAU,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,aAAa,EAA6C,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main poll loop. Coordinates: long-poll → claim → spawn → events → complete.
|
|
3
|
+
*
|
|
4
|
+
* Concurrency: v0.1 is single-threaded. One job at a time per runner. A
|
|
5
|
+
* founder running multiple agents in parallel can run multiple `founderos-runner`
|
|
6
|
+
* processes against the same token (lastSeenAt is debounced server-side) or
|
|
7
|
+
* issue one token per machine. v0.2 may add intra-process concurrency.
|
|
8
|
+
*
|
|
9
|
+
* Backoff:
|
|
10
|
+
* - On API errors during the long-poll, exponential backoff (1s → 30s cap)
|
|
11
|
+
* so a 500-erroring server doesn't get hammered.
|
|
12
|
+
* - On 401 (token revoked / invalid), we exit non-zero so a supervisor
|
|
13
|
+
* (systemd, launchd) can surface the auth failure to the operator.
|
|
14
|
+
*/
|
|
15
|
+
import { RunnerApiClient, makeEventId } from "./api.js";
|
|
16
|
+
import type { RunnerConfig } from "./config.js";
|
|
17
|
+
import { runClaude } from "./spawn.js";
|
|
18
|
+
export interface RunnerLogger {
|
|
19
|
+
debug: (msg: string, extra?: Record<string, unknown>) => void;
|
|
20
|
+
info: (msg: string, extra?: Record<string, unknown>) => void;
|
|
21
|
+
warn: (msg: string, extra?: Record<string, unknown>) => void;
|
|
22
|
+
error: (msg: string, extra?: Record<string, unknown>) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function consoleLogger(level: RunnerConfig["logLevel"]): RunnerLogger;
|
|
25
|
+
export interface RunnerLoopOptions {
|
|
26
|
+
config: RunnerConfig;
|
|
27
|
+
logger?: RunnerLogger;
|
|
28
|
+
/** Stop after N jobs (test seam). 0 / undefined = run forever. */
|
|
29
|
+
maxJobs?: number;
|
|
30
|
+
/** Test seam: substitute the API client. */
|
|
31
|
+
apiClient?: RunnerApiClient;
|
|
32
|
+
/** Test seam: substitute the spawner. Receives the same args as runClaude. */
|
|
33
|
+
spawnFn?: typeof runClaude;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run the polling loop. Returns when stopped (signal or maxJobs reached).
|
|
37
|
+
*/
|
|
38
|
+
export declare function runRunnerLoop(opts: RunnerLoopOptions): Promise<{
|
|
39
|
+
jobsProcessed: number;
|
|
40
|
+
}>;
|
|
41
|
+
export { makeEventId };
|
|
42
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAY,eAAe,EAAE,WAAW,EAAyC,MAAM,UAAU,CAAC;AACzG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC/D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,UAAU,CAAC,GAAG,YAAY,CAW3E;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,SAAS,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA8E/F;AAyJD,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main poll loop. Coordinates: long-poll → claim → spawn → events → complete.
|
|
3
|
+
*
|
|
4
|
+
* Concurrency: v0.1 is single-threaded. One job at a time per runner. A
|
|
5
|
+
* founder running multiple agents in parallel can run multiple `founderos-runner`
|
|
6
|
+
* processes against the same token (lastSeenAt is debounced server-side) or
|
|
7
|
+
* issue one token per machine. v0.2 may add intra-process concurrency.
|
|
8
|
+
*
|
|
9
|
+
* Backoff:
|
|
10
|
+
* - On API errors during the long-poll, exponential backoff (1s → 30s cap)
|
|
11
|
+
* so a 500-erroring server doesn't get hammered.
|
|
12
|
+
* - On 401 (token revoked / invalid), we exit non-zero so a supervisor
|
|
13
|
+
* (systemd, launchd) can surface the auth failure to the operator.
|
|
14
|
+
*/
|
|
15
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
16
|
+
import { ApiError, RunnerApiClient, makeEventId } from "./api.js";
|
|
17
|
+
import { RUNNER_VERSION } from "./version.js";
|
|
18
|
+
import { runClaude } from "./spawn.js";
|
|
19
|
+
export function consoleLogger(level) {
|
|
20
|
+
const order = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
21
|
+
const min = order[level];
|
|
22
|
+
const fmt = (m, e) => e && Object.keys(e).length > 0 ? `${m} ${JSON.stringify(e)}` : m;
|
|
23
|
+
return {
|
|
24
|
+
debug: (m, e) => order.debug >= min && console.log(`[debug] ${fmt(m, e)}`),
|
|
25
|
+
info: (m, e) => order.info >= min && console.log(`[info ] ${fmt(m, e)}`),
|
|
26
|
+
warn: (m, e) => order.warn >= min && console.warn(`[warn ] ${fmt(m, e)}`),
|
|
27
|
+
error: (m, e) => order.error >= min && console.error(`[error] ${fmt(m, e)}`),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run the polling loop. Returns when stopped (signal or maxJobs reached).
|
|
32
|
+
*/
|
|
33
|
+
export async function runRunnerLoop(opts) {
|
|
34
|
+
const { config, maxJobs } = opts;
|
|
35
|
+
const logger = opts.logger ?? consoleLogger(config.logLevel);
|
|
36
|
+
const api = opts.apiClient ?? new RunnerApiClient(config);
|
|
37
|
+
const spawnImpl = opts.spawnFn ?? runClaude;
|
|
38
|
+
let jobsProcessed = 0;
|
|
39
|
+
let backoffMs = 0;
|
|
40
|
+
const maxBackoffMs = 30_000;
|
|
41
|
+
// Ctrl-C / SIGTERM gracefully stops at the next loop boundary.
|
|
42
|
+
let stopping = false;
|
|
43
|
+
const onSignal = () => {
|
|
44
|
+
if (stopping)
|
|
45
|
+
return;
|
|
46
|
+
stopping = true;
|
|
47
|
+
logger.info("received stop signal — finishing current job then exiting");
|
|
48
|
+
};
|
|
49
|
+
process.on("SIGINT", onSignal);
|
|
50
|
+
process.on("SIGTERM", onSignal);
|
|
51
|
+
logger.info(`founderos-runner v${RUNNER_VERSION} starting`, {
|
|
52
|
+
serverUrl: config.serverUrl,
|
|
53
|
+
claudeBin: config.claudeBin,
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
while (!stopping && (!maxJobs || jobsProcessed < maxJobs)) {
|
|
57
|
+
if (backoffMs > 0)
|
|
58
|
+
await sleep(backoffMs);
|
|
59
|
+
let next;
|
|
60
|
+
try {
|
|
61
|
+
next = await api.getNext();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
65
|
+
logger.error("auth rejected — token revoked or invalid", { status: err.status });
|
|
66
|
+
process.exitCode = 2;
|
|
67
|
+
return { jobsProcessed };
|
|
68
|
+
}
|
|
69
|
+
backoffMs = Math.min(maxBackoffMs, backoffMs > 0 ? backoffMs * 2 : 1_000);
|
|
70
|
+
logger.warn("getNext failed; backing off", { err: String(err), backoffMs });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
backoffMs = 0;
|
|
74
|
+
if (next.kind === "empty") {
|
|
75
|
+
// 30s timeout from server; immediate re-poll.
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const { jobId, agentName } = next.job;
|
|
79
|
+
logger.info("got job", { jobId, agent: agentName });
|
|
80
|
+
let claim;
|
|
81
|
+
try {
|
|
82
|
+
claim = await api.claim(jobId);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.warn("claim failed; will re-poll", { jobId, err: String(err) });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (claim.kind === "lost") {
|
|
89
|
+
logger.info("claim race lost; re-polling", { jobId });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (claim.kind === "gone") {
|
|
93
|
+
logger.info("job gone before claim", { jobId });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
jobsProcessed += 1;
|
|
97
|
+
await runOneJob({ payload: claim.payload, api, config, logger, spawnImpl });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
process.off("SIGINT", onSignal);
|
|
102
|
+
process.off("SIGTERM", onSignal);
|
|
103
|
+
}
|
|
104
|
+
logger.info("runner stopped", { jobsProcessed });
|
|
105
|
+
return { jobsProcessed };
|
|
106
|
+
}
|
|
107
|
+
async function runOneJob(input) {
|
|
108
|
+
const { payload, api, config, logger, spawnImpl } = input;
|
|
109
|
+
const jobId = payload.jobId;
|
|
110
|
+
// Buffered event shipper. Drains every 50ms or when the buffer hits 32 events.
|
|
111
|
+
const queue = [];
|
|
112
|
+
let flushTimer = null;
|
|
113
|
+
let terminalReachedOnServer = false;
|
|
114
|
+
const flush = async () => {
|
|
115
|
+
if (queue.length === 0)
|
|
116
|
+
return;
|
|
117
|
+
const batch = queue.splice(0, queue.length);
|
|
118
|
+
try {
|
|
119
|
+
const res = await api.appendEvents(jobId, batch);
|
|
120
|
+
if (res.kind === "terminal") {
|
|
121
|
+
terminalReachedOnServer = true;
|
|
122
|
+
logger.warn("server reports job already terminal; stopping event ship", { jobId });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
logger.warn("appendEvents failed; dropping batch", { jobId, batchSize: batch.length, err: String(err) });
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const scheduleFlush = () => {
|
|
130
|
+
if (flushTimer)
|
|
131
|
+
return;
|
|
132
|
+
flushTimer = setTimeout(() => {
|
|
133
|
+
flushTimer = null;
|
|
134
|
+
void flush();
|
|
135
|
+
}, 50);
|
|
136
|
+
};
|
|
137
|
+
const onEvent = async (evt) => {
|
|
138
|
+
if (terminalReachedOnServer)
|
|
139
|
+
return;
|
|
140
|
+
queue.push(evt);
|
|
141
|
+
if (queue.length >= 32) {
|
|
142
|
+
if (flushTimer) {
|
|
143
|
+
clearTimeout(flushTimer);
|
|
144
|
+
flushTimer = null;
|
|
145
|
+
}
|
|
146
|
+
await flush();
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
scheduleFlush();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
// Pull instructions out of the runtime config. Server may have base64-encoded
|
|
153
|
+
// an agent system-prompt file; runClaude materializes it to a tempfile.
|
|
154
|
+
const rt = payload.runtimeConfig ?? {};
|
|
155
|
+
const instructionsBase64 = typeof rt.instructionsFileContent === "string" ? rt.instructionsFileContent : null;
|
|
156
|
+
const timeoutSec = typeof rt.timeoutSec === "number" && rt.timeoutSec > 0
|
|
157
|
+
? rt.timeoutSec
|
|
158
|
+
: config.defaultTimeoutSec;
|
|
159
|
+
let result = null;
|
|
160
|
+
let errorMessage = null;
|
|
161
|
+
try {
|
|
162
|
+
result = await spawnImpl({
|
|
163
|
+
binary: config.claudeBin,
|
|
164
|
+
prompt: payload.prompt,
|
|
165
|
+
sessionId: payload.sessionId ?? null,
|
|
166
|
+
model: typeof rt.model === "string" ? rt.model : null,
|
|
167
|
+
maxTurns: typeof rt.maxTurns === "number" ? rt.maxTurns : null,
|
|
168
|
+
instructionsBase64,
|
|
169
|
+
timeoutSec,
|
|
170
|
+
addDirs: payload.addDirs,
|
|
171
|
+
}, { onEvent });
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
errorMessage = err instanceof Error ? err.message : String(err);
|
|
175
|
+
logger.error("spawn failed", { jobId, err: errorMessage });
|
|
176
|
+
}
|
|
177
|
+
// Drain any pending events before completing.
|
|
178
|
+
if (flushTimer) {
|
|
179
|
+
clearTimeout(flushTimer);
|
|
180
|
+
flushTimer = null;
|
|
181
|
+
}
|
|
182
|
+
await flush();
|
|
183
|
+
const cliVersion = await readClaudeVersion(config).catch(() => "unknown");
|
|
184
|
+
let body;
|
|
185
|
+
if (result) {
|
|
186
|
+
const status = result.timedOut
|
|
187
|
+
? "failed"
|
|
188
|
+
: result.exitCode === 0
|
|
189
|
+
? "completed"
|
|
190
|
+
: "failed";
|
|
191
|
+
body = {
|
|
192
|
+
status,
|
|
193
|
+
exitCode: result.exitCode,
|
|
194
|
+
signal: result.signal,
|
|
195
|
+
elapsedSec: result.elapsedSec,
|
|
196
|
+
costMicros: typeof result.finalResult?.costUsd === "number"
|
|
197
|
+
? Math.round(result.finalResult.costUsd * 1_000_000)
|
|
198
|
+
: 0,
|
|
199
|
+
sessionId: result.finalResult?.sessionId ?? null,
|
|
200
|
+
cliVersion,
|
|
201
|
+
errorMessage: result.timedOut ? "runner timed out" : null,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
body = {
|
|
206
|
+
status: "failed",
|
|
207
|
+
exitCode: -1,
|
|
208
|
+
signal: null,
|
|
209
|
+
elapsedSec: 0,
|
|
210
|
+
costMicros: 0,
|
|
211
|
+
sessionId: null,
|
|
212
|
+
cliVersion,
|
|
213
|
+
errorMessage: errorMessage ?? "spawn failed",
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const completeRes = await api.complete(jobId, body);
|
|
218
|
+
if (completeRes.kind === "already_terminal") {
|
|
219
|
+
logger.warn("complete returned 409 already_terminal; the cloud beat us to it", { jobId });
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
logger.info("job completed", { jobId, status: body.status, exitCode: body.exitCode });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
logger.error("complete failed", { jobId, err: String(err) });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function readClaudeVersion(config) {
|
|
230
|
+
// Best-effort. Use child_process synchronously to avoid extra plumbing on
|
|
231
|
+
// the hot path; the runner only reads this once per job.
|
|
232
|
+
const { spawnSync } = await import("node:child_process");
|
|
233
|
+
const out = spawnSync(config.claudeBin, ["--version"], { encoding: "utf-8" });
|
|
234
|
+
return (out.stdout ?? out.stderr ?? "").trim() || "unknown";
|
|
235
|
+
}
|
|
236
|
+
export { makeEventId };
|
|
237
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|