@veris-ai/daytona 0.2.0 → 0.3.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.
@@ -45,11 +45,21 @@ export interface EgressCredential {
45
45
  expires_at?: string;
46
46
  /** Server-served CA trust env map; the SDK's vendored list is the fallback. */
47
47
  trust_env?: Record<string, string>;
48
+ /** IPv4 addresses the gateway listens on — what strict mode pins Daytona's
49
+ * networkAllowList to. Absent on a control plane that predates the field;
50
+ * the SDK then resolves the proxy host itself, once. */
51
+ gateway_ips?: string[];
48
52
  }
49
- /** Mutable fields of a running twin. An OMITTED key is left alone; an explicit
50
- * null is a value (client_base_url: null unregisters). */
53
+ /**
54
+ * Mutable fields of a running twin. An OMITTED key is left alone; an explicit
55
+ * null is a value (client_base_url: null unregisters).
56
+ *
57
+ * There is deliberately no ttl_minutes. The server's UpdateSandboxRequest
58
+ * declares client_base_url and nothing else, and pydantic ignores unknown
59
+ * fields rather than refusing them — so a PATCH carrying a TTL answers 200 and
60
+ * changes nothing. See the note where extendTtl used to be.
61
+ */
51
62
  export interface SandboxPatch {
52
- ttl_minutes?: number;
53
63
  client_base_url?: string | null;
54
64
  }
