@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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { HARNESS_PORT, } from './containerRuntime.js';
|
|
2
|
+
// The Apple `container` adapter (macOS Containerization framework). It diverges from the
|
|
3
|
+
// Docker CLI in three ways that the seam absorbs:
|
|
4
|
+
// 1. Verbs: `container run | list | inspect | delete` (no `ps`/`rm`, no `--filter`,
|
|
5
|
+
// no `inspect -f` template) — so we list-all + filter client-side and parse JSON.
|
|
6
|
+
// 2. Identity: `--name` IS the container id, so a run is addressed by a deterministic
|
|
7
|
+
// name (`cf-<runId>`); a managed label marks ours for reaping.
|
|
8
|
+
// 3. Networking: each container runs in its own VM with its own IP — there is no
|
|
9
|
+
// published-port-on-loopback, so the orchestrator connects to `<containerIP>:8080`
|
|
10
|
+
// directly (read from `container inspect`). There is no Docker-in-Docker, so
|
|
11
|
+
// `capabilities.localDind` is false (the engine refuses the Tester's local infra
|
|
12
|
+
// mode on this runtime — see ExecutionService limited-mode gating).
|
|
13
|
+
//
|
|
14
|
+
// CLI flags verified against apple/container's command reference (run -d/-e/-l/--name/
|
|
15
|
+
// -c/-m, list --all --format json, inspect, delete --force). The inspect JSON shape for
|
|
16
|
+
// the assigned IP is not contractually documented, so the IP/state extraction is
|
|
17
|
+
// deliberately tolerant (and unit-tested against the observed shape).
|
|
18
|
+
const NAME_PREFIX = 'cf-';
|
|
19
|
+
const LABEL_MANAGED = 'cat-factory.managed=apple';
|
|
20
|
+
/**
|
|
21
|
+
* Statuses we treat as terminal — i.e. safe to reap. A container whose status we can't
|
|
22
|
+
* recognise (empty/unknown — the `container list` JSON shape is not contractual) is left
|
|
23
|
+
* ALONE rather than risk force-deleting one that is still running.
|
|
24
|
+
*/
|
|
25
|
+
const TERMINAL_STATUSES = new Set(['stopped', 'exited', 'dead']);
|
|
26
|
+
/** The deterministic container name (== id) for a run. */
|
|
27
|
+
function runName(runId) {
|
|
28
|
+
// Container names allow [a-zA-Z0-9][a-zA-Z0-9_.-]*; run ids are already in that set,
|
|
29
|
+
// but sanitise defensively so an unusual id can't produce an invalid name.
|
|
30
|
+
return `${NAME_PREFIX}${runId.replace(/[^a-zA-Z0-9_.-]/g, '-')}`;
|
|
31
|
+
}
|
|
32
|
+
/** Whether a listed container is one we manage (matched on either the id or the name). */
|
|
33
|
+
function isManaged(row) {
|
|
34
|
+
return row.id.startsWith(NAME_PREFIX) || (row.name?.startsWith(NAME_PREFIX) ?? false);
|
|
35
|
+
}
|
|
36
|
+
/** Parse `container list --format json` into a normalised {id,name,status} list, tolerantly. */
|
|
37
|
+
function parseList(stdout) {
|
|
38
|
+
const trimmed = stdout.trim();
|
|
39
|
+
if (!trimmed)
|
|
40
|
+
return [];
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(trimmed);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
if (typeof row !== 'object' || row === null)
|
|
52
|
+
continue;
|
|
53
|
+
const obj = row;
|
|
54
|
+
const config = (obj.configuration ?? obj.config);
|
|
55
|
+
const name = asString(obj.name) ?? asString(config?.name);
|
|
56
|
+
const id = asString(obj.id) ?? asString(config?.id) ?? name;
|
|
57
|
+
const status = (asString(obj.status) ?? asString(obj.state) ?? '').toLowerCase();
|
|
58
|
+
if (id)
|
|
59
|
+
out.push({ id, name, status });
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
function asString(value) {
|
|
64
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
65
|
+
}
|
|
66
|
+
/** First plausible non-gateway IPv4 found anywhere in the inspect JSON, CIDR-stripped. */
|
|
67
|
+
function extractIp(node, keyHint = '') {
|
|
68
|
+
if (typeof node === 'string') {
|
|
69
|
+
if (/gateway/i.test(keyHint))
|
|
70
|
+
return undefined;
|
|
71
|
+
const m = node.match(/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\/\d+)?\b/);
|
|
72
|
+
return m?.[1];
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(node)) {
|
|
75
|
+
for (const item of node) {
|
|
76
|
+
const found = extractIp(item, keyHint);
|
|
77
|
+
if (found)
|
|
78
|
+
return found;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (typeof node === 'object' && node !== null) {
|
|
83
|
+
// Prefer explicit address-bearing fields over a blind scan.
|
|
84
|
+
for (const key of ['address', 'ipAddress', 'ip', 'ipv4']) {
|
|
85
|
+
const v = node[key];
|
|
86
|
+
const found = extractIp(v, key);
|
|
87
|
+
if (found)
|
|
88
|
+
return found;
|
|
89
|
+
}
|
|
90
|
+
for (const [key, v] of Object.entries(node)) {
|
|
91
|
+
const found = extractIp(v, key);
|
|
92
|
+
if (found)
|
|
93
|
+
return found;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/** Parse `container inspect` output (array or object) into {ip?, running}. */
|
|
99
|
+
function parseInspect(stdout) {
|
|
100
|
+
const trimmed = stdout.trim();
|
|
101
|
+
if (!trimmed)
|
|
102
|
+
return { running: false };
|
|
103
|
+
let parsed;
|
|
104
|
+
try {
|
|
105
|
+
parsed = JSON.parse(trimmed);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return { running: false };
|
|
109
|
+
}
|
|
110
|
+
const node = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
111
|
+
if (typeof node !== 'object' || node === null)
|
|
112
|
+
return { running: false };
|
|
113
|
+
const obj = node;
|
|
114
|
+
const status = (asString(obj.status) ?? asString(obj.state) ?? '').toLowerCase();
|
|
115
|
+
return { ip: extractIp(obj), running: status === 'running' };
|
|
116
|
+
}
|
|
117
|
+
export class AppleContainerRuntimeAdapter {
|
|
118
|
+
id = 'apple';
|
|
119
|
+
binary;
|
|
120
|
+
hostAlias;
|
|
121
|
+
capabilities = { localDind: false };
|
|
122
|
+
constructor(options) {
|
|
123
|
+
this.binary = options.binary ?? 'container';
|
|
124
|
+
this.hostAlias = options.hostAlias;
|
|
125
|
+
}
|
|
126
|
+
async run(exec, spec) {
|
|
127
|
+
const name = runName(spec.runId);
|
|
128
|
+
const args = [
|
|
129
|
+
'run',
|
|
130
|
+
'-d',
|
|
131
|
+
'--name',
|
|
132
|
+
name,
|
|
133
|
+
'-l',
|
|
134
|
+
LABEL_MANAGED,
|
|
135
|
+
'-e',
|
|
136
|
+
`HARNESS_SHARED_SECRET=${spec.sharedSecret}`,
|
|
137
|
+
];
|
|
138
|
+
// No published port (connect by the container's own IP) and no --privileged
|
|
139
|
+
// (one-VM-per-container has no Docker-in-Docker; the engine never asks the Tester to
|
|
140
|
+
// run local infra here — see capabilities.localDind=false).
|
|
141
|
+
if (spec.instanceSize)
|
|
142
|
+
args.push('-m', spec.instanceSize.memory, '-c', spec.instanceSize.cpus);
|
|
143
|
+
for (const [k, v] of Object.entries(spec.env))
|
|
144
|
+
args.push('-e', `${k}=${v}`);
|
|
145
|
+
args.push(spec.image);
|
|
146
|
+
await exec(args);
|
|
147
|
+
// The name IS the container id; the transport addresses everything by it.
|
|
148
|
+
return name;
|
|
149
|
+
}
|
|
150
|
+
async find(exec, runId) {
|
|
151
|
+
const name = runName(runId);
|
|
152
|
+
const rows = parseList((await exec(['list', '--all', '--format', 'json'])).stdout);
|
|
153
|
+
// The deterministic name is the addressable handle (inspect/delete accept it). Match it
|
|
154
|
+
// against either the `id` or the `name` field, since `container list`'s `id` may be a
|
|
155
|
+
// content hash rather than the assigned name depending on the CLI version.
|
|
156
|
+
const hit = rows.find((r) => r.id === name || r.name === name);
|
|
157
|
+
return hit ? name : undefined;
|
|
158
|
+
}
|
|
159
|
+
async endpoint(exec, containerId) {
|
|
160
|
+
const { ip } = parseInspect((await exec(['inspect', containerId])).stdout);
|
|
161
|
+
if (!ip)
|
|
162
|
+
return undefined;
|
|
163
|
+
return { host: ip, port: HARNESS_PORT };
|
|
164
|
+
}
|
|
165
|
+
async isRunning(exec, containerId) {
|
|
166
|
+
try {
|
|
167
|
+
return parseInspect((await exec(['inspect', containerId])).stdout).running;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async remove(exec, containerId) {
|
|
174
|
+
await exec(['delete', '--force', containerId]).catch(() => undefined);
|
|
175
|
+
}
|
|
176
|
+
async removeRun(exec, runId) {
|
|
177
|
+
await this.remove(exec, runName(runId));
|
|
178
|
+
}
|
|
179
|
+
async reapExited(exec) {
|
|
180
|
+
// Reap only managed containers in a recognised TERMINAL state — never one whose status
|
|
181
|
+
// we can't parse, which could still be running (mirrors the Docker adapter's precise
|
|
182
|
+
// `status=exited` filter). Address by the assigned name when present (always a valid
|
|
183
|
+
// handle), else the id.
|
|
184
|
+
const rows = parseList((await exec(['list', '--all', '--format', 'json'])).stdout).filter((r) => isManaged(r) && TERMINAL_STATUSES.has(r.status));
|
|
185
|
+
if (rows.length) {
|
|
186
|
+
await exec(['delete', '--force', ...rows.map((r) => r.name ?? r.id)]).catch(() => undefined);
|
|
187
|
+
}
|
|
188
|
+
return rows.length;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=appleContainerRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appleContainerRuntime.js","sourceRoot":"","sources":["../../src/runtimes/appleContainerRuntime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** The in-container port the executor-harness listens on. */
|
|
2
|
+
export declare const HARNESS_PORT = 8080;
|
|
3
|
+
/** Injectable CLI runner (docker/podman/container) — overridable in tests. */
|
|
4
|
+
export type ContainerExec = (args: string[]) => Promise<{
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}>;
|
|
8
|
+
/** Where the orchestrator connects to reach a run's harness. */
|
|
9
|
+
export interface ContainerEndpoint {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
}
|
|
13
|
+
/** What a runtime can and cannot do, consumed by the engine's Tester gate. */
|
|
14
|
+
export interface RuntimeCapabilities {
|
|
15
|
+
/**
|
|
16
|
+
* Whether the runtime can stand the Tester's local docker-compose infra up via
|
|
17
|
+
* Docker-in-Docker (a privileged / nested-container daemon inside the job
|
|
18
|
+
* container). False for one-VM-per-container runtimes (Apple `container`), which
|
|
19
|
+
* drives the engine's Tester "limited mode".
|
|
20
|
+
*/
|
|
21
|
+
localDind: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** A per-run container to start. */
|
|
24
|
+
export interface RunContainerSpec {
|
|
25
|
+
runId: string;
|
|
26
|
+
image: string;
|
|
27
|
+
sharedSecret: string;
|
|
28
|
+
/** Run privileged (only the Tester `test` kind, only when the runtime supports DinD). */
|
|
29
|
+
privileged: boolean;
|
|
30
|
+
/** Optional `--network` (docker family only). */
|
|
31
|
+
network?: string;
|
|
32
|
+
/** Extra `-e KEY=VALUE` env passed into the container. */
|
|
33
|
+
env: Record<string, string>;
|
|
34
|
+
/** Host resource limits derived from the service's abstract instance size. */
|
|
35
|
+
instanceSize?: {
|
|
36
|
+
memory: string;
|
|
37
|
+
cpus: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A container runtime as the transport sees it. Each method takes the injected
|
|
42
|
+
* {@link ContainerExec} so the transport stays runtime-agnostic and tests can drive a
|
|
43
|
+
* fake CLI. Implementations own their own container-identity scheme (labels vs names)
|
|
44
|
+
* and their own networking model (published host port vs per-container IP).
|
|
45
|
+
*/
|
|
46
|
+
export interface ContainerRuntimeAdapter {
|
|
47
|
+
readonly id: RuntimeId;
|
|
48
|
+
readonly binary: string;
|
|
49
|
+
readonly capabilities: RuntimeCapabilities;
|
|
50
|
+
/** The hostname the harness uses to reach the orchestrator's LLM proxy (for PUBLIC_URL). */
|
|
51
|
+
readonly hostAlias: string;
|
|
52
|
+
/** Start a per-run container detached; resolves to its container id/name. */
|
|
53
|
+
run(exec: ContainerExec, spec: RunContainerSpec): Promise<string>;
|
|
54
|
+
/** The (running-or-exited) container for a run, if any. */
|
|
55
|
+
find(exec: ContainerExec, runId: string): Promise<string | undefined>;
|
|
56
|
+
/** The host+port the orchestrator should connect to, or undefined if not ready. */
|
|
57
|
+
endpoint(exec: ContainerExec, containerId: string): Promise<ContainerEndpoint | undefined>;
|
|
58
|
+
/** Whether the container is currently running. */
|
|
59
|
+
isRunning(exec: ContainerExec, containerId: string): Promise<boolean>;
|
|
60
|
+
/** Force-remove a single container (idempotent). */
|
|
61
|
+
remove(exec: ContainerExec, containerId: string): Promise<void>;
|
|
62
|
+
/** Force-remove every container for a run (idempotent). */
|
|
63
|
+
removeRun(exec: ContainerExec, runId: string): Promise<void>;
|
|
64
|
+
/** Reap exited managed containers left by crashes; resolves to the count removed. */
|
|
65
|
+
reapExited(exec: ContainerExec): Promise<number>;
|
|
66
|
+
}
|
|
67
|
+
export type RuntimeId = 'docker' | 'podman' | 'orbstack' | 'colima' | 'apple';
|
|
68
|
+
/** Per-runtime defaults — the single source of truth for binary + networking + capability. */
|
|
69
|
+
export interface RuntimeProfile {
|
|
70
|
+
id: RuntimeId;
|
|
71
|
+
/** Default CLI binary (overridable via LOCAL_DOCKER_BINARY). */
|
|
72
|
+
binary: string;
|
|
73
|
+
/** Default host alias used for PUBLIC_URL + the docker `--add-host` line. */
|
|
74
|
+
hostAlias: string;
|
|
75
|
+
/** Whether the docker-family adapter adds `--add-host=<alias>:host-gateway`. */
|
|
76
|
+
addHostGateway: boolean;
|
|
77
|
+
/** Whether the Tester's local Docker-in-Docker infra can run on this runtime. */
|
|
78
|
+
localDind: boolean;
|
|
79
|
+
/** Which adapter implementation backs this runtime. */
|
|
80
|
+
family: 'docker' | 'apple';
|
|
81
|
+
}
|
|
82
|
+
/** Resolve the runtime profile for an id (defaults to docker for an unknown value). */
|
|
83
|
+
export declare function runtimeProfile(id: RuntimeId): RuntimeProfile;
|
|
84
|
+
/**
|
|
85
|
+
* The runtime selected by `LOCAL_CONTAINER_RUNTIME` (docker | podman | orbstack |
|
|
86
|
+
* colima | apple). Defaults to `docker`; an unrecognised value also falls back to
|
|
87
|
+
* docker (logged by the preflight). Explicit selection is the supported path.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveRuntimeId(env: NodeJS.ProcessEnv): RuntimeId;
|
|
90
|
+
/**
|
|
91
|
+
* The effective host alias for the chosen runtime: an explicit `LOCAL_HARNESS_HOST_ALIAS`
|
|
92
|
+
* wins, else the runtime profile's default. Used to derive the PUBLIC_URL default so a
|
|
93
|
+
* job container can reach this service's LLM proxy on the host.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveHostAlias(env: NodeJS.ProcessEnv): string;
|
|
96
|
+
//# sourceMappingURL=containerRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containerRuntime.d.ts","sourceRoot":"","sources":["../../src/runtimes/containerRuntime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// The local-mode container-runtime seam. `LocalContainerRunnerTransport` orchestrates
|
|
2
|
+
// the per-run container lifecycle (cache, health polling, HTTP) but delegates every
|
|
3
|
+
// CLI call and the "how do I reach this container / how does it reach the host"
|
|
4
|
+
// decision to a `ContainerRuntimeAdapter`, so a runtime that diverges from the Docker
|
|
5
|
+
// CLI (today: Apple's `container`, which runs each container in its own VM with its own
|
|
6
|
+
// IP and speaks a different CLI) is a new adapter rather than a fork of the transport.
|
|
7
|
+
//
|
|
8
|
+
// Docker, Podman, OrbStack and Colima all speak the Docker CLI, so they share ONE
|
|
9
|
+
// adapter (`DockerRuntimeAdapter`) parameterised by binary + networking. Apple
|
|
10
|
+
// `container` gets its own (`AppleContainerRuntimeAdapter`).
|
|
11
|
+
/** The in-container port the executor-harness listens on. */
|
|
12
|
+
export const HARNESS_PORT = 8080;
|
|
13
|
+
const PROFILES = {
|
|
14
|
+
// Docker Desktop / Engine — the baseline. Desktop resolves host.docker.internal
|
|
15
|
+
// natively; Linux Engine needs the host-gateway add-host (harmless on Desktop).
|
|
16
|
+
docker: {
|
|
17
|
+
id: 'docker',
|
|
18
|
+
binary: 'docker',
|
|
19
|
+
hostAlias: 'host.docker.internal',
|
|
20
|
+
addHostGateway: true,
|
|
21
|
+
localDind: true,
|
|
22
|
+
family: 'docker',
|
|
23
|
+
},
|
|
24
|
+
// Podman speaks the same CLI; host.docker.internal:host-gateway works on v4+. Rootless
|
|
25
|
+
// Podman nests containers without --privileged (set LOCAL_DOCKER_PRIVILEGED_TEST_JOBS=false).
|
|
26
|
+
// NOTE: Podman needs fully-qualified image refs (ghcr.io/…, localhost/…).
|
|
27
|
+
podman: {
|
|
28
|
+
id: 'podman',
|
|
29
|
+
binary: 'podman',
|
|
30
|
+
hostAlias: 'host.docker.internal',
|
|
31
|
+
addHostGateway: true,
|
|
32
|
+
localDind: true,
|
|
33
|
+
family: 'docker',
|
|
34
|
+
},
|
|
35
|
+
// OrbStack ships a drop-in `docker` CLI and resolves host.docker.internal natively;
|
|
36
|
+
// published ports forward to the host loopback. Works like Docker Desktop.
|
|
37
|
+
orbstack: {
|
|
38
|
+
id: 'orbstack',
|
|
39
|
+
binary: 'docker',
|
|
40
|
+
hostAlias: 'host.docker.internal',
|
|
41
|
+
addHostGateway: true,
|
|
42
|
+
localDind: true,
|
|
43
|
+
family: 'docker',
|
|
44
|
+
},
|
|
45
|
+
// Colima runs dockerd in a Lima VM. Ports forward to the host loopback, but
|
|
46
|
+
// host.docker.internal/host-gateway resolves to the VM, not the Mac host where the
|
|
47
|
+
// orchestrator runs — so the harness often can't reach the LLM proxy with the default
|
|
48
|
+
// alias. host.lima.internal is the Lima-provided host alias; if it still doesn't
|
|
49
|
+
// resolve, set PUBLIC_URL to the Mac's LAN IP (see deploy/local/README.md).
|
|
50
|
+
colima: {
|
|
51
|
+
id: 'colima',
|
|
52
|
+
binary: 'docker',
|
|
53
|
+
hostAlias: 'host.lima.internal',
|
|
54
|
+
addHostGateway: false,
|
|
55
|
+
localDind: true,
|
|
56
|
+
family: 'docker',
|
|
57
|
+
},
|
|
58
|
+
// Apple `container` (macOS): one lightweight VM per container, each with its own IP.
|
|
59
|
+
// No published-port model (reach the container by IP), no host.docker.internal, and
|
|
60
|
+
// no Docker-in-Docker — so the Tester's local infra mode is unavailable (limited mode).
|
|
61
|
+
// The default host alias is the macOS Virtualization-framework vmnet gateway; override
|
|
62
|
+
// via PUBLIC_URL / LOCAL_HARNESS_HOST_ALIAS if your subnet differs.
|
|
63
|
+
apple: {
|
|
64
|
+
id: 'apple',
|
|
65
|
+
binary: 'container',
|
|
66
|
+
hostAlias: '192.168.64.1',
|
|
67
|
+
addHostGateway: false,
|
|
68
|
+
localDind: false,
|
|
69
|
+
family: 'apple',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const RUNTIME_IDS = Object.keys(PROFILES);
|
|
73
|
+
/** Resolve the runtime profile for an id (defaults to docker for an unknown value). */
|
|
74
|
+
export function runtimeProfile(id) {
|
|
75
|
+
return PROFILES[id] ?? PROFILES.docker;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The runtime selected by `LOCAL_CONTAINER_RUNTIME` (docker | podman | orbstack |
|
|
79
|
+
* colima | apple). Defaults to `docker`; an unrecognised value also falls back to
|
|
80
|
+
* docker (logged by the preflight). Explicit selection is the supported path.
|
|
81
|
+
*/
|
|
82
|
+
export function resolveRuntimeId(env) {
|
|
83
|
+
const raw = env.LOCAL_CONTAINER_RUNTIME?.trim().toLowerCase();
|
|
84
|
+
if (raw && RUNTIME_IDS.includes(raw))
|
|
85
|
+
return raw;
|
|
86
|
+
return 'docker';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The effective host alias for the chosen runtime: an explicit `LOCAL_HARNESS_HOST_ALIAS`
|
|
90
|
+
* wins, else the runtime profile's default. Used to derive the PUBLIC_URL default so a
|
|
91
|
+
* job container can reach this service's LLM proxy on the host.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveHostAlias(env) {
|
|
94
|
+
const explicit = env.LOCAL_HARNESS_HOST_ALIAS?.trim();
|
|
95
|
+
if (explicit)
|
|
96
|
+
return explicit;
|
|
97
|
+
return runtimeProfile(resolveRuntimeId(env)).hostAlias;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=containerRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containerRuntime.js","sourceRoot":"","sources":["../../src/runtimes/containerRuntime.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,oFAAoF;AACpF,gFAAgF;AAChF,sFAAsF;AACtF,wFAAwF;AACxF,uFAAuF;AACvF,EAAE;AACF,kFAAkF;AAClF,+EAA+E;AAC/E,6DAA6D;AAE7D,6DAA6D;AAC7D,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;AAmFhC,MAAM,QAAQ,GAAsC;IAClD,gFAAgF;IAChF,gFAAgF;IAChF,MAAM,EAAE;QACN,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,sBAAsB;QACjC,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,QAAQ;KACjB;IACD,uFAAuF;IACvF,8FAA8F;IAC9F,0EAA0E;IAC1E,MAAM,EAAE;QACN,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,sBAAsB;QACjC,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,QAAQ;KACjB;IACD,oFAAoF;IACpF,2EAA2E;IAC3E,QAAQ,EAAE;QACR,EAAE,EAAE,UAAU;QACd,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,sBAAsB;QACjC,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,QAAQ;KACjB;IACD,4EAA4E;IAC5E,mFAAmF;IACnF,sFAAsF;IACtF,iFAAiF;IACjF,4EAA4E;IAC5E,MAAM,EAAE;QACN,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,oBAAoB;QAC/B,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,QAAQ;KACjB;IACD,qFAAqF;IACrF,oFAAoF;IACpF,wFAAwF;IACxF,uFAAuF;IACvF,oEAAoE;IACpE,KAAK,EAAE;QACL,EAAE,EAAE,OAAO;QACX,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,cAAc;QACzB,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,KAAK;QAChB,MAAM,EAAE,OAAO;KAChB;CACF,CAAA;AAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAgB,CAAA;AAExD,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAAC,EAAa;IAC1C,OAAO,QAAQ,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAA;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAsB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC7D,IAAI,GAAG,IAAK,WAAwB,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAgB,CAAA;IAC3E,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAsB;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,CAAA;IACrD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,OAAO,cAAc,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;AACxD,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ContainerEndpoint, type ContainerExec, type ContainerRuntimeAdapter, type RunContainerSpec, type RuntimeId } from './containerRuntime.js';
|
|
2
|
+
export interface DockerRuntimeAdapterOptions {
|
|
3
|
+
id: RuntimeId;
|
|
4
|
+
binary: string;
|
|
5
|
+
hostAlias: string;
|
|
6
|
+
/** Add `--add-host=<hostAlias>:host-gateway` so the harness can reach the host. */
|
|
7
|
+
addHostGateway: boolean;
|
|
8
|
+
localDind: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare class DockerRuntimeAdapter implements ContainerRuntimeAdapter {
|
|
11
|
+
readonly id: RuntimeId;
|
|
12
|
+
readonly binary: string;
|
|
13
|
+
readonly hostAlias: string;
|
|
14
|
+
readonly capabilities: {
|
|
15
|
+
localDind: boolean;
|
|
16
|
+
};
|
|
17
|
+
private readonly addHostGateway;
|
|
18
|
+
constructor(options: DockerRuntimeAdapterOptions);
|
|
19
|
+
run(exec: ContainerExec, spec: RunContainerSpec): Promise<string>;
|
|
20
|
+
find(exec: ContainerExec, runId: string): Promise<string | undefined>;
|
|
21
|
+
endpoint(exec: ContainerExec, containerId: string): Promise<ContainerEndpoint | undefined>;
|
|
22
|
+
isRunning(exec: ContainerExec, containerId: string): Promise<boolean>;
|
|
23
|
+
remove(exec: ContainerExec, containerId: string): Promise<void>;
|
|
24
|
+
removeRun(exec: ContainerExec, runId: string): Promise<void>;
|
|
25
|
+
reapExited(exec: ContainerExec): Promise<number>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=dockerRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerRuntime.d.ts","sourceRoot":"","sources":["../../src/runtimes/dockerRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,uBAAuB,EAE5B,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAA;AAY9B,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,SAAS,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,cAAc,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,qBAAa,oBAAqB,YAAW,uBAAuB;IAClE,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAA;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,YAAY,OAAO,EAAE,2BAA2B,EAM/C;IAEK,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBtE;IAEK,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU1E;IAEK,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAS/F;IAEK,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E;IAEK,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;IAEK,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAejE;IAEK,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBrD;CACF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { HARNESS_PORT, } from './containerRuntime.js';
|
|
2
|
+
// The Docker-CLI adapter — covers Docker, Podman, OrbStack and Colima, which all speak
|
|
3
|
+
// the same `run/ps/port/inspect/rm` surface. It is the behaviour the transport had
|
|
4
|
+
// inline before the seam was extracted, parameterised by binary + networking. A run's
|
|
5
|
+
// container is labelled with the run id (the container key) and a managed marker; the
|
|
6
|
+
// harness `:8080` is published to an ephemeral host port read back with `docker port`.
|
|
7
|
+
/** Labels the per-run container by its run id (a run's steps share one container). */
|
|
8
|
+
const LABEL_RUN = 'cat-factory.runId';
|
|
9
|
+
const LABEL_MANAGED = 'cat-factory.managed=local-docker';
|
|
10
|
+
export class DockerRuntimeAdapter {
|
|
11
|
+
id;
|
|
12
|
+
binary;
|
|
13
|
+
hostAlias;
|
|
14
|
+
capabilities;
|
|
15
|
+
addHostGateway;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.id = options.id;
|
|
18
|
+
this.binary = options.binary;
|
|
19
|
+
this.hostAlias = options.hostAlias;
|
|
20
|
+
this.addHostGateway = options.addHostGateway;
|
|
21
|
+
this.capabilities = { localDind: options.localDind };
|
|
22
|
+
}
|
|
23
|
+
async run(exec, spec) {
|
|
24
|
+
const args = [
|
|
25
|
+
'run',
|
|
26
|
+
'-d',
|
|
27
|
+
'--label',
|
|
28
|
+
`${LABEL_RUN}=${spec.runId}`,
|
|
29
|
+
'--label',
|
|
30
|
+
LABEL_MANAGED,
|
|
31
|
+
'-p',
|
|
32
|
+
`127.0.0.1:0:${HARNESS_PORT}`,
|
|
33
|
+
'-e',
|
|
34
|
+
`HARNESS_SHARED_SECRET=${spec.sharedSecret}`,
|
|
35
|
+
];
|
|
36
|
+
if (spec.instanceSize)
|
|
37
|
+
args.push('--memory', spec.instanceSize.memory, '--cpus', spec.instanceSize.cpus);
|
|
38
|
+
if (spec.privileged)
|
|
39
|
+
args.push('--privileged');
|
|
40
|
+
if (this.addHostGateway)
|
|
41
|
+
args.push(`--add-host=${this.hostAlias}:host-gateway`);
|
|
42
|
+
if (spec.network)
|
|
43
|
+
args.push('--network', spec.network);
|
|
44
|
+
for (const [k, v] of Object.entries(spec.env))
|
|
45
|
+
args.push('-e', `${k}=${v}`);
|
|
46
|
+
args.push(spec.image);
|
|
47
|
+
const { stdout } = await exec(args);
|
|
48
|
+
const containerId = stdout.trim().split(/\s+/).pop();
|
|
49
|
+
if (!containerId)
|
|
50
|
+
throw new Error(`${this.binary} run returned no container id`);
|
|
51
|
+
return containerId;
|
|
52
|
+
}
|
|
53
|
+
async find(exec, runId) {
|
|
54
|
+
const { stdout } = await exec([
|
|
55
|
+
'ps',
|
|
56
|
+
'-aq',
|
|
57
|
+
'--filter',
|
|
58
|
+
`label=${LABEL_RUN}=${runId}`,
|
|
59
|
+
'--filter',
|
|
60
|
+
`label=${LABEL_MANAGED}`,
|
|
61
|
+
]);
|
|
62
|
+
return stdout.trim().split('\n')[0]?.trim() || undefined;
|
|
63
|
+
}
|
|
64
|
+
async endpoint(exec, containerId) {
|
|
65
|
+
const { stdout } = await exec(['port', containerId, `${HARNESS_PORT}/tcp`]);
|
|
66
|
+
// e.g. "127.0.0.1:49153" (possibly several lines for IPv4/IPv6); take the last
|
|
67
|
+
// numeric segment of the first line.
|
|
68
|
+
const line = stdout.trim().split('\n')[0]?.trim();
|
|
69
|
+
if (!line)
|
|
70
|
+
return undefined;
|
|
71
|
+
const port = Number(line.slice(line.lastIndexOf(':') + 1));
|
|
72
|
+
if (!Number.isFinite(port) || port <= 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
return { host: '127.0.0.1', port };
|
|
75
|
+
}
|
|
76
|
+
async isRunning(exec, containerId) {
|
|
77
|
+
try {
|
|
78
|
+
const { stdout } = await exec(['inspect', '-f', '{{.State.Running}}', containerId]);
|
|
79
|
+
return stdout.trim() === 'true';
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async remove(exec, containerId) {
|
|
86
|
+
await exec(['rm', '-f', containerId]).catch(() => undefined);
|
|
87
|
+
}
|
|
88
|
+
async removeRun(exec, runId) {
|
|
89
|
+
const { stdout } = await exec([
|
|
90
|
+
'ps',
|
|
91
|
+
'-aq',
|
|
92
|
+
'--filter',
|
|
93
|
+
`label=${LABEL_RUN}=${runId}`,
|
|
94
|
+
'--filter',
|
|
95
|
+
`label=${LABEL_MANAGED}`,
|
|
96
|
+
]);
|
|
97
|
+
const ids = stdout
|
|
98
|
+
.trim()
|
|
99
|
+
.split('\n')
|
|
100
|
+
.map((s) => s.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
if (ids.length)
|
|
103
|
+
await exec(['rm', '-f', ...ids]).catch(() => undefined);
|
|
104
|
+
}
|
|
105
|
+
async reapExited(exec) {
|
|
106
|
+
const { stdout } = await exec([
|
|
107
|
+
'ps',
|
|
108
|
+
'-aq',
|
|
109
|
+
'--filter',
|
|
110
|
+
`label=${LABEL_MANAGED}`,
|
|
111
|
+
'--filter',
|
|
112
|
+
'status=exited',
|
|
113
|
+
]);
|
|
114
|
+
const ids = stdout
|
|
115
|
+
.trim()
|
|
116
|
+
.split('\n')
|
|
117
|
+
.map((s) => s.trim())
|
|
118
|
+
.filter(Boolean);
|
|
119
|
+
if (ids.length)
|
|
120
|
+
await exec(['rm', '-f', ...ids]).catch(() => undefined);
|
|
121
|
+
return ids.length;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=dockerRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerRuntime.js","sourceRoot":"","sources":["../../src/runtimes/dockerRuntime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ContainerRuntimeAdapter } from './containerRuntime.js';
|
|
2
|
+
export * from './containerRuntime.js';
|
|
3
|
+
export { DockerRuntimeAdapter } from './dockerRuntime.js';
|
|
4
|
+
export { AppleContainerRuntimeAdapter } from './appleContainerRuntime.js';
|
|
5
|
+
/**
|
|
6
|
+
* Build the container-runtime adapter selected by `LOCAL_CONTAINER_RUNTIME`
|
|
7
|
+
* (docker | podman | orbstack | colima | apple), applying the env overrides
|
|
8
|
+
* (`LOCAL_DOCKER_BINARY`, `LOCAL_DOCKER_ADD_HOST_GATEWAY`, `LOCAL_HARNESS_HOST_ALIAS`)
|
|
9
|
+
* on top of the runtime's profile defaults. Docker/Podman/OrbStack/Colima share the
|
|
10
|
+
* Docker-CLI adapter; Apple `container` gets its own.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createRuntimeAdapter(env: NodeJS.ProcessEnv): ContainerRuntimeAdapter;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtimes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,uBAAuB,CAAA;AAI9B,cAAc,uBAAuB,CAAA;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAA;AAEzE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,uBAAuB,CAqBpF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolveHostAlias, resolveRuntimeId, runtimeProfile, } from './containerRuntime.js';
|
|
2
|
+
import { DockerRuntimeAdapter } from './dockerRuntime.js';
|
|
3
|
+
import { AppleContainerRuntimeAdapter } from './appleContainerRuntime.js';
|
|
4
|
+
export * from './containerRuntime.js';
|
|
5
|
+
export { DockerRuntimeAdapter } from './dockerRuntime.js';
|
|
6
|
+
export { AppleContainerRuntimeAdapter } from './appleContainerRuntime.js';
|
|
7
|
+
/**
|
|
8
|
+
* Build the container-runtime adapter selected by `LOCAL_CONTAINER_RUNTIME`
|
|
9
|
+
* (docker | podman | orbstack | colima | apple), applying the env overrides
|
|
10
|
+
* (`LOCAL_DOCKER_BINARY`, `LOCAL_DOCKER_ADD_HOST_GATEWAY`, `LOCAL_HARNESS_HOST_ALIAS`)
|
|
11
|
+
* on top of the runtime's profile defaults. Docker/Podman/OrbStack/Colima share the
|
|
12
|
+
* Docker-CLI adapter; Apple `container` gets its own.
|
|
13
|
+
*/
|
|
14
|
+
export function createRuntimeAdapter(env) {
|
|
15
|
+
const profile = runtimeProfile(resolveRuntimeId(env));
|
|
16
|
+
const binary = env.LOCAL_DOCKER_BINARY?.trim() || profile.binary;
|
|
17
|
+
const hostAlias = resolveHostAlias(env);
|
|
18
|
+
if (profile.family === 'apple') {
|
|
19
|
+
return new AppleContainerRuntimeAdapter({ binary, hostAlias });
|
|
20
|
+
}
|
|
21
|
+
// An explicit LOCAL_DOCKER_ADD_HOST_GATEWAY wins; otherwise the profile default
|
|
22
|
+
// (Colima defaults off — host-gateway resolves to the Lima VM, not the Mac host).
|
|
23
|
+
const explicit = env.LOCAL_DOCKER_ADD_HOST_GATEWAY?.trim();
|
|
24
|
+
const addHostGateway = explicit ? explicit !== 'false' : profile.addHostGateway;
|
|
25
|
+
return new DockerRuntimeAdapter({
|
|
26
|
+
id: profile.id,
|
|
27
|
+
binary,
|
|
28
|
+
hostAlias,
|
|
29
|
+
addHostGateway,
|
|
30
|
+
localDind: profile.localDind,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtimes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAA;AAEzE,cAAc,uBAAuB,CAAA;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAA;AAEzE;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAsB;IACzD,MAAM,OAAO,GAAG,cAAc,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,MAAM,CAAA;IAChE,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAC/B,OAAO,IAAI,4BAA4B,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IAChE,CAAC;IAED,gFAAgF;IAChF,kFAAkF;IAClF,MAAM,QAAQ,GAAG,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,CAAA;IAC1D,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAA;IAE/E,OAAO,IAAI,oBAAoB,CAAC;QAC9B,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,MAAM;QACN,SAAS;QACT,cAAc;QACd,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { start } from '@cat-factory/node-server';
|
|
2
|
+
import type { EnvironmentProvider } from '@cat-factory/kernel';
|
|
2
3
|
export declare function startLocal(options?: {
|
|
3
4
|
env?: NodeJS.ProcessEnv;
|
|
5
|
+
environmentProvider?: EnvironmentProvider;
|
|
6
|
+
host?: string;
|
|
4
7
|
}): Promise<Awaited<ReturnType<typeof start>>>;
|
|
5
8
|
//# sourceMappingURL=server.d.ts.map
|