@miosa/sdk 1.2.4 → 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.
Files changed (100) hide show
  1. package/README.md +75 -3
  2. package/dist/index.d.ts +323 -175
  3. package/dist/index.js +441 -386
  4. package/dist/index.js.map +1 -1
  5. package/package.json +1 -2
  6. package/src/client.ts +0 -293
  7. package/src/errors.ts +0 -136
  8. package/src/http.test.ts +0 -374
  9. package/src/http.ts +0 -390
  10. package/src/index.ts +0 -564
  11. package/src/resources/admin.ts +0 -359
  12. package/src/resources/analytics.ts +0 -60
  13. package/src/resources/api-keys.ts +0 -135
  14. package/src/resources/audit-log.ts +0 -64
  15. package/src/resources/benchmarks.ts +0 -104
  16. package/src/resources/builder-sessions.ts +0 -75
  17. package/src/resources/channels.ts +0 -143
  18. package/src/resources/checkpoints.ts +0 -225
  19. package/src/resources/command-center.ts +0 -73
  20. package/src/resources/community.ts +0 -103
  21. package/src/resources/completions.ts +0 -104
  22. package/src/resources/computer-auto-stop.ts +0 -43
  23. package/src/resources/computer-env.ts +0 -76
  24. package/src/resources/computer-logs.ts +0 -50
  25. package/src/resources/computer-osa.ts +0 -76
  26. package/src/resources/computer-ports.ts +0 -91
  27. package/src/resources/computer-terminal.ts +0 -61
  28. package/src/resources/computer-volumes.ts +0 -64
  29. package/src/resources/computer.ts +0 -551
  30. package/src/resources/computers.ts +0 -75
  31. package/src/resources/credits.ts +0 -43
  32. package/src/resources/cron-jobs.ts +0 -191
  33. package/src/resources/custom_domains.ts +0 -123
  34. package/src/resources/dashboard.ts +0 -49
  35. package/src/resources/databases.ts +0 -218
  36. package/src/resources/deployments.test.ts +0 -194
  37. package/src/resources/deployments.ts +0 -1060
  38. package/src/resources/desktop.ts +0 -134
  39. package/src/resources/docker-deploy.test.ts +0 -102
  40. package/src/resources/docker-deploy.ts +0 -183
  41. package/src/resources/egress.test.ts +0 -318
  42. package/src/resources/egressAudit.ts +0 -245
  43. package/src/resources/egressNetwork.ts +0 -450
  44. package/src/resources/egressSecrets.ts +0 -577
  45. package/src/resources/email.ts +0 -212
  46. package/src/resources/embeddings.ts +0 -36
  47. package/src/resources/events.ts +0 -296
  48. package/src/resources/exec.ts +0 -319
  49. package/src/resources/external-keys.ts +0 -79
  50. package/src/resources/files.test.ts +0 -339
  51. package/src/resources/files.ts +0 -220
  52. package/src/resources/flat-custom-domains.ts +0 -127
  53. package/src/resources/functions.ts +0 -178
  54. package/src/resources/governance.test.ts +0 -355
  55. package/src/resources/governance.ts +0 -528
  56. package/src/resources/health-checks.ts +0 -165
  57. package/src/resources/integrations.ts +0 -183
  58. package/src/resources/mcp.ts +0 -70
  59. package/src/resources/models.ts +0 -45
  60. package/src/resources/network_policy.ts +0 -73
  61. package/src/resources/open-computers/agents.ts +0 -91
  62. package/src/resources/open-computers/apps.ts +0 -102
  63. package/src/resources/open-computers/clusters.ts +0 -88
  64. package/src/resources/open-computers/desktop.ts +0 -34
  65. package/src/resources/open-computers/files.ts +0 -97
  66. package/src/resources/open-computers/hosts.ts +0 -85
  67. package/src/resources/open-computers/index.ts +0 -98
  68. package/src/resources/open-computers/jobs.ts +0 -75
  69. package/src/resources/open-computers/open_computers.test.ts +0 -288
  70. package/src/resources/open-computers/secrets.ts +0 -115
  71. package/src/resources/open-computers/terminal.ts +0 -33
  72. package/src/resources/open-computers/tunnels.ts +0 -87
  73. package/src/resources/open-computers/types.ts +0 -343
  74. package/src/resources/open-computers/workspaces.ts +0 -135
  75. package/src/resources/org-invites.ts +0 -189
  76. package/src/resources/phase1.test.ts +0 -187
  77. package/src/resources/project-auth.ts +0 -142
  78. package/src/resources/project-integrations.ts +0 -133
  79. package/src/resources/provider-defaults.ts +0 -89
  80. package/src/resources/quotas.ts +0 -77
  81. package/src/resources/regions.ts +0 -94
  82. package/src/resources/sandbox-processes.ts +0 -112
  83. package/src/resources/sandbox-shares.ts +0 -83
  84. package/src/resources/sandbox-templates.ts +0 -195
  85. package/src/resources/sandboxes.live.test.ts +0 -92
  86. package/src/resources/sandboxes.test.ts +0 -653
  87. package/src/resources/sandboxes.ts +0 -1433
  88. package/src/resources/settings.ts +0 -143
  89. package/src/resources/snapshots-standalone.ts +0 -51
  90. package/src/resources/storage.ts +0 -221
  91. package/src/resources/tenant-events.ts +0 -32
  92. package/src/resources/tenant.ts +0 -148
  93. package/src/resources/usage.ts +0 -85
  94. package/src/resources/volumes.ts +0 -117
  95. package/src/resources/webhooks.ts +0 -224
  96. package/src/resources/workspace-invites.ts +0 -188
  97. package/src/resources/workspace-members.test.ts +0 -121
  98. package/src/resources/workspace-members.ts +0 -143
  99. package/src/resources/workspaces.ts +0 -285
  100. package/src/types.ts +0 -463
