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

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
@@ -165,6 +165,10 @@ console.log(list.items.map((s) => s.id));
165
165
 
166
166
  The `ConnectionConfig` class manages API server connection settings.
167
167
 
168
+ Runtime notes:
169
+ - 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.
171
+
168
172
  | Parameter | Description | Default | Environment Variable |
169
173
  | --- | --- | --- | --- |
170
174
  | `apiKey` | API key for authentication | Optional | `OPEN_SANDBOX_API_KEY` |
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/adapters/sse.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,wBAAuB,oBAAoB,CAAC,CAAC,EAC3C,GAAG,EAAE,QAAQ,EACb,IAAI,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC,aAAa,CAAC,CAAC,CAAC,CA2DlB"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/adapters/sse.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,wBAAuB,oBAAoB,CAAC,CAAC,EAC3C,GAAG,EAAE,QAAQ,EACb,IAAI,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC,aAAa,CAAC,CAAC,CAAC,CA8DlB"}
@@ -73,6 +73,8 @@ export async function* parseJsonEventStream(res, opts) {
73
73
  yield parsed;
74
74
  }
75
75
  }
76
+ // Flush any buffered UTF-8 bytes from the decoder.
77
+ buf += decoder.decode();
76
78
  // flush last line if exists
77
79
  const last = buf.trim();
