@aerol-ai/aerolvm-sdk 0.1.8 → 0.1.10
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/Image.d.ts +37 -0
- package/dist/Image.js +107 -0
- package/dist/MicroVM.d.ts +11 -1
- package/dist/MicroVM.js +15 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/internal/api/v1/paths.js +3 -3
- package/dist/internal/client.d.ts +30 -6
- package/dist/internal/client.js +106 -2
- package/dist/types.d.ts +59 -1
- package/package.json +1 -1
package/dist/Image.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image is a fluent Dockerfile builder. The shape mirrors the Daytona SDK's
|
|
3
|
+
* Image API so examples port across by changing the import. The output is a
|
|
4
|
+
* Dockerfile string accessible via `.dockerfile` — the SDK ships it to
|
|
5
|
+
* `POST /v1/images/build` when an Image is passed to `MicroVM.create`.
|
|
6
|
+
*
|
|
7
|
+
* Caller-side context (COPY/ADD from local files) needs the
|
|
8
|
+
* SB_IMAGE_BUILD_CONTEXT_ENABLED operator flag on the daemon. Without that,
|
|
9
|
+
* use only `runCommands`, `env`, `workdir`, `entrypoint`, `cmd`, `user`, and
|
|
10
|
+
* `expose` — every step they emit is RUN/ENV/WORKDIR/etc. against the base
|
|
11
|
+
* image, no local files involved.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Image {
|
|
14
|
+
private _dockerfile;
|
|
15
|
+
private constructor();
|
|
16
|
+
get dockerfile(): string;
|
|
17
|
+
/** Start a new image from `FROM <image>`. */
|
|
18
|
+
static base(image: string): Image;
|
|
19
|
+
/**
|
|
20
|
+
* Build from a raw Dockerfile string. Use when you need full control
|
|
21
|
+
* (multi-stage builds, ARG, ONBUILD, HEALTHCHECK, etc.) that the fluent
|
|
22
|
+
* API doesn't expose. Caller is responsible for the Dockerfile validity.
|
|
23
|
+
*/
|
|
24
|
+
static fromDockerfile(dockerfile: string): Image;
|
|
25
|
+
/**
|
|
26
|
+
* Append one or more RUN steps. Each argument becomes its own RUN line;
|
|
27
|
+
* an array argument is joined with `&&` so the commands share one layer.
|
|
28
|
+
*/
|
|
29
|
+
runCommands(...commands: (string | string[])[]): Image;
|
|
30
|
+
/** Append ENV K=V pairs (one ENV line per call, all vars on it). */
|
|
31
|
+
env(envVars: Record<string, string>): Image;
|
|
32
|
+
workdir(dirPath: string): Image;
|
|
33
|
+
entrypoint(entrypointCommands: string[]): Image;
|
|
34
|
+
cmd(cmd: string[]): Image;
|
|
35
|
+
user(username: string): Image;
|
|
36
|
+
expose(port: number): Image;
|
|
37
|
+
}
|
package/dist/Image.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image is a fluent Dockerfile builder. The shape mirrors the Daytona SDK's
|
|
3
|
+
* Image API so examples port across by changing the import. The output is a
|
|
4
|
+
* Dockerfile string accessible via `.dockerfile` — the SDK ships it to
|
|
5
|
+
* `POST /v1/images/build` when an Image is passed to `MicroVM.create`.
|
|
6
|
+
*
|
|
7
|
+
* Caller-side context (COPY/ADD from local files) needs the
|
|
8
|
+
* SB_IMAGE_BUILD_CONTEXT_ENABLED operator flag on the daemon. Without that,
|
|
9
|
+
* use only `runCommands`, `env`, `workdir`, `entrypoint`, `cmd`, `user`, and
|
|
10
|
+
* `expose` — every step they emit is RUN/ENV/WORKDIR/etc. against the base
|
|
11
|
+
* image, no local files involved.
|
|
12
|
+
*/
|
|
13
|
+
export class Image {
|
|
14
|
+
_dockerfile = "";
|
|
15
|
+
constructor(dockerfile) {
|
|
16
|
+
this._dockerfile = dockerfile;
|
|
17
|
+
}
|
|
18
|
+
get dockerfile() {
|
|
19
|
+
return this._dockerfile;
|
|
20
|
+
}
|
|
21
|
+
/** Start a new image from `FROM <image>`. */
|
|
22
|
+
static base(image) {
|
|
23
|
+
if (typeof image !== "string" || image.trim() === "") {
|
|
24
|
+
throw new TypeError("Image.base requires a non-empty image string");
|
|
25
|
+
}
|
|
26
|
+
return new Image(`FROM ${image.trim()}\n`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build from a raw Dockerfile string. Use when you need full control
|
|
30
|
+
* (multi-stage builds, ARG, ONBUILD, HEALTHCHECK, etc.) that the fluent
|
|
31
|
+
* API doesn't expose. Caller is responsible for the Dockerfile validity.
|
|
32
|
+
*/
|
|
33
|
+
static fromDockerfile(dockerfile) {
|
|
34
|
+
if (typeof dockerfile !== "string" || dockerfile.trim() === "") {
|
|
35
|
+
throw new TypeError("Image.fromDockerfile requires a non-empty Dockerfile string");
|
|
36
|
+
}
|
|
37
|
+
return new Image(dockerfile.endsWith("\n") ? dockerfile : dockerfile + "\n");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Append one or more RUN steps. Each argument becomes its own RUN line;
|
|
41
|
+
* an array argument is joined with `&&` so the commands share one layer.
|
|
42
|
+
*/
|
|
43
|
+
runCommands(...commands) {
|
|
44
|
+
for (const entry of commands) {
|
|
45
|
+
if (Array.isArray(entry)) {
|
|
46
|
+
const joined = entry.map((c) => c.trim()).filter(Boolean).join(" && ");
|
|
47
|
+
if (joined !== "") {
|
|
48
|
+
this._dockerfile += `RUN ${joined}\n`;
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const cmd = entry.trim();
|
|
53
|
+
if (cmd !== "") {
|
|
54
|
+
this._dockerfile += `RUN ${cmd}\n`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
/** Append ENV K=V pairs (one ENV line per call, all vars on it). */
|
|
60
|
+
env(envVars) {
|
|
61
|
+
const parts = Object.entries(envVars).map(([k, v]) => `${k}=${dockerQuote(v)}`);
|
|
62
|
+
if (parts.length > 0) {
|
|
63
|
+
this._dockerfile += `ENV ${parts.join(" ")}\n`;
|
|
64
|
+
}
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
workdir(dirPath) {
|
|
68
|
+
if (dirPath.trim() === "") {
|
|
69
|
+
throw new TypeError("Image.workdir requires a non-empty path");
|
|
70
|
+
}
|
|
71
|
+
this._dockerfile += `WORKDIR ${dirPath}\n`;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
entrypoint(entrypointCommands) {
|
|
75
|
+
this._dockerfile += `ENTRYPOINT ${jsonExecForm(entrypointCommands)}\n`;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
cmd(cmd) {
|
|
79
|
+
this._dockerfile += `CMD ${jsonExecForm(cmd)}\n`;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
user(username) {
|
|
83
|
+
if (username.trim() === "") {
|
|
84
|
+
throw new TypeError("Image.user requires a non-empty username");
|
|
85
|
+
}
|
|
86
|
+
this._dockerfile += `USER ${username}\n`;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
expose(port) {
|
|
90
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
91
|
+
throw new RangeError(`Image.expose: port ${port} is out of range`);
|
|
92
|
+
}
|
|
93
|
+
this._dockerfile += `EXPOSE ${port}\n`;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function dockerQuote(value) {
|
|
98
|
+
// Quote when the value contains whitespace or a special char; otherwise
|
|
99
|
+
// leave bare to match canonical Dockerfile style.
|
|
100
|
+
if (/^[A-Za-z0-9_\-./:@]+$/.test(value)) {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
104
|
+
}
|
|
105
|
+
function jsonExecForm(parts) {
|
|
106
|
+
return JSON.stringify(parts);
|
|
107
|
+
}
|
package/dist/MicroVM.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type APIVersion } from "./internal/client.js";
|
|
2
2
|
import { Sandbox } from "./Sandbox.js";
|
|
3
|
-
import
|
|
3
|
+
import { Image } from "./Image.js";
|
|
4
|
+
import type { BuildImageOptions, BuildImageResult, CreateOptions, CreateSessionOptions, ExecStreamHandle, ExecStreamOptions, HealthStatus, Lifecycle, MountSpecRedacted, RegisterSnapshotOptions, ResizeOptions, SandboxSnapshot, Session, SessionAttachHandle, SessionAttachOptions } from "./types.js";
|
|
4
5
|
type FetchLike = typeof fetch;
|
|
5
6
|
export interface MicroVMConfig {
|
|
6
7
|
patToken?: string;
|
|
@@ -25,10 +26,19 @@ export declare class MicroVM {
|
|
|
25
26
|
start(id: string): Promise<Sandbox>;
|
|
26
27
|
stop(id: string): Promise<Sandbox>;
|
|
27
28
|
createSnapshot(id: string, name: string): Promise<SandboxSnapshot>;
|
|
29
|
+
registerSnapshot(options: RegisterSnapshotOptions): Promise<SandboxSnapshot>;
|
|
30
|
+
registerSnapshotFromImage(name: string, image: Image, options?: Omit<RegisterSnapshotOptions, "name" | "image" | "dockerfileContent">): Promise<SandboxSnapshot>;
|
|
28
31
|
destroy(id: string): Promise<void>;
|
|
29
32
|
resize(id: string, options: ResizeOptions): Promise<Sandbox>;
|
|
30
33
|
updateLifecycle(id: string, lifecycle: Lifecycle): Promise<Sandbox>;
|
|
31
34
|
reconcile(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Build an {@link Image} on the daemon and optionally push the result to a
|
|
37
|
+
* remote registry. Push credentials are forwarded per request and never
|
|
38
|
+
* persisted by the daemon. Returns the local content-addressed tag and,
|
|
39
|
+
* when push was requested, the pushed reference.
|
|
40
|
+
*/
|
|
41
|
+
buildImage(image: Image, options?: BuildImageOptions): Promise<BuildImageResult>;
|
|
32
42
|
health(): Promise<HealthStatus>;
|
|
33
43
|
mounts(sandboxID: string): Promise<MountSpecRedacted[]>;
|
|
34
44
|
execStream(sandboxID: string, options: ExecStreamOptions): ExecStreamHandle;
|
package/dist/MicroVM.js
CHANGED
|
@@ -44,6 +44,12 @@ export class MicroVM {
|
|
|
44
44
|
async createSnapshot(id, name) {
|
|
45
45
|
return this.client.createSnapshot(id, name);
|
|
46
46
|
}
|
|
47
|
+
async registerSnapshot(options) {
|
|
48
|
+
return this.client.registerSnapshot(options);
|
|
49
|
+
}
|
|
50
|
+
async registerSnapshotFromImage(name, image, options = {}) {
|
|
51
|
+
return this.client.registerSnapshotFromImage(name, image, options);
|
|
52
|
+
}
|
|
47
53
|
async destroy(id) {
|
|
48
54
|
await this.client.destroy(id);
|
|
49
55
|
}
|
|
@@ -58,6 +64,15 @@ export class MicroVM {
|
|
|
58
64
|
async reconcile() {
|
|
59
65
|
await this.client.reconcile();
|
|
60
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Build an {@link Image} on the daemon and optionally push the result to a
|
|
69
|
+
* remote registry. Push credentials are forwarded per request and never
|
|
70
|
+
* persisted by the daemon. Returns the local content-addressed tag and,
|
|
71
|
+
* when push was requested, the pushed reference.
|
|
72
|
+
*/
|
|
73
|
+
async buildImage(image, options) {
|
|
74
|
+
return this.client.buildImage(image, options);
|
|
75
|
+
}
|
|
61
76
|
async health() {
|
|
62
77
|
return this.client.health();
|
|
63
78
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { MicroVM } from "./MicroVM.js";
|
|
2
2
|
export { Sandbox } from "./Sandbox.js";
|
|
3
|
+
export { Image } from "./Image.js";
|
|
3
4
|
export type { MicroVMConfig } from "./MicroVM.js";
|
|
4
|
-
export type { BinaryLike, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
|
|
5
|
+
export type { BinaryLike, BuildImageOptions, BuildImagePushOptions, BuildImageResult, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegisterSnapshotOptions, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// v1 path-prefix constant. The TypeScript SDK uses this to build URLs against
|
|
2
|
-
// the v1 surface of the sandbox daemon. When
|
|
3
|
-
// internal/api/
|
|
4
|
-
// constructor's apiVersion option selects which one to use.
|
|
2
|
+
// the v1 surface of the sandbox daemon. When a new wire version lands, a
|
|
3
|
+
// sibling internal/api/vN/paths.ts will export its own PATH_PREFIX, and the
|
|
4
|
+
// client constructor's apiVersion option selects which one to use.
|
|
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";
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Image } from "../Image.js";
|
|
2
|
+
import type { BinaryLike, BuildImageOptions, BuildImageResult, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecStreamHandle, ExecStreamOptions, ExposedPort, ExposePortOptions, ExposeResult, HealthStatus, Lifecycle, MountSpecRedacted, NetworkUsage, RegisterSnapshotOptions, SetNetworkLimitsOptions, ResizeOptions, Sandbox, SandboxSnapshot, Session, SessionAttachHandle, SessionAttachOptions } from "../types.js";
|
|
2
3
|
type FetchLike = typeof fetch;
|
|
3
4
|
/**
|
|
4
|
-
* Wire version of the sandbox
|
|
5
|
-
*
|
|
6
|
-
* wire version. Today only "v1" exists;
|
|
7
|
-
* breaking
|
|
5
|
+
* Wire version of the v1-style sandbox endpoints (`/v1/sandboxes`, etc.) this
|
|
6
|
+
* client speaks. The SDK and the server version independently — bumping the
|
|
7
|
+
* SDK package does not move the wire version. Today only "v1" exists; future
|
|
8
|
+
* breaking changes to the sandbox endpoints will land in a new version
|
|
9
|
+
* prefix.
|
|
10
|
+
*
|
|
11
|
+
* The `/v1/images/build` endpoint used by {@link Image}-shaped image inputs
|
|
12
|
+
* is called unconditionally when an Image is supplied to
|
|
13
|
+
* {@link APIClient.create}, regardless of `apiVersion`. Daemons older than
|
|
14
|
+
* the image-build feature return 404; the SDK turns that into a clear
|
|
15
|
+
* "daemon does not support image builds" error so the user can switch to a
|
|
16
|
+
* string image.
|
|
8
17
|
*/
|
|
9
18
|
export type APIVersion = "v1";
|
|
10
19
|
export interface APIClientConfig {
|
|
@@ -28,16 +37,31 @@ export declare class APIClient {
|
|
|
28
37
|
/**
|
|
29
38
|
* Build a versioned API path. Pass the suffix beginning with "/" (e.g.
|
|
30
39
|
* "/sandboxes") and the active version's prefix is prepended. Use this for
|
|
31
|
-
* every versioned API call so
|
|
40
|
+
* every versioned API call so a future wire version can be selected by the
|
|
32
41
|
* apiVersion option without touching call sites.
|
|
33
42
|
*/
|
|
34
43
|
private versioned;
|
|
35
44
|
create(options: CreateOptions): Promise<SandboxResource>;
|
|
45
|
+
/**
|
|
46
|
+
* Compile a fluent {@link Image} into a content-addressed image tag by
|
|
47
|
+
* POSTing its Dockerfile to `/v1/images/build`. The daemon caches by
|
|
48
|
+
* content hash, so repeated calls with the same Dockerfile are a no-op.
|
|
49
|
+
* String images are passed through unchanged.
|
|
50
|
+
*
|
|
51
|
+
* Daemons predating the image-build feature do not register the route and
|
|
52
|
+
* return 404; this method translates that into a clear error telling the
|
|
53
|
+
* caller to pass a string image instead, rather than the generic "request
|
|
54
|
+
* failed with status 404" the JSON decoder would otherwise produce.
|
|
55
|
+
*/
|
|
56
|
+
buildImage(image: Image, options?: BuildImageOptions): Promise<BuildImageResult>;
|
|
57
|
+
private resolveImage;
|
|
36
58
|
list(): Promise<SandboxResource[]>;
|
|
37
59
|
get(id: string): Promise<SandboxResource>;
|
|
38
60
|
start(id: string): Promise<SandboxResource>;
|
|
39
61
|
stop(id: string): Promise<SandboxResource>;
|
|
40
62
|
createSnapshot(id: string, name: string): Promise<SandboxSnapshot>;
|
|
63
|
+
registerSnapshot(options: RegisterSnapshotOptions): Promise<SandboxSnapshot>;
|
|
64
|
+
registerSnapshotFromImage(name: string, image: Image, options?: Omit<RegisterSnapshotOptions, "name" | "image" | "dockerfileContent">): Promise<SandboxSnapshot>;
|
|
41
65
|
destroy(id: string): Promise<void>;
|
|
42
66
|
resize(id: string, options: ResizeOptions): Promise<SandboxResource>;
|
|
43
67
|
updateLifecycle(id: string, lifecycle: Lifecycle): Promise<SandboxResource>;
|
package/dist/internal/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { PATH_PREFIX as V1_PATH_PREFIX } from "./api/v1/paths.js";
|
|
3
|
+
import { Image } from "../Image.js";
|
|
3
4
|
const DEFAULT_API_VERSION = "v1";
|
|
4
5
|
const PATH_PREFIXES = {
|
|
5
6
|
v1: V1_PATH_PREFIX,
|
|
@@ -20,16 +21,70 @@ export class APIClient {
|
|
|
20
21
|
/**
|
|
21
22
|
* Build a versioned API path. Pass the suffix beginning with "/" (e.g.
|
|
22
23
|
* "/sandboxes") and the active version's prefix is prepended. Use this for
|
|
23
|
-
* every versioned API call so
|
|
24
|
+
* every versioned API call so a future wire version can be selected by the
|
|
24
25
|
* apiVersion option without touching call sites.
|
|
25
26
|
*/
|
|
26
27
|
versioned(suffix) {
|
|
27
28
|
return `${this.versionPrefix}${suffix}`;
|
|
28
29
|
}
|
|
29
30
|
async create(options) {
|
|
30
|
-
const
|
|
31
|
+
const resolved = await this.resolveImage(options);
|
|
32
|
+
const response = await this.doJSON("POST", this.versioned("/sandboxes"), toApiCreateOptions(resolved));
|
|
31
33
|
return new SandboxResource(this, fromApiCreateSandboxResponse(response));
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Compile a fluent {@link Image} into a content-addressed image tag by
|
|
37
|
+
* POSTing its Dockerfile to `/v1/images/build`. The daemon caches by
|
|
38
|
+
* content hash, so repeated calls with the same Dockerfile are a no-op.
|
|
39
|
+
* String images are passed through unchanged.
|
|
40
|
+
*
|
|
41
|
+
* Daemons predating the image-build feature do not register the route and
|
|
42
|
+
* return 404; this method translates that into a clear error telling the
|
|
43
|
+
* caller to pass a string image instead, rather than the generic "request
|
|
44
|
+
* failed with status 404" the JSON decoder would otherwise produce.
|
|
45
|
+
*/
|
|
46
|
+
async buildImage(image, options) {
|
|
47
|
+
const body = { dockerfile_content: image.dockerfile };
|
|
48
|
+
if (options?.push) {
|
|
49
|
+
const p = options.push;
|
|
50
|
+
if (!p.registry) {
|
|
51
|
+
throw new Error("buildImage: push.registry is required when push is set");
|
|
52
|
+
}
|
|
53
|
+
if (!p.username || !p.password) {
|
|
54
|
+
throw new Error("buildImage: push.username and push.password are required when push is set");
|
|
55
|
+
}
|
|
56
|
+
body.push = {
|
|
57
|
+
registry: p.registry,
|
|
58
|
+
tag: p.tag,
|
|
59
|
+
server: p.server,
|
|
60
|
+
username: p.username,
|
|
61
|
+
password: p.password,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const response = await this.request("POST", "/v1/images/build", {
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
});
|
|
68
|
+
if (response.status === 404) {
|
|
69
|
+
await response.text().catch(() => undefined);
|
|
70
|
+
throw new Error("this daemon does not support Image builds (POST /v1/images/build is not registered) — pass a string image reference (e.g. \"ubuntu:22.04\") instead, or upgrade the daemon");
|
|
71
|
+
}
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw await decodeError(response);
|
|
74
|
+
}
|
|
75
|
+
const payload = (await response.json());
|
|
76
|
+
return { image: payload.image, pushed: payload.pushed };
|
|
77
|
+
}
|
|
78
|
+
async resolveImage(options) {
|
|
79
|
+
if (typeof options.image === "string") {
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
if (!(options.image instanceof Image)) {
|
|
83
|
+
throw new TypeError("CreateOptions.image must be a string or Image");
|
|
84
|
+
}
|
|
85
|
+
const result = await this.buildImage(options.image);
|
|
86
|
+
return { ...options, image: result.image };
|
|
87
|
+
}
|
|
33
88
|
async list() {
|
|
34
89
|
const response = await this.doJSON("GET", this.versioned("/sandboxes"));
|
|
35
90
|
return response.map((item) => this.wrap(item));
|
|
@@ -50,6 +105,35 @@ export class APIClient {
|
|
|
50
105
|
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/snapshot`, { name });
|
|
51
106
|
return fromApiSandboxSnapshot(response);
|
|
52
107
|
}
|
|
108
|
+
async registerSnapshot(options) {
|
|
109
|
+
const name = options.name.trim();
|
|
110
|
+
if (name === "") {
|
|
111
|
+
throw new Error("name is required");
|
|
112
|
+
}
|
|
113
|
+
const image = options.image?.trim() ?? "";
|
|
114
|
+
const dockerfileContent = options.dockerfileContent?.trim() ?? "";
|
|
115
|
+
if (image === "" && dockerfileContent === "") {
|
|
116
|
+
throw new Error("image or dockerfile_content is required");
|
|
117
|
+
}
|
|
118
|
+
if (image !== "" && dockerfileContent !== "") {
|
|
119
|
+
throw new Error("image and dockerfile_content are mutually exclusive");
|
|
120
|
+
}
|
|
121
|
+
const response = await this.doJSON("POST", this.versioned("/snapshots"), toApiRegisterSnapshotOptions({
|
|
122
|
+
...options,
|
|
123
|
+
name,
|
|
124
|
+
image: image || undefined,
|
|
125
|
+
dockerfileContent: dockerfileContent || undefined,
|
|
126
|
+
regionID: options.regionID?.trim() || undefined,
|
|
127
|
+
}));
|
|
128
|
+
return fromApiSandboxSnapshot(response);
|
|
129
|
+
}
|
|
130
|
+
async registerSnapshotFromImage(name, image, options = {}) {
|
|
131
|
+
return this.registerSnapshot({
|
|
132
|
+
...options,
|
|
133
|
+
name,
|
|
134
|
+
dockerfileContent: image.dockerfile,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
53
137
|
async destroy(id) {
|
|
54
138
|
await this.doJSON("DELETE", `${this.versionPrefix}/sandboxes/${id}`);
|
|
55
139
|
}
|
|
@@ -306,6 +390,20 @@ function toApiCreateOptions(options) {
|
|
|
306
390
|
runtime: options.runtime,
|
|
307
391
|
};
|
|
308
392
|
}
|
|
393
|
+
function toApiRegisterSnapshotOptions(options) {
|
|
394
|
+
return {
|
|
395
|
+
name: options.name,
|
|
396
|
+
image: options.image,
|
|
397
|
+
dockerfile_content: options.dockerfileContent,
|
|
398
|
+
context_hashes: options.contextHashes,
|
|
399
|
+
entrypoint: options.entrypoint,
|
|
400
|
+
region_id: options.regionID,
|
|
401
|
+
cpu: options.cpu,
|
|
402
|
+
gpu: options.gpu,
|
|
403
|
+
memory_mb: options.memoryMB,
|
|
404
|
+
disk_gb: options.diskGB,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
309
407
|
function toApiResizeOptions(options) {
|
|
310
408
|
return {
|
|
311
409
|
cpu: options.cpu,
|
|
@@ -389,6 +487,12 @@ function fromApiSandboxSnapshot(snapshot) {
|
|
|
389
487
|
imageID: snapshot.image_id,
|
|
390
488
|
sourceSandboxID: snapshot.source_sandbox_id,
|
|
391
489
|
createdAt: snapshot.created_at,
|
|
490
|
+
entrypoint: snapshot.entrypoint,
|
|
491
|
+
regionID: snapshot.region_id,
|
|
492
|
+
cpu: snapshot.cpu,
|
|
493
|
+
gpu: snapshot.gpu,
|
|
494
|
+
memoryMB: snapshot.memory_mb,
|
|
495
|
+
diskGB: snapshot.disk_gb,
|
|
392
496
|
};
|
|
393
497
|
}
|
|
394
498
|
function fromApiSession(session) {
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,49 @@ export interface RegistryAuth {
|
|
|
4
4
|
username: string;
|
|
5
5
|
password: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Per-request push directive for `MicroVM.buildImage`. Credentials are
|
|
9
|
+
* forwarded to the daemon as a one-shot `X-Registry-Auth` header on the
|
|
10
|
+
* push call and are never persisted.
|
|
11
|
+
*/
|
|
12
|
+
export interface BuildImagePushOptions {
|
|
13
|
+
/** Destination repository, e.g. "ghcr.io/my-org/my-image". */
|
|
14
|
+
registry: string;
|
|
15
|
+
/** Destination tag. Defaults to "latest" on the daemon when omitted. */
|
|
16
|
+
tag?: string;
|
|
17
|
+
/** Registry serveraddress, e.g. "ghcr.io". Sent inside X-Registry-Auth. */
|
|
18
|
+
server?: string;
|
|
19
|
+
username: string;
|
|
20
|
+
password: string;
|
|
21
|
+
}
|
|
22
|
+
export interface BuildImageOptions {
|
|
23
|
+
push?: BuildImagePushOptions;
|
|
24
|
+
}
|
|
25
|
+
export interface BuildImageResult {
|
|
26
|
+
/** Local content-addressed tag (always returned). */
|
|
27
|
+
image: string;
|
|
28
|
+
/** Pushed reference, e.g. "ghcr.io/my-org/my-image:v1.2.3". */
|
|
29
|
+
pushed?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RegisterSnapshotOptions {
|
|
32
|
+
/** Human-readable identifier other callers use in `CreateOptions.snapshot`. */
|
|
33
|
+
name: string;
|
|
34
|
+
/** Pre-built registry reference. Mutually exclusive with `dockerfileContent`. */
|
|
35
|
+
image?: string;
|
|
36
|
+
/** Literal Dockerfile the daemon will build. Mutually exclusive with `image`. */
|
|
37
|
+
dockerfileContent?: string;
|
|
38
|
+
/** Uploaded build-context hashes for future COPY/ADD resolution. */
|
|
39
|
+
contextHashes?: string[];
|
|
40
|
+
/** Optional command override persisted on the snapshot row. */
|
|
41
|
+
entrypoint?: string[];
|
|
42
|
+
/** Region hint persisted for read-back. */
|
|
43
|
+
regionID?: string;
|
|
44
|
+
/** Resource hints surfaced back on the snapshot row. */
|
|
45
|
+
cpu?: number;
|
|
46
|
+
gpu?: number;
|
|
47
|
+
memoryMB?: number;
|
|
48
|
+
diskGB?: number;
|
|
49
|
+
}
|
|
7
50
|
export type MountType = "s3" | "nfs" | "sshfs" | "rclone";
|
|
8
51
|
export interface MountSpec {
|
|
9
52
|
type: MountType;
|
|
@@ -60,8 +103,17 @@ export interface GPUOptions {
|
|
|
60
103
|
*/
|
|
61
104
|
deviceIDs?: string[];
|
|
62
105
|
}
|
|
106
|
+
import type { Image } from "./Image.js";
|
|
63
107
|
export interface CreateOptions {
|
|
64
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The base image for this sandbox. A bare image reference (e.g.
|
|
110
|
+
* `"ubuntu:22.04"`) is pulled by the daemon as-is. An {@link Image} builder
|
|
111
|
+
* is compiled to a Dockerfile and sent to the daemon's
|
|
112
|
+
* `POST /v1/images/build` endpoint; the resulting content-addressed tag is
|
|
113
|
+
* used for the sandbox. The daemon must have an image builder configured
|
|
114
|
+
* (every official deployment does); otherwise the build call returns 503.
|
|
115
|
+
*/
|
|
116
|
+
image: string | Image;
|
|
65
117
|
/** Number of CPU cores to allocate. Fractional values are supported (e.g. 0.5 = half a core). */
|
|
66
118
|
cpu?: number;
|
|
67
119
|
memoryMB?: number;
|
|
@@ -159,6 +211,12 @@ export interface SandboxSnapshot {
|
|
|
159
211
|
imageID?: string;
|
|
160
212
|
sourceSandboxID: string;
|
|
161
213
|
createdAt: string;
|
|
214
|
+
entrypoint?: string[];
|
|
215
|
+
regionID?: string;
|
|
216
|
+
cpu?: number;
|
|
217
|
+
gpu?: number;
|
|
218
|
+
memoryMB?: number;
|
|
219
|
+
diskGB?: number;
|
|
162
220
|
}
|
|
163
221
|
/**
|
|
164
222
|
* Discriminated result from `exposePort`. Branch on `protocol` to access the
|