@claxedo/sandbox-manager 0.9.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.
Files changed (67) hide show
  1. package/README.md +233 -26
  2. package/dist/checkpoint-manager.d.ts +29 -4
  3. package/dist/checkpoint-manager.mjs +133 -24
  4. package/dist/command.d.ts +9 -2
  5. package/dist/command.mjs +8 -8
  6. package/dist/contract.d.ts +687 -0
  7. package/dist/driver-billing.d.ts +29 -0
  8. package/dist/driver-catalog.d.ts +42 -7
  9. package/dist/driver-catalog.mjs +115 -161
  10. package/dist/drivers/boat-client.d.ts +95 -0
  11. package/dist/drivers/boat-named-snapshots.d.ts +31 -0
  12. package/dist/drivers/boat-template.d.ts +49 -0
  13. package/dist/drivers/boat-template.mjs +1196 -0
  14. package/dist/drivers/boat.d.ts +64 -0
  15. package/dist/drivers/boat.mjs +990 -0
  16. package/dist/drivers/cloudflare.d.ts +3 -3
  17. package/dist/drivers/cloudflare.mjs +208 -147
  18. package/dist/drivers/docker.d.ts +4 -3
  19. package/dist/drivers/docker.mjs +154 -126
  20. package/dist/drivers/local-brokering.d.ts +14 -0
  21. package/dist/drivers/local-brokering.mjs +1029 -0
  22. package/dist/drivers/modal.d.ts +2 -2
  23. package/dist/drivers/modal.mjs +121 -111
  24. package/dist/drivers/transient-error.d.ts +2 -0
  25. package/dist/drivers/vercel.d.ts +3 -2
  26. package/dist/drivers/vercel.mjs +151 -115
  27. package/dist/egress-policy.d.ts +134 -0
  28. package/dist/egress-warnings.d.ts +22 -0
  29. package/dist/garbage-collect.d.ts +14 -0
  30. package/dist/hosted-network-policy.d.ts +9 -2
  31. package/dist/hosted-network-policy.mjs +218 -1
  32. package/dist/image-name.d.ts +12 -2
  33. package/dist/image-name.mjs +14 -5
  34. package/dist/image.d.ts +1 -20
  35. package/dist/image.mjs +11 -88
  36. package/dist/index.d.ts +8 -578
  37. package/dist/index.mjs +1379 -320
  38. package/dist/lease-failure.d.ts +173 -0
  39. package/dist/lease-lifecycle.d.ts +65 -0
  40. package/dist/lease-retry-wait.d.ts +8 -0
  41. package/dist/lease-start.d.ts +21 -0
  42. package/dist/lease-types.d.ts +4 -3
  43. package/dist/manager.d.ts +2 -0
  44. package/dist/runtime-env.d.ts +4 -2
  45. package/dist/runtime-env.mjs +37 -2
  46. package/dist/runtime-snapshot.d.ts +49 -0
  47. package/dist/runtime-version.d.ts +9 -0
  48. package/dist/runtime-version.mjs +2 -0
  49. package/dist/service-url-exposure.d.ts +0 -1
  50. package/dist/service-url-exposure.mjs +0 -9
  51. package/dist/start-fresh.d.ts +17 -0
  52. package/dist/start-telemetry.d.ts +37 -0
  53. package/dist/stores/memory.d.ts +1 -1
  54. package/dist/stores/memory.mjs +63 -52
  55. package/package.json +26 -42
  56. package/dist/daytona-allow-list.d.ts +0 -16
  57. package/dist/daytona-allow-list.mjs +0 -11
  58. package/dist/drivers/box.d.ts +0 -52
  59. package/dist/drivers/box.mjs +0 -574
  60. package/dist/drivers/daytona.d.ts +0 -163
  61. package/dist/drivers/daytona.mjs +0 -683
  62. package/dist/drivers/exe.d.ts +0 -26
  63. package/dist/drivers/exe.mjs +0 -492
  64. package/dist/drivers/fetch-bridge.d.ts +0 -10
  65. package/dist/drivers/fetch-bridge.mjs +0 -126
  66. package/dist/lease-policy.d.ts +0 -52
  67. package/dist/lease-policy.mjs +0 -290
