@cat-factory/local-server 0.14.2 → 0.16.0

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.
Files changed (42) hide show
  1. package/dist/LocalContainerRunnerTransport.d.ts +97 -9
  2. package/dist/LocalContainerRunnerTransport.d.ts.map +1 -1
  3. package/dist/LocalContainerRunnerTransport.js +355 -93
  4. package/dist/LocalContainerRunnerTransport.js.map +1 -1
  5. package/dist/LocalProcessRunnerTransport.d.ts +63 -0
  6. package/dist/LocalProcessRunnerTransport.d.ts.map +1 -0
  7. package/dist/LocalProcessRunnerTransport.js +188 -0
  8. package/dist/LocalProcessRunnerTransport.js.map +1 -0
  9. package/dist/NativeRoutingRunnerTransport.d.ts +20 -0
  10. package/dist/NativeRoutingRunnerTransport.d.ts.map +1 -0
  11. package/dist/NativeRoutingRunnerTransport.js +50 -0
  12. package/dist/NativeRoutingRunnerTransport.js.map +1 -0
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +6 -0
  15. package/dist/config.js.map +1 -1
  16. package/dist/container.d.ts +13 -0
  17. package/dist/container.d.ts.map +1 -1
  18. package/dist/container.js +244 -17
  19. package/dist/container.js.map +1 -1
  20. package/dist/harnessHttp.d.ts +58 -0
  21. package/dist/harnessHttp.d.ts.map +1 -0
  22. package/dist/harnessHttp.js +95 -0
  23. package/dist/harnessHttp.js.map +1 -0
  24. package/dist/runtimes/appleContainerRuntime.d.ts +3 -0
  25. package/dist/runtimes/appleContainerRuntime.d.ts.map +1 -1
  26. package/dist/runtimes/appleContainerRuntime.js +8 -1
  27. package/dist/runtimes/appleContainerRuntime.js.map +1 -1
  28. package/dist/runtimes/containerRuntime.d.ts +30 -1
  29. package/dist/runtimes/containerRuntime.d.ts.map +1 -1
  30. package/dist/runtimes/containerRuntime.js +5 -0
  31. package/dist/runtimes/containerRuntime.js.map +1 -1
  32. package/dist/runtimes/dockerRuntime.d.ts +4 -0
  33. package/dist/runtimes/dockerRuntime.d.ts.map +1 -1
  34. package/dist/runtimes/dockerRuntime.js +21 -2
  35. package/dist/runtimes/dockerRuntime.js.map +1 -1
  36. package/dist/runtimes/index.d.ts.map +1 -1
  37. package/dist/runtimes/index.js +1 -0
  38. package/dist/runtimes/index.js.map +1 -1
  39. package/dist/server.d.ts.map +1 -1
  40. package/dist/server.js +4 -14
  41. package/dist/server.js.map +1 -1
  42. package/package.json +9 -8
