@veris-ai/daytona 0.2.1 → 0.3.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/dist/index.js CHANGED
@@ -174,14 +174,19 @@ var ControlPlane = class {
174
174
  }
175
175
  if (!res.ok) await this.json(res, `update sandbox ${sandboxId}`);
176
176
  }
177
- /** Extend a twin's TTL so it stays in lockstep with an extended Daytona sandbox. */
178
- async extendTtl(environmentId, sandboxId, ttlMinutes) {
179
- const res = await this.request("PATCH", `/v1/environments/${environmentId}/sandboxes/${sandboxId}`, { ttl_minutes: ttlMinutes });
180
- if (res.status === 404) {
181
- throw new TwinExpiredError(`Veris sandbox ${sandboxId} not found \u2014 cannot extend TTL`, { verisSandboxId: sandboxId });
182
- }
183
- if (!res.ok && res.status !== 405) await this.json(res, `extend TTL of ${sandboxId}`);
184
- }
177
+ // There was an extendTtl() here, and it did nothing.
178
+ //
179
+ // It PATCHed { ttl_minutes } at the sandbox resource, and the server's
180
+ // UpdateSandboxRequest declares only client_base_url. Pydantic ignores
181
+ // unknown fields, so the route answered 200 having changed no TTL at all —
182
+ // a call that reports success while doing nothing, which is worse than no
183
+ // call, because the caller then believes the twin will outlive the run.
184
+ // Nothing here called it; it was API surface promising a thing that could
185
+ // not happen.
186
+ //
187
+ // Bring it back when a route exists to carry it. Until then a twin's TTL is
188
+ // decided once, by ttl_minutes at create — where this SDK already keeps it
189
+ // in step with the Daytona sandbox's own ttlMinutes.
185
190
  /** Create-time preflight: is the gateway infrastructure up, per the control plane? */
186
191
  async gatewayHealth() {
187
192
  const res = await this.request("GET", "/v1/gateway/health");
@@ -195,40 +200,162 @@ var ControlPlane = class {
195
200
  }
196
201
  };
197
202
 
203
+ // src/run-receipt.ts
204
+ import { randomUUID } from "crypto";
205
+
198
206
  // src/receipt.ts
207
+ var PAGE_LIMIT = 1e3;
208
+ var MAX_PAGES = 20;
209
+ function rowsOf(body) {
210
+ if (!body || !Array.isArray(body.requests)) {
211
+ throw new VerisError("invalid request log: expected requests array", { phase: "receipt" });
212
+ }
213
+ const rows = body.requests;
214
+ for (const r of rows) {
215
+ if (!r || !Number.isSafeInteger(r.id) || r.id <= 0 || typeof r.method !== "string" || typeof r.path !== "string" || typeof r.tier !== "string" || !(r.status === null || typeof r.status === "number")) {
216
+ throw new VerisError("invalid request log row: stable id, method, path, tier and status required", { phase: "receipt" });
217
+ }
218
+ }
219
+ return rows;
220
+ }
199
221
  function parseRequestsBody(body) {
200
- const rows = Array.isArray(body?.requests) ? body.requests : [];
201
- const entries = rows.map((r) => {
202
- const row = r;
203
- return {
204
- method: String(row.method ?? ""),
205
- path: String(row.path ?? ""),
206
- status: typeof row.status === "number" ? row.status : null
207
- };
208
- });
209
- return { count: entries.length, entries };
222
+ const rows = rowsOf(body);
223
+ const entries = rows.filter(
224
+ (r) => ["handler", "fault", "fallback", "fallback-llm", "fallback-replay"].includes(r.tier) && !/^\/veris(?:\/|$)/.test(r.path)
225
+ ).map(({ id, method, path, status, tier }) => ({ id, method, path, status, tier }));
226
+ return { count: entries.length, entries, total: rows.length };
210
227
  }
