@claxedo/sandbox-manager 0.7.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 +10 -14
- package/dist/driver-catalog.d.ts +3 -53
- package/dist/driver-catalog.mjs +137 -128
- package/dist/drivers/box.mjs +143 -68
- package/dist/drivers/cloudflare.d.ts +4 -7
- package/dist/drivers/cloudflare.mjs +198 -108
- package/dist/drivers/daytona.d.ts +49 -13
- package/dist/drivers/daytona.mjs +225 -83
- package/dist/drivers/docker.mjs +118 -67
- package/dist/drivers/exe.mjs +74 -51
- package/dist/drivers/fetch-bridge.mjs +19 -4
- package/dist/drivers/modal.d.ts +2 -5
- package/dist/drivers/modal.mjs +130 -75
- package/dist/drivers/transient-error.d.ts +11 -0
- package/dist/drivers/vercel.d.ts +19 -22
- package/dist/drivers/vercel.mjs +178 -104
- package/dist/hosted-network-policy.d.ts +2 -2
- package/dist/hosted-network-policy.mjs +3 -3
- package/dist/image-name.mjs +34 -0
- package/dist/image.d.ts +2 -2
- package/dist/image.mjs +33 -11
- package/dist/index.d.ts +62 -17
- package/dist/index.mjs +68 -22
- package/dist/json.d.ts +13 -0
- package/dist/lease-policy.d.ts +6 -2
- package/dist/lease-policy.mjs +50 -40
- package/dist/lease-types.d.ts +11 -2
- package/dist/runtime-env.d.ts +12 -1
- package/dist/runtime-env.mjs +14 -0
- package/dist/service-url-exposure.d.ts +1 -2
- package/dist/service-url-exposure.mjs +1 -17
- package/dist/stores/memory.mjs +2 -2
- package/package.json +57 -55
- package/dist/drivers/cloudflare-egress.d.ts +0 -40
- package/dist/drivers/cloudflare-egress.mjs +0 -113
package/dist/drivers/box.mjs
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
|
+
// ../claxedo-helpers/dist/string.mjs
|
|
2
|
+
function isString(value) {
|
|
3
|
+
return typeof value === "string";
|
|
4
|
+
}
|
|
5
|
+
function trimToUndefined(value) {
|
|
6
|
+
if (!isString(value))
|
|
7
|
+
return;
|
|
8
|
+
const trimmed = value.trim();
|
|
9
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
// src/runtime-env.ts
|
|
13
|
+
function workspaceRuntimeBootEnv(input) {
|
|
14
|
+
const env = {
|
|
15
|
+
...workspaceRuntimeTargetEnv(input),
|
|
16
|
+
...workspaceRuntimeSourceEnv(input),
|
|
17
|
+
...input.env
|
|
18
|
+
};
|
|
19
|
+
if (input.runner) env.WORKSPACE_RUNTIME_RUNNER = input.runner;
|
|
20
|
+
if (input.controlEnv?.relayJwksUrl) env.WORKSPACE_RUNTIME_RELAY_JWKS_URL = input.controlEnv.relayJwksUrl;
|
|
21
|
+
if (input.controlEnv?.relayVerifyPem) env.WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM = input.controlEnv.relayVerifyPem;
|
|
22
|
+
if (input.controlEnv?.managementJwksUrl) env.WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL = input.controlEnv.managementJwksUrl;
|
|
23
|
+
if (input.controlEnv?.sessionAuthorityUrl) env.WORKSPACE_RUNTIME_SESSION_AUTHORITY_URL = input.controlEnv.sessionAuthorityUrl;
|
|
24
|
+
return env;
|
|
25
|
+
}
|
|
2
26
|
function workspaceRuntimeTargetEnv(input) {
|
|
3
27
|
return {
|
|
4
28
|
WORKSPACE_RUNTIME_WORKSPACE_ID: input.workspaceId,
|
|
@@ -25,9 +49,6 @@ function shell(input) {
|
|
|
25
49
|
// src/constants.ts
|
|
26
50
|
var DEFAULT_WORKSPACE_RUNTIME_PORT = 2593;
|
|
27
51
|
|
|
28
|
-
// src/image.ts
|
|
29
|
-
import { Image, DaytonaNotFoundError } from "@daytona/sdk";
|
|
30
|
-
|
|
31
52
|
// src/runtime-version.ts
|
|
32
53
|
var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
|
|
33
54
|
function workspaceRuntimeVersion() {
|
|
@@ -55,6 +76,22 @@ function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env =
|
|
|
55
76
|
return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
|
|
56
77
|
}
|
|
57
78
|
|
|
79
|
+
// ../claxedo-helpers/dist/guards.mjs
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function asRecord(value) {
|
|
84
|
+
return isRecord(value) ? value : void 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/json.ts
|
|
88
|
+
function text(input) {
|
|
89
|
+
return typeof input === "string" && input.length > 0 ? input : void 0;
|
|
90
|
+
}
|
|
91
|
+
function num(input) {
|
|
92
|
+
return typeof input === "number" ? input : void 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
58
95
|
// src/image.ts
|
|
59
96
|
var SANDBOX_IMAGE_REPOSITORY = sandboxImageRepository();
|
|
60
97
|
var SANDBOX_IMAGE = process.env.CLAXEDO_SANDBOX_IMAGE || defaultSandboxImage();
|
|
@@ -86,12 +123,42 @@ var SANDBOX_BASELINE_EGRESS_HOSTS = [
|
|
|
86
123
|
...SANDBOX_PACKAGE_REGISTRY_HOSTS
|
|
87
124
|
];
|
|
88
125
|
|
|
126
|
+
// ../sandbox-contract/dist/index.mjs
|
|
127
|
+
var sandboxDriverCredentialFields = {
|
|
128
|
+
exe: [{ key: "api_token", label: "API Token", secret: true }],
|
|
129
|
+
daytona: [{ key: "api_key", label: "API Key", secret: true }],
|
|
130
|
+
modal: [
|
|
131
|
+
{ key: "token_id", label: "Token ID", secret: true },
|
|
132
|
+
{ key: "token_secret", label: "Token Secret", secret: true }
|
|
133
|
+
],
|
|
134
|
+
vercel: [
|
|
135
|
+
{ key: "access_token", label: "Access Token", secret: true },
|
|
136
|
+
{ key: "team_id", label: "Team ID" },
|
|
137
|
+
{ key: "project_id", label: "Project ID" }
|
|
138
|
+
],
|
|
139
|
+
cloudflare: [
|
|
140
|
+
{ key: "api_token", label: "API Token", secret: true },
|
|
141
|
+
{ key: "worker_url", label: "Worker URL" }
|
|
142
|
+
],
|
|
143
|
+
box: [{ key: "api_key", label: "API Key", secret: true }],
|
|
144
|
+
docker: [{ key: "image", label: "Image" }]
|
|
145
|
+
};
|
|
146
|
+
var sandboxDriverLabels = {
|
|
147
|
+
exe: "exe.dev",
|
|
148
|
+
daytona: "Daytona",
|
|
149
|
+
modal: "Modal",
|
|
150
|
+
vercel: "Vercel",
|
|
151
|
+
cloudflare: "Cloudflare",
|
|
152
|
+
box: "Box",
|
|
153
|
+
docker: "Docker"
|
|
154
|
+
};
|
|
155
|
+
|
|
89
156
|
// src/driver-catalog.ts
|
|
90
157
|
var sandboxDriverCatalog = {
|
|
91
158
|
exe: {
|
|
92
159
|
id: "exe",
|
|
93
|
-
label:
|
|
94
|
-
credentialFields:
|
|
160
|
+
label: sandboxDriverLabels.exe,
|
|
161
|
+
credentialFields: sandboxDriverCredentialFields.exe,
|
|
95
162
|
metadata: {
|
|
96
163
|
driverRunsIn: ["worker", "node"],
|
|
97
164
|
hostStopBehavior: "not-supported",
|
|
@@ -111,17 +178,15 @@ var sandboxDriverCatalog = {
|
|
|
111
178
|
},
|
|
112
179
|
daytona: {
|
|
113
180
|
id: "daytona",
|
|
114
|
-
label:
|
|
115
|
-
credentialFields:
|
|
181
|
+
label: sandboxDriverLabels.daytona,
|
|
182
|
+
credentialFields: sandboxDriverCredentialFields.daytona,
|
|
116
183
|
metadata: {
|
|
117
184
|
driverRunsIn: ["worker", "node"],
|
|
118
185
|
hostStopBehavior: "suspends-host",
|
|
119
186
|
hostResumeBehavior: "same-host",
|
|
120
187
|
targetAccess: "relay",
|
|
121
188
|
secretBrokering: "native",
|
|
122
|
-
//
|
|
123
|
-
// by address: `domainAllowList` (names) alongside `networkAllowList`
|
|
124
|
-
// (CIDRs), with `networkBlockAll` as the deny-all floor.
|
|
189
|
+
// The driver translates names to domainAllowList, or addresses to networkAllowList.
|
|
125
190
|
egressControl: "hosts-and-cidrs",
|
|
126
191
|
persistence: {
|
|
127
192
|
resume: "same-sandbox",
|
|
@@ -135,20 +200,15 @@ var sandboxDriverCatalog = {
|
|
|
135
200
|
},
|
|
136
201
|
modal: {
|
|
137
202
|
id: "modal",
|
|
138
|
-
label:
|
|
139
|
-
credentialFields:
|
|
140
|
-
{ key: "token_id", label: "Token ID", secret: true },
|
|
141
|
-
{ key: "token_secret", label: "Token Secret", secret: true }
|
|
142
|
-
],
|
|
203
|
+
label: sandboxDriverLabels.modal,
|
|
204
|
+
credentialFields: sandboxDriverCredentialFields.modal,
|
|
143
205
|
metadata: {
|
|
144
206
|
driverRunsIn: ["node"],
|
|
145
207
|
hostStopBehavior: "terminates-host",
|
|
146
208
|
hostResumeBehavior: "replacement-host",
|
|
147
209
|
targetAccess: "relay",
|
|
148
210
|
secretBrokering: "none",
|
|
149
|
-
//
|
|
150
|
-
// an allowlist, and its driver throws on a host policy. A blackout is
|
|
151
|
-
// not containment for a workspace that has to clone and reach a model.
|
|
211
|
+
// The driver does not install Modal's domain allowlist or alpha sidecar.
|
|
152
212
|
egressControl: "none",
|
|
153
213
|
persistence: {
|
|
154
214
|
resume: "replacement-restore",
|
|
@@ -162,20 +222,15 @@ var sandboxDriverCatalog = {
|
|
|
162
222
|
},
|
|
163
223
|
vercel: {
|
|
164
224
|
id: "vercel",
|
|
165
|
-
label:
|
|
166
|
-
credentialFields:
|
|
167
|
-
{ key: "access_token", label: "Access Token", secret: true },
|
|
168
|
-
{ key: "team_id", label: "Team ID" },
|
|
169
|
-
{ key: "project_id", label: "Project ID" }
|
|
170
|
-
],
|
|
225
|
+
label: sandboxDriverLabels.vercel,
|
|
226
|
+
credentialFields: sandboxDriverCredentialFields.vercel,
|
|
171
227
|
metadata: {
|
|
172
228
|
driverRunsIn: ["node"],
|
|
173
229
|
hostStopBehavior: "terminates-host",
|
|
174
230
|
hostResumeBehavior: "replacement-host",
|
|
175
231
|
targetAccess: "relay",
|
|
176
232
|
secretBrokering: "native",
|
|
177
|
-
//
|
|
178
|
-
// never CIDRs.
|
|
233
|
+
// The driver translates hosts only; the SDK's subnet rules are not wired to net.cidrs.
|
|
179
234
|
egressControl: "hosts",
|
|
180
235
|
persistence: {
|
|
181
236
|
resume: "replacement-restore",
|
|
@@ -189,23 +244,15 @@ var sandboxDriverCatalog = {
|
|
|
189
244
|
},
|
|
190
245
|
cloudflare: {
|
|
191
246
|
id: "cloudflare",
|
|
192
|
-
label:
|
|
193
|
-
credentialFields:
|
|
194
|
-
{ key: "api_token", label: "API Token", secret: true },
|
|
195
|
-
{ key: "worker_url", label: "Worker URL" }
|
|
196
|
-
],
|
|
247
|
+
label: sandboxDriverLabels.cloudflare,
|
|
248
|
+
credentialFields: sandboxDriverCredentialFields.cloudflare,
|
|
197
249
|
metadata: {
|
|
198
250
|
driverRunsIn: ["worker"],
|
|
199
251
|
hostStopBehavior: "not-supported",
|
|
200
252
|
hostResumeBehavior: "same-host",
|
|
201
253
|
targetAccess: "relay",
|
|
202
|
-
secretBrokering: "
|
|
203
|
-
//
|
|
204
|
-
// (drivers/cloudflare-egress.ts) is an OPT-IN proxy the sandbox chooses
|
|
205
|
-
// to route brokered-credential requests through, not a boundary. The
|
|
206
|
-
// driver therefore drops `net` entirely — which is exactly why this
|
|
207
|
-
// must be declared, so the manager refuses instead of provisioning a
|
|
208
|
-
// sandbox the caller believes is contained.
|
|
254
|
+
secretBrokering: "native",
|
|
255
|
+
// Credential handlers do not restrict unrelated destinations.
|
|
209
256
|
egressControl: "none",
|
|
210
257
|
persistence: {
|
|
211
258
|
resume: "replacement-restore",
|
|
@@ -219,8 +266,8 @@ var sandboxDriverCatalog = {
|
|
|
219
266
|
},
|
|
220
267
|
box: {
|
|
221
268
|
id: "box",
|
|
222
|
-
label:
|
|
223
|
-
credentialFields:
|
|
269
|
+
label: sandboxDriverLabels.box,
|
|
270
|
+
credentialFields: sandboxDriverCredentialFields.box,
|
|
224
271
|
metadata: {
|
|
225
272
|
driverRunsIn: ["node"],
|
|
226
273
|
hostStopBehavior: "suspends-host",
|
|
@@ -240,8 +287,8 @@ var sandboxDriverCatalog = {
|
|
|
240
287
|
},
|
|
241
288
|
docker: {
|
|
242
289
|
id: "docker",
|
|
243
|
-
label:
|
|
244
|
-
credentialFields:
|
|
290
|
+
label: sandboxDriverLabels.docker,
|
|
291
|
+
credentialFields: sandboxDriverCredentialFields.docker,
|
|
245
292
|
metadata: {
|
|
246
293
|
driverRunsIn: ["local"],
|
|
247
294
|
hostStopBehavior: "terminates-host",
|
|
@@ -261,6 +308,24 @@ var sandboxDriverCatalog = {
|
|
|
261
308
|
}
|
|
262
309
|
};
|
|
263
310
|
|
|
311
|
+
// src/drivers/transient-error.ts
|
|
312
|
+
function textOf(input) {
|
|
313
|
+
return typeof input === "string" ? input : "";
|
|
314
|
+
}
|
|
315
|
+
function driverErrorSignals(err) {
|
|
316
|
+
const shaped = isRecord(err) ? err : {};
|
|
317
|
+
const nested = isRecord(shaped.response) ? shaped.response : {};
|
|
318
|
+
return {
|
|
319
|
+
status: num(nested.status) ?? num(shaped.status),
|
|
320
|
+
text: `${textOf(shaped.code)} ${textOf(shaped.name)} ${textOf(shaped.message)}`.toLowerCase()
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function isTransientDriverError(err, markers) {
|
|
324
|
+
const { status, text: text2 } = driverErrorSignals(err);
|
|
325
|
+
if (status !== void 0 && status >= 500) return true;
|
|
326
|
+
return markers.some((marker) => text2.includes(marker));
|
|
327
|
+
}
|
|
328
|
+
|
|
264
329
|
// src/drivers/box.ts
|
|
265
330
|
var DEFAULT_BASE_URL = "https://ascii.dev/api/box/v1";
|
|
266
331
|
var DEFAULT_RUNTIME_COMMAND = "/usr/local/bin/workspace-runtime";
|
|
@@ -280,11 +345,9 @@ function clean(input) {
|
|
|
280
345
|
const txt = input?.trim();
|
|
281
346
|
return txt ? txt : void 0;
|
|
282
347
|
}
|
|
348
|
+
var TRANSIENT_MARKERS = ["timeout", "unavailable", "econnreset", "network"];
|
|
283
349
|
function transientDriverError(err) {
|
|
284
|
-
|
|
285
|
-
if (typeof shaped.status === "number" && shaped.status >= 500) return true;
|
|
286
|
-
const text = `${shaped.code ?? ""} ${shaped.name ?? ""} ${shaped.message ?? ""}`.toLowerCase();
|
|
287
|
-
return text.includes("timeout") || text.includes("unavailable") || text.includes("econnreset") || text.includes("network");
|
|
350
|
+
return isTransientDriverError(err, TRANSIENT_MARKERS);
|
|
288
351
|
}
|
|
289
352
|
var BoxApiError = class extends Error {
|
|
290
353
|
status;
|
|
@@ -322,14 +385,29 @@ function createBoxSandboxDriver(options) {
|
|
|
322
385
|
} catch (err) {
|
|
323
386
|
throw new BoxApiError(err instanceof Error ? err.message : String(err));
|
|
324
387
|
}
|
|
325
|
-
const
|
|
326
|
-
const body =
|
|
388
|
+
const payload = await res.text();
|
|
389
|
+
const body = (payload ? asRecord(JSON.parse(payload)) : void 0) ?? {};
|
|
327
390
|
if (!res.ok || body.ok === false) {
|
|
328
|
-
const detail = clean(body.error) ?? clean(body.message) ?? res.statusText;
|
|
391
|
+
const detail = clean(text(body.error)) ?? clean(text(body.message)) ?? res.statusText;
|
|
329
392
|
throw new BoxApiError(`Box API ${init?.method ?? "GET"} ${path} failed: ${detail}`, res.status);
|
|
330
393
|
}
|
|
331
394
|
return body;
|
|
332
395
|
}
|
|
396
|
+
function boxOf(body) {
|
|
397
|
+
const box = asRecord(body.box);
|
|
398
|
+
const id = text(box?.id);
|
|
399
|
+
const state = text(box?.state);
|
|
400
|
+
if (!box || !id || !state) throw new BoxApiError("Box API response omitted its box envelope");
|
|
401
|
+
return {
|
|
402
|
+
id,
|
|
403
|
+
state,
|
|
404
|
+
...text(box.name) ? { name: text(box.name) } : {},
|
|
405
|
+
...box.url === void 0 ? {} : { url: text(box.url) ?? null },
|
|
406
|
+
...box.ip === void 0 ? {} : { ip: text(box.ip) ?? null },
|
|
407
|
+
...box.subdomain === void 0 ? {} : { subdomain: text(box.subdomain) ?? null },
|
|
408
|
+
...box.createdAt === void 0 ? {} : { createdAt: text(box.createdAt) ?? null }
|
|
409
|
+
};
|
|
410
|
+
}
|
|
333
411
|
function runtimePort(input) {
|
|
334
412
|
return input.workspaceRuntimePort ?? options.runtimePort ?? DEFAULT_WORKSPACE_RUNTIME_PORT;
|
|
335
413
|
}
|
|
@@ -341,26 +419,23 @@ function createBoxSandboxDriver(options) {
|
|
|
341
419
|
return input.snapshot ?? image;
|
|
342
420
|
}
|
|
343
421
|
function bootEnv(input, hostId) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
};
|
|
355
|
-
if (options.runner) env.WORKSPACE_RUNTIME_RUNNER = options.runner;
|
|
356
|
-
return env;
|
|
422
|
+
return workspaceRuntimeBootEnv({
|
|
423
|
+
workspaceId: input.workspaceId,
|
|
424
|
+
hostId,
|
|
425
|
+
directory: workspaceDirectory(input),
|
|
426
|
+
port: runtimePort(input),
|
|
427
|
+
host: "0.0.0.0",
|
|
428
|
+
source: input.source,
|
|
429
|
+
env: input.env,
|
|
430
|
+
runner: options.runner
|
|
431
|
+
});
|
|
357
432
|
}
|
|
358
433
|
async function exec(boxId, command) {
|
|
359
434
|
const body = await api(`/boxes/${boxId}/commands`, {
|
|
360
435
|
method: "POST",
|
|
361
436
|
body: { command }
|
|
362
437
|
});
|
|
363
|
-
const result = body.result ?? body;
|
|
438
|
+
const result = asRecord(body.result) ?? body;
|
|
364
439
|
return {
|
|
365
440
|
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
366
441
|
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
@@ -370,14 +445,14 @@ function createBoxSandboxDriver(options) {
|
|
|
370
445
|
async function execOrThrow(boxId, command, label) {
|
|
371
446
|
const result = await exec(boxId, command);
|
|
372
447
|
if ((result.exitCode ?? 0) !== 0) {
|
|
373
|
-
throw new BoxApiError(`Box ${boxId} ${label} failed (exit ${result.exitCode}): ${
|
|
448
|
+
throw new BoxApiError(`Box ${boxId} ${label} failed (exit ${result.exitCode}): ${trimToUndefined(result.stderr) ?? trimToUndefined(result.stdout) ?? ""}`);
|
|
374
449
|
}
|
|
375
450
|
return result;
|
|
376
451
|
}
|
|
377
452
|
async function waitUntilReady(boxId) {
|
|
378
453
|
const until = Date.now() + provisionTimeoutMs;
|
|
379
454
|
for (; ; ) {
|
|
380
|
-
const
|
|
455
|
+
const box = boxOf(await api(`/boxes/${boxId}`));
|
|
381
456
|
if (READY_STATES.has(box.state)) return true;
|
|
382
457
|
if (!PENDING_STATES.has(box.state)) {
|
|
383
458
|
throw new BoxApiError(`Box ${boxId} entered non-ready state: ${box.state}`);
|
|
@@ -403,7 +478,7 @@ function createBoxSandboxDriver(options) {
|
|
|
403
478
|
boxId,
|
|
404
479
|
`curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:${port}/global/health || true`
|
|
405
480
|
);
|
|
406
|
-
const code =
|
|
481
|
+
const code = trimToUndefined(probe.stdout);
|
|
407
482
|
if (code === "200") return;
|
|
408
483
|
last = `health probe returned ${code ?? "no response"}`;
|
|
409
484
|
await sleep(healthIntervalMs);
|
|
@@ -420,7 +495,7 @@ function createBoxSandboxDriver(options) {
|
|
|
420
495
|
await execOrThrow(boxId, `host ${port}`, "host publish");
|
|
421
496
|
await waitForHealth(boxId, input);
|
|
422
497
|
const urlResult = await execOrThrow(boxId, `host url ${port}`, "host url");
|
|
423
|
-
const url =
|
|
498
|
+
const url = trimToUndefined(urlResult.stdout?.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).pop());
|
|
424
499
|
if (!url) throw new BoxApiError(`Box ${boxId} did not return a public URL for port ${port}`);
|
|
425
500
|
return {
|
|
426
501
|
workspaceId: input.workspaceId,
|
|
@@ -448,7 +523,7 @@ function createBoxSandboxDriver(options) {
|
|
|
448
523
|
throw err;
|
|
449
524
|
});
|
|
450
525
|
if (!created) return { provisioning: true, retryAfterMs: provisionIntervalMs };
|
|
451
|
-
const boxId = created.
|
|
526
|
+
const boxId = boxOf(created).id;
|
|
452
527
|
const ready = await waitUntilReady(boxId);
|
|
453
528
|
if (!ready) return { provisioning: true, retryAfterMs: provisionIntervalMs };
|
|
454
529
|
return boot(boxId, input, hostId);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SandboxDriver, type SandboxDriverEnsureInput, type SandboxListingUnsupported } from "..";
|
|
2
|
+
import { type WorkspaceRuntimeControlEnv } from "../runtime-env";
|
|
2
3
|
export type CloudflareSandboxDriverOptions = {
|
|
3
4
|
/** Base URL of the deployed Cloudflare Sandbox Worker (e.g. https://sbx.example.com). */
|
|
4
5
|
workerUrl: string;
|
|
@@ -18,12 +19,8 @@ export type CloudflareSandboxDriverOptions = {
|
|
|
18
19
|
* relayVerifyPem → WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM (fallback to JWKS)
|
|
19
20
|
* managementJwksUrl → WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL (config apply)
|
|
20
21
|
*/
|
|
21
|
-
controlEnv?:
|
|
22
|
-
|
|
23
|
-
relayVerifyPem?: string;
|
|
24
|
-
managementJwksUrl?: string;
|
|
25
|
-
};
|
|
26
|
-
/** Default runner injected as WORKSPACE_RUNTIME_RUNNER, e.g. "opencode" | "claude-acp" | "codex-acp". */
|
|
22
|
+
controlEnv?: WorkspaceRuntimeControlEnv;
|
|
23
|
+
/** Default runner injected as WORKSPACE_RUNTIME_RUNNER, for example "opencode". */
|
|
27
24
|
runner?: string;
|
|
28
25
|
/** Dynamic runtime env that needs the sandbox id or current lease. */
|
|
29
26
|
env?: (input: SandboxDriverEnsureInput, sandbox: {
|