@alibaba-group/opensandbox 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -32
- package/dist/{chunk-4EF4ODU2.js → chunk-XHEWHFQ6.js} +184 -22
- package/dist/chunk-XHEWHFQ6.js.map +1 -0
- package/dist/cjs/index.cjs +345 -41
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/internal.cjs +182 -21
- package/dist/cjs/internal.cjs.map +1 -1
- package/dist/index.d.ts +47 -7
- package/dist/index.js +163 -20
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +365 -9
- package/dist/internal.js +1 -1
- package/dist/{sandboxes-CLy12BN1.d.ts → sandboxes-DL9uUHGR.d.ts} +296 -134
- package/package.json +3 -2
- package/src/adapters/commandsAdapter.ts +250 -23
- package/src/adapters/egressAdapter.ts +46 -0
- package/src/adapters/sandboxesAdapter.ts +14 -5
- package/src/api/egress.ts +184 -0
- package/src/api/execd.ts +223 -0
- package/src/api/lifecycle.ts +122 -7
- package/src/config/connection.ts +11 -0
- package/src/core/constants.ts +2 -1
- package/src/core/exceptions.ts +5 -4
- package/src/factory/adapterFactory.ts +14 -1
- package/src/factory/defaultAdapterFactory.ts +34 -5
- package/src/index.ts +8 -2
- package/src/models/execd.ts +32 -6
- package/src/models/execution.ts +2 -1
- package/src/models/sandboxes.ts +121 -4
- package/src/openapi/egressClient.ts +45 -0
- package/src/sandbox.ts +114 -12
- package/src/services/egress.ts +27 -0
- package/src/services/execdCommands.ts +41 -2
- package/src/services/sandboxes.ts +6 -2
- package/dist/chunk-4EF4ODU2.js.map +0 -1
package/README.md
CHANGED
|
@@ -55,7 +55,10 @@ try {
|
|
|
55
55
|
await sandbox.close();
|
|
56
56
|
} catch (err) {
|
|
57
57
|
if (err instanceof SandboxException) {
|
|
58
|
-
console.error(
|
|
58
|
+
console.error(
|
|
59
|
+
`Sandbox Error: [${err.error.code}] ${err.error.message ?? ""}`,
|
|
60
|
+
);
|
|
61
|
+
console.error(`Request ID: ${err.requestId ?? "N/A"}`);
|
|
59
62
|
} else {
|
|
60
63
|
console.error(err);
|
|
61
64
|
}
|
|
@@ -72,7 +75,7 @@ Manage the sandbox lifecycle, including renewal, pausing, and resuming.
|
|
|
72
75
|
const info = await sandbox.getInfo();
|
|
73
76
|
console.log("State:", info.status.state);
|
|
74
77
|
console.log("Created:", info.createdAt);
|
|
75
|
-
console.log("Expires:", info.expiresAt);
|
|
78
|
+
console.log("Expires:", info.expiresAt); // null when manual cleanup mode is used
|
|
76
79
|
|
|
77
80
|
await sandbox.pause();
|
|
78
81
|
|
|
@@ -83,6 +86,16 @@ const resumed = await sandbox.resume();
|
|
|
83
86
|
await resumed.renew(30 * 60);
|
|
84
87
|
```
|
|
85
88
|
|
|
89
|
+
Create a non-expiring sandbox by passing `timeoutSeconds: null`:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const manual = await Sandbox.create({
|
|
93
|
+
connectionConfig: config,
|
|
94
|
+
image: "ubuntu",
|
|
95
|
+
timeoutSeconds: null,
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
86
99
|
### 2. Custom Health Check
|
|
87
100
|
|
|
88
101
|
Define custom logic to determine whether the sandbox is ready/healthy. This overrides the default ping check used during readiness checks.
|
|
@@ -109,7 +122,8 @@ import type { ExecutionHandlers } from "@alibaba-group/opensandbox";
|
|
|
109
122
|
const handlers: ExecutionHandlers = {
|
|
110
123
|
onStdout: (m) => console.log("STDOUT:", m.text),
|
|
111
124
|
onStderr: (m) => console.error("STDERR:", m.text),
|
|
112
|
-
onExecutionComplete: (c) =>
|
|
125
|
+
onExecutionComplete: (c) =>
|
|
126
|
+
console.log("Finished in", c.executionTimeMs, "ms"),
|
|
113
127
|
};
|
|
114
128
|
|
|
115
129
|
await sandbox.commands.run(
|
|
@@ -124,16 +138,19 @@ await sandbox.commands.run(
|
|
|
124
138
|
Manage files and directories, including read, write, list/search, and delete.
|
|
125
139
|
|
|
126
140
|
```ts
|
|
127
|
-
await sandbox.files.createDirectories([{ path: "/tmp/demo", mode:
|
|
141
|
+
await sandbox.files.createDirectories([{ path: "/tmp/demo", mode: 755 }]);
|
|
128
142
|
|
|
129
143
|
await sandbox.files.writeFiles([
|
|
130
|
-
{ path: "/tmp/demo/hello.txt", data: "Hello World", mode:
|
|
144
|
+
{ path: "/tmp/demo/hello.txt", data: "Hello World", mode: 644 },
|
|
131
145
|
]);
|
|
132
146
|
|
|
133
147
|
const content = await sandbox.files.readFile("/tmp/demo/hello.txt");
|
|
134
148
|
console.log("Content:", content);
|
|
135
149
|
|
|
136
|
-
const files = await sandbox.files.search({
|
|
150
|
+
const files = await sandbox.files.search({
|
|
151
|
+
path: "/tmp/demo",
|
|
152
|
+
pattern: "*.txt",
|
|
153
|
+
});
|
|
137
154
|
console.log(files.map((f) => f.path));
|
|
138
155
|
|
|
139
156
|
await sandbox.files.deleteDirectories(["/tmp/demo"]);
|
|
@@ -156,7 +173,10 @@ Use `SandboxManager` for administrative tasks and finding existing sandboxes.
|
|
|
156
173
|
import { SandboxManager } from "@alibaba-group/opensandbox";
|
|
157
174
|
|
|
158
175
|
const manager = SandboxManager.create({ connectionConfig: config });
|
|
159
|
-
const list = await manager.listSandboxInfos({
|
|
176
|
+
const list = await manager.listSandboxInfos({
|
|
177
|
+
states: ["Running"],
|
|
178
|
+
pageSize: 10,
|
|
179
|
+
});
|
|
160
180
|
console.log(list.items.map((s) => s.id));
|
|
161
181
|
await manager.close();
|
|
162
182
|
```
|
|
@@ -168,17 +188,19 @@ await manager.close();
|
|
|
168
188
|
The `ConnectionConfig` class manages API server connection settings.
|
|
169
189
|
|
|
170
190
|
Runtime notes:
|
|
191
|
+
|
|
171
192
|
- In browsers, the SDK uses the global `fetch` implementation.
|
|
172
193
|
- In Node.js, every `Sandbox` and `SandboxManager` clones the base `ConnectionConfig` via `withTransportIfMissing()`, so each instance gets an isolated `undici` keep-alive pool. Call `sandbox.close()` or `manager.close()` when you are done so the SDK can release the associated agent.
|
|
173
194
|
|
|
174
|
-
| Parameter
|
|
175
|
-
|
|
|
176
|
-
| `apiKey`
|
|
177
|
-
| `domain`
|
|
178
|
-
| `protocol`
|
|
179
|
-
| `requestTimeoutSeconds` | Request timeout applied to SDK HTTP calls
|
|
180
|
-
| `debug`
|
|
181
|
-
| `headers`
|
|
195
|
+
| Parameter | Description | Default | Environment Variable |
|
|
196
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------- | ---------------------- |
|
|
197
|
+
| `apiKey` | API key for authentication | Optional | `OPEN_SANDBOX_API_KEY` |
|
|
198
|
+
| `domain` | Sandbox service domain (`host[:port]`) | `localhost:8080` | `OPEN_SANDBOX_DOMAIN` |
|
|
199
|
+
| `protocol` | HTTP protocol (`http`/`https`) | `http` | - |
|
|
200
|
+
| `requestTimeoutSeconds` | Request timeout applied to SDK HTTP calls | `30` | - |
|
|
201
|
+
| `debug` | Enable basic HTTP debug logging | `false` | - |
|
|
202
|
+
| `headers` | Extra headers applied to every request | `{}` | - |
|
|
203
|
+
| `useServerProxy` | Use sandbox server as proxy for execd/endpoint requests (e.g. when client cannot reach the sandbox directly) | `false` | - |
|
|
182
204
|
|
|
183
205
|
```ts
|
|
184
206
|
import { ConnectionConfig } from "@alibaba-group/opensandbox";
|
|
@@ -202,20 +224,23 @@ const config2 = new ConnectionConfig({
|
|
|
202
224
|
|
|
203
225
|
`Sandbox.create()` allows configuring the sandbox environment.
|
|
204
226
|
|
|
205
|
-
| Parameter
|
|
206
|
-
|
|
|
207
|
-
| `image`
|
|
208
|
-
| `timeoutSeconds`
|
|
209
|
-
| `entrypoint`
|
|
210
|
-
| `resource`
|
|
211
|
-
| `env`
|
|
212
|
-
| `metadata`
|
|
213
|
-
| `networkPolicy`
|
|
214
|
-
| `extensions`
|
|
215
|
-
| `skipHealthCheck`
|
|
216
|
-
| `healthCheck`
|
|
217
|
-
| `readyTimeoutSeconds`
|
|
218
|
-
| `healthCheckPollingInterval` | Poll interval while waiting (milliseconds)
|
|
227
|
+
| Parameter | Description | Default |
|
|
228
|
+
| ---------------------------- | ------------------------------------------------ | ---------------------------- |
|
|
229
|
+
| `image` | Docker image to use | Required |
|
|
230
|
+
| `timeoutSeconds` | Automatic termination timeout (server-side TTL) | 10 minutes |
|
|
231
|
+
| `entrypoint` | Container entrypoint command | `["tail","-f","/dev/null"]` |
|
|
232
|
+
| `resource` | CPU and memory limits (string map) | `{"cpu":"1","memory":"2Gi"}` |
|
|
233
|
+
| `env` | Environment variables | `{}` |
|
|
234
|
+
| `metadata` | Custom metadata tags | `{}` |
|
|
235
|
+
| `networkPolicy` | Optional outbound network policy (egress) | - |
|
|
236
|
+
| `extensions` | Extra server-defined fields | `{}` |
|
|
237
|
+
| `skipHealthCheck` | Skip readiness checks (`Running` + health check) | `false` |
|
|
238
|
+
| `healthCheck` | Custom readiness check | - |
|
|
239
|
+
| `readyTimeoutSeconds` | Max time to wait for readiness | 30 seconds |
|
|
240
|
+
| `healthCheckPollingInterval` | Poll interval while waiting (milliseconds) | 200 ms |
|
|
241
|
+
|
|
242
|
+
Note: metadata keys under `opensandbox.io/` are reserved for system-managed
|
|
243
|
+
labels and will be rejected by the server.
|
|
219
244
|
|
|
220
245
|
```ts
|
|
221
246
|
const sandbox = await Sandbox.create({
|
|
@@ -228,16 +253,35 @@ const sandbox = await Sandbox.create({
|
|
|
228
253
|
});
|
|
229
254
|
```
|
|
230
255
|
|
|
231
|
-
### 3.
|
|
256
|
+
### 3. Runtime Egress Policy Updates
|
|
257
|
+
|
|
258
|
+
Runtime egress reads and patches go directly to the sandbox egress sidecar.
|
|
259
|
+
The SDK first resolves the sandbox endpoint on port `18080`, then calls the sidecar `/policy` API.
|
|
260
|
+
|
|
261
|
+
Patch uses merge semantics:
|
|
262
|
+
- Incoming rules take priority over existing rules with the same `target`.
|
|
263
|
+
- Existing rules for other targets remain unchanged.
|
|
264
|
+
- Within a single patch payload, the first rule for a `target` wins.
|
|
265
|
+
- The current `defaultAction` is preserved.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const policy = await sandbox.getEgressPolicy();
|
|
269
|
+
|
|
270
|
+
await sandbox.patchEgressRules([
|
|
271
|
+
{ action: "allow", target: "www.github.com" },
|
|
272
|
+
{ action: "deny", target: "pypi.org" },
|
|
273
|
+
]);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 4. Resource cleanup
|
|
232
277
|
|
|
233
278
|
Both `Sandbox` and `SandboxManager` own a scoped HTTP agent when running on Node.js
|
|
234
279
|
so you can safely reuse the same `ConnectionConfig`. Once you are finished interacting
|
|
235
280
|
with the sandbox or administration APIs, call `sandbox.close()` / `manager.close()` to
|
|
236
|
-
release the underlying agent.
|
|
281
|
+
release the underlying agent.
|
|
237
282
|
|
|
238
283
|
## Browser Notes
|
|
239
284
|
|
|
240
285
|
- The SDK can run in browsers, but **streaming file uploads are Node-only**.
|
|
241
286
|
- If you pass `ReadableStream` or `AsyncIterable` for `writeFiles`, the browser will fall back to **buffering in memory** before upload.
|
|
242
287
|
- Reason: browsers do not support streaming `multipart/form-data` bodies with custom boundaries (required by the execd upload API).
|
|
243
|
-
|
|
@@ -14,25 +14,26 @@ var SandboxException = class extends Error {
|
|
|
14
14
|
name = "SandboxException";
|
|
15
15
|
error;
|
|
16
16
|
cause;
|
|
17
|
+
requestId;
|
|
17
18
|
constructor(opts = {}) {
|
|
18
19
|
super(opts.message);
|
|
19
20
|
this.cause = opts.cause;
|
|
20
21
|
this.error = opts.error ?? new SandboxError(SandboxError.INTERNAL_UNKNOWN_ERROR);
|
|
22
|
+
this.requestId = opts.requestId;
|
|
21
23
|
}
|
|
22
24
|
};
|
|
23
25
|
var SandboxApiException = class extends SandboxException {
|
|
24
26
|
name = "SandboxApiException";
|
|
25
27
|
statusCode;
|
|
26
|
-
requestId;
|
|
27
28
|
rawBody;
|
|
28
29
|
constructor(opts) {
|
|
29
30
|
super({
|
|
30
31
|
message: opts.message,
|
|
31
32
|
cause: opts.cause,
|
|
32
|
-
error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message)
|
|
33
|
+
error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message),
|
|
34
|
+
requestId: opts.requestId
|
|
33
35
|
});
|
|
34
36
|
this.statusCode = opts.statusCode;
|
|
35
|
-
this.requestId = opts.requestId;
|
|
36
37
|
this.rawBody = opts.rawBody;
|
|
37
38
|
}
|
|
38
39
|
};
|
|
@@ -267,11 +268,61 @@ function joinUrl(baseUrl, pathname) {
|
|
|
267
268
|
return `${base}${path}`;
|
|
268
269
|
}
|
|
269
270
|
function toRunCommandRequest(command, opts) {
|
|
270
|
-
|
|
271
|
+
if (opts?.gid != null && opts.uid == null) {
|
|
272
|
+
throw new Error("uid is required when gid is provided");
|
|
273
|
+
}
|
|
274
|
+
const body = {
|
|
271
275
|
command,
|
|
272
276
|
cwd: opts?.workingDirectory,
|
|
273
277
|
background: !!opts?.background
|
|
274
278
|
};
|
|
279
|
+
if (opts?.timeoutSeconds != null) {
|
|
280
|
+
body.timeout = Math.round(opts.timeoutSeconds * 1e3);
|
|
281
|
+
}
|
|
282
|
+
if (opts?.uid != null) {
|
|
283
|
+
body.uid = opts.uid;
|
|
284
|
+
}
|
|
285
|
+
if (opts?.gid != null) {
|
|
286
|
+
body.gid = opts.gid;
|
|
287
|
+
}
|
|
288
|
+
if (opts?.envs != null) {
|
|
289
|
+
body.envs = opts.envs;
|
|
290
|
+
}
|
|
291
|
+
return body;
|
|
292
|
+
}
|
|
293
|
+
function toRunInSessionRequest(command, opts) {
|
|
294
|
+
const body = {
|
|
295
|
+
command
|
|
296
|
+
};
|
|
297
|
+
if (opts?.workingDirectory != null) {
|
|
298
|
+
body.cwd = opts.workingDirectory;
|
|
299
|
+
}
|
|
300
|
+
if (opts?.timeout != null) {
|
|
301
|
+
body.timeout = opts.timeout;
|
|
302
|
+
}
|
|
303
|
+
return body;
|
|
304
|
+
}
|
|
305
|
+
function inferForegroundExitCode(execution) {
|
|
306
|
+
const errorValue = execution.error?.value?.trim();
|
|
307
|
+
const parsedExitCode = errorValue && /^-?\d+$/.test(errorValue) ? Number(errorValue) : Number.NaN;
|
|
308
|
+
return execution.error != null ? Number.isFinite(parsedExitCode) ? parsedExitCode : null : execution.complete ? 0 : null;
|
|
309
|
+
}
|
|
310
|
+
function assertNonBlank(value, field) {
|
|
311
|
+
if (!value.trim()) {
|
|
312
|
+
throw new Error(`${field} cannot be empty`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function parseOptionalDate(value, field) {
|
|
316
|
+
if (value == null) return void 0;
|
|
317
|
+
if (value instanceof Date) return value;
|
|
318
|
+
if (typeof value !== "string") {
|
|
319
|
+
throw new Error(`Invalid ${field}: expected ISO string, got ${typeof value}`);
|
|
320
|
+
}
|
|
321
|
+
const parsed = new Date(value);
|
|
322
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
323
|
+
throw new Error(`Invalid ${field}: ${value}`);
|
|
324
|
+
}
|
|
325
|
+
return parsed;
|
|
275
326
|
}
|
|
276
327
|
var CommandsAdapter = class {
|
|
277
328
|
constructor(client, opts) {
|
|
@@ -280,43 +331,149 @@ var CommandsAdapter = class {
|
|
|
280
331
|
this.fetch = opts.fetch ?? fetch;
|
|
281
332
|
}
|
|
282
333
|
fetch;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
334
|
+
buildRunStreamSpec(command, opts) {
|
|
335
|
+
assertNonBlank(command, "command");
|
|
336
|
+
return {
|
|
337
|
+
pathname: "/command",
|
|
338
|
+
body: toRunCommandRequest(command, opts),
|
|
339
|
+
fallbackErrorMessage: "Run command failed"
|
|
340
|
+
};
|
|
288
341
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
342
|
+
buildRunInSessionStreamSpec(sessionId, command, opts) {
|
|
343
|
+
assertNonBlank(sessionId, "sessionId");
|
|
344
|
+
assertNonBlank(command, "command");
|
|
345
|
+
return {
|
|
346
|
+
pathname: `/session/${encodeURIComponent(sessionId)}/run`,
|
|
347
|
+
body: toRunInSessionRequest(command, opts),
|
|
348
|
+
fallbackErrorMessage: "Run in session failed"
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
async *streamExecution(spec, signal) {
|
|
352
|
+
const url = joinUrl(this.opts.baseUrl, spec.pathname);
|
|
292
353
|
const res = await this.fetch(url, {
|
|
293
354
|
method: "POST",
|
|
294
355
|
headers: {
|
|
295
|
-
|
|
356
|
+
accept: "text/event-stream",
|
|
296
357
|
"content-type": "application/json",
|
|
297
358
|
...this.opts.headers ?? {}
|
|
298
359
|
},
|
|
299
|
-
body,
|
|
360
|
+
body: JSON.stringify(spec.body),
|
|
300
361
|
signal
|
|
301
362
|
});
|
|
302
|
-
for await (const ev of parseJsonEventStream(res, {
|
|
363
|
+
for await (const ev of parseJsonEventStream(res, {
|
|
364
|
+
fallbackErrorMessage: spec.fallbackErrorMessage
|
|
365
|
+
})) {
|
|
303
366
|
yield ev;
|
|
304
367
|
}
|
|
305
368
|
}
|
|
306
|
-
async
|
|
369
|
+
async consumeExecutionStream(stream, handlers, inferExitCode = false) {
|
|
307
370
|
const execution = {
|
|
308
371
|
logs: { stdout: [], stderr: [] },
|
|
309
372
|
result: []
|
|
310
373
|
};
|
|
311
374
|
const dispatcher = new ExecutionEventDispatcher(execution, handlers);
|
|
312
|
-
for await (const ev of
|
|
375
|
+
for await (const ev of stream) {
|
|
313
376
|
if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
|
|
314
377
|
ev.text = execution.id;
|
|
315
378
|
}
|
|
316
379
|
await dispatcher.dispatch(ev);
|
|
317
380
|
}
|
|
381
|
+
if (inferExitCode) {
|
|
382
|
+
execution.exitCode = inferForegroundExitCode(execution);
|
|
383
|
+
}
|
|
318
384
|
return execution;
|
|
319
385
|
}
|
|
386
|
+
async interrupt(sessionId) {
|
|
387
|
+
const { error, response } = await this.client.DELETE("/command", {
|
|
388
|
+
params: { query: { id: sessionId } }
|
|
389
|
+
});
|
|
390
|
+
throwOnOpenApiFetchError({ error, response }, "Interrupt command failed");
|
|
391
|
+
}
|
|
392
|
+
async getCommandStatus(commandId) {
|
|
393
|
+
const { data, error, response } = await this.client.GET("/command/status/{id}", {
|
|
394
|
+
params: { path: { id: commandId } }
|
|
395
|
+
});
|
|
396
|
+
throwOnOpenApiFetchError({ error, response }, "Get command status failed");
|
|
397
|
+
const ok = data;
|
|
398
|
+
if (!ok || typeof ok !== "object") {
|
|
399
|
+
throw new Error("Get command status failed: unexpected response shape");
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
id: ok.id,
|
|
403
|
+
content: ok.content,
|
|
404
|
+
running: ok.running,
|
|
405
|
+
exitCode: ok.exit_code ?? null,
|
|
406
|
+
error: ok.error,
|
|
407
|
+
startedAt: parseOptionalDate(ok.started_at, "startedAt"),
|
|
408
|
+
finishedAt: parseOptionalDate(ok.finished_at, "finishedAt") ?? null
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
async getBackgroundCommandLogs(commandId, cursor) {
|
|
412
|
+
const { data, error, response } = await this.client.GET("/command/{id}/logs", {
|
|
413
|
+
params: { path: { id: commandId }, query: cursor == null ? {} : { cursor } },
|
|
414
|
+
parseAs: "text"
|
|
415
|
+
});
|
|
416
|
+
throwOnOpenApiFetchError({ error, response }, "Get command logs failed");
|
|
417
|
+
const ok = data;
|
|
418
|
+
if (typeof ok !== "string") {
|
|
419
|
+
throw new Error("Get command logs failed: unexpected response shape");
|
|
420
|
+
}
|
|
421
|
+
const cursorHeader = response.headers.get("EXECD-COMMANDS-TAIL-CURSOR");
|
|
422
|
+
const parsedCursor = cursorHeader != null && cursorHeader !== "" ? Number(cursorHeader) : void 0;
|
|
423
|
+
return {
|
|
424
|
+
content: ok,
|
|
425
|
+
cursor: Number.isFinite(parsedCursor ?? NaN) ? parsedCursor : void 0
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
async *runStream(command, opts, signal) {
|
|
429
|
+
for await (const ev of this.streamExecution(
|
|
430
|
+
this.buildRunStreamSpec(command, opts),
|
|
431
|
+
signal
|
|
432
|
+
)) {
|
|
433
|
+
yield ev;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async run(command, opts, handlers, signal) {
|
|
437
|
+
return this.consumeExecutionStream(
|
|
438
|
+
this.runStream(command, opts, signal),
|
|
439
|
+
handlers,
|
|
440
|
+
!opts?.background
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
async createSession(options) {
|
|
444
|
+
const body = options?.workingDirectory != null ? { cwd: options.workingDirectory } : {};
|
|
445
|
+
const { data, error, response } = await this.client.POST("/session", {
|
|
446
|
+
body
|
|
447
|
+
});
|
|
448
|
+
throwOnOpenApiFetchError({ error, response }, "Create session failed");
|
|
449
|
+
const ok = data;
|
|
450
|
+
if (!ok || typeof ok.session_id !== "string") {
|
|
451
|
+
throw new Error("Create session failed: unexpected response shape");
|
|
452
|
+
}
|
|
453
|
+
return ok.session_id;
|
|
454
|
+
}
|
|
455
|
+
async *runInSessionStream(sessionId, command, opts, signal) {
|
|
456
|
+
for await (const ev of this.streamExecution(
|
|
457
|
+
this.buildRunInSessionStreamSpec(sessionId, command, opts),
|
|
458
|
+
signal
|
|
459
|
+
)) {
|
|
460
|
+
yield ev;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async runInSession(sessionId, command, options, handlers, signal) {
|
|
464
|
+
return this.consumeExecutionStream(
|
|
465
|
+
this.runInSessionStream(sessionId, command, options, signal),
|
|
466
|
+
handlers,
|
|
467
|
+
true
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
async deleteSession(sessionId) {
|
|
471
|
+
const { error, response } = await this.client.DELETE(
|
|
472
|
+
"/session/{sessionId}",
|
|
473
|
+
{ params: { path: { sessionId } } }
|
|
474
|
+
);
|
|
475
|
+
throwOnOpenApiFetchError({ error, response }, "Delete session failed");
|
|
476
|
+
}
|
|
320
477
|
};
|
|
321
478
|
|
|
322
479
|
// src/adapters/filesystemAdapter.ts
|
|
@@ -804,11 +961,15 @@ var SandboxesAdapter = class {
|
|
|
804
961
|
}
|
|
805
962
|
return d;
|
|
806
963
|
}
|
|
964
|
+
parseOptionalIsoDate(field, v) {
|
|
965
|
+
if (v == null) return null;
|
|
966
|
+
return this.parseIsoDate(field, v);
|
|
967
|
+
}
|
|
807
968
|
mapSandboxInfo(raw) {
|
|
808
969
|
return {
|
|
809
970
|
...raw ?? {},
|
|
810
971
|
createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
|
|
811
|
-
expiresAt: this.
|
|
972
|
+
expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
|
|
812
973
|
};
|
|
813
974
|
}
|
|
814
975
|
async createSandbox(req) {
|
|
@@ -824,7 +985,7 @@ var SandboxesAdapter = class {
|
|
|
824
985
|
return {
|
|
825
986
|
...raw ?? {},
|
|
826
987
|
createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
|
|
827
|
-
expiresAt: this.
|
|
988
|
+
expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
|
|
828
989
|
};
|
|
829
990
|
}
|
|
830
991
|
async getSandbox(sandboxId) {
|
|
@@ -895,9 +1056,9 @@ var SandboxesAdapter = class {
|
|
|
895
1056
|
expiresAt: raw?.expiresAt ? this.parseIsoDate("expiresAt", raw.expiresAt) : void 0
|
|
896
1057
|
};
|
|
897
1058
|
}
|
|
898
|
-
async getSandboxEndpoint(sandboxId, port) {
|
|
1059
|
+
async getSandboxEndpoint(sandboxId, port, useServerProxy = false) {
|
|
899
1060
|
const { data, error, response } = await this.client.GET("/sandboxes/{sandboxId}/endpoints/{port}", {
|
|
900
|
-
params: { path: { sandboxId, port } }
|
|
1061
|
+
params: { path: { sandboxId, port }, query: { use_server_proxy: useServerProxy } }
|
|
901
1062
|
});
|
|
902
1063
|
throwOnOpenApiFetchError({ error, response }, "Get sandbox endpoint failed");
|
|
903
1064
|
const ok = data;
|
|
@@ -918,6 +1079,7 @@ export {
|
|
|
918
1079
|
InvalidArgumentException,
|
|
919
1080
|
createExecdClient,
|
|
920
1081
|
createLifecycleClient,
|
|
1082
|
+
throwOnOpenApiFetchError,
|
|
921
1083
|
ExecutionEventDispatcher,
|
|
922
1084
|
CommandsAdapter,
|
|
923
1085
|
FilesystemAdapter,
|
|
@@ -925,4 +1087,4 @@ export {
|
|
|
925
1087
|
MetricsAdapter,
|
|
926
1088
|
SandboxesAdapter
|
|
927
1089
|
};
|
|
928
|
-
//# sourceMappingURL=chunk-
|
|
1090
|
+
//# sourceMappingURL=chunk-XHEWHFQ6.js.map
|