@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,339 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { HttpClient } from "../http.js";
3
- import type { DirEntry, FileStat } from "../types.js";
4
- import { Files } from "./files.js";
5
-
6
- // ── Helpers ────────────────────────────────────────────────────────────────────
7
-
8
- function mockFetch(status: number, body: unknown): void {
9
- vi.stubGlobal(
10
- "fetch",
11
- vi.fn().mockResolvedValue({
12
- ok: status >= 200 && status < 300,
13
- status,
14
- headers: new Headers({ "content-type": "application/json" }),
15
- json: () => Promise.resolve(body),
16
- arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
17
- body: null,
18
- }),
19
- );
20
- }
21
-
22
- function makeFiles(computerId = "computer-123"): Files {
23
- const http = new HttpClient({
24
- baseUrl: "https://api.miosa.ai/api/v1",
25
- apiKey: "msk_test_key",
26
- timeout: 5_000,
27
- maxRetries: 0,
28
- });
29
- return new Files(http, computerId);
30
- }
31
-
32
- // ── stat ───────────────────────────────────────────────────────────────────────
33
-
34
- describe("Files.stat", () => {
35
- beforeEach(() => vi.restoreAllMocks());
36
-
37
- it("should POST to /computers/:id/files/stat with path", async () => {
38
- const mockStat: FileStat = {
39
- path: "/home/user/main.py",
40
- size: 42,
41
- mode: 0o100644,
42
- is_dir: false,
43
- is_symlink: false,
44
- modified_at: "2026-01-01T00:00:00Z",
45
- };
46
- mockFetch(200, { data: mockStat });
47
-
48
- const files = makeFiles();
49
- const result = await files.stat("/home/user/main.py");
50
-
51
- expect(result).toEqual(mockStat);
52
-
53
- const [url, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
54
- string,
55
- RequestInit,
56
- ];
57
- expect(url).toContain("/computers/computer-123/files/stat");
58
- expect(init.method).toBe("POST");
59
- expect(JSON.parse(init.body as string)).toEqual({
60
- path: "/home/user/main.py",
61
- });
62
- });
63
-
64
- it("should unwrap data envelope from response", async () => {
65
- const stat: FileStat = {
66
- path: "/tmp/x",
67
- size: 0,
68
- mode: 0o40755,
69
- is_dir: true,
70
- is_symlink: false,
71
- modified_at: "2026-01-01T00:00:00Z",
72
- };
73
- mockFetch(200, { data: stat });
74
- const result = await makeFiles().stat("/tmp/x");
75
- expect(result.is_dir).toBe(true);
76
- expect(result.size).toBe(0);
77
- });
78
-
79
- it("should include symlink_target when present", async () => {
80
- const stat: FileStat = {
81
- path: "/home/user/link",
82
- size: 10,
83
- mode: 0o120777,
84
- is_dir: false,
85
- is_symlink: true,
86
- symlink_target: "/home/user/real.txt",
87
- modified_at: "2026-01-01T00:00:00Z",
88
- };
89
- mockFetch(200, { data: stat });
90
- const result = await makeFiles().stat("/home/user/link");
91
- expect(result.is_symlink).toBe(true);
92
- expect(result.symlink_target).toBe("/home/user/real.txt");
93
- });
94
- });
95
-
96
- // ── mkdir ──────────────────────────────────────────────────────────────────────
97
-
98
- describe("Files.mkdir", () => {
99
- beforeEach(() => vi.restoreAllMocks());
100
-
101
- it("should POST to /computers/:id/files/mkdir with defaults", async () => {
102
- mockFetch(201, { data: { status: "created", path: "/home/user/project" } });
103
-
104
- await makeFiles().mkdir("/home/user/project");
105
-
106
- const [url, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
107
- string,
108
- RequestInit,
109
- ];
110
- expect(url).toContain("/computers/computer-123/files/mkdir");
111
- const body = JSON.parse(init.body as string);
112
- expect(body.path).toBe("/home/user/project");
113
- expect(body.recursive).toBe(true); // default
114
- expect(body.mode).toBe("0755"); // default
115
- });
116
-
117
- it("should send correct mode when integer is passed", async () => {
118
- mockFetch(201, { data: { status: "created" } });
119
-
120
- await makeFiles().mkdir("/tmp/test", { mode: 0o700 });
121
-
122
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
123
- string,
124
- RequestInit,
125
- ];
126
- const body = JSON.parse(init.body as string);
127
- expect(body.mode).toBe("0700");
128
- });
129
-
130
- it("should respect recursive:false", async () => {
131
- mockFetch(201, { data: { status: "created" } });
132
-
133
- await makeFiles().mkdir("/tmp/strict", { recursive: false });
134
-
135
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
136
- string,
137
- RequestInit,
138
- ];
139
- const body = JSON.parse(init.body as string);
140
- expect(body.recursive).toBe(false);
141
- });
142
- });
143
-
144
- // ── rename ─────────────────────────────────────────────────────────────────────
145
-
146
- describe("Files.rename", () => {
147
- beforeEach(() => vi.restoreAllMocks());
148
-
149
- it("should POST to /computers/:id/files/rename with from and to", async () => {
150
- mockFetch(200, { data: { status: "renamed" } });
151
-
152
- await makeFiles().rename("/home/user/a.txt", "/home/user/b.txt");
153
-
154
- const [url, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
155
- string,
156
- RequestInit,
157
- ];
158
- expect(url).toContain("/files/rename");
159
- const body = JSON.parse(init.body as string);
160
- expect(body.from).toBe("/home/user/a.txt");
161
- expect(body.to).toBe("/home/user/b.txt");
162
- });
163
- });
164
-
165
- // ── copy ───────────────────────────────────────────────────────────────────────
166
-
167
- describe("Files.copy", () => {
168
- beforeEach(() => vi.restoreAllMocks());
169
-
170
- it("should POST to /computers/:id/files/copy with recursive:false by default", async () => {
171
- mockFetch(201, { data: { status: "copied" } });
172
-
173
- await makeFiles().copy("/home/user/src.txt", "/home/user/dst.txt");
174
-
175
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
176
- string,
177
- RequestInit,
178
- ];
179
- const body = JSON.parse(init.body as string);
180
- expect(body.from).toBe("/home/user/src.txt");
181
- expect(body.to).toBe("/home/user/dst.txt");
182
- expect(body.recursive).toBe(false);
183
- });
184
-
185
- it("should send recursive:true when option is set", async () => {
186
- mockFetch(201, { data: { status: "copied" } });
187
-
188
- await makeFiles().copy("/home/user/src", "/home/user/dst", {
189
- recursive: true,
190
- });
191
-
192
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
193
- string,
194
- RequestInit,
195
- ];
196
- const body = JSON.parse(init.body as string);
197
- expect(body.recursive).toBe(true);
198
- });
199
- });
200
-
201
- // ── chmod ──────────────────────────────────────────────────────────────────────
202
-
203
- describe("Files.chmod", () => {
204
- beforeEach(() => vi.restoreAllMocks());
205
-
206
- it("should POST to /computers/:id/files/chmod with octal integer", async () => {
207
- mockFetch(200, { data: { status: "ok" } });
208
-
209
- await makeFiles().chmod("/home/user/run.sh", 0o755);
210
-
211
- const [url, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
212
- string,
213
- RequestInit,
214
- ];
215
- expect(url).toContain("/files/chmod");
216
- const body = JSON.parse(init.body as string);
217
- expect(body.path).toBe("/home/user/run.sh");
218
- expect(body.mode).toBe("0755");
219
- });
220
-
221
- it("should accept a string mode directly", async () => {
222
- mockFetch(200, { data: { status: "ok" } });
223
-
224
- await makeFiles().chmod("/home/user/read.txt", "0644");
225
-
226
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
227
- string,
228
- RequestInit,
229
- ];
230
- const body = JSON.parse(init.body as string);
231
- expect(body.mode).toBe("0644");
232
- });
233
-
234
- it("should zero-pad mode < 4 digits", async () => {
235
- mockFetch(200, { data: { status: "ok" } });
236
-
237
- // 0o77 = 63 decimal → "0077"
238
- await makeFiles().chmod("/tmp/x", 0o77);
239
-
240
- const [, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
241
- string,
242
- RequestInit,
243
- ];
244
- const body = JSON.parse(init.body as string);
245
- expect(body.mode).toBe("0077");
246
- });
247
- });
248
-
249
- // ── readdir ────────────────────────────────────────────────────────────────────
250
-
251
- describe("Files.readdir", () => {
252
- beforeEach(() => vi.restoreAllMocks());
253
-
254
- const mockEntries: DirEntry[] = [
255
- {
256
- name: "main.py",
257
- is_dir: false,
258
- is_symlink: false,
259
- size: 100,
260
- modified_at: "2026-01-01T00:00:00Z",
261
- },
262
- {
263
- name: "tests",
264
- is_dir: true,
265
- is_symlink: false,
266
- size: 0,
267
- modified_at: "2026-01-01T00:00:00Z",
268
- },
269
- ];
270
-
271
- it("should GET /computers/:id/files/readdir with path query param", async () => {
272
- mockFetch(200, { data: { entries: mockEntries, path: "/workspace" } });
273
-
274
- const result = await makeFiles().readdir("/workspace");
275
-
276
- const [url] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [string];
277
- expect(url).toContain("/files/readdir");
278
- expect(url).toContain("path=%2Fworkspace");
279
- expect(result).toEqual(mockEntries);
280
- });
281
-
282
- it("should return an array of DirEntry", async () => {
283
- mockFetch(200, { data: { entries: mockEntries, path: "/workspace" } });
284
-
285
- const entries = await makeFiles().readdir("/workspace");
286
- expect(entries).toHaveLength(2);
287
- expect(entries[0]!.name).toBe("main.py");
288
- expect(entries[0]!.is_dir).toBe(false);
289
- expect(entries[1]!.name).toBe("tests");
290
- expect(entries[1]!.is_dir).toBe(true);
291
- });
292
- });
293
-
294
- // ── writeFile / readFile helpers ───────────────────────────────────────────────
295
-
296
- describe("Files.writeFile", () => {
297
- beforeEach(() => vi.restoreAllMocks());
298
-
299
- it("should delegate to upload() with base64-encoded content", async () => {
300
- mockFetch(201, {
301
- file: { path: "/home/user/hello.txt" },
302
- success: true,
303
- });
304
-
305
- await makeFiles().writeFile("/home/user/hello.txt", "hello");
306
-
307
- const [url, init] = (fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [
308
- string,
309
- RequestInit,
310
- ];
311
- expect(url).toContain("/files/write");
312
- const body = JSON.parse(init.body as string);
313
- expect(body.path).toBe("/home/user/hello.txt");
314
- expect(typeof body.content_base64).toBe("string");
315
- });
316
- });
317
-
318
- describe("Files.readFile", () => {
319
- beforeEach(() => vi.restoreAllMocks());
320
-
321
- it("should delegate to download() and decode UTF-8", async () => {
322
- const content = "print('hello')";
323
- const bytes = new TextEncoder().encode(content);
324
- vi.stubGlobal(
325
- "fetch",
326
- vi.fn().mockResolvedValue({
327
- ok: true,
328
- status: 200,
329
- headers: new Headers({ "content-type": "application/octet-stream" }),
330
- json: () => Promise.reject(new Error("not json")),
331
- arrayBuffer: () => Promise.resolve(bytes.buffer as ArrayBuffer),
332
- body: null,
333
- }),
334
- );
335
-
336
- const result = await makeFiles().readFile("/home/user/main.py");
337
- expect(result).toBe(content);
338
- });
339
- });
@@ -1,220 +0,0 @@
1
- import { HttpClient } from "../http.js";
2
- import type {
3
- CopyParams,
4
- DirEntry,
5
- DirListResult,
6
- FileDeleteParams,
7
- FileEntry,
8
- FileExportParams,
9
- FileExportResult,
10
- FileListResult,
11
- FileStat,
12
- MkdirParams,
13
- } from "../types.js";
14
-
15
- function toBase64(bytes: Uint8Array): string {
16
- // Works on both Node (Buffer available) and browsers/Deno/Bun (btoa).
17
- const maybeBuffer = (
18
- globalThis as {
19
- Buffer?: { from(b: Uint8Array): { toString(e: string): string } };
20
- }
21
- ).Buffer;
22
- if (maybeBuffer) return maybeBuffer.from(bytes).toString("base64");
23
- let bin = "";
24
- for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);
25
- return btoa(bin);
26
- }
27
-
28
- export class Files {
29
- private readonly http: HttpClient;
30
- private readonly computerId: string;
31
-
32
- constructor(http: HttpClient, computerId: string) {
33
- this.http = http;
34
- this.computerId = computerId;
35
- }
36
-
37
- private base(): string {
38
- return `/computers/${this.computerId}/files`;
39
- }
40
-
41
- /**
42
- * Upload a local file to the computer.
43
- *
44
- * In a Node.js environment pass a Buffer or Blob; in a browser pass a File
45
- * or Blob. The remote path is where the file will be written inside the VM.
46
- *
47
- * @param content - File content as Blob, Buffer, Uint8Array, or string.
48
- * @param remotePath - Absolute path inside the VM (e.g. /home/user/file.txt).
49
- * @param filename - Optional filename hint (defaults to basename of remotePath).
50
- */
51
- async upload(
52
- content: Blob | Uint8Array | string,
53
- remotePath: string,
54
- _filename?: string,
55
- ): Promise<FileEntry> {
56
- // The hosted API is JSON-only — it doesn't accept multipart uploads.
57
- // We send {path, content_base64} so binary payloads round-trip cleanly.
58
- let bytes: Uint8Array;
59
- if (typeof content === "string") {
60
- bytes = new TextEncoder().encode(content);
61
- } else if (content instanceof Uint8Array) {
62
- bytes = content;
63
- } else {
64
- // Blob → bytes
65
- const buf = await content.arrayBuffer();
66
- bytes = new Uint8Array(buf);
67
- }
68
-
69
- const contentBase64 = toBase64(bytes);
70
-
71
- return this.http.post<FileEntry>(`${this.base()}/write`, {
72
- path: remotePath,
73
- content_base64: contentBase64,
74
- });
75
- }
76
-
77
- /**
78
- * List files and directories at the given path inside the VM.
79
- */
80
- async list(path: string): Promise<FileListResult> {
81
- return this.http.get<FileListResult>(this.base(), { path });
82
- }
83
-
84
- /**
85
- * Download a file from the computer. Returns raw bytes.
86
- */
87
- async download(path: string): Promise<Uint8Array> {
88
- const url = `${this.base()}/download`;
89
- // Build query manually so getBinary can still use the helper
90
- return this.http.getBinary(`${url}?path=${encodeURIComponent(path)}`);
91
- }
92
-
93
- /**
94
- * Export a file from the computer and receive a temporary signed URL.
95
- */
96
- async export(path: string): Promise<FileExportResult> {
97
- const params: FileExportParams = { path };
98
- return this.http.post<FileExportResult>(`${this.base()}/export`, params);
99
- }
100
-
101
- /**
102
- * Delete a file or directory inside the computer.
103
- */
104
- async delete(path: string): Promise<void> {
105
- const params: FileDeleteParams = { path };
106
- return this.http.delete<void>(this.base(), params);
107
- }
108
-
109
- // ── Phase-7 stdlib-parity methods ──────────────────────────────────────────
110
-
111
- /**
112
- * Stat a path inside the computer. Does not follow symlinks (lstat semantics).
113
- *
114
- * Returns `{path, size, mode, is_dir, is_symlink, symlink_target?, modified_at}`.
115
- */
116
- async stat(path: string): Promise<FileStat> {
117
- const result = await this.http.post<{ data: FileStat }>(
118
- `${this.base()}/stat`,
119
- { path },
120
- );
121
- return result.data;
122
- }
123
-
124
- /**
125
- * Create a directory inside the computer.
126
- *
127
- * @param path - Absolute path to create (e.g. `/home/user/project`).
128
- * @param options - `{ recursive?: boolean, mode?: number }`. Defaults: recursive=true, mode=0o755.
129
- */
130
- async mkdir(path: string, options: MkdirParams = {}): Promise<void> {
131
- const modeStr =
132
- options.mode !== undefined
133
- ? options.mode.toString(8).padStart(4, "0")
134
- : "0755";
135
-
136
- await this.http.post<unknown>(`${this.base()}/mkdir`, {
137
- path,
138
- recursive: options.recursive ?? true,
139
- mode: modeStr,
140
- });
141
- }
142
-
143
- /**
144
- * Rename (move) a path inside the computer.
145
- *
146
- * Creates the destination parent directory if it does not exist.
147
- */
148
- async rename(from: string, to: string): Promise<void> {
149
- await this.http.post<unknown>(`${this.base()}/rename`, { from, to });
150
- }
151
-
152
- /**
153
- * Copy a file or directory inside the computer.
154
- *
155
- * @param from - Source absolute path.
156
- * @param to - Destination absolute path.
157
- * @param options - `{ recursive?: boolean }`. Required when source is a directory.
158
- */
159
- async copy(
160
- from: string,
161
- to: string,
162
- options: CopyParams = {},
163
- ): Promise<void> {
164
- await this.http.post<unknown>(`${this.base()}/copy`, {
165
- from,
166
- to,
167
- recursive: options.recursive ?? false,
168
- });
169
- }
170
-
171
- /**
172
- * Change permissions on a path inside the computer.
173
- *
174
- * @param path - Absolute path inside the VM.
175
- * @param mode - Unix mode bits as an octal integer (e.g. `0o755`) or string (`"0755"`).
176
- */
177
- async chmod(path: string, mode: number | string): Promise<void> {
178
- const modeStr =
179
- typeof mode === "number" ? mode.toString(8).padStart(4, "0") : mode;
180
- await this.http.post<unknown>(`${this.base()}/chmod`, {
181
- path,
182
- mode: modeStr,
183
- });
184
- }
185
-
186
- /**
187
- * Return a rich directory listing for the given path.
188
- *
189
- * Each entry includes `{name, is_dir, is_symlink, size, modified_at}`.
190
- * Richer than `list()` which uses `find` under the hood.
191
- *
192
- * @param path - Absolute path to list.
193
- * @param _withTypes - Kept for API symmetry; types are always returned.
194
- */
195
- async readdir(path: string, _withTypes = true): Promise<DirEntry[]> {
196
- const result = await this.http.get<DirListResult>(
197
- `${this.base()}/readdir`,
198
- { path },
199
- );
200
- return result.data.entries;
201
- }
202
-
203
- // ── Read/write text helpers ─────────────────────────────────────────────────
204
-
205
- /**
206
- * Write a UTF-8 string to a file inside the computer.
207
- * Convenience wrapper around `upload()`.
208
- */
209
- async writeFile(path: string, content: string): Promise<void> {
210
- await this.upload(content, path);
211
- }
212
-
213
- /**
214
- * Read a file from the computer and return its contents as a UTF-8 string.
215
- */
216
- async readFile(path: string): Promise<string> {
217
- const bytes = await this.download(path);
218
- return new TextDecoder().decode(bytes);
219
- }
220
- }
@@ -1,127 +0,0 @@
1
- /**
2
- * FlatCustomDomains resource — tenant-scoped custom domains at /custom-domains.
3
- *
4
- * The per-computer and per-deployment domain APIs live on those resources;
5
- * this is the flat tenant-level list view for white-label platforms managing
6
- * many domains across all resources.
7
- */
8
-
9
- import { randomUUID } from "node:crypto";
10
-
11
- import type { HttpClient } from "../http.js";
12
-
13
- // ── Branded IDs ──────────────────────────────────────────────────────────────
14
-
15
- export type CustomDomainId = string & { readonly __brand: "CustomDomainId" };
16
-
17
- // ── Resource shapes ──────────────────────────────────────────────────────────
18
-
19
- export interface CustomDomainData {
20
- id: CustomDomainId;
21
- tenant_id: string;
22
- domain: string;
23
- state?: string;
24
- resource_type?: string | null;
25
- resource_id?: string | null;
26
- verified?: boolean;
27
- verification_token?: string | null;
28
- created_at?: string;
29
- updated_at?: string;
30
- [key: string]: unknown;
31
- }
32
-
33
- // ── Request payloads ─────────────────────────────────────────────────────────
34
-
35
- export interface CustomDomainListParams {
36
- limit?: number;
37
- cursor?: string;
38
- [key: string]: string | number | boolean | undefined;
39
- }
40
-
41
- export interface CustomDomainCreateParams {
42
- domain: string;
43
- resource_type?: string;
44
- resourceType?: string;
45
- resource_id?: string;
46
- resourceId?: string;
47
- redirect_policy?: "none" | "www_to_apex" | "apex_to_www";
48
- redirectPolicy?: "none" | "www_to_apex" | "apex_to_www";
49
- idempotencyKey?: string;
50
- [key: string]: unknown;
51
- }
52
-
53
- // ── Helpers ───────────────────────────────────────────────────────────────────
54
-
55
- function unwrap<T>(payload: unknown): T {
56
- if (payload && typeof payload === "object" && "data" in (payload as object)) {
57
- return (payload as { data: T }).data;
58
- }
59
- return payload as T;
60
- }
61
-
62
- function listItems<T>(
63
- payload: unknown,
64
- candidateKeys: string[] = ["data", "domains", "items"],
65
- ): T[] {
66
- if (Array.isArray(payload)) return payload;
67
- if (!payload || typeof payload !== "object") return [];
68
- const p = payload as Record<string, unknown>;
69
- if (Array.isArray(p.data)) return p.data as T[];
70
- for (const key of candidateKeys) {
71
- if (Array.isArray(p[key])) return p[key] as T[];
72
- }
73
- return [];
74
- }
75
-
76
- function stripUndefined(
77
- input: Record<string, unknown>,
78
- ): Record<string, unknown> {
79
- return Object.fromEntries(
80
- Object.entries(input).filter(([, v]) => v !== undefined),
81
- );
82
- }
83
-
84
- function idempotencyKey(key?: string): string {
85
- return key ?? randomUUID();
86
- }
87
-
88
- // ── Main resource ─────────────────────────────────────────────────────────────
89
-
90
- export class FlatCustomDomains {
91
- constructor(private readonly http: HttpClient) {}
92
-
93
- async list(params: CustomDomainListParams = {}): Promise<CustomDomainData[]> {
94
- const query = stripUndefined({ ...params }) as Record<
95
- string,
96
- string | number | boolean | undefined
97
- >;
98
- const data = await this.http.get<unknown>("/custom-domains", query);
99
- return listItems<CustomDomainData>(data);
100
- }
101
-
102
- async create(params: CustomDomainCreateParams): Promise<CustomDomainData> {
103
- const {
104
- idempotencyKey: ikey,
105
- resourceType,
106
- resourceId,
107
- redirectPolicy,
108
- ...rest
109
- } = params;
110
- const body = stripUndefined({
111
- ...rest,
112
- resource_type: resourceType ?? rest.resource_type,
113
- resource_id: resourceId ?? rest.resource_id,
114
- redirect_policy: redirectPolicy ?? rest.redirect_policy,
115
- });
116
- const data = await this.http.request<unknown>("/custom-domains", {
117
- method: "POST",
118
- body,
119
- headers: { "Idempotency-Key": idempotencyKey(ikey) },
120
- });
121
- return unwrap<CustomDomainData>(data);
122
- }
123
-
124
- async delete(domainId: string): Promise<void> {
125
- await this.http.delete<unknown>(`/custom-domains/${domainId}`);
126
- }
127
- }