@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.
Files changed (81) hide show
  1. package/README.md +181 -0
  2. package/dist/index.d.ts +4689 -0
  3. package/dist/index.js +6045 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +63 -0
  6. package/src/client.ts +249 -0
  7. package/src/errors.ts +136 -0
  8. package/src/http.test.ts +374 -0
  9. package/src/http.ts +390 -0
  10. package/src/index.ts +452 -0
  11. package/src/resources/admin.ts +348 -0
  12. package/src/resources/analytics.ts +60 -0
  13. package/src/resources/api-keys.ts +119 -0
  14. package/src/resources/audit-log.ts +64 -0
  15. package/src/resources/benchmarks.ts +104 -0
  16. package/src/resources/builder-sessions.ts +75 -0
  17. package/src/resources/channels.ts +143 -0
  18. package/src/resources/checkpoints.ts +225 -0
  19. package/src/resources/command-center.ts +73 -0
  20. package/src/resources/community.ts +103 -0
  21. package/src/resources/completions.ts +104 -0
  22. package/src/resources/computer-auto-stop.ts +43 -0
  23. package/src/resources/computer-env.ts +76 -0
  24. package/src/resources/computer-logs.ts +50 -0
  25. package/src/resources/computer-osa.ts +76 -0
  26. package/src/resources/computer-ports.ts +91 -0
  27. package/src/resources/computer-terminal.ts +61 -0
  28. package/src/resources/computer-volumes.ts +64 -0
  29. package/src/resources/computer.ts +530 -0
  30. package/src/resources/computers.ts +75 -0
  31. package/src/resources/credits.ts +43 -0
  32. package/src/resources/cron-jobs.ts +191 -0
  33. package/src/resources/custom_domains.ts +123 -0
  34. package/src/resources/dashboard.ts +49 -0
  35. package/src/resources/databases.ts +218 -0
  36. package/src/resources/deployments.ts +777 -0
  37. package/src/resources/desktop.ts +134 -0
  38. package/src/resources/email.ts +212 -0
  39. package/src/resources/embeddings.ts +36 -0
  40. package/src/resources/events.ts +296 -0
  41. package/src/resources/exec.ts +319 -0
  42. package/src/resources/external-keys.ts +79 -0
  43. package/src/resources/files.test.ts +339 -0
  44. package/src/resources/files.ts +220 -0
  45. package/src/resources/flat-custom-domains.ts +127 -0
  46. package/src/resources/functions.ts +178 -0
  47. package/src/resources/health-checks.ts +165 -0
  48. package/src/resources/integrations.ts +183 -0
  49. package/src/resources/mcp.ts +70 -0
  50. package/src/resources/models.ts +45 -0
  51. package/src/resources/network_policy.ts +73 -0
  52. package/src/resources/open-computers/agents.ts +91 -0
  53. package/src/resources/open-computers/apps.ts +102 -0
  54. package/src/resources/open-computers/clusters.ts +88 -0
  55. package/src/resources/open-computers/desktop.ts +34 -0
  56. package/src/resources/open-computers/files.ts +97 -0
  57. package/src/resources/open-computers/hosts.ts +85 -0
  58. package/src/resources/open-computers/index.ts +98 -0
  59. package/src/resources/open-computers/jobs.ts +75 -0
  60. package/src/resources/open-computers/open_computers.test.ts +288 -0
  61. package/src/resources/open-computers/secrets.ts +115 -0
  62. package/src/resources/open-computers/terminal.ts +33 -0
  63. package/src/resources/open-computers/tunnels.ts +87 -0
  64. package/src/resources/open-computers/types.ts +343 -0
  65. package/src/resources/open-computers/workspaces.ts +135 -0
  66. package/src/resources/project-auth.ts +142 -0
  67. package/src/resources/project-integrations.ts +133 -0
  68. package/src/resources/provider-defaults.ts +89 -0
  69. package/src/resources/regions.ts +94 -0
  70. package/src/resources/sandbox-templates.ts +195 -0
  71. package/src/resources/sandboxes.live.test.ts +92 -0
  72. package/src/resources/sandboxes.test.ts +624 -0
  73. package/src/resources/sandboxes.ts +1173 -0
  74. package/src/resources/settings.ts +143 -0
  75. package/src/resources/snapshots-standalone.ts +51 -0
  76. package/src/resources/storage.ts +221 -0
  77. package/src/resources/tenant.ts +39 -0
  78. package/src/resources/usage.ts +85 -0
  79. package/src/resources/volumes.ts +117 -0
  80. package/src/resources/webhooks.ts +171 -0
  81. package/src/types.ts +460 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * BuilderSessions — durable, cross-device Builder UI state.
