@fastgpt-sdk/sandbox-adapter 0.0.30 → 0.0.32

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.
@@ -9,7 +9,7 @@ import type { ContentReplaceEntry, DirectoryEntry, ExecuteOptions, ExecuteResult
9
9
  * Subclasses can override the polyfill service in their constructor.
10
10
  */
11
11
  export declare abstract class BaseSandboxAdapter implements ISandbox {
12
- abstract readonly id: SandboxId;
12
+ abstract readonly id?: SandboxId;
13
13
  abstract readonly provider: string;
14
14
  protected _status: SandboxStatus;
15
15
  protected polyfillService?: CommandPolyfillService;
@@ -22,6 +22,7 @@ export declare abstract class BaseSandboxAdapter implements ISandbox {
22
22
  abstract delete(): Promise<void>;
23
23
  abstract getInfo(): Promise<SandboxInfo | null>;
24
24
  waitUntilReady(timeoutMs?: number): Promise<void>;
25
+ waitUntilDeleted(timeoutMs?: number): Promise<void>;
25
26
  renewExpiration(_additionalSeconds: number): Promise<void>;
26
27
  abstract execute(command: string, options?: ExecuteOptions): Promise<ExecuteResult>;
27
28
  executeStream(command: string, handlers: StreamHandlers, options?: ExecuteOptions): Promise<void>;
@@ -4,7 +4,7 @@
4
4
  export interface E2BConfig {
5
5
  /** E2B API Key */
6
6
  apiKey: string;
7
- /** 可选的沙盒 ID,用于连接到已存在的沙盒 */
7
+ /** 的沙盒 ID,用于连接到已存在的沙盒 */
8
8
  sandboxId: string;
9
9
  /** 可选的模板 ID,用于创建新沙盒 */
10
10
  template?: string;
@@ -12,8 +12,9 @@ export type SandboxRuntimeType = 'docker' | 'kubernetes';
12
12
  * Connection configuration options for OpenSandboxAdapter.
13
13
  */
14
14
  export interface OpenSandboxConnectionConfig {
15
+ sessionId: string;
15
16
  /** Base URL for the OpenSandbox API */
16
- baseUrl?: string;
17
+ baseUrl: string;
17
18
  /** API key for authentication */
18
19
  apiKey?: string;
19
20
  /** SDK request timeout in seconds */
@@ -50,14 +51,15 @@ export interface OpenSandboxConnectionConfig {
50
51
  */
51
52
  export declare class OpenSandboxAdapter extends BaseSandboxAdapter {
52
53
  private connectionConfig;
53
- private createConfig?;
54
+ private createConfig;
54
55
  readonly provider: "opensandbox";
55
56
  readonly runtime: SandboxRuntimeType;
56
57
  private _sandbox?;
57
58
  private _connection;
58
- private _id;
59
- constructor(connectionConfig?: OpenSandboxConnectionConfig, createConfig?: OpenSandboxConfigType | undefined);
60
- get id(): SandboxId;
59
+ private _id?;
60
+ constructor(connectionConfig: OpenSandboxConnectionConfig, createConfig: OpenSandboxConfigType);
61
+ get id(): SandboxId | undefined;
62
+ private set sandbox(value);
61
63
  private get sandbox();
62
64
  private createConnectionConfig;
63
65
  private static readonly STATE_MAP;
@@ -67,9 +69,11 @@ export declare class OpenSandboxAdapter extends BaseSandboxAdapter {
67
69
  private convertResourceLimits;
68
70
  private parseResourceLimits;
69
71
  private extractExitCode;
72
+ private getSandboxBySessionId;
70
73
  ensureRunning(): Promise<void>;
71
74
  create(): Promise<void>;
72
75
  connect(sandboxId: string): Promise<void>;
76
+ private resume;
73
77
  start(): Promise<void>;
74
78
  stop(): Promise<void>;
75
79
  delete(): Promise<void>;
@@ -85,11 +89,6 @@ export declare class OpenSandboxAdapter extends BaseSandboxAdapter {
85
89
  * @returns Endpoint with host, port, protocol and url fields
86
90
  */
87
91
  getEndpoint(port: number): Promise<Endpoint>;
88
- /**
89
- * Convert SDK Endpoint (no-scheme string) to our Endpoint type.
90
- * SDK format: "localhost:44772" or "domain/route/.../44772"
91
- */
92
- private convertSdkEndpoint;
93
92
  getInfo(): Promise<SandboxInfo | null>;
94
93
  renewExpiration(additionalSeconds: number): Promise<void>;
95
94
  execute(command: string, options?: ExecuteOptions): Promise<ExecuteResult>;
@@ -1,5 +1,5 @@
1
1
  import { ImageSpec, NetworkPolicy, ResourceLimits } from '@/types';
2
- import type { Volume } from '@alibaba-group/opensandbox';
2
+ import type { Volume } from '../../../opensandbox';
3
3
  /**
4
4
  * Configuration for creating a sandbox.
5
5
  */
@@ -18,7 +18,6 @@ export declare class SealosDevboxAdapter extends BaseSandboxAdapter {
18
18
  constructor(config: SealosDevboxConfig);
19
19
  get id(): SandboxId;
20
20
  private StatusAdapt;
21
- private waitUntilDeleted;
22
21
  getInfo(): Promise<SandboxInfo | null>;
23
22
  ensureRunning(): Promise<void>;
24
23
  create(): Promise<void>;
@@ -6,7 +6,7 @@ export { SealosDevboxAdapter } from './SealosDevboxAdapter';
6
6
  export type { SealosDevboxConfig } from './SealosDevboxAdapter';
7
7
  export { OpenSandboxAdapter } from './OpenSandboxAdapter';
8
8
  export type { OpenSandboxConfigType, OpenSandboxConnectionConfig } from './OpenSandboxAdapter';
9
- export type { Volume as OpenSandboxVolume } from '@alibaba-group/opensandbox';
9
+ export type { Volume as OpenSandboxVolume } from '../../opensandbox';
10
10
  export { E2BAdapter } from './E2BAdapter';
11
11
  export type { E2BConfig } from './E2BAdapter';
12
12
  export type SandboxProviderType = 'opensandbox' | 'sealosdevbox' | 'e2b';
package/dist/index.cjs CHANGED
@@ -47519,7 +47519,19 @@ class BaseSandboxAdapter {
47519
47519
  }
47520
47520
  await this.sleep(checkInterval);
47521
47521
  }
47522
- throw new SandboxReadyTimeoutError(this.id, timeoutMs);
47522
+ throw new SandboxReadyTimeoutError(this.id ?? "Unknown", timeoutMs);
47523
+ }
47524
+ async waitUntilDeleted(timeoutMs = 120000) {
47525
+ const startTime = Date.now();
47526
+ const checkInterval = 1000;
47527
+ while (Date.now() - startTime < timeoutMs) {
47528
+ const data = await this.getInfo().catch(() => true);
47529
+ if (!data) {
47530
+ return;
47531
+ }
47532
+ await this.sleep(checkInterval);
47533
+ }
47534
+ throw new SandboxReadyTimeoutError(this.id ?? "Unknown", timeoutMs);
47523
47535
  }
47524
47536
  async renewExpiration(_additionalSeconds) {
47525
47537
  throw new FeatureNotSupportedError("Sandbox expiration renewal not supported by this provider", "renewExpiration", this.provider);
@@ -47854,18 +47866,6 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
47854
47866
  return "Error";
47855
47867
  }
47856
47868
  }
47857
- async waitUntilDeleted() {
47858
- const startTime = Date.now();
47859
- const checkInterval = 1000;
47860
- while (Date.now() - startTime < 120000) {
47861
- const data = await this.getInfo().catch(() => true);
47862
- if (!data) {
47863
- return;
47864
- }
47865
- await this.sleep(checkInterval);
47866
- }
47867
- throw new TimeoutError("Sandbox not deleted", 120000, "waitUntilDeleted");
47868
- }
47869
47869
  async getInfo() {
47870
47870
  try {
47871
47871
  const res = await this.api.info(this._id);
@@ -47995,8 +47995,8 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
47995
47995
  runtime;
47996
47996
  _sandbox;
47997
47997
  _connection;
47998
- _id = "";
47999
- constructor(connectionConfig = {}, createConfig) {
47998
+ _id;
47999
+ constructor(connectionConfig, createConfig) {
48000
48000
  super();
48001
48001
  this.connectionConfig = connectionConfig;
48002
48002
  this.createConfig = createConfig;
@@ -48007,6 +48007,10 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48007
48007
  get id() {
48008
48008
  return this._id;
48009
48009
  }
48010
+ set sandbox(sandbox) {
48011
+ this._sandbox = sandbox;
48012
+ this._id = sandbox?.id;
48013
+ }
48010
48014
  get sandbox() {
48011
48015
  if (!this._sandbox) {
48012
48016
  throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
@@ -48015,9 +48019,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48015
48019
  }
48016
48020
  createConnectionConfig() {
48017
48021
  const { baseUrl, apiKey, requestTimeoutSeconds, debug, useServerProxy } = this.connectionConfig;
48018
- if (!baseUrl) {
48019
- return new import_opensandbox.ConnectionConfig({ apiKey, requestTimeoutSeconds, debug, useServerProxy });
48020
- }
48021
48022
  return new import_opensandbox.ConnectionConfig({
48022
48023
  domain: baseUrl,
48023
48024
  apiKey,
@@ -48128,32 +48129,74 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48128
48129
  }
48129
48130
  return 0;
48130
48131
  }
48132
+ async getSandboxBySessionId() {
48133
+ const manager = import_opensandbox.SandboxManager.create({ connectionConfig: this._connection });
48134
+ const result = await manager.listSandboxInfos({
48135
+ metadata: { sessionId: this.connectionConfig.sessionId }
48136
+ });
48137
+ const val = result.items[0];
48138
+ if (val) {
48139
+ const status = this.mapStatus(val.status);
48140
+ return {
48141
+ id: val.id,
48142
+ status
48143
+ };
48144
+ }
48145
+ }
48131
48146
  async ensureRunning() {
48132
- return this.create();
48147
+ const sandbox = await this.getSandboxBySessionId();
48148
+ if (sandbox) {
48149
+ switch (sandbox.status.state) {
48150
+ case "UnExist":
48151
+ await this.create();
48152
+ break;
48153
+ case "Running":
48154
+ await this.connect(sandbox.id);
48155
+ break;
48156
+ case "Creating":
48157
+ case "Starting":
48158
+ await this.waitUntilReady();
48159
+ break;
48160
+ case "Stopping":
48161
+ case "Stopped":
48162
+ await this.resume(sandbox.id);
48163
+ break;
48164
+ case "Deleting":
48165
+ await this.waitUntilDeleted();
48166
+ await this.create();
48167
+ break;
48168
+ case "Error":
48169
+ throw new ConnectionError(`Sandbox error: ${sandbox.status.message}`);
48170
+ default:
48171
+ throw new ConnectionError(`Sandbox state ${sandbox.status.state} not supported`);
48172
+ }
48173
+ } else {
48174
+ await this.create();
48175
+ }
48133
48176
  }
48134
48177
  async create() {
48135
- if (!this.createConfig) {
48136
- throw new Error("createConfig is required to create a sandbox. Pass it as the third argument to createSandbox().");
48137
- }
48138
48178
  const cfg = this.createConfig;
48139
48179
  try {
48140
48180
  this._status = { state: "Creating" };
48141
48181
  const image = this.convertImageSpec(cfg.image);
48142
48182
  const resource = this.convertResourceLimits(cfg.resourceLimits);
48143
- this._sandbox = await import_opensandbox.Sandbox.create({
48183
+ this.sandbox = await import_opensandbox.Sandbox.create({
48144
48184
  connectionConfig: this._connection,
48145
48185
  image,
48146
48186
  entrypoint: cfg.entrypoint,
48147
48187
  timeoutSeconds: cfg.timeoutSeconds ?? null,
48148
48188
  resource,
48149
48189
  env: cfg.env,
48150
- metadata: cfg.metadata,
48190
+ metadata: {
48191
+ ...cfg.metadata,
48192
+ sessionId: this.connectionConfig.sessionId
48193
+ },
48151
48194
  volumes: cfg.volumes,
48152
48195
  skipHealthCheck: cfg.skipHealthCheck,
48153
48196
  readyTimeoutSeconds: cfg.readyTimeoutSeconds,
48154
48197
  healthCheckPollingInterval: cfg.healthCheckPollingInterval
48155
48198
  });
48156
- this._id = this._sandbox.id;
48199
+ await this.waitUntilReady();
48157
48200
  this._status = { state: "Running" };
48158
48201
  } catch (error) {
48159
48202
  this._status = { state: "Error", message: String(error) };
@@ -48163,31 +48206,51 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48163
48206
  async connect(sandboxId) {
48164
48207
  try {
48165
48208
  this._status = { state: "Starting" };
48166
- this._sandbox = await import_opensandbox.Sandbox.connect({
48209
+ this.sandbox = await import_opensandbox.Sandbox.connect({
48167
48210
  sandboxId,
48168
48211
  connectionConfig: this._connection,
48169
48212
  skipHealthCheck: this.createConfig?.skipHealthCheck,
48170
48213
  readyTimeoutSeconds: this.createConfig?.readyTimeoutSeconds,
48171
48214
  healthCheckPollingInterval: this.createConfig?.healthCheckPollingInterval
48172
48215
  });
48173
- this._id = this._sandbox.id;
48174
48216
  this._status = { state: "Running" };
48175
48217
  } catch (error) {
48176
48218
  this._status = { state: "Error", message: String(error) };
48177
48219
  throw new ConnectionError(`Failed to connect to sandbox ${sandboxId}`, this.connectionConfig.baseUrl, error);
48178
48220
  }
48179
48221
  }
48222
+ async resume(sandboxId) {
48223
+ try {
48224
+ this._status = { state: "Starting" };
48225
+ this.sandbox = await import_opensandbox.Sandbox.resume({
48226
+ sandboxId,
48227
+ connectionConfig: this._connection,
48228
+ skipHealthCheck: this.createConfig?.skipHealthCheck,
48229
+ readyTimeoutSeconds: this.createConfig?.readyTimeoutSeconds,
48230
+ healthCheckPollingInterval: this.createConfig?.healthCheckPollingInterval
48231
+ });
48232
+ this._status = { state: "Running" };
48233
+ } catch (error) {
48234
+ this._status = { state: "Error", message: String(error) };
48235
+ throw new ConnectionError(`Failed to resume sandbox ${sandboxId}`, this.connectionConfig.baseUrl, error);
48236
+ }
48237
+ }
48180
48238
  async start() {
48181
48239
  try {
48182
48240
  this._status = { state: "Starting" };
48183
- this._sandbox = await this.sandbox.resume();
48184
- this._id = this.sandbox.id;
48241
+ this.sandbox = await this.sandbox.resume();
48242
+ await this.waitUntilReady();
48185
48243
  this._status = { state: "Running" };
48186
48244
  } catch (error) {
48187
- if (error && typeof error === "object" && "code" in error && error.code === "SANDBOX::API_NOT_SUPPORTED") {
48188
- throw new FeatureNotSupportedError("Start/resume not supported by this runtime", "start", this.provider);
48245
+ const code = error instanceof import_opensandbox.SandboxException ? error.error.code : undefined;
48246
+ switch (code) {
48247
+ case "DOCKER::SANDBOX_NOT_PAUSED":
48248
+ return;
48249
+ case "SANDBOX::API_NOT_SUPPORTED":
48250
+ throw new FeatureNotSupportedError("Start/resume not supported by this runtime", "start", this.provider);
48251
+ default:
48252
+ throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
48189
48253
  }
48190
- throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
48191
48254
  }
48192
48255
  }
48193
48256
  async stop() {
@@ -48196,6 +48259,10 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48196
48259
  await this.sandbox.pause();
48197
48260
  this._status = { state: "Stopped" };
48198
48261
  } catch (error) {
48262
+ const message = error instanceof import_opensandbox.SandboxException ? error.error.message : undefined;
48263
+ if (message?.includes("already paused")) {
48264
+ return;
48265
+ }
48199
48266
  if (error && typeof error === "object" && "code" in error && error.code === "SANDBOX::API_NOT_SUPPORTED") {
48200
48267
  throw new FeatureNotSupportedError("Stop/pause not supported by this runtime", "stop", this.provider);
48201
48268
  }
@@ -48206,43 +48273,38 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48206
48273
  try {
48207
48274
  this._status = { state: "Deleting" };
48208
48275
  await this.sandbox.kill();
48209
- this._sandbox = undefined;
48210
- this._id = "";
48276
+ this.sandbox = undefined;
48211
48277
  this._status = { state: "UnExist" };
48212
48278
  } catch (error) {
48213
48279
  throw new CommandExecutionError("Failed to delete sandbox", "delete", error instanceof Error ? error : undefined);
48214
48280
  }
48215
48281
  }
48216
48282
  async close() {
48217
- if (this._sandbox) {
48218
- await this._sandbox.close();
48219
- }
48283
+ await this.sandbox.close();
48220
48284
  }
48221
48285
  async getEndpoint(port) {
48222
48286
  const sdkEndpoint = await this.sandbox.getEndpoint(port);
48223
- return this.convertSdkEndpoint(sdkEndpoint, port);
48224
- }
48225
- convertSdkEndpoint(sdkEndpoint, requestedPort) {
48226
48287
  const raw = sdkEndpoint.endpoint;
48227
48288
  const colonIdx = raw.lastIndexOf(":");
48228
48289
  const hasPathBeforeColon = colonIdx !== -1 && raw.slice(0, colonIdx).includes("/");
48229
48290
  if (colonIdx !== -1 && !hasPathBeforeColon) {
48230
48291
  const host = raw.slice(0, colonIdx);
48231
48292
  const parsedPort = parseInt(raw.slice(colonIdx + 1), 10);
48232
- const port = isNaN(parsedPort) ? requestedPort : parsedPort;
48293
+ const portNumber = isNaN(parsedPort) ? port : parsedPort;
48233
48294
  const protocol = port === 443 ? "https" : "http";
48234
- return { host, port, protocol, url: `${protocol}://${raw}` };
48295
+ return { host, port: portNumber, protocol, url: `${protocol}://${raw}` };
48235
48296
  }
48236
48297
  return {
48237
48298
  host: raw,
48238
- port: requestedPort,
48299
+ port,
48239
48300
  protocol: "https",
48240
48301
  url: `https://${raw}`
48241
48302
  };
48242
48303
  }
48243
48304
  async getInfo() {
48244
- if (!this._sandbox)
48305
+ if (!this._sandbox) {
48245
48306
  return null;
48307
+ }
48246
48308
  try {
48247
48309
  const info = await this.sandbox.getInfo();
48248
48310
  return {
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from './adapters';
2
2
  export * from './errors';
3
3
  export * from './interfaces';
4
4
  export * from './types';
5
+ export type { Volume as SandboxVolume } from '../opensandbox';
package/dist/index.js CHANGED
@@ -47503,7 +47503,19 @@ class BaseSandboxAdapter {
47503
47503
  }
47504
47504
  await this.sleep(checkInterval);
47505
47505
  }
47506
- throw new SandboxReadyTimeoutError(this.id, timeoutMs);
47506
+ throw new SandboxReadyTimeoutError(this.id ?? "Unknown", timeoutMs);
47507
+ }
47508
+ async waitUntilDeleted(timeoutMs = 120000) {
47509
+ const startTime = Date.now();
47510
+ const checkInterval = 1000;
47511
+ while (Date.now() - startTime < timeoutMs) {
47512
+ const data = await this.getInfo().catch(() => true);
47513
+ if (!data) {
47514
+ return;
47515
+ }
47516
+ await this.sleep(checkInterval);
47517
+ }
47518
+ throw new SandboxReadyTimeoutError(this.id ?? "Unknown", timeoutMs);
47507
47519
  }
47508
47520
  async renewExpiration(_additionalSeconds) {
47509
47521
  throw new FeatureNotSupportedError("Sandbox expiration renewal not supported by this provider", "renewExpiration", this.provider);
@@ -47838,18 +47850,6 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
47838
47850
  return "Error";
47839
47851
  }
47840
47852
  }
47841
- async waitUntilDeleted() {
47842
- const startTime = Date.now();
47843
- const checkInterval = 1000;
47844
- while (Date.now() - startTime < 120000) {
47845
- const data = await this.getInfo().catch(() => true);
47846
- if (!data) {
47847
- return;
47848
- }
47849
- await this.sleep(checkInterval);
47850
- }
47851
- throw new TimeoutError("Sandbox not deleted", 120000, "waitUntilDeleted");
47852
- }
47853
47853
  async getInfo() {
47854
47854
  try {
47855
47855
  const res = await this.api.info(this._id);
@@ -47979,8 +47979,8 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
47979
47979
  runtime;
47980
47980
  _sandbox;
47981
47981
  _connection;
47982
- _id = "";
47983
- constructor(connectionConfig = {}, createConfig) {
47982
+ _id;
47983
+ constructor(connectionConfig, createConfig) {
47984
47984
  super();
47985
47985
  this.connectionConfig = connectionConfig;
47986
47986
  this.createConfig = createConfig;
@@ -47991,6 +47991,10 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
47991
47991
  get id() {
47992
47992
  return this._id;
47993
47993
  }
47994
+ set sandbox(sandbox) {
47995
+ this._sandbox = sandbox;
47996
+ this._id = sandbox?.id;
47997
+ }
47994
47998
  get sandbox() {
47995
47999
  if (!this._sandbox) {
47996
48000
  throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
@@ -47999,9 +48003,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
47999
48003
  }
48000
48004
  createConnectionConfig() {
48001
48005
  const { baseUrl, apiKey, requestTimeoutSeconds, debug, useServerProxy } = this.connectionConfig;
48002
- if (!baseUrl) {
48003
- return new import_opensandbox.ConnectionConfig({ apiKey, requestTimeoutSeconds, debug, useServerProxy });
48004
- }
48005
48006
  return new import_opensandbox.ConnectionConfig({
48006
48007
  domain: baseUrl,
48007
48008
  apiKey,
@@ -48112,32 +48113,74 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48112
48113
  }
48113
48114
  return 0;
48114
48115
  }
48116
+ async getSandboxBySessionId() {
48117
+ const manager = import_opensandbox.SandboxManager.create({ connectionConfig: this._connection });
48118
+ const result = await manager.listSandboxInfos({
48119
+ metadata: { sessionId: this.connectionConfig.sessionId }
48120
+ });
48121
+ const val = result.items[0];
48122
+ if (val) {
48123
+ const status = this.mapStatus(val.status);
48124
+ return {
48125
+ id: val.id,
48126
+ status
48127
+ };
48128
+ }
48129
+ }
48115
48130
  async ensureRunning() {
48116
- return this.create();
48131
+ const sandbox = await this.getSandboxBySessionId();
48132
+ if (sandbox) {
48133
+ switch (sandbox.status.state) {
48134
+ case "UnExist":
48135
+ await this.create();
48136
+ break;
48137
+ case "Running":
48138
+ await this.connect(sandbox.id);
48139
+ break;
48140
+ case "Creating":
48141
+ case "Starting":
48142
+ await this.waitUntilReady();
48143
+ break;
48144
+ case "Stopping":
48145
+ case "Stopped":
48146
+ await this.resume(sandbox.id);
48147
+ break;
48148
+ case "Deleting":
48149
+ await this.waitUntilDeleted();
48150
+ await this.create();
48151
+ break;
48152
+ case "Error":
48153
+ throw new ConnectionError(`Sandbox error: ${sandbox.status.message}`);
48154
+ default:
48155
+ throw new ConnectionError(`Sandbox state ${sandbox.status.state} not supported`);
48156
+ }
48157
+ } else {
48158
+ await this.create();
48159
+ }
48117
48160
  }
48118
48161
  async create() {
48119
- if (!this.createConfig) {
48120
- throw new Error("createConfig is required to create a sandbox. Pass it as the third argument to createSandbox().");
48121
- }
48122
48162
  const cfg = this.createConfig;
48123
48163
  try {
48124
48164
  this._status = { state: "Creating" };
48125
48165
  const image = this.convertImageSpec(cfg.image);
48126
48166
  const resource = this.convertResourceLimits(cfg.resourceLimits);
48127
- this._sandbox = await import_opensandbox.Sandbox.create({
48167
+ this.sandbox = await import_opensandbox.Sandbox.create({
48128
48168
  connectionConfig: this._connection,
48129
48169
  image,
48130
48170
  entrypoint: cfg.entrypoint,
48131
48171
  timeoutSeconds: cfg.timeoutSeconds ?? null,
48132
48172
  resource,
48133
48173
  env: cfg.env,
48134
- metadata: cfg.metadata,
48174
+ metadata: {
48175
+ ...cfg.metadata,
48176
+ sessionId: this.connectionConfig.sessionId
48177
+ },
48135
48178
  volumes: cfg.volumes,
48136
48179
  skipHealthCheck: cfg.skipHealthCheck,
48137
48180
  readyTimeoutSeconds: cfg.readyTimeoutSeconds,
48138
48181
  healthCheckPollingInterval: cfg.healthCheckPollingInterval
48139
48182
  });
48140
- this._id = this._sandbox.id;
48183
+ await this.waitUntilReady();
48141
48184
  this._status = { state: "Running" };
48142
48185
  } catch (error) {
48143
48186
  this._status = { state: "Error", message: String(error) };
@@ -48147,31 +48190,51 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48147
48190
  async connect(sandboxId) {
48148
48191
  try {
48149
48192
  this._status = { state: "Starting" };
48150
- this._sandbox = await import_opensandbox.Sandbox.connect({
48193
+ this.sandbox = await import_opensandbox.Sandbox.connect({
48151
48194
  sandboxId,
48152
48195
  connectionConfig: this._connection,
48153
48196
  skipHealthCheck: this.createConfig?.skipHealthCheck,
48154
48197
  readyTimeoutSeconds: this.createConfig?.readyTimeoutSeconds,
48155
48198
  healthCheckPollingInterval: this.createConfig?.healthCheckPollingInterval
48156
48199
  });
48157
- this._id = this._sandbox.id;
48158
48200
  this._status = { state: "Running" };
48159
48201
  } catch (error) {
48160
48202
  this._status = { state: "Error", message: String(error) };
48161
48203
  throw new ConnectionError(`Failed to connect to sandbox ${sandboxId}`, this.connectionConfig.baseUrl, error);
48162
48204
  }
48163
48205
  }
48206
+ async resume(sandboxId) {
48207
+ try {
48208
+ this._status = { state: "Starting" };
48209
+ this.sandbox = await import_opensandbox.Sandbox.resume({
48210
+ sandboxId,
48211
+ connectionConfig: this._connection,
48212
+ skipHealthCheck: this.createConfig?.skipHealthCheck,
48213
+ readyTimeoutSeconds: this.createConfig?.readyTimeoutSeconds,
48214
+ healthCheckPollingInterval: this.createConfig?.healthCheckPollingInterval
48215
+ });
48216
+ this._status = { state: "Running" };
48217
+ } catch (error) {
48218
+ this._status = { state: "Error", message: String(error) };
48219
+ throw new ConnectionError(`Failed to resume sandbox ${sandboxId}`, this.connectionConfig.baseUrl, error);
48220
+ }
48221
+ }
48164
48222
  async start() {
48165
48223
  try {
48166
48224
  this._status = { state: "Starting" };
48167
- this._sandbox = await this.sandbox.resume();
48168
- this._id = this.sandbox.id;
48225
+ this.sandbox = await this.sandbox.resume();
48226
+ await this.waitUntilReady();
48169
48227
  this._status = { state: "Running" };
48170
48228
  } catch (error) {
48171
- if (error && typeof error === "object" && "code" in error && error.code === "SANDBOX::API_NOT_SUPPORTED") {
48172
- throw new FeatureNotSupportedError("Start/resume not supported by this runtime", "start", this.provider);
48229
+ const code = error instanceof import_opensandbox.SandboxException ? error.error.code : undefined;
48230
+ switch (code) {
48231
+ case "DOCKER::SANDBOX_NOT_PAUSED":
48232
+ return;
48233
+ case "SANDBOX::API_NOT_SUPPORTED":
48234
+ throw new FeatureNotSupportedError("Start/resume not supported by this runtime", "start", this.provider);
48235
+ default:
48236
+ throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
48173
48237
  }
48174
- throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
48175
48238
  }
48176
48239
  }
48177
48240
  async stop() {
@@ -48180,6 +48243,10 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48180
48243
  await this.sandbox.pause();
48181
48244
  this._status = { state: "Stopped" };
48182
48245
  } catch (error) {
48246
+ const message = error instanceof import_opensandbox.SandboxException ? error.error.message : undefined;
48247
+ if (message?.includes("already paused")) {
48248
+ return;
48249
+ }
48183
48250
  if (error && typeof error === "object" && "code" in error && error.code === "SANDBOX::API_NOT_SUPPORTED") {
48184
48251
  throw new FeatureNotSupportedError("Stop/pause not supported by this runtime", "stop", this.provider);
48185
48252
  }
@@ -48190,43 +48257,38 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48190
48257
  try {
48191
48258
  this._status = { state: "Deleting" };
48192
48259
  await this.sandbox.kill();
48193
- this._sandbox = undefined;
48194
- this._id = "";
48260
+ this.sandbox = undefined;
48195
48261
  this._status = { state: "UnExist" };
48196
48262
  } catch (error) {
48197
48263
  throw new CommandExecutionError("Failed to delete sandbox", "delete", error instanceof Error ? error : undefined);
48198
48264
  }
48199
48265
  }
48200
48266
  async close() {
48201
- if (this._sandbox) {
48202
- await this._sandbox.close();
48203
- }
48267
+ await this.sandbox.close();
48204
48268
  }
48205
48269
  async getEndpoint(port) {
48206
48270
  const sdkEndpoint = await this.sandbox.getEndpoint(port);
48207
- return this.convertSdkEndpoint(sdkEndpoint, port);
48208
- }
48209
- convertSdkEndpoint(sdkEndpoint, requestedPort) {
48210
48271
  const raw = sdkEndpoint.endpoint;
48211
48272
  const colonIdx = raw.lastIndexOf(":");
48212
48273
  const hasPathBeforeColon = colonIdx !== -1 && raw.slice(0, colonIdx).includes("/");
48213
48274
  if (colonIdx !== -1 && !hasPathBeforeColon) {
48214
48275
  const host = raw.slice(0, colonIdx);
48215
48276
  const parsedPort = parseInt(raw.slice(colonIdx + 1), 10);
48216
- const port = isNaN(parsedPort) ? requestedPort : parsedPort;
48277
+ const portNumber = isNaN(parsedPort) ? port : parsedPort;
48217
48278
  const protocol = port === 443 ? "https" : "http";
48218
- return { host, port, protocol, url: `${protocol}://${raw}` };
48279
+ return { host, port: portNumber, protocol, url: `${protocol}://${raw}` };
48219
48280
  }
48220
48281
  return {
48221
48282
  host: raw,
48222
- port: requestedPort,
48283
+ port,
48223
48284
  protocol: "https",
48224
48285
  url: `https://${raw}`
48225
48286
  };
48226
48287
  }
48227
48288
  async getInfo() {
48228
- if (!this._sandbox)
48289
+ if (!this._sandbox) {
48229
48290
  return null;
48291
+ }
48230
48292
  try {
48231
48293
  const info = await this.sandbox.getInfo();
48232
48294
  return {
@@ -5,7 +5,7 @@ import type { SandboxId, SandboxInfo, SandboxStatus } from '../types';
5
5
  */
6
6
  export interface ISandboxLifecycle {
7
7
  /** Unique identifier for this sandbox */
8
- readonly id: SandboxId;
8
+ readonly id?: SandboxId;
9
9
  /** Current status of the sandbox */
10
10
  readonly status: SandboxStatus;
11
11
  /**
@@ -39,6 +39,7 @@ export interface ISandboxLifecycle {
39
39
  * @throws {SandboxReadyTimeoutError} If timeout is exceeded
40
40
  */
41
41
  waitUntilReady(timeoutMs?: number): Promise<void>;
42
+ waitUntilDeleted(timeoutMs?: number): Promise<void>;
42
43
  /**
43
44
  * Renew the sandbox expiration, extending its lifetime.
44
45
  * Not all providers support this.