@claxedo/sandbox-manager 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,31 @@ import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { promisify } from "node:util";
7
7
 
8
+ // ../claxedo-helpers/dist/string.mjs
9
+ function isString(value) {
10
+ return typeof value === "string";
11
+ }
12
+ function trimToUndefined(value) {
13
+ if (!isString(value))
14
+ return;
15
+ const trimmed = value.trim();
16
+ return trimmed.length > 0 ? trimmed : void 0;
17
+ }
18
+
8
19
  // src/runtime-env.ts
20
+ function workspaceRuntimeBootEnv(input) {
21
+ const env = {
22
+ ...workspaceRuntimeTargetEnv(input),
23
+ ...workspaceRuntimeSourceEnv(input),
24
+ ...input.env
25
+ };
26
+ if (input.runner) env.WORKSPACE_RUNTIME_RUNNER = input.runner;
27
+ if (input.controlEnv?.relayJwksUrl) env.WORKSPACE_RUNTIME_RELAY_JWKS_URL = input.controlEnv.relayJwksUrl;
28
+ if (input.controlEnv?.relayVerifyPem) env.WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM = input.controlEnv.relayVerifyPem;
29
+ if (input.controlEnv?.managementJwksUrl) env.WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL = input.controlEnv.managementJwksUrl;
30
+ if (input.controlEnv?.sessionAuthorityUrl) env.WORKSPACE_RUNTIME_SESSION_AUTHORITY_URL = input.controlEnv.sessionAuthorityUrl;
31
+ return env;
32
+ }
9
33
  function workspaceRuntimeTargetEnv(input) {
10
34
  return {
11
35
  WORKSPACE_RUNTIME_WORKSPACE_ID: input.workspaceId,
@@ -32,9 +56,6 @@ function shell(input) {
32
56
  // src/constants.ts
33
57
  var DEFAULT_WORKSPACE_RUNTIME_PORT = 2593;
34
58
 
35
- // src/image.ts
36
- import { Image, DaytonaNotFoundError } from "@daytona/sdk";
37
-
38
59
  // src/runtime-version.ts
39
60
  var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
40
61
  function workspaceRuntimeVersion() {
@@ -62,6 +83,16 @@ function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env =
62
83
  return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
63
84
  }
64
85
 
86
+ // ../claxedo-helpers/dist/guards.mjs
87
+ function isRecord(value) {
88
+ return value !== null && typeof value === "object" && !Array.isArray(value);
89
+ }
90
+
91
+ // src/json.ts
92
+ function num(input) {
93
+ return typeof input === "number" ? input : void 0;
94
+ }
95
+
65
96
  // src/image.ts
66
97
  var SANDBOX_IMAGE_REPOSITORY = sandboxImageRepository();
67
98
  var SANDBOX_IMAGE = process.env.CLAXEDO_SANDBOX_IMAGE || defaultSandboxImage();
@@ -93,12 +124,42 @@ var SANDBOX_BASELINE_EGRESS_HOSTS = [
93
124
  ...SANDBOX_PACKAGE_REGISTRY_HOSTS
94
125
  ];
95
126
 
127
+ // ../sandbox-contract/dist/index.mjs
128
+ var sandboxDriverCredentialFields = {
129
+ exe: [{ key: "api_token", label: "API Token", secret: true }],
130
+ daytona: [{ key: "api_key", label: "API Key", secret: true }],
131
+ modal: [
132
+ { key: "token_id", label: "Token ID", secret: true },
133
+ { key: "token_secret", label: "Token Secret", secret: true }
134
+ ],
135
+ vercel: [
136
+ { key: "access_token", label: "Access Token", secret: true },
137
+ { key: "team_id", label: "Team ID" },
138
+ { key: "project_id", label: "Project ID" }
139
+ ],
140
+ cloudflare: [
141
+ { key: "api_token", label: "API Token", secret: true },
142
+ { key: "worker_url", label: "Worker URL" }
143
+ ],
144
+ box: [{ key: "api_key", label: "API Key", secret: true }],
145
+ docker: [{ key: "image", label: "Image" }]
146
+ };
147
+ var sandboxDriverLabels = {
148
+ exe: "exe.dev",
149
+ daytona: "Daytona",
150
+ modal: "Modal",
151
+ vercel: "Vercel",
152
+ cloudflare: "Cloudflare",
153
+ box: "Box",
154
+ docker: "Docker"
155
+ };
156
+
96
157
  // src/driver-catalog.ts
97
158
  var sandboxDriverCatalog = {
98
159
  exe: {
99
160
  id: "exe",
100
- label: "exe.dev",
101
- credentialFields: [{ key: "api_token", label: "API Token", secret: true }],
161
+ label: sandboxDriverLabels.exe,
162
+ credentialFields: sandboxDriverCredentialFields.exe,
102
163
  metadata: {
103
164
  driverRunsIn: ["worker", "node"],
104
165
  hostStopBehavior: "not-supported",
@@ -118,17 +179,15 @@ var sandboxDriverCatalog = {
118
179
  },
119
180
  daytona: {
120
181
  id: "daytona",
121
- label: "Daytona",
122
- credentialFields: [{ key: "api_key", label: "API Key", secret: true }],
182
+ label: sandboxDriverLabels.daytona,
183
+ credentialFields: sandboxDriverCredentialFields.daytona,
123
184
  metadata: {
124
185
  driverRunsIn: ["worker", "node"],
125
186
  hostStopBehavior: "suspends-host",
126
187
  hostResumeBehavior: "same-host",
127
188
  targetAccess: "relay",
128
189
  secretBrokering: "native",
129
- // Daytona is the only driver that can filter egress by NAME as well as
130
- // by address: `domainAllowList` (names) alongside `networkAllowList`
131
- // (CIDRs), with `networkBlockAll` as the deny-all floor.
190
+ // The driver translates names to domainAllowList, or addresses to networkAllowList.
132
191
  egressControl: "hosts-and-cidrs",
133
192
  persistence: {
134
193
  resume: "same-sandbox",
@@ -142,20 +201,15 @@ var sandboxDriverCatalog = {
142
201
  },
143
202
  modal: {
144
203
  id: "modal",
145
- label: "Modal",
146
- credentialFields: [
147
- { key: "token_id", label: "Token ID", secret: true },
148
- { key: "token_secret", label: "Token Secret", secret: true }
149
- ],
204
+ label: sandboxDriverLabels.modal,
205
+ credentialFields: sandboxDriverCredentialFields.modal,
150
206
  metadata: {
151
207
  driverRunsIn: ["node"],
152
208
  hostStopBehavior: "terminates-host",
153
209
  hostResumeBehavior: "replacement-host",
154
210
  targetAccess: "relay",
155
211
  secretBrokering: "none",
156
- // Modal can cut the network entirely (`blockNetwork`) but cannot express
157
- // an allowlist, and its driver throws on a host policy. A blackout is
158
- // not containment for a workspace that has to clone and reach a model.
212
+ // The driver does not install Modal's domain allowlist or alpha sidecar.
159
213
  egressControl: "none",
160
214
  persistence: {
161
215
  resume: "replacement-restore",
@@ -169,20 +223,15 @@ var sandboxDriverCatalog = {
169
223
  },
170
224
  vercel: {
171
225
  id: "vercel",
172
- label: "Vercel",
173
- credentialFields: [
174
- { key: "access_token", label: "Access Token", secret: true },
175
- { key: "team_id", label: "Team ID" },
176
- { key: "project_id", label: "Project ID" }
177
- ],
226
+ label: sandboxDriverLabels.vercel,
227
+ credentialFields: sandboxDriverCredentialFields.vercel,
178
228
  metadata: {
179
229
  driverRunsIn: ["node"],
180
230
  hostStopBehavior: "terminates-host",
181
231
  hostResumeBehavior: "replacement-host",
182
232
  targetAccess: "relay",
183
233
  secretBrokering: "native",
184
- // Vercel's sandbox firewall takes a hostname allow list (or "deny-all"),
185
- // never CIDRs.
234
+ // The driver translates hosts only; the SDK's subnet rules are not wired to net.cidrs.
186
235
  egressControl: "hosts",
187
236
  persistence: {
188
237
  resume: "replacement-restore",
@@ -196,23 +245,15 @@ var sandboxDriverCatalog = {
196
245
  },
197
246
  cloudflare: {
198
247
  id: "cloudflare",
199
- label: "Cloudflare",
200
- credentialFields: [
201
- { key: "api_token", label: "API Token", secret: true },
202
- { key: "worker_url", label: "Worker URL" }
203
- ],
248
+ label: sandboxDriverLabels.cloudflare,
249
+ credentialFields: sandboxDriverCredentialFields.cloudflare,
204
250
  metadata: {
205
251
  driverRunsIn: ["worker"],
206
252
  hostStopBehavior: "not-supported",
207
253
  hostResumeBehavior: "same-host",
208
254
  targetAccess: "relay",
209
- secretBrokering: "proxy",
210
- // The Cloudflare Sandbox Worker exposes no egress filter: its broker
211
- // (drivers/cloudflare-egress.ts) is an OPT-IN proxy the sandbox chooses
212
- // to route brokered-credential requests through, not a boundary. The
213
- // driver therefore drops `net` entirely — which is exactly why this
214
- // must be declared, so the manager refuses instead of provisioning a
215
- // sandbox the caller believes is contained.
255
+ secretBrokering: "native",
256
+ // Credential handlers do not restrict unrelated destinations.
216
257
  egressControl: "none",
217
258
  persistence: {
218
259
  resume: "replacement-restore",
@@ -226,8 +267,8 @@ var sandboxDriverCatalog = {
226
267
  },
227
268
  box: {
228
269
  id: "box",
229
- label: "Box",
230
- credentialFields: [{ key: "api_key", label: "API Key", secret: true }],
270
+ label: sandboxDriverLabels.box,
271
+ credentialFields: sandboxDriverCredentialFields.box,
231
272
  metadata: {
232
273
  driverRunsIn: ["node"],
233
274
  hostStopBehavior: "suspends-host",
@@ -247,8 +288,8 @@ var sandboxDriverCatalog = {
247
288
  },
248
289
  docker: {
249
290
  id: "docker",
250
- label: "Docker",
251
- credentialFields: [{ key: "image", label: "Image" }],
291
+ label: sandboxDriverLabels.docker,
292
+ credentialFields: sandboxDriverCredentialFields.docker,
252
293
  metadata: {
253
294
  driverRunsIn: ["local"],
254
295
  hostStopBehavior: "terminates-host",
@@ -268,6 +309,24 @@ var sandboxDriverCatalog = {
268
309
  }
269
310
  };
270
311
 
312
+ // src/drivers/transient-error.ts
313
+ function textOf(input) {
314
+ return typeof input === "string" ? input : "";
315
+ }
316
+ function driverErrorSignals(err) {
317
+ const shaped = isRecord(err) ? err : {};
318
+ const nested = isRecord(shaped.response) ? shaped.response : {};
319
+ return {
320
+ status: num(nested.status) ?? num(shaped.status),
321
+ text: `${textOf(shaped.code)} ${textOf(shaped.name)} ${textOf(shaped.message)}`.toLowerCase()
322
+ };
323
+ }
324
+ function isTransientDriverError(err, markers) {
325
+ const { status, text: text2 } = driverErrorSignals(err);
326
+ if (status !== void 0 && status >= 500) return true;
327
+ return markers.some((marker) => text2.includes(marker));
328
+ }
329
+
271
330
  // src/drivers/docker.ts
272
331
  var execFileAsync = promisify(execFile);
273
332
  var DEFAULT_OPERATION_TIMEOUT_MS = 6e4;
@@ -278,13 +337,13 @@ var DEFAULT_WORKSPACE_DIR = "/workspace";
278
337
  var DOCKER_LABEL = "claxedo";
279
338
  var AUTH_STAGE_DIR = "/tmp/claxedo-auth";
280
339
  function dockerSandboxSyncLocalAuth(env = process.env) {
281
- return !disabled(env.CLAXEDO_DOCKER_SANDBOX_SYNC_LOCAL_AUTH);
340
+ return enabledFlag(env.CLAXEDO_DOCKER_SANDBOX_SYNC_LOCAL_AUTH);
282
341
  }
283
342
  function dockerSandboxAuthHome(env = process.env) {
284
- return clean(env.CLAXEDO_DOCKER_SANDBOX_AUTH_HOME) ?? clean(env.HOME) ?? os.homedir();
343
+ return trimToUndefined(env.CLAXEDO_DOCKER_SANDBOX_AUTH_HOME) ?? trimToUndefined(env.HOME) ?? os.homedir();
285
344
  }
286
345
  function dockerSandboxImageFromEnv(env = process.env) {
287
- return clean(env.CLAXEDO_DOCKER_SANDBOX_IMAGE) ?? clean(env.CLAXEDO_SANDBOX_IMAGE) ?? SANDBOX_IMAGE;
346
+ return trimToUndefined(env.CLAXEDO_DOCKER_SANDBOX_IMAGE) ?? trimToUndefined(env.CLAXEDO_SANDBOX_IMAGE) ?? SANDBOX_IMAGE;
288
347
  }
289
348
  function parseDockerPort(input) {
290
349
  const line = input.trim().split(/\r?\n/)[0]?.trim();
@@ -315,12 +374,8 @@ function dockerLocalAuthCandidates(home) {
315
374
  }
316
375
  ];
317
376
  }
318
- function clean(input) {
319
- const txt = input?.trim();
320
- return txt ? txt : void 0;
321
- }
322
- function disabled(input) {
323
- return ["0", "false", "no", "off"].includes(input?.trim().toLowerCase() ?? "");
377
+ function enabledFlag(input) {
378
+ return ["1", "true", "yes", "on"].includes(input?.trim().toLowerCase() ?? "");
324
379
  }
325
380
  function nameFor(workspaceId) {
326
381
  const cleanId = workspaceId.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -383,10 +438,9 @@ function envArgs(input) {
383
438
  function labelArgs(input) {
384
439
  return Object.entries(input).flatMap(([key, value]) => ["--label", `${key}=${value}`]);
385
440
  }
441
+ var TRANSIENT_MARKERS = ["timeout", "temporarily unavailable", "connection refused"];
386
442
  function transientDriverError(err) {
387
- const shaped = err;
388
- const text = `${shaped.code ?? ""} ${shaped.message ?? ""}`.toLowerCase();
389
- return text.includes("timeout") || text.includes("temporarily unavailable") || text.includes("connection refused");
443
+ return isTransientDriverError(err, TRANSIENT_MARKERS);
390
444
  }
391
445
  function createDockerSandboxDriver(options) {
392
446
  const dockerCommand = options.docker ?? docker;
@@ -411,19 +465,16 @@ function createDockerSandboxDriver(options) {
411
465
  return input.bootSource?.kind === "image" ? input.bootSource.image : input.snapshot ?? options.image;
412
466
  }
413
467
  function bootEnv(input, hostId) {
414
- const env = {
415
- ...workspaceRuntimeTargetEnv({
416
- workspaceId: input.workspaceId,
417
- hostId,
418
- directory: workspaceDirectory(input),
419
- port: runtimePort(input),
420
- host: "0.0.0.0"
421
- }),
422
- ...workspaceRuntimeSourceEnv({ source: input.source }),
423
- ...input.env
424
- };
425
- if (options.runner) env.WORKSPACE_RUNTIME_RUNNER = options.runner;
426
- return env;
468
+ return workspaceRuntimeBootEnv({
469
+ workspaceId: input.workspaceId,
470
+ hostId,
471
+ directory: workspaceDirectory(input),
472
+ port: runtimePort(input),
473
+ host: "0.0.0.0",
474
+ source: input.source,
475
+ env: input.env,
476
+ runner: options.runner
477
+ });
427
478
  }
428
479
  async function readyTarget(input, sandboxId, hostId) {
429
480
  const out = await dockerCommand(["port", sandboxId, `${runtimePort(input)}/tcp`], timeoutMs);
@@ -59,12 +59,42 @@ function defaultSnapshotName(version = workspaceRuntimeVersion(), buildId, env =
59
59
  return `claxedo-workspace-runtime-${snapshotVersion(version)}${buildIdSuffix(buildId, env)}-v${SNAPSHOT_SCHEMA_VERSION}`;
60
60
  }
61
61
 
62
+ // ../sandbox-contract/dist/index.mjs
63
+ var sandboxDriverCredentialFields = {
64
+ exe: [{ key: "api_token", label: "API Token", secret: true }],
65
+ daytona: [{ key: "api_key", label: "API Key", secret: true }],
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
+ box: [{ key: "api_key", label: "API Key", secret: true }],
80
+ docker: [{ key: "image", label: "Image" }]
81
+ };
82
+ var sandboxDriverLabels = {
83
+ exe: "exe.dev",
84
+ daytona: "Daytona",
85
+ modal: "Modal",
86
+ vercel: "Vercel",
87
+ cloudflare: "Cloudflare",
88
+ box: "Box",
89
+ docker: "Docker"
90
+ };
91
+
62
92
  // src/driver-catalog.ts
63
93
  var sandboxDriverCatalog = {
64
94
  exe: {
65
95
  id: "exe",
66
- label: "exe.dev",
67
- credentialFields: [{ key: "api_token", label: "API Token", secret: true }],
96
+ label: sandboxDriverLabels.exe,
97
+ credentialFields: sandboxDriverCredentialFields.exe,
68
98
  metadata: {
69
99
  driverRunsIn: ["worker", "node"],
70
100
  hostStopBehavior: "not-supported",
@@ -84,17 +114,15 @@ var sandboxDriverCatalog = {
84
114
  },
85
115
  daytona: {
86
116
  id: "daytona",
87
- label: "Daytona",
88
- credentialFields: [{ key: "api_key", label: "API Key", secret: true }],
117
+ label: sandboxDriverLabels.daytona,
118
+ credentialFields: sandboxDriverCredentialFields.daytona,
89
119
  metadata: {
90
120
  driverRunsIn: ["worker", "node"],
91
121
  hostStopBehavior: "suspends-host",
92
122
  hostResumeBehavior: "same-host",
93
123
  targetAccess: "relay",
94
124
  secretBrokering: "native",
95
- // Daytona is the only driver that can filter egress by NAME as well as
96
- // by address: `domainAllowList` (names) alongside `networkAllowList`
97
- // (CIDRs), with `networkBlockAll` as the deny-all floor.
125
+ // The driver translates names to domainAllowList, or addresses to networkAllowList.
98
126
  egressControl: "hosts-and-cidrs",
99
127
  persistence: {
100
128
  resume: "same-sandbox",
@@ -108,20 +136,15 @@ var sandboxDriverCatalog = {
108
136
  },
109
137
  modal: {
110
138
  id: "modal",
111
- label: "Modal",
112
- credentialFields: [
113
- { key: "token_id", label: "Token ID", secret: true },
114
- { key: "token_secret", label: "Token Secret", secret: true }
115
- ],
139
+ label: sandboxDriverLabels.modal,
140
+ credentialFields: sandboxDriverCredentialFields.modal,
116
141
  metadata: {
117
142
  driverRunsIn: ["node"],
118
143
  hostStopBehavior: "terminates-host",
119
144
  hostResumeBehavior: "replacement-host",
120
145
  targetAccess: "relay",
121
146
  secretBrokering: "none",
122
- // Modal can cut the network entirely (`blockNetwork`) but cannot express
123
- // an allowlist, and its driver throws on a host policy. A blackout is
124
- // not containment for a workspace that has to clone and reach a model.
147
+ // The driver does not install Modal's domain allowlist or alpha sidecar.
125
148
  egressControl: "none",
126
149
  persistence: {
127
150
  resume: "replacement-restore",
@@ -135,20 +158,15 @@ var sandboxDriverCatalog = {
135
158
  },
136
159
  vercel: {
137
160
  id: "vercel",
138
- label: "Vercel",
139
- credentialFields: [
140
- { key: "access_token", label: "Access Token", secret: true },
141
- { key: "team_id", label: "Team ID" },
142
- { key: "project_id", label: "Project ID" }
143
- ],
161
+ label: sandboxDriverLabels.vercel,
162
+ credentialFields: sandboxDriverCredentialFields.vercel,
144
163
  metadata: {
145
164
  driverRunsIn: ["node"],
146
165
  hostStopBehavior: "terminates-host",
147
166
  hostResumeBehavior: "replacement-host",
148
167
  targetAccess: "relay",
149
168
  secretBrokering: "native",
150
- // Vercel's sandbox firewall takes a hostname allow list (or "deny-all"),
151
- // never CIDRs.
169
+ // The driver translates hosts only; the SDK's subnet rules are not wired to net.cidrs.
152
170
  egressControl: "hosts",
153
171
  persistence: {
154
172
  resume: "replacement-restore",
@@ -162,23 +180,15 @@ var sandboxDriverCatalog = {
162
180
  },
163
181
  cloudflare: {
164
182
  id: "cloudflare",
165
- label: "Cloudflare",
166
- credentialFields: [
167
- { key: "api_token", label: "API Token", secret: true },
168
- { key: "worker_url", label: "Worker URL" }
169
- ],
183
+ label: sandboxDriverLabels.cloudflare,
184
+ credentialFields: sandboxDriverCredentialFields.cloudflare,
170
185
  metadata: {
171
186
  driverRunsIn: ["worker"],
172
187
  hostStopBehavior: "not-supported",
173
188
  hostResumeBehavior: "same-host",
174
189
  targetAccess: "relay",
175
- secretBrokering: "proxy",
176
- // The Cloudflare Sandbox Worker exposes no egress filter: its broker
177
- // (drivers/cloudflare-egress.ts) is an OPT-IN proxy the sandbox chooses
178
- // to route brokered-credential requests through, not a boundary. The
179
- // driver therefore drops `net` entirely — which is exactly why this
180
- // must be declared, so the manager refuses instead of provisioning a
181
- // sandbox the caller believes is contained.
190
+ secretBrokering: "native",
191
+ // Credential handlers do not restrict unrelated destinations.
182
192
  egressControl: "none",
183
193
  persistence: {
184
194
  resume: "replacement-restore",
@@ -192,8 +202,8 @@ var sandboxDriverCatalog = {
192
202
  },
193
203
  box: {
194
204
  id: "box",
195
- label: "Box",
196
- credentialFields: [{ key: "api_key", label: "API Key", secret: true }],
205
+ label: sandboxDriverLabels.box,
206
+ credentialFields: sandboxDriverCredentialFields.box,
197
207
  metadata: {
198
208
  driverRunsIn: ["node"],
199
209
  hostStopBehavior: "suspends-host",
@@ -213,8 +223,8 @@ var sandboxDriverCatalog = {
213
223
  },
214
224
  docker: {
215
225
  id: "docker",
216
- label: "Docker",
217
- credentialFields: [{ key: "image", label: "Image" }],
226
+ label: sandboxDriverLabels.docker,
227
+ credentialFields: sandboxDriverCredentialFields.docker,
218
228
  metadata: {
219
229
  driverRunsIn: ["local"],
220
230
  hostStopBehavior: "terminates-host",
@@ -234,8 +244,20 @@ var sandboxDriverCatalog = {
234
244
  }
235
245
  };
236
246
 
247
+ // ../claxedo-helpers/dist/guards.mjs
248
+ function isRecord(value) {
249
+ return value !== null && typeof value === "object" && !Array.isArray(value);
250
+ }
251
+ function asRecord(value) {
252
+ return isRecord(value) ? value : void 0;
253
+ }
254
+
255
+ // src/json.ts
256
+ function text(input) {
257
+ return typeof input === "string" && input.length > 0 ? input : void 0;
258
+ }
259
+
237
260
  // src/image.ts
238
- import { Image, DaytonaNotFoundError } from "@daytona/sdk";
239
261
  var SANDBOX_IMAGE_REPOSITORY = sandboxImageRepository();
240
262
  var SANDBOX_IMAGE = process.env.CLAXEDO_SANDBOX_IMAGE || defaultSandboxImage();
241
263
  var SNAPSHOT_NAME = process.env.CLAXEDO_SNAPSHOT_NAME || defaultSnapshotName();
@@ -269,7 +291,8 @@ var DEFAULT_HEALTH_TIMEOUT_MS = 6e4;
269
291
  var DEFAULT_HEALTH_INTERVAL_MS = 1e3;
270
292
  function exeWorkspaceName(workspaceId, epoch) {
271
293
  const slug = workspaceId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30) || "workspace";
272
- const hash = [...workspaceId].reduce((value, char) => Math.imul(value ^ char.charCodeAt(0), 16777619), 2166136261);
294
+ let hash = 2166136261;
295
+ for (const char of workspaceId) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
273
296
  return `claxedo-ws-${slug}-${(hash >>> 0).toString(36).slice(0, 8)}-g${epoch}`.slice(0, 63).replace(/-+$/g, "");
274
297
  }
275
298
  function createExeSandboxDriver(options) {
@@ -292,17 +315,17 @@ function createExeSandboxDriver(options) {
292
315
  body: command,
293
316
  signal: AbortSignal.timeout(operationTimeoutMs)
294
317
  });
295
- const text = await response.text();
296
- const body = text ? JSON.parse(text) : {};
318
+ const payload = await response.text();
319
+ const body = (payload ? asRecord(JSON.parse(payload)) : void 0) ?? {};
297
320
  if (!response.ok || body.ok === false || typeof body.error === "string") {
298
- throw new Error(`exe.dev command failed (${response.status}): ${String(body.error ?? body.message ?? response.statusText)}`);
321
+ const detail = text(body.error) ?? text(body.message) ?? response.statusText;
322
+ throw new Error(`exe.dev command failed (${response.status}): ${detail}`);
299
323
  }
300
324
  return body;
301
325
  }
302
326
  function vm(input) {
303
- if (!input || typeof input !== "object") return;
304
- const value = input;
305
- if (typeof value.vm_name !== "string") return;
327
+ const value = asRecord(input);
328
+ if (!value || typeof value.vm_name !== "string") return void 0;
306
329
  return {
307
330
  vm_name: value.vm_name,
308
331
  ...typeof value.https_url === "string" ? { https_url: value.https_url } : {},
@@ -315,11 +338,11 @@ function createExeSandboxDriver(options) {
315
338
  const cached = labels.get(item.vm_name);
316
339
  if (cached) return cached;
317
340
  const tags = item.tags ?? [];
318
- if (!tags.includes("claxedo")) return;
341
+ if (!tags.includes("claxedo")) return void 0;
319
342
  const workspaceId = tags.find((tag) => tag.startsWith("workspace-"))?.slice("workspace-".length);
320
- if (!workspaceId) return;
343
+ if (!workspaceId) return void 0;
321
344
  const epoch = item.vm_name.match(/-g(\d+)$/)?.[1];
322
- if (!epoch) return;
345
+ if (!epoch) return void 0;
323
346
  return { app: DEFAULT_EXE_APP_LABEL, workspaceId, epoch };
324
347
  }
325
348
  async function inspect(name) {
@@ -340,7 +363,7 @@ function createExeSandboxDriver(options) {
340
363
  }
341
364
  async function execute(name, command) {
342
365
  const body = await api(`ssh ${shell(name)} ${shell(command)}`);
343
- const result = body.result && typeof body.result === "object" ? body.result : body;
366
+ const result = asRecord(body.result) ?? body;
344
367
  return {
345
368
  stdout: typeof result.stdout === "string" ? result.stdout : "",
346
369
  stderr: typeof result.stderr === "string" ? result.stderr : "",
@@ -1,3 +1,11 @@
1
+ // ../claxedo-helpers/dist/guards.mjs
2
+ function isRecord(value) {
3
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4
+ }
5
+ function asRecord(value) {
6
+ return isRecord(value) ? value : void 0;
7
+ }
8
+
1
9
  // src/drivers/fetch-bridge.ts
2
10
  function cleanUrl(input) {
3
11
  return input.replace(/\/+$/, "");
@@ -10,6 +18,12 @@ function assertLifecycleSettings(options) {
10
18
  throw new Error("Fetch bridge sandbox driver requires a finite auto-delete policy");
11
19
  }
12
20
  }
21
+ function stringMap(input) {
22
+ const row = asRecord(input);
23
+ if (!row) return void 0;
24
+ const entries = Object.entries(row).filter((entry) => typeof entry[1] === "string");
25
+ return Object.fromEntries(entries);
26
+ }
13
27
  function headers(token) {
14
28
  return {
15
29
  "content-type": "application/json",
@@ -17,8 +31,8 @@ function headers(token) {
17
31
  };
18
32
  }
19
33
  function target(input) {
20
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("Fetch bridge sandbox driver returned an invalid response");
21
- const row = input;
34
+ const row = asRecord(input);
35
+ if (!row) throw new Error("Fetch bridge sandbox driver returned an invalid response");
22
36
  if (row.status === "provisioning") {
23
37
  return {
24
38
  provisioning: true,
@@ -34,7 +48,7 @@ function target(input) {
34
48
  url: row.url,
35
49
  hostId: row.hostId,
36
50
  driverResourceId: typeof row.driverResourceId === "string" ? row.driverResourceId : void 0,
37
- labels: row.labels && typeof row.labels === "object" && !Array.isArray(row.labels) ? row.labels : void 0
51
+ labels: stringMap(row.labels)
38
52
  };
39
53
  }
40
54
  function bridgeTarget(input) {
@@ -43,7 +57,8 @@ function bridgeTarget(input) {
43
57
  return result;
44
58
  }
45
59
  function targetList(input) {
46
- const rows = Array.isArray(input) ? input : input && typeof input === "object" && !Array.isArray(input) && Array.isArray(input.targets) ? input.targets : void 0;
60
+ const targets = asRecord(input)?.targets;
61
+ const rows = Array.isArray(input) ? input : Array.isArray(targets) ? targets : void 0;
47
62
  if (!rows) throw new Error("Fetch bridge sandbox driver returned an invalid target list");
48
63
  return rows.map(bridgeTarget);
49
64
  }
@@ -1,4 +1,5 @@
1
1
  import type { SandboxDriver, SandboxDriverEnsureInput } from "..";
2
+ import { type WorkspaceRuntimeControlEnv } from "../runtime-env";
2
3
  type ModalAppLike = unknown;
3
4
  type ModalImageLike = {
4
5
  imageId?: string;
@@ -49,11 +50,7 @@ export type ModalSandboxDriverOptions = {
49
50
  runtimeCommand?: string;
50
51
  workspaceDir?: string;
51
52
  runner?: string;
52
- controlEnv?: {
53
- relayJwksUrl?: string;
54
- relayVerifyPem?: string;
55
- managementJwksUrl?: string;
56
- };
53
+ controlEnv?: WorkspaceRuntimeControlEnv;
57
54
  env?: (input: SandboxDriverEnsureInput, host: {
58
55
  id: string;
59
56
  }) => Record<string, string> | Promise<Record<string, string>>;