@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,75 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { HttpClient } from "../../http.js";
|
|
2
|
+
import type {
|
|
3
|
+
HostId,
|
|
4
|
+
SecretCreateParams,
|
|
5
|
+
SecretData,
|
|
6
|
+
SecretId,
|
|
7
|
+
SecretUpdateParams,
|
|
8
|
+
} from "./types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Secrets resource — manage encrypted per-host (and per-tenant) env vars.
|
|
12
|
+
*
|
|
13
|
+
* Secrets are injected into exec sessions and PTY sessions on the host.
|
|
14
|
+
* The value is encrypted at rest; `reveal()` decrypts it for one-time display.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Tenant-wide secret (shared across all hosts)
|
|
18
|
+
* await client.openComputers.secrets.createForTenant({ name: "OPENAI_KEY", value: "sk-..." });
|
|
19
|
+
*
|
|
20
|
+
* // Host-specific secret
|
|
21
|
+
* const secret = await client.openComputers.secrets.createForHost(hostId, {
|
|
22
|
+
* name: "DB_PASSWORD", value: "hunter2",
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class Secrets {
|
|
27
|
+
private readonly http: HttpClient;
|
|
28
|
+
|
|
29
|
+
constructor(http: HttpClient) {
|
|
30
|
+
this.http = http;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Tenant-scoped secrets ────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* List all tenant-scoped secrets.
|
|
37
|
+
*/
|
|
38
|
+
async listForTenant(): Promise<SecretData[]> {
|
|
39
|
+
const result = await this.http.get<{ data: SecretData[] }>(
|
|
40
|
+
"/opencomputers/secrets",
|
|
41
|
+
);
|
|
42
|
+
return result.data;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a tenant-scoped secret (available to all hosts).
|
|
47
|
+
*/
|
|
48
|
+
async createForTenant(params: SecretCreateParams): Promise<SecretData> {
|
|
49
|
+
return this.http.post<SecretData>("/opencomputers/secrets", params);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Host-scoped secrets ──────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* List secrets scoped to a specific host.
|
|
56
|
+
*/
|
|
57
|
+
async listForHost(hostId: HostId | string): Promise<SecretData[]> {
|
|
58
|
+
const result = await this.http.get<{ data: SecretData[] }>(
|
|
59
|
+
`/opencomputers/hosts/${hostId}/secrets`,
|
|
60
|
+
);
|
|
61
|
+
return result.data;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a secret scoped to a specific host.
|
|
66
|
+
*/
|
|
67
|
+
async createForHost(
|
|
68
|
+
hostId: HostId | string,
|
|
69
|
+
params: SecretCreateParams,
|
|
70
|
+
): Promise<SecretData> {
|
|
71
|
+
return this.http.post<SecretData>(
|
|
72
|
+
`/opencomputers/hosts/${hostId}/secrets`,
|
|
73
|
+
params,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Update a host-scoped secret (rotate value or update description).
|
|
79
|
+
*/
|
|
80
|
+
async updateForHost(
|
|
81
|
+
hostId: HostId | string,
|
|
82
|
+
secretId: SecretId | string,
|
|
83
|
+
params: SecretUpdateParams,
|
|
84
|
+
): Promise<SecretData> {
|
|
85
|
+
return this.http.patch<SecretData>(
|
|
86
|
+
`/opencomputers/hosts/${hostId}/secrets/${secretId}`,
|
|
87
|
+
params,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Delete a host-scoped secret.
|
|
93
|
+
*/
|
|
94
|
+
async deleteForHost(
|
|
95
|
+
hostId: HostId | string,
|
|
96
|
+
secretId: SecretId | string,
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
return this.http.delete<void>(
|
|
99
|
+
`/opencomputers/hosts/${hostId}/secrets/${secretId}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Reveal (decrypt and return) the plaintext value of a secret.
|
|
105
|
+
* Use sparingly — each call is audit-logged.
|
|
106
|
+
*/
|
|
107
|
+
async reveal(
|
|
108
|
+
hostId: HostId | string,
|
|
109
|
+
secretId: SecretId | string,
|
|
110
|
+
): Promise<{ value: string }> {
|
|
111
|
+
return this.http.post<{ value: string }>(
|
|
112
|
+
`/opencomputers/hosts/${hostId}/secrets/${secretId}/reveal`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HttpClient } from "../../http.js";
|
|
2
|
+
import type { HostId, WsTicket } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Terminal resource — issue WebSocket tickets for interactive PTY sessions on
|
|
6
|
+
* a remote OpenComputers host.
|
|
7
|
+
*
|
|
8
|
+
* The ticket is consumed by a WebSocket upgrade at the URL in `ws_url`. Use
|
|
9
|
+
* xterm.js or any raw WebSocket client to drive the terminal.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const { ws_url } = await client.openComputers.terminal.ticket(hostId);
|
|
13
|
+
* const ws = new WebSocket(ws_url);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class Terminal {
|
|
17
|
+
private readonly http: HttpClient;
|
|
18
|
+
|
|
19
|
+
constructor(http: HttpClient) {
|
|
20
|
+
this.http = http;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Issue a short-lived WebSocket ticket for a terminal session.
|
|
25
|
+
*
|
|
26
|
+
* The ticket expires quickly — open the WebSocket immediately after receiving it.
|
|
27
|
+
*/
|
|
28
|
+
async ticket(hostId: HostId | string): Promise<WsTicket> {
|
|
29
|
+
return this.http.post<WsTicket>(
|
|
30
|
+
`/opencomputers/hosts/${hostId}/terminal/ticket`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { HttpClient } from "../../http.js";
|
|
2
|
+
import type {
|
|
3
|
+
HostId,
|
|
4
|
+
TunnelCreateParams,
|
|
5
|
+
TunnelData,
|
|
6
|
+
TunnelId,
|
|
7
|
+
TunnelListResponse,
|
|
8
|
+
TunnelUpdateParams,
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tunnels resource — expose ports on a remote host as publicly reachable URLs.
|
|
13
|
+
*
|
|
14
|
+
* The public proxy is served at `/t/:slug/*path`. The `auth_mode` field on each
|
|
15
|
+
* tunnel controls whether the URL is open to the internet or requires
|
|
16
|
+
* tenant credentials / a password.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const tunnel = await client.openComputers.tunnels.create(hostId, {
|
|
20
|
+
* target_port: 8080,
|
|
21
|
+
* auth_mode: "public",
|
|
22
|
+
* });
|
|
23
|
+
* console.log(tunnel.public_url); // https://api.miosa.ai/t/<slug>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class Tunnels {
|
|
27
|
+
private readonly http: HttpClient;
|
|
28
|
+
|
|
29
|
+
constructor(http: HttpClient) {
|
|
30
|
+
this.http = http;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private base(hostId: string): string {
|
|
34
|
+
return `/opencomputers/hosts/${hostId}/tunnels`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* List all tunnels for a host.
|
|
39
|
+
*/
|
|
40
|
+
async list(hostId: HostId | string): Promise<TunnelListResponse> {
|
|
41
|
+
return this.http.get<TunnelListResponse>(this.base(hostId));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a new tunnel that forwards traffic to `target_port` on the host.
|
|
46
|
+
*/
|
|
47
|
+
async create(
|
|
48
|
+
hostId: HostId | string,
|
|
49
|
+
params: TunnelCreateParams,
|
|
50
|
+
): Promise<TunnelData> {
|
|
51
|
+
return this.http.post<TunnelData>(this.base(hostId), params);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Fetch a single tunnel.
|
|
56
|
+
*/
|
|
57
|
+
async get(
|
|
58
|
+
hostId: HostId | string,
|
|
59
|
+
tunnelId: TunnelId | string,
|
|
60
|
+
): Promise<TunnelData> {
|
|
61
|
+
return this.http.get<TunnelData>(`${this.base(hostId)}/${tunnelId}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Update a tunnel (target port, auth mode, or enabled state).
|
|
66
|
+
*/
|
|
67
|
+
async update(
|
|
68
|
+
hostId: HostId | string,
|
|
69
|
+
tunnelId: TunnelId | string,
|
|
70
|
+
params: TunnelUpdateParams,
|
|
71
|
+
): Promise<TunnelData> {
|
|
72
|
+
return this.http.patch<TunnelData>(
|
|
73
|
+
`${this.base(hostId)}/${tunnelId}`,
|
|
74
|
+
params,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Delete a tunnel. The public URL immediately becomes unreachable.
|
|
80
|
+
*/
|
|
81
|
+
async delete(
|
|
82
|
+
hostId: HostId | string,
|
|
83
|
+
tunnelId: TunnelId | string,
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
return this.http.delete<void>(`${this.base(hostId)}/${tunnelId}`);
|
|
86
|
+
}
|
|
87
|
+
}
|