@ash-ai/sandbox 0.0.1
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 -0
- package/dist/bridge-client.d.ts +26 -0
- package/dist/bridge-client.d.ts.map +1 -0
- package/dist/bridge-client.js +123 -0
- package/dist/bridge-client.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +39 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +161 -0
- package/dist/manager.js.map +1 -0
- package/dist/pool.d.ts +69 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +251 -0
- package/dist/pool.js.map +1 -0
- package/dist/resource-limits.d.ts +21 -0
- package/dist/resource-limits.d.ts.map +1 -0
- package/dist/resource-limits.js +139 -0
- package/dist/resource-limits.js.map +1 -0
- package/dist/snapshot-gcs.d.ts +13 -0
- package/dist/snapshot-gcs.d.ts.map +1 -0
- package/dist/snapshot-gcs.js +63 -0
- package/dist/snapshot-gcs.js.map +1 -0
- package/dist/snapshot-s3.d.ts +13 -0
- package/dist/snapshot-s3.d.ts.map +1 -0
- package/dist/snapshot-s3.js +75 -0
- package/dist/snapshot-s3.js.map +1 -0
- package/dist/snapshot-store.d.ts +29 -0
- package/dist/snapshot-store.d.ts.map +1 -0
- package/dist/snapshot-store.js +74 -0
- package/dist/snapshot-store.js.map +1 -0
- package/dist/state-persistence.d.ts +45 -0
- package/dist/state-persistence.d.ts.map +1 -0
- package/dist/state-persistence.js +208 -0
- package/dist/state-persistence.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ash Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BridgeCommand, type BridgeEvent } from '@ash-ai/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Client-side of the bridge Unix socket protocol.
|
|
4
|
+
* Connects to a bridge process inside a sandbox and sends commands / receives events.
|
|
5
|
+
*/
|
|
6
|
+
export declare class BridgeClient {
|
|
7
|
+
private socketPath;
|
|
8
|
+
private socket;
|
|
9
|
+
private buffer;
|
|
10
|
+
private listeners;
|
|
11
|
+
constructor(socketPath: string);
|
|
12
|
+
/**
|
|
13
|
+
* Connect to the bridge socket and wait for the 'ready' event.
|
|
14
|
+
*/
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Send a command and return an async iterable of events until 'done' or 'error'.
|
|
18
|
+
*/
|
|
19
|
+
sendCommand(cmd: BridgeCommand): AsyncGenerator<BridgeEvent>;
|
|
20
|
+
disconnect(): void;
|
|
21
|
+
get connected(): boolean;
|
|
22
|
+
private setupDataHandler;
|
|
23
|
+
private addListener;
|
|
24
|
+
private removeListener;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=bridge-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,WAAW,EAA2B,MAAM,gBAAgB,CAAC;AAE/G;;;GAGG;AACH,qBAAa,YAAY;IAKX,OAAO,CAAC,UAAU;IAJ9B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,SAAS,CAA2C;gBAExC,UAAU,EAAE,MAAM;IAEtC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B;;OAEG;IACI,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAAC,WAAW,CAAC;IAqCnE,UAAU,IAAI,IAAI;IAMlB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,cAAc;CAGvB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { encode, decode, BRIDGE_READY_TIMEOUT_MS } from '@ash-ai/shared';
|
|
3
|
+
/**
|
|
4
|
+
* Client-side of the bridge Unix socket protocol.
|
|
5
|
+
* Connects to a bridge process inside a sandbox and sends commands / receives events.
|
|
6
|
+
*/
|
|
7
|
+
export class BridgeClient {
|
|
8
|
+
socketPath;
|
|
9
|
+
socket = null;
|
|
10
|
+
buffer = '';
|
|
11
|
+
listeners = [];
|
|
12
|
+
constructor(socketPath) {
|
|
13
|
+
this.socketPath = socketPath;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Connect to the bridge socket and wait for the 'ready' event.
|
|
17
|
+
*/
|
|
18
|
+
async connect() {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const timeout = setTimeout(() => {
|
|
21
|
+
reject(new Error(`Bridge did not become ready within ${BRIDGE_READY_TIMEOUT_MS}ms`));
|
|
22
|
+
}, BRIDGE_READY_TIMEOUT_MS);
|
|
23
|
+
const tryConnect = () => {
|
|
24
|
+
const sock = net.createConnection(this.socketPath);
|
|
25
|
+
sock.on('connect', () => {
|
|
26
|
+
this.socket = sock;
|
|
27
|
+
this.setupDataHandler(sock);
|
|
28
|
+
});
|
|
29
|
+
sock.on('error', (err) => {
|
|
30
|
+
if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED' || err.code === 'EINVAL') {
|
|
31
|
+
// Socket not ready yet — retry
|
|
32
|
+
sock.destroy();
|
|
33
|
+
setTimeout(tryConnect, 100);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
reject(err);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
// Wait for 'ready' event
|
|
42
|
+
const onReady = (event) => {
|
|
43
|
+
if (event.ev === 'ready') {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
this.removeListener(onReady);
|
|
46
|
+
resolve();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
this.addListener(onReady);
|
|
50
|
+
tryConnect();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Send a command and return an async iterable of events until 'done' or 'error'.
|
|
55
|
+
*/
|
|
56
|
+
async *sendCommand(cmd) {
|
|
57
|
+
if (!this.socket)
|
|
58
|
+
throw new Error('Not connected');
|
|
59
|
+
this.socket.write(encode(cmd));
|
|
60
|
+
// Yield events until done
|
|
61
|
+
const queue = [];
|
|
62
|
+
let resolve = null;
|
|
63
|
+
let done = false;
|
|
64
|
+
const listener = (event) => {
|
|
65
|
+
queue.push(event);
|
|
66
|
+
resolve?.();
|
|
67
|
+
};
|
|
68
|
+
this.addListener(listener);
|
|
69
|
+
try {
|
|
70
|
+
while (!done) {
|
|
71
|
+
if (queue.length === 0) {
|
|
72
|
+
await new Promise((r) => { resolve = r; });
|
|
73
|
+
resolve = null;
|
|
74
|
+
}
|
|
75
|
+
while (queue.length > 0) {
|
|
76
|
+
const event = queue.shift();
|
|
77
|
+
yield event;
|
|
78
|
+
if (event.ev === 'done' || event.ev === 'error') {
|
|
79
|
+
done = true;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.removeListener(listener);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
disconnect() {
|
|
90
|
+
this.socket?.destroy();
|
|
91
|
+
this.socket = null;
|
|
92
|
+
this.listeners = [];
|
|
93
|
+
}
|
|
94
|
+
get connected() {
|
|
95
|
+
return this.socket !== null && !this.socket.destroyed;
|
|
96
|
+
}
|
|
97
|
+
setupDataHandler(sock) {
|
|
98
|
+
sock.on('data', (chunk) => {
|
|
99
|
+
this.buffer += chunk.toString();
|
|
100
|
+
let newline;
|
|
101
|
+
while ((newline = this.buffer.indexOf('\n')) !== -1) {
|
|
102
|
+
const line = this.buffer.slice(0, newline);
|
|
103
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
104
|
+
if (line.trim()) {
|
|
105
|
+
const event = decode(line);
|
|
106
|
+
for (const listener of this.listeners) {
|
|
107
|
+
listener(event);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
sock.on('close', () => {
|
|
113
|
+
this.socket = null;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
addListener(fn) {
|
|
117
|
+
this.listeners.push(fn);
|
|
118
|
+
}
|
|
119
|
+
removeListener(fn) {
|
|
120
|
+
this.listeners = this.listeners.filter((l) => l !== fn);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=bridge-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-client.js","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { SandboxManager } from './manager.js';
|
|
2
|
+
export type { ManagedSandbox, CreateSandboxOpts } from './manager.js';
|
|
3
|
+
export { SandboxPool } from './pool.js';
|
|
4
|
+
export type { PoolEntry, SandboxPoolOpts, LiveSandboxState, SandboxDb } from './pool.js';
|
|
5
|
+
export { BridgeClient } from './bridge-client.js';
|
|
6
|
+
export { spawnWithLimits, isOomExit, getDirSizeKb, startDiskMonitor, createCgroup, addToCgroup, removeCgroup, DEFAULT_SANDBOX_LIMITS, } from './resource-limits.js';
|
|
7
|
+
export type { SpawnResult, SandboxSpawnOpts } from './resource-limits.js';
|
|
8
|
+
export { persistSessionState, restoreSessionState, hasPersistedState, deleteSessionState, getStateMetadata, syncStateToCloud, restoreStateFromCloud, deleteCloudState, } from './state-persistence.js';
|
|
9
|
+
export type { SnapshotStore } from './snapshot-store.js';
|
|
10
|
+
export { createSnapshotStore, getSnapshotStore, resetSnapshotStore } from './snapshot-store.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SandboxManager } from './manager.js';
|
|
2
|
+
export { SandboxPool } from './pool.js';
|
|
3
|
+
export { BridgeClient } from './bridge-client.js';
|
|
4
|
+
export { spawnWithLimits, isOomExit, getDirSizeKb, startDiskMonitor, createCgroup, addToCgroup, removeCgroup, DEFAULT_SANDBOX_LIMITS, } from './resource-limits.js';
|
|
5
|
+
export { persistSessionState, restoreSessionState, hasPersistedState, deleteSessionState, getStateMetadata, syncStateToCloud, restoreStateFromCloud, deleteCloudState, } from './state-persistence.js';
|
|
6
|
+
export { createSnapshotStore, getSnapshotStore, resetSnapshotStore } from './snapshot-store.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { SandboxLimits } from '@ash-ai/shared';
|
|
3
|
+
import { BridgeClient } from './bridge-client.js';
|
|
4
|
+
export interface ManagedSandbox {
|
|
5
|
+
id: string;
|
|
6
|
+
process: ChildProcess;
|
|
7
|
+
client: BridgeClient;
|
|
8
|
+
socketPath: string;
|
|
9
|
+
workspaceDir: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
limits: SandboxLimits;
|
|
12
|
+
}
|
|
13
|
+
export interface CreateSandboxOpts {
|
|
14
|
+
agentDir: string;
|
|
15
|
+
sessionId: string;
|
|
16
|
+
/** Use a specific sandbox ID instead of generating a random one. */
|
|
17
|
+
id?: string;
|
|
18
|
+
/** Skip copying agent files into workspace (for resume when workspace already exists). */
|
|
19
|
+
skipAgentCopy?: boolean;
|
|
20
|
+
limits?: Partial<SandboxLimits>;
|
|
21
|
+
onOomKill?: (sandboxId: string) => void;
|
|
22
|
+
}
|
|
23
|
+
export declare class SandboxManager {
|
|
24
|
+
private sandboxes;
|
|
25
|
+
private sandboxesDir;
|
|
26
|
+
private bridgeEntry;
|
|
27
|
+
private defaultLimits;
|
|
28
|
+
constructor(opts: {
|
|
29
|
+
sandboxesDir: string;
|
|
30
|
+
bridgeEntry: string;
|
|
31
|
+
defaultLimits?: Partial<SandboxLimits>;
|
|
32
|
+
});
|
|
33
|
+
create(opts: CreateSandboxOpts): Promise<ManagedSandbox>;
|
|
34
|
+
get(id: string): ManagedSandbox | undefined;
|
|
35
|
+
destroy(id: string): Promise<void>;
|
|
36
|
+
destroyAll(): Promise<void>;
|
|
37
|
+
get activeCount(): number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAA0B,MAAM,oBAAoB,CAAC;AAM/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0FAA0F;IAC1F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AASD,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAAgB;gBAEzB,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;KAAE;IAOjG,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IA8H9D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BlC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,IAAI,WAAW,IAAI,MAAM,CAExB;CACF"}
|
package/dist/manager.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { execSync, execFileSync } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, cpSync, unlinkSync, existsSync, chmodSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { SANDBOX_ENV_ALLOWLIST, DEFAULT_SANDBOX_LIMITS, INSTALL_SCRIPT_TIMEOUT_MS } from '@ash-ai/shared';
|
|
7
|
+
import { BridgeClient } from './bridge-client.js';
|
|
8
|
+
import { spawnWithLimits, isOomExit, startDiskMonitor } from './resource-limits.js';
|
|
9
|
+
export class SandboxManager {
|
|
10
|
+
sandboxes = new Map();
|
|
11
|
+
sandboxesDir;
|
|
12
|
+
bridgeEntry;
|
|
13
|
+
defaultLimits;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.sandboxesDir = opts.sandboxesDir;
|
|
16
|
+
this.bridgeEntry = opts.bridgeEntry;
|
|
17
|
+
this.defaultLimits = { ...DEFAULT_SANDBOX_LIMITS, ...opts.defaultLimits };
|
|
18
|
+
mkdirSync(this.sandboxesDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
async create(opts) {
|
|
21
|
+
const id = opts.id ?? randomUUID();
|
|
22
|
+
const shortId = id.slice(0, 8);
|
|
23
|
+
const sandboxDir = join(this.sandboxesDir, id);
|
|
24
|
+
const workspaceDir = join(sandboxDir, 'workspace');
|
|
25
|
+
// macOS limits Unix socket paths to 104 bytes — use /tmp with short ID
|
|
26
|
+
const socketPath = join(tmpdir(), `ash-${shortId}.sock`);
|
|
27
|
+
const limits = { ...this.defaultLimits, ...opts.limits };
|
|
28
|
+
if (!opts.skipAgentCopy) {
|
|
29
|
+
// Copy entire agent directory into workspace — no special cases.
|
|
30
|
+
// CLAUDE.md, .claude/, .mcp.json, and any other files the SDK needs
|
|
31
|
+
// all live in the agent dir and get copied as-is.
|
|
32
|
+
cpSync(opts.agentDir, workspaceDir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Resume path: workspace already exists, just ensure the dir is there
|
|
36
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
// SECURITY: Allowlist env — nothing else leaks to sandbox
|
|
39
|
+
const env = {};
|
|
40
|
+
for (const key of SANDBOX_ENV_ALLOWLIST) {
|
|
41
|
+
if (process.env[key]) {
|
|
42
|
+
env[key] = process.env[key];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
env.ASH_BRIDGE_SOCKET = socketPath;
|
|
46
|
+
env.ASH_AGENT_DIR = opts.agentDir;
|
|
47
|
+
env.ASH_WORKSPACE_DIR = workspaceDir;
|
|
48
|
+
env.ASH_SANDBOX_ID = id;
|
|
49
|
+
env.ASH_SESSION_ID = opts.sessionId;
|
|
50
|
+
// When ASH_SANDBOX_UID is set (Docker), run bridge as non-root user.
|
|
51
|
+
// Claude Code refuses --dangerously-skip-permissions as root.
|
|
52
|
+
const sandboxUid = process.env.ASH_SANDBOX_UID ? parseInt(process.env.ASH_SANDBOX_UID, 10) : undefined;
|
|
53
|
+
const sandboxGid = process.env.ASH_SANDBOX_GID ? parseInt(process.env.ASH_SANDBOX_GID, 10) : undefined;
|
|
54
|
+
if (sandboxUid !== undefined) {
|
|
55
|
+
env.HOME = `/home/ash-sandbox`;
|
|
56
|
+
// Recursively chown so the non-root sandbox user can write to all files
|
|
57
|
+
execSync(`chown -R ${sandboxUid}:${sandboxGid ?? sandboxUid} '${sandboxDir}'`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
env.HOME = workspaceDir;
|
|
61
|
+
}
|
|
62
|
+
// Run install.sh if present (only on fresh creation, not resume)
|
|
63
|
+
if (!opts.skipAgentCopy) {
|
|
64
|
+
const installScript = join(workspaceDir, 'install.sh');
|
|
65
|
+
if (existsSync(installScript)) {
|
|
66
|
+
console.log(`[sandbox:${shortId}] Running install.sh...`);
|
|
67
|
+
chmodSync(installScript, 0o755);
|
|
68
|
+
try {
|
|
69
|
+
const installOutput = execFileSync(installScript, [], {
|
|
70
|
+
cwd: workspaceDir,
|
|
71
|
+
env,
|
|
72
|
+
timeout: INSTALL_SCRIPT_TIMEOUT_MS,
|
|
73
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
74
|
+
...(sandboxUid !== undefined && { uid: sandboxUid, gid: sandboxGid }),
|
|
75
|
+
});
|
|
76
|
+
if (installOutput.length > 0) {
|
|
77
|
+
console.log(`[sandbox:${shortId}:install] ${installOutput.toString().trimEnd()}`);
|
|
78
|
+
}
|
|
79
|
+
console.log(`[sandbox:${shortId}] install.sh completed`);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
83
|
+
throw new Error(`install.sh failed for sandbox ${shortId}: ${msg}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const spawnOpts = {
|
|
88
|
+
env, cwd: workspaceDir, stdio: ['ignore', 'pipe', 'pipe'],
|
|
89
|
+
...(sandboxUid !== undefined && { uid: sandboxUid, gid: sandboxGid }),
|
|
90
|
+
};
|
|
91
|
+
const { child, cleanup: resourceCleanup } = spawnWithLimits('node', [this.bridgeEntry], spawnOpts, limits, { sandboxId: id, workspaceDir, agentDir: opts.agentDir });
|
|
92
|
+
child.stdout?.on('data', (chunk) => {
|
|
93
|
+
console.log(`[sandbox:${shortId}:out] ${chunk.toString().trimEnd()}`);
|
|
94
|
+
});
|
|
95
|
+
child.stderr?.on('data', (chunk) => {
|
|
96
|
+
console.error(`[sandbox:${shortId}:err] ${chunk.toString().trimEnd()}`);
|
|
97
|
+
});
|
|
98
|
+
// OOM detection
|
|
99
|
+
child.on('exit', (code, signal) => {
|
|
100
|
+
console.error(`[sandbox:${shortId}] exited code=${code} signal=${signal}`);
|
|
101
|
+
if (isOomExit(code, signal)) {
|
|
102
|
+
console.error(`[sandbox:${shortId}] OOM killed (memory limit: ${limits.memoryMb}MB)`);
|
|
103
|
+
opts.onOomKill?.(id);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const client = new BridgeClient(socketPath);
|
|
107
|
+
await client.connect();
|
|
108
|
+
const sandbox = {
|
|
109
|
+
id,
|
|
110
|
+
process: child,
|
|
111
|
+
client,
|
|
112
|
+
socketPath,
|
|
113
|
+
workspaceDir,
|
|
114
|
+
createdAt: new Date().toISOString(),
|
|
115
|
+
limits,
|
|
116
|
+
};
|
|
117
|
+
// Disk monitoring — kill sandbox if workspace exceeds disk limit
|
|
118
|
+
const diskMonitor = startDiskMonitor(workspaceDir, limits.diskMb, () => {
|
|
119
|
+
console.error(`[sandbox:${shortId}] exceeded disk limit (${limits.diskMb}MB), killing`);
|
|
120
|
+
this.destroy(id);
|
|
121
|
+
});
|
|
122
|
+
this.sandboxes.set(id, { sandbox, resourceCleanup, diskMonitor });
|
|
123
|
+
return sandbox;
|
|
124
|
+
}
|
|
125
|
+
get(id) {
|
|
126
|
+
return this.sandboxes.get(id)?.sandbox;
|
|
127
|
+
}
|
|
128
|
+
async destroy(id) {
|
|
129
|
+
const internal = this.sandboxes.get(id);
|
|
130
|
+
if (!internal)
|
|
131
|
+
return;
|
|
132
|
+
const { sandbox, resourceCleanup, diskMonitor } = internal;
|
|
133
|
+
clearInterval(diskMonitor);
|
|
134
|
+
sandbox.client.disconnect();
|
|
135
|
+
if (sandbox.process.exitCode === null) {
|
|
136
|
+
sandbox.process.kill('SIGTERM');
|
|
137
|
+
// Wait for exit with timeout
|
|
138
|
+
await Promise.race([
|
|
139
|
+
new Promise((resolve) => sandbox.process.on('exit', resolve)),
|
|
140
|
+
new Promise((resolve) => setTimeout(resolve, 5000)),
|
|
141
|
+
]);
|
|
142
|
+
if (sandbox.process.exitCode === null) {
|
|
143
|
+
sandbox.process.kill('SIGKILL');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
unlinkSync(sandbox.socketPath);
|
|
148
|
+
}
|
|
149
|
+
catch { /* already gone */ }
|
|
150
|
+
resourceCleanup();
|
|
151
|
+
this.sandboxes.delete(id);
|
|
152
|
+
}
|
|
153
|
+
async destroyAll() {
|
|
154
|
+
const ids = [...this.sandboxes.keys()];
|
|
155
|
+
await Promise.all(ids.map((id) => this.destroy(id)));
|
|
156
|
+
}
|
|
157
|
+
get activeCount() {
|
|
158
|
+
return this.sandboxes.size;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
|
package/dist/pool.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PoolStats, SandboxState, SandboxRecord } from '@ash-ai/shared';
|
|
2
|
+
import type { ManagedSandbox, CreateSandboxOpts, SandboxManager } from './manager.js';
|
|
3
|
+
export type LiveSandboxState = 'warming' | 'warm' | 'waiting' | 'running';
|
|
4
|
+
/**
|
|
5
|
+
* Subset of the Db interface that SandboxPool needs.
|
|
6
|
+
* The full Db (with agent/session methods) lives in the server package.
|
|
7
|
+
* This narrower type avoids a circular dependency.
|
|
8
|
+
*/
|
|
9
|
+
export interface SandboxDb {
|
|
10
|
+
insertSandbox(id: string, agentName: string, workspaceDir: string, sessionId?: string): Promise<void>;
|
|
11
|
+
updateSandboxState(id: string, state: SandboxState): Promise<void>;
|
|
12
|
+
updateSandboxSession(id: string, sessionId: string | null): Promise<void>;
|
|
13
|
+
touchSandbox(id: string): Promise<void>;
|
|
14
|
+
getSandbox(id: string): Promise<SandboxRecord | null>;
|
|
15
|
+
countSandboxes(): Promise<number>;
|
|
16
|
+
getBestEvictionCandidate(): Promise<SandboxRecord | null>;
|
|
17
|
+
getIdleSandboxes(olderThan: string): Promise<SandboxRecord[]>;
|
|
18
|
+
deleteSandbox(id: string): Promise<void>;
|
|
19
|
+
markAllSandboxesCold(): Promise<number>;
|
|
20
|
+
}
|
|
21
|
+
export interface PoolEntry {
|
|
22
|
+
sandbox: ManagedSandbox;
|
|
23
|
+
state: LiveSandboxState;
|
|
24
|
+
sessionId: string | null;
|
|
25
|
+
agentName: string;
|
|
26
|
+
}
|
|
27
|
+
export interface SandboxPoolOpts {
|
|
28
|
+
manager: SandboxManager;
|
|
29
|
+
db: SandboxDb;
|
|
30
|
+
dataDir: string;
|
|
31
|
+
maxCapacity?: number;
|
|
32
|
+
idleTimeoutMs?: number;
|
|
33
|
+
onBeforeEvict?: (entry: PoolEntry) => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export declare class SandboxPool {
|
|
36
|
+
private live;
|
|
37
|
+
private sessionIndex;
|
|
38
|
+
private manager;
|
|
39
|
+
private db;
|
|
40
|
+
private dataDir;
|
|
41
|
+
private maxCapacity;
|
|
42
|
+
private idleTimeoutMs;
|
|
43
|
+
private onBeforeEvict?;
|
|
44
|
+
private sweepTimer;
|
|
45
|
+
private resumeWarmHits;
|
|
46
|
+
private resumeColdHits;
|
|
47
|
+
constructor(opts: SandboxPoolOpts);
|
|
48
|
+
init(): Promise<void>;
|
|
49
|
+
create(opts: CreateSandboxOpts & {
|
|
50
|
+
agentName: string;
|
|
51
|
+
}): Promise<ManagedSandbox>;
|
|
52
|
+
get(sandboxId: string): ManagedSandbox | undefined;
|
|
53
|
+
getEntry(sandboxId: string): PoolEntry | undefined;
|
|
54
|
+
getSandboxForSession(sessionId: string): ManagedSandbox | undefined;
|
|
55
|
+
destroy(sandboxId: string): Promise<void>;
|
|
56
|
+
destroyAll(): Promise<void>;
|
|
57
|
+
markRunning(sandboxId: string): void;
|
|
58
|
+
markWaiting(sandboxId: string): void;
|
|
59
|
+
private evictOne;
|
|
60
|
+
sweepIdle(): Promise<number>;
|
|
61
|
+
startIdleSweep(): void;
|
|
62
|
+
stopIdleSweep(): void;
|
|
63
|
+
recordWarmHit(): void;
|
|
64
|
+
recordColdHit(): void;
|
|
65
|
+
get stats(): PoolStats;
|
|
66
|
+
statsAsync(): Promise<PoolStats>;
|
|
67
|
+
get activeCount(): number;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE7E,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGtF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtG,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACtD,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,wBAAwB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC1D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC9D,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,YAAY,CAA6B;IAEjD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAC,CAAsC;IAC5D,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAAK;gBAEf,IAAI,EAAE,eAAe;IAW3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAgCtF,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAoBlD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIlD,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAM7D,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAWpC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;YAatB,QAAQ;IA4ChB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BlC,cAAc,IAAI,IAAI;IAWtB,aAAa,IAAI,IAAI;IASrB,aAAa,IAAI,IAAI;IACrB,aAAa,IAAI,IAAI;IAIrB,IAAI,KAAK,IAAI,SAAS,CAyBrB;IAEK,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC;IAWtC,IAAI,WAAW,IAAI,MAAM,CAExB;CACF"}
|