@douglas-agent/sandbank-aio 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AIOSandboxAdapter.d.ts +38 -0
- package/dist/AIOSandboxAdapter.d.ts.map +1 -0
- package/dist/AIOSandboxAdapter.js +253 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +43 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AdapterSandbox, Capability, CreateConfig, ListFilter, SandboxAdapter, SandboxInfo } from '@douglas-agent/sandbank-core';
|
|
2
|
+
import { type AIOSandboxAdapterConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* AIOSandboxAdapter — wraps `ghcr.io/agent-infra/sandbox` Docker container
|
|
5
|
+
* lifecycle with dockerode, exposes business operations through
|
|
6
|
+
* `@agent-infra/sandbox` `SandboxClient`.
|
|
7
|
+
*
|
|
8
|
+
* Each `createSandbox()` call:
|
|
9
|
+
* 1. allocates a free host port from the configured range
|
|
10
|
+
* 2. `docker run` the AIO Sandbox image with port `8080` → host port mapping
|
|
11
|
+
* 3. polls container's `/health` until 200 (or timeout)
|
|
12
|
+
* 4. wraps the connected `SandboxClient` in an `AdapterSandbox`
|
|
13
|
+
*
|
|
14
|
+
* `destroySandbox(id)` runs `docker rm -f`. `listSandboxes()` enumerates
|
|
15
|
+
* containers labeled `sandbank-aio=true` to avoid touching unrelated containers
|
|
16
|
+
* on the host.
|
|
17
|
+
*/
|
|
18
|
+
export declare class AIOSandboxAdapter implements SandboxAdapter {
|
|
19
|
+
readonly name = "aio";
|
|
20
|
+
readonly capabilities: ReadonlySet<Capability>;
|
|
21
|
+
private readonly docker;
|
|
22
|
+
private readonly image;
|
|
23
|
+
private readonly portRange;
|
|
24
|
+
private readonly healthTimeoutSec;
|
|
25
|
+
private readonly apiKey?;
|
|
26
|
+
constructor(cfg?: AIOSandboxAdapterConfig);
|
|
27
|
+
createSandbox(config: CreateConfig): Promise<AdapterSandbox>;
|
|
28
|
+
getSandbox(id: string): Promise<AdapterSandbox>;
|
|
29
|
+
listSandboxes(filter?: ListFilter): Promise<SandboxInfo[]>;
|
|
30
|
+
destroySandbox(id: string): Promise<void>;
|
|
31
|
+
private makeClient;
|
|
32
|
+
private allocPort;
|
|
33
|
+
private portFree;
|
|
34
|
+
private waitHealth;
|
|
35
|
+
private extractHostPort;
|
|
36
|
+
private wrap;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=AIOSandboxAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AIOSandboxAdapter.d.ts","sourceRoot":"","sources":["../src/AIOSandboxAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EAGZ,UAAU,EACV,cAAc,EACd,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA2B5E;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,CAAoB;IAElE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;gBAErB,GAAG,GAAE,uBAA4B;IAcvC,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAiC5D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuB/C,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsB1D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C,OAAO,CAAC,UAAU;YASJ,SAAS;YAUT,QAAQ;YAYR,UAAU;IAmBxB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,IAAI;CASb"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import Docker from 'dockerode';
|
|
2
|
+
import { SandboxClient } from '@agent-infra/sandbox';
|
|
3
|
+
import { SandboxNotFoundError, ProviderError } from '@douglas-agent/sandbank-core';
|
|
4
|
+
import { AIO_CAPABILITIES } from './types.js';
|
|
5
|
+
const DEFAULT_IMAGE = 'ghcr.io/agent-infra/sandbox:latest';
|
|
6
|
+
const DEFAULT_PORT_RANGE = [49152, 65535];
|
|
7
|
+
const DEFAULT_HEALTH_TIMEOUT_SEC = 60;
|
|
8
|
+
const SANDBOX_INTERNAL_PORT = 8080;
|
|
9
|
+
/** Docker container state → sandbank SandboxState */
|
|
10
|
+
function mapDockerState(s) {
|
|
11
|
+
switch (s) {
|
|
12
|
+
case 'created':
|
|
13
|
+
return 'creating';
|
|
14
|
+
case 'running':
|
|
15
|
+
return 'running';
|
|
16
|
+
case 'paused':
|
|
17
|
+
case 'restarting':
|
|
18
|
+
return 'stopped';
|
|
19
|
+
case 'exited':
|
|
20
|
+
case 'dead':
|
|
21
|
+
return 'terminated';
|
|
22
|
+
case 'removing':
|
|
23
|
+
return 'stopped';
|
|
24
|
+
default:
|
|
25
|
+
return 'error';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* AIOSandboxAdapter — wraps `ghcr.io/agent-infra/sandbox` Docker container
|
|
30
|
+
* lifecycle with dockerode, exposes business operations through
|
|
31
|
+
* `@agent-infra/sandbox` `SandboxClient`.
|
|
32
|
+
*
|
|
33
|
+
* Each `createSandbox()` call:
|
|
34
|
+
* 1. allocates a free host port from the configured range
|
|
35
|
+
* 2. `docker run` the AIO Sandbox image with port `8080` → host port mapping
|
|
36
|
+
* 3. polls container's `/health` until 200 (or timeout)
|
|
37
|
+
* 4. wraps the connected `SandboxClient` in an `AdapterSandbox`
|
|
38
|
+
*
|
|
39
|
+
* `destroySandbox(id)` runs `docker rm -f`. `listSandboxes()` enumerates
|
|
40
|
+
* containers labeled `sandbank-aio=true` to avoid touching unrelated containers
|
|
41
|
+
* on the host.
|
|
42
|
+
*/
|
|
43
|
+
export class AIOSandboxAdapter {
|
|
44
|
+
name = 'aio';
|
|
45
|
+
capabilities = AIO_CAPABILITIES;
|
|
46
|
+
docker;
|
|
47
|
+
image;
|
|
48
|
+
portRange;
|
|
49
|
+
healthTimeoutSec;
|
|
50
|
+
apiKey;
|
|
51
|
+
constructor(cfg = {}) {
|
|
52
|
+
this.docker = new Docker(cfg.dockerSocketPath
|
|
53
|
+
? { socketPath: cfg.dockerSocketPath }
|
|
54
|
+
: cfg.dockerHost
|
|
55
|
+
? { host: cfg.dockerHost }
|
|
56
|
+
: undefined);
|
|
57
|
+
this.image = cfg.image ?? DEFAULT_IMAGE;
|
|
58
|
+
this.portRange = cfg.portRange ?? DEFAULT_PORT_RANGE;
|
|
59
|
+
this.healthTimeoutSec = cfg.healthTimeoutSec ?? DEFAULT_HEALTH_TIMEOUT_SEC;
|
|
60
|
+
this.apiKey = cfg.apiKey;
|
|
61
|
+
}
|
|
62
|
+
async createSandbox(config) {
|
|
63
|
+
const port = await this.allocPort();
|
|
64
|
+
const envArr = Object.entries(config.env ?? {}).map(([k, v]) => `${k}=${v}`);
|
|
65
|
+
let container;
|
|
66
|
+
try {
|
|
67
|
+
container = await this.docker.createContainer({
|
|
68
|
+
Image: config.image ?? this.image,
|
|
69
|
+
Env: envArr,
|
|
70
|
+
Labels: { 'sandbank-aio': 'true' },
|
|
71
|
+
HostConfig: {
|
|
72
|
+
PortBindings: {
|
|
73
|
+
[`${SANDBOX_INTERNAL_PORT}/tcp`]: [{ HostPort: String(port) }],
|
|
74
|
+
},
|
|
75
|
+
SecurityOpt: ['seccomp=unconfined'],
|
|
76
|
+
AutoRemove: true,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
await container.start();
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
throw new ProviderError('aio', e);
|
|
83
|
+
}
|
|
84
|
+
const baseUrl = `http://localhost:${port}`;
|
|
85
|
+
await this.waitHealth(baseUrl);
|
|
86
|
+
const client = this.makeClient(baseUrl);
|
|
87
|
+
const createdAt = new Date().toISOString();
|
|
88
|
+
return this.wrap(container.id, baseUrl, client, 'running', createdAt);
|
|
89
|
+
}
|
|
90
|
+
async getSandbox(id) {
|
|
91
|
+
const container = this.docker.getContainer(id);
|
|
92
|
+
let info;
|
|
93
|
+
try {
|
|
94
|
+
info = await container.inspect();
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
if (e.statusCode === 404) {
|
|
98
|
+
throw new SandboxNotFoundError('aio', id);
|
|
99
|
+
}
|
|
100
|
+
throw new ProviderError('aio', e, id);
|
|
101
|
+
}
|
|
102
|
+
const port = this.extractHostPort(info);
|
|
103
|
+
const baseUrl = port ? `http://localhost:${port}` : '';
|
|
104
|
+
const client = baseUrl ? this.makeClient(baseUrl) : undefined;
|
|
105
|
+
return this.wrap(info.Id, baseUrl, client, mapDockerState(info.State.Status), info.Created);
|
|
106
|
+
}
|
|
107
|
+
async listSandboxes(filter) {
|
|
108
|
+
const containers = await this.docker.listContainers({
|
|
109
|
+
all: true,
|
|
110
|
+
filters: { label: ['sandbank-aio=true'] },
|
|
111
|
+
});
|
|
112
|
+
const states = filter?.state
|
|
113
|
+
? Array.isArray(filter.state)
|
|
114
|
+
? filter.state
|
|
115
|
+
: [filter.state]
|
|
116
|
+
: null;
|
|
117
|
+
const limit = filter?.limit ?? Infinity;
|
|
118
|
+
return containers
|
|
119
|
+
.map((c) => ({
|
|
120
|
+
id: c.Id,
|
|
121
|
+
state: mapDockerState(c.State),
|
|
122
|
+
createdAt: new Date(c.Created * 1000).toISOString(),
|
|
123
|
+
image: c.Image,
|
|
124
|
+
}))
|
|
125
|
+
.filter((info) => (states ? states.includes(info.state) : true))
|
|
126
|
+
.slice(0, limit);
|
|
127
|
+
}
|
|
128
|
+
async destroySandbox(id) {
|
|
129
|
+
try {
|
|
130
|
+
await this.docker.getContainer(id).remove({ force: true });
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
if (e.statusCode === 404) {
|
|
134
|
+
return; // idempotent
|
|
135
|
+
}
|
|
136
|
+
throw new ProviderError('aio', e, id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ── internals ────────────────────────────────────────
|
|
140
|
+
makeClient(baseUrl) {
|
|
141
|
+
const headers = {};
|
|
142
|
+
if (this.apiKey)
|
|
143
|
+
headers['X-API-Key'] = this.apiKey;
|
|
144
|
+
return new SandboxClient({
|
|
145
|
+
environment: () => baseUrl,
|
|
146
|
+
headers: Object.keys(headers).length ? headers : undefined,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async allocPort() {
|
|
150
|
+
// Naive linear probe within range. dockerode's `PortBindings` returns
|
|
151
|
+
// EADDRINUSE if the port is taken, so we retry. Good enough for POC.
|
|
152
|
+
const [lo, hi] = this.portRange;
|
|
153
|
+
for (let p = lo; p <= hi; p++) {
|
|
154
|
+
if (await this.portFree(p))
|
|
155
|
+
return p;
|
|
156
|
+
}
|
|
157
|
+
throw new ProviderError('aio', new Error(`portRange [${lo},${hi}] exhausted`));
|
|
158
|
+
}
|
|
159
|
+
async portFree(port) {
|
|
160
|
+
// Best-effort host-side check. dockerode does the authoritative bind.
|
|
161
|
+
return new Promise((resolve) => {
|
|
162
|
+
const net = globalThis.require?.('net');
|
|
163
|
+
if (!net)
|
|
164
|
+
return resolve(true); // skip in test environments
|
|
165
|
+
const srv = net.createServer();
|
|
166
|
+
srv.once('error', () => resolve(false));
|
|
167
|
+
srv.once('listening', () => srv.close(() => resolve(true)));
|
|
168
|
+
srv.listen(port, '127.0.0.1');
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async waitHealth(baseUrl) {
|
|
172
|
+
const deadline = Date.now() + this.healthTimeoutSec * 1000;
|
|
173
|
+
let lastErr;
|
|
174
|
+
while (Date.now() < deadline) {
|
|
175
|
+
try {
|
|
176
|
+
const res = await fetch(`${baseUrl}/health`, { method: 'GET' });
|
|
177
|
+
if (res.ok)
|
|
178
|
+
return;
|
|
179
|
+
lastErr = new Error(`HTTP ${res.status}`);
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
lastErr = e;
|
|
183
|
+
}
|
|
184
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
185
|
+
}
|
|
186
|
+
throw new ProviderError('aio', new Error(`/health did not become ready within ${this.healthTimeoutSec}s: ${lastErr?.message ?? 'unknown'}`));
|
|
187
|
+
}
|
|
188
|
+
extractHostPort(info) {
|
|
189
|
+
const bindings = info.NetworkSettings?.Ports?.[`${SANDBOX_INTERNAL_PORT}/tcp`];
|
|
190
|
+
const first = bindings?.[0];
|
|
191
|
+
if (!first?.HostPort)
|
|
192
|
+
return null;
|
|
193
|
+
const port = parseInt(first.HostPort, 10);
|
|
194
|
+
return Number.isFinite(port) ? port : null;
|
|
195
|
+
}
|
|
196
|
+
wrap(id, baseUrl, client, state, createdAt) {
|
|
197
|
+
return new AIOAdapterSandbox(id, baseUrl, client, state, createdAt, this.docker);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/** AdapterSandbox impl: delegates exec/file ops to SandboxClient (HTTP). */
|
|
201
|
+
class AIOAdapterSandbox {
|
|
202
|
+
id;
|
|
203
|
+
baseUrl;
|
|
204
|
+
client;
|
|
205
|
+
state;
|
|
206
|
+
createdAt;
|
|
207
|
+
docker;
|
|
208
|
+
constructor(id, baseUrl, client, state, createdAt, docker) {
|
|
209
|
+
this.id = id;
|
|
210
|
+
this.baseUrl = baseUrl;
|
|
211
|
+
this.client = client;
|
|
212
|
+
this.state = state;
|
|
213
|
+
this.createdAt = createdAt;
|
|
214
|
+
this.docker = docker;
|
|
215
|
+
}
|
|
216
|
+
async exec(command, options) {
|
|
217
|
+
if (!this.client) {
|
|
218
|
+
throw new ProviderError('aio', new Error('no API baseUrl (likely stopped)'), this.id);
|
|
219
|
+
}
|
|
220
|
+
// Delegate to SandboxClient.bash.exec.
|
|
221
|
+
// NOTE: the exact SDK shape varies by @agent-infra/sandbox version —
|
|
222
|
+
// implementation-phase task confirms current API & wires up timeout/cwd.
|
|
223
|
+
try {
|
|
224
|
+
const result = (await this.client.bash?.exec?.({
|
|
225
|
+
command,
|
|
226
|
+
cwd: options?.cwd,
|
|
227
|
+
timeout_ms: options?.timeout,
|
|
228
|
+
}));
|
|
229
|
+
return {
|
|
230
|
+
stdout: result?.stdout ?? '',
|
|
231
|
+
stderr: result?.stderr ?? '',
|
|
232
|
+
exitCode: result?.exit_code ?? 0,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
throw new ProviderError('aio', e, this.id);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async writeFile(path, content) {
|
|
240
|
+
if (!this.client) {
|
|
241
|
+
throw new ProviderError('aio', new Error('no API baseUrl'), this.id);
|
|
242
|
+
}
|
|
243
|
+
const body = typeof content === 'string' ? content : Buffer.from(content).toString('utf8');
|
|
244
|
+
await this.client.file?.write?.({ path, content: body });
|
|
245
|
+
}
|
|
246
|
+
async readFile(path) {
|
|
247
|
+
if (!this.client) {
|
|
248
|
+
throw new ProviderError('aio', new Error('no API baseUrl'), this.id);
|
|
249
|
+
}
|
|
250
|
+
const result = (await this.client.file?.read?.({ path }));
|
|
251
|
+
return new TextEncoder().encode(result?.content ?? '');
|
|
252
|
+
}
|
|
253
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Capability } from '@douglas-agent/sandbank-core';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for AIOSandboxAdapter.
|
|
4
|
+
*
|
|
5
|
+
* The adapter uses dockerode to manage container lifecycle (create / destroy)
|
|
6
|
+
* and `@agent-infra/sandbox` `SandboxClient` to talk to the sandbox HTTP API
|
|
7
|
+
* once the container is healthy.
|
|
8
|
+
*/
|
|
9
|
+
export interface AIOSandboxAdapterConfig {
|
|
10
|
+
/**
|
|
11
|
+
* AIO Sandbox container image. Defaults to upstream release.
|
|
12
|
+
* @default 'ghcr.io/agent-infra/sandbox:latest'
|
|
13
|
+
*/
|
|
14
|
+
image?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Docker daemon socket path. When undefined, dockerode falls back to its
|
|
17
|
+
* default (Unix socket on Linux/macOS, named pipe on Windows).
|
|
18
|
+
* @example '/var/run/docker.sock'
|
|
19
|
+
*/
|
|
20
|
+
dockerSocketPath?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Docker daemon TCP endpoint (alternative to socket path).
|
|
23
|
+
* @example 'tcp://localhost:2375'
|
|
24
|
+
*/
|
|
25
|
+
dockerHost?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Host port range to allocate for sandbox HTTP API binding.
|
|
28
|
+
* Each `createSandbox` reserves one port from this range.
|
|
29
|
+
* @default [49152, 65535] // standard ephemeral range
|
|
30
|
+
*/
|
|
31
|
+
portRange?: [number, number];
|
|
32
|
+
/**
|
|
33
|
+
* Max seconds to wait for sandbox /health to respond 200 after `docker start`.
|
|
34
|
+
* @default 60
|
|
35
|
+
*/
|
|
36
|
+
healthTimeoutSec?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Optional API key forwarded to SandboxClient via `X-API-Key` header.
|
|
39
|
+
* Most self-hosted AIO Sandbox deployments leave this unset.
|
|
40
|
+
*/
|
|
41
|
+
apiKey?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Capabilities declared by AIOSandboxAdapter. */
|
|
44
|
+
export declare const AIO_CAPABILITIES: ReadonlySet<Capability>;
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,UAAU,CAInD,CAAC"}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@douglas-agent/sandbank-aio",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AIO Sandbox adapter (字节 agent-infra/sandbox) for Sandbank — dockerode lifecycle + @agent-infra/sandbox SDK for in-container ops",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"homepage": "https://github.com/Xeonice/sandbank-douglas-agent",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Xeonice/sandbank-douglas-agent.git",
|
|
11
|
+
"directory": "packages/aio"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"sandbox",
|
|
15
|
+
"ai-agent",
|
|
16
|
+
"aio-sandbox",
|
|
17
|
+
"agent-infra",
|
|
18
|
+
"bytedance"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"clean": "rm -rf dist"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@douglas-agent/sandbank-core": "^0.3.6",
|
|
36
|
+
"@agent-infra/sandbox": "^1.0.16",
|
|
37
|
+
"dockerode": "^4.0.10"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/dockerode": "^3.3.40",
|
|
41
|
+
"typescript": "^5.7.3"
|
|
42
|
+
}
|
|
43
|
+
}
|