@aerol-ai/aerolvm-sdk 0.1.5 → 0.1.7
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/MicroVM.d.ts +8 -0
- package/dist/MicroVM.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/internal/api/v1/paths.d.ts +1 -0
- package/dist/internal/api/v1/paths.js +7 -0
- package/dist/internal/client.d.ts +36 -16
- package/dist/internal/client.js +86 -61
- package/dist/types.d.ts +29 -0
- package/package.json +3 -3
package/dist/MicroVM.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type APIVersion } from "./internal/client.js";
|
|
1
2
|
import { Sandbox } from "./Sandbox.js";
|
|
2
3
|
import type { CreateOptions, CreateSessionOptions, ExecStreamHandle, ExecStreamOptions, HealthStatus, Lifecycle, MountSpecRedacted, ResizeOptions, Session, SessionAttachHandle, SessionAttachOptions } from "./types.js";
|
|
3
4
|
type FetchLike = typeof fetch;
|
|
@@ -5,6 +6,13 @@ export interface MicroVMConfig {
|
|
|
5
6
|
patToken?: string;
|
|
6
7
|
apiUrl?: string;
|
|
7
8
|
fetch?: FetchLike;
|
|
9
|
+
/**
|
|
10
|
+
* Wire version of the sandbox daemon API to call. Defaults to the SDK's
|
|
11
|
+
* pinned default ("v1" today). The SDK package version and the API wire
|
|
12
|
+
* version evolve independently — you can pin one without affecting the
|
|
13
|
+
* other.
|
|
14
|
+
*/
|
|
15
|
+
apiVersion?: APIVersion;
|
|
8
16
|
}
|
|
9
17
|
export declare class MicroVM {
|
|
10
18
|
readonly apiUrl: string;
|
package/dist/MicroVM.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { MicroVM } from "./MicroVM.js";
|
|
2
2
|
export { Sandbox } from "./Sandbox.js";
|
|
3
3
|
export type { MicroVMConfig } from "./MicroVM.js";
|
|
4
|
-
export type { BinaryLike, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
|
|
4
|
+
export type { BinaryLike, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, HealthStatus, Lifecycle, MountSpec, MountSpecRedacted, MountType, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, UpdateLifecycleOptions, } from "./types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PATH_PREFIX = "/v1";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// v1 path-prefix constant. The TypeScript SDK uses this to build URLs against
|
|
2
|
+
// the v1 surface of the sandbox daemon. When v2 lands, a sibling
|
|
3
|
+
// internal/api/v2/paths.ts will export its own PATH_PREFIX, and the client
|
|
4
|
+
// constructor's apiVersion option selects which one to use.
|
|
5
|
+
//
|
|
6
|
+
// Keep this in sync with pkg/api/v1/dto.go::PathPrefix on the server.
|
|
7
|
+
export const PATH_PREFIX = "/v1";
|
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import type { BinaryLike, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecStreamHandle, ExecStreamOptions, ExposedPort, HealthStatus, Lifecycle, MountSpecRedacted, ResizeOptions, Sandbox, Session, SessionAttachHandle, SessionAttachOptions } from "../types.js";
|
|
1
|
+
import type { BinaryLike, CreateOptions, CreateSessionOptions, ExecRequest, ExecResult, ExecStreamHandle, ExecStreamOptions, ExposedPort, ExposePortOptions, ExposeResult, HealthStatus, Lifecycle, MountSpecRedacted, ResizeOptions, Sandbox, Session, SessionAttachHandle, SessionAttachOptions } from "../types.js";
|
|
2
2
|
type FetchLike = typeof fetch;
|
|
3
|
+
/**
|
|
4
|
+
* Wire version of the sandbox daemon API this client speaks. The SDK and the
|
|
5
|
+
* server version independently — bumping the SDK package does not move the
|
|
6
|
+
* wire version. Today only "v1" exists; "v2" will be added when a wire-level
|
|
7
|
+
* breaking change is needed on the server.
|
|
8
|
+
*/
|
|
9
|
+
export type APIVersion = "v1";
|
|
3
10
|
export interface APIClientConfig {
|
|
4
11
|
baseURL: string;
|
|
5
12
|
patToken?: string;
|
|
6
13
|
fetch?: FetchLike;
|
|
14
|
+
/**
|
|
15
|
+
* Wire version to call. Defaults to "v1". Pin this if you need a specific
|
|
16
|
+
* version — otherwise the SDK will track its own default, which may move in
|
|
17
|
+
* a future major SDK release.
|
|
18
|
+
*/
|
|
19
|
+
apiVersion?: APIVersion;
|
|
7
20
|
}
|
|
8
21
|
export declare class APIClient {
|
|
9
22
|
readonly baseURL: string;
|
|
23
|
+
readonly apiVersion: APIVersion;
|
|
10
24
|
private readonly patToken;
|
|
11
25
|
private readonly fetchFn;
|
|
26
|
+
private readonly versionPrefix;
|
|
12
27
|
constructor(config: APIClientConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Build a versioned API path. Pass the suffix beginning with "/" (e.g.
|
|
30
|
+
* "/sandboxes") and the active version's prefix is prepended. Use this for
|
|
31
|
+
* every versioned API call so v2 (when it lands) can be selected by the
|
|
32
|
+
* apiVersion option without touching call sites.
|
|
33
|
+
*/
|
|
34
|
+
private versioned;
|
|
13
35
|
create(options: CreateOptions): Promise<SandboxResource>;
|
|
14
36
|
list(): Promise<SandboxResource[]>;
|
|
15
37
|
get(id: string): Promise<SandboxResource>;
|
|
@@ -31,20 +53,20 @@ export declare class APIClient {
|
|
|
31
53
|
attachSession(id: string, sessionID: string, options?: SessionAttachOptions): SessionAttachHandle;
|
|
32
54
|
uploadFile(id: string, targetPath: string, data: BinaryLike): Promise<void>;
|
|
33
55
|
downloadFile(id: string, targetPath: string): Promise<Uint8Array>;
|
|
34
|
-
exposePort(id: string, port: number): Promise<string>;
|
|
35
|
-
/**
|
|
36
|
-
* Publish a raw TCP port through caddy-l4. The returned URL is in
|
|
37
|
-
* `tcp://<host>:<port>` form and works directly with `psql`, `redis-cli`,
|
|
38
|
-
* `mysql`, `mongosh`, and any other native-protocol client.
|
|
39
|
-
*/
|
|
40
|
-
exposeTCPPort(id: string, port: number): Promise<string>;
|
|
41
56
|
/**
|
|
42
|
-
* Publish a
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
57
|
+
* Publish a sandbox container port. The chosen wire protocol is selected by
|
|
58
|
+
* `options.protocol` (defaults to `"http"`):
|
|
59
|
+
*
|
|
60
|
+
* - `"http"`: Caddy HTTP reverse proxy at `https://<id>-<port>.<domain>`.
|
|
61
|
+
* - `"tcp"`: raw caddy-l4 listener on a parent-host port — pair with
|
|
62
|
+
* native protocol clients (psql, redis-cli, mysql, mongosh).
|
|
63
|
+
* - `"tls"`: caddy-l4 TLS-SNI route on the shared listener. Requires the
|
|
64
|
+
* daemon to have a domain configured AND `SB_L4_TLS_LISTEN` set.
|
|
65
|
+
*
|
|
66
|
+
* Returns a discriminated result keyed on `protocol`. Only the `"tcp"`
|
|
67
|
+
* variant carries `host` / `hostPort` — everything else is in `url`.
|
|
45
68
|
*/
|
|
46
|
-
|
|
47
|
-
private exposePortWithProtocol;
|
|
69
|
+
exposePort(id: string, port: number, options?: ExposePortOptions): Promise<ExposeResult>;
|
|
48
70
|
unexposePort(id: string, port: number): Promise<void>;
|
|
49
71
|
reconcile(): Promise<void>;
|
|
50
72
|
health(): Promise<HealthStatus>;
|
|
@@ -94,9 +116,7 @@ export declare class SandboxResource implements Sandbox {
|
|
|
94
116
|
attachSession(sessionID: string, options?: SessionAttachOptions): SessionAttachHandle;
|
|
95
117
|
uploadFile(targetPath: string, data: BinaryLike): Promise<void>;
|
|
96
118
|
downloadFile(targetPath: string): Promise<Uint8Array>;
|
|
97
|
-
exposePort(port: number): Promise<
|
|
98
|
-
exposeTCPPort(port: number): Promise<string>;
|
|
99
|
-
exposeTLSPort(port: number): Promise<string>;
|
|
119
|
+
exposePort(port: number, options?: ExposePortOptions): Promise<ExposeResult>;
|
|
100
120
|
unexposePort(port: number): Promise<void>;
|
|
101
121
|
start(): Promise<this>;
|
|
102
122
|
stop(): Promise<this>;
|
package/dist/internal/client.js
CHANGED
|
@@ -1,133 +1,145 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
|
+
import { PATH_PREFIX as V1_PATH_PREFIX } from "./api/v1/paths.js";
|
|
3
|
+
const DEFAULT_API_VERSION = "v1";
|
|
4
|
+
const PATH_PREFIXES = {
|
|
5
|
+
v1: V1_PATH_PREFIX,
|
|
6
|
+
};
|
|
2
7
|
export class APIClient {
|
|
3
8
|
baseURL;
|
|
9
|
+
apiVersion;
|
|
4
10
|
patToken;
|
|
5
11
|
fetchFn;
|
|
12
|
+
versionPrefix;
|
|
6
13
|
constructor(config) {
|
|
7
14
|
this.baseURL = config.baseURL.replace(/\/+$/, "");
|
|
8
15
|
this.patToken = config.patToken ?? "";
|
|
9
16
|
this.fetchFn = config.fetch ?? fetch;
|
|
17
|
+
this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
|
|
18
|
+
this.versionPrefix = PATH_PREFIXES[this.apiVersion];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a versioned API path. Pass the suffix beginning with "/" (e.g.
|
|
22
|
+
* "/sandboxes") and the active version's prefix is prepended. Use this for
|
|
23
|
+
* every versioned API call so v2 (when it lands) can be selected by the
|
|
24
|
+
* apiVersion option without touching call sites.
|
|
25
|
+
*/
|
|
26
|
+
versioned(suffix) {
|
|
27
|
+
return `${this.versionPrefix}${suffix}`;
|
|
10
28
|
}
|
|
11
29
|
async create(options) {
|
|
12
|
-
const response = await this.doJSON("POST", "/
|
|
30
|
+
const response = await this.doJSON("POST", this.versioned("/sandboxes"), toApiCreateOptions(options));
|
|
13
31
|
return new SandboxResource(this, fromApiCreateSandboxResponse(response));
|
|
14
32
|
}
|
|
15
33
|
async list() {
|
|
16
|
-
const response = await this.doJSON("GET", "/
|
|
34
|
+
const response = await this.doJSON("GET", this.versioned("/sandboxes"));
|
|
17
35
|
return response.map((item) => this.wrap(item));
|
|
18
36
|
}
|
|
19
37
|
async get(id) {
|
|
20
|
-
const response = await this.doJSON("GET",
|
|
38
|
+
const response = await this.doJSON("GET", `${this.versionPrefix}/sandboxes/${id}`);
|
|
21
39
|
return this.wrap(response);
|
|
22
40
|
}
|
|
23
41
|
async start(id) {
|
|
24
|
-
const response = await this.doJSON("POST",
|
|
42
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/start`);
|
|
25
43
|
return this.wrap(response);
|
|
26
44
|
}
|
|
27
45
|
async stop(id) {
|
|
28
|
-
const response = await this.doJSON("POST",
|
|
46
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/stop`);
|
|
29
47
|
return this.wrap(response);
|
|
30
48
|
}
|
|
31
49
|
async destroy(id) {
|
|
32
|
-
await this.doJSON("DELETE",
|
|
50
|
+
await this.doJSON("DELETE", `${this.versionPrefix}/sandboxes/${id}`);
|
|
33
51
|
}
|
|
34
52
|
async resize(id, options) {
|
|
35
|
-
const response = await this.doJSON("POST",
|
|
53
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/resize`, toApiResizeOptions(options));
|
|
36
54
|
return this.wrap(response);
|
|
37
55
|
}
|
|
38
56
|
async updateLifecycle(id, lifecycle) {
|
|
39
|
-
const response = await this.doJSON("PUT",
|
|
57
|
+
const response = await this.doJSON("PUT", `${this.versionPrefix}/sandboxes/${id}/lifecycle`, toApiLifecycle(lifecycle));
|
|
40
58
|
return this.wrap(response);
|
|
41
59
|
}
|
|
42
60
|
async exec(id, request) {
|
|
43
|
-
const response = await this.doJSON("POST",
|
|
61
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/toolbox/process/execute`, toApiExecRequest(request));
|
|
44
62
|
return fromApiExecResult(response);
|
|
45
63
|
}
|
|
46
64
|
execStream(id, options) {
|
|
47
|
-
return openExecStream(this.baseURL, this.patToken, id, options);
|
|
65
|
+
return openExecStream(this.baseURL, this.versionPrefix, this.patToken, id, options);
|
|
48
66
|
}
|
|
49
67
|
async createSession(id, options) {
|
|
50
|
-
const response = await this.doJSON("POST",
|
|
68
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/sessions`, toApiCreateSessionOptions(options));
|
|
51
69
|
return fromApiSession(response);
|
|
52
70
|
}
|
|
53
71
|
async listSessions(id) {
|
|
54
|
-
const response = await this.doJSON("GET",
|
|
72
|
+
const response = await this.doJSON("GET", `${this.versionPrefix}/sandboxes/${id}/sessions`);
|
|
55
73
|
return response.sessions.map(fromApiSession);
|
|
56
74
|
}
|
|
57
75
|
async getSession(id, sessionID) {
|
|
58
|
-
const response = await this.doJSON("GET",
|
|
76
|
+
const response = await this.doJSON("GET", `${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}`);
|
|
59
77
|
return fromApiSession(response);
|
|
60
78
|
}
|
|
61
79
|
async deleteSession(id, sessionID) {
|
|
62
|
-
await this.doJSON("DELETE",
|
|
80
|
+
await this.doJSON("DELETE", `${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}`);
|
|
63
81
|
}
|
|
64
82
|
async signalSession(id, sessionID, signal) {
|
|
65
|
-
await this.doJSON("POST",
|
|
83
|
+
await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}/signal`, { signal });
|
|
66
84
|
}
|
|
67
85
|
async resizeSession(id, sessionID, cols, rows) {
|
|
68
|
-
await this.doJSON("POST",
|
|
86
|
+
await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}/resize`, { cols, rows });
|
|
69
87
|
}
|
|
70
88
|
async sessionLog(id, sessionID) {
|
|
71
|
-
return this.doBytes(
|
|
89
|
+
return this.doBytes(`${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}/log`);
|
|
72
90
|
}
|
|
73
91
|
async sessionRecording(id, sessionID) {
|
|
74
|
-
return this.doBytes(
|
|
92
|
+
return this.doBytes(`${this.versionPrefix}/sandboxes/${id}/sessions/${sessionID}/recording`);
|
|
75
93
|
}
|
|
76
94
|
attachSession(id, sessionID, options = {}) {
|
|
77
|
-
return openSessionAttach(this.baseURL, this.patToken, id, sessionID, options);
|
|
95
|
+
return openSessionAttach(this.baseURL, this.versionPrefix, this.patToken, id, sessionID, options);
|
|
78
96
|
}
|
|
79
97
|
async uploadFile(id, targetPath, data) {
|
|
80
98
|
const form = new FormData();
|
|
81
99
|
form.set("path", targetPath);
|
|
82
100
|
form.set("file", toBlob(data), basename(targetPath));
|
|
83
|
-
const response = await this.request("POST",
|
|
101
|
+
const response = await this.request("POST", `${this.versionPrefix}/sandboxes/${id}/toolbox/files/upload`, { body: form });
|
|
84
102
|
if (!response.ok) {
|
|
85
103
|
throw await decodeError(response);
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
106
|
async downloadFile(id, targetPath) {
|
|
89
|
-
const response = await this.request("GET",
|
|
107
|
+
const response = await this.request("GET", `${this.versionPrefix}/sandboxes/${id}/toolbox/files/download?path=${encodeURIComponent(targetPath)}`);
|
|
90
108
|
if (!response.ok) {
|
|
91
109
|
throw await decodeError(response);
|
|
92
110
|
}
|
|
93
111
|
return new Uint8Array(await response.arrayBuffer());
|
|
94
112
|
}
|
|
95
|
-
async exposePort(id, port) {
|
|
96
|
-
return this.exposePortWithProtocol(id, port);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Publish a raw TCP port through caddy-l4. The returned URL is in
|
|
100
|
-
* `tcp://<host>:<port>` form and works directly with `psql`, `redis-cli`,
|
|
101
|
-
* `mysql`, `mongosh`, and any other native-protocol client.
|
|
102
|
-
*/
|
|
103
|
-
async exposeTCPPort(id, port) {
|
|
104
|
-
return this.exposePortWithProtocol(id, port, "tcp");
|
|
105
|
-
}
|
|
106
113
|
/**
|
|
107
|
-
* Publish a
|
|
108
|
-
* `
|
|
109
|
-
*
|
|
114
|
+
* Publish a sandbox container port. The chosen wire protocol is selected by
|
|
115
|
+
* `options.protocol` (defaults to `"http"`):
|
|
116
|
+
*
|
|
117
|
+
* - `"http"`: Caddy HTTP reverse proxy at `https://<id>-<port>.<domain>`.
|
|
118
|
+
* - `"tcp"`: raw caddy-l4 listener on a parent-host port — pair with
|
|
119
|
+
* native protocol clients (psql, redis-cli, mysql, mongosh).
|
|
120
|
+
* - `"tls"`: caddy-l4 TLS-SNI route on the shared listener. Requires the
|
|
121
|
+
* daemon to have a domain configured AND `SB_L4_TLS_LISTEN` set.
|
|
122
|
+
*
|
|
123
|
+
* Returns a discriminated result keyed on `protocol`. Only the `"tcp"`
|
|
124
|
+
* variant carries `host` / `hostPort` — everything else is in `url`.
|
|
110
125
|
*/
|
|
111
|
-
async
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const body = protocol ? { protocol } : undefined;
|
|
116
|
-
const response = await this.doJSON("POST", `/v1/sandboxes/${id}/ports/${port}`, body);
|
|
117
|
-
return response.public_url;
|
|
126
|
+
async exposePort(id, port, options = {}) {
|
|
127
|
+
const body = options.protocol ? { protocol: options.protocol } : undefined;
|
|
128
|
+
const response = await this.doJSON("POST", `${this.versionPrefix}/sandboxes/${id}/ports/${port}`, body);
|
|
129
|
+
return fromApiExposePortResponse(response);
|
|
118
130
|
}
|
|
119
131
|
async unexposePort(id, port) {
|
|
120
|
-
await this.doJSON("DELETE",
|
|
132
|
+
await this.doJSON("DELETE", `${this.versionPrefix}/sandboxes/${id}/ports/${port}`);
|
|
121
133
|
}
|
|
122
134
|
async reconcile() {
|
|
123
|
-
await this.doJSON("POST", "/
|
|
135
|
+
await this.doJSON("POST", this.versioned("/admin/reconcile"));
|
|
124
136
|
}
|
|
125
137
|
async health() {
|
|
126
138
|
const response = await this.doJSON("GET", "/health");
|
|
127
139
|
return fromApiHealthStatus(response);
|
|
128
140
|
}
|
|
129
141
|
async mounts(id) {
|
|
130
|
-
const response = await this.doJSON("GET",
|
|
142
|
+
const response = await this.doJSON("GET", `${this.versionPrefix}/sandboxes/${id}/mounts`);
|
|
131
143
|
return response.mounts.map(fromApiMountSpecRedacted);
|
|
132
144
|
}
|
|
133
145
|
wrap(sandbox) {
|
|
@@ -219,14 +231,8 @@ export class SandboxResource {
|
|
|
219
231
|
async downloadFile(targetPath) {
|
|
220
232
|
return this.client.downloadFile(this.id, targetPath);
|
|
221
233
|
}
|
|
222
|
-
async exposePort(port) {
|
|
223
|
-
return this.client.exposePort(this.id, port);
|
|
224
|
-
}
|
|
225
|
-
async exposeTCPPort(port) {
|
|
226
|
-
return this.client.exposeTCPPort(this.id, port);
|
|
227
|
-
}
|
|
228
|
-
async exposeTLSPort(port) {
|
|
229
|
-
return this.client.exposeTLSPort(this.id, port);
|
|
234
|
+
async exposePort(port, options) {
|
|
235
|
+
return this.client.exposePort(this.id, port, options);
|
|
230
236
|
}
|
|
231
237
|
async unexposePort(port) {
|
|
232
238
|
await this.client.unexposePort(this.id, port);
|
|
@@ -379,6 +385,21 @@ function fromApiExposedPort(port) {
|
|
|
379
385
|
createdAt: port.created_at,
|
|
380
386
|
};
|
|
381
387
|
}
|
|
388
|
+
function fromApiExposePortResponse(response) {
|
|
389
|
+
switch (response.protocol) {
|
|
390
|
+
case "tcp":
|
|
391
|
+
return {
|
|
392
|
+
protocol: "tcp",
|
|
393
|
+
url: response.public_url,
|
|
394
|
+
host: response.host ?? "",
|
|
395
|
+
hostPort: response.host_port ?? 0,
|
|
396
|
+
};
|
|
397
|
+
case "tls":
|
|
398
|
+
return { protocol: "tls", url: response.public_url };
|
|
399
|
+
case "http":
|
|
400
|
+
return { protocol: "http", url: response.public_url };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
382
403
|
function fromApiExecResult(result) {
|
|
383
404
|
return {
|
|
384
405
|
stdout: result.stdout,
|
|
@@ -497,8 +518,14 @@ function describeWSClose(event) {
|
|
|
497
518
|
parts.push(`wasClean=${e.wasClean}`);
|
|
498
519
|
return parts.join(" ");
|
|
499
520
|
}
|
|
500
|
-
function
|
|
501
|
-
const
|
|
521
|
+
function toWebSocketBinaryFrame(data) {
|
|
522
|
+
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
523
|
+
const frame = new Uint8Array(bytes.byteLength);
|
|
524
|
+
frame.set(bytes);
|
|
525
|
+
return frame;
|
|
526
|
+
}
|
|
527
|
+
function openExecStream(baseURL, versionPrefix, patToken, sandboxID, options) {
|
|
528
|
+
const wsURL = baseURL.replace(/^http/, "ws") + `${versionPrefix}/sandboxes/${encodeURIComponent(sandboxID)}/toolbox/process/exec/stream`;
|
|
502
529
|
const WS = globalThis.WebSocket;
|
|
503
530
|
if (!WS) {
|
|
504
531
|
throw new Error("WebSocket is not available in this runtime — Node 22+ or a browser is required");
|
|
@@ -587,8 +614,7 @@ function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
|
587
614
|
failWith(detail !== "" ? `websocket error: ${detail}` : "websocket error");
|
|
588
615
|
});
|
|
589
616
|
const sendBinary = (data) => {
|
|
590
|
-
|
|
591
|
-
ws.send(bytes);
|
|
617
|
+
ws.send(toWebSocketBinaryFrame(data));
|
|
592
618
|
};
|
|
593
619
|
return {
|
|
594
620
|
write: sendBinary,
|
|
@@ -604,8 +630,8 @@ function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
|
604
630
|
done,
|
|
605
631
|
};
|
|
606
632
|
}
|
|
607
|
-
function openSessionAttach(baseURL, patToken, sandboxID, sessionID, options) {
|
|
608
|
-
const wsURL = baseURL.replace(/^http/, "ws") +
|
|
633
|
+
function openSessionAttach(baseURL, versionPrefix, patToken, sandboxID, sessionID, options) {
|
|
634
|
+
const wsURL = baseURL.replace(/^http/, "ws") + `${versionPrefix}/sandboxes/${encodeURIComponent(sandboxID)}/sessions/${encodeURIComponent(sessionID)}/attach`;
|
|
609
635
|
const WS = globalThis.WebSocket;
|
|
610
636
|
if (!WS) {
|
|
611
637
|
throw new Error("WebSocket is not available in this runtime — Node 22+ or a browser is required");
|
|
@@ -687,8 +713,7 @@ function openSessionAttach(baseURL, patToken, sandboxID, sessionID, options) {
|
|
|
687
713
|
failWith(detail !== "" ? `websocket error: ${detail}` : "websocket error");
|
|
688
714
|
});
|
|
689
715
|
const sendBinary = (data) => {
|
|
690
|
-
|
|
691
|
-
ws.send(bytes);
|
|
716
|
+
ws.send(toWebSocketBinaryFrame(data));
|
|
692
717
|
};
|
|
693
718
|
return {
|
|
694
719
|
write: sendBinary,
|
package/dist/types.d.ts
CHANGED
|
@@ -129,6 +129,35 @@ export interface ExposedPort {
|
|
|
129
129
|
publicURL: string;
|
|
130
130
|
createdAt: string;
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Wire protocol an exposure publishes through:
|
|
134
|
+
* - "http": Caddy HTTP reverse proxy at https://<id>-<port>.<domain>.
|
|
135
|
+
* - "tcp": raw caddy-l4 listener on a parent-host port.
|
|
136
|
+
* - "tls": caddy-l4 TLS-SNI route on the shared :443 listener.
|
|
137
|
+
*/
|
|
138
|
+
export type ExposeProtocol = "http" | "tcp" | "tls";
|
|
139
|
+
export interface ExposePortOptions {
|
|
140
|
+
/** Defaults to "http" when omitted. */
|
|
141
|
+
protocol?: ExposeProtocol;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Discriminated result from `exposePort`. Branch on `protocol` to access the
|
|
145
|
+
* fields that are meaningful for the chosen wire protocol — only the raw-TCP
|
|
146
|
+
* variant carries `host` and `hostPort`, which are what native protocol
|
|
147
|
+
* clients (psql, redis-cli, mysql, mongosh, …) need to dial.
|
|
148
|
+
*/
|
|
149
|
+
export type ExposeResult = {
|
|
150
|
+
protocol: "http";
|
|
151
|
+
url: string;
|
|
152
|
+
} | {
|
|
153
|
+
protocol: "tcp";
|
|
154
|
+
url: string;
|
|
155
|
+
host: string;
|
|
156
|
+
hostPort: number;
|
|
157
|
+
} | {
|
|
158
|
+
protocol: "tls";
|
|
159
|
+
url: string;
|
|
160
|
+
};
|
|
132
161
|
export interface Sandbox {
|
|
133
162
|
id: string;
|
|
134
163
|
image: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aerol-ai/aerolvm-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"test": "tsc -p tsconfig.test.json --outDir dist-test && node --test dist-test/*.test.js"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^
|
|
24
|
-
"typescript": "^
|
|
23
|
+
"@types/node": "^25.6.2",
|
|
24
|
+
"typescript": "^6.0.3",
|
|
25
25
|
"tsx": "^4.21.0"
|
|
26
26
|
}
|
|
27
27
|
}
|