@alibaba-group/opensandbox 0.1.3-dev1 → 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.
@@ -169,11 +169,6 @@ interface ServerStreamEvent extends Record<string, unknown> {
169
169
  results?: Record<string, unknown>;
170
170
  error?: Record<string, unknown>;
171
171
  }
172
- interface RunCommandRequest extends Record<string, unknown> {
173
- command: string;
174
- cwd?: string;
175
- background?: boolean;
176
- }
177
172
  interface CodeContextRequest extends Record<string, unknown> {
178
173
  language: string;
179
174
  }
@@ -187,6 +182,24 @@ interface RunCommandOpts {
187
182
  * Run command in detached mode.
188
183
  */
189
184
  background?: boolean;
185
+ /**
186
+ * Maximum execution time in seconds; server will terminate the command when reached.
187
+ * If omitted, the server will not enforce any timeout.
188
+ */
189
+ timeoutSeconds?: number;
190
+ }
191
+ interface CommandStatus {
192
+ id?: string;
193
+ content?: string;
194
+ running?: boolean;
195
+ exitCode?: number | null;
196
+ error?: string;
197
+ startedAt?: Date;
198
+ finishedAt?: Date | null;
199
+ }
200
+ interface CommandLogs {
201
+ content: string;
202
+ cursor?: number;
190
203
  }
191
204
  type CommandExecution = Execution;