@@ -0,0 +1,63 @@
1
+ import { spawn } from 'node:child_process';
2
+ import type { RunnerDispatchKind, RunnerJobRef, RunnerJobView, RunnerTransport } from '@cat-factory/kernel';
3
+ export interface LocalProcessRunnerTransportOptions {
4
+ /**
5
+ * Path to the executor-harness HTTP server entry (its `server.js`/`server.ts`). Spawned
6
+ * as `node <entry>`; with a `.ts` entry, Node's type-stripping (Node 24+) runs it.
7
+ */
8
+ harnessEntry: string;
9
+ /** Node executable to spawn the harness with. Default `process.execPath`. */
10
+ nodePath?: string;
11
+ /** Extra args to pass to node before the entry (e.g. `--experimental-strip-types`). */
12
+ nodeArgs?: string[];
13
+ /** Shared secret injected as `HARNESS_SHARED_SECRET` + sent on every call. Default random. */
14
+ sharedSecret?: string;
15
+ /** Extra env for the harness process (e.g. GITHUB_ALLOWED_HOSTS). */
16
+ env?: Record<string, string>;
17
+ /** Injectable fetch — defaults to the global. */
18
+ fetchImpl?: typeof fetch;
19
+ /** Injectable spawn — defaults to node:child_process.spawn (overridable in tests). */
20
+ spawnImpl?: typeof spawn;
21
+ /** Injectable free-port picker — defaults to an ephemeral OS port (overridable in tests). */
22
+ pickPort?: () => Promise<number>;
23
+ /** How long to wait for the harness `/health` after spawn. Default 30s. */
24
+ readyTimeoutMs?: number;
25
+ /** Per-HTTP-call timeout. Default 30s. */
26
+ requestTimeoutMs?: number;
27
+ }
28
+ export declare class LocalProcessRunnerTransport implements RunnerTransport {
29
+ private readonly harnessEntry;
30
+ private readonly nodePath;
31
+ private readonly nodeArgs;
32
+ private readonly sharedSecret;
33
+ private readonly extraEnv;
34
+ private readonly fetchImpl;
35
+ private readonly spawnImpl;
36
+ private readonly pickPort;
37
+ private readonly readyTimeoutMs;
38
+ private readonly requestTimeoutMs;
39
+ /** The single long-lived harness process, started lazily and reused across all runs. */
40
+ private proc;
41
+ private starting;
42
+ constructor(options: LocalProcessRunnerTransportOptions);
43
+ dispatch(ref: RunnerJobRef, spec: Record<string, unknown>, kind?: RunnerDispatchKind): Promise<void>;
44
+ poll(ref: RunnerJobRef): Promise<RunnerJobView>;
45
+ /**
46
+ * No per-run teardown: the harness host process is long-lived and reused across runs
47
+ * (the harness already removes each job's ephemeral workspace itself). Provided so the
48
+ * port contract is satisfied; kept idempotent.
49
+ */
50
+ release(): Promise<void>;
51
+ /** Stop the harness process (for shutdown / tests). Idempotent. */
52
+ shutdown(): Promise<void>;
53
+ private ensureProcess;
54
+ private startProcess;
55
+ private waitForHealth;
56
+ }
57
+ /**
58
+ * Build a {@link LocalProcessRunnerTransport} from the environment. Requires
59
+ * `LOCAL_HARNESS_ENTRY` (the path to the executor-harness server entry to run as a host
60
+ * process). The native CLIs (`claude` / `codex`) must already be installed on the host.
61
+ */
62
+ export declare function createLocalProcessTransportFromEnv(env: NodeJS.ProcessEnv): LocalProcessRunnerTransport;
63
+ //# sourceMappingURL=LocalProcessRunnerTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalProcessRunnerTransport.d.ts","sourceRoot":"","sources":["../src/LocalProcessRunnerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAG7D,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AA0B5B,MAAM,WAAW,kCAAkC;IACjD;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAChC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAeD,qBAAa,2BAA4B,YAAW,eAAe;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAEzC,wFAAwF;IACxF,OAAO,CAAC,IAAI,CAAoE;IAChF,OAAO,CAAC,QAAQ,CAA6E;IAE7F,YAAY,OAAO,EAAE,kCAAkC,EAWtD;IAEK,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA4B,GACjC,OAAO,CAAC,IAAI,CAAC,CAYf;IAEK,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAapD;IAED;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7B;IAED,mEAAmE;IAC7D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAK9B;YAIa,aAAa;YAWb,YAAY;IAoC1B,OAAO,CAAC,aAAa;CAYtB;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAChD,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,2BAA2B,CAmB7B"}
@@ -0,0 +1,188 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { createServer } from 'node:net';
4
+ import { EVICTION_ERROR, pollHarnessJob, postHarnessJob, waitForHarnessHealth, } from './harnessHttp.js';
5
+ // The NATIVE local runner backend (opt-in via `LOCAL_NATIVE_AGENTS`): instead of a Docker
6
+ // container per run, it runs the SAME executor-harness as a long-lived HOST PROCESS on
7
+ // 127.0.0.1 and drives it through the harness's existing HTTP API. So all the harness
8
+ // machinery — git clone/push/PR, structured-output, watchdogs, the JobRegistry, progress —
9
+ // is reused unchanged; the only difference from the container transport is WHERE the harness
10
+ // runs (a host `node` process vs a container) and that the agent uses the developer's OWN
11
+ // installed `claude` / `codex` CLI with its ambient login (the executor sets `ambientAuth`
12
+ // on the job, so no credential is leased). This bypasses Docker entirely.
13
+ //
14
+ // SECURITY: the agent runs as a plain host subprocess with the developer's full shell/file
15
+ // access and their personal subscription — no container sandbox, no spend metering, no
16
+ // model-locking. Acceptable ONLY because local mode is the developer's own machine; it is
17
+ // therefore opt-in (default off) and reachable only from `buildLocalContainer`.
18
+ /** The harness is always on loopback for the native host-process transport. */
19
+ const endpointFor = (port) => ({ host: '127.0.0.1', port });
20
+ /** An ephemeral free localhost port (best-effort; a tiny TOCTOU window is fine for dev). */
21
+ function ephemeralPort() {
22
+ return new Promise((resolve, reject) => {
23
+ const srv = createServer();
24
+ srv.on('error', reject);
25
+ srv.listen(0, '127.0.0.1', () => {
26
+ const addr = srv.address();
27
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
28
+ srv.close(() => (port ? resolve(port) : reject(new Error('could not pick a free port'))));
29
+ });
30
+ });
31
+ }
32
+ export class LocalProcessRunnerTransport {
33
+ harnessEntry;
34
+ nodePath;
35
+ nodeArgs;
36
+ sharedSecret;
37
+ extraEnv;
38
+ fetchImpl;
39
+ spawnImpl;
40
+ pickPort;
41
+ readyTimeoutMs;
42
+ requestTimeoutMs;
43
+ /** The single long-lived harness process, started lazily and reused across all runs. */
44
+ proc;
45
+ starting;
46
+ constructor(options) {
47
+ this.harnessEntry = options.harnessEntry;
48
+ this.nodePath = options.nodePath ?? process.execPath;
49
+ this.nodeArgs = options.nodeArgs ?? [];
50
+ this.sharedSecret = options.sharedSecret ?? randomBytes(24).toString('hex');
51
+ this.extraEnv = options.env ?? {};
52
+ this.fetchImpl = options.fetchImpl ?? fetch;
53
+ this.spawnImpl = options.spawnImpl ?? spawn;
54
+ this.pickPort = options.pickPort ?? ephemeralPort;
55
+ this.readyTimeoutMs = options.readyTimeoutMs ?? 30_000;
56
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
57
+ }
58
+ async dispatch(ref, spec, kind = 'agent') {
59
+ const proc = await this.ensureProcess();
60
+ // The harness keys jobs by the per-step `ref.jobId` in the body; a re-dispatch
61
+ // (durable-driver replay) re-POSTs, which the JobRegistry treats as a re-attach.
62
+ await postHarnessJob({
63
+ fetchImpl: this.fetchImpl,
64
+ endpoint: endpointFor(proc.port),
65
+ secret: this.sharedSecret,
66
+ body: { ...spec, kind },
67
+ timeoutMs: this.requestTimeoutMs,
68
+ label: 'Native harness',
69
+ });
70
+ }
71
+ async poll(ref) {
72
+ const proc = this.proc;
73
+ // The process died (or was never started) → report an eviction so the run can recover.
74
+ if (!proc || proc.exited)
75
+ return { state: 'failed', error: EVICTION_ERROR };
76
+ return pollHarnessJob({
77
+ fetchImpl: this.fetchImpl,
78
+ endpoint: endpointFor(proc.port),
79
+ jobId: ref.jobId,
80
+ secret: this.sharedSecret,
81
+ timeoutMs: this.requestTimeoutMs,
82
+ label: 'Native harness',
83
+ isDead: () => proc.exited,
84
+ });
85
+ }
86
+ /**
87
+ * No per-run teardown: the harness host process is long-lived and reused across runs
88
+ * (the harness already removes each job's ephemeral workspace itself). Provided so the
89
+ * port contract is satisfied; kept idempotent.
90
+ */
91
+ async release() {
92
+ // intentionally a no-op
93
+ }
94
+ /** Stop the harness process (for shutdown / tests). Idempotent. */
95
+ async shutdown() {
96
+ const proc = this.proc;
97
+ this.proc = undefined;
98
+ this.starting = undefined;
99
+ if (proc && !proc.exited)
100
+ proc.child.kill();
101
+ }
102
+ // --- internals ----------------------------------------------------------
103
+ async ensureProcess() {
104
+ if (this.proc && !this.proc.exited)
105
+ return this.proc;
106
+ this.starting ??= this.startProcess();
107
+ try {
108
+ this.proc = await this.starting;
109
+ return this.proc;
110
+ }
111
+ finally {
112
+ this.starting = undefined;
113
+ }
114
+ }
115
+ async startProcess() {
116
+ const port = await this.pickPort();
117
+ const child = this.spawnImpl(this.nodePath, [...this.nodeArgs, this.harnessEntry], {
118
+ env: {
119
+ ...process.env,
120
+ ...this.extraEnv,
121
+ PORT: String(port),
122
+ HARNESS_SHARED_SECRET: this.sharedSecret,
123
+ // The harness only auto-listens when NODE_ENV !== 'test'.
124
+ NODE_ENV: 'production',
125
+ },
126
+ stdio: 'ignore',
127
+ });
128
+ const handle = { child, port, exited: false };
129
+ // The harness child is long-lived and not detached, but Node does NOT auto-kill a
130
+ // child when the parent exits — without this, every dev restart orphans a `node
131
+ // <harness>` process still bound to its port (and possibly mid-run on the developer's
132
+ // live Claude/Codex login). `shutdown()` covers the graceful path; this `exit` hook is
133
+ // the backstop for SIGTERM/SIGINT/uncaught exits that reach `process.exit` directly.
134
+ const killOnParentExit = () => {
135
+ try {
136
+ child.kill();
137
+ }
138
+ catch {
139
+ // best-effort
140
+ }
141
+ };
142
+ process.once('exit', killOnParentExit);
143
+ child.on('exit', () => {
144
+ handle.exited = true;
145
+ process.removeListener('exit', killOnParentExit);
146
+ if (this.proc === handle)
147
+ this.proc = undefined;
148
+ });
149
+ await this.waitForHealth(port, handle);
150
+ return handle;
151
+ }
152
+ waitForHealth(port, handle) {
153
+ return waitForHarnessHealth({
154
+ fetchImpl: this.fetchImpl,
155
+ endpoint: endpointFor(port),
156
+ readyTimeoutMs: this.readyTimeoutMs,
157
+ requestTimeoutMs: this.requestTimeoutMs,
158
+ intervalMs: 200,
159
+ isDead: () => handle.exited,
160
+ deadError: 'the native harness process exited before becoming healthy',
161
+ timeoutError: `Timed out waiting for the native harness on 127.0.0.1:${port} to become healthy`,
162
+ });
163
+ }
164
+ }
165
+ /**
166
+ * Build a {@link LocalProcessRunnerTransport} from the environment. Requires
167
+ * `LOCAL_HARNESS_ENTRY` (the path to the executor-harness server entry to run as a host
168
+ * process). The native CLIs (`claude` / `codex`) must already be installed on the host.
169
+ */
170
+ export function createLocalProcessTransportFromEnv(env) {
171
+ const harnessEntry = env.LOCAL_HARNESS_ENTRY?.trim();
172
+ if (!harnessEntry) {
173
+ throw new Error('LOCAL_HARNESS_ENTRY is required for native local mode (LOCAL_NATIVE_AGENTS): set it to ' +
174
+ 'the executor-harness server entry path (its built server.js, or src/server.ts run via ' +
175
+ 'Node type-stripping).');
176
+ }
177
+ const nodeArgs = env.LOCAL_HARNESS_NODE_ARGS?.trim()
178
+ ? env.LOCAL_HARNESS_NODE_ARGS.trim().split(/\s+/)
179
+ : undefined;
180
+ const allowedHosts = env.GITHUB_ALLOWED_HOSTS?.trim();
181
+ return new LocalProcessRunnerTransport({
182
+ harnessEntry,
183
+ ...(nodeArgs ? { nodeArgs } : {}),
184
+ sharedSecret: env.HARNESS_SHARED_SECRET?.trim() || undefined,
185
+ ...(allowedHosts ? { env: { GITHUB_ALLOWED_HOSTS: allowedHosts } } : {}),
186
+ });
187
+ }
188
+ //# sourceMappingURL=LocalProcessRunnerTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalProcessRunnerTransport.js","sourceRoot":"","sources":["../src/LocalProcessRunnerTransport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { RunnerDispatchKind, RunnerDispatchOptions, RunnerJobRef, RunnerJobView, RunnerTransport } from '@cat-factory/kernel';
2
+ export declare class NativeRoutingRunnerTransport implements RunnerTransport {
3
+ /** Host-process transport for ambient (native CLI) steps — built lazily, cached. */
4
+ private readonly ambient;
5
+ /** Per-run container transport for everything else — built lazily, cached (the
6
+ * container transport resolves its pool config from the DB, so this may be async). */
7
+ private readonly managed;
8
+ /** ref → the transport that handled its dispatch, so poll/release hit the same backend. */
9
+ private readonly routed;
10
+ constructor(
11
+ /** Host-process transport for ambient (native CLI) steps — built lazily, cached. */
12
+ ambient: () => RunnerTransport | Promise<RunnerTransport>,
13
+ /** Per-run container transport for everything else — built lazily, cached (the
14
+ * container transport resolves its pool config from the DB, so this may be async). */
15
+ managed: () => RunnerTransport | Promise<RunnerTransport>);
16
+ dispatch(ref: RunnerJobRef, spec: Record<string, unknown>, kind?: RunnerDispatchKind, options?: RunnerDispatchOptions): Promise<void>;
17
+ poll(ref: RunnerJobRef): Promise<RunnerJobView>;
18
+ release(ref: RunnerJobRef): Promise<void>;
19
+ }
20
+ //# sourceMappingURL=NativeRoutingRunnerTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeRoutingRunnerTransport.d.ts","sourceRoot":"","sources":["../src/NativeRoutingRunnerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAqB5B,qBAAa,4BAA6B,YAAW,eAAe;IAKhE,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB;0FACsF;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAE5D;IACE,oFAAoF;IACnE,OAAO,EAAE,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAC1E;0FACsF;IACrE,OAAO,EAAE,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,EACxE;IAEE,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA4B,EAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAIf;IAEK,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAMpD;IAEK,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9C;CACF"}
@@ -0,0 +1,50 @@
1
+ // NATIVE-MODE transport router (local facade, `LOCAL_NATIVE_AGENTS`): native mode runs the
2
+ // developer's OWN `claude` / `codex` CLI as a host process (no sandbox), which is only
3
+ // appropriate for the steps that actually use that ambient login. The executor flags such a
4
+ // step with `ambientAuth: true` on its job body; everything else (a proxy/`pi` model, or a
5
+ // non-native vendor reusing the claude-code harness) MUST still run in a sandboxed per-run
6
+ // container, exactly as the README promises ("proxy-only models still need the container
7
+ // path"). Previously native mode sent EVERY dispatch to the host process, silently running
8
+ // proxy-model steps unsandboxed.
9
+ //
10
+ // So this routes per JOB (not per run — a single run legitimately mixes an ambient Claude
11
+ // step with a proxy step): `ambientAuth` jobs → the host-process transport; the rest → the
12
+ // container transport (built lazily, so a native deployment that only runs Claude/Codex
13
+ // never needs LOCAL_HARNESS_IMAGE; a proxy step without an image fails loudly there). The
14
+ // chosen transport is remembered per ref so poll/release reach the same backend.
15
+ const EVICTION_ERROR = 'Job not found (container evicted or crashed)';
16
+ const refKey = (ref) => `${ref.runId}:${ref.jobId}`;
17
+ export class NativeRoutingRunnerTransport {
18
+ ambient;
19
+ managed;
20
+ /** ref → the transport that handled its dispatch, so poll/release hit the same backend. */
21
+ routed = new Map();
22
+ constructor(
23
+ /** Host-process transport for ambient (native CLI) steps — built lazily, cached. */
24
+ ambient,
25
+ /** Per-run container transport for everything else — built lazily, cached (the
26
+ * container transport resolves its pool config from the DB, so this may be async). */
27
+ managed) {
28
+ this.ambient = ambient;
29
+ this.managed = managed;
30
+ }
31
+ async dispatch(ref, spec, kind = 'agent', options) {
32
+ const transport = await (spec.ambientAuth === true ? this.ambient() : this.managed());
33
+ this.routed.set(refKey(ref), transport);
34
+ await transport.dispatch(ref, spec, kind, options);
35
+ }
36
+ async poll(ref) {
37
+ // Unknown ref (a fresh process after a durable replay): report an eviction so the
38
+ // sweeper re-drives — the re-dispatch (idempotent) re-populates the routing map.
39
+ const transport = this.routed.get(refKey(ref));
40
+ if (!transport)
41
+ return { state: 'failed', error: EVICTION_ERROR };
42
+ return transport.poll(ref);
43
+ }
44
+ async release(ref) {
45
+ const transport = this.routed.get(refKey(ref));
46
+ this.routed.delete(refKey(ref));
47
+ await transport?.release?.(ref);
48
+ }
49
+ }
50
+ //# sourceMappingURL=NativeRoutingRunnerTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeRoutingRunnerTransport.js","sourceRoot":"","sources":["../src/NativeRoutingRunnerTransport.ts"],"names":[],"mappings":"AAQA,2FAA2F;AAC3F,uFAAuF;AACvF,4FAA4F;AAC5F,2FAA2F;AAC3F,2FAA2F;AAC3F,yFAAyF;AACzF,2FAA2F;AAC3F,iCAAiC;AACjC,EAAE;AACF,0FAA0F;AAC1F,2FAA2F;AAC3F,wFAAwF;AACxF,0FAA0F;AAC1F,iFAAiF;AAEjF,MAAM,cAAc,GAAG,8CAA8C,CAAA;AAErE,MAAM,MAAM,GAAG,CAAC,GAAiB,EAAU,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,CAAA;AAEzE,MAAM,OAAO,4BAA4B;IAMpB,OAAO;IAGP,OAAO;IAR1B,2FAA2F;IAC1E,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAA;IAE5D;IACE,oFAAoF;IACnE,OAAyD;IAC1E;0FACsF;IACrE,OAAyD;uBAHzD,OAAO;uBAGP,OAAO;IACvB,CAAC;IAEJ,KAAK,CAAC,QAAQ,CACZ,GAAiB,EACjB,IAA6B,EAC7B,IAAI,GAAuB,OAAO,EAClC,OAA+B;QAE/B,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QACrF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAA;QACvC,MAAM,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAiB;QAC1B,kFAAkF;QAClF,iFAAiF;QACjF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC9C,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;QACjE,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAiB;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/B,MAAM,SAAS,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;IACjC,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAgBpD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAuB5E;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAEjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAgBpD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CA6B5E;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAEjE"}
package/dist/config.js CHANGED
@@ -38,6 +38,12 @@ export function applyLocalDefaults(env) {
38
38
  // alias routes back to this service on the host. The docker-family transport
39
39
  // publishes that alias on Linux via `--add-host=<alias>:host-gateway`.
40
40
  PUBLIC_URL: env.PUBLIC_URL?.trim() || `http://${hostAlias}:${port}`,
41
+ // Assemble the ephemeral-environment module by default so the Tester's "delegate test
42
+ // environments to a provider" opt-in works once a developer registers a provider — the
43
+ // module is inert (and the local default stays host DinD) until they connect one AND
44
+ // flip the toggle, so defaulting it on has no behavioural cost. Set ENVIRONMENTS_ENABLED
45
+ // explicitly to override.
46
+ ENVIRONMENTS_ENABLED: env.ENVIRONMENTS_ENABLED?.trim() || 'true',
41
47
  };
