@miosa/sdk 1.2.5 → 1.2.6
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 +107 -59
- package/dist/index.d.ts +331 -202
- package/dist/index.js +572 -603
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/client.ts +0 -293
- package/src/errors.ts +0 -136
- package/src/http.test.ts +0 -374
- package/src/http.ts +0 -390
- package/src/index.ts +0 -569
- package/src/resources/admin.ts +0 -348
- package/src/resources/analytics.ts +0 -60
- package/src/resources/api-keys.ts +0 -119
- package/src/resources/audit-log.ts +0 -64
- package/src/resources/benchmarks.ts +0 -104
- package/src/resources/builder-sessions.ts +0 -75
- package/src/resources/channels.ts +0 -143
- package/src/resources/checkpoints.ts +0 -225
- package/src/resources/command-center.ts +0 -73
- package/src/resources/community.ts +0 -103
- package/src/resources/completions.ts +0 -104
- package/src/resources/computer-auto-stop.ts +0 -43
- package/src/resources/computer-env.ts +0 -76
- package/src/resources/computer-logs.ts +0 -50
- package/src/resources/computer-osa.ts +0 -76
- package/src/resources/computer-ports.ts +0 -91
- package/src/resources/computer-terminal.ts +0 -61
- package/src/resources/computer-volumes.ts +0 -64
- package/src/resources/computer.ts +0 -551
- package/src/resources/computers.ts +0 -75
- package/src/resources/credits.ts +0 -43
- package/src/resources/cron-jobs.ts +0 -191
- package/src/resources/custom_domains.ts +0 -123
- package/src/resources/dashboard.ts +0 -49
- package/src/resources/databases.ts +0 -218
- package/src/resources/deployments.test.ts +0 -197
- package/src/resources/deployments.ts +0 -1208
- package/src/resources/desktop.ts +0 -134
- package/src/resources/devices.test.ts +0 -92
- package/src/resources/devices.ts +0 -291
- package/src/resources/egress.test.ts +0 -318
- package/src/resources/egressAudit.ts +0 -245
- package/src/resources/egressNetwork.ts +0 -450
- package/src/resources/egressSecrets.ts +0 -577
- package/src/resources/email.ts +0 -212
- package/src/resources/embeddings.ts +0 -36
- package/src/resources/events.ts +0 -296
- package/src/resources/exec.ts +0 -319
- package/src/resources/external-keys.ts +0 -79
- package/src/resources/files.test.ts +0 -339
- package/src/resources/files.ts +0 -220
- package/src/resources/flat-custom-domains.ts +0 -127
- package/src/resources/functions.ts +0 -178
- package/src/resources/health-checks.ts +0 -165
- package/src/resources/integrations.ts +0 -183
- package/src/resources/mcp.ts +0 -70
- package/src/resources/models.ts +0 -45
- package/src/resources/network_policy.ts +0 -73
- package/src/resources/open-computers/agents.ts +0 -91
- package/src/resources/open-computers/apps.ts +0 -102
- package/src/resources/open-computers/clusters.ts +0 -88
- package/src/resources/open-computers/desktop.ts +0 -34
- package/src/resources/open-computers/files.ts +0 -97
- package/src/resources/open-computers/hosts.ts +0 -85
- package/src/resources/open-computers/index.ts +0 -98
- package/src/resources/open-computers/jobs.ts +0 -75
- package/src/resources/open-computers/open_computers.test.ts +0 -288
- package/src/resources/open-computers/secrets.ts +0 -115
- package/src/resources/open-computers/terminal.ts +0 -33
- package/src/resources/open-computers/tunnels.ts +0 -87
- package/src/resources/open-computers/types.ts +0 -343
- package/src/resources/open-computers/workspaces.ts +0 -135
- package/src/resources/org-invites.ts +0 -189
- package/src/resources/project-auth.ts +0 -142
- package/src/resources/project-integrations.ts +0 -133
- package/src/resources/provider-defaults.ts +0 -89
- package/src/resources/regions.ts +0 -94
- package/src/resources/sandbox-templates.ts +0 -195
- package/src/resources/sandboxes.live.test.ts +0 -92
- package/src/resources/sandboxes.test.ts +0 -655
- package/src/resources/sandboxes.ts +0 -1437
- package/src/resources/settings.ts +0 -143
- package/src/resources/snapshots-standalone.ts +0 -51
- package/src/resources/storage.ts +0 -221
- package/src/resources/tenant.ts +0 -66
- package/src/resources/usage.ts +0 -85
- package/src/resources/volumes.ts +0 -117
- package/src/resources/webhooks.ts +0 -239
- package/src/resources/workspace-invites.ts +0 -188
- package/src/resources/workspace-members.test.ts +0 -121
- package/src/resources/workspace-members.ts +0 -143
- package/src/types.ts +0 -463
|
@@ -1,1437 +0,0 @@
|
|
|
1
|
-
import { MiosaError } from "../errors.js";
|
|
2
|
-
import { HttpClient } from "../http.js";
|
|
3
|
-
import { SandboxAudit } from "./egressAudit.js";
|
|
4
|
-
import { SandboxNetwork } from "./egressNetwork.js";
|
|
5
|
-
import { SandboxSecrets } from "./egressSecrets.js";
|
|
6
|
-
|
|
7
|
-
function encodeContent(content: string | Uint8Array): string {
|
|
8
|
-
const bytes =
|
|
9
|
-
typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
10
|
-
const maybeBuffer = (
|
|
11
|
-
globalThis as {
|
|
12
|
-
Buffer?: { from(b: Uint8Array): { toString(e: string): string } };
|
|
13
|
-
}
|
|
14
|
-
).Buffer;
|
|
15
|
-
if (maybeBuffer) return maybeBuffer.from(bytes).toString("base64");
|
|
16
|
-
let bin = "";
|
|
17
|
-
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);
|
|
18
|
-
return btoa(bin);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const SANDBOX_TEMPLATE = "miosa-sandbox";
|
|
22
|
-
|
|
23
|
-
export type SandboxId = string & { readonly __brand: "SandboxId" };
|
|
24
|
-
export type SandboxState =
|
|
25
|
-
| "provisioning"
|
|
26
|
-
| "running"
|
|
27
|
-
| "paused"
|
|
28
|
-
| "destroyed"
|
|
29
|
-
| "error";
|
|
30
|
-
|
|
31
|
-
export interface SandboxCreateParams {
|
|
32
|
-
templateId?: string;
|
|
33
|
-
template_id?: string;
|
|
34
|
-
image?: string;
|
|
35
|
-
cpuCount?: number;
|
|
36
|
-
cpu_count?: number;
|
|
37
|
-
memoryMb?: number;
|
|
38
|
-
memory_mb?: number;
|
|
39
|
-
diskMb?: number;
|
|
40
|
-
disk_mb?: number;
|
|
41
|
-
diskSizeMb?: number;
|
|
42
|
-
disk_size_mb?: number;
|
|
43
|
-
timeoutSec?: number;
|
|
44
|
-
timeout_sec?: number;
|
|
45
|
-
idleTimeoutSec?: number;
|
|
46
|
-
idle_timeout_sec?: number;
|
|
47
|
-
alwaysOn?: boolean;
|
|
48
|
-
always_on?: boolean;
|
|
49
|
-
env?: Record<string, string>;
|
|
50
|
-
metadata?: Record<string, unknown>;
|
|
51
|
-
services?: Array<Record<string, unknown>>;
|
|
52
|
-
readinessProbe?: Record<string, unknown>;
|
|
53
|
-
readiness_probe?: Record<string, unknown>;
|
|
54
|
-
database?: Record<string, unknown> | boolean;
|
|
55
|
-
githubRepoUrl?: string;
|
|
56
|
-
github_repo_url?: string;
|
|
57
|
-
githubBranch?: string;
|
|
58
|
-
github_branch?: string;
|
|
59
|
-
githubClonePath?: string;
|
|
60
|
-
github_clone_path?: string;
|
|
61
|
-
name?: string;
|
|
62
|
-
region?: string;
|
|
63
|
-
entrypoint?: string;
|
|
64
|
-
tags?: string[];
|
|
65
|
-
idempotencyKey?: string;
|
|
66
|
-
idempotency_key?: string;
|
|
67
|
-
slug?: string;
|
|
68
|
-
// White-label attribution. See platform/attribution docs.
|
|
69
|
-
externalWorkspaceId?: string;
|
|
70
|
-
external_workspace_id?: string;
|
|
71
|
-
externalUserId?: string;
|
|
72
|
-
external_user_id?: string;
|
|
73
|
-
externalProjectId?: string;
|
|
74
|
-
external_project_id?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface SandboxListParams {
|
|
78
|
-
state?: SandboxState | string;
|
|
79
|
-
tags?: string[];
|
|
80
|
-
externalWorkspaceId?: string;
|
|
81
|
-
external_workspace_id?: string;
|
|
82
|
-
externalUserId?: string;
|
|
83
|
-
external_user_id?: string;
|
|
84
|
-
externalProjectId?: string;
|
|
85
|
-
external_project_id?: string;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface SandboxExecOptions {
|
|
89
|
-
cwd?: string;
|
|
90
|
-
workingDir?: string;
|
|
91
|
-
working_dir?: string;
|
|
92
|
-
env?: Record<string, string>;
|
|
93
|
-
timeout?: number;
|
|
94
|
-
timeoutSec?: number;
|
|
95
|
-
timeout_sec?: number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface SandboxExecResult {
|
|
99
|
-
stdout: string;
|
|
100
|
-
stderr: string;
|
|
101
|
-
exitCode: number;
|
|
102
|
-
exit_code: number;
|
|
103
|
-
durationMs?: number;
|
|
104
|
-
duration_ms?: number;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export type SandboxExecEvent =
|
|
108
|
-
| { type?: "stdout"; line: string }
|
|
109
|
-
| { type?: "stderr"; line: string }
|
|
110
|
-
| { type?: "exit"; exit_code: number; exitCode?: number }
|
|
111
|
-
| Record<string, unknown>;
|
|
112
|
-
|
|
113
|
-
export interface SandboxExecRunner {
|
|
114
|
-
(command: string, options?: SandboxExecOptions): Promise<SandboxExecResult>;
|
|
115
|
-
run(
|
|
116
|
-
command: string,
|
|
117
|
-
options?: SandboxExecOptions,
|
|
118
|
-
): Promise<SandboxExecResult>;
|
|
119
|
-
stream(
|
|
120
|
-
command: string,
|
|
121
|
-
options?: SandboxExecOptions,
|
|
122
|
-
): AsyncIterableIterator<SandboxExecEvent>;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export interface SandboxData {
|
|
126
|
-
id: SandboxId;
|
|
127
|
-
state: SandboxState;
|
|
128
|
-
ready?: boolean;
|
|
129
|
-
template_id?: string;
|
|
130
|
-
image_id?: string | null;
|
|
131
|
-
cpu_count?: number | null;
|
|
132
|
-
memory_mb?: number | null;
|
|
133
|
-
disk_mb?: number | null;
|
|
134
|
-
disk_size_mb?: number | null;
|
|
135
|
-
timeout_sec?: number | null;
|
|
136
|
-
boot_path?: string | null;
|
|
137
|
-
boot_ms?: number | null;
|
|
138
|
-
ready_at?: string | null;
|
|
139
|
-
preview_url?: string | null;
|
|
140
|
-
metadata?: Record<string, unknown>;
|
|
141
|
-
inserted_at?: string;
|
|
142
|
-
created_at?: string;
|
|
143
|
-
started_at?: string | null;
|
|
144
|
-
destroyed_at?: string | null;
|
|
145
|
-
total_runtime_sec?: number | null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export interface SandboxTemplate {
|
|
149
|
-
id: string;
|
|
150
|
-
name?: string;
|
|
151
|
-
slug?: string;
|
|
152
|
-
description?: string;
|
|
153
|
-
image_id?: string;
|
|
154
|
-
built_in?: boolean;
|
|
155
|
-
status?: string;
|
|
156
|
-
preview_port?: number | null;
|
|
157
|
-
workdir?: string;
|
|
158
|
-
install_command?: string | null;
|
|
159
|
-
start_command?: string | null;
|
|
160
|
-
readiness_probe?: Record<string, unknown> | null;
|
|
161
|
-
artifact_paths?: string[];
|
|
162
|
-
runtimes?: string[];
|
|
163
|
-
tags?: string[];
|
|
164
|
-
build_spec?: Record<string, unknown>;
|
|
165
|
-
current_build_id?: string | null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export interface SandboxTemplateList {
|
|
169
|
-
data: SandboxTemplate[];
|
|
170
|
-
default_template_id?: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface SandboxBuildSpec {
|
|
174
|
-
from: string;
|
|
175
|
-
vcpu?: number;
|
|
176
|
-
memoryMib?: number;
|
|
177
|
-
diskMib?: number;
|
|
178
|
-
steps?: Array<Record<string, unknown>>;
|
|
179
|
-
env?: Record<string, string>;
|
|
180
|
-
workdir?: string;
|
|
181
|
-
user?: string;
|
|
182
|
-
startCmd?: string;
|
|
183
|
-
readyCmd?: string;
|
|
184
|
-
previewPort?: number;
|
|
185
|
-
artifactPaths?: string[];
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export interface SandboxBuildSpecError {
|
|
189
|
-
code: string;
|
|
190
|
-
field: string;
|
|
191
|
-
message: string;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export interface SandboxBuildSpecValidation {
|
|
195
|
-
valid: boolean;
|
|
196
|
-
build_spec?: Record<string, unknown>;
|
|
197
|
-
errors?: SandboxBuildSpecError[];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export interface SandboxTemplateCreateParams {
|
|
201
|
-
name: string;
|
|
202
|
-
buildSpec?: SandboxBuildSpec;
|
|
203
|
-
build_spec?: SandboxBuildSpec;
|
|
204
|
-
slug?: string;
|
|
205
|
-
description?: string;
|
|
206
|
-
metadata?: Record<string, unknown>;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export interface SandboxTemplateBuildCreateParams {
|
|
210
|
-
buildSpec?: SandboxBuildSpec;
|
|
211
|
-
build_spec?: SandboxBuildSpec;
|
|
212
|
-
metadata?: Record<string, unknown>;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export interface SandboxTemplateBuild {
|
|
216
|
-
id: string;
|
|
217
|
-
sandbox_template_id: string;
|
|
218
|
-
source_type: "build_spec" | string;
|
|
219
|
-
state:
|
|
220
|
-
| "queued"
|
|
221
|
-
| "building"
|
|
222
|
-
| "certifying"
|
|
223
|
-
| "snapshotting"
|
|
224
|
-
| "ready"
|
|
225
|
-
| "failed"
|
|
226
|
-
| "cancelled";
|
|
227
|
-
image_id?: string | null;
|
|
228
|
-
rootfs_path?: string | null;
|
|
229
|
-
snapshot_manifest?: Record<string, unknown>;
|
|
230
|
-
log_url?: string | null;
|
|
231
|
-
error_code?: string | null;
|
|
232
|
-
error_message?: string | null;
|
|
233
|
-
started_at?: string | null;
|
|
234
|
-
finished_at?: string | null;
|
|
235
|
-
duration_ms?: number | null;
|
|
236
|
-
build_spec?: Record<string, unknown>;
|
|
237
|
-
inserted_at?: string;
|
|
238
|
-
updated_at?: string;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export interface SandboxFileEntry {
|
|
242
|
-
name: string;
|
|
243
|
-
path: string;
|
|
244
|
-
size?: number;
|
|
245
|
-
is_dir?: boolean;
|
|
246
|
-
isDir?: boolean;
|
|
247
|
-
modified_at?: string;
|
|
248
|
-
modifiedAt?: string;
|
|
249
|
-
[key: string]: unknown;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export interface SandboxFileTreeNode {
|
|
253
|
-
path: string;
|
|
254
|
-
name: string;
|
|
255
|
-
type: "file" | "dir";
|
|
256
|
-
size?: number;
|
|
257
|
-
modified_at?: string;
|
|
258
|
-
children?: SandboxFileTreeNode[];
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export interface SandboxWriteManyEntry {
|
|
262
|
-
path: string;
|
|
263
|
-
content: string | Uint8Array;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export interface SandboxWriteManyResult {
|
|
267
|
-
written: Array<{ path: string; size_bytes: number }>;
|
|
268
|
-
failed: Array<{ path: string; error: string }>;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export interface SandboxFileChange {
|
|
272
|
-
type: "created" | "modified" | "deleted";
|
|
273
|
-
path: string;
|
|
274
|
-
size_bytes?: number;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export interface SandboxEnvVar {
|
|
278
|
-
key: string;
|
|
279
|
-
encrypted: boolean;
|
|
280
|
-
value?: string;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export interface SandboxFileList {
|
|
284
|
-
path?: string;
|
|
285
|
-
entries: SandboxFileEntry[];
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export interface SandboxFileStat {
|
|
289
|
-
path: string;
|
|
290
|
-
size?: number;
|
|
291
|
-
is_dir?: boolean;
|
|
292
|
-
isDir?: boolean;
|
|
293
|
-
mode?: string;
|
|
294
|
-
modified_at?: string;
|
|
295
|
-
modifiedAt?: string;
|
|
296
|
-
[key: string]: unknown;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export interface SandboxSnapshot {
|
|
300
|
-
id: string;
|
|
301
|
-
sandbox_id?: string;
|
|
302
|
-
status?: string;
|
|
303
|
-
comment?: string;
|
|
304
|
-
created_at?: string;
|
|
305
|
-
updated_at?: string;
|
|
306
|
-
[key: string]: unknown;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export interface SandboxDeployParams {
|
|
310
|
-
name?: string;
|
|
311
|
-
deploymentId?: string;
|
|
312
|
-
deployment_id?: string;
|
|
313
|
-
path?: string;
|
|
314
|
-
sourcePath?: string;
|
|
315
|
-
source_path?: string;
|
|
316
|
-
outputPath?: string;
|
|
317
|
-
output_path?: string;
|
|
318
|
-
sourceSnapshotPath?: string;
|
|
319
|
-
source_snapshot_path?: string;
|
|
320
|
-
entrypoint?: string;
|
|
321
|
-
buildCommand?: string;
|
|
322
|
-
build_command?: string;
|
|
323
|
-
runCommand?: string;
|
|
324
|
-
run_command?: string;
|
|
325
|
-
startCommand?: string;
|
|
326
|
-
start_command?: string;
|
|
327
|
-
port?: number;
|
|
328
|
-
healthCheckPath?: string;
|
|
329
|
-
health_check_path?: string;
|
|
330
|
-
deploymentType?: "miosa_deploy" | "docker_deploy" | "docker-deploy" | string;
|
|
331
|
-
deployment_type?: "miosa_deploy" | "docker_deploy" | "docker-deploy" | string;
|
|
332
|
-
dockerDeployTemplateId?: string;
|
|
333
|
-
docker_deploy_template_id?: string;
|
|
334
|
-
type?: "static" | "dynamic" | "server" | string;
|
|
335
|
-
mode?: "static" | "dynamic" | "server" | string;
|
|
336
|
-
database?: boolean | Record<string, unknown>;
|
|
337
|
-
resources?: Record<string, unknown>;
|
|
338
|
-
domain?: string;
|
|
339
|
-
customDomain?: string;
|
|
340
|
-
custom_domain?: string;
|
|
341
|
-
idempotencyKey?: string;
|
|
342
|
-
idempotency_key?: string;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
type WireEnvelope<T> = T | { data: T };
|
|
346
|
-
|
|
347
|
-
function unwrap<T>(payload: WireEnvelope<T>): T {
|
|
348
|
-
if (
|
|
349
|
-
payload !== null &&
|
|
350
|
-
typeof payload === "object" &&
|
|
351
|
-
"data" in payload &&
|
|
352
|
-
(payload as { data?: unknown }).data !== undefined
|
|
353
|
-
) {
|
|
354
|
-
return (payload as { data: T }).data;
|
|
355
|
-
}
|
|
356
|
-
return payload as T;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function listItems(
|
|
360
|
-
payload:
|
|
361
|
-
| WireEnvelope<SandboxData[]>
|
|
362
|
-
| { sandboxes: SandboxData[] }
|
|
363
|
-
| { items: SandboxData[] },
|
|
364
|
-
): SandboxData[] {
|
|
365
|
-
if (Array.isArray(payload)) return payload;
|
|
366
|
-
if ("data" in payload && Array.isArray(payload.data)) return payload.data;
|
|
367
|
-
if ("sandboxes" in payload && Array.isArray(payload.sandboxes))
|
|
368
|
-
return payload.sandboxes;
|
|
369
|
-
if ("items" in payload && Array.isArray(payload.items)) return payload.items;
|
|
370
|
-
return [];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function createBody(params: SandboxCreateParams = {}): Record<string, unknown> {
|
|
374
|
-
const templateId =
|
|
375
|
-
params.templateId ?? params.template_id ?? params.image ?? SANDBOX_TEMPLATE;
|
|
376
|
-
return stripUndefined({
|
|
377
|
-
template_id: templateId,
|
|
378
|
-
cpu_count: params.cpuCount ?? params.cpu_count,
|
|
379
|
-
memory_mb: params.memoryMb ?? params.memory_mb,
|
|
380
|
-
disk_mb: params.diskMb ?? params.disk_mb,
|
|
381
|
-
disk_size_mb: params.diskSizeMb ?? params.disk_size_mb,
|
|
382
|
-
timeout_sec: params.timeoutSec ?? params.timeout_sec,
|
|
383
|
-
idle_timeout_sec: params.idleTimeoutSec ?? params.idle_timeout_sec,
|
|
384
|
-
always_on: params.alwaysOn ?? params.always_on,
|
|
385
|
-
env: params.env,
|
|
386
|
-
metadata: params.metadata,
|
|
387
|
-
services: params.services,
|
|
388
|
-
readiness_probe: params.readinessProbe ?? params.readiness_probe,
|
|
389
|
-
database: params.database,
|
|
390
|
-
github_repo_url: params.githubRepoUrl ?? params.github_repo_url,
|
|
391
|
-
github_branch: params.githubBranch ?? params.github_branch,
|
|
392
|
-
github_clone_path: params.githubClonePath ?? params.github_clone_path,
|
|
393
|
-
name: params.name,
|
|
394
|
-
region: params.region,
|
|
395
|
-
entrypoint: params.entrypoint,
|
|
396
|
-
tags: params.tags,
|
|
397
|
-
slug: params.slug,
|
|
398
|
-
external_workspace_id:
|
|
399
|
-
params.externalWorkspaceId ?? params.external_workspace_id,
|
|
400
|
-
external_user_id: params.externalUserId ?? params.external_user_id,
|
|
401
|
-
external_project_id: params.externalProjectId ?? params.external_project_id,
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function execBody(
|
|
406
|
-
command: string,
|
|
407
|
-
options: SandboxExecOptions = {},
|
|
408
|
-
): Record<string, unknown> {
|
|
409
|
-
return stripUndefined({
|
|
410
|
-
command,
|
|
411
|
-
cwd: options.cwd ?? options.workingDir ?? options.working_dir,
|
|
412
|
-
env: options.env,
|
|
413
|
-
timeout: options.timeout ?? options.timeoutSec ?? options.timeout_sec,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function stripUndefined(
|
|
418
|
-
input: Record<string, unknown>,
|
|
419
|
-
): Record<string, unknown> {
|
|
420
|
-
return Object.fromEntries(
|
|
421
|
-
Object.entries(input).filter(([, value]) => value !== undefined),
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
export class SandboxCommands {
|
|
426
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
427
|
-
|
|
428
|
-
run(
|
|
429
|
-
command: string,
|
|
430
|
-
options?: SandboxExecOptions,
|
|
431
|
-
): Promise<SandboxExecResult> {
|
|
432
|
-
return this.sandbox.exec.run(command, options);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
stream(
|
|
436
|
-
command: string,
|
|
437
|
-
options?: SandboxExecOptions,
|
|
438
|
-
): AsyncIterableIterator<SandboxExecEvent> {
|
|
439
|
-
return this.sandbox.exec.stream(command, options);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export class SandboxFiles {
|
|
444
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
445
|
-
|
|
446
|
-
async write(path: string, content: string | Uint8Array): Promise<void> {
|
|
447
|
-
await this.sandbox.writeFile(path, content);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
async read(path: string): Promise<Uint8Array> {
|
|
451
|
-
return this.sandbox.download(path);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async readText(path: string): Promise<string> {
|
|
455
|
-
return new TextDecoder().decode(await this.read(path));
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
list(path = "/workspace"): Promise<SandboxFileList> {
|
|
459
|
-
return this.sandbox.listFiles(path);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
stat(path: string): Promise<SandboxFileStat> {
|
|
463
|
-
return this.sandbox.statFile(path);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
upload(path: string, content: string | Uint8Array): Promise<void> {
|
|
467
|
-
return this.write(path, content);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
download(path: string): Promise<Uint8Array> {
|
|
471
|
-
return this.read(path);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/** GET /api/v1/sandboxes/{id}/files/tree — recursive directory tree. */
|
|
475
|
-
async tree(path = "/workspace", depth = 3): Promise<SandboxFileTreeNode> {
|
|
476
|
-
const http = (this.sandbox as unknown as { http: HttpClient }).http;
|
|
477
|
-
const response = await http.get<unknown>(
|
|
478
|
-
`/sandboxes/${this.sandbox.id}/files/tree`,
|
|
479
|
-
{ path, depth },
|
|
480
|
-
);
|
|
481
|
-
if (
|
|
482
|
-
response &&
|
|
483
|
-
typeof response === "object" &&
|
|
484
|
-
"data" in (response as object)
|
|
485
|
-
) {
|
|
486
|
-
return (response as { data: SandboxFileTreeNode }).data;
|
|
487
|
-
}
|
|
488
|
-
return response as SandboxFileTreeNode;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/** POST /api/v1/sandboxes/{id}/files/write-many — write multiple files atomically. */
|
|
492
|
-
async writeMany(
|
|
493
|
-
files: SandboxWriteManyEntry[],
|
|
494
|
-
): Promise<SandboxWriteManyResult> {
|
|
495
|
-
const http = (this.sandbox as unknown as { http: HttpClient }).http;
|
|
496
|
-
const payload = files.map((f) => ({
|
|
497
|
-
path: f.path,
|
|
498
|
-
content_base64: encodeContent(f.content),
|
|
499
|
-
}));
|
|
500
|
-
const response = await http.post<unknown>(
|
|
501
|
-
`/sandboxes/${this.sandbox.id}/files/write-many`,
|
|
502
|
-
{ files: payload },
|
|
503
|
-
);
|
|
504
|
-
if (
|
|
505
|
-
response &&
|
|
506
|
-
typeof response === "object" &&
|
|
507
|
-
"data" in (response as object)
|
|
508
|
-
) {
|
|
509
|
-
return (response as { data: SandboxWriteManyResult }).data;
|
|
510
|
-
}
|
|
511
|
-
return response as SandboxWriteManyResult;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/** GET /api/v1/sandboxes/{id}/files/watch (SSE) — live file change events. */
|
|
515
|
-
watch(): AsyncIterableIterator<SandboxFileChange> {
|
|
516
|
-
const http = (this.sandbox as unknown as { http: HttpClient }).http;
|
|
517
|
-
return http.stream<SandboxFileChange>(
|
|
518
|
-
`/sandboxes/${this.sandbox.id}/files/watch`,
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
export class SandboxPreview {
|
|
524
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
525
|
-
|
|
526
|
-
expose(port?: number): Promise<string> {
|
|
527
|
-
return this.sandbox.expose(port);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
export class SandboxArtifacts {
|
|
532
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
533
|
-
|
|
534
|
-
list(): Promise<Record<string, unknown>> {
|
|
535
|
-
return this.sandbox.getArtifacts();
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
export class SandboxLogs {
|
|
540
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
541
|
-
|
|
542
|
-
get(lines?: number): Promise<string | Record<string, unknown>> {
|
|
543
|
-
return this.sandbox.getLogs(lines);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
stream(): AsyncIterableIterator<Record<string, unknown>> {
|
|
547
|
-
return this.sandbox.streamLogs();
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export class SandboxSnapshots {
|
|
552
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
553
|
-
|
|
554
|
-
create(comment?: string): Promise<SandboxSnapshot> {
|
|
555
|
-
return this.sandbox.createSnapshot(comment);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
list(): Promise<SandboxSnapshot[]> {
|
|
559
|
-
return this.sandbox.listSnapshots();
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
restore(snapshotId: string): Promise<Sandbox> {
|
|
563
|
-
return this.sandbox.restoreSnapshot(snapshotId);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
delete(snapshotId: string): Promise<void> {
|
|
567
|
-
return this.sandbox.deleteSnapshot(snapshotId);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
export class SandboxTerminal {
|
|
572
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
573
|
-
|
|
574
|
-
async create(
|
|
575
|
-
params: {
|
|
576
|
-
cols?: number;
|
|
577
|
-
rows?: number;
|
|
578
|
-
shell?: string;
|
|
579
|
-
cwd?: string;
|
|
580
|
-
env?: Record<string, string>;
|
|
581
|
-
} = {},
|
|
582
|
-
): Promise<Record<string, unknown>> {
|
|
583
|
-
const body = Object.fromEntries(
|
|
584
|
-
Object.entries(params).filter(([, v]) => v !== undefined),
|
|
585
|
-
);
|
|
586
|
-
const response = unwrap(
|
|
587
|
-
await (this.sandbox as unknown as { http: HttpClient }).http.post<
|
|
588
|
-
WireEnvelope<Record<string, unknown>>
|
|
589
|
-
>(`/sandboxes/${this.sandbox.id}/terminal`, body),
|
|
590
|
-
);
|
|
591
|
-
return response as Record<string, unknown>;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
async delete(sessionId: string): Promise<void> {
|
|
595
|
-
await (this.sandbox as unknown as { http: HttpClient }).http.delete(
|
|
596
|
-
`/sandboxes/${this.sandbox.id}/terminal/${sessionId}`,
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
export class SandboxEvents {
|
|
602
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
603
|
-
|
|
604
|
-
/** Stream live sandbox events via SSE. */
|
|
605
|
-
stream(): AsyncIterableIterator<Record<string, unknown>> {
|
|
606
|
-
return (this.sandbox as unknown as { http: HttpClient }).http.stream<
|
|
607
|
-
Record<string, unknown>
|
|
608
|
-
>(`/sandboxes/${this.sandbox.id}/events`);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
export class SandboxPreviews {
|
|
613
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
614
|
-
|
|
615
|
-
private get http(): HttpClient {
|
|
616
|
-
return (this.sandbox as unknown as { http: HttpClient }).http;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async list(): Promise<Record<string, unknown>[]> {
|
|
620
|
-
const response = await this.http.get<unknown>(
|
|
621
|
-
`/sandboxes/${this.sandbox.id}/previews`,
|
|
622
|
-
);
|
|
623
|
-
if (Array.isArray(response)) return response as Record<string, unknown>[];
|
|
624
|
-
if (response && typeof response === "object") {
|
|
625
|
-
const r = response as Record<string, unknown>;
|
|
626
|
-
for (const k of ["data", "previews", "items"]) {
|
|
627
|
-
if (Array.isArray(r[k])) return r[k] as Record<string, unknown>[];
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
return [];
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
async create(
|
|
634
|
-
port: number,
|
|
635
|
-
opts: Record<string, unknown> = {},
|
|
636
|
-
): Promise<Record<string, unknown>> {
|
|
637
|
-
const body = {
|
|
638
|
-
port,
|
|
639
|
-
...Object.fromEntries(
|
|
640
|
-
Object.entries(opts).filter(([, v]) => v !== undefined),
|
|
641
|
-
),
|
|
642
|
-
};
|
|
643
|
-
return unwrap(
|
|
644
|
-
await this.http.post<WireEnvelope<Record<string, unknown>>>(
|
|
645
|
-
`/sandboxes/${this.sandbox.id}/previews`,
|
|
646
|
-
body,
|
|
647
|
-
),
|
|
648
|
-
) as Record<string, unknown>;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
async get(previewId: string): Promise<Record<string, unknown>> {
|
|
652
|
-
return unwrap(
|
|
653
|
-
await this.http.get<WireEnvelope<Record<string, unknown>>>(
|
|
654
|
-
`/sandboxes/${this.sandbox.id}/previews/${previewId}`,
|
|
655
|
-
),
|
|
656
|
-
) as Record<string, unknown>;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
async delete(previewId: string): Promise<void> {
|
|
660
|
-
await this.http.delete(
|
|
661
|
-
`/sandboxes/${this.sandbox.id}/previews/${previewId}`,
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/** Mint a share token for previewId. */
|
|
666
|
-
async share(
|
|
667
|
-
previewId: string,
|
|
668
|
-
opts: { ttl_seconds?: number; expires_in_sec?: number } = {},
|
|
669
|
-
): Promise<Record<string, unknown>> {
|
|
670
|
-
return unwrap(
|
|
671
|
-
await this.http.post<WireEnvelope<Record<string, unknown>>>(
|
|
672
|
-
`/sandboxes/${this.sandbox.id}/previews/${previewId}/share`,
|
|
673
|
-
{ ttl_seconds: opts.ttl_seconds ?? opts.expires_in_sec ?? 3600 },
|
|
674
|
-
),
|
|
675
|
-
) as Record<string, unknown>;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/** Invalidate every share token associated with previewId. */
|
|
679
|
-
async revokeShare(previewId: string): Promise<void> {
|
|
680
|
-
await this.http.delete(
|
|
681
|
-
`/sandboxes/${this.sandbox.id}/previews/${previewId}/share`,
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
export class SandboxEnv {
|
|
687
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
688
|
-
|
|
689
|
-
private get http(): HttpClient {
|
|
690
|
-
return (this.sandbox as unknown as { http: HttpClient }).http;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
/** GET /api/v1/sandboxes/{id}/env → list of env vars. */
|
|
694
|
-
async get(): Promise<SandboxEnvVar[]> {
|
|
695
|
-
const response = await this.http.get<unknown>(
|
|
696
|
-
`/sandboxes/${this.sandbox.id}/env`,
|
|
697
|
-
);
|
|
698
|
-
if (Array.isArray(response)) return response as SandboxEnvVar[];
|
|
699
|
-
if (response && typeof response === "object") {
|
|
700
|
-
const r = response as Record<string, unknown>;
|
|
701
|
-
for (const k of ["data", "vars", "env", "items"]) {
|
|
702
|
-
if (Array.isArray(r[k])) return r[k] as SandboxEnvVar[];
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return [];
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/** @deprecated Use get() */
|
|
709
|
-
async list(): Promise<SandboxEnvVar[]> {
|
|
710
|
-
return this.get();
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
/** PUT /api/v1/sandboxes/{id}/env — set (replace) env vars. */
|
|
714
|
-
async set(
|
|
715
|
-
vars: Array<{ key: string; value: string; encrypted?: boolean }>,
|
|
716
|
-
): Promise<SandboxEnvVar[]> {
|
|
717
|
-
const response = await this.http.put<unknown>(
|
|
718
|
-
`/sandboxes/${this.sandbox.id}/env`,
|
|
719
|
-
{ vars },
|
|
720
|
-
);
|
|
721
|
-
if (Array.isArray(response)) return response as SandboxEnvVar[];
|
|
722
|
-
if (response && typeof response === "object") {
|
|
723
|
-
const r = response as Record<string, unknown>;
|
|
724
|
-
for (const k of ["data", "vars", "env", "items"]) {
|
|
725
|
-
if (Array.isArray(r[k])) return r[k] as SandboxEnvVar[];
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
return [];
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
/** DELETE /api/v1/sandboxes/{id}/env/{key} — remove a single env var. */
|
|
732
|
-
async delete(key: string): Promise<void> {
|
|
733
|
-
await this.http.delete<unknown>(
|
|
734
|
-
`/sandboxes/${this.sandbox.id}/env/${encodeURIComponent(key)}`,
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
export class SandboxTags {
|
|
740
|
-
constructor(private readonly sandbox: Sandbox) {}
|
|
741
|
-
|
|
742
|
-
/** Replace the full tag list with tags. */
|
|
743
|
-
async set(tags: string[]): Promise<Record<string, unknown>> {
|
|
744
|
-
return unwrap(
|
|
745
|
-
await (this.sandbox as unknown as { http: HttpClient }).http.patch<
|
|
746
|
-
WireEnvelope<Record<string, unknown>>
|
|
747
|
-
>(`/sandboxes/${this.sandbox.id}/tags`, { tags }),
|
|
748
|
-
) as Record<string, unknown>;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
export class Sandbox {
|
|
753
|
-
data: SandboxData;
|
|
754
|
-
readonly commands: SandboxCommands;
|
|
755
|
-
readonly exec: SandboxExecRunner;
|
|
756
|
-
readonly files: SandboxFiles;
|
|
757
|
-
readonly preview: SandboxPreview;
|
|
758
|
-
readonly artifacts: SandboxArtifacts;
|
|
759
|
-
readonly logs: SandboxLogs;
|
|
760
|
-
readonly snapshots: SandboxSnapshots;
|
|
761
|
-
/** PTY session control — create/delete. */
|
|
762
|
-
readonly terminal: SandboxTerminal;
|
|
763
|
-
/** SSE event stream. */
|
|
764
|
-
readonly events: SandboxEvents;
|
|
765
|
-
/** Preview CRUD + share/revokeShare. */
|
|
766
|
-
readonly previews: SandboxPreviews;
|
|
767
|
-
/** Read-only env var listing. */
|
|
768
|
-
readonly env: SandboxEnv;
|
|
769
|
-
/** Tag replacement. */
|
|
770
|
-
readonly tags: SandboxTags;
|
|
771
|
-
/** Encrypted secrets + OAuth credentials scoped to this sandbox. */
|
|
772
|
-
readonly secrets: SandboxSecrets;
|
|
773
|
-
/** Egress allowlist + policies scoped to this sandbox. */
|
|
774
|
-
readonly network: SandboxNetwork;
|
|
775
|
-
/** Egress audit log + live tail scoped to this sandbox. */
|
|
776
|
-
readonly audit: SandboxAudit;
|
|
777
|
-
|
|
778
|
-
constructor(
|
|
779
|
-
private readonly http: HttpClient,
|
|
780
|
-
data: SandboxData,
|
|
781
|
-
) {
|
|
782
|
-
this.data = data;
|
|
783
|
-
const runExec = (command: string, options?: SandboxExecOptions) =>
|
|
784
|
-
this.runExec(command, options);
|
|
785
|
-
this.exec = Object.assign(runExec, {
|
|
786
|
-
run: runExec,
|
|
787
|
-
stream: (command: string, options?: SandboxExecOptions) =>
|
|
788
|
-
this.execStream(command, options),
|
|
789
|
-
});
|
|
790
|
-
this.commands = new SandboxCommands(this);
|
|
791
|
-
this.files = new SandboxFiles(this);
|
|
792
|
-
this.preview = new SandboxPreview(this);
|
|
793
|
-
this.artifacts = new SandboxArtifacts(this);
|
|
794
|
-
this.logs = new SandboxLogs(this);
|
|
795
|
-
this.snapshots = new SandboxSnapshots(this);
|
|
796
|
-
this.terminal = new SandboxTerminal(this);
|
|
797
|
-
this.events = new SandboxEvents(this);
|
|
798
|
-
this.previews = new SandboxPreviews(this);
|
|
799
|
-
this.env = new SandboxEnv(this);
|
|
800
|
-
this.tags = new SandboxTags(this);
|
|
801
|
-
// Egress (security) namespaces — pre-scoped to this sandbox id.
|
|
802
|
-
const sandboxId = data.id as string;
|
|
803
|
-
this.secrets = new SandboxSecrets(http, sandboxId);
|
|
804
|
-
this.network = new SandboxNetwork(http, sandboxId);
|
|
805
|
-
this.audit = new SandboxAudit(http, sandboxId);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
get id(): SandboxId {
|
|
809
|
-
return this.data.id;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
get state(): SandboxState {
|
|
813
|
-
return this.data.state;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
get ready(): boolean {
|
|
817
|
-
return this.data.ready ?? this.data.state === "running";
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
get templateId(): string {
|
|
821
|
-
return this.data.template_id ?? this.data.image_id ?? "";
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
async refresh(): Promise<Sandbox> {
|
|
825
|
-
this.data = unwrap(
|
|
826
|
-
await this.http.get<WireEnvelope<SandboxData>>(`/sandboxes/${this.id}`),
|
|
827
|
-
);
|
|
828
|
-
return this;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
private async runExec(
|
|
832
|
-
command: string,
|
|
833
|
-
options?: SandboxExecOptions,
|
|
834
|
-
): Promise<SandboxExecResult> {
|
|
835
|
-
this.assertRunning("exec");
|
|
836
|
-
const response = unwrap(
|
|
837
|
-
await this.http.post<WireEnvelope<Record<string, unknown>>>(
|
|
838
|
-
`/sandboxes/${this.id}/exec`,
|
|
839
|
-
execBody(command, options),
|
|
840
|
-
),
|
|
841
|
-
);
|
|
842
|
-
const exitCode = Number(response.exit_code ?? response.exitCode ?? 0);
|
|
843
|
-
const result: SandboxExecResult = {
|
|
844
|
-
stdout: String(response.stdout ?? ""),
|
|
845
|
-
stderr: String(response.stderr ?? ""),
|
|
846
|
-
exitCode,
|
|
847
|
-
exit_code: exitCode,
|
|
848
|
-
};
|
|
849
|
-
if (typeof response.duration_ms === "number") {
|
|
850
|
-
result.durationMs = response.duration_ms;
|
|
851
|
-
result.duration_ms = response.duration_ms;
|
|
852
|
-
}
|
|
853
|
-
return result;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
private execStream(
|
|
857
|
-
command: string,
|
|
858
|
-
options?: SandboxExecOptions,
|
|
859
|
-
): AsyncIterableIterator<SandboxExecEvent> {
|
|
860
|
-
this.assertRunning("exec.stream");
|
|
861
|
-
return this.http.stream<SandboxExecEvent>(
|
|
862
|
-
`/sandboxes/${this.id}/exec/stream`,
|
|
863
|
-
{
|
|
864
|
-
method: "POST",
|
|
865
|
-
body: execBody(command, options),
|
|
866
|
-
},
|
|
867
|
-
);
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
async writeFile(path: string, content: string | Uint8Array): Promise<void> {
|
|
871
|
-
this.assertRunning("writeFile");
|
|
872
|
-
const bytes =
|
|
873
|
-
typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
874
|
-
await this.http.post(`/sandboxes/${this.id}/files`, {
|
|
875
|
-
path,
|
|
876
|
-
content: toBase64(bytes),
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
async download(path: string): Promise<Uint8Array> {
|
|
881
|
-
this.assertRunning("download");
|
|
882
|
-
return this.http.getBinary(
|
|
883
|
-
`/sandboxes/${this.id}/files/${path.replace(/^\/+/, "")}`,
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
async readFile(path: string): Promise<string> {
|
|
888
|
-
return new TextDecoder().decode(await this.download(path));
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
async listFiles(path = "/workspace"): Promise<SandboxFileList> {
|
|
892
|
-
this.assertRunning("files.list");
|
|
893
|
-
const response = unwrap(
|
|
894
|
-
await this.http.get<WireEnvelope<SandboxFileList>>(
|
|
895
|
-
`/sandboxes/${this.id}/files`,
|
|
896
|
-
{ path },
|
|
897
|
-
),
|
|
898
|
-
);
|
|
899
|
-
return response;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
async statFile(path: string): Promise<SandboxFileStat> {
|
|
903
|
-
this.assertRunning("files.stat");
|
|
904
|
-
return unwrap(
|
|
905
|
-
await this.http.post<WireEnvelope<SandboxFileStat>>(
|
|
906
|
-
`/sandboxes/${this.id}/files/stat`,
|
|
907
|
-
{ path },
|
|
908
|
-
),
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
async expose(port?: number): Promise<string> {
|
|
913
|
-
this.assertRunning("expose");
|
|
914
|
-
const response = unwrap(
|
|
915
|
-
await this.http.post<WireEnvelope<Record<string, unknown>>>(
|
|
916
|
-
`/sandboxes/${this.id}/expose`,
|
|
917
|
-
port === undefined ? {} : { port },
|
|
918
|
-
),
|
|
919
|
-
);
|
|
920
|
-
return String(response.url ?? response.preview_url ?? "");
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
async startTemplate(
|
|
924
|
-
options: Record<string, unknown> = {},
|
|
925
|
-
): Promise<Record<string, unknown>> {
|
|
926
|
-
this.assertRunning("startTemplate");
|
|
927
|
-
return unwrap(
|
|
928
|
-
await this.http.post<WireEnvelope<Record<string, unknown>>>(
|
|
929
|
-
`/sandboxes/${this.id}/template/start`,
|
|
930
|
-
options,
|
|
931
|
-
),
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
async getArtifacts(): Promise<Record<string, unknown>> {
|
|
936
|
-
return unwrap(
|
|
937
|
-
await this.http.get<WireEnvelope<Record<string, unknown>>>(
|
|
938
|
-
`/sandboxes/${this.id}/artifacts`,
|
|
939
|
-
),
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
async getLogs(lines?: number): Promise<string | Record<string, unknown>> {
|
|
944
|
-
const response = await this.http.get<WireEnvelope<Record<string, unknown>>>(
|
|
945
|
-
`/sandboxes/${this.id}/logs`,
|
|
946
|
-
{ lines },
|
|
947
|
-
);
|
|
948
|
-
return unwrap(response);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
streamLogs(): AsyncIterableIterator<Record<string, unknown>> {
|
|
952
|
-
return this.http.stream<Record<string, unknown>>(
|
|
953
|
-
`/sandboxes/${this.id}/logs/stream`,
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
async createSnapshot(comment?: string): Promise<SandboxSnapshot> {
|
|
958
|
-
this.assertRunning("snapshots.create");
|
|
959
|
-
return unwrap(
|
|
960
|
-
await this.http.post<WireEnvelope<SandboxSnapshot>>(
|
|
961
|
-
`/sandboxes/${this.id}/snapshots`,
|
|
962
|
-
comment ? { comment } : {},
|
|
963
|
-
),
|
|
964
|
-
);
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
async listSnapshots(): Promise<SandboxSnapshot[]> {
|
|
968
|
-
return unwrap(
|
|
969
|
-
await this.http.get<WireEnvelope<SandboxSnapshot[]>>(
|
|
970
|
-
`/sandboxes/${this.id}/snapshots`,
|
|
971
|
-
),
|
|
972
|
-
);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
async restoreSnapshot(snapshotId: string): Promise<Sandbox> {
|
|
976
|
-
const data = unwrap(
|
|
977
|
-
await this.http.post<WireEnvelope<SandboxData>>(
|
|
978
|
-
`/sandboxes/${this.id}/restore/${snapshotId}`,
|
|
979
|
-
{},
|
|
980
|
-
),
|
|
981
|
-
);
|
|
982
|
-
return new Sandbox(this.http, data);
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
async deleteSnapshot(snapshotId: string): Promise<void> {
|
|
986
|
-
await this.http.delete(`/sandboxes/${this.id}/snapshots/${snapshotId}`);
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
/**
|
|
990
|
-
* Fork (clone) this sandbox into a new sandbox via copy-on-write snapshot.
|
|
991
|
-
* The original sandbox continues running unchanged.
|
|
992
|
-
*/
|
|
993
|
-
async fork(
|
|
994
|
-
opts: { name?: string; metadata?: Record<string, unknown> } = {},
|
|
995
|
-
): Promise<Sandbox> {
|
|
996
|
-
this.assertRunning("fork");
|
|
997
|
-
const body: Record<string, unknown> = {};
|
|
998
|
-
if (opts.name !== undefined) body.name = opts.name;
|
|
999
|
-
if (opts.metadata !== undefined) body.metadata = opts.metadata;
|
|
1000
|
-
const data = unwrap(
|
|
1001
|
-
await this.http.post<WireEnvelope<SandboxData>>(
|
|
1002
|
-
`/sandboxes/${this.id}/fork`,
|
|
1003
|
-
body,
|
|
1004
|
-
),
|
|
1005
|
-
);
|
|
1006
|
-
return new Sandbox(this.http, data);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
/**
|
|
1010
|
-
* PATCH /api/v1/sandboxes/{id} — update mutable sandbox fields.
|
|
1011
|
-
*/
|
|
1012
|
-
async update(params: {
|
|
1013
|
-
name?: string;
|
|
1014
|
-
slug?: string;
|
|
1015
|
-
tags?: string[];
|
|
1016
|
-
metadata?: Record<string, unknown>;
|
|
1017
|
-
always_on?: boolean;
|
|
1018
|
-
timeout_sec?: number;
|
|
1019
|
-
idle_timeout_sec?: number;
|
|
1020
|
-
}): Promise<Sandbox> {
|
|
1021
|
-
const body: Record<string, unknown> = {};
|
|
1022
|
-
for (const [k, v] of Object.entries(params)) {
|
|
1023
|
-
if (v !== undefined) body[k] = v;
|
|
1024
|
-
}
|
|
1025
|
-
const data = unwrap(
|
|
1026
|
-
await this.http.patch<WireEnvelope<SandboxData>>(
|
|
1027
|
-
`/sandboxes/${this.id}`,
|
|
1028
|
-
body,
|
|
1029
|
-
),
|
|
1030
|
-
);
|
|
1031
|
-
this.data = data;
|
|
1032
|
-
return this;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
/**
|
|
1036
|
-
* POST /api/v1/sandboxes/{id}/preview-token → {token, url, expires_at, scope}
|
|
1037
|
-
*/
|
|
1038
|
-
async previewToken(
|
|
1039
|
-
expiresIn = 3600,
|
|
1040
|
-
scope = "read",
|
|
1041
|
-
): Promise<{
|
|
1042
|
-
token: string;
|
|
1043
|
-
url: string;
|
|
1044
|
-
expires_at: string;
|
|
1045
|
-
scope: string;
|
|
1046
|
-
[key: string]: unknown;
|
|
1047
|
-
}> {
|
|
1048
|
-
const raw = await this.http.post<unknown>(
|
|
1049
|
-
`/sandboxes/${this.id}/preview-token`,
|
|
1050
|
-
{ expires_in: expiresIn, scope },
|
|
1051
|
-
);
|
|
1052
|
-
if (raw && typeof raw === "object" && "data" in (raw as object)) {
|
|
1053
|
-
return (
|
|
1054
|
-
raw as {
|
|
1055
|
-
data: {
|
|
1056
|
-
token: string;
|
|
1057
|
-
url: string;
|
|
1058
|
-
expires_at: string;
|
|
1059
|
-
scope: string;
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
).data;
|
|
1063
|
-
}
|
|
1064
|
-
return raw as {
|
|
1065
|
-
token: string;
|
|
1066
|
-
url: string;
|
|
1067
|
-
expires_at: string;
|
|
1068
|
-
scope: string;
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
async pause(): Promise<Sandbox> {
|
|
1073
|
-
const data = unwrap(
|
|
1074
|
-
await this.http.post<WireEnvelope<SandboxData>>(
|
|
1075
|
-
`/sandboxes/${this.id}/pause`,
|
|
1076
|
-
{},
|
|
1077
|
-
),
|
|
1078
|
-
);
|
|
1079
|
-
this.data = data;
|
|
1080
|
-
return this;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
async resume(): Promise<Sandbox> {
|
|
1084
|
-
const data = unwrap(
|
|
1085
|
-
await this.http.post<WireEnvelope<SandboxData>>(
|
|
1086
|
-
`/sandboxes/${this.id}/resume`,
|
|
1087
|
-
{},
|
|
1088
|
-
),
|
|
1089
|
-
);
|
|
1090
|
-
this.data = data;
|
|
1091
|
-
return this;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
async deploy(
|
|
1095
|
-
params: SandboxDeployParams = {},
|
|
1096
|
-
): Promise<Record<string, unknown>> {
|
|
1097
|
-
const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
|
|
1098
|
-
const requestOptions: Parameters<HttpClient["request"]>[1] = {
|
|
1099
|
-
method: "POST",
|
|
1100
|
-
body: stripUndefined({
|
|
1101
|
-
name: params.name,
|
|
1102
|
-
deployment_id: params.deploymentId ?? params.deployment_id,
|
|
1103
|
-
output_path:
|
|
1104
|
-
params.outputPath ??
|
|
1105
|
-
params.output_path ??
|
|
1106
|
-
params.path ??
|
|
1107
|
-
params.sourcePath ??
|
|
1108
|
-
params.source_path,
|
|
1109
|
-
source_snapshot_path:
|
|
1110
|
-
params.sourceSnapshotPath ?? params.source_snapshot_path,
|
|
1111
|
-
entrypoint: params.entrypoint,
|
|
1112
|
-
build_command: params.buildCommand ?? params.build_command,
|
|
1113
|
-
run_command: params.runCommand ?? params.run_command,
|
|
1114
|
-
start_command: params.startCommand ?? params.start_command,
|
|
1115
|
-
port: params.port,
|
|
1116
|
-
health_check_path: params.healthCheckPath ?? params.health_check_path,
|
|
1117
|
-
deployment_type: params.deploymentType ?? params.deployment_type,
|
|
1118
|
-
docker_deploy_template_id:
|
|
1119
|
-
params.dockerDeployTemplateId ?? params.docker_deploy_template_id,
|
|
1120
|
-
type: params.type,
|
|
1121
|
-
mode: params.mode,
|
|
1122
|
-
database: params.database,
|
|
1123
|
-
resources: params.resources,
|
|
1124
|
-
domain: params.domain,
|
|
1125
|
-
custom_domain: params.customDomain ?? params.custom_domain,
|
|
1126
|
-
}),
|
|
1127
|
-
};
|
|
1128
|
-
if (idempotencyKey) {
|
|
1129
|
-
requestOptions.headers = { "Idempotency-Key": idempotencyKey };
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
return unwrap(
|
|
1133
|
-
await this.http.request<WireEnvelope<Record<string, unknown>>>(
|
|
1134
|
-
`/sandboxes/${this.id}/deploy`,
|
|
1135
|
-
requestOptions,
|
|
1136
|
-
),
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
async deployDocker(
|
|
1141
|
-
params: SandboxDeployParams = {},
|
|
1142
|
-
): Promise<Record<string, unknown>> {
|
|
1143
|
-
return this.deploy({ ...params, deploymentType: "docker_deploy" });
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
/** Check readiness of the sandbox (GET /sandboxes/:id/readiness). */
|
|
1147
|
-
async readiness(): Promise<Record<string, unknown>> {
|
|
1148
|
-
return unwrap(
|
|
1149
|
-
await this.http.get<WireEnvelope<Record<string, unknown>>>(
|
|
1150
|
-
`/sandboxes/${this.id}/readiness`,
|
|
1151
|
-
),
|
|
1152
|
-
) as Record<string, unknown>;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
/**
|
|
1156
|
-
* Block until the sandbox reports ready, or *timeout* seconds elapse.
|
|
1157
|
-
*
|
|
1158
|
-
* When `stream` is `true` (the default) this opens an SSE connection
|
|
1159
|
-
* to `GET /sandboxes/:id/readiness/stream` and waits for an
|
|
1160
|
-
* `event: ready` frame. The server emits `ready` immediately if the
|
|
1161
|
-
* sandbox is already ready, otherwise as soon as the readiness PubSub
|
|
1162
|
-
* message fires.
|
|
1163
|
-
*
|
|
1164
|
-
* Returns `true` once the sandbox is ready, `false` on `event: timeout`
|
|
1165
|
-
* or when the local timeout elapses before ready.
|
|
1166
|
-
*
|
|
1167
|
-
* If the SSE endpoint returns 404 (server pre-dates the streaming
|
|
1168
|
-
* endpoint) this transparently falls back to polling
|
|
1169
|
-
* {@link readiness} every 10 ms until ready or timeout.
|
|
1170
|
-
*/
|
|
1171
|
-
async waitUntilReady(
|
|
1172
|
-
options: { timeout?: number; stream?: boolean } = {},
|
|
1173
|
-
): Promise<boolean> {
|
|
1174
|
-
const timeout = options.timeout ?? 30;
|
|
1175
|
-
const stream = options.stream ?? true;
|
|
1176
|
-
|
|
1177
|
-
if (stream) {
|
|
1178
|
-
const sseResult = await this.tryReadinessStream(timeout);
|
|
1179
|
-
if (sseResult !== null) return sseResult;
|
|
1180
|
-
// null === fall through to polling fallback (404 or transport error)
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
// Polling fallback — fixed 10 ms tick, no exponential backoff.
|
|
1184
|
-
const deadlineMs = Date.now() + timeout * 1000;
|
|
1185
|
-
while (Date.now() < deadlineMs) {
|
|
1186
|
-
try {
|
|
1187
|
-
const data = await this.readiness();
|
|
1188
|
-
if (data.ready === true || data.status === "ready") return true;
|
|
1189
|
-
} catch {
|
|
1190
|
-
// swallow transient errors and keep polling
|
|
1191
|
-
}
|
|
1192
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1193
|
-
}
|
|
1194
|
-
return false;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
/**
|
|
1198
|
-
* Returns `true` / `false` for terminal SSE events, or `null` if the
|
|
1199
|
-
* stream endpoint is unavailable (404 or transport error) so callers
|
|
1200
|
-
* can fall back to polling.
|
|
1201
|
-
*/
|
|
1202
|
-
private async tryReadinessStream(
|
|
1203
|
-
timeoutSec: number,
|
|
1204
|
-
): Promise<boolean | null> {
|
|
1205
|
-
const abort = new AbortController();
|
|
1206
|
-
const timer = setTimeout(() => abort.abort(), (timeoutSec + 5) * 1000);
|
|
1207
|
-
try {
|
|
1208
|
-
const headers: Record<string, string> = {
|
|
1209
|
-
Authorization: `Bearer ${this.http.apiKey}`,
|
|
1210
|
-
Accept: "text/event-stream",
|
|
1211
|
-
"User-Agent": "@miosa/sdk/1.0.0",
|
|
1212
|
-
};
|
|
1213
|
-
const response = await fetch(
|
|
1214
|
-
`${this.http.baseUrl}/sandboxes/${this.id}/readiness/stream`,
|
|
1215
|
-
{ method: "GET", headers, signal: abort.signal },
|
|
1216
|
-
);
|
|
1217
|
-
if (response.status === 404) return null;
|
|
1218
|
-
if (!response.ok || !response.body) return null;
|
|
1219
|
-
|
|
1220
|
-
const reader = response.body.getReader();
|
|
1221
|
-
const decoder = new TextDecoder();
|
|
1222
|
-
let buffer = "";
|
|
1223
|
-
try {
|
|
1224
|
-
while (true) {
|
|
1225
|
-
const { done, value } = await reader.read();
|
|
1226
|
-
if (done) break;
|
|
1227
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1228
|
-
let newlineIdx: number;
|
|
1229
|
-
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
1230
|
-
const line = buffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
1231
|
-
buffer = buffer.slice(newlineIdx + 1);
|
|
1232
|
-
if (line.startsWith("event:")) {
|
|
1233
|
-
const evt = line.slice(6).trim();
|
|
1234
|
-
if (evt === "ready") return true;
|
|
1235
|
-
if (evt === "timeout") return false;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
} finally {
|
|
1240
|
-
try {
|
|
1241
|
-
reader.releaseLock();
|
|
1242
|
-
} catch {
|
|
1243
|
-
// already released
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
// stream closed without a terminal event — let caller decide
|
|
1247
|
-
return null;
|
|
1248
|
-
} catch {
|
|
1249
|
-
return null;
|
|
1250
|
-
} finally {
|
|
1251
|
-
clearTimeout(timer);
|
|
1252
|
-
try {
|
|
1253
|
-
abort.abort();
|
|
1254
|
-
} catch {
|
|
1255
|
-
// signal already aborted
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
async destroy(): Promise<void> {
|
|
1261
|
-
if (this.data.state === "destroyed") return;
|
|
1262
|
-
await this.http.delete(`/sandboxes/${this.id}`);
|
|
1263
|
-
this.data = {
|
|
1264
|
-
...this.data,
|
|
1265
|
-
state: "destroyed",
|
|
1266
|
-
destroyed_at: new Date().toISOString(),
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
delete(): Promise<void> {
|
|
1271
|
-
return this.destroy();
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
private assertRunning(operation: string): void {
|
|
1275
|
-
if (this.data.state === "destroyed") {
|
|
1276
|
-
throw new MiosaError(
|
|
1277
|
-
`Sandbox ${this.id} has been destroyed`,
|
|
1278
|
-
400,
|
|
1279
|
-
"SANDBOX_DESTROYED",
|
|
1280
|
-
);
|
|
1281
|
-
}
|
|
1282
|
-
if (this.data.state !== "running") {
|
|
1283
|
-
throw new MiosaError(
|
|
1284
|
-
`Cannot ${operation} on sandbox ${this.id}: state is ${this.data.state}, expected running`,
|
|
1285
|
-
409,
|
|
1286
|
-
"SANDBOX_NOT_RUNNING",
|
|
1287
|
-
);
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
export class Sandboxes {
|
|
1293
|
-
constructor(private readonly http: HttpClient) {}
|
|
1294
|
-
|
|
1295
|
-
async create(params: SandboxCreateParams = {}): Promise<Sandbox> {
|
|
1296
|
-
const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
|
|
1297
|
-
const requestOptions: Parameters<HttpClient["request"]>[1] = {
|
|
1298
|
-
method: "POST",
|
|
1299
|
-
body: createBody(params),
|
|
1300
|
-
};
|
|
1301
|
-
if (idempotencyKey) {
|
|
1302
|
-
requestOptions.headers = { "Idempotency-Key": idempotencyKey };
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
const data = unwrap(
|
|
1306
|
-
await this.http.request<WireEnvelope<SandboxData>>(
|
|
1307
|
-
"/sandboxes",
|
|
1308
|
-
requestOptions,
|
|
1309
|
-
),
|
|
1310
|
-
);
|
|
1311
|
-
return new Sandbox(this.http, data);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
async list(params: SandboxListParams = {}): Promise<Sandbox[]> {
|
|
1315
|
-
const data = await this.http.get<
|
|
1316
|
-
| WireEnvelope<SandboxData[]>
|
|
1317
|
-
| { sandboxes: SandboxData[] }
|
|
1318
|
-
| { items: SandboxData[] }
|
|
1319
|
-
>("/sandboxes", {
|
|
1320
|
-
state: params.state,
|
|
1321
|
-
tags: params.tags?.join(","),
|
|
1322
|
-
external_workspace_id:
|
|
1323
|
-
params.externalWorkspaceId ?? params.external_workspace_id,
|
|
1324
|
-
external_user_id: params.externalUserId ?? params.external_user_id,
|
|
1325
|
-
external_project_id:
|
|
1326
|
-
params.externalProjectId ?? params.external_project_id,
|
|
1327
|
-
});
|
|
1328
|
-
return listItems(data).map((item) => new Sandbox(this.http, item));
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
async get(id: SandboxId | string): Promise<Sandbox> {
|
|
1332
|
-
const data = unwrap(
|
|
1333
|
-
await this.http.get<WireEnvelope<SandboxData>>(`/sandboxes/${id}`),
|
|
1334
|
-
);
|
|
1335
|
-
return new Sandbox(this.http, data);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
connect(id: SandboxId | string): Promise<Sandbox> {
|
|
1339
|
-
return this.get(id);
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
async delete(id: SandboxId | string): Promise<void> {
|
|
1343
|
-
await this.http.delete(`/sandboxes/${id}`);
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
async listTemplates(
|
|
1347
|
-
options: { includeAliases?: boolean } = {},
|
|
1348
|
-
): Promise<SandboxTemplateList> {
|
|
1349
|
-
return this.http.get<SandboxTemplateList>("/sandbox-templates", {
|
|
1350
|
-
include_aliases: options.includeAliases,
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
async getTemplate(id: string): Promise<SandboxTemplate> {
|
|
1355
|
-
return this.http.get<SandboxTemplate>(`/sandbox-templates/${id}`);
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
async getBuildSpecSchema(): Promise<Record<string, unknown>> {
|
|
1359
|
-
return this.http.get<Record<string, unknown>>(
|
|
1360
|
-
"/sandbox-templates/build-spec",
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
async validateBuildSpec(
|
|
1365
|
-
buildSpec: SandboxBuildSpec,
|
|
1366
|
-
): Promise<SandboxBuildSpecValidation> {
|
|
1367
|
-
return this.http.post<SandboxBuildSpecValidation>(
|
|
1368
|
-
"/sandbox-templates/validate",
|
|
1369
|
-
{
|
|
1370
|
-
build_spec: buildSpec,
|
|
1371
|
-
},
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
async createTemplate(
|
|
1376
|
-
params: SandboxTemplateCreateParams,
|
|
1377
|
-
): Promise<SandboxTemplate> {
|
|
1378
|
-
const response = await this.http.post<WireEnvelope<SandboxTemplate>>(
|
|
1379
|
-
"/sandbox-templates",
|
|
1380
|
-
stripUndefined({
|
|
1381
|
-
name: params.name,
|
|
1382
|
-
slug: params.slug,
|
|
1383
|
-
description: params.description,
|
|
1384
|
-
build_spec: params.buildSpec ?? params.build_spec,
|
|
1385
|
-
metadata: params.metadata,
|
|
1386
|
-
}),
|
|
1387
|
-
);
|
|
1388
|
-
return unwrap(response);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
async createTemplateBuild(
|
|
1392
|
-
templateId: string,
|
|
1393
|
-
params: SandboxTemplateBuildCreateParams = {},
|
|
1394
|
-
): Promise<SandboxTemplateBuild> {
|
|
1395
|
-
const response = await this.http.post<WireEnvelope<SandboxTemplateBuild>>(
|
|
1396
|
-
`/sandbox-templates/${templateId}/builds`,
|
|
1397
|
-
stripUndefined({
|
|
1398
|
-
build_spec: params.buildSpec ?? params.build_spec,
|
|
1399
|
-
metadata: params.metadata,
|
|
1400
|
-
}),
|
|
1401
|
-
);
|
|
1402
|
-
return unwrap(response);
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
async listTemplateBuilds(
|
|
1406
|
-
templateId: string,
|
|
1407
|
-
): Promise<SandboxTemplateBuild[]> {
|
|
1408
|
-
const response = await this.http.get<WireEnvelope<SandboxTemplateBuild[]>>(
|
|
1409
|
-
`/sandbox-templates/${templateId}/builds`,
|
|
1410
|
-
);
|
|
1411
|
-
return unwrap(response);
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
async getTemplateBuild(buildId: string): Promise<SandboxTemplateBuild> {
|
|
1415
|
-
const response = await this.http.get<WireEnvelope<SandboxTemplateBuild>>(
|
|
1416
|
-
`/sandbox-template-builds/${buildId}`,
|
|
1417
|
-
);
|
|
1418
|
-
return unwrap(response);
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
function toBase64(bytes: Uint8Array): string {
|
|
1423
|
-
const maybeBuffer = (
|
|
1424
|
-
globalThis as {
|
|
1425
|
-
Buffer?: {
|
|
1426
|
-
from(data: Uint8Array): { toString(encoding: string): string };
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
).Buffer;
|
|
1430
|
-
if (maybeBuffer) return maybeBuffer.from(bytes).toString("base64");
|
|
1431
|
-
|
|
1432
|
-
let binary = "";
|
|
1433
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
1434
|
-
binary += String.fromCharCode(bytes[index]!);
|
|
1435
|
-
}
|
|
1436
|
-
return btoa(binary);
|
|
1437
|
-
}
|