@claxedo/sandbox-manager 0.7.0 → 0.10.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 +245 -42
- package/dist/checkpoint-manager.d.ts +29 -4
- package/dist/checkpoint-manager.mjs +133 -24
- package/dist/command.d.ts +9 -2
- package/dist/command.mjs +8 -8
- package/dist/contract.d.ts +687 -0
- package/dist/driver-billing.d.ts +29 -0
- package/dist/driver-catalog.d.ts +43 -58
- package/dist/driver-catalog.mjs +192 -229
- package/dist/drivers/boat-client.d.ts +95 -0
- package/dist/drivers/boat-named-snapshots.d.ts +31 -0
- package/dist/drivers/boat-template.d.ts +49 -0
- package/dist/drivers/boat-template.mjs +1196 -0
- package/dist/drivers/boat.d.ts +64 -0
- package/dist/drivers/boat.mjs +990 -0
- package/dist/drivers/cloudflare.d.ts +5 -8
- package/dist/drivers/cloudflare.mjs +364 -213
- package/dist/drivers/docker.d.ts +4 -3
- package/dist/drivers/docker.mjs +244 -165
- package/dist/drivers/local-brokering.d.ts +14 -0
- package/dist/drivers/local-brokering.mjs +1029 -0
- package/dist/drivers/modal.d.ts +4 -7
- package/dist/drivers/modal.mjs +220 -155
- package/dist/drivers/transient-error.d.ts +13 -0
- package/dist/drivers/vercel.d.ts +21 -23
- package/dist/drivers/vercel.mjs +279 -169
- package/dist/egress-policy.d.ts +134 -0
- package/dist/egress-warnings.d.ts +22 -0
- package/dist/garbage-collect.d.ts +14 -0
- package/dist/hosted-network-policy.d.ts +11 -4
- package/dist/hosted-network-policy.mjs +221 -4
- package/dist/image-name.d.ts +12 -2
- package/dist/image-name.mjs +43 -0
- package/dist/image.d.ts +1 -20
- package/dist/image.mjs +11 -66
- package/dist/index.d.ts +8 -533
- package/dist/index.mjs +1420 -315
- package/dist/json.d.ts +13 -0
- package/dist/lease-failure.d.ts +173 -0
- package/dist/lease-lifecycle.d.ts +65 -0
- package/dist/lease-retry-wait.d.ts +8 -0
- package/dist/lease-start.d.ts +21 -0
- package/dist/lease-types.d.ts +15 -5
- package/dist/manager.d.ts +2 -0
- package/dist/runtime-env.d.ts +15 -2
- package/dist/runtime-env.mjs +50 -1
- package/dist/runtime-snapshot.d.ts +49 -0
- package/dist/runtime-version.d.ts +9 -0
- package/dist/runtime-version.mjs +2 -0
- package/dist/service-url-exposure.d.ts +1 -3
- package/dist/service-url-exposure.mjs +1 -26
- package/dist/start-fresh.d.ts +17 -0
- package/dist/start-telemetry.d.ts +37 -0
- package/dist/stores/memory.d.ts +1 -1
- package/dist/stores/memory.mjs +65 -54
- package/package.json +67 -81
- package/dist/daytona-allow-list.d.ts +0 -16
- package/dist/daytona-allow-list.mjs +0 -11
- package/dist/drivers/box.d.ts +0 -52
- package/dist/drivers/box.mjs +0 -499
- package/dist/drivers/cloudflare-egress.d.ts +0 -40
- package/dist/drivers/cloudflare-egress.mjs +0 -113
- package/dist/drivers/daytona.d.ts +0 -127
- package/dist/drivers/daytona.mjs +0 -541
- package/dist/drivers/exe.d.ts +0 -26
- package/dist/drivers/exe.mjs +0 -469
- package/dist/drivers/fetch-bridge.d.ts +0 -10
- package/dist/drivers/fetch-bridge.mjs +0 -111
- package/dist/lease-policy.d.ts +0 -48
- package/dist/lease-policy.mjs +0 -280
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SandboxMachineClass } from "@claxedo/sandbox-contract";
|
|
2
|
+
/** One size a provider sells, as its own catalog names and prices it. */
|
|
3
|
+
export type SandboxMachineRate = {
|
|
4
|
+
/** The provider's own name for the size. */
|
|
5
|
+
size: string;
|
|
6
|
+
vcpu: number;
|
|
7
|
+
memoryGb: number;
|
|
8
|
+
usdPerHour: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* How a provider bills the machine a sandbox runs on, read from its published
|
|
12
|
+
* pricing. `rates.default` is also the one size of a driver that offers no
|
|
13
|
+
* choice. `excludes` names a component the hourly rate leaves out because the
|
|
14
|
+
* provider meters it on use rather than on time.
|
|
15
|
+
*/
|
|
16
|
+
export type SandboxDriverBilling = {
|
|
17
|
+
source: {
|
|
18
|
+
url: string;
|
|
19
|
+
readOn: string;
|
|
20
|
+
};
|
|
21
|
+
meter: string;
|
|
22
|
+
rates: Partial<Record<SandboxMachineClass, SandboxMachineRate>>;
|
|
23
|
+
excludes?: string;
|
|
24
|
+
};
|
|
25
|
+
export declare const boatBilling: SandboxDriverBilling;
|
|
26
|
+
export declare const cloudflareBilling: SandboxDriverBilling;
|
|
27
|
+
/** The rate a sandbox of this class runs at; a driver with one size answers it for the class it was not asked for. */
|
|
28
|
+
export declare function sandboxMachineRate(billing: SandboxDriverBilling | undefined, machineClass: SandboxMachineClass | null): SandboxMachineRate | undefined;
|
|
29
|
+
export declare function sandboxMachineCostUsd(rate: SandboxMachineRate, activeMs: number): number;
|
package/dist/driver-catalog.d.ts
CHANGED
|
@@ -1,68 +1,53 @@
|
|
|
1
|
-
import { validateSandboxPersistenceCapabilities, type SandboxDriverMetadata } from "./
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
daytona?: {
|
|
9
|
-
api_key?: string;
|
|
10
|
-
};
|
|
11
|
-
modal?: {
|
|
12
|
-
token_id?: string;
|
|
13
|
-
token_secret?: string;
|
|
14
|
-
};
|
|
15
|
-
vercel?: {
|
|
16
|
-
access_token?: string;
|
|
17
|
-
team_id?: string;
|
|
18
|
-
project_id?: string;
|
|
19
|
-
};
|
|
20
|
-
cloudflare?: {
|
|
21
|
-
api_token?: string;
|
|
22
|
-
worker_url?: string;
|
|
23
|
-
};
|
|
24
|
-
box?: {
|
|
25
|
-
api_key?: string;
|
|
26
|
-
};
|
|
27
|
-
docker?: {
|
|
28
|
-
image?: string;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
export type SandboxDriverConfig = {
|
|
32
|
-
default_driver?: SandboxDriverID;
|
|
33
|
-
auth?: SandboxDriverAuth;
|
|
34
|
-
};
|
|
35
|
-
export type SandboxDriverCatalogEntry = {
|
|
36
|
-
id: SandboxDriverID;
|
|
1
|
+
import { validateSandboxPersistenceCapabilities, type SandboxDriverMetadata } from "./contract";
|
|
2
|
+
import { type SandboxDriverBilling } from "./driver-billing";
|
|
3
|
+
import { sandboxDriverId, type SandboxDriverEnv, type SandboxDriverID } from "@claxedo/sandbox-contract";
|
|
4
|
+
export { sandboxDriverId };
|
|
5
|
+
export { sandboxMachineCostUsd, sandboxMachineRate, type SandboxDriverBilling, type SandboxMachineRate } from "./driver-billing";
|
|
6
|
+
export type SandboxDriverCatalogEntry<ID extends string = SandboxDriverID> = {
|
|
7
|
+
id: ID;
|
|
37
8
|
label: string;
|
|
38
9
|
metadata: SandboxDriverMetadata;
|
|
39
|
-
credentialFields:
|
|
10
|
+
credentialFields: ReadonlyArray<{
|
|
40
11
|
key: string;
|
|
41
12
|
label: string;
|
|
42
13
|
secret?: boolean;
|
|
43
14
|
}>;
|
|
15
|
+
runtimeNetwork: {
|
|
16
|
+
controlPlane: "docker-host" | "direct";
|
|
17
|
+
relay: "configured" | "local";
|
|
18
|
+
};
|
|
19
|
+
runtimeEnv: "ensure" | "callback";
|
|
20
|
+
preparedImage: boolean;
|
|
21
|
+
/** Absent where no published rate for the machines this driver places on has been established. */
|
|
22
|
+
billing?: SandboxDriverBilling;
|
|
44
23
|
};
|
|
45
24
|
export { validateSandboxPersistenceCapabilities };
|
|
25
|
+
export declare const localBrokeringTestDriverCatalogEntry: {
|
|
26
|
+
readonly id: "local-brokering-test";
|
|
27
|
+
readonly label: "Local brokering test driver";
|
|
28
|
+
readonly credentialFields: readonly [];
|
|
29
|
+
readonly runtimeNetwork: {
|
|
30
|
+
readonly controlPlane: "direct";
|
|
31
|
+
readonly relay: "local";
|
|
32
|
+
};
|
|
33
|
+
readonly runtimeEnv: "ensure";
|
|
34
|
+
readonly preparedImage: false;
|
|
35
|
+
readonly metadata: {
|
|
36
|
+
readonly driverRunsIn: ["local"];
|
|
37
|
+
readonly hostStopBehavior: "terminates-host";
|
|
38
|
+
readonly hostResumeBehavior: "same-host";
|
|
39
|
+
readonly targetAccess: "loopback";
|
|
40
|
+
readonly secretBrokering: "native";
|
|
41
|
+
readonly egressControl: "hosts";
|
|
42
|
+
readonly persistence: {
|
|
43
|
+
readonly resume: "same-sandbox";
|
|
44
|
+
readonly capture: "none";
|
|
45
|
+
readonly clone: false;
|
|
46
|
+
readonly captureSource: "not-applicable";
|
|
47
|
+
readonly retention: "provider-managed";
|
|
48
|
+
readonly restoreMount: "same-resource";
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
};
|
|
46
52
|
export declare const sandboxDriverCatalog: Record<SandboxDriverID, SandboxDriverCatalogEntry>;
|
|
47
|
-
export declare function isSandboxDriverID(input: string | undefined): input is SandboxDriverID;
|
|
48
|
-
export declare function sandboxDriverId(input: string | undefined, cfg?: SandboxDriverConfig, env?: SandboxDriverEnv): "box" | "cloudflare" | "daytona" | "docker" | "exe" | "modal" | "vercel" | undefined;
|
|
49
|
-
export declare function defaultSandboxDriverID(cfg?: SandboxDriverConfig, env?: SandboxDriverEnv): SandboxDriverID;
|
|
50
|
-
export declare function sandboxDriverAuth<T extends SandboxDriverID>(cfg: SandboxDriverConfig | undefined, id: T, env?: SandboxDriverEnv): SandboxDriverAuth[T] | undefined;
|
|
51
|
-
export declare function hasSandboxDriverAuth(cfg: SandboxDriverConfig | undefined, id: SandboxDriverID, env?: SandboxDriverEnv): boolean;
|
|
52
53
|
export declare function dockerSandboxDriverEnabled(env?: SandboxDriverEnv): boolean;
|
|
53
|
-
export declare function listSandboxDrivers(cfg?: SandboxDriverConfig, env?: SandboxDriverEnv, managedDriverIds?: ReadonlySet<string>): {
|
|
54
|
-
default_driver: "box" | "cloudflare" | "daytona" | "docker" | "exe" | "modal" | "vercel";
|
|
55
|
-
drivers: {
|
|
56
|
-
id: "box" | "cloudflare" | "daytona" | "docker" | "exe" | "modal" | "vercel";
|
|
57
|
-
label: string;
|
|
58
|
-
fields: {
|
|
59
|
-
key: string;
|
|
60
|
-
label: string;
|
|
61
|
-
secret?: boolean;
|
|
62
|
-
}[];
|
|
63
|
-
configured: boolean;
|
|
64
|
-
source: "config" | "managed" | "none";
|
|
65
|
-
default: boolean;
|
|
66
|
-
}[];
|
|
67
|
-
};
|
|
68
|
-
type SandboxDriverEnv = Record<string, string | undefined>;
|
package/dist/driver-catalog.mjs
CHANGED
|
@@ -1,30 +1,4 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var SANDBOX_MODEL_PROVIDER_HOSTS = [
|
|
3
|
-
"api.anthropic.com",
|
|
4
|
-
"api.openai.com",
|
|
5
|
-
"generativelanguage.googleapis.com",
|
|
6
|
-
"openrouter.ai",
|
|
7
|
-
// Model catalog + engine config, fetched on runner startup.
|
|
8
|
-
"models.dev",
|
|
9
|
-
"opencode.ai"
|
|
10
|
-
];
|
|
11
|
-
var SANDBOX_PACKAGE_REGISTRY_HOSTS = [
|
|
12
|
-
"registry.npmjs.org",
|
|
13
|
-
"nodejs.org",
|
|
14
|
-
"pypi.org",
|
|
15
|
-
"files.pythonhosted.org",
|
|
16
|
-
"crates.io",
|
|
17
|
-
"static.crates.io",
|
|
18
|
-
"index.crates.io",
|
|
19
|
-
"proxy.golang.org",
|
|
20
|
-
"sum.golang.org"
|
|
21
|
-
];
|
|
22
|
-
var SANDBOX_BASELINE_EGRESS_HOSTS = [
|
|
23
|
-
...SANDBOX_MODEL_PROVIDER_HOSTS,
|
|
24
|
-
...SANDBOX_PACKAGE_REGISTRY_HOSTS
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
// src/index.ts
|
|
1
|
+
// src/contract.ts
|
|
28
2
|
function validateSandboxPersistenceCapabilities(input) {
|
|
29
3
|
if (input.resume === "replacement-restore" && input.capture === "none") {
|
|
30
4
|
return { valid: false, reason: "replacement restore requires a capture source" };
|
|
@@ -38,100 +12,176 @@ function validateSandboxPersistenceCapabilities(input) {
|
|
|
38
12
|
if (input.capture === "same-resource" && input.restoreMount !== "same-resource") {
|
|
39
13
|
return { valid: false, reason: "same-resource capture requires same-resource restore" };
|
|
40
14
|
}
|
|
41
|
-
if (input.restoreMount === "copy-on-write" && input.capture !== "directories") {
|
|
42
|
-
return { valid: false, reason: "copy-on-write restore requires directory capture" };
|
|
43
|
-
}
|
|
44
15
|
return { valid: true };
|
|
45
16
|
}
|
|
46
17
|
|
|
47
|
-
// src/
|
|
48
|
-
var
|
|
49
|
-
|
|
50
|
-
|
|
18
|
+
// src/driver-billing.ts
|
|
19
|
+
var HOUR_SECONDS = 3600;
|
|
20
|
+
var boatBilling = {
|
|
21
|
+
source: { url: "https://docs.boat.dev/pricing", readOn: "2026-10-12" },
|
|
22
|
+
meter: "Boat bills machine time per second while a sandbox runs, at an hourly rate per size; a stopped sandbox costs nothing.",
|
|
23
|
+
rates: {
|
|
24
|
+
small: { size: "small", vcpu: 2, memoryGb: 4, usdPerHour: 0.018 },
|
|
25
|
+
default: { size: "default", vcpu: 4, memoryGb: 8, usdPerHour: 0.036 },
|
|
26
|
+
large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
|
|
30
|
+
var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
|
|
31
|
+
var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
|
|
32
|
+
var CLOUDFLARE_STANDARD_1 = { size: "standard-1", vcpu: 0.5, memoryGb: 4, diskGb: 8 };
|
|
33
|
+
var cloudflareBilling = {
|
|
34
|
+
source: { url: "https://developers.cloudflare.com/containers/pricing/", readOn: "2026-10-12" },
|
|
35
|
+
meter: "Cloudflare bills a container for every 10 ms it runs: memory at $0.0000025 per GiB-second and disk at $0.00000007 per GB-second for the whole run, plus CPU at $0.00002 per vCPU-second of active use.",
|
|
36
|
+
rates: {
|
|
37
|
+
default: {
|
|
38
|
+
size: CLOUDFLARE_STANDARD_1.size,
|
|
39
|
+
vcpu: CLOUDFLARE_STANDARD_1.vcpu,
|
|
40
|
+
memoryGb: CLOUDFLARE_STANDARD_1.memoryGb,
|
|
41
|
+
usdPerHour: (CLOUDFLARE_STANDARD_1.memoryGb * CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY + CLOUDFLARE_STANDARD_1.diskGb * CLOUDFLARE_USD_PER_GB_SECOND_DISK) * HOUR_SECONDS
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
excludes: `active CPU, billed at $${CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE} per vCPU-second and not measured by Claxedo, and the monthly allowance a Workers Paid plan includes`
|
|
45
|
+
};
|
|
46
|
+
function sandboxMachineRate(billing, machineClass) {
|
|
47
|
+
if (!billing) return void 0;
|
|
48
|
+
return billing.rates[machineClass ?? "default"] ?? (Object.keys(billing.rates).length === 1 ? billing.rates.default : void 0);
|
|
49
|
+
}
|
|
50
|
+
function sandboxMachineCostUsd(rate, activeMs) {
|
|
51
|
+
return rate.usdPerHour * activeMs / (HOUR_SECONDS * 1e3);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// ../sandbox-contract/dist/index.mjs
|
|
55
|
+
function isString(value) {
|
|
56
|
+
return typeof value === "string";
|
|
57
|
+
}
|
|
58
|
+
function trimToUndefined(value) {
|
|
59
|
+
if (!isString(value))
|
|
60
|
+
return;
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
63
|
+
}
|
|
64
|
+
var sandboxDriverIds = ["modal", "vercel", "cloudflare", "boat", "docker"];
|
|
65
|
+
var sandboxDriverCredentialFields = {
|
|
66
|
+
modal: [
|
|
67
|
+
{ key: "token_id", label: "Token ID", secret: true },
|
|
68
|
+
{ key: "token_secret", label: "Token Secret", secret: true }
|
|
69
|
+
],
|
|
70
|
+
vercel: [
|
|
71
|
+
{ key: "access_token", label: "Access Token", secret: true },
|
|
72
|
+
{ key: "team_id", label: "Team ID" },
|
|
73
|
+
{ key: "project_id", label: "Project ID" }
|
|
74
|
+
],
|
|
75
|
+
cloudflare: [
|
|
76
|
+
{ key: "api_token", label: "API Token", secret: true },
|
|
77
|
+
{ key: "worker_url", label: "Worker URL" }
|
|
78
|
+
],
|
|
79
|
+
boat: [{ key: "api_key", label: "API Key", secret: true }],
|
|
80
|
+
docker: [{ key: "image", label: "Image" }]
|
|
81
|
+
};
|
|
82
|
+
var sandboxDriverLabels = {
|
|
83
|
+
modal: "Modal",
|
|
84
|
+
vercel: "Vercel",
|
|
85
|
+
cloudflare: "Cloudflare",
|
|
86
|
+
boat: "Boat",
|
|
87
|
+
docker: "Docker"
|
|
88
|
+
};
|
|
89
|
+
function isSandboxDriverID(input) {
|
|
90
|
+
return !!input && sandboxDriverIds.includes(input);
|
|
58
91
|
}
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
return repository ? repository : DEFAULT_SANDBOX_IMAGE_REPOSITORY;
|
|
92
|
+
function enabled(input) {
|
|
93
|
+
return ["1", "true", "yes", "on"].includes(input?.trim().toLowerCase() ?? "");
|
|
62
94
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
return id ? `-${snapshotVersion(id)}` : "";
|
|
95
|
+
function dockerSandboxDriverEnabled(env = process.env) {
|
|
96
|
+
return enabled(env.CLAXEDO_ENABLE_DOCKER_SANDBOX) || enabled(env.CLAXEDO_DEV_DOCKER_SANDBOX);
|
|
66
97
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
98
|
+
function sandboxDriverAuthValues(cfg, id, env = process.env) {
|
|
99
|
+
if (id === "modal") {
|
|
100
|
+
const token_id = trimToUndefined(cfg?.auth?.modal?.token_id) ?? trimToUndefined(env.MODAL_TOKEN_ID);
|
|
101
|
+
const token_secret = trimToUndefined(cfg?.auth?.modal?.token_secret) ?? trimToUndefined(env.MODAL_TOKEN_SECRET);
|
|
102
|
+
return token_id && token_secret ? { token_id, token_secret } : void 0;
|
|
103
|
+
}
|
|
104
|
+
if (id === "vercel") {
|
|
105
|
+
const access_token = trimToUndefined(cfg?.auth?.vercel?.access_token) ?? trimToUndefined(env.VERCEL_TOKEN);
|
|
106
|
+
const team_id = trimToUndefined(cfg?.auth?.vercel?.team_id) ?? trimToUndefined(env.VERCEL_TEAM_ID);
|
|
107
|
+
const project_id = trimToUndefined(cfg?.auth?.vercel?.project_id) ?? trimToUndefined(env.VERCEL_PROJECT_ID);
|
|
108
|
+
return access_token && team_id && project_id ? { access_token, team_id, project_id } : void 0;
|
|
109
|
+
}
|
|
110
|
+
if (id === "cloudflare") {
|
|
111
|
+
const api_token = trimToUndefined(cfg?.auth?.cloudflare?.api_token) ?? trimToUndefined(env.CLOUDFLARE_API_TOKEN);
|
|
112
|
+
const configuredUrl = trimToUndefined(cfg?.auth?.cloudflare?.worker_url) ?? trimToUndefined(env.CLOUDFLARE_SANDBOX_WORKER_URL);
|
|
113
|
+
const worker_url = configuredUrl ? cloudflareWorkerBaseUrl(configuredUrl) : void 0;
|
|
114
|
+
return api_token && worker_url ? { api_token, worker_url } : void 0;
|
|
115
|
+
}
|
|
116
|
+
if (id === "boat") {
|
|
117
|
+
const api_key = trimToUndefined(cfg?.auth?.boat?.api_key) ?? trimToUndefined(env.BOAT_API_KEY);
|
|
118
|
+
return api_key ? { api_key } : void 0;
|
|
119
|
+
}
|
|
120
|
+
if (!dockerSandboxDriverEnabled(env))
|
|
121
|
+
return;
|
|
122
|
+
const image = trimToUndefined(cfg?.auth?.docker?.image) ?? trimToUndefined(env.CLAXEDO_DOCKER_SANDBOX_IMAGE) ?? trimToUndefined(env.CLAXEDO_SANDBOX_IMAGE);
|
|
123
|
+
return image ? { image } : {};
|
|
124
|
+
}
|
|
125
|
+
function cloudflareWorkerBaseUrl(input) {
|
|
126
|
+
let url;
|
|
127
|
+
try {
|
|
128
|
+
url = new URL(input.trim());
|
|
129
|
+
} catch {
|
|
130
|
+
throw new Error("Cloudflare Worker URL must be a valid HTTPS endpoint");
|
|
131
|
+
}
|
|
132
|
+
if (url.protocol !== "https:" || url.username || url.password || input.includes("?") || input.includes("#")) {
|
|
133
|
+
throw new Error("Cloudflare Worker URL requires HTTPS without credentials, query or fragment");
|
|
134
|
+
}
|
|
135
|
+
return url.origin + url.pathname.replace(/\/+$/, "");
|
|
136
|
+
}
|
|
137
|
+
function sandboxDriverId(input, cfg, env = process.env) {
|
|
138
|
+
if (!isSandboxDriverID(input))
|
|
139
|
+
return;
|
|
140
|
+
if (input === "docker" && !sandboxDriverAuthValues(cfg, input, env))
|
|
141
|
+
return;
|
|
142
|
+
return input;
|
|
69
143
|
}
|
|
70
144
|
|
|
71
145
|
// src/driver-catalog.ts
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
daytona: {
|
|
96
|
-
id: "daytona",
|
|
97
|
-
label: "Daytona",
|
|
98
|
-
credentialFields: [{ key: "api_key", label: "API Key", secret: true }],
|
|
99
|
-
metadata: {
|
|
100
|
-
driverRunsIn: ["worker", "node"],
|
|
101
|
-
hostStopBehavior: "suspends-host",
|
|
102
|
-
hostResumeBehavior: "same-host",
|
|
103
|
-
targetAccess: "relay",
|
|
104
|
-
secretBrokering: "native",
|
|
105
|
-
// Daytona is the only driver that can filter egress by NAME as well as
|
|
106
|
-
// by address: `domainAllowList` (names) alongside `networkAllowList`
|
|
107
|
-
// (CIDRs), with `networkBlockAll` as the deny-all floor.
|
|
108
|
-
egressControl: "hosts-and-cidrs",
|
|
109
|
-
persistence: {
|
|
110
|
-
resume: "same-sandbox",
|
|
111
|
-
capture: "filesystem",
|
|
112
|
-
clone: false,
|
|
113
|
-
captureSource: "preserved",
|
|
114
|
-
retention: "provider-managed",
|
|
115
|
-
restoreMount: "new-resource"
|
|
116
|
-
}
|
|
146
|
+
var localBrokeringTestDriverCatalogEntry = {
|
|
147
|
+
id: "local-brokering-test",
|
|
148
|
+
label: "Local brokering test driver",
|
|
149
|
+
credentialFields: [],
|
|
150
|
+
runtimeNetwork: { controlPlane: "direct", relay: "local" },
|
|
151
|
+
runtimeEnv: "ensure",
|
|
152
|
+
preparedImage: false,
|
|
153
|
+
metadata: {
|
|
154
|
+
driverRunsIn: ["local"],
|
|
155
|
+
hostStopBehavior: "terminates-host",
|
|
156
|
+
hostResumeBehavior: "same-host",
|
|
157
|
+
targetAccess: "loopback",
|
|
158
|
+
secretBrokering: "native",
|
|
159
|
+
egressControl: "hosts",
|
|
160
|
+
persistence: {
|
|
161
|
+
resume: "same-sandbox",
|
|
162
|
+
capture: "none",
|
|
163
|
+
clone: false,
|
|
164
|
+
captureSource: "not-applicable",
|
|
165
|
+
retention: "provider-managed",
|
|
166
|
+
restoreMount: "same-resource"
|
|
117
167
|
}
|
|
118
|
-
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var sandboxDriverCatalog = {
|
|
119
171
|
modal: {
|
|
120
172
|
id: "modal",
|
|
121
|
-
label:
|
|
122
|
-
credentialFields:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
173
|
+
label: sandboxDriverLabels.modal,
|
|
174
|
+
credentialFields: sandboxDriverCredentialFields.modal,
|
|
175
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
176
|
+
runtimeEnv: "callback",
|
|
177
|
+
preparedImage: true,
|
|
126
178
|
metadata: {
|
|
127
179
|
driverRunsIn: ["node"],
|
|
128
180
|
hostStopBehavior: "terminates-host",
|
|
129
181
|
hostResumeBehavior: "replacement-host",
|
|
130
182
|
targetAccess: "relay",
|
|
131
183
|
secretBrokering: "none",
|
|
132
|
-
//
|
|
133
|
-
// an allowlist, and its driver throws on a host policy. A blackout is
|
|
134
|
-
// not containment for a workspace that has to clone and reach a model.
|
|
184
|
+
// The driver does not install Modal's domain allowlist or alpha sidecar.
|
|
135
185
|
egressControl: "none",
|
|
136
186
|
persistence: {
|
|
137
187
|
resume: "replacement-restore",
|
|
@@ -145,20 +195,18 @@ var sandboxDriverCatalog = {
|
|
|
145
195
|
},
|
|
146
196
|
vercel: {
|
|
147
197
|
id: "vercel",
|
|
148
|
-
label:
|
|
149
|
-
credentialFields:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
],
|
|
198
|
+
label: sandboxDriverLabels.vercel,
|
|
199
|
+
credentialFields: sandboxDriverCredentialFields.vercel,
|
|
200
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
201
|
+
runtimeEnv: "callback",
|
|
202
|
+
preparedImage: false,
|
|
154
203
|
metadata: {
|
|
155
204
|
driverRunsIn: ["node"],
|
|
156
205
|
hostStopBehavior: "terminates-host",
|
|
157
206
|
hostResumeBehavior: "replacement-host",
|
|
158
207
|
targetAccess: "relay",
|
|
159
208
|
secretBrokering: "native",
|
|
160
|
-
//
|
|
161
|
-
// never CIDRs.
|
|
209
|
+
// The driver translates hosts only; the SDK's subnet rules are not wired to net.cidrs.
|
|
162
210
|
egressControl: "hosts",
|
|
163
211
|
persistence: {
|
|
164
212
|
resume: "replacement-restore",
|
|
@@ -172,40 +220,42 @@ var sandboxDriverCatalog = {
|
|
|
172
220
|
},
|
|
173
221
|
cloudflare: {
|
|
174
222
|
id: "cloudflare",
|
|
175
|
-
label:
|
|
176
|
-
credentialFields:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
label: sandboxDriverLabels.cloudflare,
|
|
224
|
+
credentialFields: sandboxDriverCredentialFields.cloudflare,
|
|
225
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
226
|
+
runtimeEnv: "callback",
|
|
227
|
+
preparedImage: false,
|
|
228
|
+
billing: cloudflareBilling,
|
|
180
229
|
metadata: {
|
|
181
230
|
driverRunsIn: ["worker"],
|
|
182
|
-
hostStopBehavior: "
|
|
231
|
+
hostStopBehavior: "terminates-host",
|
|
183
232
|
hostResumeBehavior: "same-host",
|
|
233
|
+
hostWatchesIdle: true,
|
|
184
234
|
targetAccess: "relay",
|
|
185
|
-
secretBrokering: "
|
|
186
|
-
//
|
|
187
|
-
// (drivers/cloudflare-egress.ts) is an OPT-IN proxy the sandbox chooses
|
|
188
|
-
// to route brokered-credential requests through, not a boundary. The
|
|
189
|
-
// driver therefore drops `net` entirely — which is exactly why this
|
|
190
|
-
// must be declared, so the manager refuses instead of provisioning a
|
|
191
|
-
// sandbox the caller believes is contained.
|
|
235
|
+
secretBrokering: "native",
|
|
236
|
+
// Credential handlers do not restrict unrelated destinations.
|
|
192
237
|
egressControl: "none",
|
|
238
|
+
// A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
|
|
193
239
|
persistence: {
|
|
194
240
|
resume: "replacement-restore",
|
|
195
|
-
capture: "
|
|
241
|
+
capture: "filesystem",
|
|
196
242
|
clone: false,
|
|
197
243
|
captureSource: "preserved",
|
|
198
244
|
retention: "provider-managed",
|
|
199
|
-
restoreMount: "
|
|
245
|
+
restoreMount: "new-resource"
|
|
200
246
|
}
|
|
201
247
|
}
|
|
202
248
|
},
|
|
203
|
-
|
|
204
|
-
id: "
|
|
205
|
-
label:
|
|
206
|
-
credentialFields:
|
|
249
|
+
boat: {
|
|
250
|
+
id: "boat",
|
|
251
|
+
label: sandboxDriverLabels.boat,
|
|
252
|
+
credentialFields: sandboxDriverCredentialFields.boat,
|
|
253
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
254
|
+
runtimeEnv: "callback",
|
|
255
|
+
preparedImage: true,
|
|
256
|
+
billing: boatBilling,
|
|
207
257
|
metadata: {
|
|
208
|
-
driverRunsIn: ["node"],
|
|
258
|
+
driverRunsIn: ["worker", "node"],
|
|
209
259
|
hostStopBehavior: "suspends-host",
|
|
210
260
|
hostResumeBehavior: "same-host",
|
|
211
261
|
targetAccess: "relay",
|
|
@@ -218,13 +268,17 @@ var sandboxDriverCatalog = {
|
|
|
218
268
|
captureSource: "not-applicable",
|
|
219
269
|
retention: "provider-managed",
|
|
220
270
|
restoreMount: "same-resource"
|
|
221
|
-
}
|
|
271
|
+
},
|
|
272
|
+
machineClasses: ["small", "default", "large"]
|
|
222
273
|
}
|
|
223
274
|
},
|
|
224
275
|
docker: {
|
|
225
276
|
id: "docker",
|
|
226
|
-
label:
|
|
227
|
-
credentialFields:
|
|
277
|
+
label: sandboxDriverLabels.docker,
|
|
278
|
+
credentialFields: sandboxDriverCredentialFields.docker,
|
|
279
|
+
runtimeNetwork: { controlPlane: "docker-host", relay: "configured" },
|
|
280
|
+
runtimeEnv: "callback",
|
|
281
|
+
preparedImage: true,
|
|
228
282
|
metadata: {
|
|
229
283
|
driverRunsIn: ["local"],
|
|
230
284
|
hostStopBehavior: "terminates-host",
|
|
@@ -243,106 +297,15 @@ var sandboxDriverCatalog = {
|
|
|
243
297
|
}
|
|
244
298
|
}
|
|
245
299
|
};
|
|
246
|
-
function
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
function sandboxDriverId(input, cfg, env = process.env) {
|
|
250
|
-
if (!isSandboxDriverID(input)) return;
|
|
251
|
-
if (input === "docker" && !sandboxDriverAuth(cfg, input, env)) return;
|
|
252
|
-
return input;
|
|
253
|
-
}
|
|
254
|
-
function defaultSandboxDriverID(cfg, env = process.env) {
|
|
255
|
-
return sandboxDriverId(cfg?.default_driver, cfg, env) ?? (dockerSandboxDefault(env) && sandboxDriverAuth(cfg, "docker", env) ? "docker" : "daytona");
|
|
256
|
-
}
|
|
257
|
-
function sandboxDriverAuth(cfg, id, env = process.env) {
|
|
258
|
-
if (id === "daytona") return authDaytona(cfg);
|
|
259
|
-
if (id === "exe") return authExe(cfg, env);
|
|
260
|
-
if (id === "modal") return authModal(cfg, env);
|
|
261
|
-
if (id === "vercel") return authVercel(cfg, env);
|
|
262
|
-
if (id === "cloudflare") return authCloudflare(cfg, env);
|
|
263
|
-
if (id === "box") return authBox(cfg, env);
|
|
264
|
-
return authDocker(cfg, env);
|
|
265
|
-
}
|
|
266
|
-
function hasSandboxDriverAuth(cfg, id, env = process.env) {
|
|
267
|
-
return !!sandboxDriverAuth(cfg, id, env);
|
|
268
|
-
}
|
|
269
|
-
function dockerSandboxDriverEnabled(env = process.env) {
|
|
270
|
-
return dockerSandboxEnabled(env);
|
|
271
|
-
}
|
|
272
|
-
function listSandboxDrivers(cfg, env = process.env, managedDriverIds = /* @__PURE__ */ new Set()) {
|
|
273
|
-
const def = defaultSandboxDriverID(cfg, env);
|
|
274
|
-
return {
|
|
275
|
-
default_driver: def,
|
|
276
|
-
drivers: sandboxDriverIds.filter((id) => id !== "docker" || !!sandboxDriverAuth(cfg, "docker", env)).map((id) => {
|
|
277
|
-
const configuredFromConfigOrEnv = !!sandboxDriverAuth(cfg, id, env);
|
|
278
|
-
const configured = id === "docker" ? configuredFromConfigOrEnv : configuredFromConfigOrEnv || managedDriverIds.has(id);
|
|
279
|
-
return {
|
|
280
|
-
id,
|
|
281
|
-
label: sandboxDriverCatalog[id].label,
|
|
282
|
-
fields: sandboxDriverCatalog[id].credentialFields,
|
|
283
|
-
configured,
|
|
284
|
-
source: configuredFromConfigOrEnv ? "config" : configured ? "managed" : "none",
|
|
285
|
-
default: id === def
|
|
286
|
-
};
|
|
287
|
-
})
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
function clean(input) {
|
|
291
|
-
const txt = input?.trim();
|
|
292
|
-
return txt ? txt : void 0;
|
|
293
|
-
}
|
|
294
|
-
function enabled(input) {
|
|
295
|
-
return ["1", "true", "yes", "on"].includes(input?.trim().toLowerCase() ?? "");
|
|
296
|
-
}
|
|
297
|
-
function dockerSandboxEnabled(env) {
|
|
298
|
-
return enabled(env.CLAXEDO_ENABLE_DOCKER_SANDBOX) || enabled(env.CLAXEDO_DEV_DOCKER_SANDBOX);
|
|
299
|
-
}
|
|
300
|
-
function dockerSandboxDefault(env) {
|
|
301
|
-
return enabled(env.CLAXEDO_DOCKER_SANDBOX_DEFAULT);
|
|
302
|
-
}
|
|
303
|
-
function authDaytona(cfg) {
|
|
304
|
-
const api_key = clean(cfg?.auth?.daytona?.api_key);
|
|
305
|
-
return api_key ? { api_key } : void 0;
|
|
306
|
-
}
|
|
307
|
-
function authExe(cfg, env) {
|
|
308
|
-
const api_token = clean(cfg?.auth?.exe?.api_token) ?? clean(env.EXE_DEV_API_TOKEN);
|
|
309
|
-
return api_token ? { api_token } : void 0;
|
|
310
|
-
}
|
|
311
|
-
function authModal(cfg, env) {
|
|
312
|
-
const token_id = clean(cfg?.auth?.modal?.token_id) ?? clean(env.MODAL_TOKEN_ID);
|
|
313
|
-
const token_secret = clean(cfg?.auth?.modal?.token_secret) ?? clean(env.MODAL_TOKEN_SECRET);
|
|
314
|
-
return token_id && token_secret ? { token_id, token_secret } : void 0;
|
|
315
|
-
}
|
|
316
|
-
function authVercel(cfg, env) {
|
|
317
|
-
const access_token = clean(cfg?.auth?.vercel?.access_token) ?? clean(env.VERCEL_TOKEN);
|
|
318
|
-
const team_id = clean(cfg?.auth?.vercel?.team_id) ?? clean(env.VERCEL_TEAM_ID);
|
|
319
|
-
const project_id = clean(cfg?.auth?.vercel?.project_id) ?? clean(env.VERCEL_PROJECT_ID);
|
|
320
|
-
return access_token && team_id && project_id ? { access_token, team_id, project_id } : void 0;
|
|
321
|
-
}
|
|
322
|
-
function authCloudflare(cfg, env) {
|
|
323
|
-
const api_token = clean(cfg?.auth?.cloudflare?.api_token) ?? clean(env.CLOUDFLARE_API_TOKEN);
|
|
324
|
-
const worker_url = clean(cfg?.auth?.cloudflare?.worker_url) ?? clean(env.CLOUDFLARE_SANDBOX_WORKER_URL);
|
|
325
|
-
return api_token && worker_url ? { api_token, worker_url } : void 0;
|
|
326
|
-
}
|
|
327
|
-
function authBox(cfg, env) {
|
|
328
|
-
const api_key = clean(cfg?.auth?.box?.api_key) ?? clean(env.BOX_API_KEY);
|
|
329
|
-
return api_key ? { api_key } : void 0;
|
|
330
|
-
}
|
|
331
|
-
function authDocker(cfg, env) {
|
|
332
|
-
if (!dockerSandboxEnabled(env)) return;
|
|
333
|
-
return {
|
|
334
|
-
image: clean(cfg?.auth?.docker?.image) ?? clean(env.CLAXEDO_DOCKER_SANDBOX_IMAGE) ?? clean(env.CLAXEDO_SANDBOX_IMAGE) ?? defaultSandboxImage(workspaceRuntimeVersion(), void 0, env)
|
|
335
|
-
};
|
|
300
|
+
function dockerSandboxDriverEnabled2(env = process.env) {
|
|
301
|
+
return dockerSandboxDriverEnabled(env);
|
|
336
302
|
}
|
|
337
303
|
export {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
hasSandboxDriverAuth,
|
|
341
|
-
isSandboxDriverID,
|
|
342
|
-
listSandboxDrivers,
|
|
343
|
-
sandboxDriverAuth,
|
|
304
|
+
dockerSandboxDriverEnabled2 as dockerSandboxDriverEnabled,
|
|
305
|
+
localBrokeringTestDriverCatalogEntry,
|
|
344
306
|
sandboxDriverCatalog,
|
|
345
307
|
sandboxDriverId,
|
|
346
|
-
|
|
308
|
+
sandboxMachineCostUsd,
|
|
309
|
+
sandboxMachineRate,
|
|
347
310
|
validateSandboxPersistenceCapabilities
|
|
348
311
|
};
|