@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.
- package/README.md +233 -26
- 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 +42 -7
- package/dist/driver-catalog.mjs +115 -161
- 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 +3 -3
- package/dist/drivers/cloudflare.mjs +208 -147
- package/dist/drivers/docker.d.ts +4 -3
- package/dist/drivers/docker.mjs +154 -126
- package/dist/drivers/local-brokering.d.ts +14 -0
- package/dist/drivers/local-brokering.mjs +1029 -0
- package/dist/drivers/modal.d.ts +2 -2
- package/dist/drivers/modal.mjs +121 -111
- package/dist/drivers/transient-error.d.ts +2 -0
- package/dist/drivers/vercel.d.ts +3 -2
- package/dist/drivers/vercel.mjs +151 -115
- 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 +9 -2
- package/dist/hosted-network-policy.mjs +218 -1
- package/dist/image-name.d.ts +12 -2
- package/dist/image-name.mjs +14 -5
- package/dist/image.d.ts +1 -20
- package/dist/image.mjs +11 -88
- package/dist/index.d.ts +8 -578
- package/dist/index.mjs +1379 -320
- 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 +4 -3
- package/dist/manager.d.ts +2 -0
- package/dist/runtime-env.d.ts +4 -2
- package/dist/runtime-env.mjs +37 -2
- 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 +0 -1
- package/dist/service-url-exposure.mjs +0 -9
- 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 +63 -52
- package/package.json +26 -42
- 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 -574
- package/dist/drivers/daytona.d.ts +0 -163
- package/dist/drivers/daytona.mjs +0 -683
- package/dist/drivers/exe.d.ts +0 -26
- package/dist/drivers/exe.mjs +0 -492
- package/dist/drivers/fetch-bridge.d.ts +0 -10
- package/dist/drivers/fetch-bridge.mjs +0 -126
- package/dist/lease-policy.d.ts +0 -52
- 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
|
|
255
|
+
return publicRepoHost(source.repoUrl);
|
|
39
256
|
}
|
|
40
257
|
function hostedSandboxNetworkPolicy(input) {
|
|
41
258
|
const hosts = /* @__PURE__ */ new Set();
|
package/dist/image-name.d.ts
CHANGED
|
@@ -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
|
|
7
|
-
|
|
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;
|
package/dist/image-name.mjs
CHANGED
|
@@ -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
|
-
|
|
22
|
-
|
|
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
|
|
25
|
-
return
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
51
|
+
assertSandboxImageReference,
|
|
127
52
|
defaultSandboxImage,
|
|
128
|
-
defaultSnapshotName,
|
|
129
|
-
ensureSnapshot,
|
|
130
53
|
snapshotVersion
|
|
131
54
|
};
|