@alibaba-group/opensandbox 0.1.0-dev4 → 0.1.1-dev0

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.
Files changed (92) hide show
  1. package/dist/adapters/commandsAdapter.js +1 -0
  2. package/dist/adapters/commandsAdapter.js.map +1 -0
  3. package/dist/adapters/filesystemAdapter.js +1 -0
  4. package/dist/adapters/filesystemAdapter.js.map +1 -0
  5. package/dist/adapters/healthAdapter.js +1 -0
  6. package/dist/adapters/healthAdapter.js.map +1 -0
  7. package/dist/adapters/metricsAdapter.js +1 -0
  8. package/dist/adapters/metricsAdapter.js.map +1 -0
  9. package/dist/adapters/openapiError.js +1 -0
  10. package/dist/adapters/openapiError.js.map +1 -0
  11. package/dist/adapters/sandboxesAdapter.js +1 -0
  12. package/dist/adapters/sandboxesAdapter.js.map +1 -0
  13. package/dist/adapters/sse.js +1 -0
  14. package/dist/adapters/sse.js.map +1 -0
  15. package/dist/api/execd.js +1 -0
  16. package/dist/api/execd.js.map +1 -0
  17. package/dist/api/lifecycle.js +1 -0
  18. package/dist/api/lifecycle.js.map +1 -0
  19. package/dist/config/connection.d.ts.map +1 -1
  20. package/dist/config/connection.js +5 -2
  21. package/dist/config/connection.js.map +1 -0
  22. package/dist/core/constants.js +1 -0
  23. package/dist/core/constants.js.map +1 -0
  24. package/dist/core/exceptions.js +1 -0
  25. package/dist/core/exceptions.js.map +1 -0
  26. package/dist/factory/adapterFactory.js +1 -0
  27. package/dist/factory/adapterFactory.js.map +1 -0
  28. package/dist/factory/defaultAdapterFactory.js +1 -0
  29. package/dist/factory/defaultAdapterFactory.js.map +1 -0
  30. package/dist/index.js +1 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/internal.js +1 -0
  33. package/dist/internal.js.map +1 -0
  34. package/dist/manager.js +1 -0
  35. package/dist/manager.js.map +1 -0
  36. package/dist/models/execd.js +1 -0
  37. package/dist/models/execd.js.map +1 -0
  38. package/dist/models/execution.js +1 -0
  39. package/dist/models/execution.js.map +1 -0
  40. package/dist/models/executionEventDispatcher.js +1 -0
  41. package/dist/models/executionEventDispatcher.js.map +1 -0
  42. package/dist/models/filesystem.js +1 -0
  43. package/dist/models/filesystem.js.map +1 -0
  44. package/dist/models/sandboxes.js +1 -0
  45. package/dist/models/sandboxes.js.map +1 -0
  46. package/dist/openapi/execdClient.js +1 -0
  47. package/dist/openapi/execdClient.js.map +1 -0
  48. package/dist/openapi/lifecycleClient.js +1 -0
  49. package/dist/openapi/lifecycleClient.js.map +1 -0
  50. package/dist/sandbox.js +1 -0
  51. package/dist/sandbox.js.map +1 -0
  52. package/dist/services/execdCommands.js +1 -0
  53. package/dist/services/execdCommands.js.map +1 -0
  54. package/dist/services/execdHealth.js +1 -0
  55. package/dist/services/execdHealth.js.map +1 -0
  56. package/dist/services/execdMetrics.js +1 -0
  57. package/dist/services/execdMetrics.js.map +1 -0
  58. package/dist/services/filesystem.js +1 -0
  59. package/dist/services/filesystem.js.map +1 -0
  60. package/dist/services/sandboxes.js +1 -0
  61. package/dist/services/sandboxes.js.map +1 -0
  62. package/package.json +3 -2
  63. package/src/adapters/commandsAdapter.ts +112 -0
  64. package/src/adapters/filesystemAdapter.ts +575 -0
  65. package/src/adapters/healthAdapter.ts +27 -0
  66. package/src/adapters/metricsAdapter.ts +51 -0
  67. package/src/adapters/openapiError.ts +42 -0
  68. package/src/adapters/sandboxesAdapter.ts +187 -0
  69. package/src/adapters/sse.ts +95 -0
  70. package/src/api/execd.ts +1569 -0
  71. package/src/api/lifecycle.ts +801 -0
  72. package/src/config/connection.ts +377 -0
  73. package/src/core/constants.ts +29 -0
  74. package/src/core/exceptions.ts +134 -0
  75. package/src/factory/adapterFactory.ts +51 -0
  76. package/src/factory/defaultAdapterFactory.ts +69 -0
  77. package/src/index.ts +108 -0
  78. package/src/internal.ts +39 -0
  79. package/src/manager.ts +111 -0
  80. package/src/models/execd.ts +90 -0
  81. package/src/models/execution.ts +71 -0
  82. package/src/models/executionEventDispatcher.ts +97 -0
  83. package/src/models/filesystem.ts +103 -0
  84. package/src/models/sandboxes.ts +142 -0
  85. package/src/openapi/execdClient.ts +49 -0
  86. package/src/openapi/lifecycleClient.ts +70 -0
  87. package/src/sandbox.ts +459 -0
  88. package/src/services/execdCommands.ts +35 -0
  89. package/src/services/execdHealth.ts +17 -0
  90. package/src/services/execdMetrics.ts +19 -0
  91. package/src/services/filesystem.ts +47 -0
  92. package/src/services/sandboxes.ts +42 -0
