@aerol-ai/aerolvm-sdk 0.2.2 → 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.d.ts CHANGED
@@ -2,4 +2,4 @@ export { MicroVM } from "./MicroVM.js";
2
2
  export { Sandbox } from "./Sandbox.js";
3
3
  export { Image } from "./Image.js";
4
4
  export type { MicroVMConfig } from "./MicroVM.js";
5
- export type { BinaryLike, BuildImageOptions, BuildImagePushOptions, BuildImageResult, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, Failover, FailoverPolicy, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegisterSnapshotOptions, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
5
+ export type { BinaryLike, BuildImageOptions, BuildImagePushOptions, BuildImageResult, CreateOptions, CreateSessionOptions, CustomDomain, CustomDomainStatus, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, Failover, FailoverPolicy, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegisterSnapshotOptions, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
@@ -1 +1,16 @@
1
1
  export declare const PATH_PREFIX = "/v1";
2
+ /**
3
+ * Collection URL for a sandbox's custom-domain bindings. Used for both POST
4
+ * (add) and GET (list). The DELETE variant lives at
5
+ * {@link sandboxCustomDomainPath}.
6
+ *
7
+ * `id` is interpolated verbatim — sandbox IDs are server-generated opaque
8
+ * tokens and already URL-safe.
9
+ */
10
+ export declare function sandboxCustomDomainsPath(prefix: string, id: string): string;
11
+ /**
12
+ * URL for a single custom-domain binding. The hostname is percent-encoded so
13
+ * IDN punycode (`xn--...`) and wildcard labels round-trip safely through
14
+ * `DELETE /v1/sandboxes/{id}/custom-domains/{hostname}`.
15
+ */
16
+ export declare function sandboxCustomDomainPath(prefix: string, id: string, hostname: string): string;
@@ -5,3 +5,22 @@
5
5
  //
6
6
  // Keep this in sync with pkg/api/v1/dto.go::PathPrefix on the server.
7
7
  export const PATH_PREFIX = "/v1";
8
+ /**
9
+ * Collection URL for a sandbox's custom-domain bindings. Used for both POST
10
+ * (add) and GET (list). The DELETE variant lives at
11
+ * {@link sandboxCustomDomainPath}.
12
+ *
13
+ * `id` is interpolated verbatim — sandbox IDs are server-generated opaque
14
+ * tokens and already URL-safe.
15
+ */
16
+ export function sandboxCustomDomainsPath(prefix, id) {
17
+ return `${prefix}/sandboxes/${id}/custom-domains`;
18
+ }
19
+ /**
20
+ * URL for a single custom-domain binding. The hostname is percent-encoded so
21
+ * IDN punycode (`xn--...`) and wildcard labels round-trip safely through
22
+ * `DELETE /v1/sandboxes/{id}/custom-domains/{hostname}`.
23
+ */
24
+ export function sandboxCustomDomainPath(prefix, id, hostname) {
25
+ return `${prefix}/sandboxes/${id}/custom-domains/${encodeURIComponent(hostname)}`;
26
+ }
@@ -1,5 +1,5 @@
1
1
  import { Image } from "../Image.js";
2
- import type { BinaryLike, BuildImageOptions, BuildImageResult, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecStreamHandle, ExecStreamOptions, ExposedPort, ExposePortOptions, ExposeResult, Failover, HealthStatus, Lifecycle, ListOptions, MountSpecRedacted, NetworkUsage, RegisterSnapshotOptions, SetNetworkLimitsOptions, ResizeOptions, Sandbox, SandboxSnapshot, Session, SessionAttachHandle, SessionAttachOptions } from "../types.js";
2
+ import type { BinaryLike, BuildImageOptions, BuildImageResult, CreateOptions, CreateSessionOptions, CustomDomain, ExecRequest, ExecResult, ExecStreamHandle, ExecStreamOptions, ExposedPort, ExposePortOptions, ExposeResult, Failover, HealthStatus, Lifecycle, ListOptions, MountSpecRedacted, NetworkUsage, RegisterSnapshotOptions, SetNetworkLimitsOptions, ResizeOptions, Sandbox, SandboxSnapshot, Session, SessionAttachHandle, SessionAttachOptions } from "../types.js";
3
3
  type FetchLike = typeof fetch;
4
4
  /**
5
5
  * Wire version of the v1-style sandbox endpoints (`/v1/sandboxes`, etc.) this
@@ -93,6 +93,15 @@ export declare class APIClient {
93
93
  */
94
94
  exposePort(id: string, port: number, options?: ExposePortOptions): Promise<ExposeResult>;
95
95
  unexposePort(id: string, port: number): Promise<void>;
96
+ /**
97
+ * Bind a custom hostname to a sandbox. The server lowercases the hostname
98
+ * and returns the post-add list of bindings (sorted by hostname). Calling
99
+ * with an already-registered hostname is idempotent and returns the
100
+ * existing list.
101
+ */
102
+ addCustomDomain(id: string, hostname: string): Promise<CustomDomain[]>;
103
+ listCustomDomains(id: string): Promise<CustomDomain[]>;
104
+ removeCustomDomain(id: string, hostname: string): Promise<void>;
96
105
  reconcile(): Promise<void>;
97
106
  health(): Promise<HealthStatus>;
98
107
  mounts(id: string): Promise<MountSpecRedacted[]>;
@@ -146,6 +155,19 @@ export declare class SandboxResource implements Sandbox {
146
155
  downloadFile(targetPath: string): Promise<Uint8Array>;
147
156
  exposePort(port: number, options?: ExposePortOptions): Promise<ExposeResult>;
148
157
  unexposePort(port: number): Promise<void>;
158
+ /**
159
+ * Per-sandbox custom-hostname bindings. Exposed as a namespaced accessor
160
+ * (rather than `addCustomDomain`/`removeCustomDomain`/`listCustomDomains`
161
+ * methods on the resource) so call sites read like
162
+ * `sandbox.customDomains.add("api.acme.com")`, mirroring the noun in the
163
+ * `/v1/.../custom-domains` URL space. A fresh object is returned per access
164
+ * so the closures always see the current `id` even after `refresh()`.
165
+ */
166
+ get customDomains(): {
167
+ add(hostname: string): Promise<CustomDomain[]>;
168
+ remove(hostname: string): Promise<void>;
169
+ list(): Promise<CustomDomain[]>;
170
+ };
149
171
  createSnapshot(name: string): Promise<SandboxSnapshot>;
150
172
  start(): Promise<this>;
151
173
  stop(): Promise<this>;
@@ -1,5 +1,5 @@
1
1
  import { basename } from "node:path";
2
- import { PATH_PREFIX as V1_PATH_PREFIX } from "./api/v1/paths.js";
2
+ import { PATH_PREFIX as V1_PATH_PREFIX, sandboxCustomDomainPath as v1SandboxCustomDomainPath, sandboxCustomDomainsPath as v1SandboxCustomDomainsPath, } from "./api/v1/paths.js";
3
3
  import { Image } from "../Image.js";
4
4
  const DEFAULT_API_VERSION = "v1";
5
5
  const PATH_PREFIXES = {
@@ -220,6 +220,23 @@ export class APIClient {
220
220
  async unexposePort(id, port) {
221
221
  await this.doJSON("DELETE", `${this.versionPrefix}/sandboxes/${id}/ports/${port}`);
222
222
  }
223
+ /**
224
+ * Bind a custom hostname to a sandbox. The server lowercases the hostname
225
+ * and returns the post-add list of bindings (sorted by hostname). Calling
226
+ * with an already-registered hostname is idempotent and returns the
227
+ * existing list.
228
+ */
229
+ async addCustomDomain(id, hostname) {
230
+ const response = await this.doJSON("POST", v1SandboxCustomDomainsPath(this.versionPrefix, id), { hostname });
231
+ return response.custom_domains.map(fromApiCustomDomain);
232
+ }
233
+ async listCustomDomains(id) {
234
+ const response = await this.doJSON("GET", v1SandboxCustomDomainsPath(this.versionPrefix, id));
235
+ return response.custom_domains.map(fromApiCustomDomain);
236
+ }
237
+ async removeCustomDomain(id, hostname) {
238
+ await this.doJSON("DELETE", v1SandboxCustomDomainPath(this.versionPrefix, id, hostname));
239
+ }
223
240
  async reconcile() {
224
241
  await this.doJSON("POST", this.versioned("/admin/reconcile"));
225
242
  }
@@ -334,6 +351,23 @@ export class SandboxResource {
334
351
  async unexposePort(port) {
335
352
  await this.client.unexposePort(this.id, port);
336
353
  }
354
+ /**
355
+ * Per-sandbox custom-hostname bindings. Exposed as a namespaced accessor
356
+ * (rather than `addCustomDomain`/`removeCustomDomain`/`listCustomDomains`
357
+ * methods on the resource) so call sites read like
358
+ * `sandbox.customDomains.add("api.acme.com")`, mirroring the noun in the
359
+ * `/v1/.../custom-domains` URL space. A fresh object is returned per access
360
+ * so the closures always see the current `id` even after `refresh()`.
361
+ */
362
+ get customDomains() {
363
+ const client = this.client;
364
+ const id = this.id;
365
+ return {
366
+ add: (hostname) => client.addCustomDomain(id, hostname),
367
+ remove: (hostname) => client.removeCustomDomain(id, hostname),
368
+ list: () => client.listCustomDomains(id),
369
+ };
370
+ }
337
371
  async createSnapshot(name) {
338
372
  return this.client.createSnapshot(this.id, name);
339
373
  }
@@ -390,6 +424,7 @@ function toApiCreateOptions(options) {
390
424
  lifecycle: options.lifecycle ? toApiLifecycle(options.lifecycle) : undefined,
391
425
  failover: options.failover ? toApiFailover(options.failover) : undefined,
392
426
  runtime: options.runtime,
427
+ custom_domains: options.customDomains,
393
428
  };
394
429
  }
395
430
  function toApiRegisterSnapshotOptions(options) {
@@ -439,6 +474,7 @@ function toApiLifecycle(lifecycle) {
439
474
  destroy_if_idle_for: lifecycle.destroyIfIdleFor,
440
475
  stop_at_age: lifecycle.stopAtAge,
441
476
  destroy_at_age: lifecycle.destroyAtAge,
477
+ serverless: lifecycle.serverless,
442
478
  };
443
479
  }
444
480
  function toApiFailover(failover) {
@@ -521,6 +557,15 @@ function fromApiSession(session) {
521
557
  attached: session.attached,
522
558
  };
523
559
  }
560
+ function fromApiCustomDomain(domain) {
561
+ return {
562
+ hostname: domain.hostname,
563
+ status: domain.status,
564
+ lastError: domain.last_error,
565
+ createdAt: domain.created_at,
566
+ updatedAt: domain.updated_at,
567
+ };
568
+ }
524
569
  function fromApiExposedPort(port) {
525
570
  return {
526
571
  sandboxID: port.sandbox_id,
@@ -614,6 +659,9 @@ function fromApiLifecycle(lifecycle) {
614
659
  if (lifecycle?.destroy_at_age !== undefined) {
615
660
  result.destroyAtAge = lifecycle.destroy_at_age;
616
661
  }
662
+ if (lifecycle?.serverless) {
663
+ result.serverless = true;
664
+ }
617
665
  return result;
618
666
  }
619
667
  function fromApiFailover(failover) {
package/dist/types.d.ts CHANGED
@@ -73,6 +73,13 @@ export interface Lifecycle {
73
73
  stopAtAge?: number;
74
74
  /** Duration in integer nanoseconds. */
75
75
  destroyAtAge?: number;
76
+ /**
77
+ * Opt the sandbox into HTTP wake-on-request: auto-stop when idle,
78
+ * resume on the next inbound HTTP request. Requires `stopIfIdleFor`
79
+ * to be set explicitly — the server rejects `serverless: true`
80
+ * without an idle window. TCP and TLS exposures stay always-on.
81
+ */
82
+ serverless?: boolean;
76
83
  }
77
84
  export type UpdateLifecycleOptions = Lifecycle;
78
85
  export type FailoverPolicy = "none" | "recreate";
@@ -163,6 +170,13 @@ export interface CreateOptions {
163
170
  * Not compatible with runtime="gvisor".
164
171
  */
165
172
  gpus?: GPUOptions;
173
+ /**
174
+ * Custom hostnames to bind to this sandbox at create-time. Each entry is
175
+ * lowercased server-side and progresses through the standard custom-domain
176
+ * lifecycle (pending_dns → issuing → ready). Equivalent to calling
177
+ * `sandbox.customDomains.add(host)` for each entry after create.
178
+ */
179
+ customDomains?: string[];
166
180
  }
167
181
  export interface ResizeOptions {
168
182
  /** Number of CPU cores. Fractional values supported (e.g. 0.5). */
@@ -212,6 +226,21 @@ export interface ExposedPort {
212
226
  publicURL: string;
213
227
  createdAt: string;
214
228
  }
229
+ /**
230
+ * Lifecycle states for a per-sandbox custom hostname.
231
+ * - "pending_dns": hostname registered; awaiting DNS to point at the daemon.
232
+ * - "issuing": DNS resolves; Caddy is acquiring an ACME certificate.
233
+ * - "ready": certificate issued; traffic served end-to-end on the host.
234
+ * - "failed": ACME or DNS validation failed; see `lastError`.
235
+ */
236
+ export type CustomDomainStatus = "pending_dns" | "issuing" | "ready" | "failed";
237
+ export interface CustomDomain {
238
+ hostname: string;
239
+ status: CustomDomainStatus;
240
+ lastError?: string;
241
+ createdAt: string;
242
+ updatedAt: string;
243
+ }
215
244
  /**
216
245
  * Wire protocol an exposure publishes through:
217
246
  * - "http": Caddy HTTP reverse proxy at https://<id>-<port>.<domain>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aerol-ai/aerolvm-sdk",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",