@@ -1,97 +0,0 @@
1
- import { HttpClient } from "../../http.js";
2
- import type { FsEntry, FsListResponse, FsStat, HostId } from "./types.js";
3
-
4
- /**
5
- * Files resource — direct file-system access on a remote OpenComputers host.
6
- *
7
- * Operations are proxied through the OSA daemon running on the host.
8
- *
9
- * ```ts
10
- * const entries = await client.openComputers.files.list(hostId, "/home/user");
11
- * const bytes = await client.openComputers.files.download(hostId, "/home/user/report.pdf");
12
- * await client.openComputers.files.upload(hostId, "/tmp/data.json", new TextEncoder().encode("{}"));
13
- * ```
14
- */
15
- export class OcFiles {
16
- private readonly http: HttpClient;
17
-
18
- constructor(http: HttpClient) {
19
- this.http = http;
20
- }
21
-
22
- private base(hostId: string): string {
23
- return `/opencomputers/hosts/${hostId}/fs`;
24
- }
25
-
26
- /**
27
- * List directory entries at `path`.
28
- */
29
- async list(hostId: HostId | string, path: string): Promise<FsListResponse> {
30
- return this.http.get<FsListResponse>(this.base(hostId), { path });
31
- }
32
-
33
- /**
34
- * Stat a path (does not follow symlinks).
35
- */
36
- async stat(hostId: HostId | string, path: string): Promise<FsStat> {
37
- return this.http.get<FsStat>(`${this.base(hostId)}/stat`, { path });
38
- }
39
-
40
- /**
41
- * Download a file from the host. Returns raw bytes.
42
- */
43
- async download(hostId: HostId | string, path: string): Promise<Uint8Array> {
44
- const url = `${this.base(hostId)}/download?path=${encodeURIComponent(path)}`;
45
- return this.http.getBinary(url);
46
- }
47
-
48
- /**
49
- * Upload content to `remotePath` on the host.
50
- *
51
- * @param content - `Blob`, `Uint8Array`, or UTF-8 string.
52
- * @param remotePath - Absolute destination path on the host.
53
- * @param filename - Optional filename hint; defaults to `remotePath` basename.
54
- */
55
- async upload(
56
- hostId: HostId | string,
57
- remotePath: string,
58
- content: Blob | Uint8Array | string,
59
- filename?: string,
60
- ): Promise<FsEntry> {
61
- const fd = new FormData();
62
- const name = filename ?? remotePath.split("/").pop() ?? "file";
63
-
64
- if (typeof content === "string") {
65
- fd.append("file", new Blob([content]), name);
66
- } else if (content instanceof Uint8Array) {
67
- fd.append("file", new Blob([content.buffer as ArrayBuffer]), name);
68
- } else {
69
- fd.append("file", content, name);
70
- }
71
- fd.append("path", remotePath);
72
-
73
- return this.http.postFormData<FsEntry>(`${this.base(hostId)}/upload`, fd);
74
- }
75
-
76
- /**
77
- * Delete a file or directory.
78
- *
79
- * @param recursive - When `true`, delete non-empty directories recursively.
80
- */
81
- async delete(
82
- hostId: HostId | string,
83
- path: string,
84
- recursive = false,
85
- ): Promise<void> {
86
- return this.http.delete<void>(
87
- `${this.base(hostId)}?path=${encodeURIComponent(path)}&recursive=${recursive}`,
88
- );
89
- }
90
-
91
- /**
92
- * Create a directory (and any missing parents).
93
- */
94
- async mkdir(hostId: HostId | string, path: string): Promise<void> {
95
- return this.http.post<void>(`${this.base(hostId)}/mkdir`, { path });
96
- }
97
- }
@@ -1,85 +0,0 @@
1
- import { HttpClient } from "../../http.js";
2
- import type {
3
- HostCreateParams,
4
- HostData,
5
- HostEvent,
6
- HostId,
7
- HostListResponse,
8
- HostUpdateParams,
9
- } from "./types.js";
10
-
11
- /**
12
- * Hosts resource — manage BYOC (Bring Your Own Computer) hosts registered
13
- * under your tenant.
14
- *
15
- * ```ts
16
- * const host = await client.openComputers.hosts.create({ name: "my-mac" });
17
- * console.log(host.host_key); // store this — shown only once
18
- * ```
19
- */
20
- export class Hosts {
21
- private readonly http: HttpClient;
22
-
23
- constructor(http: HttpClient) {
24
- this.http = http;
25
- }
26
-
27
- private base(): string {
28
- return "/opencomputers/hosts";
29
- }
30
-
31
- /**
32
- * List all hosts registered for the tenant.
33
- */
34
- async list(): Promise<HostListResponse> {
35
- return this.http.get<HostListResponse>(this.base());
36
- }
37
-
38
- /**
39
- * Register a new host. The returned `host_key` is shown **once** — store it
40
- * immediately and install it on the target machine.
41
- */
42
- async create(params: HostCreateParams): Promise<HostData> {
43
- return this.http.post<HostData>(this.base(), params);
44
- }
45
-
46
- /**
47
- * Fetch a single host by ID.
48
- */
49
- async get(id: HostId | string): Promise<HostData> {
50
- return this.http.get<HostData>(`${this.base()}/${id}`);
51
- }
52
-
53
- /**
54
- * Update host metadata (name / labels).
55
- */
56
- async update(
57
- id: HostId | string,
58
- params: HostUpdateParams,
59
- ): Promise<HostData> {
60
- return this.http.patch<HostData>(`${this.base()}/${id}`, params);
61
- }
62
-
63
- /**
64
- * Revoke a host. Permanently removes the registration; the host key is
65
- * invalidated and the host will disconnect on next heartbeat.
66
- */
67
- async revoke(id: HostId | string): Promise<void> {
68
- return this.http.delete<void>(`${this.base()}/${id}`);
69
- }
70
-
71
- /**
72
- * Stream host state-change events (connect / disconnect / error) as an
73
- * `AsyncIterable`.
74
- *
75
- * ```ts
76
- * for await (const event of client.openComputers.hosts.events()) {
77
- * console.log(event.type, event.host_id);
78
- * if (shouldStop) break;
79
- * }
80
- * ```
81
- */
82
- events(): AsyncIterableIterator<HostEvent> {
83
- return this.http.stream<HostEvent>("/opencomputers/hosts/events");
84
- }
85
- }
@@ -1,98 +0,0 @@
1
- import { HttpClient } from "../../http.js";
2
- import { Agents } from "./agents.js";
3
- import { Apps } from "./apps.js";
4
- import { Clusters } from "./clusters.js";
5
- import { Desktop } from "./desktop.js";
6
- import { OcFiles } from "./files.js";
7
- import { Hosts } from "./hosts.js";
8
- import { Jobs } from "./jobs.js";
9
- import { Secrets } from "./secrets.js";
10
- import { Terminal } from "./terminal.js";
11
- import { Tunnels } from "./tunnels.js";
12
- import { OcWorkspaces } from "./workspaces.js";
13
-
14
- export * from "./types.js";
15
- export { Agents } from "./agents.js";
16
- export { Apps } from "./apps.js";
17
- export { Clusters } from "./clusters.js";
18
- export { Desktop } from "./desktop.js";
19
- export { OcFiles } from "./files.js";
20
- export { Hosts } from "./hosts.js";
21
- export { Jobs } from "./jobs.js";
22
- export { Secrets } from "./secrets.js";
23
- export { Terminal } from "./terminal.js";
24
- export { Tunnels } from "./tunnels.js";
25
- export { OcWorkspaces } from "./workspaces.js";
26
-
27
- /**
28
- * OpenComputers namespace — BYOC (Bring Your Own Computer) host management.
29
- *
30
- * Register your own machines (Mac, Linux, Windows) with MIOSA and use them
31
- * just like cloud computers: run jobs, manage files, open terminals, expose
32
- * ports, dispatch AI agents, and run LLM inference clusters.
33
- *
34
- * Access via `client.openComputers`:
35
- *
36
- * ```ts
37
- * const miosa = new Miosa({ apiKey: "msk_u_..." });
38
- *
39
- * // Register a host — save host_key immediately, shown only once
40
- * const host = await miosa.openComputers.hosts.create({ name: "my-mac" });
41
- *
42
- * // Run a command
43
- * const job = await miosa.openComputers.jobs.run(host.id, { command: "npm test" });
44
- *
45
- * // Stream output
46
- * for await (const event of miosa.openComputers.jobs.stream(host.id, job.id)) {
47
- * process.stdout.write(String(event.data ?? ""));
48
- * if (event.type === "done") break;
49
- * }
50
- *
51
- * // Expose a port
52
- * const tunnel = await miosa.openComputers.tunnels.create(host.id, { target_port: 3000 });
53
- * console.log(tunnel.public_url);
54
- *
55
- * // Dispatch an AI agent
56
- * const session = await miosa.openComputers.agents.dispatch(host.id, {
57
- * task: "Run tests and fix failing ones",
58
- * });
59
- * ```
60
- */
61
- export class OpenComputers {
62
- /** Host registration and lifecycle. */
63
- readonly hosts: Hosts;
64
- /** Command execution on remote hosts. */
65
- readonly jobs: Jobs;
66
- /** File system access on remote hosts. */
67
- readonly files: OcFiles;
68
- /** Interactive terminal session tickets. */
69
- readonly terminal: Terminal;
70
- /** Desktop streaming session tickets. */
71
- readonly desktop: Desktop;
72
- /** HTTP tunnel management (expose host ports publicly). */
73
- readonly tunnels: Tunnels;
74
- /** AI agent dispatch and session management. */
75
- readonly agents: Agents;
76
- /** Multi-host LLM inference clusters. */
77
- readonly clusters: Clusters;
78
- /** App library — one-click installs on hosts. */
79
- readonly apps: Apps;
80
- /** Git-backed workspace environments. */
81
- readonly workspaces: OcWorkspaces;
82
- /** Encrypted per-host / per-tenant secrets. */
83
- readonly secrets: Secrets;
84
-
85
- constructor(http: HttpClient) {
86
- this.hosts = new Hosts(http);
87
- this.jobs = new Jobs(http);
88
- this.files = new OcFiles(http);
89
- this.terminal = new Terminal(http);
90
- this.desktop = new Desktop(http);
91
- this.tunnels = new Tunnels(http);
92
- this.agents = new Agents(http);
93
- this.clusters = new Clusters(http);
94
- this.apps = new Apps(http);
95
- this.workspaces = new OcWorkspaces(http);
96
- this.secrets = new Secrets(http);
97
- }
98
- }
@@ -1,75 +0,0 @@
1
- import { HttpClient } from "../../http.js";
2
- import type {
3
- HostId,
4
- JobData,
5
- JobEvent,
6
- JobId,
7
- JobListResponse,
8
- JobRunParams,
9
- } from "./types.js";
10
-
11
- /**
12
- * Jobs resource — run commands on a remote OpenComputers host and stream output.
13
- *
14
- * ```ts
15
- * const job = await client.openComputers.jobs.run(hostId, { command: "npm test" });
16
- * for await (const event of client.openComputers.jobs.stream(hostId, job.id)) {
17
- * process.stdout.write(String(event.data ?? ""));
18
- * if (event.type === "done") break;
19
- * }
20
- * ```
21
- */
22
- export class Jobs {
23
- private readonly http: HttpClient;
24
-
25
- constructor(http: HttpClient) {
26
- this.http = http;
27
- }
28
-
29
- private base(hostId: string): string {
30
- return `/opencomputers/hosts/${hostId}`;
31
- }
32
-
33
- /**
34
- * Dispatch a command to run on the remote host.
35
- */
36
- async run(hostId: HostId | string, params: JobRunParams): Promise<JobData> {
37
- return this.http.post<JobData>(`${this.base(hostId)}/exec`, params);
38
- }
39
-
40
- /**
41
- * List all jobs for a host.
42
- */
43
- async list(hostId: HostId | string): Promise<JobListResponse> {
44
- return this.http.get<JobListResponse>(`${this.base(hostId)}/exec`);
45
- }
46
-
47
- /**
48
- * Fetch the current state of a job.
49
- */
50
- async get(hostId: HostId | string, jobId: JobId | string): Promise<JobData> {
51
- return this.http.get<JobData>(`${this.base(hostId)}/exec/${jobId}`);
52
- }
53
-
54
- /**
55
- * Stream live output from a running job.
56
- *
57
- * Yields `JobEvent` objects with `type` of `stdout`, `stderr`, `exit`, or
58
- * `done`. Break the loop when you receive `done` or `exit`.
59
- */
60
- stream(
61
- hostId: HostId | string,
62
- jobId: JobId | string,
63
- ): AsyncIterableIterator<JobEvent> {
64
- return this.http.stream<JobEvent>(
65
- `${this.base(hostId)}/exec/${jobId}/stream`,
66
- );
67
- }
68
-
69
- /**
70
- * Cancel a running or queued job.
71
- */
72
- async cancel(hostId: HostId | string, jobId: JobId | string): Promise<void> {
73
- return this.http.delete<void>(`${this.base(hostId)}/exec/${jobId}`);
74
- }
75
- }
@@ -1,288 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { HttpClient } from "../../http.js";
3
- import { OpenComputers } from "./index.js";
4
-
5
- // ── Shared mock setup ────────────────────────────────────────────────────────
6
-
7
- const mockGet = vi.fn();
8
- const mockPost = vi.fn();
9
- const mockPatch = vi.fn();
10
- const mockDelete = vi.fn();
11
- const mockStream = vi.fn();
12
-
13
- function makeHttp(): HttpClient {
14
- const http = {} as HttpClient;
15
- http.get = mockGet;
16
- http.post = mockPost;
17
- http.patch = mockPatch;
18
- http.delete = mockDelete;
19
- http.stream = mockStream;
20
- http.getBinary = vi.fn();
21
- http.postFormData = vi.fn();
22
- return http;
23
- }
24
-
25
- beforeEach(() => {
26
- vi.clearAllMocks();
27
- });
28
-
29
- afterEach(() => {
30
- vi.restoreAllMocks();
31
- });
32
-
33
- // ── Hosts ────────────────────────────────────────────────────────────────────
34
-
35
- describe("OpenComputers.hosts", () => {
36
- const hostData = {
37
- id: "host_abc",
38
- name: "my-mac",
39
- region: null,
40
- status: "online",
41
- tenant_id: "t_1",
42
- labels: {},
43
- created_at: "2026-01-01T00:00:00Z",
44
- updated_at: "2026-01-01T00:00:00Z",
45
- };
46
-
47
- it("list() calls GET /opencomputers/hosts", async () => {
48
- mockGet.mockResolvedValue({
49
- data: [hostData],
50
- meta: { total: 1, page: 1, per_page: 20 },
51
- });
52
- const oc = new OpenComputers(makeHttp());
53
- const result = await oc.hosts.list();
54
- expect(mockGet).toHaveBeenCalledWith("/opencomputers/hosts");
55
- expect(result.data).toHaveLength(1);
56
- });
57
-
58
- it("create() calls POST /opencomputers/hosts with host_key in response", async () => {
59
- const created = { ...hostData, host_key: "hk_secret" };
60
- mockPost.mockResolvedValue(created);
61
- const oc = new OpenComputers(makeHttp());
62
- const result = await oc.hosts.create({ name: "my-mac" });
63
- expect(mockPost).toHaveBeenCalledWith("/opencomputers/hosts", {
64
- name: "my-mac",
65
- });
66
- expect(result.host_key).toBe("hk_secret");
67
- });
68
-
69
- it("get() calls GET /opencomputers/hosts/:id", async () => {
70
- mockGet.mockResolvedValue(hostData);
71
- const oc = new OpenComputers(makeHttp());
72
- await oc.hosts.get("host_abc");
73
- expect(mockGet).toHaveBeenCalledWith("/opencomputers/hosts/host_abc");
74
- });
75
-
76
- it("update() calls PATCH /opencomputers/hosts/:id", async () => {
77
- mockPatch.mockResolvedValue({ ...hostData, name: "renamed" });
78
- const oc = new OpenComputers(makeHttp());
79
- await oc.hosts.update("host_abc", { name: "renamed" });
80
- expect(mockPatch).toHaveBeenCalledWith("/opencomputers/hosts/host_abc", {
81
- name: "renamed",
82
- });
83
- });
84
-
85
- it("revoke() calls DELETE /opencomputers/hosts/:id", async () => {
86
- mockDelete.mockResolvedValue(undefined);
87
- const oc = new OpenComputers(makeHttp());
88
- await oc.hosts.revoke("host_abc");
89
- expect(mockDelete).toHaveBeenCalledWith("/opencomputers/hosts/host_abc");
90
- });
91
-
92
- it("events() returns an AsyncIterable from stream()", () => {
93
- async function* fakeStream() {
94
- yield {
95
- type: "host_connected",
96
- host_id: "host_abc",
97
- data: null,
98
- timestamp: "t",
99
- };
100
- }
101
- mockStream.mockReturnValue(fakeStream());
102
- const oc = new OpenComputers(makeHttp());
103
- const iter = oc.hosts.events();
104
- expect(mockStream).toHaveBeenCalledWith("/opencomputers/hosts/events");
105
- expect(typeof iter[Symbol.asyncIterator]).toBe("function");
106
- });
107
- });
108
-
109
- // ── Jobs ─────────────────────────────────────────────────────────────────────
110
-
111
- describe("OpenComputers.jobs", () => {
112
- const jobData = {
113
- id: "job_1",
114
- host_id: "host_abc",
115
- status: "completed",
116
- command: "npm test",
117
- args: [],
118
- env: [],
119
- cwd: null,
120
- exit_code: 0,
121
- stdout: "ok",
122
- stderr: "",
123
- created_at: "2026-01-01T00:00:00Z",
124
- updated_at: "2026-01-01T00:00:00Z",
125
- completed_at: "2026-01-01T00:00:01Z",
126
- };
127
-
128
- it("run() calls POST /opencomputers/hosts/:id/exec", async () => {
129
- mockPost.mockResolvedValue(jobData);
130
- const oc = new OpenComputers(makeHttp());
131
- const result = await oc.jobs.run("host_abc", { command: "npm test" });
132
- expect(mockPost).toHaveBeenCalledWith(
133
- "/opencomputers/hosts/host_abc/exec",
134
- { command: "npm test" },
135
- );
136
- expect(result.id).toBe("job_1");
137
- });
138
-
139
- it("list() calls GET /opencomputers/hosts/:id/exec", async () => {
140
- mockGet.mockResolvedValue({
141
- data: [jobData],
142
- meta: { total: 1, page: 1, per_page: 20 },
143
- });
144
- const oc = new OpenComputers(makeHttp());
145
- await oc.jobs.list("host_abc");
146
- expect(mockGet).toHaveBeenCalledWith("/opencomputers/hosts/host_abc/exec");
147
- });
148
-
149
- it("get() calls GET /opencomputers/hosts/:id/exec/:job_id", async () => {
150
- mockGet.mockResolvedValue(jobData);
151
- const oc = new OpenComputers(makeHttp());
152
- await oc.jobs.get("host_abc", "job_1");
153
- expect(mockGet).toHaveBeenCalledWith(
154
- "/opencomputers/hosts/host_abc/exec/job_1",
155
- );
156
- });
157
-
158
- it("stream() returns an AsyncIterable", () => {
159
- async function* fakeStream() {
160
- yield { type: "stdout", job_id: "job_1", data: "ok\n", timestamp: "t" };
161
- yield { type: "done", job_id: "job_1", data: null, timestamp: "t" };
162
- }
163
- mockStream.mockReturnValue(fakeStream());
164
- const oc = new OpenComputers(makeHttp());
165
- const iter = oc.jobs.stream("host_abc", "job_1");
166
- expect(mockStream).toHaveBeenCalledWith(
167
- "/opencomputers/hosts/host_abc/exec/job_1/stream",
168
- );
169
- expect(typeof iter[Symbol.asyncIterator]).toBe("function");
170
- });
171
-
172
- it("cancel() calls DELETE /opencomputers/hosts/:id/exec/:job_id", async () => {
173
- mockDelete.mockResolvedValue(undefined);
174
- const oc = new OpenComputers(makeHttp());
175
- await oc.jobs.cancel("host_abc", "job_1");
176
- expect(mockDelete).toHaveBeenCalledWith(
177
- "/opencomputers/hosts/host_abc/exec/job_1",
178
- );
179
- });
180
- });
181
-
182
- // ── Error paths ───────────────────────────────────────────────────────────────
183
-
184
- describe("OpenComputers error paths", () => {
185
- it("propagates 401 from hosts.list()", async () => {
186
- const { AuthError } = await import("../../errors.js");
187
- mockGet.mockRejectedValue(new AuthError("Unauthorized", "UNAUTHORIZED"));
188
- const oc = new OpenComputers(makeHttp());
189
- await expect(oc.hosts.list()).rejects.toBeInstanceOf(AuthError);
190
- });
191
-
192
- it("propagates 404 from hosts.get()", async () => {
193
- const { NotFoundError } = await import("../../errors.js");
194
- mockGet.mockRejectedValue(new NotFoundError("Host not found", "NOT_FOUND"));
195
- const oc = new OpenComputers(makeHttp());
196
- await expect(oc.hosts.get("nonexistent")).rejects.toBeInstanceOf(
197
- NotFoundError,
198
- );
199
- });
200
-
201
- it("propagates 402 from jobs.run() when credits exhausted", async () => {
202
- const { InsufficientCreditsError } = await import("../../errors.js");
203
- mockPost.mockRejectedValue(
204
- new InsufficientCreditsError(
205
- "Insufficient credits",
206
- "INSUFFICIENT_CREDITS",
207
- ),
208
- );
209
- const oc = new OpenComputers(makeHttp());
210
- await expect(
211
- oc.jobs.run("host_abc", { command: "npm test" }),
212
- ).rejects.toBeInstanceOf(InsufficientCreditsError);
213
- });
214
- });
215
-
216
- // ── Tunnels ───────────────────────────────────────────────────────────────────
217
-
218
- describe("OpenComputers.tunnels", () => {
219
- const tunnelData = {
220
- id: "tun_1",
221
- host_id: "host_abc",
222
- slug: "abc123",
223
- target_port: 3000,
224
- auth_mode: "public",
225
- public_url: "https://api.miosa.ai/t/abc123",
226
- enabled: true,
227
- created_at: "2026-01-01T00:00:00Z",
228
- updated_at: "2026-01-01T00:00:00Z",
229
- };
230
-
231
- it("create() calls POST /opencomputers/hosts/:id/tunnels", async () => {
232
- mockPost.mockResolvedValue(tunnelData);
233
- const oc = new OpenComputers(makeHttp());
234
- const result = await oc.tunnels.create("host_abc", { target_port: 3000 });
235
- expect(mockPost).toHaveBeenCalledWith(
236
- "/opencomputers/hosts/host_abc/tunnels",
237
- { target_port: 3000 },
238
- );
239
- expect(result.public_url).toContain("abc123");
240
- });
241
-
242
- it("delete() calls DELETE /opencomputers/hosts/:id/tunnels/:tid", async () => {
243
- mockDelete.mockResolvedValue(undefined);
244
- const oc = new OpenComputers(makeHttp());
245
- await oc.tunnels.delete("host_abc", "tun_1");
246
- expect(mockDelete).toHaveBeenCalledWith(
247
- "/opencomputers/hosts/host_abc/tunnels/tun_1",
248
- );
249
- });
250
- });
251
-
252
- // ── Agents ────────────────────────────────────────────────────────────────────
253
-
254
- describe("OpenComputers.agents", () => {
255
- const sessionData = {
256
- id: "sess_1",
257
- host_id: "host_abc",
258
- task: "run tests",
259
- model_id: null,
260
- status: "pending",
261
- max_turns: 20,
262
- turns_used: 0,
263
- created_at: "2026-01-01T00:00:00Z",
264
- updated_at: "2026-01-01T00:00:00Z",
265
- completed_at: null,
266
- error: null,
267
- };
268
-
269
- it("dispatch() calls POST /opencomputers/hosts/:id/agent/dispatch", async () => {
270
- mockPost.mockResolvedValue(sessionData);
271
- const oc = new OpenComputers(makeHttp());
272
- const result = await oc.agents.dispatch("host_abc", { task: "run tests" });
273
- expect(mockPost).toHaveBeenCalledWith(
274
- "/opencomputers/hosts/host_abc/agent/dispatch",
275
- { task: "run tests" },
276
- );
277
- expect(result.id).toBe("sess_1");
278
- });
279
-
280
- it("cancel() calls DELETE /opencomputers/hosts/:id/agent/sessions/:id", async () => {
281
- mockDelete.mockResolvedValue(undefined);
282
- const oc = new OpenComputers(makeHttp());
283
- await oc.agents.cancel("host_abc", "sess_1");
284
- expect(mockDelete).toHaveBeenCalledWith(
285
- "/opencomputers/hosts/host_abc/agent/sessions/sess_1",
286
- );
287
- });
288
- });