@alibaba-group/opensandbox 0.1.0-dev3 → 0.1.0-dev4

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 CHANGED
@@ -52,6 +52,7 @@ try {
52
52
 
53
53
  // Optional but recommended: terminate the remote instance when you are done.
54
54
  await sandbox.kill();
55
+ await sandbox.close();
55
56
  } catch (err) {
56
57
  if (err instanceof SandboxException) {
57
58
  console.error(`Sandbox Error: [${err.error.code}] ${err.error.message ?? ""}`);
@@ -157,6 +158,7 @@ import { SandboxManager } from "@alibaba-group/opensandbox";
157
158
  const manager = SandboxManager.create({ connectionConfig: config });
158
159
  const list = await manager.listSandboxInfos({ states: ["Running"], pageSize: 10 });
159
160
  console.log(list.items.map((s) => s.id));
161
+ await manager.close();
160
162
  ```
161
163
 
162
164
  ## Configuration
@@ -167,7 +169,7 @@ The `ConnectionConfig` class manages API server connection settings.
167
169
 
168
170
  Runtime notes:
169
171
  - In browsers, the SDK uses the global `fetch` implementation.
170
- - In Node.js, the SDK creates a dedicated internal `fetch` backed by an isolated connection pool.
172
+ - 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.
171
173
 
172
174
  | Parameter | Description | Default | Environment Variable |
173
175
  | --- | --- | --- | --- |
@@ -214,6 +216,13 @@ const config2 = new ConnectionConfig({
214
216
  | `readyTimeoutSeconds` | Max time to wait for readiness | 30 seconds |
215
217
  | `healthCheckPollingInterval` | Poll interval while waiting (milliseconds) | 200 ms |
216
218
 
219
+ ### 3. Resource cleanup
220
+
221
+ Both `Sandbox` and `SandboxManager` own a scoped HTTP agent when running on Node.js
222
+ so you can safely reuse the same `ConnectionConfig`. Once you are finished interacting
223
+ with the sandbox or administration APIs, call `sandbox.close()` / `manager.close()` to
224
+ release the underlying agent.
225
+
217
226
  ## Browser Notes
218
227
 
219
228
  - The SDK can run in browsers, but **streaming file uploads are Node-only**.
@@ -33,18 +33,14 @@ export declare class ConnectionConfig {
33
33
  readonly domain: string;
34
34
  readonly apiKey?: string;
35
35
  readonly headers: Record<string, string>;
36
- readonly fetch: typeof fetch;
37
- /**
38
- * Fetch function intended for long-lived streaming requests (SSE).
39
- *
40
- * This is separate from {@link fetch} so callers can supply a different implementation
41
- * (e.g. Node undici with bodyTimeout disabled) and so the SDK can apply distinct
42
- * timeout semantics for streaming vs normal HTTP calls.
43
- */
44
- readonly sseFetch: typeof fetch;
36
+ private _fetch;
37
+ private _sseFetch;
45
38
  readonly requestTimeoutSeconds: number;
46
39
  readonly debug: boolean;
47
40
  readonly userAgent: string;
41
+ private _closeTransport;
42
+ private _closePromise;
43
+ private _transportInitialized;
48
44
  /**
49
45
  * Create a connection configuration.
50
46
  *
@@ -53,6 +49,21 @@ export declare class ConnectionConfig {
53
49
  * - `OPEN_SANDBOX_API_KEY`
54
50
  */
55
51
  constructor(opts?: ConnectionConfigOptions);
52
+ get fetch(): typeof fetch;
53
+ get sseFetch(): typeof fetch;
56
54
  getBaseUrl(): string;
55
+ private initializeTransport;
56
+ /**
57
+ * Ensure this configuration has transport helpers (fetch/SSE) allocated.
58
+ *
59
+ * On Node.js this creates a dedicated `undici` dispatcher; on browsers it
60
+ * simply reuses the global fetch. Returns either `this` or a cloned config
61
+ * with the transport initialized.
62
+ */
63
+ withTransportIfMissing(): ConnectionConfig;
64
+ /**
65
+ * Close the Node.js agent owned by this configuration.
66
+ */
67
+ closeTransport(): Promise<void>;
57
68
  }
58
69
  //# sourceMappingURL=connection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/config/connection.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2KD,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAA8B;IAExD;;;;;;OAMG;gBACS,IAAI,GAAE,uBAA4B;IAyD9C,UAAU,IAAI,MAAM;CAUrB"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/config/connection.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAsMD,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAsB;IACvC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAA8B;IACxD,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,qBAAqB,CAAS;IAEtC;;;;;;OAMG;gBACS,IAAI,GAAE,uBAA4B;IAsC9C,IAAI,KAAK,IAAI,OAAO,KAAK,CAExB;IAED,IAAI,QAAQ,IAAI,OAAO,KAAK,CAE3B;IAED,UAAU,IAAI,MAAM;IAWpB,OAAO,CAAC,mBAAmB;IAqB3B;;;;;;OAMG;IACH,sBAAsB,IAAI,gBAAgB;IAiB1C;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAKtC"}
@@ -35,7 +35,7 @@ function stripV1Suffix(s) {
35
35
  const trimmed = stripTrailingSlashes(s);
36
36
  return trimmed.endsWith("/v1") ? trimmed.slice(0, -3) : trimmed;
37
37
  }
38
- const DEFAULT_KEEPALIVE_TIMEOUT_MS = 15_000;
38
+ const DEFAULT_KEEPALIVE_TIMEOUT_MS = 30_000;
39
39
  function normalizeDomainBase(input) {
40
40
  // Accept a full URL and preserve its path prefix (if any).
41
41
  if (input.startsWith("http://") || input.startsWith("https://")) {
@@ -50,17 +50,21 @@ function normalizeDomainBase(input) {
50
50
  }
51
51
  function createNodeFetch() {
52
52
  if (!isNodeRuntime()) {
53
- return fetch;
53
+ return {
54
+ fetch,
55
+ close: async () => {
56
+ // Browser fetch has no pooled dispatcher to close.
57
+ },
58
+ };
54
59
  }
60
+ const baseFetch = fetch;
55
61
  let dispatcher;
56
62
  let dispatcherPromise = null;
57
- const baseFetch = fetch;
58
- return async (input, init) => {
63
+ const nodeFetch = async (input, init) => {
59
64
  dispatcherPromise ??= (async () => {
60
65
  try {
61
66
  const mod = await import("undici");
62
- const Agent = mod
63
- .Agent;
67
+ const Agent = mod.Agent;
64
68
  if (!Agent) {
65
69
  return undefined;
66
70
  }
@@ -83,6 +87,24 @@ function createNodeFetch() {
83
87
  }
84
88
  return baseFetch(input, init);
85
89
  };
90
+ return {
91
+ fetch: nodeFetch,
92
+ close: async () => {
93
+ if (dispatcherPromise) {
94
+ await dispatcherPromise.catch(() => undefined);
95
+ }
96
+ if (dispatcher &&
97
+ typeof dispatcher === "object" &&
98
+ typeof dispatcher.close === "function") {
99
+ try {
100
+ await dispatcher.close();
101
+ }
102
+ catch {
103
+ // swallow close errors
104
+ }
105
+ }
106
+ },
107
+ };
86
108
  }
87
109
  function createTimedFetch(opts) {
88
110
  const baseFetch = opts.baseFetch;
@@ -140,18 +162,14 @@ export class ConnectionConfig {
140
162
  domain;
141
163
  apiKey;
142
164
  headers;
143
- fetch;
144
- /**
145
- * Fetch function intended for long-lived streaming requests (SSE).
146
- *
147
- * This is separate from {@link fetch} so callers can supply a different implementation
148
- * (e.g. Node undici with bodyTimeout disabled) and so the SDK can apply distinct
149
- * timeout semantics for streaming vs normal HTTP calls.
150
- */
151
- sseFetch;
165
+ _fetch;
166
+ _sseFetch;
152
167
  requestTimeoutSeconds;
153
168
  debug;
154
169
  userAgent = "OpenSandbox-JS-SDK/0.1.0";
170
+ _closeTransport;
171
+ _closePromise = null;
172
+ _transportInitialized = false;
155
173
  /**
156
174
  * Create a connection configuration.
157
175
  *
@@ -186,33 +204,75 @@ export class ConnectionConfig {
186
204
  headers["user-agent"] = this.userAgent;
187
205
  }
188
206
  this.headers = headers;
189
- // Node SDK: do not expose custom fetch in ConnectionConfigOptions.
190
- // Use a node-scoped fetch with a dedicated dispatcher (keep-alive 30s).
191
- const baseFetch = createNodeFetch();
192
- const baseSseFetch = baseFetch;
193
- // Normal HTTP calls: apply requestTimeoutSeconds.
194
- this.fetch = createTimedFetch({
207
+ this._fetch = null;
208
+ this._sseFetch = null;
209
+ this._closeTransport = async () => { };
210
+ this._transportInitialized = false;
211
+ }
212
+ get fetch() {
213
+ return this._fetch ?? fetch;
214
+ }
215
+ get sseFetch() {
216
+ return this._sseFetch ?? fetch;
217
+ }
218
+ getBaseUrl() {
219
+ // If `domain` already contains a scheme, treat it as a full base URL prefix.
220
+ if (this.domain.startsWith("http://") ||
221
+ this.domain.startsWith("https://")) {
222
+ return `${stripV1Suffix(this.domain)}/v1`;
223
+ }
224
+ return `${this.protocol}://${stripV1Suffix(this.domain)}/v1`;
225
+ }
226
+ initializeTransport() {
227
+ if (this._transportInitialized)
228
+ return;
229
+ const { fetch: baseFetch, close } = createNodeFetch();
230
+ this._fetch = createTimedFetch({
195
231
  baseFetch,
196
232
  timeoutSeconds: this.requestTimeoutSeconds,
197
233
  debug: this.debug,
198
234
  defaultHeaders: this.headers,
199
235
  label: "http",
200
236
  });
201
- // Streaming calls (SSE): do not apply SDK-side timeouts; do not proactively disconnect.
202
- this.sseFetch = createTimedFetch({
203
- baseFetch: baseSseFetch,
237
+ this._sseFetch = createTimedFetch({
238
+ baseFetch,
204
239
  timeoutSeconds: 0,
205
240
  debug: this.debug,
206
241
  defaultHeaders: this.headers,
207
242
  label: "sse",
208
243
  });
244
+ this._closeTransport = close;
245
+ this._transportInitialized = true;
209
246
  }
210
- getBaseUrl() {
211
- // If `domain` already contains a scheme, treat it as a full base URL prefix.
212
- if (this.domain.startsWith("http://") ||
213
- this.domain.startsWith("https://")) {
214
- return `${stripV1Suffix(this.domain)}/v1`;
247
+ /**
248
+ * Ensure this configuration has transport helpers (fetch/SSE) allocated.
249
+ *
250
+ * On Node.js this creates a dedicated `undici` dispatcher; on browsers it
251
+ * simply reuses the global fetch. Returns either `this` or a cloned config
252
+ * with the transport initialized.
253
+ */
254
+ withTransportIfMissing() {
255
+ if (this._transportInitialized) {
256
+ return this;
215
257
  }
216
- return `${this.protocol}://${stripV1Suffix(this.domain)}/v1`;
258
+ const clone = new ConnectionConfig({
259
+ domain: this.domain,
260
+ protocol: this.protocol,
261
+ apiKey: this.apiKey,
262
+ headers: { ...this.headers },
263
+ requestTimeoutSeconds: this.requestTimeoutSeconds,
264
+ debug: this.debug,
265
+ });
266
+ clone.initializeTransport();
267
+ return clone;
268
+ }
269
+ /**
270
+ * Close the Node.js agent owned by this configuration.
271
+ */
272
+ async closeTransport() {
273
+ if (!this._transportInitialized)
274
+ return;
275
+ this._closePromise ??= this._closeTransport();
276
+ await this._closePromise;
217
277
  }
218
278
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defaultAdapterFactory.d.ts","sourceRoot":"","sources":["../../src/factory/defaultAdapterFactory.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE5I,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,GAAG,cAAc;IAWvE,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,UAAU;CA4B5D;AAED,wBAAgB,2BAA2B,IAAI,cAAc,CAE5D"}
1
+ {"version":3,"file":"defaultAdapterFactory.d.ts","sourceRoot":"","sources":["../../src/factory/defaultAdapterFactory.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE5I,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,GAAG,cAAc;IAWvE,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,UAAU;CA2B5D;AAED,wBAAgB,2BAA2B,IAAI,cAAc,CAE5D"}
@@ -44,7 +44,6 @@ export class DefaultAdapterFactory {
44
44
  });
45
45
  const commands = new CommandsAdapter(execdClient, {
46
46
  baseUrl: opts.execdBaseUrl,
47
- // Streaming calls (SSE) use a dedicated fetch, aligned with Kotlin/Python SDKs.
48
47
  fetch: opts.connectionConfig.sseFetch,
49
48
  headers: opts.connectionConfig.headers,
50
49
  });
package/dist/manager.d.ts CHANGED
@@ -18,6 +18,7 @@ export interface SandboxFilter {
18
18
  */
19
19
  export declare class SandboxManager {
20
20
  private readonly sandboxes;
21
+ private readonly connectionConfig;
21
22
  private constructor();
22
23
  static create(opts?: SandboxManagerOptions): SandboxManager;
23
24
  listSandboxInfos(filter?: SandboxFilter): Promise<ListSandboxesResponse>;
@@ -30,10 +31,11 @@ export declare class SandboxManager {
30
31
  */
31
32
  renewSandbox(sandboxId: SandboxId, timeoutSeconds: number): Promise<void>;
32
33
  /**
33
- * No-op for now (fetch-based implementation doesn't own a pooled transport).
34
+ * Release the HTTP agent resources allocated for this manager instance.
34
35
  *
35
- * This method exists so callers can consistently release resources when using
36
- * a custom {@link AdapterFactory} implementation.
36
+ * Each manager clone owns a scoped `ConnectionConfig` clone.
37
+ *
38
+ * This mirrors the Python SDK's default transport lifecycle.
37
39
  */
38
40
  close(): Promise<void>;
39
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAExF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IAC9D,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,OAAO;IAIP,MAAM,CAAC,MAAM,CAAC,IAAI,GAAE,qBAA0B,GAAG,cAAc;IAU/D,gBAAgB,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAS5E,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAI1D,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/E;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAExF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IAC9D,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IAEpD,OAAO;IAKP,MAAM,CAAC,MAAM,CAAC,IAAI,GAAE,qBAA0B,GAAG,cAAc;IAoB/D,gBAAgB,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAS5E,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAI1D,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/E;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
package/dist/manager.js CHANGED
@@ -20,17 +20,30 @@ import { createDefaultAdapterFactory } from "./factory/defaultAdapterFactory.js"
20
20
  */
21
21
  export class SandboxManager {
22
22
  sandboxes;
23
+ connectionConfig;
23
24
  constructor(opts) {
24
25
  this.sandboxes = opts.sandboxes;
26
+ this.connectionConfig = opts.connectionConfig;
25
27
  }
26
28
  static create(opts = {}) {
27
- const connectionConfig = opts.connectionConfig instanceof ConnectionConfig
29
+ const baseConnectionConfig = opts.connectionConfig instanceof ConnectionConfig
28
30
  ? opts.connectionConfig
29
31
  : new ConnectionConfig(opts.connectionConfig);
32
+ const connectionConfig = baseConnectionConfig.withTransportIfMissing();
30
33
  const lifecycleBaseUrl = connectionConfig.getBaseUrl();
31
34
  const adapterFactory = opts.adapterFactory ?? createDefaultAdapterFactory();
32
- const { sandboxes } = adapterFactory.createLifecycleStack({ connectionConfig, lifecycleBaseUrl });
33
- return new SandboxManager({ sandboxes });
35
+ let sandboxes;
36
+ try {
37
+ sandboxes = adapterFactory.createLifecycleStack({
38
+ connectionConfig,
39
+ lifecycleBaseUrl,
40
+ }).sandboxes;
41
+ }
42
+ catch (err) {
43
+ void connectionConfig.closeTransport().catch(() => undefined);
44
+ throw err;
45
+ }
46
+ return new SandboxManager({ sandboxes, connectionConfig });
34
47
  }
35
48
  listSandboxInfos(filter = {}) {
36
49
  return this.sandboxes.listSandboxes({
@@ -60,12 +73,13 @@ export class SandboxManager {
60
73
  await this.sandboxes.renewSandboxExpiration(sandboxId, { expiresAt });
61
74
  }
62
75
  /**
63
- * No-op for now (fetch-based implementation doesn't own a pooled transport).
76
+ * Release the HTTP agent resources allocated for this manager instance.
64
77
  *
65
- * This method exists so callers can consistently release resources when using
66
- * a custom {@link AdapterFactory} implementation.
78
+ * Each manager clone owns a scoped `ConnectionConfig` clone.
79
+ *
80
+ * This mirrors the Python SDK's default transport lifecycle.
67
81
  */
68
82
  async close() {
69
- // no-op
83
+ await this.connectionConfig.closeTransport();
70
84
  }
71
85
  }
package/dist/sandbox.d.ts CHANGED
@@ -111,6 +111,10 @@ export declare class Sandbox {
111
111
  */
112
112
  static resume(opts: SandboxConnectOptions): Promise<Sandbox>;
113
113
  kill(): Promise<void>;
114
+ /**
115
+ * Release any client-side resources (e.g. Node.js HTTP agents) owned by this Sandbox instance.
116
+ */
117
+ close(): Promise<void>;
114
118
  /**
115
119
  * Renew expiration by setting expiresAt to now + timeoutSeconds.
116
120
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
package/dist/sandbox.js CHANGED
@@ -61,15 +61,23 @@ export class Sandbox {
61
61
  this.metrics = opts.metrics;
62
62
  }
63
63
  static async create(opts) {
64
- const connectionConfig = opts.connectionConfig instanceof ConnectionConfig
64
+ const baseConnectionConfig = opts.connectionConfig instanceof ConnectionConfig
65
65
  ? opts.connectionConfig
66
66
  : new ConnectionConfig(opts.connectionConfig);
67
+ const connectionConfig = baseConnectionConfig.withTransportIfMissing();
67
68
  const lifecycleBaseUrl = connectionConfig.getBaseUrl();
68
69
  const adapterFactory = opts.adapterFactory ?? createDefaultAdapterFactory();
69
- const { sandboxes } = adapterFactory.createLifecycleStack({
70
- connectionConfig,
71
- lifecycleBaseUrl,
72
- });
70
+ let sandboxes;
71
+ try {
72
+ sandboxes = adapterFactory.createLifecycleStack({
73
+ connectionConfig,
74
+ lifecycleBaseUrl,
75
+ }).sandboxes;
76
+ }
77
+ catch (err) {
78
+ await connectionConfig.closeTransport();
79
+ throw err;
80
+ }
73
81
  const req = {
74
82
  image: toImageSpec(opts.image),
75
83
  entrypoint: opts.entrypoint ?? DEFAULT_ENTRYPOINT,
@@ -120,46 +128,61 @@ export class Sandbox {
120
128
  // Ignore cleanup failure; surface original error.
121
129
  }
122
130
  }
131
+ await connectionConfig.closeTransport();
123
132
  throw err;
124
133
  }
125
134
  }
126
135
  static async connect(opts) {
127
- const connectionConfig = opts.connectionConfig instanceof ConnectionConfig
136
+ const baseConnectionConfig = opts.connectionConfig instanceof ConnectionConfig
128
137
  ? opts.connectionConfig
129
138
  : new ConnectionConfig(opts.connectionConfig);
139
+ const connectionConfig = baseConnectionConfig.withTransportIfMissing();
130
140
  const adapterFactory = opts.adapterFactory ?? createDefaultAdapterFactory();
131
141
  const lifecycleBaseUrl = connectionConfig.getBaseUrl();
132
- const { sandboxes } = adapterFactory.createLifecycleStack({
133
- connectionConfig,
134
- lifecycleBaseUrl,
135
- });
136
- const endpoint = await sandboxes.getSandboxEndpoint(opts.sandboxId, DEFAULT_EXECD_PORT);
137
- const execdBaseUrl = `${connectionConfig.protocol}://${endpoint.endpoint}`;
138
- const { commands, files, health, metrics } = adapterFactory.createExecdStack({
139
- connectionConfig,
140
- execdBaseUrl,
141
- });
142
- const sbx = new Sandbox({
143
- id: opts.sandboxId,
144
- connectionConfig,
145
- adapterFactory,
146
- lifecycleBaseUrl,
147
- execdBaseUrl,
148
- sandboxes,
149
- commands,
150
- files,
151
- health,
152
- metrics,
153
- });
154
- if (!(opts.skipHealthCheck ?? false)) {
155
- await sbx.waitUntilReady({
156
- readyTimeoutSeconds: opts.readyTimeoutSeconds ?? DEFAULT_READY_TIMEOUT_SECONDS,
157
- pollingIntervalMillis: opts.healthCheckPollingInterval ??
158
- DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS,
159
- healthCheck: opts.healthCheck,
142
+ let sandboxes;
143
+ try {
144
+ sandboxes = adapterFactory.createLifecycleStack({
145
+ connectionConfig,
146
+ lifecycleBaseUrl,
147
+ }).sandboxes;
148
+ }
149
+ catch (err) {
150
+ await connectionConfig.closeTransport();
151
+ throw err;
152
+ }
153
+ try {
154
+ const endpoint = await sandboxes.getSandboxEndpoint(opts.sandboxId, DEFAULT_EXECD_PORT);
155
+ const execdBaseUrl = `${connectionConfig.protocol}://${endpoint.endpoint}`;
156
+ const { commands, files, health, metrics } = adapterFactory.createExecdStack({
157
+ connectionConfig,
158
+ execdBaseUrl,
160
159
  });
160
+ const sbx = new Sandbox({
161
+ id: opts.sandboxId,
162
+ connectionConfig,
163
+ adapterFactory,
164
+ lifecycleBaseUrl,
165
+ execdBaseUrl,
166
+ sandboxes,
167
+ commands,
168
+ files,
169
+ health,
170
+ metrics,
171
+ });
172
+ if (!(opts.skipHealthCheck ?? false)) {
173
+ await sbx.waitUntilReady({
174
+ readyTimeoutSeconds: opts.readyTimeoutSeconds ?? DEFAULT_READY_TIMEOUT_SECONDS,
175
+ pollingIntervalMillis: opts.healthCheckPollingInterval ??
176
+ DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS,
177
+ healthCheck: opts.healthCheck,
178
+ });
179
+ }
180
+ return sbx;
181
+ }
182
+ catch (err) {
183
+ await connectionConfig.closeTransport();
184
+ throw err;
161
185
  }
162
- return sbx;
163
186
  }
164
187
  async getInfo() {
165
188
  return await this.sandboxes.getSandbox(this.id);
@@ -199,21 +222,36 @@ export class Sandbox {
199
222
  * Resume a paused sandbox by id, then connect to its execd endpoint.
200
223
  */
201
224
  static async resume(opts) {
202
- const connectionConfig = opts.connectionConfig instanceof ConnectionConfig
225
+ const baseConnectionConfig = opts.connectionConfig instanceof ConnectionConfig
203
226
  ? opts.connectionConfig
204
227
  : new ConnectionConfig(opts.connectionConfig);
205
228
  const adapterFactory = opts.adapterFactory ?? createDefaultAdapterFactory();
206
- const lifecycleBaseUrl = connectionConfig.getBaseUrl();
207
- const { sandboxes } = adapterFactory.createLifecycleStack({
208
- connectionConfig,
209
- lifecycleBaseUrl,
210
- });
211
- await sandboxes.resumeSandbox(opts.sandboxId);
212
- return await Sandbox.connect({ ...opts, connectionConfig, adapterFactory });
229
+ const resumeConnectionConfig = baseConnectionConfig.withTransportIfMissing();
230
+ const lifecycleBaseUrl = resumeConnectionConfig.getBaseUrl();
231
+ let sandboxes;
232
+ try {
233
+ sandboxes = adapterFactory.createLifecycleStack({
234
+ connectionConfig: resumeConnectionConfig,
235
+ lifecycleBaseUrl,
236
+ }).sandboxes;
237
+ await sandboxes.resumeSandbox(opts.sandboxId);
238
+ }
239
+ catch (err) {
240
+ await resumeConnectionConfig.closeTransport();
241
+ throw err;
242
+ }
243
+ await resumeConnectionConfig.closeTransport();
244
+ return await Sandbox.connect({ ...opts, connectionConfig: baseConnectionConfig, adapterFactory });
213
245
  }
214
246
  async kill() {
215
247
  await this.sandboxes.deleteSandbox(this.id);
216
248
  }
249
+ /**
250
+ * Release any client-side resources (e.g. Node.js HTTP agents) owned by this Sandbox instance.
251
+ */
252
+ async close() {
253
+ await this.connectionConfig.closeTransport();
254
+ }
217
255
  /**
218
256
  * Renew expiration by setting expiresAt to now + timeoutSeconds.
219
257
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alibaba-group/opensandbox",
3
- "version": "0.1.0-dev3",
3
+ "version": "0.1.0-dev4",
4
4
  "description": "OpenSandbox TypeScript/JavaScript SDK (sandbox lifecycle + execd APIs)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",