@claxedo/sandbox-manager 0.9.0 → 0.10.1

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,3 +1,220 @@
1
+ // ../sandbox-contract/dist/index.mjs
2
+ function ipv4Number(part) {
3
+ let value;
4
+ if (/^0[xX][0-9a-fA-F]+$/.test(part))
5
+ value = Number.parseInt(part.slice(2), 16);
6
+ else if (/^0[0-7]+$/.test(part))
7
+ value = Number.parseInt(part.slice(1), 8);
8
+ else if (/^\d+$/.test(part))
9
+ value = Number.parseInt(part, 10);
10
+ else
11
+ return;
12
+ return Number.isSafeInteger(value) ? value : void 0;
13
+ }
14
+ function parseIpv4(input) {
15
+ const parts = input.split(".");
16
+ if (parts.length > 1 && parts[parts.length - 1] === "")
17
+ parts.pop();
18
+ if (parts.length < 1 || parts.length > 4)
19
+ return;
20
+ const numbers = [];
21
+ for (const part of parts) {
22
+ const value = ipv4Number(part);
23
+ if (value === void 0)
24
+ return;
25
+ numbers.push(value);
26
+ }
27
+ const last = numbers[numbers.length - 1];
28
+ if (last >= 256 ** (5 - numbers.length))
29
+ return;
30
+ for (const value of numbers.slice(0, -1))
31
+ if (value > 255)
32
+ return;
33
+ let address = last;
34
+ for (let i = 0; i < numbers.length - 1; i++)
35
+ address += numbers[i] * 256 ** (3 - i);
36
+ return address >>> 0;
37
+ }
38
+ function hextet(part) {
39
+ return /^[0-9a-fA-F]{1,4}$/.test(part) ? Number.parseInt(part, 16) : void 0;
40
+ }
41
+ function embeddedQuad(input) {
42
+ const parts = input.split(".");
43
+ if (parts.length !== 4)
44
+ return;
45
+ const octets = [];
46
+ for (const part of parts) {
47
+ if (!/^\d{1,3}$/.test(part))
48
+ return;
49
+ const value = Number.parseInt(part, 10);
50
+ if (value > 255)
51
+ return;
52
+ octets.push(value);
53
+ }
54
+ return octets;
55
+ }
56
+ function parseIpv6(input) {
57
+ let body = input;
58
+ let tail;
59
+ const lastColon = body.lastIndexOf(":");
60
+ if (lastColon >= 0 && body.includes(".")) {
61
+ tail = embeddedQuad(body.slice(lastColon + 1));
62
+ if (!tail)
63
+ return;
64
+ body = body.slice(0, lastColon + 1) + "0:0";
65
+ }
66
+ if ((body.match(/::/g) ?? []).length > 1)
67
+ return;
68
+ let halves;
69
+ if (body.includes("::")) {
70
+ const [left = "", right = ""] = body.split("::");
71
+ const leftParts = left === "" ? [] : left.split(":");
72
+ const rightParts = right === "" ? [] : right.split(":");
73
+ const missing = 8 - leftParts.length - rightParts.length;
74
+ if (missing < 1)
75
+ return;
76
+ halves = [...leftParts, ...Array.from({ length: missing }, () => "0"), ...rightParts];
77
+ } else {
78
+ halves = body.split(":");
79
+ if (halves.length !== 8)
80
+ return;
81
+ }
82
+ if (halves.length !== 8)
83
+ return;
84
+ const segments = [];
85
+ for (const part of halves) {
86
+ const value = hextet(part);
87
+ if (value === void 0)
88
+ return;
89
+ segments.push(value);
90
+ }
91
+ if (tail) {
92
+ segments[6] = tail[0] * 256 + tail[1];
93
+ segments[7] = tail[2] * 256 + tail[3];
94
+ }
95
+ return segments;
96
+ }
97
+ function parseIpAddress(input) {
98
+ if (!input)
99
+ return;
100
+ let host = input.trim().toLowerCase();
101
+ if (host.startsWith("[") && host.endsWith("]") && host.includes(":"))
102
+ host = host.slice(1, -1);
103
+ if (host.includes(":")) {
104
+ const segments = parseIpv6(host);
105
+ return segments ? { kind: "v6", segments } : void 0;
106
+ }
107
+ const value = parseIpv4(host);
108
+ return value === void 0 ? void 0 : { kind: "v4", value };
109
+ }
110
+ var V4_PRIVATE = [
111
+ [0, 8],
112
+ [167772160, 8],
113
+ [1681915904, 10],
114
+ [2130706432, 8],
115
+ [2851995648, 16],
116
+ [2886729728, 12],
117
+ [3221225472, 24],
118
+ [3221225984, 24],
119
+ [3223307264, 24],
120
+ [3224682752, 24],
121
+ [3227017984, 24],
122
+ [3232235520, 16],
123
+ [3232706560, 24],
124
+ [3323068416, 15],
125
+ [3325256704, 24],
126
+ [3405803776, 24],
127
+ [3758096384, 4],
128
+ [4026531840, 4]
129
+ ];
130
+ function inCidr(value, base, bits) {
131
+ return (value ^ base) >>> 32 - bits === 0;
132
+ }
133
+ function isPrivateIpv4(value) {
134
+ return V4_PRIVATE.some(([base, bits]) => inCidr(value, base, bits));
135
+ }
136
+ function embeddedIpv4(segments) {
137
+ const [a, b, c, d, e, f, g, h] = segments;
138
+ if (a === 0 && b === 0 && c === 0 && d === 0 && e === 0) {
139
+ if (f === 65535 || f === 0)
140
+ return g * 65536 + h >>> 0;
141
+ }
142
+ if (a === 100 && b === 65435 && c === 0 && d === 0 && e === 0 && f === 0)
143
+ return g * 65536 + h >>> 0;
144
+ if (a === 8194)
145
+ return b * 65536 + c >>> 0;
146
+ if (a === 8193 && b === 0)
147
+ return (g ^ 65535) * 65536 + (h ^ 65535) >>> 0;
148
+ return;
149
+ }
150
+ function isUnspecified(segments) {
151
+ return segments.every((segment) => segment === 0);
152
+ }
153
+ function isIpv6Loopback(segments) {
154
+ return segments.slice(0, 7).every((segment) => segment === 0) && segments[7] === 1;
155
+ }
156
+ function isPrivateIpAddress(ip) {
157
+ if (ip.kind === "v4")
158
+ return isPrivateIpv4(ip.value);
159
+ const segments = ip.segments;
160
+ if (isUnspecified(segments) || isIpv6Loopback(segments))
161
+ return true;
162
+ const first = segments[0];
163
+ if ((first & 65280) === 65280)
164
+ return true;
165
+ if ((first & 65024) === 64512)
166
+ return true;
167
+ if ((first & 65472) === 65152)
168
+ return true;
169
+ if ((first & 65472) === 65216)
170
+ return true;
171
+ if (first === 256 && segments[1] === 0 && segments[2] === 0 && segments[3] === 0)
172
+ return true;
173
+ if (first === 8193 && segments[1] === 3512)
174
+ return true;
175
+ const embedded = embeddedIpv4(segments);
176
+ if (embedded !== void 0)
177
+ return isPrivateIpv4(embedded);
178
+ return false;
179
+ }
180
+ function safeRepoUrl(input) {
181
+ try {
182
+ const url = new URL(input);
183
+ return url.protocol === "https:" || url.protocol === "http:" || url.protocol === "ssh:" ? input : void 0;
184
+ } catch {
185
+ return /^[\w.-]+@[\w.-]+:[\w./-]+$/.test(input) ? input : void 0;
186
+ }
187
+ }
188
+ function repoUrlHost(input) {
189
+ if (!safeRepoUrl(input))
190
+ return;
191
+ try {
192
+ const host = new URL(input).hostname;
193
+ return host || void 0;
194
+ } catch {
195
+ return /^[\w.-]+@([\w.-]+):[\w./-]+$/.exec(input)?.[1]?.toLowerCase();
196
+ }
197
+ }
198
+ function bareRepoHost(host) {
199
+ const bare = host.trim().toLowerCase();
200
+ return bare.startsWith("[") && bare.endsWith("]") && bare.includes(":") ? bare.slice(1, -1) : bare;
201
+ }
202
+ function loopbackRepoName(bare) {
203
+ return bare === "localhost" || bare.endsWith(".localhost");
204
+ }
205
+ function publicRepoHost(repoUrl) {
206
+ const host = repoUrlHost(repoUrl);
207
+ if (!host)
208
+ return;
209
+ const bare = bareRepoHost(host);
210
+ if (loopbackRepoName(bare))
211
+ return;
212
+ const ip = parseIpAddress(bare);
213
+ if (ip && isPrivateIpAddress(ip))
214
+ return;
215
+ return host;
216
+ }
217
+
1
218
  // src/hosted-network-policy.ts