@@ -1,47 +1,50 @@
1
- // src/hosted-network-policy.ts
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
2
+ function sandboxLeaseHeldHost(lease) {
3
+ return lease?.sandboxId !== void 0 && (lease.status === "stopped" || lease.status === "unavailable") ? lease : void 0;
4
+ }
28
5
  function applySandboxLeasePatch(current, patch, updatedAt) {
29
6
  return {
30
7
  ...current,
31
8
  ...patch.status === void 0 ? {} : { status: patch.status },
32
- ...patch.sandboxId === void 0 ? {} : { sandboxId: patch.sandboxId },
33
- ...patch.url === void 0 ? {} : { url: patch.url },
34
- ...patch.hostId === void 0 ? {} : { hostId: patch.hostId },
35
- ...patch.driverResourceId === void 0 ? {} : { driverResourceId: patch.driverResourceId },
36
9
  ...patch.retryCount === void 0 ? {} : { retryCount: patch.retryCount },
37
10
  ...patch.lastHeartbeatAt === void 0 ? {} : { lastHeartbeatAt: patch.lastHeartbeatAt },
38
11
  ...patch.lastActivityAt === void 0 ? {} : { lastActivityAt: patch.lastActivityAt },
39
- ...patch.labels === void 0 ? {} : { labels: patch.labels },
12
+ ...patch.lastHealthFailureAt === void 0 ? {} : { lastHealthFailureAt: patch.lastHealthFailureAt },
40
13
  ...patch.nextRetryAt === void 0 ? {} : { nextRetryAt: patch.nextRetryAt ?? void 0 },
41
14
  ...patch.lastError === void 0 ? {} : { lastError: patch.lastError ?? void 0 },
42
15
  ...patch.checkpoint === void 0 ? {} : { checkpoint: patch.checkpoint ?? void 0 },
43
- ...patch.persistence === void 0 ? {} : { persistence: patch.persistence ?? void 0 },
44
16
  ...patch.restore === void 0 ? {} : { restore: patch.restore ?? void 0 },
17
+ ...patch.start === void 0 ? {} : { start: patch.start },
18
+ updatedAt
19
+ };
20
+ }
21
+ function applySandboxLeaseFailure(current, error, nextRetryAt, failedAt) {
22
+ return {
23
+ ...current,
24
+ status: "unavailable",
25
+ retryCount: current.retryCount + 1,
26
+ lastError: error,
27
+ nextRetryAt,
28
+ lastHealthFailureAt: failedAt,
29
+ updatedAt: failedAt,
30
+ ...current.start ? { start: { ...current.start, failures: [...current.start.failures ?? [], { epoch: current.epoch, failedAt, error }] } } : {}
31
+ };
32
+ }
33
+ function applySandboxProvisionedTarget(current, target, updatedAt) {
34
+ const sameTarget = current.status === "ready" && current.url === target.url && current.sandboxId === target.sandboxId && current.hostId === target.hostId && current.driverResourceId === target.driverResourceId;
35
+ return {
36
+ ...current,
37
+ status: target.url === void 0 ? "acquiring" : "ready",
38
+ routingId: sameTarget && current.routingId ? current.routingId : crypto.randomUUID(),
39
+ sandboxId: target.sandboxId,
40
+ url: target.url,
41
+ hostId: target.hostId,
42
+ driverResourceId: target.driverResourceId,
43
+ labels: target.labels,
44
+ ...target.persistence === void 0 ? {} : { persistence: target.persistence },
45
+ retryCount: 0,
46
+ nextRetryAt: void 0,
47
+ lastError: void 0,
45
48
  updatedAt
46
49
  };
47
50
  }
@@ -63,7 +66,7 @@ function createMemoryLeaseStore(seed = []) {
63
66
  retryAfterMs: Math.max(0, input.staleAfterMs - (now - current.updatedAt))
64
67
  };
65
68
  }
