@sandbox-engine/sdk 0.1.3 → 0.1.4
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/index.d.mts +66 -2
- package/dist/index.d.ts +66 -2
- package/dist/index.js +121 -13
- package/dist/index.mjs +119 -13
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -8,7 +8,7 @@ declare class HttpClient {
|
|
|
8
8
|
private headers;
|
|
9
9
|
get<T>(path: string, query?: Record<string, string>): Promise<T>;
|
|
10
10
|
post<T>(path: string, body?: unknown, timeoutMs?: number): Promise<T>;
|
|
11
|
-
delete(path: string, query?: Record<string, string>): Promise<
|
|
11
|
+
delete<T = void>(path: string, query?: Record<string, string>): Promise<T>;
|
|
12
12
|
openWebSocket(path: string): WebSocket;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -31,6 +31,32 @@ interface SandboxOptions {
|
|
|
31
31
|
dockerfile?: string;
|
|
32
32
|
timeout?: number;
|
|
33
33
|
}
|
|
34
|
+
interface SandboxConnectOptions {
|
|
35
|
+
serverUrl?: string;
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
}
|
|
38
|
+
interface StartProcessOptions {
|
|
39
|
+
command?: string;
|
|
40
|
+
cmd?: string;
|
|
41
|
+
args?: string[];
|
|
42
|
+
cwd?: string;
|
|
43
|
+
env?: Record<string, string>;
|
|
44
|
+
processId?: string;
|
|
45
|
+
}
|
|
46
|
+
interface BackgroundProcessInfo {
|
|
47
|
+
id: string;
|
|
48
|
+
pid: number;
|
|
49
|
+
command: string;
|
|
50
|
+
status: 'running' | 'exited';
|
|
51
|
+
exitCode: number | null;
|
|
52
|
+
}
|
|
53
|
+
interface WaitForPortOptions {
|
|
54
|
+
host?: string;
|
|
55
|
+
timeout?: number;
|
|
56
|
+
}
|
|
57
|
+
interface WaitForLogOptions {
|
|
58
|
+
timeout?: number;
|
|
59
|
+
}
|
|
34
60
|
interface ProxyEnvResult {
|
|
35
61
|
proxyBase: string;
|
|
36
62
|
expiresAt: string;
|
|
@@ -99,6 +125,37 @@ declare class Process extends EventEmitter {
|
|
|
99
125
|
wait(): Promise<ExecResult>;
|
|
100
126
|
}
|
|
101
127
|
|
|
128
|
+
declare class BackgroundProcess {
|
|
129
|
+
private readonly sandboxId;
|
|
130
|
+
private readonly client;
|
|
131
|
+
readonly id: string;
|
|
132
|
+
readonly pid: number;
|
|
133
|
+
readonly command: string;
|
|
134
|
+
constructor(sandboxId: string, client: HttpClient, info: BackgroundProcessInfo);
|
|
135
|
+
private path;
|
|
136
|
+
getStatus(): Promise<BackgroundProcessInfo>;
|
|
137
|
+
getLogs(): Promise<{
|
|
138
|
+
stdout: string;
|
|
139
|
+
stderr: string;
|
|
140
|
+
}>;
|
|
141
|
+
kill(): Promise<void>;
|
|
142
|
+
waitForExit(timeout?: number): Promise<{
|
|
143
|
+
exitCode: number;
|
|
144
|
+
}>;
|
|
145
|
+
waitForPort(port: number, opts?: WaitForPortOptions): Promise<void>;
|
|
146
|
+
waitForLog(pattern: string | RegExp, opts?: WaitForLogOptions): Promise<string>;
|
|
147
|
+
}
|
|
148
|
+
declare class ProcessManager {
|
|
149
|
+
private readonly sandboxId;
|
|
150
|
+
private readonly client;
|
|
151
|
+
constructor(sandboxId: string, client: HttpClient);
|
|
152
|
+
start(opts: StartProcessOptions): Promise<BackgroundProcess>;
|
|
153
|
+
list(): Promise<BackgroundProcessInfo[]>;
|
|
154
|
+
get(processId: string): Promise<BackgroundProcess>;
|
|
155
|
+
kill(processId: string): Promise<void>;
|
|
156
|
+
killAll(): Promise<number>;
|
|
157
|
+
}
|
|
158
|
+
|
|
102
159
|
declare class Terminal extends EventEmitter {
|
|
103
160
|
private ws;
|
|
104
161
|
constructor(ws: WebSocket);
|
|
@@ -121,8 +178,15 @@ declare class Sandbox {
|
|
|
121
178
|
private readonly client;
|
|
122
179
|
readonly id: string;
|
|
123
180
|
readonly fs: Filesystem;
|
|
181
|
+
readonly processes: ProcessManager;
|
|
124
182
|
private constructor();
|
|
183
|
+
private static resolveClient;
|
|
125
184
|
static create(opts?: SandboxOptions): Promise<Sandbox>;
|
|
185
|
+
/**
|
|
186
|
+
* Reconnect to an existing sandbox by ID.
|
|
187
|
+
* Works after server restart if the container is still running (auto-adopt).
|
|
188
|
+
*/
|
|
189
|
+
static connect(sandboxId: string, opts?: SandboxConnectOptions): Promise<Sandbox>;
|
|
126
190
|
exec(cmd: string, opts?: ExecOptions): Promise<ExecResult>;
|
|
127
191
|
exec(cmd: string, opts: ExecOptions & {
|
|
128
192
|
stream: true;
|
|
@@ -141,4 +205,4 @@ declare class Sandbox {
|
|
|
141
205
|
close(): Promise<void>;
|
|
142
206
|
}
|
|
143
207
|
|
|
144
|
-
export { type ExecOptions, type ExecResult, type FileEntry, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, type ProxyEnvResult, Sandbox, type SandboxOptions, Terminal, type WsMessage, type WsMessageType };
|
|
208
|
+
export { BackgroundProcess, type BackgroundProcessInfo, type ExecOptions, type ExecResult, type FileEntry, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, ProcessManager, type ProxyEnvResult, Sandbox, type SandboxConnectOptions, type SandboxOptions, type StartProcessOptions, Terminal, type WaitForLogOptions, type WaitForPortOptions, type WsMessage, type WsMessageType };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ declare class HttpClient {
|
|
|
8
8
|
private headers;
|
|
9
9
|
get<T>(path: string, query?: Record<string, string>): Promise<T>;
|
|
10
10
|
post<T>(path: string, body?: unknown, timeoutMs?: number): Promise<T>;
|
|
11
|
-
delete(path: string, query?: Record<string, string>): Promise<
|
|
11
|
+
delete<T = void>(path: string, query?: Record<string, string>): Promise<T>;
|
|
12
12
|
openWebSocket(path: string): WebSocket;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -31,6 +31,32 @@ interface SandboxOptions {
|
|
|
31
31
|
dockerfile?: string;
|
|
32
32
|
timeout?: number;
|
|
33
33
|
}
|
|
34
|
+
interface SandboxConnectOptions {
|
|
35
|
+
serverUrl?: string;
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
}
|
|
38
|
+
interface StartProcessOptions {
|
|
39
|
+
command?: string;
|
|
40
|
+
cmd?: string;
|
|
41
|
+
args?: string[];
|
|
42
|
+
cwd?: string;
|
|
43
|
+
env?: Record<string, string>;
|
|
44
|
+
processId?: string;
|
|
45
|
+
}
|
|
46
|
+
interface BackgroundProcessInfo {
|
|
47
|
+
id: string;
|
|
48
|
+
pid: number;
|
|
49
|
+
command: string;
|
|
50
|
+
status: 'running' | 'exited';
|
|
51
|
+
exitCode: number | null;
|
|
52
|
+
}
|
|
53
|
+
interface WaitForPortOptions {
|
|
54
|
+
host?: string;
|
|
55
|
+
timeout?: number;
|
|
56
|
+
}
|
|
57
|
+
interface WaitForLogOptions {
|
|
58
|
+
timeout?: number;
|
|
59
|
+
}
|
|
34
60
|
interface ProxyEnvResult {
|
|
35
61
|
proxyBase: string;
|
|
36
62
|
expiresAt: string;
|
|
@@ -99,6 +125,37 @@ declare class Process extends EventEmitter {
|
|
|
99
125
|
wait(): Promise<ExecResult>;
|
|
100
126
|
}
|
|
101
127
|
|
|
128
|
+
declare class BackgroundProcess {
|
|
129
|
+
private readonly sandboxId;
|
|
130
|
+
private readonly client;
|
|
131
|
+
readonly id: string;
|
|
132
|
+
readonly pid: number;
|
|
133
|
+
readonly command: string;
|
|
134
|
+
constructor(sandboxId: string, client: HttpClient, info: BackgroundProcessInfo);
|
|
135
|
+
private path;
|
|
136
|
+
getStatus(): Promise<BackgroundProcessInfo>;
|
|
137
|
+
getLogs(): Promise<{
|
|
138
|
+
stdout: string;
|
|
139
|
+
stderr: string;
|
|
140
|
+
}>;
|
|
141
|
+
kill(): Promise<void>;
|
|
142
|
+
waitForExit(timeout?: number): Promise<{
|
|
143
|
+
exitCode: number;
|
|
144
|
+
}>;
|
|
145
|
+
waitForPort(port: number, opts?: WaitForPortOptions): Promise<void>;
|
|
146
|
+
waitForLog(pattern: string | RegExp, opts?: WaitForLogOptions): Promise<string>;
|
|
147
|
+
}
|
|
148
|
+
declare class ProcessManager {
|
|
149
|
+
private readonly sandboxId;
|
|
150
|
+
private readonly client;
|
|
151
|
+
constructor(sandboxId: string, client: HttpClient);
|
|
152
|
+
start(opts: StartProcessOptions): Promise<BackgroundProcess>;
|
|
153
|
+
list(): Promise<BackgroundProcessInfo[]>;
|
|
154
|
+
get(processId: string): Promise<BackgroundProcess>;
|
|
155
|
+
kill(processId: string): Promise<void>;
|
|
156
|
+
killAll(): Promise<number>;
|
|
157
|
+
}
|
|
158
|
+
|
|
102
159
|
declare class Terminal extends EventEmitter {
|
|
103
160
|
private ws;
|
|
104
161
|
constructor(ws: WebSocket);
|
|
@@ -121,8 +178,15 @@ declare class Sandbox {
|
|
|
121
178
|
private readonly client;
|
|
122
179
|
readonly id: string;
|
|
123
180
|
readonly fs: Filesystem;
|
|
181
|
+
readonly processes: ProcessManager;
|
|
124
182
|
private constructor();
|
|
183
|
+
private static resolveClient;
|
|
125
184
|
static create(opts?: SandboxOptions): Promise<Sandbox>;
|
|
185
|
+
/**
|
|
186
|
+
* Reconnect to an existing sandbox by ID.
|
|
187
|
+
* Works after server restart if the container is still running (auto-adopt).
|
|
188
|
+
*/
|
|
189
|
+
static connect(sandboxId: string, opts?: SandboxConnectOptions): Promise<Sandbox>;
|
|
126
190
|
exec(cmd: string, opts?: ExecOptions): Promise<ExecResult>;
|
|
127
191
|
exec(cmd: string, opts: ExecOptions & {
|
|
128
192
|
stream: true;
|
|
@@ -141,4 +205,4 @@ declare class Sandbox {
|
|
|
141
205
|
close(): Promise<void>;
|
|
142
206
|
}
|
|
143
207
|
|
|
144
|
-
export { type ExecOptions, type ExecResult, type FileEntry, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, type ProxyEnvResult, Sandbox, type SandboxOptions, Terminal, type WsMessage, type WsMessageType };
|
|
208
|
+
export { BackgroundProcess, type BackgroundProcessInfo, type ExecOptions, type ExecResult, type FileEntry, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, ProcessManager, type ProxyEnvResult, Sandbox, type SandboxConnectOptions, type SandboxOptions, type StartProcessOptions, Terminal, type WaitForLogOptions, type WaitForPortOptions, type WsMessage, type WsMessageType };
|
package/dist/index.js
CHANGED
|
@@ -30,8 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
BackgroundProcess: () => BackgroundProcess,
|
|
33
34
|
Filesystem: () => Filesystem,
|
|
34
35
|
Process: () => Process,
|
|
36
|
+
ProcessManager: () => ProcessManager,
|
|
35
37
|
Sandbox: () => Sandbox,
|
|
36
38
|
Terminal: () => Terminal
|
|
37
39
|
});
|
|
@@ -86,9 +88,15 @@ var HttpClient = class {
|
|
|
86
88
|
signal: AbortSignal.timeout(3e4)
|
|
87
89
|
});
|
|
88
90
|
if (!res.ok && res.status !== 404) {
|
|
89
|
-
const
|
|
90
|
-
throw new Error(`DELETE ${path} failed (${res.status}): ${
|
|
91
|
+
const text2 = await res.text();
|
|
92
|
+
throw new Error(`DELETE ${path} failed (${res.status}): ${text2}`);
|
|
93
|
+
}
|
|
94
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") {
|
|
95
|
+
return void 0;
|
|
91
96
|
}
|
|
97
|
+
const text = await res.text();
|
|
98
|
+
if (!text) return void 0;
|
|
99
|
+
return JSON.parse(text);
|
|
92
100
|
}
|
|
93
101
|
openWebSocket(path) {
|
|
94
102
|
const wsUrl = this.baseUrl.replace(/^http/, "ws") + path;
|
|
@@ -186,6 +194,89 @@ var Process = class extends import_node_events.EventEmitter {
|
|
|
186
194
|
}
|
|
187
195
|
};
|
|
188
196
|
|
|
197
|
+
// src/background-process.ts
|
|
198
|
+
var BackgroundProcess = class {
|
|
199
|
+
constructor(sandboxId, client, info) {
|
|
200
|
+
this.sandboxId = sandboxId;
|
|
201
|
+
this.client = client;
|
|
202
|
+
this.id = info.id;
|
|
203
|
+
this.pid = info.pid;
|
|
204
|
+
this.command = info.command;
|
|
205
|
+
}
|
|
206
|
+
sandboxId;
|
|
207
|
+
client;
|
|
208
|
+
id;
|
|
209
|
+
pid;
|
|
210
|
+
command;
|
|
211
|
+
path(suffix) {
|
|
212
|
+
return `/api/sandboxes/${this.sandboxId}/processes/${this.id}${suffix}`;
|
|
213
|
+
}
|
|
214
|
+
async getStatus() {
|
|
215
|
+
return this.client.get(this.path(""));
|
|
216
|
+
}
|
|
217
|
+
async getLogs() {
|
|
218
|
+
return this.client.get(this.path("/logs"));
|
|
219
|
+
}
|
|
220
|
+
async kill() {
|
|
221
|
+
await this.client.delete(this.path(""));
|
|
222
|
+
}
|
|
223
|
+
async waitForExit(timeout) {
|
|
224
|
+
return this.client.post(this.path("/wait-exit"), { timeout }, (timeout ?? 3e5) + 5e3);
|
|
225
|
+
}
|
|
226
|
+
async waitForPort(port, opts = {}) {
|
|
227
|
+
await this.client.post(
|
|
228
|
+
this.path("/wait-for-port"),
|
|
229
|
+
{ port, host: opts.host, timeout: opts.timeout },
|
|
230
|
+
(opts.timeout ?? 3e4) + 5e3
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
async waitForLog(pattern, opts = {}) {
|
|
234
|
+
const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
|
|
235
|
+
const res = await this.client.post(
|
|
236
|
+
this.path("/wait-for-log"),
|
|
237
|
+
{ pattern: patternStr, timeout: opts.timeout },
|
|
238
|
+
(opts.timeout ?? 3e4) + 5e3
|
|
239
|
+
);
|
|
240
|
+
return res.match;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var ProcessManager = class {
|
|
244
|
+
constructor(sandboxId, client) {
|
|
245
|
+
this.sandboxId = sandboxId;
|
|
246
|
+
this.client = client;
|
|
247
|
+
}
|
|
248
|
+
sandboxId;
|
|
249
|
+
client;
|
|
250
|
+
async start(opts) {
|
|
251
|
+
const info = await this.client.post(
|
|
252
|
+
`/api/sandboxes/${this.sandboxId}/processes`,
|
|
253
|
+
opts
|
|
254
|
+
);
|
|
255
|
+
return new BackgroundProcess(this.sandboxId, this.client, info);
|
|
256
|
+
}
|
|
257
|
+
async list() {
|
|
258
|
+
const res = await this.client.get(
|
|
259
|
+
`/api/sandboxes/${this.sandboxId}/processes`
|
|
260
|
+
);
|
|
261
|
+
return res.processes;
|
|
262
|
+
}
|
|
263
|
+
async get(processId) {
|
|
264
|
+
const info = await this.client.get(
|
|
265
|
+
`/api/sandboxes/${this.sandboxId}/processes/${processId}`
|
|
266
|
+
);
|
|
267
|
+
return new BackgroundProcess(this.sandboxId, this.client, info);
|
|
268
|
+
}
|
|
269
|
+
async kill(processId) {
|
|
270
|
+
await this.client.delete(`/api/sandboxes/${this.sandboxId}/processes/${processId}`);
|
|
271
|
+
}
|
|
272
|
+
async killAll() {
|
|
273
|
+
const res = await this.client.delete(
|
|
274
|
+
`/api/sandboxes/${this.sandboxId}/processes`
|
|
275
|
+
);
|
|
276
|
+
return res?.killed ?? 0;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
189
280
|
// src/terminal.ts
|
|
190
281
|
var import_node_events2 = require("events");
|
|
191
282
|
var Terminal = class extends import_node_events2.EventEmitter {
|
|
@@ -227,26 +318,41 @@ var Sandbox = class _Sandbox {
|
|
|
227
318
|
this.client = client;
|
|
228
319
|
this.id = id;
|
|
229
320
|
this.fs = new Filesystem(id, client);
|
|
321
|
+
this.processes = new ProcessManager(id, client);
|
|
230
322
|
}
|
|
231
323
|
client;
|
|
232
324
|
id;
|
|
233
325
|
fs;
|
|
326
|
+
processes;
|
|
327
|
+
static resolveClient(opts = {}) {
|
|
328
|
+
const serverUrl = opts.serverUrl ?? process.env.SANDBOX_SERVER_URL ?? "http://localhost:4000";
|
|
329
|
+
const apiKey = opts.apiKey ?? process.env.SANDBOX_API_KEY ?? "dev-api-key";
|
|
330
|
+
return new HttpClient(serverUrl, apiKey);
|
|
331
|
+
}
|
|
234
332
|
static async create(opts = {}) {
|
|
235
|
-
const
|
|
236
|
-
serverUrl = process.env.SANDBOX_SERVER_URL ?? "http://localhost:4000",
|
|
237
|
-
apiKey = process.env.SANDBOX_API_KEY ?? "dev-api-key",
|
|
238
|
-
template,
|
|
239
|
-
dockerfile,
|
|
240
|
-
timeout
|
|
241
|
-
} = opts;
|
|
242
|
-
const client = new HttpClient(serverUrl, apiKey);
|
|
333
|
+
const client = _Sandbox.resolveClient(opts);
|
|
243
334
|
const data = await client.post("/api/sandboxes", {
|
|
244
|
-
template,
|
|
245
|
-
dockerfile,
|
|
246
|
-
timeout
|
|
335
|
+
template: opts.template,
|
|
336
|
+
dockerfile: opts.dockerfile,
|
|
337
|
+
timeout: opts.timeout
|
|
247
338
|
});
|
|
248
339
|
return new _Sandbox(client, data.id);
|
|
249
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Reconnect to an existing sandbox by ID.
|
|
343
|
+
* Works after server restart if the container is still running (auto-adopt).
|
|
344
|
+
*/
|
|
345
|
+
static async connect(sandboxId, opts = {}) {
|
|
346
|
+
const client = _Sandbox.resolveClient(opts);
|
|
347
|
+
const data = await client.post(
|
|
348
|
+
`/api/sandboxes/${sandboxId}/connect`,
|
|
349
|
+
{}
|
|
350
|
+
);
|
|
351
|
+
if (data.status === "stopped") {
|
|
352
|
+
throw new Error(`Sandbox ${sandboxId} is stopped`);
|
|
353
|
+
}
|
|
354
|
+
return new _Sandbox(client, data.id);
|
|
355
|
+
}
|
|
250
356
|
async exec(cmd, opts = {}) {
|
|
251
357
|
const { args, cwd, env, timeout, stream } = opts;
|
|
252
358
|
if (stream) {
|
|
@@ -325,8 +431,10 @@ var Sandbox = class _Sandbox {
|
|
|
325
431
|
};
|
|
326
432
|
// Annotate the CommonJS export names for ESM import in node:
|
|
327
433
|
0 && (module.exports = {
|
|
434
|
+
BackgroundProcess,
|
|
328
435
|
Filesystem,
|
|
329
436
|
Process,
|
|
437
|
+
ProcessManager,
|
|
330
438
|
Sandbox,
|
|
331
439
|
Terminal
|
|
332
440
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -47,9 +47,15 @@ var HttpClient = class {
|
|
|
47
47
|
signal: AbortSignal.timeout(3e4)
|
|
48
48
|
});
|
|
49
49
|
if (!res.ok && res.status !== 404) {
|
|
50
|
-
const
|
|
51
|
-
throw new Error(`DELETE ${path} failed (${res.status}): ${
|
|
50
|
+
const text2 = await res.text();
|
|
51
|
+
throw new Error(`DELETE ${path} failed (${res.status}): ${text2}`);
|
|
52
|
+
}
|
|
53
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") {
|
|
54
|
+
return void 0;
|
|
52
55
|
}
|
|
56
|
+
const text = await res.text();
|
|
57
|
+
if (!text) return void 0;
|
|
58
|
+
return JSON.parse(text);
|
|
53
59
|
}
|
|
54
60
|
openWebSocket(path) {
|
|
55
61
|
const wsUrl = this.baseUrl.replace(/^http/, "ws") + path;
|
|
@@ -147,6 +153,89 @@ var Process = class extends EventEmitter {
|
|
|
147
153
|
}
|
|
148
154
|
};
|
|
149
155
|
|
|
156
|
+
// src/background-process.ts
|
|
157
|
+
var BackgroundProcess = class {
|
|
158
|
+
constructor(sandboxId, client, info) {
|
|
159
|
+
this.sandboxId = sandboxId;
|
|
160
|
+
this.client = client;
|
|
161
|
+
this.id = info.id;
|
|
162
|
+
this.pid = info.pid;
|
|
163
|
+
this.command = info.command;
|
|
164
|
+
}
|
|
165
|
+
sandboxId;
|
|
166
|
+
client;
|
|
167
|
+
id;
|
|
168
|
+
pid;
|
|
169
|
+
command;
|
|
170
|
+
path(suffix) {
|
|
171
|
+
return `/api/sandboxes/${this.sandboxId}/processes/${this.id}${suffix}`;
|
|
172
|
+
}
|
|
173
|
+
async getStatus() {
|
|
174
|
+
return this.client.get(this.path(""));
|
|
175
|
+
}
|
|
176
|
+
async getLogs() {
|
|
177
|
+
return this.client.get(this.path("/logs"));
|
|
178
|
+
}
|
|
179
|
+
async kill() {
|
|
180
|
+
await this.client.delete(this.path(""));
|
|
181
|
+
}
|
|
182
|
+
async waitForExit(timeout) {
|
|
183
|
+
return this.client.post(this.path("/wait-exit"), { timeout }, (timeout ?? 3e5) + 5e3);
|
|
184
|
+
}
|
|
185
|
+
async waitForPort(port, opts = {}) {
|
|
186
|
+
await this.client.post(
|
|
187
|
+
this.path("/wait-for-port"),
|
|
188
|
+
{ port, host: opts.host, timeout: opts.timeout },
|
|
189
|
+
(opts.timeout ?? 3e4) + 5e3
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
async waitForLog(pattern, opts = {}) {
|
|
193
|
+
const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
|
|
194
|
+
const res = await this.client.post(
|
|
195
|
+
this.path("/wait-for-log"),
|
|
196
|
+
{ pattern: patternStr, timeout: opts.timeout },
|
|
197
|
+
(opts.timeout ?? 3e4) + 5e3
|
|
198
|
+
);
|
|
199
|
+
return res.match;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var ProcessManager = class {
|
|
203
|
+
constructor(sandboxId, client) {
|
|
204
|
+
this.sandboxId = sandboxId;
|
|
205
|
+
this.client = client;
|
|
206
|
+
}
|
|
207
|
+
sandboxId;
|
|
208
|
+
client;
|
|
209
|
+
async start(opts) {
|
|
210
|
+
const info = await this.client.post(
|
|
211
|
+
`/api/sandboxes/${this.sandboxId}/processes`,
|
|
212
|
+
opts
|
|
213
|
+
);
|
|
214
|
+
return new BackgroundProcess(this.sandboxId, this.client, info);
|
|
215
|
+
}
|
|
216
|
+
async list() {
|
|
217
|
+
const res = await this.client.get(
|
|
218
|
+
`/api/sandboxes/${this.sandboxId}/processes`
|
|
219
|
+
);
|
|
220
|
+
return res.processes;
|
|
221
|
+
}
|
|
222
|
+
async get(processId) {
|
|
223
|
+
const info = await this.client.get(
|
|
224
|
+
`/api/sandboxes/${this.sandboxId}/processes/${processId}`
|
|
225
|
+
);
|
|
226
|
+
return new BackgroundProcess(this.sandboxId, this.client, info);
|
|
227
|
+
}
|
|
228
|
+
async kill(processId) {
|
|
229
|
+
await this.client.delete(`/api/sandboxes/${this.sandboxId}/processes/${processId}`);
|
|
230
|
+
}
|
|
231
|
+
async killAll() {
|
|
232
|
+
const res = await this.client.delete(
|
|
233
|
+
`/api/sandboxes/${this.sandboxId}/processes`
|
|
234
|
+
);
|
|
235
|
+
return res?.killed ?? 0;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
150
239
|
// src/terminal.ts
|
|
151
240
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
152
241
|
var Terminal = class extends EventEmitter2 {
|
|
@@ -188,26 +277,41 @@ var Sandbox = class _Sandbox {
|
|
|
188
277
|
this.client = client;
|
|
189
278
|
this.id = id;
|
|
190
279
|
this.fs = new Filesystem(id, client);
|
|
280
|
+
this.processes = new ProcessManager(id, client);
|
|
191
281
|
}
|
|
192
282
|
client;
|
|
193
283
|
id;
|
|
194
284
|
fs;
|
|
285
|
+
processes;
|
|
286
|
+
static resolveClient(opts = {}) {
|
|
287
|
+
const serverUrl = opts.serverUrl ?? process.env.SANDBOX_SERVER_URL ?? "http://localhost:4000";
|
|
288
|
+
const apiKey = opts.apiKey ?? process.env.SANDBOX_API_KEY ?? "dev-api-key";
|
|
289
|
+
return new HttpClient(serverUrl, apiKey);
|
|
290
|
+
}
|
|
195
291
|
static async create(opts = {}) {
|
|
196
|
-
const
|
|
197
|
-
serverUrl = process.env.SANDBOX_SERVER_URL ?? "http://localhost:4000",
|
|
198
|
-
apiKey = process.env.SANDBOX_API_KEY ?? "dev-api-key",
|
|
199
|
-
template,
|
|
200
|
-
dockerfile,
|
|
201
|
-
timeout
|
|
202
|
-
} = opts;
|
|
203
|
-
const client = new HttpClient(serverUrl, apiKey);
|
|
292
|
+
const client = _Sandbox.resolveClient(opts);
|
|
204
293
|
const data = await client.post("/api/sandboxes", {
|
|
205
|
-
template,
|
|
206
|
-
dockerfile,
|
|
207
|
-
timeout
|
|
294
|
+
template: opts.template,
|
|
295
|
+
dockerfile: opts.dockerfile,
|
|
296
|
+
timeout: opts.timeout
|
|
208
297
|
});
|
|
209
298
|
return new _Sandbox(client, data.id);
|
|
210
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Reconnect to an existing sandbox by ID.
|
|
302
|
+
* Works after server restart if the container is still running (auto-adopt).
|
|
303
|
+
*/
|
|
304
|
+
static async connect(sandboxId, opts = {}) {
|
|
305
|
+
const client = _Sandbox.resolveClient(opts);
|
|
306
|
+
const data = await client.post(
|
|
307
|
+
`/api/sandboxes/${sandboxId}/connect`,
|
|
308
|
+
{}
|
|
309
|
+
);
|
|
310
|
+
if (data.status === "stopped") {
|
|
311
|
+
throw new Error(`Sandbox ${sandboxId} is stopped`);
|
|
312
|
+
}
|
|
313
|
+
return new _Sandbox(client, data.id);
|
|
314
|
+
}
|
|
211
315
|
async exec(cmd, opts = {}) {
|
|
212
316
|
const { args, cwd, env, timeout, stream } = opts;
|
|
213
317
|
if (stream) {
|
|
@@ -285,8 +389,10 @@ var Sandbox = class _Sandbox {
|
|
|
285
389
|
}
|
|
286
390
|
};
|
|
287
391
|
export {
|
|
392
|
+
BackgroundProcess,
|
|
288
393
|
Filesystem,
|
|
289
394
|
Process,
|
|
395
|
+
ProcessManager,
|
|
290
396
|
Sandbox,
|
|
291
397
|
Terminal
|
|
292
398
|
};
|
package/package.json
CHANGED