211
- async function fetchReceiptEntry(svc) {
212
- const url = `${svc.control_url}/veris/requests`;
213
- const res = await fetch(url);
228
+ async function readPage(svc, query) {
229
+ const res = await fetch(`${svc.control_url.replace(/\/$/, "")}/veris/requests?${query}`, {
230
+ signal: AbortSignal.timeout(3e4),
231
+ redirect: "error"
232
+ });
214
233
  const text = await res.text();
215
- if (!res.ok) {
216
- throw new VerisError(`could not read receipt for service '${svc.name}' (${res.status})`, {
217
- phase: "receipt",
218
- responseBody: text.slice(0, 500)
219
- });
234
+ if (!res.ok) throw new VerisError(`could not read receipt for service '${svc.name}' (${res.status})`, { phase: "receipt" });
235
+ let body;
236
+ try {
237
+ body = JSON.parse(text);
238
+ } catch {
239
+ throw new VerisError(`service '${svc.name}' returned a non-JSON receipt body`, { phase: "receipt" });
220
240
  }
221
- let raw;
241
+ return rowsOf(body);
242
+ }
243
+ async function fetchWatermark(svc) {
244
+ const rows = await readPage(svc, new URLSearchParams({ limit: "1", order: "desc" }));
245
+ if (rows.length > 1) throw new VerisError("request log ignored watermark limit", { phase: "receipt" });
246
+ return rows[0]?.id ?? 0;
247
+ }
248
+ async function fetchReceiptEntry(svc, sinceId = 0) {
249
+ if (!Number.isSafeInteger(sinceId) || sinceId < 0) throw new VerisError("invalid receipt watermark", { phase: "receipt" });
250
+ const end = await fetchWatermark(svc);
251
+ if (end < sinceId) throw new VerisError("receipt baseline invalid: log moved backwards; take a new baseline", { phase: "receipt" });
252
+ const entries = [];
253
+ const raw = [];
254
+ let mark = sinceId;
255
+ let incompleteReason;
256
+ for (let page = 0; mark < end; page++) {
257
+ if (page >= MAX_PAGES) {
258
+ incompleteReason = "page-limit";
259
+ break;
260
+ }
261
+ let rows;
262
+ try {
263
+ rows = await readPage(svc, new URLSearchParams({ limit: String(PAGE_LIMIT), order: "asc", since_id: String(mark) }));
264
+ } catch (error) {
265
+ if (!raw.length) throw error;
266
+ incompleteReason = "read-failed";
267
+ break;
268
+ }
269
+ let previous = mark;
270
+ if (!rows.length || rows.some((r) => {
271
+ const bad = r.id <= previous;
272
+ previous = r.id;
273
+ return bad;
274
+ })) {
275
+ incompleteReason = "pagination-not-progressing";
276
+ break;
277
+ }
278
+ const window = rows.filter((r) => r.id <= end);
279
+ raw.push(...window);
280
+ entries.push(...parseRequestsBody({ requests: window }).entries);
281
+ mark = rows[rows.length - 1].id;
282
+ }
283
+ entries.sort((a, b) => b.id - a.id);
284
+ return {
285
+ requests: entries.length,
286
+ controlUrl: svc.control_url,
287
+ entries,
288
+ sinceId,
289
+ untilId: end,
290
+ capped: Boolean(incompleteReason),
291
+ ...incompleteReason ? { incompleteReason } : {},
292
+ raw: { requests: raw }
293
+ };
294
+ }
295
+
296
+ // src/run-receipt.ts
297
+ var HEADER = "x-veris-receipt-baseline";
298
+ function markerOf(row) {
222
299
  try {
223
- raw = JSON.parse(text);
300
+ const headers = typeof row.request_headers === "string" ? JSON.parse(row.request_headers) : row.request_headers;
301
+ return headers?.[HEADER];
224
302
  } catch {
225
- throw new VerisError(`service '${svc.name}' returned a non-JSON receipt body`, {
226
- phase: "receipt",
227
- responseBody: text.slice(0, 500)
303
+ return void 0;
304
+ }
305
+ }
306
+ async function captureBaseline(twinId, sandboxId, services) {
307
+ const marks = await Promise.all(services.map(async (svc) => {
308
+ const marker = randomUUID();
309
+ const res = await fetch(`${svc.control_url.replace(/\/$/, "")}/veris/schema`, {
310
+ headers: { [HEADER]: marker },
311
+ signal: AbortSignal.timeout(3e4),
312
+ redirect: "error"
228
313
  });
314
+ await res.text();
315
+ if (!res.ok) throw new VerisError(`could not establish baseline for '${svc.name}' (${res.status})`, { phase: "receipt" });
316
+ for (let attempt = 0; attempt < 5; attempt++) {
317
+ const rows = await readPage(svc, new URLSearchParams({ limit: "1000", order: "desc" }));
318
+ const anchor = rows.find((r) => markerOf(r) === marker && r.tier === "control" && r.path === "/veris/schema");
319
+ if (anchor) return [svc.name, { controlUrl: svc.control_url, id: anchor.id, marker }];
320
+ await new Promise((resolve) => setTimeout(resolve, 50));
321
+ }
322
+ throw new VerisError(`baseline unavailable for '${svc.name}': trace must retain control request headers`, { phase: "receipt" });
323
+ }));
324
+ return { version: 1, twinId, sandboxId, services: Object.fromEntries(marks) };
325
+ }
326
+ async function validateBaseline(baseline, twinId, sandboxId, services) {
327
+ const invalid = () => new VerisError("receipt baseline invalid: session, services or history changed; take a new baseline before execution", { phase: "receipt" });
328
+ if (baseline.version !== 1 || baseline.twinId !== twinId || baseline.sandboxId !== sandboxId || Object.keys(baseline.services).sort().join("\0") !== services.map((s) => s.name).sort().join("\0")) throw invalid();
329
+ await Promise.all(services.map(async (svc) => {
330
+ const mark = baseline.services[svc.name];
331
+ if (!mark || mark.controlUrl !== svc.control_url || !Number.isSafeInteger(mark.id) || mark.id <= 0) throw invalid();
332
+ const rows = await readPage(svc, new URLSearchParams({ limit: "1", order: "asc", since_id: String(mark.id - 1) }));
333
+ if (rows.length !== 1 || rows[0].id !== mark.id || markerOf(rows[0]) !== mark.marker) throw invalid();
334
+ }));
335
+ }
336
+
337
+ // src/service-control.ts
338
+ async function serviceControl(svc, resource, options = {}) {
339
+ const method = options.method ?? "GET";
340
+ if (!["manual", "schema", "operations", "data", "requests"].includes(resource) || !["GET", "POST", "PATCH"].includes(method) || method !== "GET" && resource !== "data" || method === "GET" && options.body !== void 0 || !/^https?:\/\//.test(svc.control_url)) {
341
+ throw new VerisError("unsupported service control operation");
342
+ }
343
+ const url = new URL(`${svc.control_url.replace(/\/$/, "")}/veris/${resource}`);
344
+ for (const [key, value] of Object.entries(options.query ?? {})) url.searchParams.set(key, value);
345
+ const res = await fetch(url, {
346
+ method,
347
+ redirect: "error",
348
+ signal: AbortSignal.timeout(3e4),
349
+ headers: { "Content-Type": "application/json" },
350
+ body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
351
+ });
352
+ const text = await res.text();
353
+ if (!res.ok) throw new VerisError(`service '${svc.name}' ${method} /veris/${resource} failed (${res.status})`);
354
+ try {
355
+ return JSON.parse(text);
356
+ } catch {
357
+ throw new VerisError(`service '${svc.name}' returned invalid control JSON`);
229
358
  }
230
- const { count, entries } = parseRequestsBody(raw);
231
- return { requests: count, controlUrl: svc.control_url, entries, raw };
232
359
  }
233
360
 
234
361
  // src/network.ts
@@ -253,6 +380,9 @@ function twinHosts(services) {
253
380
  }
254
381
  return [...hosts].sort();
255
382
  }
383
+ function directTwinHosts(services) {
384
+ return twinHosts(services.filter((s) => isHttpUrl(s.url) && !(s.routes ?? []).length));
385
+ }
256
386
  function dataPlaneHosts(services) {
257
387
  const hosts = /* @__PURE__ */ new Set();
258
388
  for (const svc of services) {
@@ -302,51 +432,30 @@ var PROCESS_CONTROLLING = /* @__PURE__ */ new Set([
302
432
  function isSafeEnvName(name) {
303
433
  return /^[A-Z][A-Z0-9_]{0,63}$/.test(name) && !PROCESS_CONTROLLING.has(name);
304
434
  }
305
- var DEFAULT_REGISTRY_HOSTS = [
306
- // JS
307
- "registry.npmjs.org",
308
- "registry.yarnpkg.com",
309
- // Python
310
- "pypi.org",
311
- "files.pythonhosted.org",
312
- // Go
313
- "proxy.golang.org",
314
- "sum.golang.org",
315
- // Rust
316
- "crates.io",
317
- "static.crates.io",
318
- "index.crates.io",
319
- // Debian/Ubuntu
320
- "deb.debian.org",
321
- "security.debian.org",
322
- "archive.ubuntu.com",
323
- "security.ubuntu.com",
324
- // Source + container hosts the above routinely redirect to
325
- "github.com",
326
- "codeload.github.com",
327
- "objects.githubusercontent.com",
328
- "raw.githubusercontent.com",
329
- "ghcr.io"
330
- ];
435
+ var IPV4_RE = /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/;
331
436
  function buildNetwork(args) {
332
- const { services, mode, gatewayHosts, allowOut = [], allowRegistries = true } = args;
333
- if (mode === "open") return {};
334
- const domains = [
335
- ...vendorHosts(services),
336
- ...gatewayHosts,
337
- ...dataPlaneHosts(services),
338
- ...allowRegistries ? DEFAULT_REGISTRY_HOSTS : [],
339
- ...allowOut
340
- ].filter((h) => Boolean(h));
341
- return {
342
- // NOT networkBlockAll: that blocks everything including the gateway, and the
343
- // allowlist is what Daytona documents as "unbypassable network-layer
344
- // enforcement". Blocking all and then allowing is not a shape the API
345
- // offers; a non-empty domainAllowList IS the deny-by-default.
346
- domainAllowList: [...new Set(domains)].sort().join(",")
347
- };
437
+ const { mode, gatewayIps: gatewayIps2 } = args;
438
+ if (mode === "open") return { params: {} };
439
+ const ips = [...new Set(gatewayIps2)];
440
+ if (!ips.length) {
441
+ throw new VerisError(
442
+ "the control plane did not name the gateway's IP addresses (egress credential `gateway_ips`) and the gateway hostname could not be resolved, so the sandbox cannot be pinned to it. Upgrade the control plane, or run with veris.egress: 'open' (Daytona still blocks anything that bypasses the proxy).",
443
+ { phase: "credential-mint" }
444
+ );
445
+ }
446
+ const bad = ips.filter((ip) => !IPV4_RE.test(ip));
447
+ if (bad.length) {
448
+ throw new VerisError(
449
+ `the control plane returned gateway addresses that are not IPv4: ${JSON.stringify(bad)} (Daytona's networkAllowList takes IPv4 CIDRs only)`,
450
+ { phase: "credential-mint" }
451
+ );
452
+ }
453
+ return { params: { networkAllowList: ips.map((ip) => `${ip}/32`).join(",") } };
348
454
  }
349
455
 
456
+ // src/gateway.ts
457
+ import { promises as dns } from "dns";
458
+
350
459
  // src/trust.ts
351
460
  var CA_CERT_PATH = "/usr/local/share/ca-certificates/veris-ca.crt";
352
461
  var SYSTEM_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
@@ -374,6 +483,80 @@ function vendoredTrustEnv() {
374
483
  NODE_EXTRA_CA_CERTS: VERIS_BUNDLE
375
484
  };
376
485
  }
486
+ function trustPrelude(env = vendoredTrustEnv()) {
487
+ return Object.entries(env).map(([k, v]) => `export ${k}='${v.replace(/'/g, `'\\''`)}';`).join(" ");
488
+ }
489
+ var BUNDLED_CA_FILES = [
490
+ { sdk: "pip (vendored certifi)", suffix: "pip/_vendor/certifi/cacert.pem" },
491
+ { sdk: "certifi", suffix: "certifi/cacert.pem" },
492
+ { sdk: "botocore", suffix: "botocore/cacert.pem" },
493
+ // Matches both stripe-python's site-packages layout and the stripe-ruby gem.
494
+ { sdk: "stripe", suffix: "stripe/data/ca-certificates.crt" },
495
+ { sdk: "httplib2", suffix: "httplib2/cacerts.txt" }
496
+ ];
497
+ var BUNDLED_CA_PATCH_SCRIPT = "/tmp/veris-patch-bundled-cas.sh";
498
+ var BUNDLED_CA_PATCHED_MARKER = "the Veris CA was appended to ";
499
+ function bundledCaPatchScript() {
500
+ const find = BUNDLED_CA_FILES.map((f) => `find / -xdev \\( -path /proc -o -path /sys -o -path /dev \\) -prune -o -path '*/${f.suffix}' -type f -print 2>/dev/null`).join("; ");
501
+ return [
502
+ `marker=$(sed -n 2p ${VERIS_CA_FILE} 2>/dev/null)`,
503
+ // No CA on disk means nothing to append, and appending nothing to every
504
+ // bundle in the image would be the worst available no-op.
505
+ `[ -n "$marker" ] || { echo "no ${VERIS_CA_FILE} to append" >&2; exit 1; }`,
506
+ // The whole loop is one brace group at the end of the pipe so `n` survives
507
+ // to the summary: a pipeline's last stage is a subshell, and a counter
508
+ // incremented inside a bare `while` would be gone by the time we report it.
509
+ `{ ${find}; } | sort -u | { n=0`,
510
+ `while IFS= read -r f; do`,
511
+ ` [ -w "$f" ] || continue`,
512
+ ` grep -qF "$marker" "$f" && continue`,
513
+ ` { printf '\\n'; cat ${VERIS_CA_FILE}; } >> "$f" || continue`,
514
+ ` n=$((n+1))`,
515
+ ` echo "${BUNDLED_CA_PATCHED_MARKER}$f"`,
516
+ `done`,
517
+ // Silence is the common case and it reads as "did that even run?", so say
518
+ // so. patchBundledCas() keeps only the lines above, so this costs it
519
+ // nothing.
520
+ `if [ "$n" -gt 0 ]; then echo "$n bundled CA file(s) patched"`,
521
+ `else echo "no bundled CA file needed patching \u2014 no SDK that ships its own is installed here, or they already trust the Veris CA"; fi`,
522
+ `}`
523
+ ].join("\n");
524
+ }
525
+ var NODE_TRUST_FLAG = "--use-openssl-ca";
526
+ function nodeOptionsWithTrust(existing) {
527
+ return appendFlag(existing, NODE_TRUST_FLAG);
528
+ }
529
+ var NODE_PROXY_PRELOAD = "/tmp/veris-node-proxy.cjs";
530
+ var NODE_PROXY_FLAG = `--require ${NODE_PROXY_PRELOAD}`;
531
+ function nodeProxyPreloadScript() {
532
+ return [
533
+ `// Preloaded by @veris-ai/daytona through NODE_OPTIONS=--require.`,
534
+ `// Gives every http(s).Agent the proxy environment, which Node applies only`,
535
+ `// to its global agents under NODE_USE_ENV_PROXY=1. An SDK that builds its own`,
536
+ `// Agent would otherwise dial the vendor directly, which this sandbox cannot.`,
537
+ `for (const mod of [require("http"), require("https")]) {`,
538
+ ` const Base = mod.Agent`,
539
+ ` mod.Agent = class Agent extends Base {`,
540
+ ` constructor(options) {`,
541
+ ` super({ proxyEnv: process.env, ...(options || {}) })`,
542
+ ` }`,
543
+ ` }`,
544
+ `}`,
545
+ ``
546
+ ].join("\n");
547
+ }
548
+ function nodeOptionsWithProxy(existing) {
549
+ return appendFlag(existing, NODE_PROXY_FLAG);
550
+ }
551
+ function verisNodeOptions(existing, opts = {}) {
552
+ const withProxy = nodeOptionsWithProxy(existing);
553
+ return opts.trust === false ? withProxy : nodeOptionsWithTrust(withProxy);
554
+ }
555
+ function appendFlag(existing, flag) {
556
+ const base = (existing ?? "").trim();
557
+ if (base === flag || base.startsWith(`${flag} `) || base.endsWith(` ${flag}`) || base.includes(` ${flag} `)) return base;
558
+ return base ? `${base} ${flag}` : flag;
559
+ }
377
560
  var CA_INSTALL_CMD = [
378
561
  "update-ca-certificates",
379
562
  `(keytool -importcert -noprompt -cacerts -storepass changeit -alias veris -file ${CA_CERT_PATH} 2>/dev/null || true)`,
@@ -408,6 +591,16 @@ function gatewayProxyUrl(credential) {
408
591
  }
409
592
  return `http://${encodeURIComponent(credential.username)}:x@${credential.connect_address}`;
410
593
  }
594
+ async function gatewayIps(credential, proxyUrl) {
595
+ if (credential.gateway_ips?.length) return credential.gateway_ips;
596
+ const host = new URL(proxyUrl).hostname;
597
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) return [host];
598
+ try {
599
+ return await dns.resolve4(host);
600
+ } catch {
601
+ return [];
602
+ }
603
+ }
411
604
  function assertProxyUrl(raw) {
412
605
  let u;
413
606
  try {
@@ -434,6 +627,8 @@ function assertProxyUrl(raw) {
434
627
  }
435
628
  async function installCa(sandbox, caPem) {
436
629
  await sandbox.fs.uploadFile(Buffer.from(caPem, "utf8"), VERIS_CA_FILE);
630
+ await sandbox.fs.uploadFile(Buffer.from(bundledCaPatchScript(), "utf8"), BUNDLED_CA_PATCH_SCRIPT);
631
+ await installNodeProxyPreload(sandbox);
437
632
  const script = [
438
633
  `chmod 0644 ${VERIS_CA_FILE}`,
439
634
  // Public roots first so they keep working; ours appended. `cat` of a
@@ -459,6 +654,13 @@ async function installCa(sandbox, caPem) {
459
654
  );
460
655
  }
461
656
  }
657
+ async function installNodeProxyPreload(sandbox) {
658
+ await sandbox.fs.uploadFile(Buffer.from(nodeProxyPreloadScript(), "utf8"), NODE_PROXY_PRELOAD);
659
+ }
660
+ async function patchBundledCas(sandbox) {
661
+ const r = await sh(sandbox, `sh ${BUNDLED_CA_PATCH_SCRIPT}`, 180).catch((e) => ({ exitCode: 1, result: String(e) }));
662
+ return (r.result ?? "").split("\n").filter((line) => line.startsWith(BUNDLED_CA_PATCHED_MARKER)).map((line) => line.slice(BUNDLED_CA_PATCHED_MARKER.length).trim()).filter(Boolean);
663
+ }
462
664
  async function probeCanary(sandbox, canaryHost, expectedTwinId) {
463
665
  if (!HOSTNAME_RE.test(canaryHost)) {
464
666
  throw new ReceiptIntegrityError(
@@ -512,9 +714,15 @@ var VerisApiImpl = class {
512
714
  this.ctx = ctx;
513
715
  }
514
716
  ctx;
717
+ get ownsTwin() {
718
+ return this.ctx.ownsTwin;
719
+ }
515
720
  get sandboxId() {
516
721
  return this.ctx.twinId;
517
722
  }
723
+ get environmentId() {
724
+ return this.ctx.environmentId;
725
+ }
518
726
  get mode() {
519
727
  return "gateway";
520
728
  }
@@ -540,10 +748,13 @@ var VerisApiImpl = class {
540
748
  }
541
749
  async receipt(service) {
542
750
  await probeCanary(this.ctx.sandbox, this.ctx.canaryHost, this.ctx.twinId);
543
- if (service !== void 0) return fetchReceiptEntry(await this.resolveService(service));
751
+ if (service !== void 0) {
752
+ const svc = await this.resolveService(service);
753
+ return fetchReceiptEntry(svc, this.watermark(svc.name));
754
+ }
544
755
  const services = await this.services();
545
756
  const entries = await Promise.all(
546
- services.filter((s) => isHttpUrl(s.control_url)).map(async (svc) => [svc.name, await fetchReceiptEntry(svc)])
757
+ services.filter((s) => isHttpUrl(s.control_url)).map(async (svc) => [svc.name, await fetchReceiptEntry(svc, this.watermark(svc.name))])
547
758
  );
548
759
  return {
549
760
  services: Object.fromEntries(entries),
@@ -559,10 +770,45 @@ var VerisApiImpl = class {
559
770
  leaks() {
560
771
  return ["udp-quic-possible", "ech-possible"];
561
772
  }
773
+ /** Where this service's log was when the run began. 0 reads all of it. */
774
+ watermark(service) {
775
+ return this.ctx.watermarks?.[service] ?? 0;
776
+ }
777
+ async verifyIntegrity() {
778
+ await probeCanary(this.ctx.sandbox, this.ctx.canaryHost, this.ctx.twinId);
779
+ }
780
+ async receiptBaseline() {
781
+ await this.verifyIntegrity();
782
+ return captureBaseline(
783
+ this.ctx.twinId,
784
+ this.ctx.sandbox.id,
785
+ (await this.services()).filter((s) => isHttpUrl(s.control_url))
786
+ );
787
+ }
788
+ async receiptSince(baseline, service) {
789
+ await this.verifyIntegrity();
790
+ const services = (await this.services()).filter((s) => isHttpUrl(s.control_url));
791
+ if (service !== void 0 && !services.some((s) => s.name === service)) {
792
+ throw new VerisError(`unknown HTTP service '${service}'`, { phase: "receipt" });
793
+ }
794
+ await validateBaseline(baseline, this.ctx.twinId, this.ctx.sandbox.id, services);
795
+ const selected = service === void 0 ? services : services.filter((s) => s.name === service);
796
+ const entries = await Promise.all(selected.map(async (svc) => [svc.name, await fetchReceiptEntry(svc, baseline.services[svc.name].id)]));
797
+ await validateBaseline(baseline, this.ctx.twinId, this.ctx.sandbox.id, await this.services().then((s) => s.filter((v) => isHttpUrl(v.control_url))));
798
+ return { services: Object.fromEntries(entries), mode: "gateway", integrity: "verified", leaks: this.leaks() };
799
+ }
800
+ async control(service, resource, options) {
801
+ const svc = (await this.services()).find((s) => s.name === service);
802
+ if (!svc) throw new VerisError(`unknown service '${service}'`);
803
+ return serviceControl(svc, resource, options);
804
+ }
562
805
  async assertTouched(service, match) {
563
806
  const entry = await this.receipt(service);
564
807
  const need = match?.minRequests ?? 1;
565
808
  const matched = match ? entry.entries.filter((r) => (match.method === void 0 || r.method.toUpperCase() === match.method.toUpperCase()) && (match.path === void 0 || r.path.includes(match.path))) : entry.entries;
809
+ if (matched.length < need && entry.capped) {
810
+ throw new VerisError(`receipt for '${service}' is incomplete; insufficient evidence`, { phase: "receipt" });
811
+ }
566
812
  if (matched.length < need) {
567
813
  const what = match ? `matching ${match.method ?? "ANY"} ${match.path ?? "*"} (${matched.length}/${need})` : "any intercepted requests";
568
814
  throw new VerisUntouchedError(
@@ -579,6 +825,36 @@ var VerisApiImpl = class {
579
825
  getTrustEnv() {
580
826
  return vendoredTrustEnv();
581
827
  }
828
+ /**
829
+ * The same variables as a shell prelude, for a caller that has no env map to
830
+ * fill — anything running a command through a session or a shell it did not
831
+ * build the environment for. Daytona resets SSL_CERT_FILE,
832
+ * REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE and NODE_EXTRA_CA_CERTS to its own CA
833
+ * file inside the sandbox, and that file cannot verify the gateway's leaf, so
834
+ * a command that inherits them fails on certificate validation:
835
+ * `uv sync` dies with "invalid peer certificate: UnknownIssuer".
836
+ *
837
+ * sbx.process.executeCommand(`${sbx.veris.trustPrelude()} uv sync`)
838
+ */
839
+ trustPrelude() {
840
+ return trustPrelude(this.getTrustEnv());
841
+ }
842
+ /**
843
+ * Append the Veris CA to the CA bundles the code under test's own SDKs ship.
844
+ *
845
+ * The variables above reach every client that reads one. stripe-python does
846
+ * not: it passes `verify=stripe.ca_bundle_path`, so the first Stripe call
847
+ * fails with "Could not verify Stripe's SSL certificate" in a sandbox where
848
+ * curl, Node and `requests` all succeed. This patches the file itself, which
849
+ * is the Daytona-shaped version of the veris CLI's --patch-bundled-cas.
850
+ *
851
+ * Call it AFTER installing dependencies — the bundles arrive with them, so
852
+ * there is nothing to patch at create time. Safe to call again; it returns
853
+ * only the files it actually changed.
854
+ */
855
+ patchBundledCas() {
856
+ return patchBundledCas(this.ctx.sandbox);
857
+ }
582
858
  async deliverTo(target, opts = {}) {
583
859
  const url = typeof target === "number" ? (await this.ctx.sandbox.getPreviewLink(target)).url : target;
584
860
  await this.ctx.controlPlane.updateSandbox(
@@ -610,8 +886,70 @@ var VerisApiImpl = class {
610
886
  }
611
887
  };
612
888
 
889
+ // src/profile.ts
890
+ import { readFileSync } from "fs";
891
+ import { homedir } from "os";
892
+ import { join } from "path";
893
+ import { parse } from "yaml";
894
+ var DEFAULT_API_BASE = "https://svc.api.veris.ai";
895
+ function profilePath(home = homedir()) {
896
+ return join(home, ".veris", "twin.yaml");
897
+ }
898
+ function readOrUndefined(path) {
899
+ try {
900
+ return readFileSync(path, "utf8");
901
+ } catch (e) {
902
+ if (e.code === "ENOENT") return void 0;
903
+ throw e;
904
+ }
905
+ }
906
+ function parseProfileFile(text, path) {
907
+ if (text === void 0) return void 0;
908
+ let parsed;
909
+ try {
910
+ parsed = parse(text);
911
+ } catch (cause) {
912
+ throw new VerisError(`${path} is not valid YAML`, { phase: "credentials", cause });
913
+ }
914
+ if (parsed === null || parsed === void 0) return {};
915
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
916
+ throw new VerisError(`${path} is not a profile file (expected a mapping)`, { phase: "credentials" });
917
+ }
918
+ return parsed;
919
+ }
920
+ function selectProfileName(env, file) {
921
+ return env.VERIS_PROFILE || file?.active_profile || "default";
922
+ }
923
+ function resolveVerisCredentials(opts = {}) {
924
+ const env = opts.env ?? process.env;
925
+ const path = opts.path ?? profilePath();
926
+ const file = parseProfileFile((opts.readFile ?? readOrUndefined)(path), path);
927
+ const name = selectProfileName(env, file);
928
+ const profile = file?.profiles?.[name] ?? void 0;
929
+ const found = profile !== void 0 && profile !== null;
930
+ const apiKey = opts.apiKey || env.VERIS_API_KEY || (found ? profile.api_key : void 0) || void 0;
931
+ const keySource = opts.apiKey ? "option" : env.VERIS_API_KEY ? "env" : apiKey ? "profile" : "none";
932
+ const profileBase = found ? profile.api_base : void 0;
933
+ const apiBase = (opts.apiBase || env.VERIS_API_BASE || profileBase || DEFAULT_API_BASE).replace(/\/$/, "");
934
+ const baseSource = opts.apiBase ? "option" : env.VERIS_API_BASE ? "env" : profileBase ? "profile" : "default";
935
+ return { apiKey, apiBase, keySource, baseSource, profile: { name, path, found } };
936
+ }
937
+ function missingKeyWhere(c) {
938
+ const where = c.profile.found ? `profile '${c.profile.name}' in ${c.profile.path} has no api_key` : `${c.profile.path} has no profile '${c.profile.name}'`;
939
+ return `VERIS_API_KEY is not set, and ${where}`;
940
+ }
941
+ var KEY_SOURCES_HINT = "Run `veris login` (the veris CLI saves the key there; `--profile <name>` for another plane, and VERIS_PROFILE picks it here), or set VERIS_API_KEY in the environment. Get a key at https://studio.veris.ai";
942
+ function missingKeyMessage(c) {
943
+ return `no Veris API key: ${missingKeyWhere(c)}. ${KEY_SOURCES_HINT}`;
944
+ }
945
+ function requireVerisCredentials(opts = {}) {
946
+ const c = resolveVerisCredentials(opts);
947
+ if (!c.apiKey) throw new MissingCredentialsError(missingKeyMessage(c), { phase: "credentials" });
948
+ return c;
949
+ }
950
+
613
951
  // src/version.ts
614
- var SDK_VERSION = true ? "0.2.1" : "0.0.0-dev";
952
+ var SDK_VERSION = true ? "0.3.1" : "0.0.0-dev";
615
953
 
616
954
  // src/daytona.ts
617
955
  var LABEL = {
@@ -621,12 +959,24 @@ var LABEL = {
621
959
  mode: "veris_mode",
622
960
  egress: "veris_egress",
623
961
  ownsTwin: "veris_owns_twin",
624
- canaryHost: "veris_canary_host"
962
+ canaryHost: "veris_canary_host",
963
+ /** Unique to ONE create() call, and the only way back to a sandbox whose
964
+ * create threw before it could hand the object over — see reapFailedCreate.
965
+ * The twin id will not do: an attached twin is shared by every sandbox on
966
+ * it, and deleting the wrong one of those would be worse than the leak. */
967
+ createId: "veris_create_id"
625
968
  };
626
969
  var VERIS_LABEL_KEYS = Object.values(LABEL);
627
970
  function isVerisSandbox(sbx) {
628
971
  return typeof sbx.verisSandboxId === "string";
629
972
  }
973
+ function verisTwinId(sandbox) {
974
+ return sandbox.labels?.[LABEL.twinId];
975
+ }
976
+ function verisOwnsTwin(sandbox) {
977
+ const labels = sandbox.labels ?? {};
978
+ return labels[LABEL.twinId] !== void 0 && labels[LABEL.ownsTwin] !== "false";
979
+ }
630
980
  var Daytona = class extends BaseDaytona {
631
981
  verisDefaults;
632
982
  constructor(config) {
@@ -646,6 +996,7 @@ var Daytona = class extends BaseDaytona {
646
996
  const egress = v.egress ?? "strict";
647
997
  const ttlMinutes = v.ttlMinutes ?? 60;
648
998
  const ownsTwin = !v.attachSandboxId;
999
+ const createId = crypto.randomUUID();
649
1000
  const twin = await this.provisionTwin(controlPlane, v, coords, ttlMinutes);
650
1001
  const cleanupTwin = async () => {
651
1002
  if (ownsTwin) await controlPlane.deleteTwin(twin.environment_id, twin.id).catch(() => {
@@ -653,6 +1004,8 @@ var Daytona = class extends BaseDaytona {
653
1004
  };
654
1005
  let sandbox;
655
1006
  let credential;
1007
+ let watermarks = {};
1008
+ let daytonaAsked = false;
656
1009
  try {
657
1010
  credential = await controlPlane.mintEgressCredential(twin.environment_id, twin.id);
658
1011
  if (!credential) {
@@ -669,17 +1022,22 @@ var Daytona = class extends BaseDaytona {
669
1022
  }
670
1023
  const services = twin.services?.length ? twin.services : await controlPlane.services(twin.id);
671
1024
  const proxyUrl = gatewayProxyUrl(credential);
672
- const network = buildNetwork({
673
- services,
674
- mode: egress,
675
- // The gateway has to be reachable or nothing is: taken from the URL we
676
- // are actually going to use, so the two can never disagree.
677
- gatewayHosts: [new URL(proxyUrl).hostname, credential.canary_host].filter(Boolean),
678
- allowOut: v.allowOut,
679
- allowRegistries: v.allowRegistries
680
- });
1025
+ const network = buildNetwork({ mode: egress, gatewayIps: await gatewayIps(credential, proxyUrl) });
1026
+ watermarks = await readWatermarks(services);
681
1027
  const verisManaged = {
682
1028
  ...v.installCa !== false ? sanitizeTrustEnv(void 0) : {},
1029
+ // Node reads none of the variables above once Daytona has overwritten
1030
+ // them; the trust flag makes it read OpenSSL's store, which the CA
1031
+ // install fills. The preload gives every http(s).Agent the proxy
1032
+ // environment, which NODE_USE_ENV_PROXY below reaches only for the
1033
+ // global ones. Appended, not replaced: a caller's own NODE_OPTIONS keep
1034
+ // working.
1035
+ NODE_OPTIONS: verisNodeOptions(rest.envVars?.NODE_OPTIONS, { trust: v.installCa !== false }),
1036
+ // Node ignores HTTPS_PROXY unless told to, and Daytona blocks anything
1037
+ // that dials out directly — so without this every plain `https.get`
1038
+ // and `fetch` in Node 24+ dies with ECONNRESET instead of reaching
1039
+ // the gateway. Older Node needs a proxy agent; the variable is inert there.
1040
+ NODE_USE_ENV_PROXY: "1",
683
1041
  ...v.dataPlaneEnv !== false ? dataPlaneEnv(services) : {},
684
1042
  VERIS_SANDBOX_ID: twin.id
685
1043
  };
@@ -694,27 +1052,31 @@ var Daytona = class extends BaseDaytona {
694
1052
  [LABEL.egress]: egress,
695
1053
  [LABEL.ownsTwin]: String(ownsTwin),
696
1054
  [LABEL.mode]: "gateway",
697
- [LABEL.canaryHost]: credential.canary_host
1055
+ [LABEL.canaryHost]: credential.canary_host,
1056
+ [LABEL.createId]: createId
698
1057
  },
699
- ...network,
700
- // 3. Where Daytona forwards everything the allowlist permits. Chained,
701
- // not advisory: an unreachable gateway makes allowed traffic 502
702
- // rather than quietly going direct.
1058
+ ...network.params,
1059
+ // 3. Where Daytona forwards everything. Chained, not advisory: an
1060
+ // unreachable gateway makes traffic 502 rather than quietly going
1061
+ // direct.
703
1062
  outboundProxyUrl: proxyUrl,
704
1063
  ttlMinutes: rest.ttlMinutes ?? ttlMinutes
705
1064
  };
1065
+ daytonaAsked = true;
706
1066
  sandbox = await this.baseCreate(createParams, options);
707
1067
  } catch (cause) {
1068
+ const failed = daytonaAsked ? await reapFailedCreate((labels) => super.list({ labels }), createId) : void 0;
708
1069
  await cleanupTwin();
709
1070
  if (cause instanceof VerisError) throw cause;
710
- throw new VerisError("Daytona sandbox create failed", {
1071
+ throw new VerisError(sandboxCreateMessage(cause, failed, rest), {
711
1072
  phase: "sandbox-create",
712
1073
  verisSandboxId: twin.id,
713
1074
  cause
714
1075
  });
715
1076
  }
716
1077
  try {
717
- await installCa(sandbox, credential.ca_pem);
1078
+ if (v.installCa !== false) await installCa(sandbox, credential.ca_pem);
1079
+ else await installNodeProxyPreload(sandbox);
718
1080
  await probeCanary(sandbox, credential.canary_host, twin.id);
719
1081
  } catch (err) {
720
1082
  await sandbox.delete().catch(() => {
@@ -728,7 +1090,8 @@ var Daytona = class extends BaseDaytona {
728
1090
  twinId: twin.id,
729
1091
  egress,
730
1092
  ownsTwin,
731
- canaryHost: credential.canary_host
1093
+ canaryHost: credential.canary_host,
1094
+ watermarks
732
1095
  });
733
1096
  }
734
1097
  /**
@@ -760,9 +1123,10 @@ var Daytona = class extends BaseDaytona {
760
1123
  const labels = sandbox.labels ?? {};
761
1124
  const twinId = labels[LABEL.twinId];
762
1125
  if (!twinId) return sandbox;
763
- const apiKey = this.verisDefaults.apiKey ?? process.env.VERIS_API_KEY;
1126
+ const creds = resolveVerisCredentials({ apiKey: this.verisDefaults.apiKey, apiBase: this.verisDefaults.apiBase });
1127
+ const apiKey = creds.apiKey;
764
1128
  if (!apiKey) return sandbox;
765
- const trustedBase = this.verisDefaults.apiBase ?? process.env.VERIS_API_BASE;
1129
+ const trustedBase = creds.baseSource === "default" ? void 0 : creds.apiBase;
766
1130
  const labelBase = labels[LABEL.apiBase];
767
1131
  if (trustedBase && labelBase && labelBase !== trustedBase) {
768
1132
  throw new VerisError(
@@ -844,19 +1208,48 @@ var Daytona = class extends BaseDaytona {
844
1208
  };
845
1209
  var daytona_default = Daytona;
846
1210
  function resolveCoordinates(v) {
847
- const apiKey = v.apiKey ?? process.env.VERIS_API_KEY;
848
- if (!apiKey) {
849
- throw new MissingCredentialsError(
850
- "no Veris API key: set VERIS_API_KEY in your environment, or pass veris.apiKey. Get one at https://studio.veris.ai",
851
- { phase: "credentials" }
852
- );
853
- }
1211
+ const creds = requireVerisCredentials({ apiKey: v.apiKey, apiBase: v.apiBase });
854
1212
  return {
855
- apiKey,
1213
+ apiKey: creds.apiKey,
856
1214
  environmentId: v.environmentId ?? process.env.VERIS_ENVIRONMENT_ID,
857
- apiBase: (v.apiBase ?? process.env.VERIS_API_BASE ?? "https://svc.api.veris.ai").replace(/\/$/, "")
1215
+ apiBase: creds.apiBase
858
1216
  };
859
1217
  }
1218
+ async function reapFailedCreate(list, createId) {
1219
+ try {
1220
+ for await (const sandbox of list({ [LABEL.createId]: createId })) {
1221
+ if (sandbox.labels?.[LABEL.createId] !== createId) continue;
1222
+ if (!sandbox.errorReason) await sandbox.refreshData().catch(() => {
1223
+ });
1224
+ const found = { id: sandbox.id, state: sandbox.state, errorReason: sandbox.errorReason };
1225
+ return { ...found, deleted: await sandbox.delete().then(() => true, () => false) };
1226
+ }
1227
+ } catch {
1228
+ }
1229
+ return void 0;
1230
+ }
1231
+ function sandboxCreateMessage(cause, failed, params = {}) {
1232
+ const base = "Daytona sandbox create failed";
1233
+ if (!failed) return base;
1234
+ const reason = failed.errorReason?.trim().replace(/[.\s]+$/, "");
1235
+ const parts = [reason ? `${base}: ${reason}.` : `${base}.`];
1236
+ const image = typeof params.image === "string" ? params.image : void 0;
1237
+ if (image && failed.state === "build_failed") {
1238
+ parts.push(
1239
+ `Daytona builds "${image}" on its own servers and pulls it from a registry, so an image that exists only in your local Docker daemon is not one it can see.`
1240
+ );
1241
+ }
1242
+ parts.push(failed.deleted ? `The half-built sandbox ${failed.id} was deleted.` : `The half-built sandbox ${failed.id} could not be deleted \u2014 remove it with \`veris-daytona teardown ${failed.id}\`.`);
1243
+ const raw = cause instanceof Error ? cause.message : "";
1244
+ if (raw) parts.push(`(Daytona SDK: ${raw})`);
1245
+ return parts.join(" ");
1246
+ }
1247
+ async function readWatermarks(services) {
1248
+ const marks = await Promise.all(
1249
+ services.filter((s) => isHttpUrl(s.control_url)).map(async (svc) => [svc.name, await fetchWatermark(svc).catch(() => 0)])
1250
+ );
1251
+ return Object.fromEntries(marks);
1252
+ }
860
1253
  function stripVeris(params) {
861
1254
  const { veris: _veris, ...rest } = params ?? {};
862
1255
  return rest;
@@ -868,12 +1261,85 @@ function reserveLabels(labels) {
868
1261
  }
869
1262
  return out;
870
1263
  }
1264
+
1265
+ // src/daytona-key.ts
1266
+ var DAYTONA_API_URL = "https://app.daytona.io/api";
1267
+ var DELETE_SANDBOXES = "delete:sandboxes";
1268
+ function daytonaApiUrl(env = process.env) {
1269
+ return (env.DAYTONA_API_URL || env.DAYTONA_SERVER_URL || DAYTONA_API_URL).replace(/\/$/, "");
1270
+ }
1271
+ async function fetchDaytonaKey(apiKey, apiUrl = daytonaApiUrl()) {
1272
+ try {
1273
+ const res = await fetch(`${apiUrl}/api-keys/current`, {
1274
+ headers: { Authorization: `Bearer ${apiKey}` }
1275
+ });
1276
+ if (!res.ok) return void 0;
1277
+ const body = await res.json();
1278
+ if (!Array.isArray(body.permissions)) return void 0;
1279
+ return {
1280
+ name: typeof body.name === "string" ? body.name : "",
1281
+ permissions: body.permissions.filter((p) => typeof p === "string")
1282
+ };
1283
+ } catch {
1284
+ return void 0;
1285
+ }
1286
+ }
1287
+ function canDeleteSandboxes(key) {
1288
+ return key === void 0 ? void 0 : key.permissions.includes(DELETE_SANDBOXES);
1289
+ }
1290
+ function describeKey(key) {
1291
+ const name = key.name ? `"${key.name}" ` : "";
1292
+ return `${name}(permissions: ${key.permissions.join(", ") || "none"})`;
1293
+ }
1294
+ var DELETE_PERMISSION_FIX = 'create a key with the "delete sandboxes" permission at https://app.daytona.io/dashboard/keys';
1295
+ function cannotTeardownWarning(key, autoStopMinutes, autoDeleteMinutes) {
1296
+ return `DAYTONA_API_KEY ${describeKey(key)} lacks \`${DELETE_SANDBOXES}\`, so \`veris-daytona teardown\` will be refused for the sandbox about to be created, and \`run\` cannot delete its own. The box still stops after ${autoStopMinutes} idle minutes and is deleted ${autoDeleteMinutes} minutes after it stops. For a teardown that works, ${DELETE_PERMISSION_FIX} and use it instead.`;
1297
+ }
1298
+
1299
+ // src/teardown.ts
1300
+ var TEARDOWN_USAGE = `usage: veris-daytona teardown <daytona-sandbox-id>
1301
+
1302
+ Deletes the Daytona sandbox. The id is the one \`veris-daytona provision\`
1303
+ printed as daytonaSandboxId \u2014 not the Veris twin's id.
1304
+
1305
+ The twin follows the rule it was created under: one this package created is
1306
+ deleted with the sandbox, one it attached to (\`provision\`, or \`run --sandbox\`)
1307
+ is yours and is left running. Which of the two happened is printed.
1308
+
1309
+ needs: DAYTONA_API_KEY with the ${DELETE_SANDBOXES} permission \u2014 a key made with
1310
+ "write sandboxes" alone is refused, and the refusal says when Daytona will stop
1311
+ and delete the box on its own. To reach a twin that must be deleted, a Veris
1312
+ key too: VERIS_API_KEY, or the profile \`veris login\` saved.
1313
+
1314
+ exit code: 0 when the sandbox is deleted; 1 when there is no such sandbox, or
1315
+ the key may not delete one.`;
1316
+ function isPermissionDenied(e) {
1317
+ return typeof e === "object" && e !== null && e.statusCode === 403;
1318
+ }
1319
+ function sandboxBrakes(r) {
1320
+ const stop = r.autoStopMinutes && r.autoStopMinutes > 0 ? `stops after ${r.autoStopMinutes} idle minutes` : "never stops on its own";
1321
+ const del = r.autoDeleteMinutes === void 0 || r.autoDeleteMinutes < 0 ? "is never auto-deleted" : r.autoDeleteMinutes === 0 ? "is deleted as soon as it stops" : `is deleted ${r.autoDeleteMinutes} minutes after it stops`;
1322
+ const destroy = r.expiresAt ? `, and is destroyed at ${r.expiresAt} whatever state it is in` : "";
1323
+ return `${stop} and ${del}${destroy}`;
1324
+ }
1325
+ function teardownRefusedMessage(r) {
1326
+ const key = r.key ? `this DAYTONA_API_KEY ${describeKey(r.key)} lacks the \`${DELETE_SANDBOXES}\` permission` : `Daytona answered 403 (Access denied), which is what a DAYTONA_API_KEY without the \`${DELETE_SANDBOXES}\` permission gets \u2014 the key's own record could not be read to confirm it`;
1327
+ const twin = r.twinId === void 0 ? "" : r.ownsTwin ? ` Its twin ${r.twinId} was deleted before Daytona refused, so nothing is left on the Veris side.` : ` Twin ${r.twinId} is yours and was not touched.`;
1328
+ return `Daytona refused to delete sandbox ${r.sandboxId}: ${key}. The sandbox is still there: it ${sandboxBrakes(r)}.${twin} To delete it sooner, ${DELETE_PERMISSION_FIX}, then run \`veris-daytona teardown ${r.sandboxId}\` with it \u2014 or delete the sandbox in the Daytona dashboard.`;
1329
+ }
871
1330
  export {
1331
+ BUNDLED_CA_FILES,
1332
+ BUNDLED_CA_PATCH_SCRIPT,
872
1333
  CA_CERT_PATH,
873
1334
  ControlPlane,
874
- DEFAULT_REGISTRY_HOSTS,
1335
+ DAYTONA_API_URL,
1336
+ DEFAULT_API_BASE,
1337
+ DELETE_SANDBOXES,
875
1338
  Daytona,
876
1339
  MissingCredentialsError,
1340
+ NODE_PROXY_FLAG,
1341
+ NODE_PROXY_PRELOAD,
1342
+ NODE_TRUST_FLAG,
877
1343
  ReceiptIntegrityError,
878
1344
  SDK_VERSION,
879
1345
  SYSTEM_BUNDLE,
@@ -887,13 +1353,35 @@ export {
887
1353
  VerisGatewayUnreachableError,
888
1354
  VerisUntouchedError,
889
1355
  assertHttpControlPlane,
1356
+ buildNetwork,
1357
+ canDeleteSandboxes,
1358
+ cannotTeardownWarning,
890
1359
  dataPlaneHosts,
1360
+ daytonaApiUrl,
891
1361
  daytona_default as default,
1362
+ directTwinHosts,
1363
+ fetchDaytonaKey,
892
1364
  fetchManual,
1365
+ gatewayIps,
893
1366
  gatewayProxyUrl,
1367
+ isPermissionDenied,
894
1368
  isVerisSandbox,
1369
+ missingKeyMessage,
1370
+ nodeOptionsWithProxy,
1371
+ nodeOptionsWithTrust,
1372
+ nodeProxyPreloadScript,
1373
+ patchBundledCas,
1374
+ profilePath,
1375
+ requireVerisCredentials,
1376
+ resolveVerisCredentials,
1377
+ sandboxBrakes,
1378
+ teardownRefusedMessage,
1379
+ trustPrelude,
895
1380
  twinHosts,
896
1381
  vendorHosts,
897
- vendoredTrustEnv
1382
+ vendoredTrustEnv,
1383
+ verisNodeOptions,
1384
+ verisOwnsTwin,
1385
+ verisTwinId
898
1386
  };
899
1387
  //# sourceMappingURL=index.js.map