@sandbox-engine/sdk 0.1.4 → 0.2.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/dist/index.d.mts +79 -3
- package/dist/index.d.ts +79 -3
- package/dist/index.js +98 -4
- package/dist/index.mjs +98 -4
- package/package.json +8 -2
package/dist/index.d.mts
CHANGED
|
@@ -30,11 +30,31 @@ interface SandboxOptions {
|
|
|
30
30
|
*/
|
|
31
31
|
dockerfile?: string;
|
|
32
32
|
timeout?: number;
|
|
33
|
+
/** Environment variables injected into the container for the entire session. */
|
|
34
|
+
env?: Record<string, string>;
|
|
33
35
|
}
|
|
34
36
|
interface SandboxConnectOptions {
|
|
35
37
|
serverUrl?: string;
|
|
36
38
|
apiKey?: string;
|
|
37
39
|
}
|
|
40
|
+
interface SandboxListOptions {
|
|
41
|
+
serverUrl?: string;
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
}
|
|
44
|
+
interface SandboxSummary {
|
|
45
|
+
id: string;
|
|
46
|
+
status: string;
|
|
47
|
+
template: string;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
timeout: number;
|
|
50
|
+
expiresAt: string | null;
|
|
51
|
+
agentUrl: string;
|
|
52
|
+
ports: Record<string, number>;
|
|
53
|
+
}
|
|
54
|
+
interface StreamLogsOptions {
|
|
55
|
+
onStdout?: (line: string) => void;
|
|
56
|
+
onStderr?: (line: string) => void;
|
|
57
|
+
}
|
|
38
58
|
interface StartProcessOptions {
|
|
39
59
|
command?: string;
|
|
40
60
|
cmd?: string;
|
|
@@ -92,10 +112,38 @@ interface FileWriteManyResult {
|
|
|
92
112
|
error?: string;
|
|
93
113
|
}>;
|
|
94
114
|
}
|
|
115
|
+
interface FileStat {
|
|
116
|
+
path: string;
|
|
117
|
+
size: number;
|
|
118
|
+
mtime: string;
|
|
119
|
+
isDirectory: boolean;
|
|
120
|
+
isFile: boolean;
|
|
121
|
+
}
|
|
122
|
+
interface FileWatchEvent {
|
|
123
|
+
type: 'change' | 'rename' | 'error';
|
|
124
|
+
eventType: 'create' | 'modify' | 'delete';
|
|
125
|
+
path: string;
|
|
126
|
+
data?: string;
|
|
127
|
+
}
|
|
128
|
+
interface FileWatchOptions {
|
|
129
|
+
recursive?: boolean;
|
|
130
|
+
onEvent: (event: FileWatchEvent) => void;
|
|
131
|
+
}
|
|
132
|
+
interface ExposePortOptions {
|
|
133
|
+
/** Human-readable label for this port (e.g. 'api', 'frontend'). */
|
|
134
|
+
name?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Access token embedded in the preview URL.
|
|
137
|
+
* Use a stable value to get a consistent URL across sandbox restarts.
|
|
138
|
+
* When set, the URL becomes: https://{port}-{id}-{token}.{domain}
|
|
139
|
+
*/
|
|
140
|
+
token?: string;
|
|
141
|
+
}
|
|
95
142
|
interface PortMapping {
|
|
96
143
|
containerPort: number;
|
|
97
|
-
hostPort: number;
|
|
98
144
|
url: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
token?: string;
|
|
99
147
|
}
|
|
100
148
|
type WsMessageType = 'stdout' | 'stderr' | 'exit' | 'error' | 'input' | 'resize';
|
|
101
149
|
interface WsMessage {
|
|
@@ -116,6 +164,12 @@ declare class Filesystem {
|
|
|
116
164
|
writeMany(files: FileWriteEntry[]): Promise<FileWriteManyResult>;
|
|
117
165
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
118
166
|
delete(filePath: string): Promise<void>;
|
|
167
|
+
stat(filePath: string): Promise<FileStat>;
|
|
168
|
+
readBytes(filePath: string): Promise<Uint8Array>;
|
|
169
|
+
writeBytes(filePath: string, data: Uint8Array | Buffer): Promise<void>;
|
|
170
|
+
watch(dirPath: string, opts: FileWatchOptions): Promise<{
|
|
171
|
+
close: () => void;
|
|
172
|
+
}>;
|
|
119
173
|
}
|
|
120
174
|
|
|
121
175
|
declare class Process extends EventEmitter {
|
|
@@ -144,6 +198,9 @@ declare class BackgroundProcess {
|
|
|
144
198
|
}>;
|
|
145
199
|
waitForPort(port: number, opts?: WaitForPortOptions): Promise<void>;
|
|
146
200
|
waitForLog(pattern: string | RegExp, opts?: WaitForLogOptions): Promise<string>;
|
|
201
|
+
streamLogs(opts?: StreamLogsOptions): {
|
|
202
|
+
close: () => void;
|
|
203
|
+
};
|
|
147
204
|
}
|
|
148
205
|
declare class ProcessManager {
|
|
149
206
|
private readonly sandboxId;
|
|
@@ -181,6 +238,7 @@ declare class Sandbox {
|
|
|
181
238
|
readonly processes: ProcessManager;
|
|
182
239
|
private constructor();
|
|
183
240
|
private static resolveClient;
|
|
241
|
+
static list(opts?: SandboxListOptions): Promise<SandboxSummary[]>;
|
|
184
242
|
static create(opts?: SandboxOptions): Promise<Sandbox>;
|
|
185
243
|
/**
|
|
186
244
|
* Reconnect to an existing sandbox by ID.
|
|
@@ -196,13 +254,31 @@ declare class Sandbox {
|
|
|
196
254
|
getProxyEnv: (secretNames: string[]) => Promise<ProxyEnvResult>;
|
|
197
255
|
};
|
|
198
256
|
readonly ports: {
|
|
257
|
+
/**
|
|
258
|
+
* List all currently exposed ports for this sandbox.
|
|
259
|
+
*/
|
|
199
260
|
list: () => Promise<PortMapping[]>;
|
|
200
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Expose any port (1024–65535) and get a public HTTPS URL.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* const { url } = await sandbox.ports.expose(5173, { name: 'vite', token: 'my-token' })
|
|
266
|
+
* // https://5173-{sandboxId}-my-token.preview.yourdomain.com
|
|
267
|
+
*/
|
|
268
|
+
expose: (containerPort: number, opts?: ExposePortOptions) => Promise<PortMapping>;
|
|
269
|
+
/**
|
|
270
|
+
* Remove a port from public access.
|
|
271
|
+
*/
|
|
201
272
|
unexpose: (containerPort: number) => Promise<void>;
|
|
273
|
+
/**
|
|
274
|
+
* Check whether a token grants access to an exposed port.
|
|
275
|
+
* Returns true if the port has no token requirement or if the token matches.
|
|
276
|
+
*/
|
|
277
|
+
validateToken: (containerPort: number, token: string) => Promise<boolean>;
|
|
202
278
|
};
|
|
203
279
|
info(): Promise<SandboxApiResponse>;
|
|
204
280
|
keepalive(timeout?: number): Promise<SandboxApiResponse>;
|
|
205
281
|
close(): Promise<void>;
|
|
206
282
|
}
|
|
207
283
|
|
|
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 };
|
|
284
|
+
export { BackgroundProcess, type BackgroundProcessInfo, type ExecOptions, type ExecResult, type FileEntry, type FileStat, type FileWatchEvent, type FileWatchOptions, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, ProcessManager, type ProxyEnvResult, Sandbox, type SandboxConnectOptions, type SandboxListOptions, type SandboxOptions, type SandboxSummary, type StartProcessOptions, type StreamLogsOptions, Terminal, type WaitForLogOptions, type WaitForPortOptions, type WsMessage, type WsMessageType };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,11 +30,31 @@ interface SandboxOptions {
|
|
|
30
30
|
*/
|
|
31
31
|
dockerfile?: string;
|
|
32
32
|
timeout?: number;
|
|
33
|
+
/** Environment variables injected into the container for the entire session. */
|
|
34
|
+
env?: Record<string, string>;
|
|
33
35
|
}
|
|
34
36
|
interface SandboxConnectOptions {
|
|
35
37
|
serverUrl?: string;
|
|
36
38
|
apiKey?: string;
|
|
37
39
|
}
|
|
40
|
+
interface SandboxListOptions {
|
|
41
|
+
serverUrl?: string;
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
}
|
|
44
|
+
interface SandboxSummary {
|
|
45
|
+
id: string;
|
|
46
|
+
status: string;
|
|
47
|
+
template: string;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
timeout: number;
|
|
50
|
+
expiresAt: string | null;
|
|
51
|
+
agentUrl: string;
|
|
52
|
+
ports: Record<string, number>;
|
|
53
|
+
}
|
|
54
|
+
interface StreamLogsOptions {
|
|
55
|
+
onStdout?: (line: string) => void;
|
|
56
|
+
onStderr?: (line: string) => void;
|
|
57
|
+
}
|
|
38
58
|
interface StartProcessOptions {
|
|
39
59
|
command?: string;
|
|
40
60
|
cmd?: string;
|
|
@@ -92,10 +112,38 @@ interface FileWriteManyResult {
|
|
|
92
112
|
error?: string;
|
|
93
113
|
}>;
|
|
94
114
|
}
|
|
115
|
+
interface FileStat {
|
|
116
|
+
path: string;
|
|
117
|
+
size: number;
|
|
118
|
+
mtime: string;
|
|
119
|
+
isDirectory: boolean;
|
|
120
|
+
isFile: boolean;
|
|
121
|
+
}
|
|
122
|
+
interface FileWatchEvent {
|
|
123
|
+
type: 'change' | 'rename' | 'error';
|
|
124
|
+
eventType: 'create' | 'modify' | 'delete';
|
|
125
|
+
path: string;
|
|
126
|
+
data?: string;
|
|
127
|
+
}
|
|
128
|
+
interface FileWatchOptions {
|
|
129
|
+
recursive?: boolean;
|
|
130
|
+
onEvent: (event: FileWatchEvent) => void;
|
|
131
|
+
}
|
|
132
|
+
interface ExposePortOptions {
|
|
133
|
+
/** Human-readable label for this port (e.g. 'api', 'frontend'). */
|
|
134
|
+
name?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Access token embedded in the preview URL.
|
|
137
|
+
* Use a stable value to get a consistent URL across sandbox restarts.
|
|
138
|
+
* When set, the URL becomes: https://{port}-{id}-{token}.{domain}
|
|
139
|
+
*/
|
|
140
|
+
token?: string;
|
|
141
|
+
}
|
|
95
142
|
interface PortMapping {
|
|
96
143
|
containerPort: number;
|
|
97
|
-
hostPort: number;
|
|
98
144
|
url: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
token?: string;
|
|
99
147
|
}
|
|
100
148
|
type WsMessageType = 'stdout' | 'stderr' | 'exit' | 'error' | 'input' | 'resize';
|
|
101
149
|
interface WsMessage {
|
|
@@ -116,6 +164,12 @@ declare class Filesystem {
|
|
|
116
164
|
writeMany(files: FileWriteEntry[]): Promise<FileWriteManyResult>;
|
|
117
165
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
118
166
|
delete(filePath: string): Promise<void>;
|
|
167
|
+
stat(filePath: string): Promise<FileStat>;
|
|
168
|
+
readBytes(filePath: string): Promise<Uint8Array>;
|
|
169
|
+
writeBytes(filePath: string, data: Uint8Array | Buffer): Promise<void>;
|
|
170
|
+
watch(dirPath: string, opts: FileWatchOptions): Promise<{
|
|
171
|
+
close: () => void;
|
|
172
|
+
}>;
|
|
119
173
|
}
|
|
120
174
|
|
|
121
175
|
declare class Process extends EventEmitter {
|
|
@@ -144,6 +198,9 @@ declare class BackgroundProcess {
|
|
|
144
198
|
}>;
|
|
145
199
|
waitForPort(port: number, opts?: WaitForPortOptions): Promise<void>;
|
|
146
200
|
waitForLog(pattern: string | RegExp, opts?: WaitForLogOptions): Promise<string>;
|
|
201
|
+
streamLogs(opts?: StreamLogsOptions): {
|
|
202
|
+
close: () => void;
|
|
203
|
+
};
|
|
147
204
|
}
|
|
148
205
|
declare class ProcessManager {
|
|
149
206
|
private readonly sandboxId;
|
|
@@ -181,6 +238,7 @@ declare class Sandbox {
|
|
|
181
238
|
readonly processes: ProcessManager;
|
|
182
239
|
private constructor();
|
|
183
240
|
private static resolveClient;
|
|
241
|
+
static list(opts?: SandboxListOptions): Promise<SandboxSummary[]>;
|
|
184
242
|
static create(opts?: SandboxOptions): Promise<Sandbox>;
|
|
185
243
|
/**
|
|
186
244
|
* Reconnect to an existing sandbox by ID.
|
|
@@ -196,13 +254,31 @@ declare class Sandbox {
|
|
|
196
254
|
getProxyEnv: (secretNames: string[]) => Promise<ProxyEnvResult>;
|
|
197
255
|
};
|
|
198
256
|
readonly ports: {
|
|
257
|
+
/**
|
|
258
|
+
* List all currently exposed ports for this sandbox.
|
|
259
|
+
*/
|
|
199
260
|
list: () => Promise<PortMapping[]>;
|
|
200
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Expose any port (1024–65535) and get a public HTTPS URL.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* const { url } = await sandbox.ports.expose(5173, { name: 'vite', token: 'my-token' })
|
|
266
|
+
* // https://5173-{sandboxId}-my-token.preview.yourdomain.com
|
|
267
|
+
*/
|
|
268
|
+
expose: (containerPort: number, opts?: ExposePortOptions) => Promise<PortMapping>;
|
|
269
|
+
/**
|
|
270
|
+
* Remove a port from public access.
|
|
271
|
+
*/
|
|
201
272
|
unexpose: (containerPort: number) => Promise<void>;
|
|
273
|
+
/**
|
|
274
|
+
* Check whether a token grants access to an exposed port.
|
|
275
|
+
* Returns true if the port has no token requirement or if the token matches.
|
|
276
|
+
*/
|
|
277
|
+
validateToken: (containerPort: number, token: string) => Promise<boolean>;
|
|
202
278
|
};
|
|
203
279
|
info(): Promise<SandboxApiResponse>;
|
|
204
280
|
keepalive(timeout?: number): Promise<SandboxApiResponse>;
|
|
205
281
|
close(): Promise<void>;
|
|
206
282
|
}
|
|
207
283
|
|
|
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 };
|
|
284
|
+
export { BackgroundProcess, type BackgroundProcessInfo, type ExecOptions, type ExecResult, type FileEntry, type FileStat, type FileWatchEvent, type FileWatchOptions, type FileWriteEntry, type FileWriteManyResult, Filesystem, type PortMapping, Process, ProcessManager, type ProxyEnvResult, Sandbox, type SandboxConnectOptions, type SandboxListOptions, type SandboxOptions, type SandboxSummary, type StartProcessOptions, type StreamLogsOptions, Terminal, type WaitForLogOptions, type WaitForPortOptions, type WsMessage, type WsMessageType };
|
package/dist/index.js
CHANGED
|
@@ -149,6 +149,49 @@ var Filesystem = class {
|
|
|
149
149
|
async delete(filePath) {
|
|
150
150
|
await this.client.delete(`/api/sandboxes/${this.sandboxId}/files`, { path: filePath });
|
|
151
151
|
}
|
|
152
|
+
async stat(filePath) {
|
|
153
|
+
return this.client.get(
|
|
154
|
+
`/api/sandboxes/${this.sandboxId}/files/stat`,
|
|
155
|
+
{ path: filePath }
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
async readBytes(filePath) {
|
|
159
|
+
const res = await this.client.get(
|
|
160
|
+
`/api/sandboxes/${this.sandboxId}/files/bytes`,
|
|
161
|
+
{ path: filePath }
|
|
162
|
+
);
|
|
163
|
+
return Uint8Array.from(Buffer.from(res.data, "base64"));
|
|
164
|
+
}
|
|
165
|
+
async writeBytes(filePath, data) {
|
|
166
|
+
await this.client.post(`/api/sandboxes/${this.sandboxId}/files/bytes`, {
|
|
167
|
+
path: filePath,
|
|
168
|
+
data: Buffer.from(data).toString("base64")
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async watch(dirPath, opts) {
|
|
172
|
+
const ws = this.client.openWebSocket(`/api/sandboxes/${this.sandboxId}/files/watch`);
|
|
173
|
+
await new Promise((resolve, reject) => {
|
|
174
|
+
ws.once("open", () => {
|
|
175
|
+
ws.send(JSON.stringify({ path: dirPath, recursive: opts.recursive ?? false }));
|
|
176
|
+
resolve();
|
|
177
|
+
});
|
|
178
|
+
ws.once("error", reject);
|
|
179
|
+
});
|
|
180
|
+
ws.on("message", (raw) => {
|
|
181
|
+
try {
|
|
182
|
+
const event = JSON.parse(raw.toString());
|
|
183
|
+
opts.onEvent(event);
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
close: () => {
|
|
189
|
+
if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
|
|
190
|
+
ws.close();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
152
195
|
};
|
|
153
196
|
|
|
154
197
|
// src/process.ts
|
|
@@ -239,6 +282,29 @@ var BackgroundProcess = class {
|
|
|
239
282
|
);
|
|
240
283
|
return res.match;
|
|
241
284
|
}
|
|
285
|
+
streamLogs(opts = {}) {
|
|
286
|
+
const ws = this.client.openWebSocket(
|
|
287
|
+
`/api/sandboxes/${this.sandboxId}/processes/logs/stream`
|
|
288
|
+
);
|
|
289
|
+
ws.on("open", () => {
|
|
290
|
+
ws.send(JSON.stringify({ processId: this.id }));
|
|
291
|
+
});
|
|
292
|
+
ws.on("message", (raw) => {
|
|
293
|
+
try {
|
|
294
|
+
const msg = JSON.parse(raw.toString());
|
|
295
|
+
if (msg.type === "stdout") opts.onStdout?.(msg.data ?? "");
|
|
296
|
+
else if (msg.type === "stderr") opts.onStderr?.(msg.data ?? "");
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return {
|
|
301
|
+
close: () => {
|
|
302
|
+
if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
|
|
303
|
+
ws.close();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
242
308
|
};
|
|
243
309
|
var ProcessManager = class {
|
|
244
310
|
constructor(sandboxId, client) {
|
|
@@ -329,12 +395,17 @@ var Sandbox = class _Sandbox {
|
|
|
329
395
|
const apiKey = opts.apiKey ?? process.env.SANDBOX_API_KEY ?? "dev-api-key";
|
|
330
396
|
return new HttpClient(serverUrl, apiKey);
|
|
331
397
|
}
|
|
398
|
+
static async list(opts = {}) {
|
|
399
|
+
const client = _Sandbox.resolveClient(opts);
|
|
400
|
+
return client.get("/api/sandboxes");
|
|
401
|
+
}
|
|
332
402
|
static async create(opts = {}) {
|
|
333
403
|
const client = _Sandbox.resolveClient(opts);
|
|
334
404
|
const data = await client.post("/api/sandboxes", {
|
|
335
405
|
template: opts.template,
|
|
336
406
|
dockerfile: opts.dockerfile,
|
|
337
|
-
timeout: opts.timeout
|
|
407
|
+
timeout: opts.timeout,
|
|
408
|
+
env: opts.env
|
|
338
409
|
});
|
|
339
410
|
return new _Sandbox(client, data.id);
|
|
340
411
|
}
|
|
@@ -398,22 +469,45 @@ var Sandbox = class _Sandbox {
|
|
|
398
469
|
}
|
|
399
470
|
};
|
|
400
471
|
ports = {
|
|
472
|
+
/**
|
|
473
|
+
* List all currently exposed ports for this sandbox.
|
|
474
|
+
*/
|
|
401
475
|
list: async () => {
|
|
402
476
|
const res = await this.client.get(
|
|
403
477
|
`/api/sandboxes/${this.id}/ports`
|
|
404
478
|
);
|
|
405
479
|
return res.ports;
|
|
406
480
|
},
|
|
407
|
-
|
|
408
|
-
|
|
481
|
+
/**
|
|
482
|
+
* Expose any port (1024–65535) and get a public HTTPS URL.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* const { url } = await sandbox.ports.expose(5173, { name: 'vite', token: 'my-token' })
|
|
486
|
+
* // https://5173-{sandboxId}-my-token.preview.yourdomain.com
|
|
487
|
+
*/
|
|
488
|
+
expose: async (containerPort, opts = {}) => {
|
|
489
|
+
return this.client.post(
|
|
409
490
|
`/api/sandboxes/${this.id}/ports/${containerPort}/expose`,
|
|
410
|
-
|
|
491
|
+
opts
|
|
411
492
|
);
|
|
412
493
|
},
|
|
494
|
+
/**
|
|
495
|
+
* Remove a port from public access.
|
|
496
|
+
*/
|
|
413
497
|
unexpose: async (containerPort) => {
|
|
414
498
|
await this.client.delete(
|
|
415
499
|
`/api/sandboxes/${this.id}/ports/${containerPort}/expose`
|
|
416
500
|
);
|
|
501
|
+
},
|
|
502
|
+
/**
|
|
503
|
+
* Check whether a token grants access to an exposed port.
|
|
504
|
+
* Returns true if the port has no token requirement or if the token matches.
|
|
505
|
+
*/
|
|
506
|
+
validateToken: async (containerPort, token) => {
|
|
507
|
+
const res = await this.client.get(
|
|
508
|
+
`/api/sandboxes/${this.id}/ports/${containerPort}/validate?token=${encodeURIComponent(token)}`
|
|
509
|
+
);
|
|
510
|
+
return res.valid;
|
|
417
511
|
}
|
|
418
512
|
};
|
|
419
513
|
async info() {
|
package/dist/index.mjs
CHANGED
|
@@ -108,6 +108,49 @@ var Filesystem = class {
|
|
|
108
108
|
async delete(filePath) {
|
|
109
109
|
await this.client.delete(`/api/sandboxes/${this.sandboxId}/files`, { path: filePath });
|
|
110
110
|
}
|
|
111
|
+
async stat(filePath) {
|
|
112
|
+
return this.client.get(
|
|
113
|
+
`/api/sandboxes/${this.sandboxId}/files/stat`,
|
|
114
|
+
{ path: filePath }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
async readBytes(filePath) {
|
|
118
|
+
const res = await this.client.get(
|
|
119
|
+
`/api/sandboxes/${this.sandboxId}/files/bytes`,
|
|
120
|
+
{ path: filePath }
|
|
121
|
+
);
|
|
122
|
+
return Uint8Array.from(Buffer.from(res.data, "base64"));
|
|
123
|
+
}
|
|
124
|
+
async writeBytes(filePath, data) {
|
|
125
|
+
await this.client.post(`/api/sandboxes/${this.sandboxId}/files/bytes`, {
|
|
126
|
+
path: filePath,
|
|
127
|
+
data: Buffer.from(data).toString("base64")
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async watch(dirPath, opts) {
|
|
131
|
+
const ws = this.client.openWebSocket(`/api/sandboxes/${this.sandboxId}/files/watch`);
|
|
132
|
+
await new Promise((resolve, reject) => {
|
|
133
|
+
ws.once("open", () => {
|
|
134
|
+
ws.send(JSON.stringify({ path: dirPath, recursive: opts.recursive ?? false }));
|
|
135
|
+
resolve();
|
|
136
|
+
});
|
|
137
|
+
ws.once("error", reject);
|
|
138
|
+
});
|
|
139
|
+
ws.on("message", (raw) => {
|
|
140
|
+
try {
|
|
141
|
+
const event = JSON.parse(raw.toString());
|
|
142
|
+
opts.onEvent(event);
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
close: () => {
|
|
148
|
+
if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
|
|
149
|
+
ws.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
111
154
|
};
|
|
112
155
|
|
|
113
156
|
// src/process.ts
|
|
@@ -198,6 +241,29 @@ var BackgroundProcess = class {
|
|
|
198
241
|
);
|
|
199
242
|
return res.match;
|
|
200
243
|
}
|
|
244
|
+
streamLogs(opts = {}) {
|
|
245
|
+
const ws = this.client.openWebSocket(
|
|
246
|
+
`/api/sandboxes/${this.sandboxId}/processes/logs/stream`
|
|
247
|
+
);
|
|
248
|
+
ws.on("open", () => {
|
|
249
|
+
ws.send(JSON.stringify({ processId: this.id }));
|
|
250
|
+
});
|
|
251
|
+
ws.on("message", (raw) => {
|
|
252
|
+
try {
|
|
253
|
+
const msg = JSON.parse(raw.toString());
|
|
254
|
+
if (msg.type === "stdout") opts.onStdout?.(msg.data ?? "");
|
|
255
|
+
else if (msg.type === "stderr") opts.onStderr?.(msg.data ?? "");
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
close: () => {
|
|
261
|
+
if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
|
|
262
|
+
ws.close();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
201
267
|
};
|
|
202
268
|
var ProcessManager = class {
|
|
203
269
|
constructor(sandboxId, client) {
|
|
@@ -288,12 +354,17 @@ var Sandbox = class _Sandbox {
|
|
|
288
354
|
const apiKey = opts.apiKey ?? process.env.SANDBOX_API_KEY ?? "dev-api-key";
|
|
289
355
|
return new HttpClient(serverUrl, apiKey);
|
|
290
356
|
}
|
|
357
|
+
static async list(opts = {}) {
|
|
358
|
+
const client = _Sandbox.resolveClient(opts);
|
|
359
|
+
return client.get("/api/sandboxes");
|
|
360
|
+
}
|
|
291
361
|
static async create(opts = {}) {
|
|
292
362
|
const client = _Sandbox.resolveClient(opts);
|
|
293
363
|
const data = await client.post("/api/sandboxes", {
|
|
294
364
|
template: opts.template,
|
|
295
365
|
dockerfile: opts.dockerfile,
|
|
296
|
-
timeout: opts.timeout
|
|
366
|
+
timeout: opts.timeout,
|
|
367
|
+
env: opts.env
|
|
297
368
|
});
|
|
298
369
|
return new _Sandbox(client, data.id);
|
|
299
370
|
}
|
|
@@ -357,22 +428,45 @@ var Sandbox = class _Sandbox {
|
|
|
357
428
|
}
|
|
358
429
|
};
|
|
359
430
|
ports = {
|
|
431
|
+
/**
|
|
432
|
+
* List all currently exposed ports for this sandbox.
|
|
433
|
+
*/
|
|
360
434
|
list: async () => {
|
|
361
435
|
const res = await this.client.get(
|
|
362
436
|
`/api/sandboxes/${this.id}/ports`
|
|
363
437
|
);
|
|
364
438
|
return res.ports;
|
|
365
439
|
},
|
|
366
|
-
|
|
367
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Expose any port (1024–65535) and get a public HTTPS URL.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* const { url } = await sandbox.ports.expose(5173, { name: 'vite', token: 'my-token' })
|
|
445
|
+
* // https://5173-{sandboxId}-my-token.preview.yourdomain.com
|
|
446
|
+
*/
|
|
447
|
+
expose: async (containerPort, opts = {}) => {
|
|
448
|
+
return this.client.post(
|
|
368
449
|
`/api/sandboxes/${this.id}/ports/${containerPort}/expose`,
|
|
369
|
-
|
|
450
|
+
opts
|
|
370
451
|
);
|
|
371
452
|
},
|
|
453
|
+
/**
|
|
454
|
+
* Remove a port from public access.
|
|
455
|
+
*/
|
|
372
456
|
unexpose: async (containerPort) => {
|
|
373
457
|
await this.client.delete(
|
|
374
458
|
`/api/sandboxes/${this.id}/ports/${containerPort}/expose`
|
|
375
459
|
);
|
|
460
|
+
},
|
|
461
|
+
/**
|
|
462
|
+
* Check whether a token grants access to an exposed port.
|
|
463
|
+
* Returns true if the port has no token requirement or if the token matches.
|
|
464
|
+
*/
|
|
465
|
+
validateToken: async (containerPort, token) => {
|
|
466
|
+
const res = await this.client.get(
|
|
467
|
+
`/api/sandboxes/${this.id}/ports/${containerPort}/validate?token=${encodeURIComponent(token)}`
|
|
468
|
+
);
|
|
469
|
+
return res.valid;
|
|
376
470
|
}
|
|
377
471
|
};
|
|
378
472
|
async info() {
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sandbox-engine/sdk",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "SDK for creating and managing isolated sandbox environments",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sandbox",
|
|
7
|
+
"docker",
|
|
8
|
+
"code-execution",
|
|
9
|
+
"isolated",
|
|
10
|
+
"e2b"
|
|
11
|
+
],
|
|
6
12
|
"license": "MIT",
|
|
7
13
|
"main": "dist/index.js",
|
|
8
14
|
"module": "dist/index.mjs",
|