@cat-factory/local-server 0.6.0 → 0.7.3
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 -21
- package/dist/LocalContainerRunnerTransport.d.ts +83 -0
- package/dist/LocalContainerRunnerTransport.d.ts.map +1 -0
- package/dist/LocalContainerRunnerTransport.js +247 -0
- package/dist/LocalContainerRunnerTransport.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -4
- package/dist/config.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +14 -4
- package/dist/container.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/runtimes/appleContainerRuntime.d.ts +21 -0
- package/dist/runtimes/appleContainerRuntime.d.ts.map +1 -0
- package/dist/runtimes/appleContainerRuntime.js +191 -0
- package/dist/runtimes/appleContainerRuntime.js.map +1 -0
- package/dist/runtimes/containerRuntime.d.ts +96 -0
- package/dist/runtimes/containerRuntime.d.ts.map +1 -0
- package/dist/runtimes/containerRuntime.js +99 -0
- package/dist/runtimes/containerRuntime.js.map +1 -0
- package/dist/runtimes/dockerRuntime.d.ts +27 -0
- package/dist/runtimes/dockerRuntime.d.ts.map +1 -0
- package/dist/runtimes/dockerRuntime.js +124 -0
- package/dist/runtimes/dockerRuntime.js.map +1 -0
- package/dist/runtimes/index.d.ts +13 -0
- package/dist/runtimes/index.d.ts.map +1 -0
- package/dist/runtimes/index.js +33 -0
- package/dist/runtimes/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +67 -17
- package/dist/server.js.map +1 -1
- package/package.json +13 -8
- package/dist/LocalDockerRunnerTransport.d.ts +0 -95
- package/dist/LocalDockerRunnerTransport.d.ts.map +0 -1
- package/dist/LocalDockerRunnerTransport.js +0 -342
- package/dist/LocalDockerRunnerTransport.js.map +0 -1
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAyB9D,wBAAsB,UAAU,CAC9B,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;CACT,GACL,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAwD5C"}
|
package/dist/server.js
CHANGED
|
@@ -1,23 +1,46 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
1
3
|
import { start } from '@cat-factory/node-server';
|
|
2
4
|
import { logger } from '@cat-factory/server';
|
|
3
5
|
import { applyLocalDefaults } from './config.js';
|
|
4
6
|
import { buildLocalContainer } from './container.js';
|
|
5
7
|
import { githubPatCreationUrl } from './github.js';
|
|
6
|
-
import {
|
|
8
|
+
import { createLocalContainerTransportFromEnv } from './LocalContainerRunnerTransport.js';
|
|
9
|
+
import { createRuntimeAdapter, resolveRuntimeId } from './runtimes/index.js';
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
7
11
|
// Boot the local-mode service. It reuses the Node facade's `start()` — Postgres +
|
|
8
12
|
// pg-boss + the execution worker + sweepers, served over @hono/node-server — passing
|
|
9
|
-
// the local composition root so agent jobs run in local
|
|
10
|
-
// is reached via a PAT. Requires
|
|
11
|
-
//
|
|
12
|
-
// serves and only container
|
|
13
|
+
// the local composition root so agent jobs run in local containers (Docker/Podman/
|
|
14
|
+
// OrbStack/Colima/Apple `container`) and GitHub is reached via a PAT. Requires
|
|
15
|
+
// DATABASE_URL (point it at the local Postgres); set LOCAL_HARNESS_IMAGE to run
|
|
16
|
+
// repo-operating agent jobs (without it the board still serves and only container
|
|
17
|
+
// kinds fail, loudly).
|
|
18
|
+
//
|
|
19
|
+
// `environmentProvider` injects a NATIVE ephemeral-environment adapter (e.g. Kargo)
|
|
20
|
+
// in place of the generic HttpEnvironmentProvider — this is the supported seam for a
|
|
21
|
+
// local deployment to wire its own provider while keeping local mode's preflight
|
|
22
|
+
// (orphan reaping, PAT/auth warnings) and differentiators (local container transport,
|
|
23
|
+
// PAT-backed GitHub client). It threads straight through to `buildNodeContainer`'s
|
|
24
|
+
// `environmentProvider` option. `buildContainer` is intentionally NOT exposed: overriding
|
|
25
|
+
// it would discard local mode's differentiators.
|
|
13
26
|
export async function startLocal(options = {}) {
|
|
14
27
|
const env = options.env ?? process.env;
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
28
|
+
// The auth gate defaults OPEN in local mode and the listener binds to all interfaces
|
|
29
|
+
// (so on native Linux Docker the agent containers can reach the LLM proxy via the
|
|
30
|
+
// bridge gateway). That combination means anyone on your network can reach the API —
|
|
31
|
+
// surface it so it is a choice, not a surprise. Lock it down with AUTH_DEV_OPEN=false,
|
|
32
|
+
// or HOST=127.0.0.1 on Docker Desktop (where host.docker.internal still resolves).
|
|
33
|
+
const localized = applyLocalDefaults(env);
|
|
34
|
+
// Container-runtime preflight: log the selected runtime + its capabilities + the host
|
|
35
|
+
// alias the harness will use to reach this service, and probe that the CLI is present.
|
|
36
|
+
// A misconfigured runtime then fails loud at boot rather than on the first dispatch.
|
|
37
|
+
await preflightRuntime(localized);
|
|
38
|
+
// Best-effort: reap per-run containers a previous run orphaned (a crash or hard kill
|
|
39
|
+
// can leave exited managed containers behind, since their `release()` never ran).
|
|
40
|
+
// Skipped when no image is configured (nothing to reap).
|
|
41
|
+
if (localized.LOCAL_HARNESS_IMAGE?.trim()) {
|
|
19
42
|
try {
|
|
20
|
-
const reaped = await
|
|
43
|
+
const reaped = await createLocalContainerTransportFromEnv(localized).reapExited();
|
|
21
44
|
if (reaped > 0)
|
|
22
45
|
logger.info({ reaped }, 'reaped orphaned local job containers');
|
|
23
46
|
}
|
|
@@ -25,12 +48,6 @@ export async function startLocal(options = {}) {
|
|
|
25
48
|
logger.warn({ err: err instanceof Error ? err.message : String(err) }, 'could not reap orphaned local job containers');
|
|
26
49
|
}
|
|
27
50
|
}
|
|
28
|
-
// The auth gate defaults OPEN in local mode and the listener binds to all interfaces
|
|
29
|
-
// (so on native Linux Docker the agent containers can reach the LLM proxy via the
|
|
30
|
-
// bridge gateway). That combination means anyone on your network can reach the API —
|
|
31
|
-
// surface it so it is a choice, not a surprise. Lock it down with AUTH_DEV_OPEN=false,
|
|
32
|
-
// or HOST=127.0.0.1 on Docker Desktop (where host.docker.internal still resolves).
|
|
33
|
-
const localized = applyLocalDefaults(env);
|
|
34
51
|
// GitHub is reached via a PAT in local mode (there is no GitHub-App connect flow). Without
|
|
35
52
|
// one the board still serves, but every repo-operating agent step — clone, push, open PR,
|
|
36
53
|
// the CI gate, the real merge — fails. Surface it at boot with a click-through URL that
|
|
@@ -45,6 +62,39 @@ export async function startLocal(options = {}) {
|
|
|
45
62
|
'on your network can reach the API. Set AUTH_DEV_OPEN=false, or HOST=127.0.0.1 on ' +
|
|
46
63
|
'Docker Desktop, to restrict it.');
|
|
47
64
|
}
|
|
48
|
-
return start({
|
|
65
|
+
return start({
|
|
66
|
+
env,
|
|
67
|
+
host: options.host,
|
|
68
|
+
buildContainer: (o) => buildLocalContainer({ ...o, environmentProvider: options.environmentProvider }),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Log the resolved container runtime + capabilities + networking, and probe that its CLI
|
|
73
|
+
* is installed. Non-fatal: the board still boots if the binary is missing (only
|
|
74
|
+
* container-backed agent kinds then fail), mirroring how a missing image is handled.
|
|
75
|
+
*/
|
|
76
|
+
async function preflightRuntime(localized) {
|
|
77
|
+
const adapter = createRuntimeAdapter(localized);
|
|
78
|
+
logger.info({
|
|
79
|
+
runtime: resolveRuntimeId(localized),
|
|
80
|
+
binary: adapter.binary,
|
|
81
|
+
localDind: adapter.capabilities.localDind,
|
|
82
|
+
hostAlias: adapter.hostAlias,
|
|
83
|
+
publicUrl: localized.PUBLIC_URL,
|
|
84
|
+
}, 'local mode: container runtime selected');
|
|
85
|
+
if (!adapter.capabilities.localDind) {
|
|
86
|
+
logger.info(`local mode: the '${resolveRuntimeId(localized)}' runtime cannot run the Tester's local ` +
|
|
87
|
+
`docker-compose infra (no Docker-in-Docker). Tasks must use the ephemeral test ` +
|
|
88
|
+
`environment (with an environment provider configured) or a 'No infra dependencies' ` +
|
|
89
|
+
`service; a local-infra Tester run is refused at start.`);
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await execFileAsync(adapter.binary, ['--version'], { timeout: 10_000 });
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
logger.warn({ err: err instanceof Error ? err.message : String(err), binary: adapter.binary }, `local mode: container CLI '${adapter.binary}' is not runnable — repo-operating agent ` +
|
|
96
|
+
`steps will fail until it is installed and on PATH (or set LOCAL_DOCKER_BINARY / ` +
|
|
97
|
+
`LOCAL_CONTAINER_RUNTIME).`);
|
|
98
|
+
}
|
|
49
99
|
}
|
|
50
100
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/local-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.3",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/kibertoad/cat-factory.git",
|
|
7
|
+
"directory": "backend/runtimes/local"
|
|
8
|
+
},
|
|
4
9
|
"description": "Local-mode runtime facade for the Agent Architecture Board: the @cat-factory/node-server stack with agent jobs run as local Docker/Podman containers and GitHub accessed via a personal access token — a developer can run the whole product on their own machine.",
|
|
5
10
|
"files": [
|
|
6
11
|
"dist"
|
|
@@ -20,18 +25,18 @@
|
|
|
20
25
|
},
|
|
21
26
|
"dependencies": {
|
|
22
27
|
"drizzle-orm": "1.0.0-rc.3",
|
|
23
|
-
"@cat-factory/agents": "0.
|
|
24
|
-
"@cat-factory/contracts": "0.
|
|
25
|
-
"@cat-factory/
|
|
26
|
-
"@cat-factory/orchestration": "0.
|
|
27
|
-
"@cat-factory/
|
|
28
|
-
"@cat-factory/server": "0.
|
|
28
|
+
"@cat-factory/agents": "0.7.2",
|
|
29
|
+
"@cat-factory/contracts": "0.7.2",
|
|
30
|
+
"@cat-factory/node-server": "0.7.2",
|
|
31
|
+
"@cat-factory/orchestration": "0.7.2",
|
|
32
|
+
"@cat-factory/kernel": "0.7.2",
|
|
33
|
+
"@cat-factory/server": "0.7.2"
|
|
29
34
|
},
|
|
30
35
|
"devDependencies": {
|
|
31
36
|
"@types/node": "^24.13.2",
|
|
32
37
|
"typescript": "7.0.1-rc",
|
|
33
38
|
"vitest": "^4.1.9",
|
|
34
|
-
"@cat-factory/conformance": "0.
|
|
39
|
+
"@cat-factory/conformance": "0.7.2"
|
|
35
40
|
},
|
|
36
41
|
"scripts": {
|
|
37
42
|
"build": "tsc -b tsconfig.build.json",
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { RunnerDispatchKind, RunnerDispatchOptions, RunnerJobRef, RunnerJobView, RunnerTransport } from '@cat-factory/kernel';
|
|
2
|
-
/** Injectable docker/podman CLI runner — overridable in tests. */
|
|
3
|
-
export type DockerExec = (args: string[]) => Promise<{
|
|
4
|
-
stdout: string;
|
|
5
|
-
stderr: string;
|
|
6
|
-
}>;
|
|
7
|
-
export interface LocalDockerRunnerTransportOptions {
|
|
8
|
-
/** The executor-harness image ref (a GHCR pull or a locally built tag). */
|
|
9
|
-
image: string;
|
|
10
|
-
/** The container CLI binary. Default `docker` (Podman works via the same surface). */
|
|
11
|
-
binary?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Shared secret injected as `HARNESS_SHARED_SECRET` and sent as the
|
|
14
|
-
* `x-harness-secret` header on every call. Defaults to a random per-process value.
|
|
15
|
-
*/
|
|
16
|
-
sharedSecret?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Add `--add-host=host.docker.internal:host-gateway` so the harness can reach the
|
|
19
|
-
* backend LLM proxy at `host.docker.internal` (needed on Linux; harmless on
|
|
20
|
-
* Docker Desktop). Default true.
|
|
21
|
-
*/
|
|
22
|
-
addHostGateway?: boolean;
|
|
23
|
-
/** Optional `--network` for the container. */
|
|
24
|
-
network?: string;
|
|
25
|
-
/** Extra `-e KEY=VALUE` env passed into the container (rarely needed). */
|
|
26
|
-
env?: Record<string, string>;
|
|
27
|
-
/** Injectable docker exec — defaults to running {@link binary} via execFile. */
|
|
28
|
-
exec?: DockerExec;
|
|
29
|
-
/** Injectable fetch — defaults to the global. */
|
|
30
|
-
fetchImpl?: typeof fetch;
|
|
31
|
-
/** How long to wait for the container's port + `/health` after start. Default 60s. */
|
|
32
|
-
readyTimeoutMs?: number;
|
|
33
|
-
/** Per-HTTP-call timeout. Default 30s. */
|
|
34
|
-
requestTimeoutMs?: number;
|
|
35
|
-
/**
|
|
36
|
-
* Run the Tester (`test`) job container with `--privileged` so its in-container
|
|
37
|
-
* Docker-in-Docker daemon can start and the Tester can `docker compose up` the
|
|
38
|
-
* service's local infra. This is the local analogue of the Cloudflare harness's
|
|
39
|
-
* rootless-dockerd path, but reliable: on a developer machine privileged DinD
|
|
40
|
-
* "just works", keeping the service's dependencies on the job container's own
|
|
41
|
-
* `localhost` (exactly what the Tester prompt assumes). Default true; set false to
|
|
42
|
-
* fall back to the harness's best-effort rootless daemon (e.g. under Podman, whose
|
|
43
|
-
* rootless containers can run nested Podman without `--privileged`). Only the
|
|
44
|
-
* `test` kind gets it — every other kind runs unprivileged. See entrypoint.sh.
|
|
45
|
-
*/
|
|
46
|
-
privilegedTestJobs?: boolean;
|
|
47
|
-
}
|
|
48
|
-
export declare class LocalDockerRunnerTransport implements RunnerTransport {
|
|
49
|
-
private readonly image;
|
|
50
|
-
private readonly binary;
|
|
51
|
-
private readonly sharedSecret;
|
|
52
|
-
private readonly addHostGateway;
|
|
53
|
-
private readonly network?;
|
|
54
|
-
private readonly extraEnv;
|
|
55
|
-
private readonly exec;
|
|
56
|
-
private readonly fetchImpl;
|
|
57
|
-
private readonly readyTimeoutMs;
|
|
58
|
-
private readonly requestTimeoutMs;
|
|
59
|
-
private readonly privilegedTestJobs;
|
|
60
|
-
/** runId → resolved container handle, to spare a `docker` lookup on the hot poll path. */
|
|
61
|
-
private readonly cache;
|
|
62
|
-
constructor(options: LocalDockerRunnerTransportOptions);
|
|
63
|
-
dispatch(ref: RunnerJobRef, spec: Record<string, unknown>, kind?: RunnerDispatchKind, options?: RunnerDispatchOptions): Promise<void>;
|
|
64
|
-
poll(ref: RunnerJobRef): Promise<RunnerJobView>;
|
|
65
|
-
/**
|
|
66
|
-
* Reclaim the per-RUN container now (`docker rm -f`) rather than leaving it idle —
|
|
67
|
-
* this tears down the whole run's container (and with it any step still running in
|
|
68
|
-
* it). Best-effort and idempotent: removing an already-gone container is a no-op.
|
|
69
|
-
*/
|
|
70
|
-
release(ref: RunnerJobRef): Promise<void>;
|
|
71
|
-
/**
|
|
72
|
-
* Reap exited per-job containers this transport manages — orphans a crash or hard
|
|
73
|
-
* kill left behind (release() never ran for them). Best-effort; returns the count
|
|
74
|
-
* removed. Call once at boot, before any job is in flight.
|
|
75
|
-
*/
|
|
76
|
-
reapExited(): Promise<number>;
|
|
77
|
-
/** Force-remove every (running or exited) container labelled with this run id. */
|
|
78
|
-
private removeContainersForRun;
|
|
79
|
-
private url;
|
|
80
|
-
/** The container handle for a run from the cache, else rediscovered by label. */
|
|
81
|
-
private resolve;
|
|
82
|
-
/** The (running-or-exited) container id labelled with this run id, if any. */
|
|
83
|
-
private findContainer;
|
|
84
|
-
/** Parse the published host port for the harness port, or undefined if unmapped. */
|
|
85
|
-
private hostPort;
|
|
86
|
-
private waitForPort;
|
|
87
|
-
private waitForHealth;
|
|
88
|
-
private isRunning;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Build a {@link LocalDockerRunnerTransport} from the process environment. The image
|
|
92
|
-
* ref (`LOCAL_HARNESS_IMAGE`) is required; everything else has sane local defaults.
|
|
93
|
-
*/
|
|
94
|
-
export declare function createLocalDockerTransportFromEnv(env: NodeJS.ProcessEnv): LocalDockerRunnerTransport;
|
|
95
|
-
//# sourceMappingURL=LocalDockerRunnerTransport.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalDockerRunnerTransport.d.ts","sourceRoot":"","sources":["../src/LocalDockerRunnerTransport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAkD5B,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAExF,MAAM,WAAW,iCAAiC;IAChD,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,gFAAgF;IAChF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,sFAAsF;IACtF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAID,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAE5C,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2D;IAEjF,YAAY,OAAO,EAAE,iCAAiC,EAarD;IAEK,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA0B,EAChC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA+Df;IAEK,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAmCpD;IAED;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9C;IAED;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBlC;IAID,kFAAkF;YACpE,sBAAsB;IAiBpC,OAAO,CAAC,GAAG;IAIX,iFAAiF;YACnE,OAAO;IAYrB,8EAA8E;YAChE,aAAa;IAY3B,oFAAoF;YACtE,QAAQ;YAUR,WAAW;YAYX,aAAa;YAmBb,SAAS;CASxB;AAUD;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,0BAA0B,CAmB5B"}
|
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process';
|
|
2
|
-
import { randomBytes } from 'node:crypto';
|
|
3
|
-
import { promisify } from 'node:util';
|
|
4
|
-
import { resolveDockerResources } from '@cat-factory/contracts';
|
|
5
|
-
const execFileAsync = promisify(execFile);
|
|
6
|
-
// The local-mode runner backend: each RUN gets its OWN local Docker/Podman container
|
|
7
|
-
// — the SAME executor-harness image the Cloudflare Worker runs per-run Containers
|
|
8
|
-
// from — which hosts that run's whole sequence of step jobs. It is the local analogue
|
|
9
|
-
// of `CloudflareContainerTransport` (a per-run Cloudflare Container) and of
|
|
10
|
-
// `RunnerPoolTransport` (an org's self-hosted pool): the ContainerAgentExecutor drives
|
|
11
|
-
// all three identically through the `RunnerTransport` port, addressed by a
|
|
12
|
-
// {@link RunnerJobRef} — the run id (which container) plus the per-step job id.
|
|
13
|
-
//
|
|
14
|
-
// A container is started per RUN (`docker run -d`, harness `:8080` published to an
|
|
15
|
-
// ephemeral host port), labelled with the run id so the run's later steps re-attach to
|
|
16
|
-
// it instead of starting a duplicate, and the harness keys each step's job by the
|
|
17
|
-
// per-step job id (so siblings never collide). The harness reaches this service's LLM proxy at
|
|
18
|
-
// `host.docker.internal` — published via `--add-host` on Linux — and clones/pushes
|
|
19
|
-
// to github.com directly with the per-job token in the request body. Nothing
|
|
20
|
-
// long-lived is mounted: the per-job GitHub + proxy tokens travel in the POST body
|
|
21
|
-
// and live only for the job, in the container's ephemeral filesystem.
|
|
22
|
-
/** Maps a dispatch kind to the harness HTTP route that starts that job. */
|
|
23
|
-
const KIND_ROUTE = {
|
|
24
|
-
run: '/run',
|
|
25
|
-
blueprint: '/blueprint',
|
|
26
|
-
spec: '/spec',
|
|
27
|
-
explore: '/explore',
|
|
28
|
-
bootstrap: '/bootstrap',
|
|
29
|
-
'ci-fix': '/ci-fix',
|
|
30
|
-
'resolve-conflicts': '/resolve-conflicts',
|
|
31
|
-
merge: '/merge',
|
|
32
|
-
test: '/test',
|
|
33
|
-
'fix-tests': '/fix-tests',
|
|
34
|
-
};
|
|
35
|
-
// The failed-poll error the engine classifies as a container eviction (matched by
|
|
36
|
-
// orchestration `isContainerEvictionError`, also used by the bootstrap flow). A
|
|
37
|
-
// vanished/exited local container maps to it so the run stops and the stale-run
|
|
38
|
-
// sweeper can re-drive it — mirroring the Worker transport's 404 mapping.
|
|
39
|
-
const EVICTION_ERROR = 'Job not found (container evicted or crashed)';
|
|
40
|
-
// Labels the per-RUN container by its run id (the container key). A run's steps share
|
|
41
|
-
// one container, so this is the run id, not a per-step job id.
|
|
42
|
-
const LABEL_RUN = 'cat-factory.runId';
|
|
43
|
-
const LABEL_MANAGED = 'cat-factory.managed=local-docker';
|
|
44
|
-
/** The port the harness listens on inside the container. */
|
|
45
|
-
const HARNESS_PORT = 8080;
|
|
46
|
-
const SECRET_HEADER = 'x-harness-secret';
|
|
47
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
|
-
export class LocalDockerRunnerTransport {
|
|
49
|
-
image;
|
|
50
|
-
binary;
|
|
51
|
-
sharedSecret;
|
|
52
|
-
addHostGateway;
|
|
53
|
-
network;
|
|
54
|
-
extraEnv;
|
|
55
|
-
exec;
|
|
56
|
-
fetchImpl;
|
|
57
|
-
readyTimeoutMs;
|
|
58
|
-
requestTimeoutMs;
|
|
59
|
-
privilegedTestJobs;
|
|
60
|
-
/** runId → resolved container handle, to spare a `docker` lookup on the hot poll path. */
|
|
61
|
-
cache = new Map();
|
|
62
|
-
constructor(options) {
|
|
63
|
-
this.image = options.image;
|
|
64
|
-
this.binary = options.binary ?? 'docker';
|
|
65
|
-
this.sharedSecret = options.sharedSecret ?? randomBytes(24).toString('hex');
|
|
66
|
-
this.addHostGateway = options.addHostGateway ?? true;
|
|
67
|
-
this.network = options.network;
|
|
68
|
-
this.extraEnv = options.env ?? {};
|
|
69
|
-
this.exec =
|
|
70
|
-
options.exec ?? ((args) => execFileAsync(this.binary, args, { maxBuffer: 16 * 1024 * 1024 }));
|
|
71
|
-
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
72
|
-
this.readyTimeoutMs = options.readyTimeoutMs ?? 60_000;
|
|
73
|
-
this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
74
|
-
this.privilegedTestJobs = options.privilegedTestJobs ?? true;
|
|
75
|
-
}
|
|
76
|
-
async dispatch(ref, spec, kind = 'run', options) {
|
|
77
|
-
// The container is per-RUN: a run's first step starts it, later steps re-attach to
|
|
78
|
-
// it (resolved by the run-id label), and the harness keys each step's job by the
|
|
79
|
-
// per-step `ref.jobId` carried in the spec body.
|
|
80
|
-
const runId = ref.runId;
|
|
81
|
-
let resolved = await this.resolve(runId);
|
|
82
|
-
if (!resolved) {
|
|
83
|
-
// A prior attempt may have left an exited/dead container under this run label
|
|
84
|
-
// (resolve() returns undefined for one whose port is no longer published). Remove
|
|
85
|
-
// any such container first so it can't shadow the fresh one in later label lookups
|
|
86
|
-
// (findContainer returns the first match).
|
|
87
|
-
await this.removeContainersForRun(runId);
|
|
88
|
-
const args = [
|
|
89
|
-
'run',
|
|
90
|
-
'-d',
|
|
91
|
-
'--label',
|
|
92
|
-
`${LABEL_RUN}=${runId}`,
|
|
93
|
-
'--label',
|
|
94
|
-
LABEL_MANAGED,
|
|
95
|
-
'-p',
|
|
96
|
-
`127.0.0.1:0:${HARNESS_PORT}`,
|
|
97
|
-
'-e',
|
|
98
|
-
`HARNESS_SHARED_SECRET=${this.sharedSecret}`,
|
|
99
|
-
];
|
|
100
|
-
// Size the per-job container on the host daemon from the service's abstract
|
|
101
|
-
// instance size — the local backend never touches a cloud, it just provisions a
|
|
102
|
-
// bigger/smaller Docker container (`--memory`/`--cpus`).
|
|
103
|
-
if (options?.instanceSize) {
|
|
104
|
-
const { memory, cpus } = resolveDockerResources(options.instanceSize);
|
|
105
|
-
args.push('--memory', memory, '--cpus', cpus);
|
|
106
|
-
}
|
|
107
|
-
// The Tester stands its infra up with `docker compose` INSIDE the job container
|
|
108
|
-
// (Docker-in-Docker), so the dependencies sit on the container's own localhost.
|
|
109
|
-
// Run that one kind privileged so the in-container daemon can start. No other
|
|
110
|
-
// kind needs Docker, so none other gets elevated.
|
|
111
|
-
if (kind === 'test' && this.privilegedTestJobs)
|
|
112
|
-
args.push('--privileged');
|
|
113
|
-
if (this.addHostGateway)
|
|
114
|
-
args.push('--add-host=host.docker.internal:host-gateway');
|
|
115
|
-
if (this.network)
|
|
116
|
-
args.push('--network', this.network);
|
|
117
|
-
for (const [k, v] of Object.entries(this.extraEnv))
|
|
118
|
-
args.push('-e', `${k}=${v}`);
|
|
119
|
-
args.push(this.image);
|
|
120
|
-
const { stdout } = await this.exec(args);
|
|
121
|
-
const containerId = stdout.trim().split(/\s+/).pop();
|
|
122
|
-
if (!containerId)
|
|
123
|
-
throw new Error('docker run returned no container id');
|
|
124
|
-
const port = await this.waitForPort(containerId);
|
|
125
|
-
resolved = { containerId, port };
|
|
126
|
-
this.cache.set(runId, resolved);
|
|
127
|
-
await this.waitForHealth(resolved.port);
|
|
128
|
-
}
|
|
129
|
-
// POST the job to the kind's route. Idempotent: re-attaching to an already-running
|
|
130
|
-
// container re-POSTs, which the harness's per-id registry treats as a re-attach.
|
|
131
|
-
const res = await this.fetchImpl(this.url(resolved.port, KIND_ROUTE[kind]), {
|
|
132
|
-
method: 'POST',
|
|
133
|
-
headers: { 'content-type': 'application/json', [SECRET_HEADER]: this.sharedSecret },
|
|
134
|
-
body: JSON.stringify(spec),
|
|
135
|
-
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
136
|
-
});
|
|
137
|
-
if (!res.ok) {
|
|
138
|
-
throw new Error(`Local container dispatch failed (HTTP ${res.status}): ${await safeText(res)}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
async poll(ref) {
|
|
142
|
-
const resolved = await this.resolve(ref.runId);
|
|
143
|
-
// No container for this run at all → it was evicted/reaped (or never started).
|
|
144
|
-
if (!resolved)
|
|
145
|
-
return { state: 'failed', error: EVICTION_ERROR };
|
|
146
|
-
let res;
|
|
147
|
-
try {
|
|
148
|
-
// Address the per-RUN container, but read the per-step job by its own id.
|
|
149
|
-
res = await this.fetchImpl(this.url(resolved.port, `/jobs/${encodeURIComponent(ref.jobId)}`), {
|
|
150
|
-
method: 'GET',
|
|
151
|
-
headers: { [SECRET_HEADER]: this.sharedSecret },
|
|
152
|
-
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (err) {
|
|
156
|
-
// Connection refused / DNS gone: the container most likely exited. Confirm via
|
|
157
|
-
// the daemon — if it is no longer running, report an eviction so the run stops;
|
|
158
|
-
// otherwise surface the transient error so the caller can retry.
|
|
159
|
-
if (!(await this.isRunning(resolved.containerId))) {
|
|
160
|
-
this.cache.delete(ref.runId);
|
|
161
|
-
return { state: 'failed', error: EVICTION_ERROR };
|
|
162
|
-
}
|
|
163
|
-
throw err;
|
|
164
|
-
}
|
|
165
|
-
// The container is up but the harness no longer knows this job id (it was reaped
|
|
166
|
-
// after completion, or the container was recreated): treat as an eviction.
|
|
167
|
-
if (res.status === 404)
|
|
168
|
-
return { state: 'failed', error: EVICTION_ERROR };
|
|
169
|
-
if (!res.ok) {
|
|
170
|
-
throw new Error(`Local container job poll failed (HTTP ${res.status}): ${await safeText(res)}`);
|
|
171
|
-
}
|
|
172
|
-
return (await res.json());
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Reclaim the per-RUN container now (`docker rm -f`) rather than leaving it idle —
|
|
176
|
-
* this tears down the whole run's container (and with it any step still running in
|
|
177
|
-
* it). Best-effort and idempotent: removing an already-gone container is a no-op.
|
|
178
|
-
*/
|
|
179
|
-
async release(ref) {
|
|
180
|
-
const containerId = this.cache.get(ref.runId)?.containerId ?? (await this.findContainer(ref.runId));
|
|
181
|
-
this.cache.delete(ref.runId);
|
|
182
|
-
if (!containerId)
|
|
183
|
-
return;
|
|
184
|
-
await this.exec(['rm', '-f', containerId]).catch(() => undefined);
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Reap exited per-job containers this transport manages — orphans a crash or hard
|
|
188
|
-
* kill left behind (release() never ran for them). Best-effort; returns the count
|
|
189
|
-
* removed. Call once at boot, before any job is in flight.
|
|
190
|
-
*/
|
|
191
|
-
async reapExited() {
|
|
192
|
-
const { stdout } = await this.exec([
|
|
193
|
-
'ps',
|
|
194
|
-
'-aq',
|
|
195
|
-
'--filter',
|
|
196
|
-
`label=${LABEL_MANAGED}`,
|
|
197
|
-
'--filter',
|
|
198
|
-
'status=exited',
|
|
199
|
-
]);
|
|
200
|
-
const ids = stdout
|
|
201
|
-
.trim()
|
|
202
|
-
.split('\n')
|
|
203
|
-
.map((s) => s.trim())
|
|
204
|
-
.filter(Boolean);
|
|
205
|
-
if (ids.length)
|
|
206
|
-
await this.exec(['rm', '-f', ...ids]).catch(() => undefined);
|
|
207
|
-
return ids.length;
|
|
208
|
-
}
|
|
209
|
-
// --- internals ----------------------------------------------------------
|
|
210
|
-
/** Force-remove every (running or exited) container labelled with this run id. */
|
|
211
|
-
async removeContainersForRun(runId) {
|
|
212
|
-
const { stdout } = await this.exec([
|
|
213
|
-
'ps',
|
|
214
|
-
'-aq',
|
|
215
|
-
'--filter',
|
|
216
|
-
`label=${LABEL_RUN}=${runId}`,
|
|
217
|
-
'--filter',
|
|
218
|
-
`label=${LABEL_MANAGED}`,
|
|
219
|
-
]);
|
|
220
|
-
const ids = stdout
|
|
221
|
-
.trim()
|
|
222
|
-
.split('\n')
|
|
223
|
-
.map((s) => s.trim())
|
|
224
|
-
.filter(Boolean);
|
|
225
|
-
if (ids.length)
|
|
226
|
-
await this.exec(['rm', '-f', ...ids]).catch(() => undefined);
|
|
227
|
-
}
|
|
228
|
-
url(port, path) {
|
|
229
|
-
return `http://127.0.0.1:${port}${path}`;
|
|
230
|
-
}
|
|
231
|
-
/** The container handle for a run from the cache, else rediscovered by label. */
|
|
232
|
-
async resolve(runId) {
|
|
233
|
-
const cached = this.cache.get(runId);
|
|
234
|
-
if (cached)
|
|
235
|
-
return cached;
|
|
236
|
-
const containerId = await this.findContainer(runId);
|
|
237
|
-
if (!containerId)
|
|
238
|
-
return undefined;
|
|
239
|
-
const port = await this.hostPort(containerId);
|
|
240
|
-
if (port === undefined)
|
|
241
|
-
return undefined;
|
|
242
|
-
const resolved = { containerId, port };
|
|
243
|
-
this.cache.set(runId, resolved);
|
|
244
|
-
return resolved;
|
|
245
|
-
}
|
|
246
|
-
/** The (running-or-exited) container id labelled with this run id, if any. */
|
|
247
|
-
async findContainer(runId) {
|
|
248
|
-
const { stdout } = await this.exec([
|
|
249
|
-
'ps',
|
|
250
|
-
'-aq',
|
|
251
|
-
'--filter',
|
|
252
|
-
`label=${LABEL_RUN}=${runId}`,
|
|
253
|
-
'--filter',
|
|
254
|
-
`label=${LABEL_MANAGED}`,
|
|
255
|
-
]);
|
|
256
|
-
return stdout.trim().split('\n')[0]?.trim() || undefined;
|
|
257
|
-
}
|
|
258
|
-
/** Parse the published host port for the harness port, or undefined if unmapped. */
|
|
259
|
-
async hostPort(containerId) {
|
|
260
|
-
const { stdout } = await this.exec(['port', containerId, `${HARNESS_PORT}/tcp`]);
|
|
261
|
-
// e.g. "127.0.0.1:49153" (possibly several lines for IPv4/IPv6); take the last
|
|
262
|
-
// numeric segment of the first line.
|
|
263
|
-
const line = stdout.trim().split('\n')[0]?.trim();
|
|
264
|
-
if (!line)
|
|
265
|
-
return undefined;
|
|
266
|
-
const port = Number(line.slice(line.lastIndexOf(':') + 1));
|
|
267
|
-
return Number.isFinite(port) && port > 0 ? port : undefined;
|
|
268
|
-
}
|
|
269
|
-
async waitForPort(containerId) {
|
|
270
|
-
const deadline = Date.now() + this.readyTimeoutMs;
|
|
271
|
-
for (;;) {
|
|
272
|
-
const port = await this.hostPort(containerId).catch(() => undefined);
|
|
273
|
-
if (port !== undefined)
|
|
274
|
-
return port;
|
|
275
|
-
if (Date.now() >= deadline) {
|
|
276
|
-
throw new Error(`Timed out waiting for container ${containerId} to publish its port`);
|
|
277
|
-
}
|
|
278
|
-
await delay(250);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
async waitForHealth(port) {
|
|
282
|
-
const deadline = Date.now() + this.readyTimeoutMs;
|
|
283
|
-
for (;;) {
|
|
284
|
-
try {
|
|
285
|
-
const res = await this.fetchImpl(this.url(port, '/health'), {
|
|
286
|
-
method: 'GET',
|
|
287
|
-
signal: AbortSignal.timeout(Math.min(this.requestTimeoutMs, 5_000)),
|
|
288
|
-
});
|
|
289
|
-
if (res.ok)
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
// not up yet
|
|
294
|
-
}
|
|
295
|
-
if (Date.now() >= deadline) {
|
|
296
|
-
throw new Error(`Timed out waiting for the harness on :${port} to become healthy`);
|
|
297
|
-
}
|
|
298
|
-
await delay(300);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
async isRunning(containerId) {
|
|
302
|
-
try {
|
|
303
|
-
const { stdout } = await this.exec(['inspect', '-f', '{{.State.Running}}', containerId]);
|
|
304
|
-
return stdout.trim() === 'true';
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
// No such container → not running.
|
|
308
|
-
return false;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
async function safeText(res) {
|
|
313
|
-
try {
|
|
314
|
-
return (await res.text()).slice(0, 500);
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
return '(no body)';
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Build a {@link LocalDockerRunnerTransport} from the process environment. The image
|
|
322
|
-
* ref (`LOCAL_HARNESS_IMAGE`) is required; everything else has sane local defaults.
|
|
323
|
-
*/
|
|
324
|
-
export function createLocalDockerTransportFromEnv(env) {
|
|
325
|
-
const image = env.LOCAL_HARNESS_IMAGE?.trim();
|
|
326
|
-
if (!image) {
|
|
327
|
-
throw new Error('LOCAL_HARNESS_IMAGE is required for local mode: set it to the executor-harness image ref ' +
|
|
328
|
-
'(a GHCR pull or a tag built from backend/internal/executor-harness/Dockerfile).');
|
|
329
|
-
}
|
|
330
|
-
return new LocalDockerRunnerTransport({
|
|
331
|
-
image,
|
|
332
|
-
binary: env.LOCAL_DOCKER_BINARY?.trim() || 'docker',
|
|
333
|
-
sharedSecret: env.HARNESS_SHARED_SECRET?.trim() || undefined,
|
|
334
|
-
network: env.LOCAL_DOCKER_NETWORK?.trim() || undefined,
|
|
335
|
-
addHostGateway: env.LOCAL_DOCKER_ADD_HOST_GATEWAY?.trim() !== 'false',
|
|
336
|
-
// Default on: the Tester stands its docker-compose infra up via Docker-in-Docker,
|
|
337
|
-
// which needs a privileged job container. Set to `false` for runtimes that run
|
|
338
|
-
// nested containers without it (e.g. rootless Podman).
|
|
339
|
-
privilegedTestJobs: env.LOCAL_DOCKER_PRIVILEGED_TEST_JOBS?.trim() !== 'false',
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
//# sourceMappingURL=LocalDockerRunnerTransport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LocalDockerRunnerTransport.js","sourceRoot":"","sources":["../src/LocalDockerRunnerTransport.ts"],"names":[],"mappings":"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"}
|