@crouter/env-docker 0.0.0-stage → 0.3.377
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/README.md +64 -2
- package/dist/docker-cli.d.ts +10 -0
- package/dist/docker-cli.js +30 -0
- package/dist/health.d.ts +7 -0
- package/dist/health.js +38 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +86 -0
- package/dist/inspect.d.ts +30 -0
- package/dist/inspect.js +66 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +1 -0
- package/package.json +31 -4
package/README.md
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @crouter/env-docker — start and attach to a crtrd container from Node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@crouter/env-docker"><img alt="npm" src="https://img.shields.io/npm/v/@crouter/env-docker?label=npm"></a>
|
|
7
|
+
<a href="https://github.com/crouton-labs/crouter/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/badge/license-GPL--3.0-blue"></a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
`@crouter/env-docker` starts, attaches to and stops a `crtrd` (the crouter daemon) Docker container through the `docker` CLI, and hands back the connection settings that [`@crouter/sdk`](https://www.npmjs.com/package/@crouter/sdk) takes. It has no dependencies, including on the SDK.
|
|
11
|
+
|
|
12
|
+
It is not a Docker client library or an orchestrator: it runs one container and tells you how to reach it. The image contains no model credential, so you configure a provider inside the container before an agent run can do anything.
|
|
13
|
+
|
|
14
|
+
[Docs](https://docs.crouter.ai/docs/sdk/docker) · [SDK](https://docs.crouter.ai/docs/sdk) · [crouter repository](https://github.com/crouton-labs/crouter) · [Source](https://github.com/crouton-labs/crouter/tree/main/packages/crouter-env-docker)
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @crouter/env-docker @crouter/sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The Docker CLI must be on `PATH`, and able to pull `ghcr.io/crouton-labs/crtrd` (or the image you pass).
|
|
23
|
+
|
|
24
|
+
## Start a container
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { start } from '@crouter/env-docker';
|
|
28
|
+
import Crouter from '@crouter/sdk';
|
|
29
|
+
|
|
30
|
+
const env = await start({
|
|
31
|
+
name: 'my-crouter',
|
|
32
|
+
volume: 'my-agent-home',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const client = new Crouter(env.connection());
|
|
36
|
+
console.log(await client.system.health());
|
|
37
|
+
|
|
38
|
+
await env.stop();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`start()` creates a container from `ghcr.io/crouton-labs/crtrd:latest` unless you pass `image`, generates a bearer token, binds the mapped port to `127.0.0.1`, and waits for `/healthz`. `connection()` returns `{ baseURL, headers }`, the shape `new Crouter(...)` takes. `stop()` removes the container.
|
|
42
|
+
|
|
43
|
+
Options: `env` sets container environment variables, `port` selects a host port, `volume` mounts a named Docker volume at the crouter home. A named volume keeps canvas state and memory when the container is recreated.
|
|
44
|
+
|
|
45
|
+
## Attach to an existing container
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { attach } from '@crouter/env-docker';
|
|
49
|
+
|
|
50
|
+
const env = await attach('my-crouter');
|
|
51
|
+
const connection = env.connection();
|
|
52
|
+
await env.stop(); // stops the container but does not remove it
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`attach(name)` reads the container's mapped port and its `CRTRD_TOKEN`.
|
|
56
|
+
|
|
57
|
+
## Related
|
|
58
|
+
|
|
59
|
+
- [`@crouter/sdk`](../crouter-sdk) — the client that consumes `connection()`.
|
|
60
|
+
- [`docker/README.md`](https://github.com/crouton-labs/crouter/blob/main/docker/README.md) — running the `crtrd` image with `docker run`, its environment variables, and building it locally.
|
|
61
|
+
- [Main repository](https://github.com/crouton-labs/crouter) — the `crtr` CLI, the `crtrd` daemon, and contribution guidelines.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
[GPL-3.0-only](https://github.com/crouton-labs/crouter/blob/main/LICENSE).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A `docker` CLI invocation that exited non-zero. Carries stderr separately
|
|
2
|
+
* so callers can fold it into a more specific message without re-parsing. */
|
|
3
|
+
export declare class DockerCliError extends Error {
|
|
4
|
+
readonly stderr: string;
|
|
5
|
+
constructor(message: string, stderr: string);
|
|
6
|
+
}
|
|
7
|
+
/** Run `docker <args>` and return trimmed stdout, or throw `DockerCliError`
|
|
8
|
+
* with stderr folded into the message. Never logs anything itself — a
|
|
9
|
+
* caller passing `-e CRTRD_TOKEN=...` in `args` must not print `args`. */
|
|
10
|
+
export declare function docker(args: string[]): Promise<string>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
/** A `docker` CLI invocation that exited non-zero. Carries stderr separately
|
|
5
|
+
* so callers can fold it into a more specific message without re-parsing. */
|
|
6
|
+
export class DockerCliError extends Error {
|
|
7
|
+
stderr;
|
|
8
|
+
constructor(message, stderr) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'DockerCliError';
|
|
11
|
+
this.stderr = stderr;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Run `docker <args>` and return trimmed stdout, or throw `DockerCliError`
|
|
15
|
+
* with stderr folded into the message. Never logs anything itself — a
|
|
16
|
+
* caller passing `-e CRTRD_TOKEN=...` in `args` must not print `args`. */
|
|
17
|
+
export async function docker(args) {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execFileAsync('docker', args, { maxBuffer: 16 * 1024 * 1024 });
|
|
20
|
+
return stdout.trim();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const stderr = isExecError(error) ? error.stderr.trim() : '';
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
+
throw new DockerCliError(`docker ${args[0] ?? ''} failed: ${stderr || message}`, stderr);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function isExecError(error) {
|
|
29
|
+
return typeof error === 'object' && error !== null && typeof error.stderr === 'string';
|
|
30
|
+
}
|
package/dist/health.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Poll `<baseUrl>/healthz` until it answers 200, `opts.timeoutMs` elapses,
|
|
2
|
+
* or `opts.isAlive` reports the container already exited. */
|
|
3
|
+
export declare function waitForHealthy(baseUrl: string, headers: Record<string, string>, opts: {
|
|
4
|
+
timeoutMs: number;
|
|
5
|
+
intervalMs: number;
|
|
6
|
+
isAlive?: () => Promise<boolean>;
|
|
7
|
+
}): Promise<void>;
|
package/dist/health.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { request } from 'node:http';
|
|
2
|
+
/** Poll `<baseUrl>/healthz` until it answers 200, `opts.timeoutMs` elapses,
|
|
3
|
+
* or `opts.isAlive` reports the container already exited. */
|
|
4
|
+
export async function waitForHealthy(baseUrl, headers, opts) {
|
|
5
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
6
|
+
let lastError;
|
|
7
|
+
while (Date.now() < deadline) {
|
|
8
|
+
if (opts.isAlive && !(await opts.isAlive())) {
|
|
9
|
+
throw new Error(`container exited before /healthz answered${lastError ? ` (last probe: ${lastError})` : ''}`);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const status = await probe(baseUrl, headers);
|
|
13
|
+
if (status === 200)
|
|
14
|
+
return;
|
|
15
|
+
lastError = `HTTP ${status}`;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
19
|
+
}
|
|
20
|
+
await sleep(opts.intervalMs);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`/healthz did not return 200 within ${opts.timeoutMs}ms (last: ${lastError ?? 'no response'})`);
|
|
23
|
+
}
|
|
24
|
+
function probe(baseUrl, headers) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const url = new URL('/healthz', baseUrl);
|
|
27
|
+
const req = request(url, { method: 'GET', headers, timeout: 2000 }, (res) => {
|
|
28
|
+
res.resume();
|
|
29
|
+
resolve(res.statusCode ?? 0);
|
|
30
|
+
});
|
|
31
|
+
req.on('timeout', () => req.destroy(new Error('healthz probe timed out')));
|
|
32
|
+
req.on('error', reject);
|
|
33
|
+
req.end();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function sleep(ms) {
|
|
37
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
38
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Connection, DockerEnvironment, StartOptions } from './types.js';
|
|
2
|
+
export type { Connection, DockerEnvironment, StartOptions };
|
|
3
|
+
/**
|
|
4
|
+
* Default image tag — the `latest` the release workflow's `publish-image` job
|
|
5
|
+
* pushes alongside the version tag. Pass `image` to pin a version or to run a
|
|
6
|
+
* locally built tag (e.g. `crtrd:local`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_IMAGE = "ghcr.io/crouton-labs/crtrd:latest";
|
|
9
|
+
/** Run the crtrd image, wait until `/healthz` answers 200 through the mapped
|
|
10
|
+
* port, and return its connection settings. `stop()` stops and
|
|
11
|
+
* removes the container. */
|
|
12
|
+
export declare function start(opts?: StartOptions): Promise<DockerEnvironment>;
|
|
13
|
+
/** Wrap an already-running container by name. Reads its mapped host port and
|
|
14
|
+
* `CRTRD_TOKEN` via `docker inspect`. `stop()` only stops the container —
|
|
15
|
+
* it does not remove it. */
|
|
16
|
+
export declare function attach(name: string): Promise<DockerEnvironment>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { docker } from './docker-cli.js';
|
|
3
|
+
import { waitForHealthy } from './health.js';
|
|
4
|
+
import { CONTAINER_PORT, inspectContainer, isContainerRunning, readHostPortFromInspect, readTokenFromInspect, resolveHostPort, tryReadLogs, } from './inspect.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default image tag — the `latest` the release workflow's `publish-image` job
|
|
7
|
+
* pushes alongside the version tag. Pass `image` to pin a version or to run a
|
|
8
|
+
* locally built tag (e.g. `crtrd:local`).
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_IMAGE = 'ghcr.io/crouton-labs/crtrd:latest';
|
|
11
|
+
const HEALTH_TIMEOUT_MS = 60_000;
|
|
12
|
+
const HEALTH_INTERVAL_MS = 300;
|
|
13
|
+
function generateToken() {
|
|
14
|
+
return randomBytes(24).toString('hex');
|
|
15
|
+
}
|
|
16
|
+
function generateName() {
|
|
17
|
+
return `crtrd-env-${randomBytes(6).toString('hex')}`;
|
|
18
|
+
}
|
|
19
|
+
/** Run the crtrd image, wait until `/healthz` answers 200 through the mapped
|
|
20
|
+
* port, and return its connection settings. `stop()` stops and
|
|
21
|
+
* removes the container. */
|
|
22
|
+
export async function start(opts = {}) {
|
|
23
|
+
const image = opts.image ?? DEFAULT_IMAGE;
|
|
24
|
+
const name = opts.name ?? generateName();
|
|
25
|
+
const token = generateToken();
|
|
26
|
+
const args = ['run', '-d', '--name', name, '-e', `CRTRD_TOKEN=${token}`];
|
|
27
|
+
for (const [key, value] of Object.entries(opts.env ?? {})) {
|
|
28
|
+
args.push('-e', `${key}=${value}`);
|
|
29
|
+
}
|
|
30
|
+
if (opts.volume) {
|
|
31
|
+
args.push('-v', `${opts.volume}:/home/agent/.crouter`);
|
|
32
|
+
}
|
|
33
|
+
// Bind loopback explicitly: docker's default publishes to 0.0.0.0, which
|
|
34
|
+
// would put a daemon holding the caller's provider keys on the LAN behind
|
|
35
|
+
// nothing but its bearer token.
|
|
36
|
+
args.push('-p', `127.0.0.1:${opts.port ?? 0}:${CONTAINER_PORT}`);
|
|
37
|
+
args.push(image);
|
|
38
|
+
const containerId = await docker(args);
|
|
39
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
40
|
+
let baseUrl;
|
|
41
|
+
try {
|
|
42
|
+
const port = await resolveHostPort(containerId);
|
|
43
|
+
baseUrl = `http://127.0.0.1:${port}`;
|
|
44
|
+
await waitForHealthy(baseUrl, headers, {
|
|
45
|
+
timeoutMs: HEALTH_TIMEOUT_MS,
|
|
46
|
+
intervalMs: HEALTH_INTERVAL_MS,
|
|
47
|
+
isAlive: () => isContainerRunning(containerId),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const logs = await tryReadLogs(containerId);
|
|
52
|
+
await docker(['rm', '-f', containerId]).catch(() => { });
|
|
53
|
+
throw new Error(`crouter-env-docker: start() container '${name}' (${containerId.slice(0, 12)}) never became healthy: ${error instanceof Error ? error.message : String(error)}${logs ? `\n--- docker logs ---\n${logs}` : ''}`);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
containerId,
|
|
57
|
+
name,
|
|
58
|
+
connection() {
|
|
59
|
+
return { baseURL: baseUrl, headers };
|
|
60
|
+
},
|
|
61
|
+
async stop() {
|
|
62
|
+
await docker(['rm', '-f', containerId]);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Wrap an already-running container by name. Reads its mapped host port and
|
|
67
|
+
* `CRTRD_TOKEN` via `docker inspect`. `stop()` only stops the container —
|
|
68
|
+
* it does not remove it. */
|
|
69
|
+
export async function attach(name) {
|
|
70
|
+
const inspection = await inspectContainer(name);
|
|
71
|
+
const containerId = inspection.Id;
|
|
72
|
+
const token = readTokenFromInspect(inspection, name);
|
|
73
|
+
const port = readHostPortFromInspect(inspection, name);
|
|
74
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
75
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
76
|
+
return {
|
|
77
|
+
containerId,
|
|
78
|
+
name,
|
|
79
|
+
connection() {
|
|
80
|
+
return { baseURL: baseUrl, headers };
|
|
81
|
+
},
|
|
82
|
+
async stop() {
|
|
83
|
+
await docker(['stop', containerId]);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const CONTAINER_PORT = 7777;
|
|
2
|
+
interface DockerInspectOutput {
|
|
3
|
+
Id: string;
|
|
4
|
+
State?: {
|
|
5
|
+
Running?: boolean;
|
|
6
|
+
};
|
|
7
|
+
Config?: {
|
|
8
|
+
Env?: string[];
|
|
9
|
+
};
|
|
10
|
+
NetworkSettings?: {
|
|
11
|
+
Ports?: Record<string, {
|
|
12
|
+
HostIp?: string;
|
|
13
|
+
HostPort?: string;
|
|
14
|
+
}[] | null>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** `docker inspect <name>` for one container. Throws a message naming `name`
|
|
18
|
+
* when the container does not exist — the caller (`attach()`) surfaces this
|
|
19
|
+
* as-is, which is the "attach fails clearly because the container is gone"
|
|
20
|
+
* contract. */
|
|
21
|
+
export declare function inspectContainer(name: string): Promise<DockerInspectOutput>;
|
|
22
|
+
export declare function readTokenFromInspect(inspection: DockerInspectOutput, name: string): string;
|
|
23
|
+
export declare function readHostPortFromInspect(inspection: DockerInspectOutput, name: string): number;
|
|
24
|
+
export declare function isContainerRunning(containerId: string): Promise<boolean>;
|
|
25
|
+
export declare function tryReadLogs(containerId: string): Promise<string | undefined>;
|
|
26
|
+
/** `docker port <id> <containerPort>/tcp` prints one host binding per line —
|
|
27
|
+
* e.g. `0.0.0.0:54321` and `[::]:54321` for a dual-stack mapping. Take the
|
|
28
|
+
* first line's port. */
|
|
29
|
+
export declare function resolveHostPort(containerId: string): Promise<number>;
|
|
30
|
+
export {};
|
package/dist/inspect.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { docker } from './docker-cli.js';
|
|
2
|
+
export const CONTAINER_PORT = 7777;
|
|
3
|
+
/** `docker inspect <name>` for one container. Throws a message naming `name`
|
|
4
|
+
* when the container does not exist — the caller (`attach()`) surfaces this
|
|
5
|
+
* as-is, which is the "attach fails clearly because the container is gone"
|
|
6
|
+
* contract. */
|
|
7
|
+
export async function inspectContainer(name) {
|
|
8
|
+
let stdout;
|
|
9
|
+
try {
|
|
10
|
+
stdout = await docker(['inspect', name]);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new Error(`crouter-env-docker: attach('${name}') failed — no such container (${error instanceof Error ? error.message : String(error)})`);
|
|
14
|
+
}
|
|
15
|
+
const parsed = JSON.parse(stdout);
|
|
16
|
+
const entry = parsed[0];
|
|
17
|
+
if (!entry) {
|
|
18
|
+
throw new Error(`crouter-env-docker: attach('${name}') failed — docker inspect returned no data`);
|
|
19
|
+
}
|
|
20
|
+
return entry;
|
|
21
|
+
}
|
|
22
|
+
export function readTokenFromInspect(inspection, name) {
|
|
23
|
+
const env = inspection.Config?.Env ?? [];
|
|
24
|
+
const entry = env.find((line) => line.startsWith('CRTRD_TOKEN='));
|
|
25
|
+
if (!entry) {
|
|
26
|
+
throw new Error(`crouter-env-docker: attach('${name}') failed — container has no CRTRD_TOKEN env var (was it started by this package's start()?)`);
|
|
27
|
+
}
|
|
28
|
+
return entry.slice('CRTRD_TOKEN='.length);
|
|
29
|
+
}
|
|
30
|
+
export function readHostPortFromInspect(inspection, name) {
|
|
31
|
+
const bindings = inspection.NetworkSettings?.Ports?.[`${CONTAINER_PORT}/tcp`];
|
|
32
|
+
const hostPort = bindings?.[0]?.HostPort;
|
|
33
|
+
if (!hostPort) {
|
|
34
|
+
throw new Error(`crouter-env-docker: attach('${name}') failed — no host port mapped for ${CONTAINER_PORT}/tcp (is the container running?)`);
|
|
35
|
+
}
|
|
36
|
+
return Number(hostPort);
|
|
37
|
+
}
|
|
38
|
+
export async function isContainerRunning(containerId) {
|
|
39
|
+
try {
|
|
40
|
+
const state = await docker(['inspect', '-f', '{{.State.Running}}', containerId]);
|
|
41
|
+
return state === 'true';
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function tryReadLogs(containerId) {
|
|
48
|
+
try {
|
|
49
|
+
return await docker(['logs', '--tail', '50', containerId]);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** `docker port <id> <containerPort>/tcp` prints one host binding per line —
|
|
56
|
+
* e.g. `0.0.0.0:54321` and `[::]:54321` for a dual-stack mapping. Take the
|
|
57
|
+
* first line's port. */
|
|
58
|
+
export async function resolveHostPort(containerId) {
|
|
59
|
+
const output = await docker(['port', containerId, `${CONTAINER_PORT}/tcp`]);
|
|
60
|
+
const firstLine = output.split('\n')[0]?.trim() ?? '';
|
|
61
|
+
const match = firstLine.match(/:(\d+)$/);
|
|
62
|
+
if (!match) {
|
|
63
|
+
throw new Error(`crouter-env-docker: could not determine the host port docker mapped for container ${containerId} (docker port output: ${JSON.stringify(output)})`);
|
|
64
|
+
}
|
|
65
|
+
return Number(match[1]);
|
|
66
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Connection {
|
|
2
|
+
baseURL: string;
|
|
3
|
+
headers?: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
export interface DockerEnvironment {
|
|
6
|
+
connection(): Connection;
|
|
7
|
+
/** The container's full id. */
|
|
8
|
+
containerId: string;
|
|
9
|
+
/** The container's name — as passed to `start({ name })` or generated, or the name passed to `attach()`. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** `start()`'s container: stop and remove. `attach()`'s container: stop only. */
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface StartOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Image to run. Default: `DEFAULT_IMAGE` (`ghcr.io/crouton-labs/crtrd:latest`).
|
|
17
|
+
* Pass a version tag to pin, or a locally built tag (e.g. `crtrd:local`).
|
|
18
|
+
*/
|
|
19
|
+
image?: string;
|
|
20
|
+
/** Extra env vars forwarded into the container — model provider keys, etc. Never logged. */
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
/** Container name. A named container is later addressable via `attach()`. Default: a generated random name. */
|
|
23
|
+
name?: string;
|
|
24
|
+
/** Named volume mounted at `CRTR_HOME` (`/home/agent/.crouter`). Default: none — an ephemeral, unnamed home. */
|
|
25
|
+
volume?: string;
|
|
26
|
+
/** Host port to bind, always on `127.0.0.1`. Default: a random free port docker assigns. */
|
|
27
|
+
port?: number;
|
|
28
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crouter/env-docker",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.377",
|
|
4
|
+
"description": "Docker connection manager for the crouter SDK — start, attach to, and stop a crtrd container over the docker CLI. Zero crouter imports.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/crouton-labs/crouter.git",
|
|
30
|
+
"directory": "packages/crouter-env-docker"
|
|
31
|
+
},
|
|
32
|
+
"license": "GPL-3.0-only"
|
|
33
|
+
}
|