66
- const resumable = current?.status === "stopped";
69
+ const held = sandboxLeaseHeldHost(current);
67
70
  const next = {
68
71
  workspaceId,
69
72
  homeRegion: current?.homeRegion ?? input.homeRegion,
@@ -73,23 +76,34 @@ function createMemoryLeaseStore(seed = []) {
73
76
  retryCount: current?.retryCount ?? 0,
74
77
  createdAt: current?.createdAt ?? now,
75
78
  updatedAt: now,
76
- sandboxId: resumable ? current.sandboxId : void 0,
77
- url: resumable ? current.url : void 0,
78
- hostId: resumable ? current.hostId : void 0,
79
- driverResourceId: resumable ? current.driverResourceId : void 0,
80
- lastHeartbeatAt: resumable ? current.lastHeartbeatAt : void 0,
81
- lastActivityAt: resumable ? current.lastActivityAt : void 0,
82
- labels: resumable ? current.labels : void 0,
79
+ sandboxId: held?.sandboxId,
80
+ url: held?.url,
81
+ hostId: held?.hostId,
82
+ driverResourceId: held?.driverResourceId,
83
+ lastHeartbeatAt: held?.lastHeartbeatAt,
84
+ lastActivityAt: held?.lastActivityAt,
85
+ labels: held ? held.labels : input.labels,
86
+ machineClass: input.machineClass ?? held?.machineClass,
83
87
  checkpoint: current?.checkpoint,
84
88
  persistence: current?.persistence,
85
- restore: current?.restore
89
+ restore: current?.restore,
90
+ start: input.start
86
91
  };
87
92
  leases.set(workspaceId, next);
88
93
  return { acquired: true, lease: next };
89
94
  },