55
65
  export interface ControlPlaneOpts {
@@ -82,8 +92,6 @@ export declare class ControlPlane {
82
92
  mintEgressCredential(environmentId: string, sandboxId: string): Promise<EgressCredential | null>;
83
93
  /** PATCH the twin resource. Omitted fields are untouched by the server. */
84
94
  updateSandbox(environmentId: string, sandboxId: string, patch: SandboxPatch): Promise<void>;
85
- /** Extend a twin's TTL so it stays in lockstep with an extended Daytona sandbox. */
86
- extendTtl(environmentId: string, sandboxId: string, ttlMinutes: number): Promise<void>;
87
95
  /** Create-time preflight: is the gateway infrastructure up, per the control plane? */
88
96
  gatewayHealth(): Promise<void>;
89
97
  }
@@ -0,0 +1,28 @@
1
+ export declare const DAYTONA_API_URL = "https://app.daytona.io/api";
2
+ /** The permission DELETE /api/sandbox/<id> checks for. */
3
+ export declare const DELETE_SANDBOXES = "delete:sandboxes";
4
+ /** The part of GET /api/api-keys/current this package reads. */
5
+ export interface DaytonaKeyInfo {
6
+ name: string;
7
+ permissions: string[];
8
+ }
9
+ /** Where the Daytona API is, resolved the way @daytona/sdk resolves it. */
10
+ export declare function daytonaApiUrl(env?: NodeJS.ProcessEnv): string;
11
+ /**
12
+ * The key's own record, or undefined when Daytona would not say — a network
13
+ * error, a non-2xx, a body without a permissions list. Undefined means
14
+ * "unknown", and a caller must not read it as "cannot delete".
15
+ */
16
+ export declare function fetchDaytonaKey(apiKey: string, apiUrl?: string): Promise<DaytonaKeyInfo | undefined>;
17
+ /** True, false, or undefined when the key's permissions are not known. */
18
+ export declare function canDeleteSandboxes(key: DaytonaKeyInfo | undefined): boolean | undefined;
19
+ /** `"ci-key" (permissions: write:sandboxes)` — how a message names the key. */
20
+ export declare function describeKey(key: DaytonaKeyInfo): string;
21
+ /** How to get a key that can delete. One sentence, used wherever the lack is reported. */
22
+ export declare const DELETE_PERMISSION_FIX = "create a key with the \"delete sandboxes\" permission at https://app.daytona.io/dashboard/keys";
23
+ /**
24
+ * The warning `provision` and `run` print before creating a box with a key
25
+ * that cannot delete one. Says what will happen instead — the box's own
26
+ * brakes — so the reader can decide whether that is good enough.
27
+ */
28
+ export declare function cannotTeardownWarning(key: DaytonaKeyInfo, autoStopMinutes: number, autoDeleteMinutes: number): string;
package/dist/daytona.d.ts CHANGED
@@ -4,25 +4,25 @@ import type { VerisApi } from './veris-api';
4
4
  import type { EgressMode } from './network';
5
5
  import { CA_CERT_PATH } from './trust';
6
6
  export interface VerisOpts {
7
- /** Veris API key. Falls back to process.env.VERIS_API_KEY. Required. */
7
+ /** Veris API key. Falls back to process.env.VERIS_API_KEY, then to the
8
+ * profile `veris login` saved in ~/.veris/twin.yaml. Required. */
8
9
  apiKey?: string;
9
10
  /** Veris environment the twin is deployed from. Falls back to process.env.VERIS_ENVIRONMENT_ID. */
10
11
  environmentId?: string;
11
- /** Control plane base. Falls back to process.env.VERIS_API_BASE, then 'https://svc.api.veris.ai'. */
12
+ /** Control plane base. Falls back to process.env.VERIS_API_BASE, then the
13
+ * profile's api_base, then 'https://svc.api.veris.ai'. */
12
14
  apiBase?: string;
13
15
  /** Attach to an EXISTING twin instead of provisioning one (advanced). delete() will NOT remove it. */
14
16
  attachSandboxId?: string;
15
17
  /** Twin TTL backstop, minutes. Default 60, kept in step with the sandbox's ttlMinutes. */
16
18
  ttlMinutes?: number;
17
- /** 'strict' (default): the sandbox reaches only its twin, its data planes,
18
- * the control plane and package registries. 'open': no allowlist at all —
19
- * debugging only, because a bypassing client then reaches the real vendor. */
19
+ /** 'strict' (default): Daytona may reach the Veris gateway's address and
20
+ * nothing else; everything the code dials goes through the gateway, which
21
+ * answers vendor hostnames from the twin and passes registries and other
22
+ * public hosts through. 'open': no Daytona allowlist — for a control plane
23
+ * that has not published the gateway's addresses. Daytona blocks anything
24
+ * that bypasses the proxy in both modes. */
20
25
  egress?: EgressMode;
21
- /** Extra hostnames to allow out. */
22
- allowOut?: string[];
23
- /** Allow package registries (npm, PyPI, apt, …). Default true: a coding
24
- * sandbox that cannot install dependencies is not usable. */
25
- allowRegistries?: boolean;
26
26
  /** Install the gateway CA into the sandbox trust store. Default true; without
27
27
  * it every HTTPS call to a vendor host fails certificate validation. */
28
28
  installCa?: boolean;
@@ -43,6 +43,24 @@ export type VerisSandbox = Sandbox & {
43
43
  };
44
44
  /** Is this sandbox one of ours? Narrows for callers who hold a bare Sandbox. */
45
45
  export declare function isVerisSandbox(sbx: Sandbox): sbx is VerisSandbox;
46
+ /**
47
+ * The twin id stamped on this sandbox at create, if it has one.
48
+ *
49
+ * Read from the labels rather than from `sandbox.verisSandboxId`, so it answers
50
+ * for a sandbox whose Veris surface could NOT be rehydrated — no VERIS_API_KEY
51
+ * in the environment, say. That is exactly the case where a caller about to
52
+ * delete the sandbox needs telling that a twin exists and is out of reach.
53
+ */
54
+ export declare function verisTwinId(sandbox: Sandbox): string | undefined;
55
+ /**
56
+ * Does deleting this sandbox delete its twin too?
57
+ *
58
+ * True only for a twin this package created. A twin attached to with
59
+ * `veris.attachSandboxId` belongs to whoever made it and outlives the sandbox,
60
+ * and a sandbox with no twin at all has nothing to delete. This is the same
61
+ * label the wrapped `delete()` reads, so the two can never disagree.
62
+ */
63
+ export declare function verisOwnsTwin(sandbox: Sandbox): boolean;
46
64
  export declare class Daytona extends BaseDaytona {
47
65
  private readonly verisDefaults;
48
66
  constructor(config?: VerisDaytonaConfig);
@@ -89,4 +107,62 @@ export declare class Daytona extends BaseDaytona {
89
107
  private baseCreate;
90
108
  }
91
109
  export default Daytona;
110
+ /** What a create that threw left behind in Daytona, once it has been read back. */
111
+ export interface FailedSandbox {
112
+ /** The DAYTONA sandbox id. Not the twin's. */
113
+ id: string;
114
+ /** Daytona's own state for it — `build_failed` for an image that never built. */
115
+ state?: string;
116
+ /** Daytona's account of what went wrong, when it kept one. */
117
+ errorReason?: string;
118
+ /** Whether the delete actually went through. The message says either way,
119
+ * because "was deleted" has to be true or it is worse than saying nothing. */
120
+ deleted: boolean;
121
+ }
122
+ /** The parts of a Daytona Sandbox the reaper touches. */
123
+ interface ReapableSandbox {
124
+ id: string;
125
+ state?: string;
126
+ errorReason?: string;
127
+ labels?: Record<string, string>;
128
+ refreshData(): Promise<void>;
129
+ delete(): Promise<void>;
130
+ }
131
+ /**
132
+ * Delete the half-built sandbox a failed create() left in Daytona, and bring
133
+ * back the reason Daytona recorded for it.
134
+ *
135
+ * The leak this exists for: `create({ image: 'only-on-my-laptop' })` builds
136
+ * server-side and fails at build time, so the SDK throws instead of returning
137
+ * a Sandbox — and a box sits in `build_failed` that nothing here can delete,
138
+ * until an autoDeleteInterval eventually reaps it. Found by the create id
139
+ * stamped in its labels, which is the one thing about it we know for certain.
140
+ *
141
+ * Best effort throughout: the create is already failing, and a reaper that
142
+ * threw would replace the real reason with its own.
143
+ */
144
+ export declare function reapFailedCreate(list: (labels: Record<string, string>) => AsyncIterable<ReapableSandbox>, createId: string): Promise<FailedSandbox | undefined>;
145
+ /**
146
+ * What a failed create says.
147
+ *
148
+ * Two things were missing from "Daytona sandbox create failed: Sandbox 7ba4…
149
+ * failed to start with status: build_failed, error reason: null".
150
+ *
151
+ * The reason, first. Daytona's SDK renders `error reason: ${this.errorReason}`
152
+ * off the Sandbox object it was polling, and the state change that ends the
153
+ * wait arrives over the event stream, which carries the new state and not the
154
+ * reason — so the field is still the null it started as, while Daytona's own
155
+ * record for that sandbox says "pull access denied, repository does not
156
+ * exist". Reading the record back is the whole fix.
157
+ *
158
+ * And, for the failure a first-timer actually hits, whose image it is. Daytona
159
+ * resolves and builds it on its own machines, from a registry; the tag sitting
160
+ * in the laptop's Docker daemon is not something it can see.
161
+ *
162
+ * The cause's own text is folded in HERE rather than left to VerisError, whose
163
+ * fold would otherwise append it after the sentences that explain it.
164
+ */
165
+ export declare function sandboxCreateMessage(cause: unknown, failed: FailedSandbox | undefined, params?: {
166
+ image?: unknown;
167
+ }): string;
92
168
  export { CA_CERT_PATH };
package/dist/exec.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export interface ExecOptions {
2
+ /** The Daytona sandbox id — `provision`'s daytonaSandboxId. Not the twin's. */
3
+ sandboxId: string;
4
+ /** Directory to run in. Unset: the same workDir `provision` printed. */
5
+ cwd?: string;
6
+ /** KEY=VALUE pairs exported on top of the trust environment. */
7
+ env: Record<string, string>;
8
+ /** Seconds the command may run. */
9
+ timeoutSeconds: number;
10
+ /** The command itself, everything after `--`. */
11
+ command: string;
12
+ }
13
+ export declare const EXEC_USAGE = "usage: veris-daytona exec <daytona-sandbox-id> [options] -- <command>\n\nRuns <command> in a sandbox that already exists \u2014 the id `veris-daytona\nprovision` printed as daytonaSandboxId \u2014 with the Veris trust environment\napplied, and streams the output as it happens.\n\n --cwd <dir> directory to run in (default: <sandbox home>/veris-run, provision's workDir)\n --env KEY=VALUE exported on top of the trust environment (repeatable)\n --timeout <seconds> how long <command> may run (default: 1800)\n\nThe trust environment is applied for you, and that is the point of the verb:\nDaytona overwrites SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE and\nNODE_EXTRA_CA_CERTS inside the sandbox with its own CA file, which cannot verify\nthe twin gateway's certificates, and `daytona exec` has no way to set a variable\nat all. Every command here is run with the Veris values exported in front of it.\n--env is applied after them and wins.\n\nOutput streams rather than arriving at the end, so a long install or suite shows\nits progress. No receipt is read and no verdict is passed \u2014 take a watermark\nbefore and read `veris sandbox trace --since` after.\n\nneeds: DAYTONA_API_KEY.\n\nexit code: the command's own. 124 when it ran out of --timeout; 1 when there is\nno such sandbox; 2 on a usage error.";
14
+ /** Parse everything after `exec`. Pure; throws UsageError with a human message. */
15
+ export declare function parseExecArgs(argv: readonly string[]): ExecOptions;
package/dist/gateway.d.ts CHANGED
@@ -25,6 +25,19 @@ export declare function gatewayProxyUrl(credential: {
25
25
  connect_address?: string;
26
26
  username: string;
27
27
  }): string;
28
+ /**
29
+ * The gateway's IPv4 addresses, which strict mode pins Daytona to.
30
+ *
31
+ * The credential's own list wins: the control plane publishes the reserved
32
+ * load-balancer address behind the gateway hostname, and that is the authority
33
+ * on where the gateway is. A control plane that predates the field gets one
34
+ * DNS lookup of the proxy host from here — the caller's resolver is a weaker
35
+ * source, but it is what an older plane leaves. Empty when neither answers;
36
+ * buildNetwork turns that into the error that names the fix.
37
+ */
38
+ export declare function gatewayIps(credential: {
39
+ gateway_ips?: string[];
40
+ }, proxyUrl: string): Promise<string[]>;
28
41
  /**
29
42
  * Make the gateway's CA trusted, without requiring anything of the image.
30
43
  *
@@ -47,12 +60,33 @@ export declare function gatewayProxyUrl(credential: {
47
60
  * Daytona overrides the best-known trust variables with its own CA, correctly
48
61
  * for its proxy. The dozen it does not set still point at this bundle, which
49
62
  * carries both CAs and every public root — so those tools verify rather than
50
- * break.
63
+ * break. Node is the exception that makes ours load-bearing today: it ignores
64
+ * HTTPS_PROXY, is forwarded end to end, and validates OUR leaf with Daytona's
65
+ * file. Its file is read-only, so the store install above is what carries
66
+ * Node: NODE_TRUST_FLAG makes Node read the directory that install fills.
51
67
  *
52
68
  * The system-store install still runs when it can, for anything that reads the
53
69
  * store directly rather than honouring the variables. It is best-effort.
54
70
  */
55
71
  export declare function installCa(sandbox: Sandbox, caPem: string): Promise<void>;
72
+ /**
73
+ * Run the bundled-CA patcher installed above, and report what it changed.
74
+ *
75
+ * The gap it closes: an SDK that ships its own CA bundle and loads it by
76
+ * explicit path reads none of the eighteen trust variables. stripe-python is
77
+ * the measured case — `verify=stripe.ca_bundle_path`, and the first call fails
78
+ * with "Could not verify Stripe's SSL certificate" in a sandbox where curl,
79
+ * Node and `requests` are all fine. This is the Daytona equivalent of the veris
80
+ * CLI's --patch-bundled-cas.
81
+ *
82
+ * Call it AFTER dependencies are installed. Idempotent, so calling it again
83
+ * after installing more costs one filesystem scan and reports nothing new.
84
+ *
85
+ * Never fatal. A sandbox with no such bundle is the normal case, and a scan
86
+ * that could not run is a missed optimisation, not a broken run — the failure
87
+ * it prevents still announces itself clearly in the client's own error.
88
+ */
89
+ export declare function patchBundledCas(sandbox: Sandbox): Promise<string[]>;
56
90
  /**
57
91
  * The canary probe: one HTTPS request from inside the sandbox to a reserved
58
92
  * hostname only the gateway answers, with the twin id in the body.