192
205
  interface Metrics extends Record<string, unknown> {
@@ -223,6 +236,14 @@ interface ExecdCommands {
223
236
  * Note: Execd spec uses `DELETE /command?id=<sessionId>`.
224
237
  */
225
238
  interrupt(sessionId: string): Promise<void>;
239
+ /**
240
+ * Get the current running status for a command id.
241
+ */
242
+ getCommandStatus(commandId: string): Promise<CommandStatus>;
243
+ /**
244
+ * Get background command logs (non-streamed).
245
+ */
246
+ getBackgroundCommandLogs(commandId: string, cursor?: number): Promise<CommandLogs>;
226
247
  }
227
248
 
228
249
  interface ExecdHealth {
@@ -275,6 +296,64 @@ interface NetworkPolicy extends Record<string, unknown> {
275
296
  */
276
297
  egress?: NetworkRule[];
277
298
  }
299
+ /**
300
+ * Host path bind mount backend.
301
+ *
302
+ * Maps a directory on the host filesystem into the container.
303
+ * Only available when the runtime supports host mounts.
304
+ */
305
+ interface Host extends Record<string, unknown> {
306
+ /**
307
+ * Absolute path on the host filesystem to mount.
308
+ */
309
+ path: string;
310
+ }
311
+ /**
312
+ * Kubernetes PersistentVolumeClaim mount backend.
313
+ *
314
+ * References an existing PVC in the same namespace as the sandbox pod.
315
+ * Only available in Kubernetes runtime.
316
+ */
317
+ interface PVC extends Record<string, unknown> {
318
+ /**
319
+ * Name of the PersistentVolumeClaim in the same namespace.
320
+ */
321
+ claimName: string;
322
+ }
323
+ /**
324
+ * Storage mount definition for a sandbox.
325
+ *
326
+ * Each volume entry contains:
327
+ * - A unique name identifier
328
+ * - Exactly one backend (host, pvc) with backend-specific fields
329
+ * - Common mount settings (mountPath, readOnly, subPath)
330
+ */
331
+ interface Volume extends Record<string, unknown> {
332
+ /**
333
+ * Unique identifier for the volume within the sandbox.
334
+ */
335
+ name: string;
336
+ /**
337
+ * Host path bind mount backend (mutually exclusive with pvc).
338
+ */
339
+ host?: Host;
340
+ /**
341
+ * Kubernetes PVC mount backend (mutually exclusive with host).
342
+ */
343
+ pvc?: PVC;
344
+ /**
345
+ * Absolute path inside the container where the volume is mounted.
346
+ */
347
+ mountPath: string;
348
+ /**
349
+ * If true, the volume is mounted as read-only. Defaults to false (read-write).
350
+ */
351
+ readOnly?: boolean;
352
+ /**
353
+ * Optional subdirectory under the backend path to mount.
354
+ */
355
+ subPath?: string;
356
+ }
278
357
  type SandboxState = "Creating" | "Running" | "Pausing" | "Paused" | "Resuming" | "Deleting" | "Deleted" | "Error" | string;
279
358
  interface SandboxStatus extends Record<string, unknown> {
280
359
  state: SandboxState;
@@ -310,6 +389,10 @@ interface CreateSandboxRequest extends Record<string, unknown> {
310
389
  * Optional outbound network policy for the sandbox.
311
390
  */
312
391
  networkPolicy?: NetworkPolicy;
392
+ /**
393
+ * Optional list of volume mounts for persistent storage.
394
+ */
395
+ volumes?: Volume[];
313
396
  extensions?: Record<string, unknown>;
314
397
  }
315
398
  interface CreateSandboxResponse extends Record<string, unknown> {
@@ -348,6 +431,11 @@ interface RenewSandboxExpirationResponse extends Record<string, unknown> {
348
431
  }
349
432
  interface Endpoint extends Record<string, unknown> {
350
433
  endpoint: string;
434
+ /**
435
+ * Headers that must be included on every request targeting this endpoint
436
+ * (e.g. when the server requires them for routing or auth). Omit or empty if not required.
437
+ */
438
+ headers?: Record<string, string>;
351
439
  }
352
440
  interface ListSandboxesParams {
353
441
  /**
@@ -372,7 +460,7 @@ interface Sandboxes {
372
460
  pauseSandbox(sandboxId: SandboxId): Promise<void>;
373
461
  resumeSandbox(sandboxId: SandboxId): Promise<void>;
374
462
  renewSandboxExpiration(sandboxId: SandboxId, req: RenewSandboxExpirationRequest): Promise<RenewSandboxExpirationResponse>;
375
- getSandboxEndpoint(sandboxId: SandboxId, port: number): Promise<Endpoint>;
463
+ getSandboxEndpoint(sandboxId: SandboxId, port: number, useServerProxy?: boolean): Promise<Endpoint>;
376
464
  }
377
465
 
378
- export type { RenewSandboxExpirationRequest as A, ReplaceFileContentItem as B, CodeContextRequest as C, RunCommandOpts as D, ExecdCommands as E, FileInfo as F, RunCommandRequest as G, SearchEntry as H, SearchFilesResponse as I, SetPermissionEntry as J, SupportedLanguage as K, ListSandboxesResponse as L, Metrics as M, NetworkPolicy as N, OutputMessage as O, Permission as P, RenewSandboxExpirationResponse as R, Sandboxes as S, WriteEntry as W, SandboxFiles as a, ExecdHealth as b, ExecdMetrics as c, SandboxId as d, SandboxInfo as e, Execution as f, ExecutionHandlers as g, ServerStreamEvent as h, SandboxMetrics as i, Endpoint as j, CommandExecution as k, ContentReplaceEntry as l, CreateSandboxRequest as m, CreateSandboxResponse as n, ExecutionComplete as o, ExecutionError as p, ExecutionInit as q, ExecutionResult as r, FileMetadata as s, FilesInfoResponse as t, ListSandboxesParams as u, MoveEntry as v, NetworkRule as w, NetworkRuleAction as x, PingResponse as y, RenameFileItem as z };
466
+ export type { Permission as A, PingResponse as B, CodeContextRequest as C, RenameFileItem as D, ExecdCommands as E, FileInfo as F, RenewSandboxExpirationRequest as G, Host as H, ReplaceFileContentItem as I, RunCommandOpts as J, SearchEntry as K, ListSandboxesResponse as L, Metrics as M, NetworkPolicy as N, OutputMessage as O, PVC as P, SearchFilesResponse as Q, RenewSandboxExpirationResponse as R, Sandboxes as S, SetPermissionEntry as T, SupportedLanguage as U, Volume as V, WriteEntry as W, SandboxFiles as a, ExecdHealth as b, ExecdMetrics as c, SandboxId as d, SandboxInfo as e, Execution as f, ExecutionHandlers as g, ServerStreamEvent as h, SandboxMetrics as i, Endpoint as j, CommandExecution as k, CommandLogs as l, CommandStatus as m, ContentReplaceEntry as n, CreateSandboxRequest as o, CreateSandboxResponse as p, ExecutionComplete as q, ExecutionError as r, ExecutionInit as s, ExecutionResult as t, FileMetadata as u, FilesInfoResponse as v, ListSandboxesParams as w, MoveEntry as x, NetworkRule as y, NetworkRuleAction as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alibaba-group/opensandbox",
3
- "version": "0.1.3-dev1",
3
+ "version": "0.1.4",
4
4
  "description": "OpenSandbox TypeScript/JavaScript SDK (sandbox lifecycle + execd APIs)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -16,7 +16,13 @@ import type { ExecdClient } from "../openapi/execdClient.js";
16
16
  import { throwOnOpenApiFetchError } from "./openapiError.js";
17
17
  import { parseJsonEventStream } from "./sse.js";
18
18
  import type { paths as ExecdPaths } from "../api/execd.js";
19
- import type { CommandExecution, RunCommandOpts, ServerStreamEvent } from "../models/execd.js";
19
+ import type {
20
+ CommandExecution,
21
+ CommandLogs,
22
+ CommandStatus,
23
+ RunCommandOpts,
24
+ ServerStreamEvent,
25
+ } from "../models/execd.js";
20
26
  import type { ExecdCommands } from "../services/execdCommands.js";
21
27
  import type { ExecutionHandlers } from "../models/execution.js";
22
28
  import { ExecutionEventDispatcher } from "../models/executionEventDispatcher.js";
@@ -29,13 +35,34 @@ function joinUrl(baseUrl: string, pathname: string): string {
29
35
 
30
36
  type ApiRunCommandRequest =
31
37
  ExecdPaths["/command"]["post"]["requestBody"]["content"]["application/json"];
38
+ type ApiCommandStatusOk =
39
+ ExecdPaths["/command/status/{id}"]["get"]["responses"][200]["content"]["application/json"];
40
+ type ApiCommandLogsOk =
41
+ ExecdPaths["/command/{id}/logs"]["get"]["responses"][200]["content"]["text/plain"];
32
42
 
33
43
  function toRunCommandRequest(command: string, opts?: RunCommandOpts): ApiRunCommandRequest {
34
- return {
44
+ const body: ApiRunCommandRequest = {
35
45
  command,
36
46
  cwd: opts?.workingDirectory,
37
47
  background: !!opts?.background,
38
48
  };
49
+ if (opts?.timeoutSeconds != null) {
50
+ body.timeout = Math.round(opts.timeoutSeconds * 1000);
51
+ }
52
+ return body;
53
+ }
54
+
55
+ function parseOptionalDate(value: unknown, field: string): Date | undefined {
56
+ if (value == null) return undefined;
57
+ if (value instanceof Date) return value;
58
+ if (typeof value !== "string") {
59
+ throw new Error(`Invalid ${field}: expected ISO string, got ${typeof value}`);
60
+ }
61
+ const parsed = new Date(value);
62
+ if (Number.isNaN(parsed.getTime())) {
63
+ throw new Error(`Invalid ${field}: ${value}`);
64
+ }
65
+ return parsed;
39
66
  }
40
67
 
41
68
  export interface CommandsAdapterOptions {
@@ -64,6 +91,44 @@ export class CommandsAdapter implements ExecdCommands {
64
91
  throwOnOpenApiFetchError({ error, response }, "Interrupt command failed");
65
92
  }
66
93
 
94
+ async getCommandStatus(commandId: string): Promise<CommandStatus> {
95
+ const { data, error, response } = await this.client.GET("/command/status/{id}", {
96
+ params: { path: { id: commandId } },
97
+ });
98
+ throwOnOpenApiFetchError({ error, response }, "Get command status failed");
99
+ const ok = data as ApiCommandStatusOk | undefined;
100
+ if (!ok || typeof ok !== "object") {
101
+ throw new Error("Get command status failed: unexpected response shape");
102
+ }
103
+ return {
104
+ id: ok.id,
105
+ content: ok.content,
106
+ running: ok.running,
107
+ exitCode: ok.exit_code ?? null,
108
+ error: ok.error,
109
+ startedAt: parseOptionalDate(ok.started_at, "startedAt"),
110
+ finishedAt: parseOptionalDate(ok.finished_at, "finishedAt") ?? null,
111
+ };
112
+ }
113
+
114
+ async getBackgroundCommandLogs(commandId: string, cursor?: number): Promise<CommandLogs> {
115
+ const { data, error, response } = await this.client.GET("/command/{id}/logs", {
116
+ params: { path: { id: commandId }, query: cursor == null ? {} : { cursor } },
117
+ parseAs: "text",
118
+ });
119
+ throwOnOpenApiFetchError({ error, response }, "Get command logs failed");
120
+ const ok = data as ApiCommandLogsOk | undefined;
121
+ if (typeof ok !== "string") {
122
+ throw new Error("Get command logs failed: unexpected response shape");
123
+ }
124
+ const cursorHeader = response.headers.get("EXECD-COMMANDS-TAIL-CURSOR");
125
+ const parsedCursor = (cursorHeader != null && cursorHeader !== "") ? Number(cursorHeader) : undefined;
126
+ return {
127
+ content: ok,
128
+ cursor: Number.isFinite(parsedCursor ?? NaN) ? parsedCursor : undefined,
129
+ };
130
+ }
131
+
67
132
  async *runStream(
68
133
  command: string,
69
134
  opts?: RunCommandOpts,
@@ -173,9 +173,13 @@ export class SandboxesAdapter implements Sandboxes {
173
173
  } as RenewSandboxExpirationResponse;
174
174
  }
175
175
 
176
- async getSandboxEndpoint(sandboxId: SandboxId, port: number): Promise<Endpoint> {
176
+ async getSandboxEndpoint(
177
+ sandboxId: SandboxId,
178
+ port: number,
179
+ useServerProxy = false
180
+ ): Promise<Endpoint> {
177
181
  const { data, error, response } = await this.client.GET("/sandboxes/{sandboxId}/endpoints/{port}", {
178
- params: { path: { sandboxId, port } },
182
+ params: { path: { sandboxId, port }, query: { use_server_proxy: useServerProxy } },
179
183
  });
180
184
  throwOnOpenApiFetchError({ error, response }, "Get sandbox endpoint failed");
181
185
  const ok = data as ApiEndpointOk | undefined;
package/src/api/execd.ts CHANGED
@@ -157,6 +157,8 @@ export interface paths {
157
157
  * @description Executes a shell command and streams the output in real-time using SSE (Server-Sent Events).
158
158
  * The command can run in foreground or background mode. The response includes stdout, stderr,
159
159
  * execution status, and completion events.
160
+ * Optionally specify `timeout` (milliseconds) to enforce a maximum runtime; the server will
161
+ * terminate the process when the timeout is reached.
160
162
  */
161
163
  post: operations["runCommand"];
162
164
  /**
@@ -519,6 +521,12 @@ export interface components {
519
521
  * @example false
520
522
  */
521
523
  background: boolean;
524
+ /**
525
+ * Format: int64
526
+ * @description Maximum allowed execution time in milliseconds before the command is forcefully terminated by the server. If omitted, the server will not enforce any timeout.
527
+ * @example 60000
528
+ */
529
+ timeout?: number;
522
530
  };
523
531
  /** @description Command execution status (foreground or background) */
524
532
  CommandStatusResponse: {
@@ -394,7 +394,10 @@ export interface paths {
394
394
  */
395
395
  get: {
396
396
  parameters: {
397
- query?: never;
397
+ query?: {
398
+ /** @description Whether to return a server-proxied URL */
399
+ use_server_proxy?: boolean;
400
+ };
398
401
  header?: never;
399
402
  path: {
400
403
  /** @description Unique sandbox identifier */
@@ -642,6 +645,12 @@ export interface components {
642
645
  * the sidecar starts in allow-all mode until updated.
643
646
  */
644
647
  networkPolicy?: components["schemas"]["NetworkPolicy"];
648
+ /**
649
+ * @description Storage mounts for the sandbox. Each volume entry specifies a named backend-specific
650
+ * storage source and common mount settings. Exactly one backend type must be specified
651
+ * per volume entry.
652
+ */
653
+ volumes?: components["schemas"]["Volume"][];
645
654
  /**
646
655
  * @description Opaque container for provider-specific or transient parameters not supported by the core API.
647
656
  *
@@ -715,6 +724,10 @@ export interface components {
715
724
  * Example: endpoint.opensandbox.io/sandboxes/abc123/port/8080
716
725
  */
717
726
  endpoint: string;
727
+ /** @description Requests targeting the sandbox must include the corresponding header(s). */
728
+ headers?: {
729
+ [key: string]: string;
730
+ };
718
731
  };
719
732
  /**
720
733
  * @description Egress network policy matching the sidecar `/policy` request body.
@@ -742,6 +755,63 @@ export interface components {
742
755
  */
743
756
  target: string;
744
757
  };
758
+ /**
759
+ * @description Storage mount definition for a sandbox. Each volume entry contains:
760
+ * - A unique name identifier
761
+ * - Exactly one backend struct (host, pvc, etc.) with backend-specific fields
762
+ * - Common mount settings (mountPath, readOnly, subPath)
763
+ */
764
+ Volume: {
765
+ /**
766
+ * @description Unique identifier for the volume within the sandbox.
767
+ * Must be a valid DNS label (lowercase alphanumeric, hyphens allowed, max 63 chars).
768
+ */
769
+ name: string;
770
+ host?: components["schemas"]["Host"];
771
+ pvc?: components["schemas"]["PVC"];
772
+ /**
773
+ * @description Absolute path inside the container where the volume is mounted.
774
+ * Must start with '/'.
775
+ */
776
+ mountPath: string;
777
+ /**
778
+ * @description If true, the volume is mounted as read-only. Defaults to false (read-write).
779
+ * @default false
780
+ */
781
+ readOnly: boolean;
782
+ /**
783
+ * @description Optional subdirectory under the backend path to mount.
784
+ * Must be a relative path without '..' components.
785
+ */
786
+ subPath?: string;
787
+ };
788
+ /**
789
+ * @description Host path bind mount backend. Maps a directory on the host filesystem
790
+ * into the container. Only available when the runtime supports host mounts.
791
+ *
792
+ * Security note: Host paths are restricted by server-side allowlist.
793
+ * Users must specify paths under permitted prefixes.
794
+ */
795
+ Host: {
796
+ /**
797
+ * @description Absolute path on the host filesystem to mount.
798
+ * Must start with '/' and be under an allowed prefix.
799
+ */
800
+ path: string;
801
+ };
802
+ /**
803
+ * @description Kubernetes PersistentVolumeClaim mount backend. References an existing
804
+ * PVC in the same namespace as the sandbox pod.
805
+ *
806
+ * Only available in Kubernetes runtime.
807
+ */
808
+ PVC: {
809
+ /**
810
+ * @description Name of the PersistentVolumeClaim in the same namespace.
811
+ * Must be a valid Kubernetes resource name.
812
+ */
813
+ claimName: string;
814
+ };
745
815
  };
746
816
  responses: {
747
817
  /** @description Error response envelope */
@@ -45,6 +45,11 @@ export interface ConnectionConfigOptions {
45
45
  * Enable basic debug logging for HTTP requests (best-effort).
46
46
  */
47
47
  debug?: boolean;
48
+ /**
49
+ * Use sandbox server as proxy for process execd requests.
50
+ * Useful when the client SDK cannot access the created sandbox directly.
51
+ */
52
+ useServerProxy?: boolean;
48
53
  }
49
54
 
50
55
  function isNodeRuntime(): boolean {
@@ -253,6 +258,10 @@ export class ConnectionConfig {
253
258
  readonly requestTimeoutSeconds: number;
254
259
  readonly debug: boolean;
255
260
  readonly userAgent: string = DEFAULT_USER_AGENT;
261
+ /**
262
+ * Use sandbox server as proxy for endpoint requests (default false).
263
+ */
264
+ readonly useServerProxy: boolean;
256
265
  private _closeTransport: () => Promise<void>;
257
266
  private _closePromise: Promise<void> | null = null;
258
267
  private _transportInitialized = false;
@@ -280,6 +289,7 @@ export class ConnectionConfig {
280
289
  ? opts.requestTimeoutSeconds
281
290
  : 30;
282
291
  this.debug = !!opts.debug;
292
+ this.useServerProxy = !!opts.useServerProxy;
283
293
 
284
294
  const headers: Record<string, string> = { ...(opts.headers ?? {}) };
285
295
  // Attach API key via header unless the user already provided one.
@@ -363,6 +373,7 @@ export class ConnectionConfig {
363
373
  headers: { ...this.headers },
364
374
  requestTimeoutSeconds: this.requestTimeoutSeconds,
365
375
  debug: this.debug,
376
+ useServerProxy: this.useServerProxy,
366
377
  });
367
378
  clone.initializeTransport();
368
379
  return clone;
@@ -26,4 +26,4 @@ export const DEFAULT_READY_TIMEOUT_SECONDS = 30;
26
26
  export const DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS = 200;
27
27
 
28
28
  export const DEFAULT_REQUEST_TIMEOUT_SECONDS = 30;
29
- export const DEFAULT_USER_AGENT = "OpenSandbox-JS-SDK/0.1.1";
29
+ export const DEFAULT_USER_AGENT = "OpenSandbox-JS-SDK/0.1.4";
@@ -31,6 +31,7 @@ export interface LifecycleStack {
31
31
  export interface CreateExecdStackOptions {
32
32
  connectionConfig: ConnectionConfig;
33
33
  execdBaseUrl: string;
34
+ endpointHeaders?: Record<string, string>;
34
35
  }
35
36
 
36
37
  export interface ExecdStack {
@@ -36,9 +36,13 @@ export class DefaultAdapterFactory implements AdapterFactory {
36
36
  }
37
37
 
38
38
  createExecdStack(opts: CreateExecdStackOptions): ExecdStack {
39
+ const headers: Record<string, string> = {
40
+ ...(opts.connectionConfig.headers ?? {}),
41
+ ...(opts.endpointHeaders ?? {}),
42
+ };
39
43
  const execdClient = createExecdClient({
40
44
  baseUrl: opts.execdBaseUrl,
41
- headers: opts.connectionConfig.headers,
45
+ headers,
42
46
  fetch: opts.connectionConfig.fetch,
43
47
  });
44
48
 
@@ -47,12 +51,12 @@ export class DefaultAdapterFactory implements AdapterFactory {
47
51
  const files = new FilesystemAdapter(execdClient, {
48
52
  baseUrl: opts.execdBaseUrl,
49
53
  fetch: opts.connectionConfig.fetch,
50
- headers: opts.connectionConfig.headers,
54
+ headers,
51
55
  });
52
56
  const commands = new CommandsAdapter(execdClient, {
53
57
  baseUrl: opts.execdBaseUrl,
54
58
  fetch: opts.connectionConfig.sseFetch,
55
- headers: opts.connectionConfig.headers,
59
+ headers,
56
60
  });
57
61
 
58
62
  return {
package/src/index.ts CHANGED
@@ -33,15 +33,18 @@ export type {
33
33
  CreateSandboxRequest,
34
34
  CreateSandboxResponse,
35
35
  Endpoint,
36
+ Host,
36
37
  ListSandboxesParams,
37
38
  ListSandboxesResponse,
38
39
  NetworkPolicy,
39
40
  NetworkRule,
40
41
  NetworkRuleAction,
42
+ PVC,
41
43
  RenewSandboxExpirationRequest,
42
44
  RenewSandboxExpirationResponse,
43
45
  SandboxId,
44
46
  SandboxInfo,
47
+ Volume,
45
48
  } from "./models/sandboxes.js";
46
49
 
47
50
  export type { Sandboxes } from "./services/sandboxes.js";
@@ -63,8 +66,9 @@ export type {
63
66
 
64
67
  export type {
65
68
  CommandExecution,
69
+ CommandLogs,
70
+ CommandStatus,
66
71
  RunCommandOpts,
67
- RunCommandRequest,
68
72
  ServerStreamEvent,
69
73
  CodeContextRequest,
70
74
  SupportedLanguage,
@@ -37,12 +37,6 @@ export interface ServerStreamEvent extends Record<string, unknown> {
37
37
  error?: Record<string, unknown>;
38
38
  }
39
39
 
40
- export interface RunCommandRequest extends Record<string, unknown> {
41
- command: string;
42
- cwd?: string;
43
- background?: boolean;
44
- }
45
-
46
40
  export interface CodeContextRequest extends Record<string, unknown> {
47
41
  language: string;
48
42
  }
@@ -64,6 +58,26 @@ export interface RunCommandOpts {
64
58
  * Run command in detached mode.
65
59
  */
66
60
  background?: boolean;
61
+ /**
62
+ * Maximum execution time in seconds; server will terminate the command when reached.
63
+ * If omitted, the server will not enforce any timeout.
64
+ */
65
+ timeoutSeconds?: number;
66
+ }
67
+
68
+ export interface CommandStatus {
69
+ id?: string;
70
+ content?: string;
71
+ running?: boolean;
72
+ exitCode?: number | null;
73
+ error?: string;
74
+ startedAt?: Date;
75
+ finishedAt?: Date | null;
76
+ }
77
+
78
+ export interface CommandLogs {
79
+ content: string;
80
+ cursor?: number;
67
81
  }
68
82
 
69
83
  export type CommandExecution = Execution;
@@ -62,6 +62,71 @@ export interface NetworkPolicy extends Record<string, unknown> {
62
62
  egress?: NetworkRule[];
63
63
  }
64
64
 
65
+ // ============================================================================
66
+ // Volume Models
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Host path bind mount backend.
71
+ *
72
+ * Maps a directory on the host filesystem into the container.
73
+ * Only available when the runtime supports host mounts.
74
+ */
75
+ export interface Host extends Record<string, unknown> {
76
+ /**
77
+ * Absolute path on the host filesystem to mount.
78
+ */
79
+ path: string;
80
+ }
81
+
82
+ /**
83
+ * Kubernetes PersistentVolumeClaim mount backend.
84
+ *
85
+ * References an existing PVC in the same namespace as the sandbox pod.
86
+ * Only available in Kubernetes runtime.
87
+ */
88
+ export interface PVC extends Record<string, unknown> {
89
+ /**
90
+ * Name of the PersistentVolumeClaim in the same namespace.
91
+ */
92
+ claimName: string;
93
+ }
94
+
95
+ /**
96
+ * Storage mount definition for a sandbox.
97
+ *
98
+ * Each volume entry contains:
99
+ * - A unique name identifier
100
+ * - Exactly one backend (host, pvc) with backend-specific fields
101
+ * - Common mount settings (mountPath, readOnly, subPath)
102
+ */
103
+ export interface Volume extends Record<string, unknown> {
104
+ /**
105
+ * Unique identifier for the volume within the sandbox.
106
+ */
107
+ name: string;
108
+ /**
109
+ * Host path bind mount backend (mutually exclusive with pvc).
110
+ */
111
+ host?: Host;
112
+ /**
113
+ * Kubernetes PVC mount backend (mutually exclusive with host).
114
+ */
115
+ pvc?: PVC;
116
+ /**
117
+ * Absolute path inside the container where the volume is mounted.
118
+ */
119
+ mountPath: string;
120
+ /**
121
+ * If true, the volume is mounted as read-only. Defaults to false (read-write).
122
+ */
123
+ readOnly?: boolean;
124
+ /**
125
+ * Optional subdirectory under the backend path to mount.
126
+ */
127
+ subPath?: string;
128
+ }
129
+
65
130
  export type SandboxState =
66
131
  | "Creating"
67
132
  | "Running"
@@ -109,6 +174,10 @@ export interface CreateSandboxRequest extends Record<string, unknown> {
109
174
  * Optional outbound network policy for the sandbox.
110
175
  */
111
176
  networkPolicy?: NetworkPolicy;
177
+ /**
178
+ * Optional list of volume mounts for persistent storage.
179
+ */
180
+ volumes?: Volume[];
112
181
  extensions?: Record<string, unknown>;
113
182
  }
114
183
 
@@ -153,6 +222,11 @@ export interface RenewSandboxExpirationResponse extends Record<string, unknown>
153
222
 
154
223
  export interface Endpoint extends Record<string, unknown> {
155
224
  endpoint: string;
225
+ /**
226
+ * Headers that must be included on every request targeting this endpoint
227
+ * (e.g. when the server requires them for routing or auth). Omit or empty if not required.
228
+ */
229
+ headers?: Record<string, string>;
156
230
  }
157
231
 
158
232
  export interface ListSandboxesParams {