@sandbox-kit/daytona 0.1.0 → 0.1.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 CHANGED
@@ -1,17 +1,22 @@
1
- # Daytona provider for Sandbox Kit
1
+ # `@sandbox-kit/daytona`
2
2
 
3
- Independent package over `@daytona/sdk@0.225.0`, published separately from the
4
- core `@sandbox-kit/sandbox` client. Use it through that shared API.
3
+ Daytona provider for [`@sandbox-kit/sandbox`](https://www.npmjs.com/package/@sandbox-kit/sandbox).
4
+ Use the shared client API; this package supplies the `daytona()` provider.
5
+
6
+ ## Install
5
7
 
6
8
  ```sh
7
9
  npm install @sandbox-kit/sandbox @sandbox-kit/daytona
8
10
  ```
9
11
 
10
- ## Usage
12
+ Requires Node.js 20, 22, or 24.15+, and a Daytona API key (or credentials your
13
+ Daytona SDK already resolves from the environment).
14
+
15
+ ## Quick start
11
16
 
12
17
  ```ts
13
- import { Client } from "@sandbox-kit/sandbox";
14
18
  import { daytona } from "@sandbox-kit/daytona";
19
+ import { Client } from "@sandbox-kit/sandbox";
15
20
 
16
21
  await using client = new Client({
17
22
  provider: daytona(),
@@ -19,38 +24,68 @@ await using client = new Client({
19
24
  timeout: 120_000,
20
25
  });
21
26
 
27
+ // Default snapshot for the target:
28
+ const sandbox = await client.create();
29
+ console.log(sandbox.id);
30
+ ```
31
+
32
+ Create from an image and size resources:
33
+
34
+ ```ts
22
35
  const sandbox = await client.create({
23
36
  source: { image: { reference: "daytonaio/playground:latest" } },
37
+ resources: {
38
+ cpuCores: 2,
39
+ memoryMib: 4096n,
40
+ diskMib: 8192n,
41
+ },
24
42
  runtime: { language: "typescript" },
25
43
  });
26
- console.log(sandbox.id);
27
44
  ```
28
45
 
29
- Auth may be omitted when the native Daytona SDK should resolve credentials from
30
- its environment. Closing the client releases local SDK resources only.
46
+ Closing the client releases local SDK resources only. Daytona keeps the sandbox
47
+ until you stop or delete it, or a lifetime policy fires.
48
+
49
+ ## Configuration
50
+
51
+ | Setting | Notes |
52
+ | --------------- | ------------------------------------------------------------- |
53
+ | API key | `auth: { apiKey: { key } }` |
54
+ | Bearer / JWT | `auth: { bearerToken: { token } }` — requires organization ID |
55
+ | Omitted `auth` | Daytona SDK environment credentials |
56
+ | Organization | `scope: { organizationId }` |
57
+ | Endpoint | `endpoint` — defaults to `https://app.daytona.io/api` |
58
+ | Region / target | `region` |
59
+
60
+ Remote endpoints require HTTPS. HTTP is allowed for localhost or loopback only.
61
+
62
+ ## What you can do
63
+
64
+ **Create** from the default snapshot, a named snapshot, or a registry image.
65
+ Optional runtime language/user, network allowlists, secrets, labels, TTL,
66
+ ephemeral behavior, and stop/pause/archive/delete policies.
67
+
68
+ **Lifecycle** on the handle: `get`, `refresh`, `start`, `stop`, `pause`,
69
+ `archive`, `delete`, and local `close`. Capability depends on the sandbox class
70
+ (container, VM, Windows, GPU). Check with `sandbox.supports(...)` or
71
+ `sandbox.capabilities()`.
72
+
73
+ Daytona does not support unverified `attach`. Use `client.get(id)` for a
74
+ provider-verified handle.
75
+
76
+ CPU requests are whole cores. Memory and disk are whole GiB expressed as MiB.
77
+ Resource overrides on create require an **image** source; snapshot creates
78
+ inherit snapshot resources.
31
79
 
32
80
  ## Examples
33
81
 
34
- | Example | Project |
35
- | ---------- | -------------------------------------------------------------------------------------------------------------- |
36
- | Creation | [create-daytona-sandbox](../../../../examples/typescript/daytona/create-daytona-sandbox/README.md) |
37
- | Policies | [create-daytona-with-policies](../../../../examples/typescript/daytona/create-daytona-with-policies/README.md) |
38
- | Retrieval | [get-daytona-sandbox](../../../../examples/typescript/daytona/get-daytona-sandbox/README.md) |
39
- | Full index | [TypeScript examples](../../../../examples/typescript/README.md) |
40
-
41
- ## Implementation notes
42
-
43
- Checks and portable field mappings are handwritten in `checks/provider_checks.ts`,
44
- `mappings/provider_mappings.ts`, and `config/provider_config.ts`, kept in sync with
45
- `specs/providers/daytona.yaml`. `src/daytona.ts` owns typed Daytona SDK call
46
- ordering and lifecycle orchestration; `src/mappings.ts` owns remaining
47
- provider-specific composition.
48
-
49
- ## Dependency audit
50
-
51
- `npm audit` reports four high-severity findings in the pinned
52
- `@daytona/sdk@0.225.0` dependency chain (`fast-glob` → `micromatch` → `braces`).
53
- The registry reports no fix (`braces` latest is still vulnerable; Daytona still
54
- depends on `fast-glob`). The provider does not override those transitive
55
- packages, because overrides could create an unsupported SDK combination.
56
- Reassess the audit when Daytona drops or patches that chain.
82
+ | Example | Project |
83
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | Creation | [create-daytona-sandbox](https://github.com/sandbox-kit/kit/tree/main/examples/typescript/daytona/create-daytona-sandbox) |
85
+ | Policies | [create-daytona-with-policies](https://github.com/sandbox-kit/kit/tree/main/examples/typescript/daytona/create-daytona-with-policies) |
86
+ | Retrieval | [get-daytona-sandbox](https://github.com/sandbox-kit/kit/tree/main/examples/typescript/daytona/get-daytona-sandbox) |
87
+ | Full index | [TypeScript examples](https://github.com/sandbox-kit/kit/tree/main/examples/typescript) |
88
+
89
+ ## License
90
+
91
+ MIT
@@ -1,4 +1,5 @@
1
- import { ErrorKind, PolicyMode, sandboxError, CreateFieldLifetime, CreateFieldNetworkOutboundCidrsEntries, CreateFieldNetworkOutboundDomainsEntries, CreateFieldNetworkOutboundProxyUrl, CreateFieldRuntimeLanguage, CreateFieldSecuritySecretsEnvironmentVariable } from "@sandbox-kit/sandbox";
1
+ import { ErrorKind, PolicyMode, CreateFieldLifetime, CreateFieldNetworkOutboundCidrsEntries, CreateFieldNetworkOutboundDomainsEntries, CreateFieldNetworkOutboundProxyUrl, CreateFieldRuntimeLanguage, CreateFieldSecuritySecretsEnvironmentVariable, validCidr, validDomain, validEnvName, } from "@sandbox-kit/sandbox";
2
+ import { providerError } from "#errors.js";
2
3
  // validateProviderCreate rejects creation settings this provider cannot honor.
3
4
  // Rules are inventoried in specs/providers/daytona.yaml checks.
4
5
  export function validateProviderCreate(request) {
@@ -7,48 +8,98 @@ export function validateProviderCreate(request) {
7
8
  language !== "python" &&
8
9
  language !== "javascript" &&
9
10
  language !== "typescript") {
10
- fail(ErrorKind.Unsupported, CreateFieldRuntimeLanguage, "runtime.language is outside supported values");
11
+ providerError({
12
+ kind: ErrorKind.Unsupported,
13
+ operation: "create",
14
+ field: CreateFieldRuntimeLanguage,
15
+ message: "runtime.language is outside supported values",
16
+ });
11
17
  }
12
18
  const outboundProxyUrl = request.network?.outboundProxyUrl;
13
19
  if (outboundProxyUrl !== undefined && !validHttpUrl(outboundProxyUrl)) {
14
- fail(ErrorKind.InvalidArgument, CreateFieldNetworkOutboundProxyUrl, "network.outbound_proxy_url is outside supported values");
20
+ providerError({
21
+ kind: ErrorKind.InvalidArgument,
22
+ operation: "create",
23
+ field: CreateFieldNetworkOutboundProxyUrl,
24
+ message: "network.outbound_proxy_url is outside supported values",
25
+ });
15
26
  }
16
27
  for (const value of request.network?.outboundCidrs ?? []) {
17
28
  if (!validCidr(value)) {
18
- fail(ErrorKind.InvalidArgument, CreateFieldNetworkOutboundCidrsEntries, "network.outbound_cidrs.entries is outside supported values");
29
+ providerError({
30
+ kind: ErrorKind.InvalidArgument,
31
+ operation: "create",
32
+ field: CreateFieldNetworkOutboundCidrsEntries,
33
+ message: "network.outbound_cidrs.entries is outside supported values",
34
+ });
19
35
  }
20
36
  }
21
37
  for (const value of request.network?.outboundDomains ?? []) {
22
38
  if (!validDomain(value)) {
23
- fail(ErrorKind.InvalidArgument, CreateFieldNetworkOutboundDomainsEntries, "network.outbound_domains.entries is outside supported values");
39
+ providerError({
40
+ kind: ErrorKind.InvalidArgument,
41
+ operation: "create",
42
+ field: CreateFieldNetworkOutboundDomainsEntries,
43
+ message: "network.outbound_domains.entries is outside supported values",
44
+ });
24
45
  }
25
46
  }
26
47
  for (const item of request.security?.secrets ?? []) {
27
48
  const environmentVariable = item.environmentVariable;
28
49
  if (environmentVariable !== undefined &&
29
50
  !validEnvName(environmentVariable)) {
30
- fail(ErrorKind.InvalidArgument, CreateFieldSecuritySecretsEnvironmentVariable, "security.secrets.environment_variable is outside supported values");
51
+ providerError({
52
+ kind: ErrorKind.InvalidArgument,
53
+ operation: "create",
54
+ field: CreateFieldSecuritySecretsEnvironmentVariable,
55
+ message: "security.secrets.environment_variable is outside supported values",
56
+ });
31
57
  }
32
58
  }
33
59
  if (delayed(request.lifetime?.idleStop) &&
34
60
  delayed(request.lifetime?.idlePause)) {
35
- invalid(CreateFieldLifetime, "incompatible lifetime policies");
61
+ providerError({
62
+ kind: ErrorKind.InvalidArgument,
63
+ operation: "create",
64
+ field: CreateFieldLifetime,
65
+ message: "incompatible lifetime policies",
66
+ });
36
67
  }
37
68
  if (delayed(request.lifetime?.idlePause) &&
38
69
  request.lifetime?.ephemeral === true) {
39
- invalid(CreateFieldLifetime, "incompatible lifetime policies");
70
+ providerError({
71
+ kind: ErrorKind.InvalidArgument,
72
+ operation: "create",
73
+ field: CreateFieldLifetime,
74
+ message: "incompatible lifetime policies",
75
+ });
40
76
  }
41
77
  if (delayed(request.lifetime?.idlePause) &&
42
78
  immediate(request.lifetime?.stoppedDelete)) {
43
- invalid(CreateFieldLifetime, "incompatible lifetime policies");
79
+ providerError({
80
+ kind: ErrorKind.InvalidArgument,
81
+ operation: "create",
82
+ field: CreateFieldLifetime,
83
+ message: "incompatible lifetime policies",
84
+ });
44
85
  }
45
86
  if (delayed(request.lifetime?.stoppedDelete) &&
46
87
  request.lifetime?.ephemeral === true) {
47
- invalid(CreateFieldLifetime, "incompatible lifetime policies");
88
+ providerError({
89
+ kind: ErrorKind.InvalidArgument,
90
+ operation: "create",
91
+ field: CreateFieldLifetime,
92
+ message: "incompatible lifetime policies",
93
+ });
48
94
  }
49
95
  if (delayed(request.lifetime?.stoppedArchive) &&
50
96
  request.lifetime?.ephemeral === true) {
51
- invalid(CreateFieldLifetime, "incompatible lifetime policies");
97
+ providerError({
98
+ kind: ErrorKind.InvalidArgument,
99
+ operation: "create",
100
+ field: CreateFieldLifetime,
101
+ message: "incompatible lifetime policies",
102
+ });
52
103
  }
53
104
  }
54
105
  function delayed(policy) {
@@ -61,9 +112,6 @@ function immediate(policy) {
61
112
  policy?.after !== undefined &&
62
113
  policy.after === 0);
63
114
  }
64
- function validEnvName(value) {
65
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
66
- }
67
115
  function validHttpUrl(value) {
68
116
  try {
69
117
  const parsed = new URL(value);
@@ -74,64 +122,3 @@ function validHttpUrl(value) {
74
122
  return false;
75
123
  }
76
124
  }
77
- function validDomain(value) {
78
- const domain = value.startsWith("*.") ? value.slice(2) : value;
79
- return (domain.length <= 253 &&
80
- /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.?$/i.test(domain));
81
- }
82
- function validCidr(value) {
83
- const separator = value.lastIndexOf("/");
84
- if (separator <= 0)
85
- return false;
86
- const address = value.slice(0, separator);
87
- const prefix = Number(value.slice(separator + 1));
88
- if (!Number.isInteger(prefix))
89
- return false;
90
- return address.includes(":")
91
- ? prefix >= 0 && prefix <= 128 && validIpv6(address)
92
- : prefix >= 0 && prefix <= 32 && validIpv4(address);
93
- }
94
- function validIpv4(value) {
95
- const parts = value.split(".");
96
- return (parts.length === 4 &&
97
- parts.every((part) => /^(0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
98
- }
99
- function validIpv6(value) {
100
- if ((value.match(/::/g)?.length ?? 0) > 1)
101
- return false;
102
- const halves = value.split("::");
103
- if (halves.length > 2)
104
- return false;
105
- const count = (part) => {
106
- if (part === "")
107
- return 0;
108
- const groups = part.split(":");
109
- let total = 0;
110
- for (const group of groups) {
111
- if (/^[0-9a-f]{1,4}$/i.test(group))
112
- total++;
113
- else if (validIpv4(group))
114
- total += 2;
115
- else
116
- return undefined;
117
- }
118
- return total;
119
- };
120
- const left = count(halves[0] ?? "");
121
- const right = count(halves[1] ?? "");
122
- return (left !== undefined &&
123
- right !== undefined &&
124
- (halves.length === 2 ? left + right < 8 : left === 8));
125
- }
126
- function invalid(field, message) {
127
- fail(ErrorKind.InvalidArgument, field, message);
128
- }
129
- function fail(kind, field, message) {
130
- throw sandboxError({
131
- kind,
132
- provider: "daytona",
133
- operation: "create",
134
- field,
135
- message: "sandbox-kit daytona: " + message,
136
- });
137
- }
@@ -1,4 +1,3 @@
1
1
  import type { DaytonaConfig as NativeClientConfig } from "@daytona/sdk";
2
2
  import { type Config } from "@sandbox-kit/sandbox";
3
- export type Environment = Readonly<Record<string, string | undefined>>;
4
- export declare function mapGeneratedClientConfig(config: Config, environment?: Environment): NativeClientConfig;
3
+ export declare function mapGeneratedClientConfig(config: Config): NativeClientConfig;
@@ -1,17 +1,13 @@
1
1
  // Client configuration composition for the native SDK constructor.
2
2
  // Kept in sync with specs/providers/daytona.yaml client: rules (not emitted).
3
- import { ErrorKind, sandboxError, validateConfig, ConfigFieldAuth, ConfigFieldScopeAppName, ConfigFieldScopeEnvironment, ConfigFieldScopeProjectId } from "@sandbox-kit/sandbox";
3
+ import { ErrorKind, ConfigFieldAuth, ConfigFieldScopeAppName, ConfigFieldScopeEnvironment, ConfigFieldScopeProjectId, } from "@sandbox-kit/sandbox";
4
4
  import { mapAPIKeyCredentials, mapBearerAuth, mapClientSettings, mapScope, } from "#mappings/provider_mappings.js";
5
- export function mapGeneratedClientConfig(config, environment = environmentVariables()) {
6
- let source = config;
7
- if (source.endpoint === undefined)
8
- source = {
9
- ...source,
10
- endpoint: environment.DAYTONA_API_URL ??
11
- environment.DAYTONA_SERVER_URL ??
12
- "https://app.daytona.io/api",
13
- };
14
- validateConfig(source, "daytona", "initialize");
5
+ import { providerError } from "#errors.js";
6
+ const defaultEndpoint = "https://app.daytona.io/api";
7
+ export function mapGeneratedClientConfig(config) {
8
+ const source = config.endpoint === undefined
9
+ ? { ...config, endpoint: defaultEndpoint }
10
+ : config;
15
11
  const target = mapClientSettings(source, "initialize");
16
12
  if (source.scope !== undefined) {
17
13
  const mapped = mapScope(source.scope, "initialize");
@@ -34,16 +30,11 @@ export function mapGeneratedClientConfig(config, environment = environmentVariab
34
30
  }
35
31
  return target;
36
32
  }
37
- function environmentVariables() {
38
- return (globalThis
39
- .process?.env ?? {});
40
- }
41
33
  function unsupported(field, message) {
42
- throw sandboxError({
34
+ providerError({
43
35
  kind: ErrorKind.Unsupported,
44
- provider: "daytona",
45
36
  operation: "initialize",
46
37
  field,
47
- message: "sandbox-kit daytona: " + message,
38
+ message,
48
39
  });
49
40
  }
package/dist/daytona.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Daytona, } from "@daytona/sdk";
2
- import { durationMs } from "@bufbuild/protobuf/wkt";
3
- import { IsolationKind, LifecycleCapability, metadataObjectAsMap } from "@sandbox-kit/sandbox";
2
+ import { IsolationKind, LifecycleCapability, metadataObjectAsMap, } from "@sandbox-kit/sandbox";
4
3
  import { mapClientConfig, mapCreateRequest, mapSandboxInfo, } from "#mappings/mappings.js";
5
- import { mapDaytonaError } from "./errors.js";
4
+ import { mapDaytonaError } from "#errors.js";
6
5
  import { BackendBase, ProviderBase } from "./provider.js";
7
6
  /** @internal Exported from this module for boundary testing, but not from the package root. */
8
7
  export class DaytonaProvider extends ProviderBase {
@@ -30,7 +29,7 @@ class DaytonaBackend extends BackendBase {
30
29
  try {
31
30
  options.signal?.throwIfAborted();
32
31
  const params = mapCreateRequest(request);
33
- const timeout = options.timeout === undefined ? undefined : options.timeout / 1_000;
32
+ const timeout = secondsFromMs(options.timeout);
34
33
  const sandbox = await this.native.create(params, timeout === undefined ? undefined : { timeout });
35
34
  return { sandbox: mapSandboxInfo(sandbox, "create") };
36
35
  }
@@ -65,19 +64,19 @@ class DaytonaBackend extends BackendBase {
65
64
  });
66
65
  }
67
66
  async start(id, request) {
68
- return this.withSandbox(id, "start", (value) => value.start(seconds(request.timeout)));
67
+ return this.withSandbox(id, "start", (value) => value.start(secondsFromMs(request.timeout)));
69
68
  }
70
69
  async stop(id, request) {
71
- return this.withSandbox(id, "stop", (value) => value.stop(seconds(request.timeout), request.force));
70
+ return this.withSandbox(id, "stop", (value) => value.stop(secondsFromMs(request.timeout), request.force));
72
71
  }
73
72
  async pause(id, request) {
74
- return this.withSandbox(id, "pause", (value) => value.pause(seconds(request.timeout)));
73
+ return this.withSandbox(id, "pause", (value) => value.pause(secondsFromMs(request.timeout)));
75
74
  }
76
75
  async archive(id, _request) {
77
76
  return this.withSandbox(id, "archive", (value) => value.archive());
78
77
  }
79
78
  async delete(id, request) {
80
- return this.withSandbox(id, "delete", (value) => value.delete(seconds(request.timeout), request.wait));
79
+ return this.withSandbox(id, "delete", (value) => value.delete(secondsFromMs(request.timeout), request.wait));
81
80
  }
82
81
  async closeSandbox(_id) { }
83
82
  async withSandbox(id, operation, action) {
@@ -99,8 +98,9 @@ class DaytonaBackend extends BackendBase {
99
98
  }
100
99
  }
101
100
  }
102
- function seconds(value) {
103
- return value === undefined ? undefined : durationMs(value) / 1_000;
101
+ /** Native Daytona lifecycle APIs take whole seconds; Kit timeouts are ms. */
102
+ function secondsFromMs(value) {
103
+ return value === undefined ? undefined : value / 1_000;
104
104
  }
105
105
  /** Selects Daytona as the provider for the shared Sandbox Kit client. */
106
106
  export function daytona() {
package/dist/errors.d.ts CHANGED
@@ -1,2 +1,11 @@
1
- import { SandboxError } from "@sandbox-kit/sandbox";
1
+ import { SandboxError, type ErrorInfo } from "@sandbox-kit/sandbox";
2
+ /** Inputs for throwing a Daytona-scoped validation or mapping error. */
3
+ export interface ProviderErrorInput {
4
+ kind: ErrorInfo["kind"];
5
+ operation: string;
6
+ field: string;
7
+ message: string;
8
+ }
2
9
  export declare function mapDaytonaError(error: unknown, operation: string): SandboxError;
10
+ /** Local throw helper for mappings, checks, and config. */
11
+ export declare function providerError(input: ProviderErrorInput): never;
package/dist/errors.js CHANGED
@@ -2,14 +2,14 @@
2
2
  // Classification order: existing SandboxError, abort/timeout DOMException,
3
3
  // then DaytonaError code/status maps, then unknown. Cause stays on SandboxError.cause.
4
4
  import { DaytonaError } from "@daytona/sdk";
5
- import { ErrorKind, SandboxError, sandboxError } from "@sandbox-kit/sandbox";
5
+ import { ErrorKind, SandboxError, sandboxError, } from "@sandbox-kit/sandbox";
6
6
  export function mapDaytonaError(error, operation) {
7
7
  if (error instanceof SandboxError)
8
8
  return error;
9
9
  if (error instanceof DOMException && error.name === "AbortError")
10
- return create(ErrorKind.Canceled, error, operation);
10
+ return create({ kind: ErrorKind.Canceled, error, operation });
11
11
  if (error instanceof DOMException && error.name === "TimeoutError")
12
- return create(ErrorKind.Timeout, error, operation);
12
+ return create({ kind: ErrorKind.Timeout, error, operation });
13
13
  let kind = ErrorKind.Unknown;
14
14
  let statusCode;
15
15
  let providerCode;
@@ -72,8 +72,37 @@ export function mapDaytonaError(error, operation) {
72
72
  if (kind === ErrorKind.Unknown && error.statusCode === 0 && !error.code)
73
73
  kind = ErrorKind.Unavailable;
74
74
  }
75
- return create(kind, error, operation, statusCode, providerCode, providerSource);
75
+ return create({
76
+ kind,
77
+ error,
78
+ operation,
79
+ ...(statusCode === undefined ? {} : { statusCode }),
80
+ ...(providerCode === undefined ? {} : { providerCode }),
81
+ ...(providerSource === undefined ? {} : { providerSource }),
82
+ });
76
83
  }
77
- function create(kind, error, operation, statusCode, providerCode, providerSource) {
78
- return sandboxError({ kind, provider: "daytona", operation, field: "", message: error instanceof Error ? error.message : "sandbox-kit daytona: provider operation failed", ...(statusCode === undefined ? {} : { statusCode }), ...(providerCode === undefined ? {} : { providerCode }), ...(providerSource === undefined ? {} : { providerSource }) }, error);
84
+ function create(input) {
85
+ const { kind, error, operation, statusCode, providerCode, providerSource } = input;
86
+ return sandboxError({
87
+ kind,
88
+ provider: "daytona",
89
+ operation,
90
+ field: "",
91
+ message: error instanceof Error
92
+ ? error.message
93
+ : "sandbox-kit daytona: provider operation failed",
94
+ ...(statusCode === undefined ? {} : { statusCode }),
95
+ ...(providerCode === undefined ? {} : { providerCode }),
96
+ ...(providerSource === undefined ? {} : { providerSource }),
97
+ }, error);
98
+ }
99
+ /** Local throw helper for mappings, checks, and config. */
100
+ export function providerError(input) {
101
+ throw sandboxError({
102
+ kind: input.kind,
103
+ provider: "daytona",
104
+ operation: input.operation,
105
+ field: input.field,
106
+ message: "sandbox-kit daytona: " + input.message,
107
+ });
79
108
  }
@@ -1,7 +1,6 @@
1
1
  import { type Config, type CreateOptions, type SandboxInfo } from "@sandbox-kit/sandbox";
2
2
  import { type CreateSandboxFromImageParams, type CreateSandboxFromSnapshotParams, type DaytonaConfig, type Sandbox as DaytonaSandbox } from "@daytona/sdk";
3
- import { type Environment } from "#config/provider_config.js";
4
3
  export type DaytonaCreateParams = CreateSandboxFromImageParams | CreateSandboxFromSnapshotParams;
5
- export declare function mapClientConfig(config: Config, environment?: Environment): DaytonaConfig;
4
+ export declare function mapClientConfig(config: Config): DaytonaConfig;
6
5
  export declare function mapCreateRequest(request: CreateOptions): DaytonaCreateParams;
7
6
  export declare function mapSandboxInfo(value: DaytonaSandbox, operation?: string): SandboxInfo;
@@ -1,12 +1,13 @@
1
1
  // Provider-specific orchestration around generated portable field mappings.
2
- import { EgressMode, ErrorKind, IsolationKind, SandboxState, SecretInjection, SnapshotKind, ValueOrigin, WaitCondition, sandboxError, CreateFieldIsolation, CreateFieldNetwork, CreateFieldNetworkEgress, CreateFieldNetworkLinkedSandbox, CreateFieldObservabilityVerbose, CreateFieldPlacement, CreateFieldProvisioningWaitFor, CreateFieldReadiness, CreateFieldResources, CreateFieldResourcesCpuCores, CreateFieldRuntime, CreateFieldSecuritySecretsEnvironmentVariable, CreateFieldSecuritySecretsInjection, CreateFieldSourceSnapshotKind, CreateFieldRuntimeUser, CreateFieldSecuritySecretsReference, CreateFieldSecurityWorkloadIdentity, CreateFieldSourceImageReference, CreateFieldSourceSnapshotProviderKind, CreateFieldSourceSnapshotReference, CreateFieldSourceWarmPool, CreateFieldNetworkOutboundCidrs, CreateFieldNetworkOutboundDomains, CreateFieldLifetimeIdleTerminate, CreateFieldObservabilityTelemetryEndpoint, } from "@sandbox-kit/sandbox";
2
+ import { EgressMode, ErrorKind, IsolationKind, SandboxState, SecretInjection, SnapshotKind, ValueOrigin, WaitCondition, CreateFieldIsolation, CreateFieldNetwork, CreateFieldNetworkEgress, CreateFieldNetworkLinkedSandbox, CreateFieldObservabilityVerbose, CreateFieldPlacement, CreateFieldProvisioningWaitFor, CreateFieldReadiness, CreateFieldResources, CreateFieldResourcesCpuCores, CreateFieldRuntime, CreateFieldSecuritySecretsEnvironmentVariable, CreateFieldSecuritySecretsInjection, CreateFieldSourceSnapshotKind, CreateFieldRuntimeUser, CreateFieldSecuritySecretsReference, CreateFieldSecurityWorkloadIdentity, CreateFieldSourceImageReference, CreateFieldSourceSnapshotProviderKind, CreateFieldSourceSnapshotReference, CreateFieldSourceWarmPool, CreateFieldNetworkOutboundCidrs, CreateFieldNetworkOutboundDomains, CreateFieldLifetimeIdleTerminate, CreateFieldObservabilityTelemetryEndpoint, } from "@sandbox-kit/sandbox";
3
3
  import { metadataObjectFromMap } from "@sandbox-kit/sandbox";
4
4
  import {} from "@daytona/sdk";
5
5
  import { mapAllocatedResources as generatedMapAllocatedResources, mapCommonCreateFields as generatedMapCommonCreateFields, mapLifetimePolicies as generatedMapLifetimePolicies, mapResources as generatedMapResources, mapSandboxInfo as generatedMapSandboxInfo, } from "./provider_mappings.js";
6
6
  import { validateProviderCreate } from "#checks/provider_checks.js";
7
- import { mapGeneratedClientConfig, } from "#config/provider_config.js";
8
- export function mapClientConfig(config, environment) {
9
- const result = mapGeneratedClientConfig(config, environment);
7
+ import { mapGeneratedClientConfig } from "#config/provider_config.js";
8
+ import { providerError } from "#errors.js";
9
+ export function mapClientConfig(config) {
10
+ const result = mapGeneratedClientConfig(config);
10
11
  if (config.auth?.bearerToken !== undefined &&
11
12
  config.scope?.organizationId === undefined)
12
13
  configInvalid("scope.organizationId", "organization ID is required with bearer authentication");
@@ -313,29 +314,26 @@ function nonblank(value, field) {
313
314
  return value;
314
315
  }
315
316
  function invalid(field, message) {
316
- throw sandboxError({
317
+ providerError({
317
318
  kind: ErrorKind.InvalidArgument,
318
- provider: "daytona",
319
319
  operation: "create",
320
320
  field,
321
- message: "sandbox-kit daytona: " + message,
321
+ message,
322
322
  });
323
323
  }
324
324
  function unsupported(field, message) {
325
- throw sandboxError({
325
+ providerError({
326
326
  kind: ErrorKind.Unsupported,
327
- provider: "daytona",
328
327
  operation: "create",
329
328
  field,
330
- message: "sandbox-kit daytona: " + message,
329
+ message,
331
330
  });
332
331
  }
333
332
  function configInvalid(field, message) {
334
- throw sandboxError({
333
+ providerError({
335
334
  kind: ErrorKind.InvalidArgument,
336
- provider: "daytona",
337
335
  operation: "initialize",
338
336
  field,
339
- message: "sandbox-kit daytona: " + message,
337
+ message,
340
338
  });
341
339
  }
@@ -1,95 +1,101 @@
1
1
  // Provider field mappings between shared CreateOptions/Config and the native SDK.
2
2
  // Inventoried in specs/providers/daytona.yaml groups; kept in sync by hand (not emitted).
3
- import { ErrorKind, PolicyMode, ValueOrigin, sandboxError, CreateFieldLifetimeIdlePause, CreateFieldLifetimeIdleStop, CreateFieldLifetimeStoppedArchive, CreateFieldLifetimeStoppedDelete, CreateFieldResourcesCpuCores, CreateFieldResourcesDiskMib, CreateFieldResourcesMemoryMib, InfoFieldId, InfoFieldLabels, InfoFieldName, InfoFieldProviderState, InfoFieldRegion, } from "@sandbox-kit/sandbox";
3
+ import { ErrorKind, PolicyMode, ValueOrigin, CreateFieldLifetimeIdlePause, CreateFieldLifetimeIdleStop, CreateFieldLifetimeStoppedArchive, CreateFieldLifetimeStoppedDelete, CreateFieldResourcesCpuCores, CreateFieldResourcesDiskMib, CreateFieldResourcesMemoryMib, InfoFieldId, InfoFieldLabels, InfoFieldName, InfoFieldProviderState, InfoFieldRegion, } from "@sandbox-kit/sandbox";
4
+ import { providerError } from "#errors.js";
4
5
  export function mapLifetimePolicies(source, operation = "create") {
5
6
  const target = {};
6
7
  if (source === undefined)
7
8
  return target;
8
- if (source.idleStop !== undefined) {
9
- const policy = source.idleStop;
10
- const mode = policy.mode ?? PolicyMode.Default;
11
- if (mode === PolicyMode.Disabled) {
12
- target.autoStopInterval = 0;
13
- }
14
- else if (mode === PolicyMode.After) {
15
- if (policy.after === undefined)
16
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdleStop, "duration is required", operation);
17
- const afterMs = policy.after;
18
- if (afterMs === 0)
19
- mappingError(ErrorKind.Unsupported, CreateFieldLifetimeIdleStop, "immediate action is not representable", operation);
20
- if (!Number.isFinite(afterMs) || afterMs < 0 || afterMs % 60_000 !== 0)
21
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdleStop, "delay cannot be represented in whole minutes", operation);
22
- const minutes = afterMs / 60_000;
23
- target.autoStopInterval = minutes;
24
- }
25
- else if (mode !== PolicyMode.Default) {
26
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdleStop, "unknown policy mode", operation);
27
- }
28
- }
29
- if (source.idlePause !== undefined) {
30
- const policy = source.idlePause;
31
- const mode = policy.mode ?? PolicyMode.Default;
32
- if (mode === PolicyMode.Disabled) {
33
- target.autoPauseInterval = 0;
34
- }
35
- else if (mode === PolicyMode.After) {
36
- if (policy.after === undefined)
37
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdlePause, "duration is required", operation);
38
- const afterMs = policy.after;
39
- if (afterMs === 0)
40
- mappingError(ErrorKind.Unsupported, CreateFieldLifetimeIdlePause, "immediate action is not representable", operation);
41
- if (!Number.isFinite(afterMs) || afterMs < 0 || afterMs % 60_000 !== 0)
42
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdlePause, "delay cannot be represented in whole minutes", operation);
43
- const minutes = afterMs / 60_000;
44
- target.autoPauseInterval = minutes;
45
- }
46
- else if (mode !== PolicyMode.Default) {
47
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeIdlePause, "unknown policy mode", operation);
48
- }
49
- }
50
- if (source.stoppedArchive !== undefined) {
51
- const policy = source.stoppedArchive;
52
- const mode = policy.mode ?? PolicyMode.Default;
53
- if (mode === PolicyMode.Disabled) {
54
- mappingError(ErrorKind.Unsupported, CreateFieldLifetimeStoppedArchive, "explicit disabling is not representable", operation);
55
- }
56
- else if (mode === PolicyMode.After) {
57
- if (policy.after === undefined)
58
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedArchive, "duration is required", operation);
59
- const afterMs = policy.after;
60
- if (afterMs === 0)
61
- mappingError(ErrorKind.Unsupported, CreateFieldLifetimeStoppedArchive, "immediate action is not representable", operation);
62
- if (!Number.isFinite(afterMs) || afterMs < 0 || afterMs % 60_000 !== 0)
63
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedArchive, "delay cannot be represented in whole minutes", operation);
64
- const minutes = afterMs / 60_000;
65
- if (minutes > 43200)
66
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedArchive, "delay exceeds documented maximum", operation);
67
- target.autoArchiveInterval = minutes;
68
- }
69
- else if (mode !== PolicyMode.Default) {
70
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedArchive, "unknown policy mode", operation);
71
- }
9
+ const idleStop = mapMinutePolicy(source.idleStop, {
10
+ field: CreateFieldLifetimeIdleStop,
11
+ operation,
12
+ allowDisable: true,
13
+ forbidImmediate: true,
14
+ });
15
+ if (idleStop !== undefined)
16
+ target.autoStopInterval = idleStop;
17
+ const idlePause = mapMinutePolicy(source.idlePause, {
18
+ field: CreateFieldLifetimeIdlePause,
19
+ operation,
20
+ allowDisable: true,
21
+ forbidImmediate: true,
22
+ });
23
+ if (idlePause !== undefined)
24
+ target.autoPauseInterval = idlePause;
25
+ const stoppedArchive = mapMinutePolicy(source.stoppedArchive, {
26
+ field: CreateFieldLifetimeStoppedArchive,
27
+ operation,
28
+ allowDisable: false,
29
+ forbidImmediate: true,
30
+ maxMinutes: 43200,
31
+ });
32
+ if (stoppedArchive !== undefined)
33
+ target.autoArchiveInterval = stoppedArchive;
34
+ const stoppedDelete = mapMinutePolicy(source.stoppedDelete, {
35
+ field: CreateFieldLifetimeStoppedDelete,
36
+ operation,
37
+ allowDisable: false,
38
+ forbidImmediate: false,
39
+ });
40
+ if (stoppedDelete !== undefined)
41
+ target.autoDeleteInterval = stoppedDelete;
42
+ return target;
43
+ }
44
+ function mapMinutePolicy(policy, spec) {
45
+ if (policy === undefined)
46
+ return undefined;
47
+ const mode = policy.mode ?? PolicyMode.Default;
48
+ if (mode === PolicyMode.Disabled) {
49
+ if (!spec.allowDisable)
50
+ mappingError({
51
+ kind: ErrorKind.Unsupported,
52
+ field: spec.field,
53
+ message: "explicit disabling is not representable",
54
+ operation: spec.operation,
55
+ });
56
+ return 0;
72
57
  }
73
- if (source.stoppedDelete !== undefined) {
74
- const policy = source.stoppedDelete;
75
- const mode = policy.mode ?? PolicyMode.Default;
76
- if (mode === PolicyMode.Disabled) {
77
- mappingError(ErrorKind.Unsupported, CreateFieldLifetimeStoppedDelete, "explicit disabling is not representable", operation);
78
- }
79
- else if (mode === PolicyMode.After) {
80
- if (policy.after === undefined)
81
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedDelete, "duration is required", operation);
82
- const afterMs = policy.after;
83
- if (!Number.isFinite(afterMs) || afterMs < 0 || afterMs % 60_000 !== 0)
84
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedDelete, "delay cannot be represented in whole minutes", operation);
85
- const minutes = afterMs / 60_000;
86
- target.autoDeleteInterval = minutes;
87
- }
88
- else if (mode !== PolicyMode.Default) {
89
- mappingError(ErrorKind.InvalidArgument, CreateFieldLifetimeStoppedDelete, "unknown policy mode", operation);
90
- }
58
+ if (mode === PolicyMode.After) {
59
+ if (policy.after === undefined)
60
+ mappingError({
61
+ kind: ErrorKind.InvalidArgument,
62
+ field: spec.field,
63
+ message: "duration is required",
64
+ operation: spec.operation,
65
+ });
66
+ const afterMs = policy.after;
67
+ if (spec.forbidImmediate && afterMs === 0)
68
+ mappingError({
69
+ kind: ErrorKind.Unsupported,
70
+ field: spec.field,
71
+ message: "immediate action is not representable",
72
+ operation: spec.operation,
73
+ });
74
+ if (!Number.isFinite(afterMs) || afterMs < 0 || afterMs % 60_000 !== 0)
75
+ mappingError({
76
+ kind: ErrorKind.InvalidArgument,
77
+ field: spec.field,
78
+ message: "delay cannot be represented in whole minutes",
79
+ operation: spec.operation,
80
+ });
81
+ const minutes = afterMs / 60_000;
82
+ if (spec.maxMinutes !== undefined && minutes > spec.maxMinutes)
83
+ mappingError({
84
+ kind: ErrorKind.InvalidArgument,
85
+ field: spec.field,
86
+ message: "delay exceeds documented maximum",
87
+ operation: spec.operation,
88
+ });
89
+ return minutes;
91
90
  }
92
- return target;
91
+ if (mode !== PolicyMode.Default)
92
+ mappingError({
93
+ kind: ErrorKind.InvalidArgument,
94
+ field: spec.field,
95
+ message: "unknown policy mode",
96
+ operation: spec.operation,
97
+ });
98
+ return undefined;
93
99
  }
94
100
  export function mapCommonCreateFields(source, operation = "create") {
95
101
  const target = {};
@@ -123,7 +129,12 @@ export function mapResources(source, operation = "create") {
123
129
  if (source.cpuCores !== undefined) {
124
130
  const value = source.cpuCores;
125
131
  if (!Number.isInteger(value) || value > 2147483647)
126
- mappingError(ErrorKind.InvalidArgument, CreateFieldResourcesCpuCores, "cpu_cores cannot be represented", operation);
132
+ mappingError({
133
+ kind: ErrorKind.InvalidArgument,
134
+ field: CreateFieldResourcesCpuCores,
135
+ message: "cpu_cores cannot be represented",
136
+ operation,
137
+ });
127
138
  target.cpu = Number(value);
128
139
  }
129
140
  }
@@ -131,7 +142,12 @@ export function mapResources(source, operation = "create") {
131
142
  if (source.memoryMib !== undefined) {
132
143
  const value = source.memoryMib;
133
144
  if (value % 1024n !== 0n || value / 1024n > 2147483647n)
134
- mappingError(ErrorKind.InvalidArgument, CreateFieldResourcesMemoryMib, "memory_mib cannot be represented", operation);
145
+ mappingError({
146
+ kind: ErrorKind.InvalidArgument,
147
+ field: CreateFieldResourcesMemoryMib,
148
+ message: "memory_mib cannot be represented",
149
+ operation,
150
+ });
135
151
  target.memory = Number(value / 1024n);
136
152
  }
137
153
  }
@@ -139,7 +155,12 @@ export function mapResources(source, operation = "create") {
139
155
  if (source.diskMib !== undefined) {
140
156
  const value = source.diskMib;
141
157
  if (value % 1024n !== 0n || value / 1024n > 2147483647n)
142
- mappingError(ErrorKind.InvalidArgument, CreateFieldResourcesDiskMib, "disk_mib cannot be represented", operation);
158
+ mappingError({
159
+ kind: ErrorKind.InvalidArgument,
160
+ field: CreateFieldResourcesDiskMib,
161
+ message: "disk_mib cannot be represented",
162
+ operation,
163
+ });
143
164
  target.disk = Number(value / 1024n);
144
165
  }
145
166
  }
@@ -207,7 +228,12 @@ export function mapAllocatedResources(source, operation = "create") {
207
228
  if (source.memory !== undefined) {
208
229
  const value = source.memory;
209
230
  if (!Number.isSafeInteger(value) || value < 0)
210
- mappingError(ErrorKind.InvalidResponse, "memory", "memory cannot be represented", operation);
231
+ mappingError({
232
+ kind: ErrorKind.InvalidResponse,
233
+ field: "memory",
234
+ message: "memory cannot be represented",
235
+ operation,
236
+ });
211
237
  target.memoryMib = BigInt(value) * 1024n;
212
238
  }
213
239
  }
@@ -215,7 +241,12 @@ export function mapAllocatedResources(source, operation = "create") {
215
241
  if (source.disk !== undefined) {
216
242
  const value = source.disk;
217
243
  if (!Number.isSafeInteger(value) || value < 0)
218
- mappingError(ErrorKind.InvalidResponse, "disk", "disk cannot be represented", operation);
244
+ mappingError({
245
+ kind: ErrorKind.InvalidResponse,
246
+ field: "disk",
247
+ message: "disk cannot be represented",
248
+ operation,
249
+ });
219
250
  target.diskMib = BigInt(value) * 1024n;
220
251
  }
221
252
  }
@@ -271,12 +302,6 @@ export function mapScope(source, operation = "initialize") {
271
302
  }
272
303
  return target;
273
304
  }
274
- function mappingError(kind, field, message, operation) {
275
- throw sandboxError({
276
- kind,
277
- provider: "daytona",
278
- operation,
279
- field,
280
- message: "sandbox-kit daytona: " + message,
281
- });
305
+ function mappingError(input) {
306
+ providerError(input);
282
307
  }
package/package.json CHANGED
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "@sandbox-kit/daytona",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "daytona provider for sandbox kit",
5
+ "keywords": [
6
+ "sandbox-kit",
7
+ "sandbox",
8
+ "daytona",
9
+ "sdk",
10
+ "provider"
11
+ ],
5
12
  "license": "MIT",
6
13
  "type": "module",
7
14
  "packageManager": "npm@11.19.0",
@@ -45,6 +52,10 @@
45
52
  "#config/*.js": {
46
53
  "@sandbox-kit/source": "./src/config/*.ts",
47
54
  "default": "./dist/config/*.js"
55
+ },
56
+ "#errors.js": {
57
+ "@sandbox-kit/source": "./src/errors.ts",
58
+ "default": "./dist/errors.js"
48
59
  }
49
60
  },
50
61
  "scripts": {
@@ -55,7 +66,7 @@
55
66
  "dependencies": {
56
67
  "@bufbuild/protobuf": "2.16.0",
57
68
  "@daytona/sdk": "0.225.0",
58
- "@sandbox-kit/sandbox": "0.1.0"
69
+ "@sandbox-kit/sandbox": "0.1.1"
59
70
  },
60
71
  "devDependencies": {
61
72
  "@types/node": "24.10.1",