78
80
  if (last) {
@@ -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;AAkGD,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;IAmD9C,UAAU,IAAI,MAAM;CAOrB"}
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,11 +1,11 @@
1
1
  // Copyright 2026 Alibaba Group Holding Ltd.
2
- //
2
+ //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
5
5
  // You may obtain a copy of the License at
6
- //
6
+ //
7
7
  // http://www.apache.org/licenses/LICENSE-2.0
8
- //
8
+ //
9
9
  // Unless required by applicable law or agreed to in writing, software
10
10
  // distributed under the License is distributed on an "AS IS" BASIS,
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -13,7 +13,7 @@
13
13
  // limitations under the License.
14
14
  function isNodeRuntime() {
15
15
  const p = globalThis?.process;
16
- return !!(p?.versions?.node);
16
+ return !!p?.versions?.node;
17
17
  }
18
18
  function redactHeaders(headers) {
19
19
  const out = { ...headers };
@@ -35,6 +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
39
  function normalizeDomainBase(input) {
39
40
  // Accept a full URL and preserve its path prefix (if any).
40
41
  if (input.startsWith("http://") || input.startsWith("https://")) {
@@ -47,6 +48,42 @@ function normalizeDomainBase(input) {
47
48
  // No scheme: treat as "host[:port]" or "host[:port]/prefix" and normalize trailing "/v1" or "/".
48
49
  return { domainBase: stripV1Suffix(input) };
49
50
  }
51
+ function createNodeFetch() {
52
+ if (!isNodeRuntime()) {
53
+ return fetch;
54
+ }
55
+ let dispatcher;
56
+ let dispatcherPromise = null;
57
+ const baseFetch = fetch;
58
+ return async (input, init) => {
59
+ dispatcherPromise ??= (async () => {
60
+ try {
61
+ const mod = await import("undici");
62
+ const Agent = mod
63
+ .Agent;
64
+ if (!Agent) {
65
+ return undefined;
66
+ }
67
+ dispatcher = new Agent({
68
+ keepAliveTimeout: DEFAULT_KEEPALIVE_TIMEOUT_MS,
69
+ keepAliveMaxTimeout: DEFAULT_KEEPALIVE_TIMEOUT_MS,
70
+ });
71
+ return dispatcher;
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ })();
77
+ if (dispatcherPromise) {
78
+ await dispatcherPromise;
79
+ }
80
+ if (dispatcher) {
81
+ const mergedInit = { ...(init ?? {}), dispatcher };
82
+ return baseFetch(input, mergedInit);
83
+ }
84
+ return baseFetch(input, init);
85
+ };
86
+ }
50
87
  function createTimedFetch(opts) {
51
88
  const baseFetch = opts.baseFetch;
52
89
  const timeoutSeconds = opts.timeoutSeconds;
@@ -55,7 +92,9 @@ function createTimedFetch(opts) {
55
92
  const label = opts.label;
56
93
  return async (input, init) => {
57
94
  const method = init?.method ?? "GET";
58
- const url = typeof input === "string" ? input : input?.toString?.() ?? String(input);
95
+ const url = typeof input === "string"
96
+ ? input
97
+ : input?.toString?.() ?? String(input);
59
98
  const ac = new AbortController();
60
99
  const timeoutMs = Math.floor(timeoutSeconds * 1000);
61
100
  const t = Number.isFinite(timeoutMs) && timeoutMs > 0
@@ -73,7 +112,10 @@ function createTimedFetch(opts) {
73
112
  signal: ac.signal,
74
113
  };
75
114
  if (debug) {
76
- const mergedHeaders = { ...defaultHeaders, ...(init?.headers ?? {}) };
115
+ const mergedHeaders = {
116
+ ...defaultHeaders,
117
+ ...(init?.headers ?? {}),
118
+ };
77
119
  // eslint-disable-next-line no-console
78
120
  console.log(`[opensandbox:${label}] ->`, method, url, redactHeaders(mergedHeaders));
79
121
  }
@@ -126,9 +168,10 @@ export class ConnectionConfig {
126
168
  this.protocol = normalized.protocol ?? opts.protocol ?? "http";
127
169
  this.domain = normalized.domainBase;
128
170
  this.apiKey = opts.apiKey ?? envApiKey;
129
- this.requestTimeoutSeconds = typeof opts.requestTimeoutSeconds === "number"
130
- ? opts.requestTimeoutSeconds
131
- : 30;
171
+ this.requestTimeoutSeconds =
172
+ typeof opts.requestTimeoutSeconds === "number"
173
+ ? opts.requestTimeoutSeconds
174
+ : 30;
132
175
  this.debug = !!opts.debug;
133
176
  const headers = { ...(opts.headers ?? {}) };
134
177
  // Attach API key via header unless the user already provided one.
@@ -136,14 +179,17 @@ export class ConnectionConfig {
136
179
  headers["OPEN-SANDBOX-API-KEY"] = this.apiKey;
137
180
  }
138
181
  // Best-effort user-agent (Node only).
139
- if (isNodeRuntime() && this.userAgent && !headers["user-agent"] && !headers["User-Agent"]) {
182
+ if (isNodeRuntime() &&
183
+ this.userAgent &&
184
+ !headers["user-agent"] &&
185
+ !headers["User-Agent"]) {
140
186
  headers["user-agent"] = this.userAgent;
141
187
  }
142
188
  this.headers = headers;
143
189
  // Node SDK: do not expose custom fetch in ConnectionConfigOptions.
144
- // Use the runtime's global fetch (Node >= 20).
145
- const baseFetch = fetch;
146
- const baseSseFetch = fetch;
190
+ // Use a node-scoped fetch with a dedicated dispatcher (keep-alive 30s).
191
+ const baseFetch = createNodeFetch();
192
+ const baseSseFetch = baseFetch;
147
193
  // Normal HTTP calls: apply requestTimeoutSeconds.
148
194
  this.fetch = createTimedFetch({
149
195
  baseFetch,
@@ -163,7 +209,8 @@ export class ConnectionConfig {
163
209
  }
164
210
  getBaseUrl() {
165
211
  // If `domain` already contains a scheme, treat it as a full base URL prefix.
166
- if (this.domain.startsWith("http://") || this.domain.startsWith("https://")) {
212
+ if (this.domain.startsWith("http://") ||
213
+ this.domain.startsWith("https://")) {
167
214
  return `${stripV1Suffix(this.domain)}/v1`;
168
215
  }
169
216
  return `${this.protocol}://${stripV1Suffix(this.domain)}/v1`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alibaba-group/opensandbox",
3
- "version": "0.1.0-dev2",
3
+ "version": "0.1.0-dev3",
4
4
  "description": "OpenSandbox TypeScript/JavaScript SDK (sandbox lifecycle + execd APIs)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -33,7 +33,8 @@
33
33
  "node": ">=20"
34
34
  },
35
35
  "dependencies": {
36
- "openapi-fetch": "^0.13.8"
36
+ "openapi-fetch": "^0.13.8",
37
+ "undici": "^7.18.2"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@eslint/js": "^9.39.2",