@aerol-ai/aerolvm-sdk 0.1.6 → 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 +78 -57
- package/dist/types.d.ts +29 -0
- package/package.json +1 -1
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,
|
|
@@ -503,8 +524,8 @@ function toWebSocketBinaryFrame(data) {
|
|
|
503
524
|
frame.set(bytes);
|
|
504
525
|
return frame;
|
|
505
526
|
}
|
|
506
|
-
function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
507
|
-
const wsURL = baseURL.replace(/^http/, "ws") +
|
|
527
|
+
function openExecStream(baseURL, versionPrefix, patToken, sandboxID, options) {
|
|
528
|
+
const wsURL = baseURL.replace(/^http/, "ws") + `${versionPrefix}/sandboxes/${encodeURIComponent(sandboxID)}/toolbox/process/exec/stream`;
|
|
508
529
|
const WS = globalThis.WebSocket;
|
|
509
530
|
if (!WS) {
|
|
510
531
|
throw new Error("WebSocket is not available in this runtime — Node 22+ or a browser is required");
|
|
@@ -609,8 +630,8 @@ function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
|
609
630
|
done,
|
|
610
631
|
};
|
|
611
632
|
}
|
|
612
|
-
function openSessionAttach(baseURL, patToken, sandboxID, sessionID, options) {
|
|
613
|
-
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`;
|
|
614
635
|
const WS = globalThis.WebSocket;
|
|
615
636
|
if (!WS) {
|
|
616
637
|
throw new Error("WebSocket is not available in this runtime — Node 22+ or a browser is required");
|
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;
|