90
- async update(workspaceId, expectedEpoch, patch) {
95
+ async recordTarget(workspaceId, expectedEpoch, target) {
91
96
  const current = leases.get(workspaceId);
92
97
  if (!current || current.epoch !== expectedEpoch) return void 0;
98
+ if (current.status === "stopped" || current.status === "destroyed") return void 0;
99
+ const next = applySandboxProvisionedTarget(current, target, Date.now());
100
+ leases.set(workspaceId, next);
101
+ return next;
102
+ },
103
+ async update(workspaceId, expectedEpoch, patch, expectedStatus) {
104
+ const current = leases.get(workspaceId);
105
+ if (!current || current.epoch !== expectedEpoch) return void 0;
106
+ if (expectedStatus !== void 0 && current.status !== expectedStatus) return void 0;
93
107
  const next = applySandboxLeasePatch(current, patch, Date.now());
94
108
  leases.set(workspaceId, next);
95
109
  return next;
@@ -97,14 +111,8 @@ function createMemoryLeaseStore(seed = []) {
97
111
  async recordFailure(workspaceId, expectedEpoch, error, nextRetryAt) {
98
112
  const current = leases.get(workspaceId);
99
113
  if (!current || current.epoch !== expectedEpoch) return void 0;
100
- const next = {
101
- ...current,
102
- status: "unavailable",
103
- retryCount: current.retryCount + 1,
104
- lastError: error,
105
- nextRetryAt,
106
- updatedAt: Date.now()
107
- };
114
+ if (current.status === "stopped" || current.status === "destroyed") return void 0;
115
+ const next = applySandboxLeaseFailure(current, error, nextRetryAt, Date.now());
108
116
  leases.set(workspaceId, next);
109
117
  return next;
110
118
  },
@@ -126,6 +134,7 @@ function sandboxLease(input) {
126
134
  homeRegion: input.homeRegion ?? "us-east",
127
135
  driver: input.driver ?? "test",
128
136
  epoch: input.epoch ?? 1,
137
+ routingId: input.routingId,
129
138
  status: input.status ?? "ready",
130
139
  retryCount: input.retryCount ?? 0,
131
140
  createdAt: input.createdAt ?? Date.now(),
@@ -141,7 +150,9 @@ function sandboxLease(input) {
141
150
  labels: input.labels,
142
151
  checkpoint: input.checkpoint,
143
152
  persistence: input.persistence,
144
- restore: input.restore
153
+ restore: input.restore,
154
+ start: input.start,
155
+ machineClass: input.machineClass
145
156
  };
146
157
  }
147
158
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "type": "module",
3
- "description": "Sandbox lifecycle manager with epoch-based leases and pluggable drivers for Daytona, Modal, Vercel Sandbox, Cloudflare, Box, and Docker",
3
+ "description": "Sandbox lifecycle manager with epoch-based leases and pluggable drivers for Modal, Vercel Sandbox, Cloudflare, Boat, and Docker",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,12 +10,11 @@
10
10
  "keywords": [
11
11
  "claxedo",
12
12
  "sandbox",
13
- "daytona",
14
13
  "modal",
15
14
  "vercel",
16
15
  "cloudflare",
17
16
  "docker",
18
- "box",
17
+ "boat",
19
18
  "agents"
20
19
  ],
21
20
  "main": "./dist/index.mjs",
@@ -63,19 +62,19 @@
63
62
  "import": "./dist/driver-catalog.mjs",
64
63
  "default": "./dist/driver-catalog.mjs"
65
64
  },
66
- "./drivers/box": {
67
- "development": "./src/drivers/box.ts",
68
- "bun": "./src/drivers/box.ts",
69
- "types": "./dist/drivers/box.d.ts",
70
- "import": "./dist/drivers/box.mjs",
71
- "default": "./dist/drivers/box.mjs"
65
+ "./drivers/boat": {
66
+ "development": "./src/drivers/boat.ts",
67
+ "bun": "./src/drivers/boat.ts",
68
+ "types": "./dist/drivers/boat.d.ts",
69
+ "import": "./dist/drivers/boat.mjs",
70
+ "default": "./dist/drivers/boat.mjs"
72
71
  },
73
- "./drivers/exe": {
74
- "development": "./src/drivers/exe.ts",
75
- "bun": "./src/drivers/exe.ts",
76
- "types": "./dist/drivers/exe.d.ts",
77
- "import": "./dist/drivers/exe.mjs",
78
- "default": "./dist/drivers/exe.mjs"
72
+ "./drivers/boat-template": {
73
+ "development": "./src/drivers/boat-template.ts",
74
+ "bun": "./src/drivers/boat-template.ts",
75
+ "types": "./dist/drivers/boat-template.d.ts",
76
+ "import": "./dist/drivers/boat-template.mjs",
77
+ "default": "./dist/drivers/boat-template.mjs"
79
78
  },
80
79
  "./drivers/cloudflare": {
81
80
  "development": "./src/drivers/cloudflare.ts",
@@ -84,13 +83,6 @@
84
83
  "import": "./dist/drivers/cloudflare.mjs",
85
84
  "default": "./dist/drivers/cloudflare.mjs"
86
85
  },
87
- "./drivers/daytona": {
88
- "development": "./src/drivers/daytona.ts",
89
- "bun": "./src/drivers/daytona.ts",
90
- "types": "./dist/drivers/daytona.d.ts",
91
- "import": "./dist/drivers/daytona.mjs",
92
- "default": "./dist/drivers/daytona.mjs"
93
- },
94
86
  "./drivers/docker": {
95
87
  "development": "./src/drivers/docker.ts",
96
88
  "bun": "./src/drivers/docker.ts",
@@ -98,12 +90,12 @@
98
90
  "import": "./dist/drivers/docker.mjs",
99
91
  "default": "./dist/drivers/docker.mjs"
100
92
  },
101
- "./drivers/fetch-bridge": {
102
- "development": "./src/drivers/fetch-bridge.ts",
103
- "bun": "./src/drivers/fetch-bridge.ts",
104
- "types": "./dist/drivers/fetch-bridge.d.ts",
105
- "import": "./dist/drivers/fetch-bridge.mjs",
106
- "default": "./dist/drivers/fetch-bridge.mjs"
93
+ "./drivers/local-brokering": {
94
+ "development": "./src/drivers/local-brokering.ts",
95
+ "bun": "./src/drivers/local-brokering.ts",
96
+ "types": "./dist/drivers/local-brokering.d.ts",
97
+ "import": "./dist/drivers/local-brokering.mjs",
98
+ "default": "./dist/drivers/local-brokering.mjs"
107
99
  },
108
100
  "./drivers/modal": {
109
101
  "development": "./src/drivers/modal.ts",
@@ -133,13 +125,6 @@
133
125
  "import": "./dist/image-name.mjs",
134
126
  "default": "./dist/image-name.mjs"
135
127
  },
136
- "./lease-policy": {
137
- "development": "./src/lease-policy.ts",
138
- "bun": "./src/lease-policy.ts",
139
- "types": "./dist/lease-policy.d.ts",
140
- "import": "./dist/lease-policy.mjs",
141
- "default": "./dist/lease-policy.mjs"
142
- },
143
128
  "./lease-types": {
144
129
  "development": "./src/lease-types.ts",
145
130
  "bun": "./src/lease-types.ts",
@@ -178,17 +163,17 @@
178
163
  "access": "public"
179
164
  },
180
165
  "name": "@claxedo/sandbox-manager",
181
- "version": "0.9.0",
166
+ "version": "0.10.0",
182
167
  "scripts": {
183
- "build": "tsx scripts/build.ts",
168
+ "build": "bun scripts/build.ts",
184
169
  "prepublishOnly": "npm run build",
185
170
  "typecheck": "tsc --noEmit -p tsconfig.json",
186
- "test": "mkdir -p .artifacts/unit && node ../../scripts/test-deadline.mjs bun test src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml"
171
+ "live:boat": "bun scripts/boat-live-check.ts",
172
+ "test": "mkdir -p .artifacts/unit && node ../../script/test-deadline.mjs bun test src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml"
187
173
  },
188
174
  "dependencies": {
189
- "@claxedo/helpers": "0.1.0",
190
- "@claxedo/sandbox-contract": "0.8.0",
191
- "@daytona/sdk": "0.211.2",
175
+ "@claxedo/helpers": "0.2.0",
176
+ "@claxedo/sandbox-contract": "0.10.0",
192
177
  "@vercel/sandbox": "3.3.0",
193
178
  "modal": "0.10.1"
194
179
  },
@@ -197,7 +182,6 @@
197
182
  "@types/bun": "1.3.13",
198
183
  "@types/node": "24.12.2",
199
184
  "esbuild": "0.25.12",
200
- "tsx": "4.22.3",
201
185
  "typescript": "catalog:",
202
186
  "vitest": "2.1.9"
203
187
  }
@@ -1,16 +0,0 @@
1
- /**
2
- * Format CIDRs for Daytona SDK `networkAllowList` parameter.
3
- * Daytona currently accepts at most 10 entries.
4
- */
5
- export declare function formatDaytonaAllowList(cidrs: string[]): string;
6
- /**
7
- * Format hostnames for Daytona SDK `domainAllowList` parameter.
8
- *
9
- * Deliberately does NOT truncate, unlike the CIDR formatter above. Silently
10
- * dropping entries from an egress allowlist is the worst of both worlds: the
11
- * sandbox stays contained, but it loses reachability the caller believed it
12
- * granted and nothing anywhere says so. Daytona documents no cap on
13
- * `domainAllowList`; if one exists, an over-long list fails the create call
14
- * loudly, which is the outcome we want.
15
- */
16
- export declare function formatDaytonaDomainAllowList(hosts: string[]): string;
@@ -1,11 +0,0 @@
1
- // src/daytona-allow-list.ts
2
- function formatDaytonaAllowList(cidrs) {
3
- return cidrs.slice(0, 10).join(",");
4
- }
5
- function formatDaytonaDomainAllowList(hosts) {
6
- return [...new Set(hosts)].join(",");
7
- }
8
- export {
9
- formatDaytonaAllowList,
10
- formatDaytonaDomainAllowList
11
- };
@@ -1,52 +0,0 @@
1
- import type { SandboxDriver, SandboxDriverEnsureInput } from "..";
2
- export type BoxApiBox = {
3
- id: string;
4
- name?: string;
5
- state: string;
6
- url?: string | null;
7
- ip?: string | null;
8
- subdomain?: string | null;
9
- createdAt?: string | null;
10
- };
11
- export type BoxCommandResult = {
12
- stdout?: string;
13
- stderr?: string;
14
- exitCode?: number;
15
- };
16
- export type BoxFetch = (input: string, init?: RequestInit) => Promise<Response>;
17
- export type BoxSandboxDriverOptions = {
18
- apiKey: string;
19
- baseUrl?: string;
20
- image?: string;
21
- runtimePort?: number;
22
- runtimeCommand?: string;
23
- workspaceDir?: string;
24
- runner?: string;
25
- /** Container name used for the runtime container inside the box. */
26
- containerName?: string;
27
- /**
28
- * Box auto-archives after this many seconds of inactivity. `null` (default)
29
- * disables auto-archival so the SandboxManager owns the lifecycle; a number
30
- * lets Box reclaim idle boxes to save cost.
31
- */
32
- ttlSeconds?: number | null;
33
- /**
34
- * Optional `docker login` before the pull, for a private runtime registry
35
- * (the default image lives on ghcr.io). Omit for public images.
36
- */
37
- registryAuth?: {
38
- server: string;
39
- username: string;
40
- password: string;
41
- };
42
- env?: (input: SandboxDriverEnsureInput, host: {
43
- id: string;
44
- }) => Record<string, string> | Promise<Record<string, string>>;
45
- provisionTimeoutMs?: number;
46
- provisionIntervalMs?: number;
47
- healthTimeoutMs?: number;
48
- healthIntervalMs?: number;
49
- operationTimeoutMs?: number;
50
- fetchImpl?: BoxFetch;
51
- };
52
- export declare function createBoxSandboxDriver(options: BoxSandboxDriverOptions): SandboxDriver;