@miosa/sdk 0.3.0
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 +181 -0
- package/dist/index.d.ts +4689 -0
- package/dist/index.js +6045 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/client.ts +249 -0
- package/src/errors.ts +136 -0
- package/src/http.test.ts +374 -0
- package/src/http.ts +390 -0
- package/src/index.ts +452 -0
- package/src/resources/admin.ts +348 -0
- package/src/resources/analytics.ts +60 -0
- package/src/resources/api-keys.ts +119 -0
- package/src/resources/audit-log.ts +64 -0
- package/src/resources/benchmarks.ts +104 -0
- package/src/resources/builder-sessions.ts +75 -0
- package/src/resources/channels.ts +143 -0
- package/src/resources/checkpoints.ts +225 -0
- package/src/resources/command-center.ts +73 -0
- package/src/resources/community.ts +103 -0
- package/src/resources/completions.ts +104 -0
- package/src/resources/computer-auto-stop.ts +43 -0
- package/src/resources/computer-env.ts +76 -0
- package/src/resources/computer-logs.ts +50 -0
- package/src/resources/computer-osa.ts +76 -0
- package/src/resources/computer-ports.ts +91 -0
- package/src/resources/computer-terminal.ts +61 -0
- package/src/resources/computer-volumes.ts +64 -0
- package/src/resources/computer.ts +530 -0
- package/src/resources/computers.ts +75 -0
- package/src/resources/credits.ts +43 -0
- package/src/resources/cron-jobs.ts +191 -0
- package/src/resources/custom_domains.ts +123 -0
- package/src/resources/dashboard.ts +49 -0
- package/src/resources/databases.ts +218 -0
- package/src/resources/deployments.ts +777 -0
- package/src/resources/desktop.ts +134 -0
- package/src/resources/email.ts +212 -0
- package/src/resources/embeddings.ts +36 -0
- package/src/resources/events.ts +296 -0
- package/src/resources/exec.ts +319 -0
- package/src/resources/external-keys.ts +79 -0
- package/src/resources/files.test.ts +339 -0
- package/src/resources/files.ts +220 -0
- package/src/resources/flat-custom-domains.ts +127 -0
- package/src/resources/functions.ts +178 -0
- package/src/resources/health-checks.ts +165 -0
- package/src/resources/integrations.ts +183 -0
- package/src/resources/mcp.ts +70 -0
- package/src/resources/models.ts +45 -0
- package/src/resources/network_policy.ts +73 -0
- package/src/resources/open-computers/agents.ts +91 -0
- package/src/resources/open-computers/apps.ts +102 -0
- package/src/resources/open-computers/clusters.ts +88 -0
- package/src/resources/open-computers/desktop.ts +34 -0
- package/src/resources/open-computers/files.ts +97 -0
- package/src/resources/open-computers/hosts.ts +85 -0
- package/src/resources/open-computers/index.ts +98 -0
- package/src/resources/open-computers/jobs.ts +75 -0
- package/src/resources/open-computers/open_computers.test.ts +288 -0
- package/src/resources/open-computers/secrets.ts +115 -0
- package/src/resources/open-computers/terminal.ts +33 -0
- package/src/resources/open-computers/tunnels.ts +87 -0
- package/src/resources/open-computers/types.ts +343 -0
- package/src/resources/open-computers/workspaces.ts +135 -0
- package/src/resources/project-auth.ts +142 -0
- package/src/resources/project-integrations.ts +133 -0
- package/src/resources/provider-defaults.ts +89 -0
- package/src/resources/regions.ts +94 -0
- package/src/resources/sandbox-templates.ts +195 -0
- package/src/resources/sandboxes.live.test.ts +92 -0
- package/src/resources/sandboxes.test.ts +624 -0
- package/src/resources/sandboxes.ts +1173 -0
- package/src/resources/settings.ts +143 -0
- package/src/resources/snapshots-standalone.ts +51 -0
- package/src/resources/storage.ts +221 -0
- package/src/resources/tenant.ts +39 -0
- package/src/resources/usage.ts +85 -0
- package/src/resources/volumes.ts +117 -0
- package/src/resources/webhooks.ts +171 -0
- package/src/types.ts +460 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import { HttpClient } from "../http.js";
|
|
2
|
+
import type { ComputerData, ComputerId } from "../types.js";
|
|
3
|
+
import { Checkpoints } from "./checkpoints.js";
|
|
4
|
+
import { ComputerAutoStop } from "./computer-auto-stop.js";
|
|
5
|
+
import { ComputerEnv } from "./computer-env.js";
|
|
6
|
+
import { ComputerLogs } from "./computer-logs.js";
|
|
7
|
+
import { ComputerOsa } from "./computer-osa.js";
|
|
8
|
+
import { ComputerPorts } from "./computer-ports.js";
|
|
9
|
+
import { ComputerTerminal } from "./computer-terminal.js";
|
|
10
|
+
import { ComputerVolumes } from "./computer-volumes.js";
|
|
11
|
+
import { CustomDomains } from "./custom_domains.js";
|
|
12
|
+
import { Desktop } from "./desktop.js";
|
|
13
|
+
import { Events } from "./events.js";
|
|
14
|
+
import { Exec } from "./exec.js";
|
|
15
|
+
import { Files } from "./files.js";
|
|
16
|
+
import { NetworkPolicy } from "./network_policy.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per-computer inbox config (GET/PATCH /computers/:id/inbox).
|
|
20
|
+
* Defined inline because it has no separate file (small surface).
|
|
21
|
+
*/
|
|
22
|
+
export class ComputerInbox {
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly http: HttpClient,
|
|
25
|
+
private readonly computerId: string,
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
async get(): Promise<Record<string, unknown>> {
|
|
29
|
+
const raw = await this.http.get<unknown>(
|
|
30
|
+
`/computers/${this.computerId}/inbox`,
|
|
31
|
+
);
|
|
32
|
+
return unwrapData(raw);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async update(
|
|
36
|
+
fields: Record<string, unknown>,
|
|
37
|
+
): Promise<Record<string, unknown>> {
|
|
38
|
+
const body = Object.fromEntries(
|
|
39
|
+
Object.entries(fields).filter(([, v]) => v !== undefined),
|
|
40
|
+
);
|
|
41
|
+
const raw = await this.http.patch<unknown>(
|
|
42
|
+
`/computers/${this.computerId}/inbox`,
|
|
43
|
+
body,
|
|
44
|
+
);
|
|
45
|
+
return unwrapData(raw);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function unwrapData(data: unknown): Record<string, unknown> {
|
|
50
|
+
if (data && typeof data === "object") {
|
|
51
|
+
const d = data as Record<string, unknown>;
|
|
52
|
+
if ("data" in d && Object.keys(d).length <= 2) {
|
|
53
|
+
return d.data as Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return (data ?? {}) as Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A Computer instance bound to a specific computer ID.
|
|
61
|
+
*
|
|
62
|
+
* Returned by `miosa.computers.create()`, `miosa.computers.get()`, and
|
|
63
|
+
* `miosa.computers.list()`. Exposes all per-computer actions as methods and
|
|
64
|
+
* sub-resources as properties.
|
|
65
|
+
*/
|
|
66
|
+
export class Computer {
|
|
67
|
+
/** Raw data from the API. Refresh with `await computer.reload()`. */
|
|
68
|
+
data: ComputerData;
|
|
69
|
+
|
|
70
|
+
/** Desktop control — screenshot, click, type, drag, etc. */
|
|
71
|
+
readonly desktop: Desktop;
|
|
72
|
+
|
|
73
|
+
/** Execute shell and Python commands. */
|
|
74
|
+
readonly exec: Exec;
|
|
75
|
+
|
|
76
|
+
/** File operations — upload, download, list, delete. */
|
|
77
|
+
readonly files: Files;
|
|
78
|
+
|
|
79
|
+
/** Firecracker checkpoint (snapshot) management. */
|
|
80
|
+
readonly checkpoints: Checkpoints;
|
|
81
|
+
|
|
82
|
+
/** Egress network policy — allow/deny rules applied at the host TAP level. */
|
|
83
|
+
readonly networkPolicy: NetworkPolicy;
|
|
84
|
+
|
|
85
|
+
/** Real-time event subscription (window, clipboard, file, process, idle). */
|
|
86
|
+
readonly events: Events;
|
|
87
|
+
|
|
88
|
+
/** Custom domain management — map your own FQDNs to this sandbox. */
|
|
89
|
+
readonly domains: CustomDomains;
|
|
90
|
+
|
|
91
|
+
/** PTY session management — create/resize. */
|
|
92
|
+
readonly terminal: ComputerTerminal;
|
|
93
|
+
|
|
94
|
+
/** In-VM OSA agent dispatch — submitTask, cancelTask, status, configure. */
|
|
95
|
+
readonly osa: ComputerOsa;
|
|
96
|
+
|
|
97
|
+
/** Idle auto-stop config — get/update. */
|
|
98
|
+
readonly autoStop: ComputerAutoStop;
|
|
99
|
+
|
|
100
|
+
/** Per-computer inbox config — get/update. */
|
|
101
|
+
readonly inbox: ComputerInbox;
|
|
102
|
+
|
|
103
|
+
/** Encrypted env var CRUD — list, set, update, delete, bulkSet. */
|
|
104
|
+
readonly env: ComputerEnv;
|
|
105
|
+
|
|
106
|
+
/** VM log read + SSE stream. */
|
|
107
|
+
readonly logs: ComputerLogs;
|
|
108
|
+
|
|
109
|
+
/** Per-port visibility control — list, get, create, update, delete. */
|
|
110
|
+
readonly ports: ComputerPorts;
|
|
111
|
+
|
|
112
|
+
/** Volume attachment — list, attach, detach. */
|
|
113
|
+
readonly volumes: ComputerVolumes;
|
|
114
|
+
|
|
115
|
+
private readonly http: HttpClient;
|
|
116
|
+
|
|
117
|
+
constructor(http: HttpClient, data: ComputerData) {
|
|
118
|
+
this.http = http;
|
|
119
|
+
this.data = data;
|
|
120
|
+
|
|
121
|
+
const id = data.id as string;
|
|
122
|
+
this.desktop = new Desktop(http, id);
|
|
123
|
+
this.exec = new Exec(http, id);
|
|
124
|
+
this.files = new Files(http, id);
|
|
125
|
+
this.checkpoints = new Checkpoints(http, id);
|
|
126
|
+
this.networkPolicy = new NetworkPolicy(http, id);
|
|
127
|
+
this.events = new Events(http.baseUrl, http.apiKey, id);
|
|
128
|
+
this.domains = new CustomDomains(http, id);
|
|
129
|
+
this.terminal = new ComputerTerminal(http, id);
|
|
130
|
+
this.osa = new ComputerOsa(http, id);
|
|
131
|
+
this.autoStop = new ComputerAutoStop(http, id);
|
|
132
|
+
this.inbox = new ComputerInbox(http, id);
|
|
133
|
+
this.env = new ComputerEnv(http, id);
|
|
134
|
+
this.logs = new ComputerLogs(http, id);
|
|
135
|
+
this.ports = new ComputerPorts(http, id);
|
|
136
|
+
this.volumes = new ComputerVolumes(http, id);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get id(): ComputerId {
|
|
140
|
+
return this.data.id;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get name(): string {
|
|
144
|
+
return this.data.name;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
get status(): ComputerData["status"] {
|
|
148
|
+
return this.data.status;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** URL-safe slug; falls back to the raw id when unset. */
|
|
152
|
+
get slug(): string {
|
|
153
|
+
return this.data.slug || (this.data.id as string);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Public HTTPS URL that forwards to a given port inside the VM.
|
|
158
|
+
*
|
|
159
|
+
* Example — expose a dev server running on port 3000:
|
|
160
|
+
* ```ts
|
|
161
|
+
* await computer.exec.bash("npm run dev &");
|
|
162
|
+
* const url = computer.previewUrl(3000);
|
|
163
|
+
* // => https://3000-<slug>.sandbox.miosa.ai
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* Works for any TCP HTTP listener. Public (no auth required); anyone with
|
|
167
|
+
* the URL can see it. Served over the ingress proxy so it inherits the
|
|
168
|
+
* wildcard TLS cert — no per-sandbox certs to manage.
|
|
169
|
+
*/
|
|
170
|
+
previewUrl(port: number, path: string = "/"): string {
|
|
171
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
172
|
+
return `https://${port}-${this.slug}.sandbox.miosa.ai${p}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Root preview URL — serves whatever is on the default app port. */
|
|
176
|
+
get publicUrl(): string {
|
|
177
|
+
return `https://${this.slug}.sandbox.miosa.ai`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── Lifecycle ─────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/** Start the computer. */
|
|
183
|
+
async start(): Promise<Computer> {
|
|
184
|
+
const updated = await this.http.post<ComputerData>(
|
|
185
|
+
`/computers/${this.id}/start`,
|
|
186
|
+
);
|
|
187
|
+
this.data = updated;
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Stop the computer. */
|
|
192
|
+
async stop(): Promise<Computer> {
|
|
193
|
+
const updated = await this.http.post<ComputerData>(
|
|
194
|
+
`/computers/${this.id}/stop`,
|
|
195
|
+
);
|
|
196
|
+
this.data = updated;
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Restart the computer. */
|
|
201
|
+
async restart(): Promise<Computer> {
|
|
202
|
+
const updated = await this.http.post<ComputerData>(
|
|
203
|
+
`/computers/${this.id}/restart`,
|
|
204
|
+
);
|
|
205
|
+
this.data = updated;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Permanently destroy the computer. After this call the instance is invalid. */
|
|
210
|
+
async destroy(): Promise<void> {
|
|
211
|
+
return this.http.delete<void>(`/computers/${this.id}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Reload metadata from the API and update `this.data`. */
|
|
215
|
+
async reload(): Promise<Computer> {
|
|
216
|
+
this.data = await this.http.get<ComputerData>(`/computers/${this.id}`);
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── Desktop shortcuts ─────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Capture a desktop screenshot as PNG bytes.
|
|
224
|
+
* Shortcut for `computer.desktop.screenshot()`.
|
|
225
|
+
*/
|
|
226
|
+
async screenshot(): Promise<Uint8Array> {
|
|
227
|
+
return this.desktop.screenshot();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Capture a desktop screenshot encoded as a base64 string.
|
|
232
|
+
* Convenience for AI agents that pass screenshots to LLMs.
|
|
233
|
+
*/
|
|
234
|
+
async screenshotBase64(): Promise<string> {
|
|
235
|
+
const bytes = await this.desktop.screenshot();
|
|
236
|
+
// Browser-safe base64 — Buffer isn't always available.
|
|
237
|
+
let binary = "";
|
|
238
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
239
|
+
binary += String.fromCharCode(bytes[i]!);
|
|
240
|
+
}
|
|
241
|
+
return typeof btoa === "function"
|
|
242
|
+
? btoa(binary)
|
|
243
|
+
: Buffer.from(bytes).toString("base64");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Click at the given coordinates (default left button).
|
|
248
|
+
* Shortcut for `computer.desktop.click(x, y)`.
|
|
249
|
+
*/
|
|
250
|
+
async click(x: number, y: number): Promise<void> {
|
|
251
|
+
await this.desktop.click(x, y);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Explicit left-button click. Alias for `click(x, y)`. */
|
|
255
|
+
async leftClick(x: number, y: number): Promise<void> {
|
|
256
|
+
await this.desktop.click(x, y, "left");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Right-button click. */
|
|
260
|
+
async rightClick(x: number, y: number): Promise<void> {
|
|
261
|
+
await this.desktop.click(x, y, "right");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Double-click at the given coordinates. */
|
|
265
|
+
async doubleClick(x: number, y: number): Promise<void> {
|
|
266
|
+
await this.desktop.doubleClick(x, y);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Type text into the focused element.
|
|
271
|
+
* Shortcut for `computer.desktop.type(text)`.
|
|
272
|
+
*/
|
|
273
|
+
async type(text: string): Promise<void> {
|
|
274
|
+
await this.desktop.type(text);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Send a key or key combo.
|
|
279
|
+
* Shortcut for `computer.desktop.key(key)`.
|
|
280
|
+
*/
|
|
281
|
+
async key(key: string): Promise<void> {
|
|
282
|
+
await this.desktop.key(key);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Scroll in a direction.
|
|
287
|
+
* Shortcut for `computer.desktop.scroll(direction, clicks)`.
|
|
288
|
+
*/
|
|
289
|
+
async scroll(
|
|
290
|
+
direction: Parameters<Desktop["scroll"]>[0],
|
|
291
|
+
clicks = 3,
|
|
292
|
+
): Promise<void> {
|
|
293
|
+
await this.desktop.scroll(direction, clicks);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Click and drag between two points.
|
|
298
|
+
* Shortcut for `computer.desktop.drag(fromX, fromY, toX, toY)`.
|
|
299
|
+
*/
|
|
300
|
+
async drag(
|
|
301
|
+
fromX: number,
|
|
302
|
+
fromY: number,
|
|
303
|
+
toX: number,
|
|
304
|
+
toY: number,
|
|
305
|
+
): Promise<void> {
|
|
306
|
+
await this.desktop.drag(fromX, fromY, toX, toY);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Wait for N seconds inside the desktop environment.
|
|
311
|
+
* Shortcut for `computer.desktop.wait(seconds)`.
|
|
312
|
+
*/
|
|
313
|
+
async wait(seconds: number): Promise<void> {
|
|
314
|
+
await this.desktop.wait(seconds);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ─── Exec shortcuts ────────────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Run a shell command.
|
|
321
|
+
* Shortcut for `computer.exec.bash(command)`.
|
|
322
|
+
*/
|
|
323
|
+
async bash(command: string, timeout?: number) {
|
|
324
|
+
return this.exec.bash(command, timeout);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Run Python code.
|
|
329
|
+
* Shortcut for `computer.exec.python(code)`.
|
|
330
|
+
*/
|
|
331
|
+
async python(code: string, timeout?: number) {
|
|
332
|
+
return this.exec.python(code, timeout);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ─── Window / cursor / app shortcuts ───────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
/** List open windows on the desktop. */
|
|
338
|
+
async windows(): Promise<Awaited<ReturnType<Desktop["windows"]>>> {
|
|
339
|
+
return this.desktop.windows();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Get the current cursor position. */
|
|
343
|
+
async cursor(): Promise<Awaited<ReturnType<Desktop["cursor"]>>> {
|
|
344
|
+
return this.desktop.cursor();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Focus a specific window by ID. */
|
|
348
|
+
async focusWindow(windowId: string): Promise<void> {
|
|
349
|
+
await this.desktop.focusWindow(windowId);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Launch an installed app inside the VM. */
|
|
353
|
+
async launch(appName: string): Promise<void> {
|
|
354
|
+
await this.desktop.launch(appName);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ─── Scoped filesystem helper ──────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Return a `ScopedFs` bound to `workingDir`. All path arguments to
|
|
361
|
+
* `readFile`, `writeFile`, and `readdir` are resolved relative to that
|
|
362
|
+
* directory — no need to repeat the prefix on every call.
|
|
363
|
+
*
|
|
364
|
+
* ```ts
|
|
365
|
+
* const fs = computer.fs("/workspace");
|
|
366
|
+
* await fs.writeFile("main.py", "print(1)");
|
|
367
|
+
* const src = await fs.readFile("main.py");
|
|
368
|
+
* const entries = await fs.readdir(".");
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
fs(workingDir: string): ScopedFs {
|
|
372
|
+
return new ScopedFs(this.files, workingDir);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ─── Direct methods ────────────────────────────────────────────────────────
|
|
376
|
+
|
|
377
|
+
/** Get VNC credentials for this computer. */
|
|
378
|
+
async vncCredentials(): Promise<Record<string, unknown>> {
|
|
379
|
+
return unwrapData(
|
|
380
|
+
await this.http.get<unknown>(`/computers/${this.id}/vnc-credentials`),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** List installed apps inside the VM. */
|
|
385
|
+
async apps(): Promise<Record<string, unknown>> {
|
|
386
|
+
return unwrapData(
|
|
387
|
+
await this.http.get<unknown>(`/computers/${this.id}/apps`),
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Get URL map for this computer (VNC, preview, etc.). */
|
|
392
|
+
async urls(): Promise<Record<string, unknown>> {
|
|
393
|
+
return unwrapData(
|
|
394
|
+
await this.http.get<unknown>(`/computers/${this.id}/urls`),
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Mint a short-lived stream token for this computer. */
|
|
399
|
+
async streamToken(): Promise<Record<string, unknown>> {
|
|
400
|
+
return unwrapData(
|
|
401
|
+
await this.http.post<unknown>(`/computers/${this.id}/stream-token`),
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Clone this computer into a new one. */
|
|
406
|
+
async clone(opts: Record<string, unknown> = {}): Promise<Computer> {
|
|
407
|
+
const body = Object.fromEntries(
|
|
408
|
+
Object.entries(opts).filter(([, v]) => v !== undefined),
|
|
409
|
+
);
|
|
410
|
+
const raw = await this.http.post<unknown>(
|
|
411
|
+
`/computers/${this.id}/clone`,
|
|
412
|
+
body,
|
|
413
|
+
);
|
|
414
|
+
const data = unwrapData(raw) as unknown as ComputerData;
|
|
415
|
+
return new Computer(this.http, data);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Resize the computer (change CPU/memory/disk). */
|
|
419
|
+
async resize(size: string): Promise<Computer> {
|
|
420
|
+
const updated = await this.http.post<ComputerData>(
|
|
421
|
+
`/computers/${this.id}/resize`,
|
|
422
|
+
{ size },
|
|
423
|
+
);
|
|
424
|
+
this.data = updated;
|
|
425
|
+
return this;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Move the computer to a different region or host. */
|
|
429
|
+
async move(opts: Record<string, unknown>): Promise<Computer> {
|
|
430
|
+
const body = Object.fromEntries(
|
|
431
|
+
Object.entries(opts).filter(([, v]) => v !== undefined),
|
|
432
|
+
);
|
|
433
|
+
const updated = await this.http.post<ComputerData>(
|
|
434
|
+
`/computers/${this.id}/move`,
|
|
435
|
+
body,
|
|
436
|
+
);
|
|
437
|
+
this.data = updated;
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** Capture a region of the desktop as PNG bytes. */
|
|
442
|
+
async screenshotRegion(
|
|
443
|
+
x: number,
|
|
444
|
+
y: number,
|
|
445
|
+
width: number,
|
|
446
|
+
height: number,
|
|
447
|
+
): Promise<Uint8Array> {
|
|
448
|
+
return this.http.request<Uint8Array>(
|
|
449
|
+
`/computers/${this.id}/screenshot-region`,
|
|
450
|
+
{
|
|
451
|
+
method: "POST",
|
|
452
|
+
body: { x, y, width, height },
|
|
453
|
+
binary: true,
|
|
454
|
+
},
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Get time-series metrics (window: "1h" | "24h" | "7d"). */
|
|
459
|
+
async metrics(window = "1h"): Promise<Record<string, unknown>> {
|
|
460
|
+
return unwrapData(
|
|
461
|
+
await this.http.get<unknown>(`/computers/${this.id}/metrics`, {
|
|
462
|
+
window,
|
|
463
|
+
}),
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ─── Serialisation ─────────────────────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
toJSON(): ComputerData {
|
|
470
|
+
return this.data;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
toString(): string {
|
|
474
|
+
return `Computer(${this.id}, ${this.status})`;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* A thin filesystem wrapper that prepends a fixed working directory to all
|
|
480
|
+
* path arguments, mirroring the Node.js `fs/promises` surface.
|
|
481
|
+
*
|
|
482
|
+
* Obtain via `computer.fs("/workspace")`.
|
|
483
|
+
*/
|
|
484
|
+
export class ScopedFs {
|
|
485
|
+
private readonly files: Files;
|
|
486
|
+
private readonly workingDir: string;
|
|
487
|
+
|
|
488
|
+
constructor(files: Files, workingDir: string) {
|
|
489
|
+
this.files = files;
|
|
490
|
+
// Normalise: strip trailing slash unless it's the root.
|
|
491
|
+
this.workingDir =
|
|
492
|
+
workingDir !== "/" ? workingDir.replace(/\/$/, "") : workingDir;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Resolve a relative or absolute path against `workingDir`. */
|
|
496
|
+
private resolve(path: string): string {
|
|
497
|
+
if (path.startsWith("/")) return path;
|
|
498
|
+
const joined = `${this.workingDir}/${path}`;
|
|
499
|
+
// Collapse double slashes and simple `.` segments.
|
|
500
|
+
return joined.replace(/\/\.\//g, "/").replace(/\/+/g, "/");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** Write a UTF-8 string to a file (relative or absolute path). */
|
|
504
|
+
async writeFile(path: string, content: string): Promise<void> {
|
|
505
|
+
return this.files.writeFile(this.resolve(path), content);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Read a file and return its UTF-8 contents (relative or absolute path). */
|
|
509
|
+
async readFile(path: string): Promise<string> {
|
|
510
|
+
return this.files.readFile(this.resolve(path));
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** List a directory (relative or absolute path). */
|
|
514
|
+
async readdir(path: string): Promise<import("../types.js").DirEntry[]> {
|
|
515
|
+
return this.files.readdir(this.resolve(path));
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** Stat a path (relative or absolute). */
|
|
519
|
+
async stat(path: string): Promise<import("../types.js").FileStat> {
|
|
520
|
+
return this.files.stat(this.resolve(path));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** Create a directory (relative or absolute). */
|
|
524
|
+
async mkdir(
|
|
525
|
+
path: string,
|
|
526
|
+
options?: import("../types.js").MkdirParams,
|
|
527
|
+
): Promise<void> {
|
|
528
|
+
return this.files.mkdir(this.resolve(path), options);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { HttpClient } from "../http.js";
|
|
2
|
+
import type {
|
|
3
|
+
ComputerCreateParams,
|
|
4
|
+
ComputerData,
|
|
5
|
+
ComputerId,
|
|
6
|
+
ComputerListParams,
|
|
7
|
+
ComputerUpdateParams,
|
|
8
|
+
} from "../types.js";
|
|
9
|
+
import { Computer } from "./computer.js";
|
|
10
|
+
|
|
11
|
+
export class Computers {
|
|
12
|
+
private readonly http: HttpClient;
|
|
13
|
+
|
|
14
|
+
constructor(http: HttpClient) {
|
|
15
|
+
this.http = http;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create a new computer. The computer starts in `creating` state — call
|
|
20
|
+
* `computer.start()` once it reaches `stopped` to boot it.
|
|
21
|
+
*/
|
|
22
|
+
async create(params: ComputerCreateParams): Promise<Computer> {
|
|
23
|
+
const data = await this.http.post<ComputerData>("/computers", {
|
|
24
|
+
template_type: "miosa-desktop",
|
|
25
|
+
size: "small",
|
|
26
|
+
...params,
|
|
27
|
+
});
|
|
28
|
+
return new Computer(this.http, data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* List all computers for the authenticated tenant.
|
|
33
|
+
*/
|
|
34
|
+
async list(params?: ComputerListParams): Promise<Computer[]> {
|
|
35
|
+
const query: Record<string, string | number | boolean | undefined> = {
|
|
36
|
+
page: params?.page,
|
|
37
|
+
per_page: params?.per_page,
|
|
38
|
+
status: params?.status,
|
|
39
|
+
};
|
|
40
|
+
const response = await this.http.get<{ data: ComputerData[] }>(
|
|
41
|
+
"/computers",
|
|
42
|
+
query,
|
|
43
|
+
);
|
|
44
|
+
return response.data.map((d) => new Computer(this.http, d));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fetch a single computer by ID.
|
|
49
|
+
*/
|
|
50
|
+
async get(id: ComputerId | string): Promise<Computer> {
|
|
51
|
+
const data = await this.http.get<ComputerData>(`/computers/${id}`);
|
|
52
|
+
return new Computer(this.http, data);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Update computer metadata or name.
|
|
57
|
+
*/
|
|
58
|
+
async update(
|
|
59
|
+
id: ComputerId | string,
|
|
60
|
+
params: ComputerUpdateParams,
|
|
61
|
+
): Promise<Computer> {
|
|
62
|
+
const data = await this.http.patch<ComputerData>(
|
|
63
|
+
`/computers/${id}`,
|
|
64
|
+
params,
|
|
65
|
+
);
|
|
66
|
+
return new Computer(this.http, data);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Permanently delete a computer.
|
|
71
|
+
*/
|
|
72
|
+
async delete(id: ComputerId | string): Promise<void> {
|
|
73
|
+
return this.http.delete<void>(`/computers/${id}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { HttpClient } from "../http.js";
|
|
2
|
+
import type {
|
|
3
|
+
CreditBalance,
|
|
4
|
+
CreditTransactionListResponse,
|
|
5
|
+
CreditUsage,
|
|
6
|
+
} from "../types.js";
|
|
7
|
+
|
|
8
|
+
export interface CreditTransactionListParams {
|
|
9
|
+
page?: number;
|
|
10
|
+
per_page?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Credits {
|
|
14
|
+
private readonly http: HttpClient;
|
|
15
|
+
|
|
16
|
+
constructor(http: HttpClient) {
|
|
17
|
+
this.http = http;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Get the current credit balance for the authenticated tenant. */
|
|
21
|
+
async balance(): Promise<CreditBalance> {
|
|
22
|
+
return this.http.get<CreditBalance>("/credits/balance");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** List credit transactions (purchases, deductions). */
|
|
26
|
+
async transactions(
|
|
27
|
+
params?: CreditTransactionListParams,
|
|
28
|
+
): Promise<CreditTransactionListResponse> {
|
|
29
|
+
const query: Record<string, number | undefined> = {
|
|
30
|
+
page: params?.page,
|
|
31
|
+
per_page: params?.per_page,
|
|
32
|
+
};
|
|
33
|
+
return this.http.get<CreditTransactionListResponse>(
|
|
34
|
+
"/credits/transactions",
|
|
35
|
+
query,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Get aggregated credit usage for the current billing period. */
|
|
40
|
+
async usage(): Promise<CreditUsage> {
|
|
41
|
+
return this.http.get<CreditUsage>("/credits/usage");
|
|
42
|
+
}
|
|
43
|
+
}
|