3
+ *
4
+ * Routes: /builder/sessions/*
5
+ * Accepts msk_* API keys or JWT.
6
+ * Sessions are optimal_sessions with resource_type="sandbox".
7
+ */
8
+
9
+ import type { HttpClient } from "../http.js";
10
+
11
+ function unwrap(data: unknown): Record<string, unknown> {
12
+ if (data && typeof data === "object") {
13
+ const d = data as Record<string, unknown>;
14
+ for (const k of ["data", "sessions", "items"]) {
15
+ if (k in d) return d[k] as Record<string, unknown>;
16
+ }
17
+ }
18
+ return data as Record<string, unknown>;
19
+ }
20
+
21
+ function unwrapList(data: unknown): Record<string, unknown>[] {
22
+ if (Array.isArray(data)) return data as Record<string, unknown>[];
23
+ if (data && typeof data === "object") {
24
+ const d = data as Record<string, unknown>;
25
+ for (const k of ["data", "sessions", "items"]) {
26
+ if (Array.isArray(d[k])) return d[k] as Record<string, unknown>[];
27
+ }
28
+ }
29
+ return [];
30
+ }
31
+
32
+ export interface BuilderSessionListParams {
33
+ limit?: number;
34
+ [key: string]: string | number | boolean | undefined;
35
+ }
36
+
37
+ export class BuilderSessions {
38
+ constructor(private readonly http: HttpClient) {}
39
+
40
+ async list(
41
+ params: BuilderSessionListParams = {},
42
+ ): Promise<Record<string, unknown>[]> {
43
+ const query = { limit: 50, ...params } as Record<
44
+ string,
45
+ string | number | boolean | undefined
46
+ >;
47
+ return unwrapList(await this.http.get<unknown>("/builder/sessions", query));
48
+ }
49
+
50
+ /**
51
+ * Get a single session. The platform router only exposes index +
52
+ * title-update + delete, so this filters list() client-side.
53
+ */
54
+ async get(sessionId: string): Promise<Record<string, unknown>> {
55
+ const all = await this.list();
56
+ return (
57
+ all.find((s) => (s as Record<string, unknown>).id === sessionId) ?? {}
58
+ );
59
+ }
60
+
61
+ async updateTitle(
62
+ sessionId: string,
63
+ title: string,
64
+ ): Promise<Record<string, unknown>> {
65
+ return unwrap(
66
+ await this.http.patch<unknown>(`/builder/sessions/${sessionId}/title`, {
67
+ title,
68
+ }),
69
+ );
70
+ }
71
+
72
+ async delete(sessionId: string): Promise<void> {
73
+ await this.http.delete(`/builder/sessions/${sessionId}`);
74
+ }
75
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Channels — notification preferences + per-channel enable/disable.
3
+ */
4
+
5
+ import type { HttpClient } from "../http.js";
6
+
7
+ // ── Resource shapes ──────────────────────────────────────────────────────────
8
+
9
+ export interface ChannelData {
10
+ id?: string;
11
+ type?: string;
12
+ name?: string;
13
+ enabled?: boolean;
14
+ config?: Record<string, unknown>;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ // ── Request payloads ─────────────────────────────────────────────────────────
19
+
20
+ export interface ChannelListParams {
21
+ type?: string;
22
+ enabled?: boolean;
23
+ [key: string]: string | number | boolean | undefined;
24
+ }
25
+
26
+ export interface ChannelCreateParams {
27
+ type: string;
28
+ name?: string;
29
+ config?: Record<string, unknown>;
30
+ [key: string]: unknown;
31
+ }
32
+
33
+ export interface ChannelUpdateParams {
34
+ name?: string;
35
+ config?: Record<string, unknown>;
36
+ [key: string]: unknown;
37
+ }
38
+
39
+ export interface NotificationPrefsUpdateParams {
40
+ [key: string]: unknown;
41
+ }
42
+
43
+ // ── Helpers ───────────────────────────────────────────────────────────────────
44
+
45
+ function unwrap<T>(payload: unknown): T {
46
+ if (payload && typeof payload === "object") {
47
+ const p = payload as Record<string, unknown>;
48
+ for (const k of ["data", "channels", "notifications", "items"]) {
49
+ if (k in p) return p[k] as T;
50
+ }
51
+ }
52
+ return payload as T;
53
+ }
54
+
55
+ function stripUndefined(
56
+ input: Record<string, unknown>,
57
+ ): Record<string, string | number | boolean | undefined> {
58
+ return Object.fromEntries(
59
+ Object.entries(input).filter(([, v]) => v !== undefined),
60
+ ) as Record<string, string | number | boolean | undefined>;
61
+ }
62
+
63
+ function stripUndefObj(
64
+ input: Record<string, unknown>,
65
+ ): Record<string, unknown> {
66
+ return Object.fromEntries(
67
+ Object.entries(input).filter(([, v]) => v !== undefined),
68
+ );
69
+ }
70
+
71
+ // ── Main resource ─────────────────────────────────────────────────────────────
72
+
73
+ export class Channels {
74
+ constructor(private readonly http: HttpClient) {}
75
+
76
+ /** List all channels for the tenant. */
77
+ async list(params: ChannelListParams = {}): Promise<ChannelData[]> {
78
+ const query = stripUndefined(params as Record<string, unknown>);
79
+ const data = await this.http.get<unknown>("/channels", query);
80
+ const result = unwrap<ChannelData[] | unknown>(data);
81
+ if (Array.isArray(result)) return result;
82
+ return [];
83
+ }
84
+
85
+ /** Get a single channel. */
86
+ async get(channelId: string): Promise<ChannelData> {
87
+ const data = await this.http.get<unknown>(`/channels/${channelId}`);
88
+ return unwrap<ChannelData>(data);
89
+ }
90
+
91
+ /** Create a new channel. */
92
+ async create(params: ChannelCreateParams): Promise<ChannelData> {
93
+ const body = stripUndefObj(params as Record<string, unknown>);
94
+ const data = await this.http.post<unknown>("/channels", body);
95
+ return unwrap<ChannelData>(data);
96
+ }
97
+
98
+ /** Update a channel. */
99
+ async update(
100
+ channelId: string,
101
+ params: ChannelUpdateParams,
102
+ ): Promise<ChannelData> {
103
+ const body = stripUndefObj(params as Record<string, unknown>);
104
+ const data = await this.http.patch<unknown>(`/channels/${channelId}`, body);
105
+ return unwrap<ChannelData>(data);
106
+ }
107
+
108
+ /** Delete a channel. */
109
+ async delete(channelId: string): Promise<void> {
110
+ await this.http.delete<unknown>(`/channels/${channelId}`);
111
+ }
112
+
113
+ // ── Notification preferences ───────────────────────────────────────────────
114
+
115
+ /** Get notification preferences across all channels. */
116
+ async listNotifications(): Promise<Record<string, unknown>> {
117
+ const data = await this.http.get<unknown>("/channels/notifications");
118
+ return unwrap<Record<string, unknown>>(data);
119
+ }
120
+
121
+ /** Update notification preferences. */
122
+ async updateNotifications(
123
+ params: NotificationPrefsUpdateParams,
124
+ ): Promise<Record<string, unknown>> {
125
+ const body = stripUndefObj(params as Record<string, unknown>);
126
+ const data = await this.http.put<unknown>("/channels/notifications", body);
127
+ return unwrap<Record<string, unknown>>(data);
128
+ }
129
+
130
+ /** Enable a channel. */
131
+ async enable(channelId: string): Promise<ChannelData> {
132
+ const data = await this.http.post<unknown>(`/channels/${channelId}/enable`);
133
+ return unwrap<ChannelData>(data);
134
+ }
135
+
136
+ /** Disable a channel. */
137
+ async disable(channelId: string): Promise<ChannelData> {
138
+ const data = await this.http.post<unknown>(
139
+ `/channels/${channelId}/disable`,
140
+ );
141
+ return unwrap<ChannelData>(data);
142
+ }
143
+ }
@@ -0,0 +1,225 @@
1
+ import type { HttpClient } from "../http.js";
2
+ import type { ComputerData } from "../types.js";
3
+
4
+ // ─── Types ────────────────────────────────────────────────────────────────────
5
+
6
+ export type SnapshotStatus =
7
+ | "creating"
8
+ | "uploading"
9
+ | "ready"
10
+ | "restoring"
11
+ | "failed"
12
+ | "deleted";
13
+
14
+ export interface SnapshotData {
15
+ id: string;
16
+ computer_id: string;
17
+ tenant_id: string;
18
+ comment: string | null;
19
+ status: SnapshotStatus;
20
+ state_size_bytes: number | null;
21
+ memory_size_bytes: number | null;
22
+ rootfs_size_bytes: number | null;
23
+ compressed_size_bytes: number | null;
24
+ s3_bucket: string | null;
25
+ s3_prefix: string | null;
26
+ parent_snapshot_id: string | null;
27
+ error: string | null;
28
+ created_at: string;
29
+ updated_at: string;
30
+ }
31
+
32
+ export interface SnapshotCreateParams {
33
+ /** Optional human-readable label for this checkpoint. */
34
+ comment?: string;
35
+ }
36
+
37
+ export interface SnapshotRestoreResult {
38
+ /** The newly provisioned Computer booted from this snapshot. */
39
+ data: ComputerData;
40
+ /** The source snapshot used for the restore. */
41
+ snapshot: SnapshotData;
42
+ }
43
+
44
+ export interface SnapshotListResponse {
45
+ data: SnapshotData[];
46
+ }
47
+
48
+ export type SnapshotProgressEvent = {
49
+ type: "snapshot_progress";
50
+ snapshot_id: string;
51
+ status: SnapshotStatus | string;
52
+ step?: string;
53
+ progress?: number;
54
+ error?: string;
55
+ };
56
+
57
+ // ─── Checkpoints ─────────────────────────────────────────────────────────────
58
+
59
+ /**
60
+ * Firecracker microVM checkpoint management for a Computer.
61
+ *
62
+ * Accessed via `computer.checkpoints`.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const snap = await computer.checkpoints.create({ comment: "before upgrade" });
67
+ * // ... do risky work ...
68
+ * const fresh = await computer.checkpoints.restore(snap.id);
69
+ * ```
70
+ */
71
+ export class Checkpoints {
72
+ private readonly http: HttpClient;
73
+ private readonly computerId: string;
74
+
75
+ constructor(http: HttpClient, computerId: string) {
76
+ this.http = http;
77
+ this.computerId = computerId;
78
+ }
79
+
80
+ private base(): string {
81
+ return `/computers/${this.computerId}/snapshots`;
82
+ }
83
+
84
+ /**
85
+ * Create a checkpoint of the running computer.
86
+ *
87
+ * The returned snapshot starts in `creating` status and progresses
88
+ * through `uploading` → `ready` asynchronously. Poll `get()` or subscribe
89
+ * to progress events via `onProgress` to know when it's ready.
90
+ *
91
+ * @param params - Optional `comment` label.
92
+ * @param onProgress - Optional callback fired for each SSE progress event.
93
+ * Only called if a SSE ticket is available (the `events` endpoint requires
94
+ * a prior `POST /api/v1/auth/sse-ticket` call).
95
+ */
96
+ async create(
97
+ params: SnapshotCreateParams = {},
98
+ onProgress?: (event: SnapshotProgressEvent) => void,
99
+ ): Promise<SnapshotData> {
100
+ const resp = await this.http.post<{ data: SnapshotData }>(
101
+ this.base(),
102
+ params,
103
+ );
104
+ const snap = resp.data;
105
+
106
+ if (onProgress) {
107
+ // Fire-and-forget progress subscription — caller decides how to await.
108
+ void this.subscribeProgress(snap.id, onProgress).catch(() => {
109
+ // Ignore SSE errors — the snapshot still proceeds on the server.
110
+ });
111
+ }
112
+
113
+ return snap;
114
+ }
115
+
116
+ /**
117
+ * List all non-deleted checkpoints for this computer.
118
+ */
119
+ async list(): Promise<SnapshotData[]> {
120
+ const resp = await this.http.get<SnapshotListResponse>(this.base());
121
+ return resp.data;
122
+ }
123
+
124
+ /**
125
+ * Fetch a single checkpoint by id.
126
+ */
127
+ async get(id: string): Promise<SnapshotData> {
128
+ const resp = await this.http.get<{ data: SnapshotData }>(
129
+ `${this.base()}/${id}`,
130
+ );
131
+ return resp.data;
132
+ }
133
+
134
+ /**
135
+ * Delete a checkpoint.
136
+ *
137
+ * Transitions the snapshot to `deleted` and schedules S3 cleanup on the
138
+ * server. After deletion the snapshot object is returned with
139
+ * `status: "deleted"`.
140
+ */
141
+ async delete(id: string): Promise<SnapshotData> {
142
+ const resp = await this.http.delete<{ data: SnapshotData }>(
143
+ `${this.base()}/${id}`,
144
+ );
145
+ return resp.data;
146
+ }
147
+
148
+ /**
149
+ * Restore a checkpoint onto a fresh Computer.
150
+ *
151
+ * The returned Computer starts in `provisioning` status. Use
152
+ * `computer.checkpoints.restore(id, onProgress)` to subscribe to restore
153
+ * progress events.
154
+ *
155
+ * @param id - Snapshot id to restore (must be in `ready` status).
156
+ * @param onProgress - Optional callback for SSE progress events during restore.
157
+ * @returns A `SnapshotRestoreResult` containing the new Computer and the
158
+ * source snapshot.
159
+ */
160
+ async restore(
161
+ id: string,
162
+ onProgress?: (event: SnapshotProgressEvent) => void,
163
+ ): Promise<SnapshotRestoreResult> {
164
+ const resp = await this.http.post<SnapshotRestoreResult>(
165
+ `/computers/${this.computerId}/restore/${id}`,
166
+ );
167
+
168
+ if (onProgress) {
169
+ void this.subscribeProgress(id, onProgress).catch(() => {});
170
+ }
171
+
172
+ return resp;
173
+ }
174
+
175
+ /**
176
+ * Subscribe to Server-Sent Events for a snapshot's progress.
177
+ *
178
+ * Yields `SnapshotProgressEvent` objects until the stream closes or
179
+ * `status` reaches a terminal state (`ready`, `failed`, `deleted`).
180
+ *
181
+ * Requires a valid SSE ticket obtained via
182
+ * `POST /api/v1/auth/sse-ticket` and passed as `?ticket=<token>`.
183
+ *
184
+ * @param id - Snapshot id to watch.
185
+ * @param ticket - Short-lived SSE ticket from the auth endpoint.
186
+ */
187
+ async *events(
188
+ id: string,
189
+ ticket: string,
190
+ ): AsyncIterableIterator<SnapshotProgressEvent> {
191
+ const path = `${this.base()}/${id}/events?ticket=${encodeURIComponent(ticket)}`;
192
+
193
+ for await (const event of this.http.stream<SnapshotProgressEvent>(path)) {
194
+ yield event;
195
+
196
+ if (
197
+ event.status === "ready" ||
198
+ event.status === "failed" ||
199
+ event.status === "deleted"
200
+ ) {
201
+ return;
202
+ }
203
+ }
204
+ }
205
+
206
+ // ── Private ──────────────────────────────────────────────────────────
207
+
208
+ /**
209
+ * Internal: subscribe to SSE progress events and call `cb` for each.
210
+ *
211
+ * Resolves when the stream closes. Any auth/network errors are silently
212
+ * swallowed so the caller isn't blocked on progress-tracking failures.
213
+ */
214
+ private async subscribeProgress(
215
+ id: string,
216
+ cb: (event: SnapshotProgressEvent) => void,
217
+ ): Promise<void> {
218
+ // We don't have a ticket here (the SDK caller would need to obtain one
219
+ // separately). This internal helper is a no-op placeholder — the public
220
+ // `events()` iterator is the proper SSE interface.
221
+ // Left here for future wiring once ticket issuance is unified.
222
+ void id;
223
+ void cb;
224
+ }
225
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * CommandCenter — agent fleet, orchestrations, metrics.
3
+ *
4
+ * Routes: /command-center/*
5
+ * Requires JWT or msk_u_* API key.
6
+ */
7
+
8
+ import type { HttpClient } from "../http.js";
9
+
10
+ function unwrap(data: unknown): Record<string, unknown> {
11
+ if (data && typeof data === "object") {
12
+ const d = data as Record<string, unknown>;
13
+ for (const k of [
14
+ "data",
15
+ "agents",
16
+ "running",
17
+ "metrics",
18
+ "presets",
19
+ "tiers",
20
+ "items",
21
+ ]) {
22
+ if (k in d) return d[k] as Record<string, unknown>;
23
+ }
24
+ }
25
+ return data as Record<string, unknown>;
26
+ }
27
+
28
+ function unwrapList(data: unknown): Record<string, unknown>[] {
29
+ if (Array.isArray(data)) return data as Record<string, unknown>[];
30
+ if (data && typeof data === "object") {
31
+ const d = data as Record<string, unknown>;
32
+ for (const k of ["data", "agents", "running", "presets", "items"]) {
33
+ if (Array.isArray(d[k])) return d[k] as Record<string, unknown>[];
34
+ }
35
+ }
36
+ return [];
37
+ }
38
+
39
+ export class CommandCenter {
40
+ constructor(private readonly http: HttpClient) {}
41
+
42
+ /** Top-level snapshot (GET /command-center). */
43
+ async overview(): Promise<Record<string, unknown>> {
44
+ return unwrap(await this.http.get<unknown>("/command-center"));
45
+ }
46
+
47
+ async agents(): Promise<Record<string, unknown>[]> {
48
+ return unwrapList(await this.http.get<unknown>("/command-center/agents"));
49
+ }
50
+
51
+ async runningAgents(): Promise<Record<string, unknown>[]> {
52
+ return unwrapList(
53
+ await this.http.get<unknown>("/command-center/agents/running"),
54
+ );
55
+ }
56
+
57
+ async metrics(): Promise<Record<string, unknown>> {
58
+ return unwrap(await this.http.get<unknown>("/command-center/metrics"));
59
+ }
60
+
61
+ async presets(): Promise<Record<string, unknown>[]> {
62
+ return unwrapList(await this.http.get<unknown>("/command-center/presets"));
63
+ }
64
+
65
+ async tiers(): Promise<Record<string, unknown>> {
66
+ return unwrap(await this.http.get<unknown>("/command-center/tiers"));
67
+ }
68
+
69
+ /** Stream live command-center events via SSE. */
70
+ events(): AsyncIterableIterator<Record<string, unknown>> {
71
+ return this.http.stream<Record<string, unknown>>("/command-center/events");
72
+ }
73
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Community — public template + agent catalog with install + rate.
3
+ *
4
+ * Routes: /community/*
5
+ * Requires JWT.
6
+ */
7
+
8
+ import type { HttpClient } from "../http.js";
9
+
10
+ function unwrap(data: unknown): Record<string, unknown> {
11
+ if (data && typeof data === "object") {
12
+ const d = data as Record<string, unknown>;
13
+ for (const k of ["data", "templates", "agents", "items"]) {
14
+ if (k in d) return d[k] as Record<string, unknown>;
15
+ }
16
+ }
17
+ return data as Record<string, unknown>;
18
+ }
19
+
20
+ function unwrapList(data: unknown): Record<string, unknown>[] {
21
+ if (Array.isArray(data)) return data as Record<string, unknown>[];
22
+ if (data && typeof data === "object") {
23
+ const d = data as Record<string, unknown>;
24
+ for (const k of ["data", "templates", "agents", "items"]) {
25
+ if (Array.isArray(d[k])) return d[k] as Record<string, unknown>[];
26
+ }
27
+ }
28
+ return [];
29
+ }
30
+
31
+ export class Community {
32
+ constructor(private readonly http: HttpClient) {}
33
+
34
+ // ── Agents ────────────────────────────────────────────────────────────
35
+
36
+ async listAgents(
37
+ filters: Record<string, string | number | boolean | undefined> = {},
38
+ ): Promise<Record<string, unknown>[]> {
39
+ const query = Object.fromEntries(
40
+ Object.entries(filters).filter(([, v]) => v !== undefined),
41
+ ) as Record<string, string | number | boolean | undefined>;
42
+ return unwrapList(await this.http.get<unknown>("/community/agents", query));
43
+ }
44
+
45
+ async getAgent(agentId: string): Promise<Record<string, unknown>> {
46
+ return unwrap(await this.http.get<unknown>(`/community/agents/${agentId}`));
47
+ }
48
+
49
+ // ── Templates ─────────────────────────────────────────────────────────
50
+
51
+ async listTemplates(
52
+ filters: Record<string, string | number | boolean | undefined> = {},
53
+ ): Promise<Record<string, unknown>[]> {
54
+ const query = Object.fromEntries(
55
+ Object.entries(filters).filter(([, v]) => v !== undefined),
56
+ ) as Record<string, string | number | boolean | undefined>;
57
+ return unwrapList(
58
+ await this.http.get<unknown>("/community/templates", query),
59
+ );
60
+ }
61
+
62
+ async getTemplate(templateId: string): Promise<Record<string, unknown>> {
63
+ return unwrap(
64
+ await this.http.get<unknown>(`/community/templates/${templateId}`),
65
+ );
66
+ }
67
+
68
+ /** Install a community template into the caller's tenant. */
69
+ async installTemplate(
70
+ templateId: string,
71
+ opts: Record<string, unknown> = {},
72
+ ): Promise<Record<string, unknown>> {
73
+ const body = Object.fromEntries(
74
+ Object.entries(opts).filter(([, v]) => v !== undefined),
75
+ );
76
+ return unwrap(
77
+ await this.http.post<unknown>(
78
+ `/community/templates/${templateId}/install`,
79
+ body,
80
+ ),
81
+ );
82
+ }
83
+
84
+ /** Rate a community template (1–5). */
85
+ async rateTemplate(
86
+ templateId: string,
87
+ rating: number,
88
+ opts: Record<string, unknown> = {},
89
+ ): Promise<Record<string, unknown>> {
90
+ const body = {
91
+ rating,
92
+ ...Object.fromEntries(
93
+ Object.entries(opts).filter(([, v]) => v !== undefined),
94
+ ),
95
+ };
96
+ return unwrap(
97
+ await this.http.post<unknown>(
98
+ `/community/templates/${templateId}/rate`,
99
+ body,
100
+ ),
101
+ );
102
+ }
103
+ }