@claxedo/sandbox-manager 0.6.0 → 0.9.0
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 +60 -15
- package/dist/checkpoint-manager.d.ts +47 -0
- package/dist/checkpoint-manager.mjs +116 -0
- package/dist/daytona-allow-list.d.ts +11 -0
- package/dist/daytona-allow-list.mjs +5 -1
- package/dist/driver-catalog.d.ts +5 -52
- package/dist/driver-catalog.mjs +263 -121
- package/dist/drivers/box.mjs +309 -39
- package/dist/drivers/cloudflare.d.ts +18 -7
- package/dist/drivers/cloudflare.mjs +423 -34
- package/dist/drivers/daytona.d.ts +82 -13
- package/dist/drivers/daytona.mjs +495 -45
- package/dist/drivers/docker.mjs +284 -38
- package/dist/drivers/exe.d.ts +26 -0
- package/dist/drivers/exe.mjs +492 -0
- package/dist/drivers/fetch-bridge.mjs +29 -5
- package/dist/drivers/modal.d.ts +2 -5
- package/dist/drivers/modal.mjs +296 -46
- package/dist/drivers/transient-error.d.ts +11 -0
- package/dist/drivers/vercel.d.ts +27 -11
- package/dist/drivers/vercel.mjs +339 -59
- package/dist/hosted-network-policy.d.ts +87 -0
- package/dist/hosted-network-policy.mjs +61 -0
- package/dist/image-name.mjs +34 -0
- package/dist/image.d.ts +18 -3
- package/dist/image.mjs +47 -32
- package/dist/index.d.ts +281 -17
- package/dist/index.mjs +393 -26
- package/dist/json.d.ts +13 -0
- package/dist/lease-policy.d.ts +6 -3
- package/dist/lease-policy.mjs +213 -54
- package/dist/lease-types.d.ts +17 -3
- package/dist/runtime-env.d.ts +12 -1
- package/dist/runtime-env.mjs +14 -0
- package/dist/runtime-version.mjs +1 -9
- package/dist/service-url-exposure.d.ts +1 -2
- package/dist/service-url-exposure.mjs +1 -17
- package/dist/stores/memory.mjs +39 -4
- package/package.json +67 -51
- package/dist/drivers/cloudflare-egress.d.ts +0 -40
- package/dist/drivers/cloudflare-egress.mjs +0 -113
package/dist/drivers/modal.mjs
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
// src/drivers/modal.ts
|
|
2
|
-
import { ModalClient, Probe } from "modal";
|
|
3
|
-
|
|
4
1
|
// src/runtime-env.ts
|
|
2
|
+
function workspaceRuntimeBootEnv(input) {
|
|
3
|
+
const env = {
|
|
4
|
+
...workspaceRuntimeTargetEnv(input),
|
|
5
|
+
...workspaceRuntimeSourceEnv(input),
|
|
6
|
+
...input.env
|
|
7
|
+
};
|
|
8
|
+
if (input.runner) env.WORKSPACE_RUNTIME_RUNNER = input.runner;
|
|
9
|
+
if (input.controlEnv?.relayJwksUrl) env.WORKSPACE_RUNTIME_RELAY_JWKS_URL = input.controlEnv.relayJwksUrl;
|
|
10
|
+
if (input.controlEnv?.relayVerifyPem) env.WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM = input.controlEnv.relayVerifyPem;
|
|
11
|
+
if (input.controlEnv?.managementJwksUrl) env.WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL = input.controlEnv.managementJwksUrl;
|
|
12
|
+
if (input.controlEnv?.sessionAuthorityUrl) env.WORKSPACE_RUNTIME_SESSION_AUTHORITY_URL = input.controlEnv.sessionAuthorityUrl;
|
|
13
|
+
return env;
|
|
14
|
+
}
|
|
5
15
|
function workspaceRuntimeTargetEnv(input) {
|
|
6
16
|
return {
|
|
7
17
|
WORKSPACE_RUNTIME_WORKSPACE_ID: input.workspaceId,
|
|
@@ -28,21 +38,10 @@ function shell(input) {
|
|
|
28
38
|
// src/constants.ts
|
|
29
39
|
var DEFAULT_WORKSPACE_RUNTIME_PORT = 2593;
|
|
30
40
|
|
|
31
|
-
// src/image.ts
|
|
32
|
-
import { Image, DaytonaNotFoundError } from "@daytona/sdk";
|
|
33
|
-
|
|
34
41
|
// src/runtime-version.ts
|
|
35
42
|
var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
|
|
36
|
-
var versionCache;
|
|
37
43
|
function workspaceRuntimeVersion() {
|
|
38
|
-
|
|
39
|
-
const envVersion = process.env.WORKSPACE_RUNTIME_VERSION;
|
|
40
|
-
if (envVersion) {
|
|
41
|
-
versionCache = envVersion;
|
|
42
|
-
return versionCache;
|
|
43
|
-
}
|
|
44
|
-
versionCache = DEFAULT_WORKSPACE_RUNTIME_VERSION;
|
|
45
|
-
return versionCache;
|
|
44
|
+
return process.env.WORKSPACE_RUNTIME_VERSION || DEFAULT_WORKSPACE_RUNTIME_VERSION;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
// src/image-name.ts
|
|
@@ -66,11 +65,253 @@ function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env =
|
|
|
66
65
|
return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
// ../claxedo-helpers/dist/guards.mjs
|
|
69
|
+
function isRecord(value) {
|
|
70
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
71
|
+
}
|
|
72
|
+
function asRecord(value) {
|
|
73
|
+
return isRecord(value) ? value : void 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/json.ts
|
|
77
|
+
function num(input) {
|
|
78
|
+
return typeof input === "number" ? input : void 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
69
81
|
// src/image.ts
|
|
70
82
|
var SANDBOX_IMAGE_REPOSITORY = sandboxImageRepository();
|
|
71
83
|
var SANDBOX_IMAGE = process.env.CLAXEDO_SANDBOX_IMAGE || defaultSandboxImage();
|
|
72
84
|
var SNAPSHOT_NAME = process.env.CLAXEDO_SNAPSHOT_NAME || defaultSnapshotName();
|
|
73
85
|
|
|
86
|
+
// src/hosted-network-policy.ts
|
|
87
|
+
var SANDBOX_MODEL_PROVIDER_HOSTS = [
|
|
88
|
+
"api.anthropic.com",
|
|
89
|
+
"api.openai.com",
|
|
90
|
+
"generativelanguage.googleapis.com",
|
|
91
|
+
"openrouter.ai",
|
|
92
|
+
// Model catalog + engine config, fetched on runner startup.
|
|
93
|
+
"models.dev",
|
|
94
|
+
"opencode.ai"
|
|
95
|
+
];
|
|
96
|
+
var SANDBOX_PACKAGE_REGISTRY_HOSTS = [
|
|
97
|
+
"registry.npmjs.org",
|
|
98
|
+
"nodejs.org",
|
|
99
|
+
"pypi.org",
|
|
100
|
+
"files.pythonhosted.org",
|
|
101
|
+
"crates.io",
|
|
102
|
+
"static.crates.io",
|
|
103
|
+
"index.crates.io",
|
|
104
|
+
"proxy.golang.org",
|
|
105
|
+
"sum.golang.org"
|
|
106
|
+
];
|
|
107
|
+
var SANDBOX_BASELINE_EGRESS_HOSTS = [
|
|
108
|
+
...SANDBOX_MODEL_PROVIDER_HOSTS,
|
|
109
|
+
...SANDBOX_PACKAGE_REGISTRY_HOSTS
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
// ../sandbox-contract/dist/index.mjs
|
|
113
|
+
var sandboxDriverCredentialFields = {
|
|
114
|
+
exe: [{ key: "api_token", label: "API Token", secret: true }],
|
|
115
|
+
daytona: [{ key: "api_key", label: "API Key", secret: true }],
|
|
116
|
+
modal: [
|
|
117
|
+
{ key: "token_id", label: "Token ID", secret: true },
|
|
118
|
+
{ key: "token_secret", label: "Token Secret", secret: true }
|
|
119
|
+
],
|
|
120
|
+
vercel: [
|
|
121
|
+
{ key: "access_token", label: "Access Token", secret: true },
|
|
122
|
+
{ key: "team_id", label: "Team ID" },
|
|
123
|
+
{ key: "project_id", label: "Project ID" }
|
|
124
|
+
],
|
|
125
|
+
cloudflare: [
|
|
126
|
+
{ key: "api_token", label: "API Token", secret: true },
|
|
127
|
+
{ key: "worker_url", label: "Worker URL" }
|
|
128
|
+
],
|
|
129
|
+
box: [{ key: "api_key", label: "API Key", secret: true }],
|
|
130
|
+
docker: [{ key: "image", label: "Image" }]
|
|
131
|
+
};
|
|
132
|
+
var sandboxDriverLabels = {
|
|
133
|
+
exe: "exe.dev",
|
|
134
|
+
daytona: "Daytona",
|
|
135
|
+
modal: "Modal",
|
|
136
|
+
vercel: "Vercel",
|
|
137
|
+
cloudflare: "Cloudflare",
|
|
138
|
+
box: "Box",
|
|
139
|
+
docker: "Docker"
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/driver-catalog.ts
|
|
143
|
+
var sandboxDriverCatalog = {
|
|
144
|
+
exe: {
|
|
145
|
+
id: "exe",
|
|
146
|
+
label: sandboxDriverLabels.exe,
|
|
147
|
+
credentialFields: sandboxDriverCredentialFields.exe,
|
|
148
|
+
metadata: {
|
|
149
|
+
driverRunsIn: ["worker", "node"],
|
|
150
|
+
hostStopBehavior: "not-supported",
|
|
151
|
+
hostResumeBehavior: "same-host",
|
|
152
|
+
targetAccess: "relay",
|
|
153
|
+
secretBrokering: "none",
|
|
154
|
+
egressControl: "none",
|
|
155
|
+
persistence: {
|
|
156
|
+
resume: "same-sandbox",
|
|
157
|
+
capture: "none",
|
|
158
|
+
clone: true,
|
|
159
|
+
captureSource: "not-applicable",
|
|
160
|
+
retention: "provider-managed",
|
|
161
|
+
restoreMount: "same-resource"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
daytona: {
|
|
166
|
+
id: "daytona",
|
|
167
|
+
label: sandboxDriverLabels.daytona,
|
|
168
|
+
credentialFields: sandboxDriverCredentialFields.daytona,
|
|
169
|
+
metadata: {
|
|
170
|
+
driverRunsIn: ["worker", "node"],
|
|
171
|
+
hostStopBehavior: "suspends-host",
|
|
172
|
+
hostResumeBehavior: "same-host",
|
|
173
|
+
targetAccess: "relay",
|
|
174
|
+
secretBrokering: "native",
|
|
175
|
+
// The driver translates names to domainAllowList, or addresses to networkAllowList.
|
|
176
|
+
egressControl: "hosts-and-cidrs",
|
|
177
|
+
persistence: {
|
|
178
|
+
resume: "same-sandbox",
|
|
179
|
+
capture: "filesystem",
|
|
180
|
+
clone: false,
|
|
181
|
+
captureSource: "preserved",
|
|
182
|
+
retention: "provider-managed",
|
|
183
|
+
restoreMount: "new-resource"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
modal: {
|
|
188
|
+
id: "modal",
|
|
189
|
+
label: sandboxDriverLabels.modal,
|
|
190
|
+
credentialFields: sandboxDriverCredentialFields.modal,
|
|
191
|
+
metadata: {
|
|
192
|
+
driverRunsIn: ["node"],
|
|
193
|
+
hostStopBehavior: "terminates-host",
|
|
194
|
+
hostResumeBehavior: "replacement-host",
|
|
195
|
+
targetAccess: "relay",
|
|
196
|
+
secretBrokering: "none",
|
|
197
|
+
// The driver does not install Modal's domain allowlist or alpha sidecar.
|
|
198
|
+
egressControl: "none",
|
|
199
|
+
persistence: {
|
|
200
|
+
resume: "replacement-restore",
|
|
201
|
+
capture: "filesystem",
|
|
202
|
+
clone: false,
|
|
203
|
+
captureSource: "preserved",
|
|
204
|
+
retention: "provider-managed",
|
|
205
|
+
restoreMount: "new-resource"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
vercel: {
|
|
210
|
+
id: "vercel",
|
|
211
|
+
label: sandboxDriverLabels.vercel,
|
|
212
|
+
credentialFields: sandboxDriverCredentialFields.vercel,
|
|
213
|
+
metadata: {
|
|
214
|
+
driverRunsIn: ["node"],
|
|
215
|
+
hostStopBehavior: "terminates-host",
|
|
216
|
+
hostResumeBehavior: "replacement-host",
|
|
217
|
+
targetAccess: "relay",
|
|
218
|
+
secretBrokering: "native",
|
|
219
|
+
// The driver translates hosts only; the SDK's subnet rules are not wired to net.cidrs.
|
|
220
|
+
egressControl: "hosts",
|
|
221
|
+
persistence: {
|
|
222
|
+
resume: "replacement-restore",
|
|
223
|
+
capture: "filesystem",
|
|
224
|
+
clone: false,
|
|
225
|
+
captureSource: "stopped",
|
|
226
|
+
retention: "explicit",
|
|
227
|
+
restoreMount: "new-resource"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
cloudflare: {
|
|
232
|
+
id: "cloudflare",
|
|
233
|
+
label: sandboxDriverLabels.cloudflare,
|
|
234
|
+
credentialFields: sandboxDriverCredentialFields.cloudflare,
|
|
235
|
+
metadata: {
|
|
236
|
+
driverRunsIn: ["worker"],
|
|
237
|
+
hostStopBehavior: "not-supported",
|
|
238
|
+
hostResumeBehavior: "same-host",
|
|
239
|
+
targetAccess: "relay",
|
|
240
|
+
secretBrokering: "native",
|
|
241
|
+
// Credential handlers do not restrict unrelated destinations.
|
|
242
|
+
egressControl: "none",
|
|
243
|
+
persistence: {
|
|
244
|
+
resume: "replacement-restore",
|
|
245
|
+
capture: "directories",
|
|
246
|
+
clone: false,
|
|
247
|
+
captureSource: "preserved",
|
|
248
|
+
retention: "provider-managed",
|
|
249
|
+
restoreMount: "copy-on-write"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
box: {
|
|
254
|
+
id: "box",
|
|
255
|
+
label: sandboxDriverLabels.box,
|
|
256
|
+
credentialFields: sandboxDriverCredentialFields.box,
|
|
257
|
+
metadata: {
|
|
258
|
+
driverRunsIn: ["node"],
|
|
259
|
+
hostStopBehavior: "suspends-host",
|
|
260
|
+
hostResumeBehavior: "same-host",
|
|
261
|
+
targetAccess: "relay",
|
|
262
|
+
secretBrokering: "none",
|
|
263
|
+
egressControl: "none",
|
|
264
|
+
persistence: {
|
|
265
|
+
resume: "same-sandbox",
|
|
266
|
+
capture: "none",
|
|
267
|
+
clone: false,
|
|
268
|
+
captureSource: "not-applicable",
|
|
269
|
+
retention: "provider-managed",
|
|
270
|
+
restoreMount: "same-resource"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
docker: {
|
|
275
|
+
id: "docker",
|
|
276
|
+
label: sandboxDriverLabels.docker,
|
|
277
|
+
credentialFields: sandboxDriverCredentialFields.docker,
|
|
278
|
+
metadata: {
|
|
279
|
+
driverRunsIn: ["local"],
|
|
280
|
+
hostStopBehavior: "terminates-host",
|
|
281
|
+
hostResumeBehavior: "same-host",
|
|
282
|
+
targetAccess: "loopback",
|
|
283
|
+
secretBrokering: "none",
|
|
284
|
+
egressControl: "none",
|
|
285
|
+
persistence: {
|
|
286
|
+
resume: "replacement-restore",
|
|
287
|
+
capture: "same-resource",
|
|
288
|
+
clone: false,
|
|
289
|
+
captureSource: "preserved",
|
|
290
|
+
retention: "provider-managed",
|
|
291
|
+
restoreMount: "same-resource"
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/drivers/transient-error.ts
|
|
298
|
+
function textOf(input) {
|
|
299
|
+
return typeof input === "string" ? input : "";
|
|
300
|
+
}
|
|
301
|
+
function driverErrorSignals(err) {
|
|
302
|
+
const shaped = isRecord(err) ? err : {};
|
|
303
|
+
const nested = isRecord(shaped.response) ? shaped.response : {};
|
|
304
|
+
return {
|
|
305
|
+
status: num(nested.status) ?? num(shaped.status),
|
|
306
|
+
text: `${textOf(shaped.code)} ${textOf(shaped.name)} ${textOf(shaped.message)}`.toLowerCase()
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function isTransientDriverError(err, markers) {
|
|
310
|
+
const { status, text: text2 } = driverErrorSignals(err);
|
|
311
|
+
if (status !== void 0 && status >= 500) return true;
|
|
312
|
+
return markers.some((marker) => text2.includes(marker));
|
|
313
|
+
}
|
|
314
|
+
|
|
74
315
|
// src/drivers/modal.ts
|
|
75
316
|
var DEFAULT_APP_NAME = "claxedo";
|
|
76
317
|
var DEFAULT_RUNTIME_COMMAND = "/usr/local/bin/workspace-runtime";
|
|
@@ -78,21 +319,28 @@ var DEFAULT_WORKSPACE_DIR = "/workspace";
|
|
|
78
319
|
var DEFAULT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
79
320
|
var DEFAULT_TUNNEL_TIMEOUT_MS = 3e4;
|
|
80
321
|
var DEFAULT_OPERATION_TIMEOUT_MS = 6e4;
|
|
322
|
+
function isModalClient(value) {
|
|
323
|
+
const row = asRecord(value);
|
|
324
|
+
return typeof asRecord(row?.apps)?.fromName === "function" && typeof asRecord(row?.images)?.fromRegistry === "function" && typeof asRecord(row?.sandboxes)?.create === "function";
|
|
325
|
+
}
|
|
81
326
|
function nameFor(workspaceId) {
|
|
82
327
|
return `claxedo-${workspaceId}`;
|
|
83
328
|
}
|
|
329
|
+
var TRANSIENT_MARKERS = ["timeout", "unavailable", "deadline"];
|
|
84
330
|
function transientDriverError(err) {
|
|
85
|
-
|
|
86
|
-
const status = shaped.response?.status ?? shaped.status;
|
|
87
|
-
if (typeof status === "number" && status >= 500) return true;
|
|
88
|
-
const text = `${shaped.code ?? ""} ${shaped.name ?? ""} ${shaped.message ?? ""}`.toLowerCase();
|
|
89
|
-
return text.includes("timeout") || text.includes("unavailable") || text.includes("deadline");
|
|
331
|
+
return isTransientDriverError(err, TRANSIENT_MARKERS);
|
|
90
332
|
}
|
|
91
333
|
function createModalSandboxDriver(options) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
334
|
+
let defaultClient;
|
|
335
|
+
function resolveClient() {
|
|
336
|
+
if (options.client) return options.client;
|
|
337
|
+
defaultClient ??= import("modal").then((sdk) => {
|
|
338
|
+
const client = new sdk.ModalClient({ tokenId: options.tokenId, tokenSecret: options.tokenSecret });
|
|
339
|
+
if (!isModalClient(client)) throw new Error("modal SDK does not expose the client surface this driver uses");
|
|
340
|
+
return client;
|
|
341
|
+
});
|
|
342
|
+
return defaultClient;
|
|
343
|
+
}
|
|
96
344
|
const appName = options.appName ?? DEFAULT_APP_NAME;
|
|
97
345
|
const runtimePort = options.runtimePort ?? DEFAULT_WORKSPACE_RUNTIME_PORT;
|
|
98
346
|
const runtimeCommand = options.runtimeCommand ?? DEFAULT_RUNTIME_COMMAND;
|
|
@@ -100,29 +348,28 @@ function createModalSandboxDriver(options) {
|
|
|
100
348
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
101
349
|
const tunnelTimeoutMs = options.tunnelTimeoutMs ?? DEFAULT_TUNNEL_TIMEOUT_MS;
|
|
102
350
|
const operationTimeoutMs = options.operationTimeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS;
|
|
103
|
-
|
|
351
|
+
async function readinessProbeFor(port) {
|
|
352
|
+
if (options.readinessProbe) return options.readinessProbe(port);
|
|
353
|
+
const { Probe } = await import("modal");
|
|
354
|
+
return Probe.withTcp(port, { intervalMs: 1e3 });
|
|
355
|
+
}
|
|
104
356
|
function workspaceDirectory(input) {
|
|
105
357
|
return input.workspaceRoot ?? workspaceDir;
|
|
106
358
|
}
|
|
107
359
|
function bootEnv(input, hostId) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
};
|
|
119
|
-
if (options.runner) env.WORKSPACE_RUNTIME_RUNNER = options.runner;
|
|
120
|
-
if (options.controlEnv?.relayJwksUrl) env.WORKSPACE_RUNTIME_RELAY_JWKS_URL = options.controlEnv.relayJwksUrl;
|
|
121
|
-
if (options.controlEnv?.relayVerifyPem) env.WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM = options.controlEnv.relayVerifyPem;
|
|
122
|
-
if (options.controlEnv?.managementJwksUrl) env.WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL = options.controlEnv.managementJwksUrl;
|
|
123
|
-
return env;
|
|
360
|
+
return workspaceRuntimeBootEnv({
|
|
361
|
+
workspaceId: input.workspaceId,
|
|
362
|
+
hostId,
|
|
363
|
+
directory: workspaceDirectory(input),
|
|
364
|
+
port: runtimePort,
|
|
365
|
+
host: "0.0.0.0",
|
|
366
|
+
source: input.source,
|
|
367
|
+
env: input.env,
|
|
368
|
+
runner: options.runner,
|
|
369
|
+
controlEnv: options.controlEnv
|
|
370
|
+
});
|
|
124
371
|
}
|
|
125
|
-
async function image(input) {
|
|
372
|
+
async function image(client, input) {
|
|
126
373
|
if (input.bootSource?.kind === "driver-snapshot") return client.images.fromId(input.bootSource.snapshotId);
|
|
127
374
|
if (input.bootSource?.kind === "image") return client.images.fromId(input.bootSource.image);
|
|
128
375
|
if (input.snapshot) return client.images.fromId(input.snapshot);
|
|
@@ -160,9 +407,10 @@ function createModalSandboxDriver(options) {
|
|
|
160
407
|
...bootEnv(input, hostId),
|
|
161
408
|
...await options.env?.(input, { id: hostId })
|
|
162
409
|
};
|
|
410
|
+
const client = await resolveClient();
|
|
163
411
|
const sandbox = await client.sandboxes.create(
|
|
164
412
|
await client.apps.fromName(appName, { createIfMissing: true }),
|
|
165
|
-
await image(input),
|
|
413
|
+
await image(client, input),
|
|
166
414
|
{
|
|
167
415
|
name: nameFor(input.workspaceId),
|
|
168
416
|
command: ["bash", "-lc", `mkdir -p ${shell(directory)}; cd ${shell(directory)}; exec ${runtimeCommand}`],
|
|
@@ -172,7 +420,7 @@ function createModalSandboxDriver(options) {
|
|
|
172
420
|
timeoutMs,
|
|
173
421
|
...options.idleTimeoutMs !== void 0 ? { idleTimeoutMs: options.idleTimeoutMs } : {},
|
|
174
422
|
tags: { ...input.labels, "claxedo.workspaceId": input.workspaceId },
|
|
175
|
-
readinessProbe:
|
|
423
|
+
readinessProbe: await readinessProbeFor(runtimePort),
|
|
176
424
|
...network(input)
|
|
177
425
|
}
|
|
178
426
|
).catch((err) => {
|
|
@@ -183,7 +431,7 @@ function createModalSandboxDriver(options) {
|
|
|
183
431
|
return readyTarget(input, sandbox, hostId);
|
|
184
432
|
}
|
|
185
433
|
async function sandboxById(target) {
|
|
186
|
-
return
|
|
434
|
+
return (await resolveClient()).sandboxes.fromId(target.sandboxId);
|
|
187
435
|
}
|
|
188
436
|
return {
|
|
189
437
|
id: "modal",
|
|
@@ -192,7 +440,9 @@ function createModalSandboxDriver(options) {
|
|
|
192
440
|
hostStopBehavior: "terminates-host",
|
|
193
441
|
hostResumeBehavior: "replacement-host",
|
|
194
442
|
targetAccess: "relay",
|
|
195
|
-
secretBrokering: "none"
|
|
443
|
+
secretBrokering: "none",
|
|
444
|
+
egressControl: "none",
|
|
445
|
+
persistence: sandboxDriverCatalog.modal.metadata.persistence
|
|
196
446
|
},
|
|
197
447
|
ensureHost,
|
|
198
448
|
resumeHost: (input) => ensureHost(input.ensure),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The status and lower-cased text a driver error carries, if it carries any. */
|
|
2
|
+
export declare function driverErrorSignals(err: unknown): {
|
|
3
|
+
status: number | undefined;
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A 5xx is transient for every driver; anything else is a per-driver judgment
|
|
8
|
+
* made from the error text, so each driver passes the markers it has actually
|
|
9
|
+
* seen its own SDK produce.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isTransientDriverError(err: unknown, markers: readonly string[]): boolean;
|
package/dist/drivers/vercel.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
1
|
+
import type { NetworkPolicy } from "@vercel/sandbox";
|
|
2
|
+
import { type SandboxBrokeredSecret, type SandboxDriver, type SandboxDriverEnsureInput } from "..";
|
|
3
|
+
import { type WorkspaceRuntimeControlEnv } from "../runtime-env";
|
|
3
4
|
type VercelSnapshotLike = {
|
|
4
5
|
snapshotId: string;
|
|
5
6
|
};
|
|
@@ -80,11 +81,7 @@ export type VercelSandboxDriverOptions = {
|
|
|
80
81
|
runtimeCommand?: string;
|
|
81
82
|
workspaceDir?: string;
|
|
82
83
|
runner?: string;
|
|
83
|
-
controlEnv?:
|
|
84
|
-
relayJwksUrl?: string;
|
|
85
|
-
relayVerifyPem?: string;
|
|
86
|
-
managementJwksUrl?: string;
|
|
87
|
-
};
|
|
84
|
+
controlEnv?: WorkspaceRuntimeControlEnv;
|
|
88
85
|
env?: (input: SandboxDriverEnsureInput, host: {
|
|
89
86
|
id: string;
|
|
90
87
|
}) => Record<string, string> | Promise<Record<string, string>>;
|
|
@@ -101,10 +98,29 @@ export type VercelSandboxDriverOptions = {
|
|
|
101
98
|
* a header on outbound requests to the allowlisted hosts, so the raw value
|
|
102
99
|
* never enters the sandbox (see @claxedo/sandbox-manager SandboxBrokeredSecret
|
|
103
100
|
* and https://vercel.com/docs/sandbox/concepts/firewall — credentials
|
|
104
|
-
* brokering).
|
|
105
|
-
*
|
|
106
|
-
*
|
|
101
|
+
* brokering).
|
|
102
|
+
*
|
|
103
|
+
* The result is the union of `base` — the create-time allow-list — and the
|
|
104
|
+
* brokered hosts, never a replacement. Replacing it is wrong in both
|
|
105
|
+
* directions: it breaks egress the caller was granted, since
|
|
106
|
+
* `updateNetworkPolicy` overwrites the whole policy, so a sandbox created with
|
|
107
|
+
* git and the npm registry allowed loses both the instant a brokered secret is
|
|
108
|
+
* attached, and the agent's next `npm install` fails mid-run. It can also
|
|
109
|
+
* silently widen egress, since a brokered host that was not in the create-time
|
|
110
|
+
* list becomes reachable, installing a policy nobody approved as a side effect
|
|
111
|
+
* of attaching a credential. A union changes exactly one thing — it adds the
|
|
112
|
+
* brokered hosts, the smallest edit that makes the credential usable.
|
|
113
|
+
*
|
|
114
|
+
* `base` is the create-time `NetworkPolicy` exactly as passed to `create`, and
|
|
115
|
+
* it is required even though `undefined` is a legal value: `undefined` means
|
|
116
|
+
* "no policy was requested", so the merge keeps egress unrestricted, expressed
|
|
117
|
+
* as the `"*"` wildcard the record form needs for the header transforms to ride
|
|
118
|
+
* alongside it. An optional parameter defaulting to
|
|
119
|
+
* deny-all-except-brokered-hosts would let an existing caller keep compiling
|
|
120
|
+
* while its egress silently widens. Spelling the base out forces that decision
|
|
121
|
+
* to be made once, visibly. A `"deny-all"` base contributes nothing, leaving
|
|
122
|
+
* deny-all-except-brokered-hosts.
|
|
107
123
|
*/
|
|
108
|
-
export declare function vercelBrokeredNetworkPolicy(secrets: SandboxBrokeredSecret[]): NetworkPolicy;
|
|
124
|
+
export declare function vercelBrokeredNetworkPolicy(secrets: SandboxBrokeredSecret[], base: NetworkPolicy | undefined): NetworkPolicy;
|
|
109
125
|
export declare function createVercelSandboxDriver(options: VercelSandboxDriverOptions): SandboxDriver;
|
|
110
126
|
export {};
|