42
48
  }
43
49
  /** The shared {@link AppConfig} with local-mode defaults applied. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,mFAAmF;AACnF,mFAAmF;AACnF,gDAAgD;AAChD,iFAAiF;AACjF,+DAA+D;AAC/D,uFAAuF;AACvF,6EAA6E;AAC7E,oFAAoF;AACpF,yDAAyD;AACzD,wEAAwE;AAExE,MAAM,YAAY,GAAG,MAAM,CAAA;AAE3B;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,YAAY,CAAA;IAC7C,gFAAgF;IAChF,qFAAqF;IACrF,qFAAqF;IACrF,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACvC,OAAO;QACL,GAAG,GAAG;QACN,oFAAoF;QACpF,sFAAsF;QACtF,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,MAAM;QAClD,+EAA+E;QAC/E,mBAAmB,EAAE,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvF,mFAAmF;QACnF,qFAAqF;QACrF,sFAAsF;QACtF,gFAAgF;QAChF,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAChF,qFAAqF;QACrF,6EAA6E;QAC7E,uEAAuE;QACvE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,UAAU,SAAS,IAAI,IAAI,EAAE;KACpE,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,GAAsB;IACpD,OAAO,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAA;AAChD,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,mFAAmF;AACnF,mFAAmF;AACnF,gDAAgD;AAChD,iFAAiF;AACjF,+DAA+D;AAC/D,uFAAuF;AACvF,6EAA6E;AAC7E,oFAAoF;AACpF,yDAAyD;AACzD,wEAAwE;AAExE,MAAM,YAAY,GAAG,MAAM,CAAA;AAE3B;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,YAAY,CAAA;IAC7C,gFAAgF;IAChF,qFAAqF;IACrF,qFAAqF;IACrF,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACvC,OAAO;QACL,GAAG,GAAG;QACN,oFAAoF;QACpF,sFAAsF;QACtF,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,MAAM;QAClD,+EAA+E;QAC/E,mBAAmB,EAAE,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvF,mFAAmF;QACnF,qFAAqF;QACrF,sFAAsF;QACtF,gFAAgF;QAChF,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAChF,qFAAqF;QACrF,6EAA6E;QAC7E,uEAAuE;QACvE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,UAAU,SAAS,IAAI,IAAI,EAAE;QACnE,sFAAsF;QACtF,uFAAuF;QACvF,qFAAqF;QACrF,yFAAyF;QACzF,0BAA0B;QAC1B,oBAAoB,EAAE,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,IAAI,MAAM;KACjE,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,GAAsB;IACpD,OAAO,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAA;AAChD,CAAC"}
@@ -1,4 +1,17 @@
1
1
  import type { NodeContainerOptions } from '@cat-factory/node-server';
2
2
  import type { ServerContainer } from '@cat-factory/server';
3
+ import type { HarnessKind } from '@cat-factory/kernel';
3
4
  export declare function buildLocalContainer(options: NodeContainerOptions): ServerContainer;
5
+ /**
6
+ * Parse `LOCAL_NATIVE_AGENTS` into the set of subscription harnesses to run natively. The
7
+ * documented form is a comma-separated list of harness ids (`claude-code,codex`); `claude`
8
+ * is accepted as an alias for `claude-code`. Blank/unset OR an explicit off value
9
+ * (`false`/`0`/`off`/`no`/`none`/`disabled`) ⇒ off (`[]`) — so disabling native mode never
10
+ * accidentally enables it. An affirmative value naming no harness (`true`/`1`/`on`/…) ⇒ BOTH
11
+ * native harnesses. Only `claude-code` / `codex` are ever native; any other unrecognised
12
+ * token is ignored. A value with neither a recognised harness nor an affirmative keyword
13
+ * (e.g. a typo) ⇒ off, so an unintelligible setting fails safe rather than enabling an
14
+ * unsandboxed, unmetered mode.
15
+ */
16
+ export declare function parseNativeHarnesses(raw: string | undefined): HarnessKind[];
4
17
  //# sourceMappingURL=container.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AACpE,OAAO,KAAK,EAAqC,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAyB7F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,eAAe,CAiFlF"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAIpE,OAAO,KAAK,EAAqC,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAG7F,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,qBAAqB,CAAA;AAuCvE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,eAAe,CA2RlF;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,EAAE,CAc3E"}