@@ -0,0 +1,187 @@
1
+ // Copyright 2026 Alibaba Group Holding Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import type { LifecycleClient } from "../openapi/lifecycleClient.js";
16
+ import { throwOnOpenApiFetchError } from "./openapiError.js";
17
+ import type { paths as LifecyclePaths } from "../api/lifecycle.js";
18
+ import type {
19
+ Sandboxes,
20
+ } from "../services/sandboxes.js";
21
+ import type {
22
+ CreateSandboxRequest,
23
+ CreateSandboxResponse,
24
+ Endpoint,
25
+ ListSandboxesParams,
26
+ ListSandboxesResponse,
27
+ RenewSandboxExpirationRequest,
28
+ RenewSandboxExpirationResponse,
29
+ SandboxId,
30
+ SandboxInfo,
31
+ } from "../models/sandboxes.js";
32
+
33
+ type ApiCreateSandboxRequest =
34
+ LifecyclePaths["/sandboxes"]["post"]["requestBody"]["content"]["application/json"];
35
+ type ApiCreateSandboxOk =
36
+ LifecyclePaths["/sandboxes"]["post"]["responses"][202]["content"]["application/json"];
37
+ type ApiGetSandboxOk =
38
+ LifecyclePaths["/sandboxes/{sandboxId}"]["get"]["responses"][200]["content"]["application/json"];
39
+ type ApiListSandboxesOk =
40
+ LifecyclePaths["/sandboxes"]["get"]["responses"][200]["content"]["application/json"];
41
+ type ApiRenewSandboxExpirationRequest =
42
+ LifecyclePaths["/sandboxes/{sandboxId}/renew-expiration"]["post"]["requestBody"]["content"]["application/json"];
43
+ type ApiRenewSandboxExpirationOk =
44
+ LifecyclePaths["/sandboxes/{sandboxId}/renew-expiration"]["post"]["responses"][200]["content"]["application/json"];
45
+ type ApiEndpointOk =
46
+ LifecyclePaths["/sandboxes/{sandboxId}/endpoints/{port}"]["get"]["responses"][200]["content"]["application/json"];
47
+
48
+ function encodeMetadataFilter(metadata: Record<string, string>): string {
49
+ // The Lifecycle API expects a single `metadata` query parameter whose value is `k=v&k2=v2`.
50
+ // The query serializer will URL-encode the value (e.g. `=` -> %3D and `&` -> %26).
51
+ const parts: string[] = [];
52
+ for (const [k, v] of Object.entries(metadata)) {
53
+ parts.push(`${k}=${v}`);
54
+ }
55
+ return parts.join("&");
56
+ }
57
+
58
+ export class SandboxesAdapter implements Sandboxes {
59
+ constructor(private readonly client: LifecycleClient) {}
60
+
61
+ private parseIsoDate(field: string, v: unknown): Date {
62
+ if (typeof v !== "string" || !v) {
63
+ throw new Error(`Invalid ${field}: expected ISO string, got ${typeof v}`);
64
+ }
65
+ const d = new Date(v);
66
+ if (Number.isNaN(d.getTime())) {
67
+ throw new Error(`Invalid ${field}: ${v}`);
68
+ }
69
+ return d;
70
+ }
71
+
72
+ private mapSandboxInfo(raw: ApiGetSandboxOk): SandboxInfo {
73
+ return {
74
+ ...(raw ?? {}),
75
+ createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
76
+ expiresAt: this.parseIsoDate("expiresAt", raw?.expiresAt),
77
+ } as SandboxInfo;
78
+ }
79
+
80
+ async createSandbox(req: CreateSandboxRequest): Promise<CreateSandboxResponse> {
81
+ // Make the OpenAPI contract explicit so backend schema changes surface quickly.
82
+ const body: ApiCreateSandboxRequest = req as unknown as ApiCreateSandboxRequest;
83
+ const { data, error, response } = await this.client.POST("/sandboxes", {
84
+ body,
85
+ });
86
+ throwOnOpenApiFetchError({ error, response }, "Create sandbox failed");
87
+ const raw = data as ApiCreateSandboxOk | undefined;
88
+ if (!raw || typeof raw !== "object") {
89
+ throw new Error("Create sandbox failed: unexpected response shape");
90
+ }
91
+ return {
92
+ ...(raw ?? {}),
93
+ createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
94
+ expiresAt: this.parseIsoDate("expiresAt", raw?.expiresAt),
95
+ } as CreateSandboxResponse;
96
+ }
97
+
98
+ async getSandbox(sandboxId: SandboxId): Promise<SandboxInfo> {
99
+ const { data, error, response } = await this.client.GET("/sandboxes/{sandboxId}", {
100
+ params: { path: { sandboxId } },
101
+ });
102
+ throwOnOpenApiFetchError({ error, response }, "Get sandbox failed");
103
+ const ok = data as ApiGetSandboxOk | undefined;
104
+ if (!ok || typeof ok !== "object") {
105
+ throw new Error("Get sandbox failed: unexpected response shape");
106
+ }
107
+ return this.mapSandboxInfo(ok);
108
+ }
109
+
110
+ async listSandboxes(params: ListSandboxesParams = {}): Promise<ListSandboxesResponse> {
111
+ const query: Record<string, string | number | boolean | undefined | null | (string | number)[]> = {};
112
+ if (params.states?.length) query.state = params.states;
113
+ if (params.metadata && Object.keys(params.metadata).length) {
114
+ query.metadata = encodeMetadataFilter(params.metadata);
115
+ }
116
+ if (params.page != null) query.page = params.page;
117
+ if (params.pageSize != null) query.pageSize = params.pageSize;
118
+
119
+ const { data, error, response } = await this.client.GET("/sandboxes", {
120
+ params: { query },
121
+ });
122
+ throwOnOpenApiFetchError({ error, response }, "List sandboxes failed");
123
+ const raw = data as ApiListSandboxesOk | undefined;
124
+ if (!raw || typeof raw !== "object") {
125
+ throw new Error("List sandboxes failed: unexpected response shape");
126
+ }
127
+ const itemsRaw = raw.items;
128
+ if (!Array.isArray(itemsRaw)) throw new Error("List sandboxes failed: unexpected items shape");
129
+ return {
130
+ ...(raw ?? {}),
131
+ items: itemsRaw.map((x) => this.mapSandboxInfo(x)),
132
+ } as ListSandboxesResponse;
133
+ }
134
+
135
+ async deleteSandbox(sandboxId: SandboxId): Promise<void> {
136
+ const { error, response } = await this.client.DELETE("/sandboxes/{sandboxId}", {
137
+ params: { path: { sandboxId } },
138
+ });
139
+ throwOnOpenApiFetchError({ error, response }, "Delete sandbox failed");
140
+ }
141
+
142
+ async pauseSandbox(sandboxId: SandboxId): Promise<void> {
143
+ const { error, response } = await this.client.POST("/sandboxes/{sandboxId}/pause", {
144
+ params: { path: { sandboxId } },
145
+ });
146
+ throwOnOpenApiFetchError({ error, response }, "Pause sandbox failed");
147
+ }
148
+
149
+ async resumeSandbox(sandboxId: SandboxId): Promise<void> {
150
+ const { error, response } = await this.client.POST("/sandboxes/{sandboxId}/resume", {
151
+ params: { path: { sandboxId } },
152
+ });
153
+ throwOnOpenApiFetchError({ error, response }, "Resume sandbox failed");
154
+ }
155
+
156
+ async renewSandboxExpiration(
157
+ sandboxId: SandboxId,
158
+ req: RenewSandboxExpirationRequest,
159
+ ): Promise<RenewSandboxExpirationResponse> {
160
+ const body: ApiRenewSandboxExpirationRequest = req as unknown as ApiRenewSandboxExpirationRequest;
161
+ const { data, error, response } = await this.client.POST("/sandboxes/{sandboxId}/renew-expiration", {
162
+ params: { path: { sandboxId } },
163
+ body,
164
+ });
165
+ throwOnOpenApiFetchError({ error, response }, "Renew sandbox expiration failed");
166
+ const raw = data as ApiRenewSandboxExpirationOk | undefined;
167
+ if (!raw || typeof raw !== "object") {
168
+ throw new Error("Renew sandbox expiration failed: unexpected response shape");
169
+ }
170
+ return {
171
+ ...(raw ?? {}),
172
+ expiresAt: raw?.expiresAt ? this.parseIsoDate("expiresAt", raw.expiresAt) : undefined,
173
+ } as RenewSandboxExpirationResponse;
174
+ }
175
+
176
+ async getSandboxEndpoint(sandboxId: SandboxId, port: number): Promise<Endpoint> {
177
+ const { data, error, response } = await this.client.GET("/sandboxes/{sandboxId}/endpoints/{port}", {
178
+ params: { path: { sandboxId, port } },
179
+ });
180
+ throwOnOpenApiFetchError({ error, response }, "Get sandbox endpoint failed");
181
+ const ok = data as ApiEndpointOk | undefined;
182
+ if (!ok || typeof ok !== "object") {
183
+ throw new Error("Get sandbox endpoint failed: unexpected response shape");
184
+ }
185
+ return ok as unknown as Endpoint;
186
+ }
187
+ }
@@ -0,0 +1,95 @@
1
+ // Copyright 2026 Alibaba Group Holding Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { SandboxApiException, SandboxError } from "../core/exceptions.js";
16
+
17
+ function tryParseJson(line: string): unknown | undefined {
18
+ try {
19
+ return JSON.parse(line);
20
+ } catch {
21
+ return undefined;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Parses an SSE-like stream that may be either:
27
+ * - standard SSE frames (`data: {...}\n\n`)
28
+ * - newline-delimited JSON (one JSON object per line)
29
+ */
30
+ export async function* parseJsonEventStream<T>(
31
+ res: Response,
32
+ opts?: { fallbackErrorMessage?: string },
33
+ ): AsyncIterable<T> {
34
+ if (!res.ok) {
35
+ const text = await res.text().catch(() => "");
36
+ const parsed = tryParseJson(text);
37
+ const err = parsed && typeof parsed === "object" ? (parsed as any) : undefined;
38
+ const requestId = res.headers.get("x-request-id") ?? undefined;
39
+ const message = err?.message ?? opts?.fallbackErrorMessage ?? `Stream request failed (status=${res.status})`;
40
+ const code = err?.code ? String(err.code) : SandboxError.UNEXPECTED_RESPONSE;
41
+ throw new SandboxApiException({
42
+ message,
43
+ statusCode: res.status,
44
+ requestId,
45
+ error: new SandboxError(code, err?.message ? String(err.message) : message),
46
+ rawBody: parsed ?? text,
47
+ });
48
+ }
49
+
50
+ if (!res.body) {
51
+ return;
52
+ }
53
+
54
+ const reader = res.body.getReader();
55
+ const decoder = new TextDecoder("utf-8");
56
+ let buf = "";
57
+
58
+ while (true) {
59
+ const { value, done } = await reader.read();
60
+ if (done) break;
61
+
62
+ buf += decoder.decode(value, { stream: true });
63
+ let idx: number;
64
+
65
+ while ((idx = buf.indexOf("\n")) >= 0) {
66
+ const rawLine = buf.slice(0, idx);
67
+ buf = buf.slice(idx + 1);
68
+
69
+ const line = rawLine.trim();
70
+ if (!line) continue;
71
+
72
+ // Support standard SSE "data:" prefix
73
+ if (line.startsWith(":")) continue;
74
+ if (line.startsWith("event:") || line.startsWith("id:") || line.startsWith("retry:")) continue;
75
+
76
+ const jsonLine = line.startsWith("data:") ? line.slice("data:".length).trim() : line;
77
+ if (!jsonLine) continue;
78
+
79
+ const parsed = tryParseJson(jsonLine);
80
+ if (!parsed) continue;
81
+ yield parsed as T;
82
+ }
83
+ }
84
+
85
+ // Flush any buffered UTF-8 bytes from the decoder.
86
+ buf += decoder.decode();
87
+
88
+ // flush last line if exists
89
+ const last = buf.trim();
90
+ if (last) {
91
+ const jsonLine = last.startsWith("data:") ? last.slice("data:".length).trim() : last;
92
+ const parsed = tryParseJson(jsonLine);
93
+ if (parsed) yield parsed as T;
94
+ }
95
+ }