2
219
  var SANDBOX_MODEL_PROVIDER_HOSTS = [
3
220
  "api.anthropic.com",
@@ -35,7 +252,7 @@ function hostOf(value) {
35
252
  }
36
253
  function sandboxSourceHost(source) {
37
254
  if (!source || source.kind !== "git") return void 0;
38
- return hostOf(source.repoUrl);
255
+ return publicRepoHost(source.repoUrl);
39
256
  }
40
257
  function hostedSandboxNetworkPolicy(input) {
41
258
  const hosts = /* @__PURE__ */ new Set();
@@ -2,6 +2,16 @@ export declare const SNAPSHOT_SCHEMA_VERSION = 8;
2
2
  export declare const DEFAULT_SANDBOX_IMAGE_REPOSITORY = "ghcr.io/kyashrathore/claxedo-sandbox";
3
3
  export type SandboxImageEnv = Record<string, string | undefined>;
4
4
  export declare function snapshotVersion(value: string): string;
5
+ /**
6
+ * A caller-supplied image string lands verbatim inside provider argv or
7
+ * commands (`docker create` argv, `docker run` inside a Boat VM). A value that
8
+ * parses as an option — a leading `-` — or that splits into multiple words
9
+ * would be consumed as provider flags, not as an image reference.
10
+ */
11
+ export declare function assertSandboxImageReference(image: string): string;
5
12
  export declare function sandboxImageRepository(env?: SandboxImageEnv): string;
6
- export declare function defaultSandboxImage(version?: string, buildId?: string, env?: SandboxImageEnv): string;
7
- export declare function defaultSnapshotName(version?: string, buildId?: string, env?: SandboxImageEnv): string;
13
+ export declare function defaultSandboxImage(version?: string, buildId?: string, env?: SandboxImageEnv, options?: {
14
+ agentPlugins?: boolean;
15
+ }): string;
16
+ /** Whether an image's tag names a build that carries the Agent Plugins runtime entry. */
17
+ export declare function sandboxImageCarriesAgentPlugins(image: string): boolean;
@@ -10,6 +10,12 @@ var DEFAULT_SANDBOX_IMAGE_REPOSITORY = "ghcr.io/kyashrathore/claxedo-sandbox";
10
10
  function snapshotVersion(value) {
11
11
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
12
12
  }
13
+ function assertSandboxImageReference(image) {
14
+ if (!image || image !== image.trim() || image.startsWith("-") || /\s/.test(image)) {
15
+ throw new Error(`sandbox image reference is not a legal image identifier: ${JSON.stringify(image)}`);
16
+ }
17
+ return image;
18
+ }
13
19
  function sandboxImageRepository(env = process.env) {
14
20
  const repository = env.CLAXEDO_SANDBOX_IMAGE_REPOSITORY?.trim();
15
21
  return repository ? repository : DEFAULT_SANDBOX_IMAGE_REPOSITORY;
@@ -18,17 +24,20 @@ function buildIdSuffix(buildId, env) {
18
24
  const id = (buildId ?? env.CLAXEDO_SANDBOX_BUILD_ID)?.trim();
19
25
  return id ? `-${snapshotVersion(id)}` : "";
20
26
  }
21
- function defaultSandboxImage(version = workspaceRuntimeVersion(), buildId, env = process.env) {
22
- return `${sandboxImageRepository(env)}:workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
27
+ var AGENT_PLUGINS_TAG = "-agent-plugins";
28
+ function defaultSandboxImage(version = workspaceRuntimeVersion(), buildId, env = process.env, options = {}) {
29
+ const feature = options.agentPlugins ? AGENT_PLUGINS_TAG : "";
30
+ return `${sandboxImageRepository(env)}:workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}${feature}-v${SNAPSHOT_SCHEMA_VERSION}`;
23
31
  }
24
- function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env = process.env) {
25
- return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
32
+ function sandboxImageCarriesAgentPlugins(image) {
33
+ return new RegExp(`${AGENT_PLUGINS_TAG}-v\\d+$`).test(image);
26
34
  }
27
35
  export {
28
36
  DEFAULT_SANDBOX_IMAGE_REPOSITORY,
29
37
  SNAPSHOT_SCHEMA_VERSION,
38
+ assertSandboxImageReference,
30
39
  defaultSandboxImage,
31
- defaultSnapshotName,
40
+ sandboxImageCarriesAgentPlugins,
32
41
  sandboxImageRepository,
33
42
  snapshotVersion
34
43
  };
package/dist/image.d.ts CHANGED
@@ -1,23 +1,4 @@
1
- import type { Daytona, Image } from "@daytona/sdk";
2
1
  export { RUNTIME_DIR, WORKSPACE_RUNTIME_PORT, WORKSPACE_DIR } from "./defaults";
3
- export { SNAPSHOT_SCHEMA_VERSION, snapshotVersion, defaultSandboxImage, defaultSnapshotName, } from "./image-name";
4
- /**
5
- * Sink for snapshot-build progress. Every message here reports a long
6
- * operation an operator is actively waiting on — a snapshot build takes
7
- * minutes, and the Daytona builder's `onLogs` stream is the only view into
8
- * whether it is progressing or wedged.
9
- *
10
- * Defaults to `console.info`, deliberately unconditional: the same reasoning as
11
- * `onEgressUnenforced` in index.ts — someone who never wires a sink still sees
12
- * the build. Pass your own to route it into a logger; pass `() => {}` only if
13
- * you have another way to observe the build.
14
- */
15
- export type SandboxImageLogSink = (message: string, context?: Record<string, unknown>) => void;
16
- export type SandboxImageOptions = {
17
- log?: SandboxImageLogSink;
18
- };
2
+ export { SNAPSHOT_SCHEMA_VERSION, snapshotVersion, assertSandboxImageReference, defaultSandboxImage, } from "./image-name";
19
3
  export declare const SANDBOX_IMAGE_REPOSITORY: string;
20
4
  export declare const SANDBOX_IMAGE: string;
21
- export declare const SNAPSHOT_NAME: string;
22
- export declare function buildSandboxImage(): Promise<Image>;
23
- export declare function ensureSnapshot(daytona: Daytona, options?: SandboxImageOptions): Promise<string>;
package/dist/image.mjs CHANGED
@@ -18,6 +18,12 @@ var DEFAULT_SANDBOX_IMAGE_REPOSITORY = "ghcr.io/kyashrathore/claxedo-sandbox";
18
18
  function snapshotVersion(value) {
19
19
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
20
20
  }
21
+ function assertSandboxImageReference(image) {
22
+ if (!image || image !== image.trim() || image.startsWith("-") || /\s/.test(image)) {
23
+ throw new Error(`sandbox image reference is not a legal image identifier: ${JSON.stringify(image)}`);
24
+ }
25
+ return image;
26
+ }
21
27
  function sandboxImageRepository(env = process.env) {
22
28
  const repository = env.CLAXEDO_SANDBOX_IMAGE_REPOSITORY?.trim();
23
29
  return repository ? repository : DEFAULT_SANDBOX_IMAGE_REPOSITORY;
@@ -26,106 +32,23 @@ function buildIdSuffix(buildId, env) {
26
32
  const id = (buildId ?? env.CLAXEDO_SANDBOX_BUILD_ID)?.trim();
27
33
  return id ? `-${snapshotVersion(id)}` : "";
28
34
  }
29
- function defaultSandboxImage(version = workspaceRuntimeVersion(), buildId, env = process.env) {
30
- return `${sandboxImageRepository(env)}:workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
31
- }
32
- function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env = process.env) {
33
- return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
34
- }
35
-
36
- // ../claxedo-helpers/dist/guards.mjs
37
- function isRecord(value) {
38
- return value !== null && typeof value === "object" && !Array.isArray(value);
39
- }
40
- function asRecord(value) {
41
- return isRecord(value) ? value : void 0;
42
- }
43
-
44
- // src/json.ts
45
- function text(input) {
46
- return typeof input === "string" && input.length > 0 ? input : void 0;
35
+ var AGENT_PLUGINS_TAG = "-agent-plugins";
36
+ function defaultSandboxImage(version = workspaceRuntimeVersion(), buildId, env = process.env, options = {}) {
37
+ const feature = options.agentPlugins ? AGENT_PLUGINS_TAG : "";
38
+ return `${sandboxImageRepository(env)}:workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}${feature}-v${SNAPSHOT_SCHEMA_VERSION}`;
47
39
  }
48
40
 
49
41
  // src/image.ts
50
- function defaultImageLogSink(message, context) {
51
- if (context) console.info(message, context);
52
- else console.info(message);
53
- }
54
42
  var SANDBOX_IMAGE_REPOSITORY = sandboxImageRepository();
55
43
  var SANDBOX_IMAGE = process.env.CLAXEDO_SANDBOX_IMAGE || defaultSandboxImage();
56
- var SNAPSHOT_NAME = process.env.CLAXEDO_SNAPSHOT_NAME || defaultSnapshotName();
57
- async function buildSandboxImage() {
58
- const { Image } = await import("@daytona/sdk");
59
- return Image.base(SANDBOX_IMAGE);
60
- }
61
- function snapshotUsable(state) {
62
- return state === "ready" || state === "active";
63
- }
64
- function snapshotView(input) {
65
- const row = asRecord(input) ?? {};
66
- return { id: text(row.id), state: text(row.state), errorReason: text(row.errorReason) };
67
- }
68
- async function activateSnapshot(snapshots, id) {
69
- const activate = asRecord(snapshots)?.activate;
70
- if (typeof activate !== "function") throw new Error("Daytona SDK does not support snapshot activation");
71
- await activate.call(snapshots, { id });
72
- }
73
- async function createSnapshot(daytona, log) {
74
- log("Creating sandbox snapshot (this may take a few minutes)...", { name: SNAPSHOT_NAME });
75
- await daytona.snapshot.create(
76
- { name: SNAPSHOT_NAME, image: await buildSandboxImage() },
77
- {
78
- timeout: 120,
79
- onLogs: (chunk) => log("[snapshot]", { text: chunk })
80
- }
81
- );
82
- return SNAPSHOT_NAME;
83
- }
84
- async function ensureSnapshot(daytona, options = {}) {
85
- const log = options.log ?? defaultImageLogSink;
86
- try {
87
- const existing = snapshotView(await daytona.snapshot.get(SNAPSHOT_NAME));
88
- if (existing.state === "inactive") {
89
- log("Snapshot is inactive, reactivating...", { name: SNAPSHOT_NAME, id: existing.id });
90
- if (!existing.id) throw new Error("Daytona snapshot is inactive but reports no id to activate");
91
- await activateSnapshot(daytona.snapshot, existing.id);
92
- for (let i = 0; i < 120; i++) {
93
- const snap = snapshotView(await daytona.snapshot.get(SNAPSHOT_NAME));
94
- if (snapshotUsable(snap.state)) return SNAPSHOT_NAME;
95
- if (snap.state === "error") throw new Error(`Snapshot activation failed: ${snap.errorReason ?? "unknown"}`);
96
- await new Promise((resolve) => setTimeout(resolve, 2e3));
97
- }
98
- throw new Error("Snapshot activation timed out");
99
- }
100
- if (existing.state === "building") {
101
- log("Snapshot is building, waiting (up to 10 min)...", { name: SNAPSHOT_NAME });
102
- for (let i = 0; i < 300; i++) {
103
- const snap = snapshotView(await daytona.snapshot.get(SNAPSHOT_NAME));
104
- if (snapshotUsable(snap.state)) return SNAPSHOT_NAME;
105
- if (snap.state === "error") throw new Error(`Snapshot build failed: ${snap.errorReason ?? "unknown"}`);
106
- if (i % 15 === 0) log("Still waiting for snapshot...", { state: snap.state, elapsed: `${i * 2}s` });
107
- await new Promise((resolve) => setTimeout(resolve, 2e3));
108
- }
109
- throw new Error("Snapshot build timed out after 10 minutes");
110
- }
111
- return SNAPSHOT_NAME;
112
- } catch (err) {
113
- const { DaytonaNotFoundError } = await import("@daytona/sdk");
114
- if (!(err instanceof DaytonaNotFoundError)) throw err;
115
- }
116
- return createSnapshot(daytona, log);
117
- }
118
44
  export {
119
45
  RUNTIME_DIR,
120
46
  SANDBOX_IMAGE,
121
47
  SANDBOX_IMAGE_REPOSITORY,
122
- SNAPSHOT_NAME,
123
48
  SNAPSHOT_SCHEMA_VERSION,
124
49
  WORKSPACE_DIR,
125
50
  WORKSPACE_RUNTIME_PORT,
126
- buildSandboxImage,
51
+ assertSandboxImageReference,
127
52
  defaultSandboxImage,
128
- defaultSnapshotName,
129
- ensureSnapshot,
130
53
  snapshotVersion
131
54
  };