@miosa/sdk 1.2.3 → 1.2.5
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 +63 -39
- package/dist/index.d.ts +201 -182
- package/dist/index.js +433 -309
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +5 -5
- package/src/index.ts +16 -11
- package/src/resources/admin.ts +0 -11
- package/src/resources/api-keys.ts +0 -16
- package/src/resources/custom_domains.ts +1 -1
- package/src/resources/deployments.test.ts +127 -51
- package/src/resources/deployments.ts +326 -125
- package/src/resources/devices.test.ts +92 -0
- package/src/resources/devices.ts +291 -0
- package/src/resources/sandboxes.test.ts +2 -0
- package/src/resources/sandboxes.ts +4 -0
- package/src/resources/tenant.ts +19 -101
- package/src/resources/webhooks.ts +54 -39
- package/src/types.ts +5 -5
- package/src/resources/docker-deploy.test.ts +0 -102
- package/src/resources/docker-deploy.ts +0 -183
- package/src/resources/governance.test.ts +0 -355
- package/src/resources/governance.ts +0 -528
- package/src/resources/phase1.test.ts +0 -187
- package/src/resources/quotas.ts +0 -77
- package/src/resources/sandbox-processes.ts +0 -112
- package/src/resources/sandbox-shares.ts +0 -83
- package/src/resources/tenant-events.ts +0 -32
- package/src/resources/workspaces.ts +0 -285
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import * as crypto from "node:crypto";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import type { HttpClient } from "../http.js";
|
|
4
|
-
import { Sandbox } from "./sandboxes.js";
|
|
5
|
-
import { Tenant } from "./tenant.js";
|
|
6
|
-
import { verifySignature, Webhooks } from "./webhooks.js";
|
|
7
|
-
|
|
8
|
-
const mockGet = vi.fn();
|
|
9
|
-
const mockPost = vi.fn();
|
|
10
|
-
const mockPut = vi.fn();
|
|
11
|
-
const mockPatch = vi.fn();
|
|
12
|
-
const mockDelete = vi.fn();
|
|
13
|
-
const mockRequest = vi.fn();
|
|
14
|
-
|
|
15
|
-
function makeHttp(): HttpClient {
|
|
16
|
-
return {
|
|
17
|
-
get: mockGet,
|
|
18
|
-
post: mockPost,
|
|
19
|
-
put: mockPut,
|
|
20
|
-
patch: mockPatch,
|
|
21
|
-
delete: mockDelete,
|
|
22
|
-
request: mockRequest,
|
|
23
|
-
} as unknown as HttpClient;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function sandboxData(overrides: Record<string, unknown> = {}) {
|
|
27
|
-
return {
|
|
28
|
-
id: "sbx_123",
|
|
29
|
-
state: "running",
|
|
30
|
-
ready: true,
|
|
31
|
-
template_id: "miosa-sandbox",
|
|
32
|
-
...overrides,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
vi.resetAllMocks();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// ── tenant.preview_domain ────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
describe("tenant.preview_domain", () => {
|
|
43
|
-
it("get calls GET /tenant/preview-domain", async () => {
|
|
44
|
-
mockGet.mockResolvedValue({
|
|
45
|
-
preview_domain: "preview.acme.com",
|
|
46
|
-
verified_at: null,
|
|
47
|
-
});
|
|
48
|
-
const tenant = new Tenant(makeHttp());
|
|
49
|
-
const result = await tenant.preview_domain.get();
|
|
50
|
-
expect(mockGet).toHaveBeenCalledWith("/tenant/preview-domain");
|
|
51
|
-
expect(result.preview_domain).toBe("preview.acme.com");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("set calls PUT /tenant/preview-domain", async () => {
|
|
55
|
-
mockPut.mockResolvedValue({ preview_domain: "preview.acme.com" });
|
|
56
|
-
const tenant = new Tenant(makeHttp());
|
|
57
|
-
await tenant.preview_domain.set("preview.acme.com");
|
|
58
|
-
expect(mockPut).toHaveBeenCalledWith("/tenant/preview-domain", {
|
|
59
|
-
preview_domain: "preview.acme.com",
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("verify calls POST /tenant/preview-domain/verify", async () => {
|
|
64
|
-
mockPost.mockResolvedValue({
|
|
65
|
-
verified: true,
|
|
66
|
-
target: "proxy.miosa.app",
|
|
67
|
-
records: [],
|
|
68
|
-
});
|
|
69
|
-
const tenant = new Tenant(makeHttp());
|
|
70
|
-
const result = await tenant.preview_domain.verify();
|
|
71
|
-
expect(mockPost).toHaveBeenCalledWith("/tenant/preview-domain/verify", {});
|
|
72
|
-
expect(result.verified).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("delete calls DELETE /tenant/preview-domain", async () => {
|
|
76
|
-
mockDelete.mockResolvedValue(undefined);
|
|
77
|
-
const tenant = new Tenant(makeHttp());
|
|
78
|
-
await tenant.preview_domain.delete();
|
|
79
|
-
expect(mockDelete).toHaveBeenCalledWith("/tenant/preview-domain");
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// ── tenant.branding ──────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
describe("tenant.branding", () => {
|
|
86
|
-
it("get calls GET /tenant/branding", async () => {
|
|
87
|
-
mockGet.mockResolvedValue({ product_name: "Acme AI" });
|
|
88
|
-
const tenant = new Tenant(makeHttp());
|
|
89
|
-
const result = await tenant.branding.get();
|
|
90
|
-
expect(result.product_name).toBe("Acme AI");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("set calls PUT /tenant/branding", async () => {
|
|
94
|
-
const branding = { product_name: "Acme", primary_color: "#ff0000" };
|
|
95
|
-
mockPut.mockResolvedValue(branding);
|
|
96
|
-
const tenant = new Tenant(makeHttp());
|
|
97
|
-
await tenant.branding.set(branding);
|
|
98
|
-
expect(mockPut).toHaveBeenCalledWith("/tenant/branding", { branding });
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// ── sandbox.update ───────────────────────────────────────────────────────────
|
|
103
|
-
|
|
104
|
-
describe("sandbox.update", () => {
|
|
105
|
-
it("calls PATCH /sandboxes/{id} with body", async () => {
|
|
106
|
-
const updated = sandboxData({ name: "renamed" });
|
|
107
|
-
mockPatch.mockResolvedValue({ data: updated });
|
|
108
|
-
const sbx = new Sandbox(makeHttp(), sandboxData());
|
|
109
|
-
await sbx.update({ name: "renamed", slug: "my-slug" });
|
|
110
|
-
expect(mockPatch).toHaveBeenCalledWith("/sandboxes/sbx_123", {
|
|
111
|
-
name: "renamed",
|
|
112
|
-
slug: "my-slug",
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("only sends defined fields", async () => {
|
|
117
|
-
mockPatch.mockResolvedValue({ data: sandboxData() });
|
|
118
|
-
const sbx = new Sandbox(makeHttp(), sandboxData());
|
|
119
|
-
await sbx.update({ always_on: true });
|
|
120
|
-
expect(mockPatch).toHaveBeenCalledWith("/sandboxes/sbx_123", {
|
|
121
|
-
always_on: true,
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// ── sandbox.previewToken ─────────────────────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
describe("sandbox.previewToken", () => {
|
|
129
|
-
it("calls POST /sandboxes/{id}/preview-token", async () => {
|
|
130
|
-
const tokenResp = {
|
|
131
|
-
token: "tok_xyz",
|
|
132
|
-
url: "https://preview.miosa.app?t=tok_xyz",
|
|
133
|
-
expires_at: "2026-05-26T01:00:00Z",
|
|
134
|
-
scope: "read",
|
|
135
|
-
};
|
|
136
|
-
mockPost.mockResolvedValue(tokenResp);
|
|
137
|
-
const sbx = new Sandbox(makeHttp(), sandboxData());
|
|
138
|
-
const result = await sbx.previewToken(3600, "read");
|
|
139
|
-
expect(mockPost).toHaveBeenCalledWith("/sandboxes/sbx_123/preview-token", {
|
|
140
|
-
expires_in: 3600,
|
|
141
|
-
scope: "read",
|
|
142
|
-
});
|
|
143
|
-
expect(result.token).toBe("tok_xyz");
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// ── verifySignature ──────────────────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
function makeHeader(payload: Buffer, secret: string, ts?: number): string {
|
|
150
|
-
const t = ts ?? Math.floor(Date.now() / 1000);
|
|
151
|
-
const signed = Buffer.concat([Buffer.from(`${t}.`), payload]);
|
|
152
|
-
const sig = crypto.createHmac("sha256", secret).update(signed).digest("hex");
|
|
153
|
-
return `t=${t},v1=${sig}`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
describe("verifySignature", () => {
|
|
157
|
-
it("returns true for a valid signature", () => {
|
|
158
|
-
const payload = Buffer.from('{"event":"sandbox.created"}');
|
|
159
|
-
const header = makeHeader(payload, "secret123");
|
|
160
|
-
expect(verifySignature(payload, header, "secret123")).toBe(true);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("returns false for wrong secret", () => {
|
|
164
|
-
const payload = Buffer.from('{"event":"sandbox.created"}');
|
|
165
|
-
const header = makeHeader(payload, "secret123");
|
|
166
|
-
expect(verifySignature(payload, header, "wrongsecret")).toBe(false);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("throws for old timestamp", () => {
|
|
170
|
-
const payload = Buffer.from("body");
|
|
171
|
-
const old = Math.floor(Date.now() / 1000) - 400;
|
|
172
|
-
const header = makeHeader(payload, "s3cr3t", old);
|
|
173
|
-
expect(() => verifySignature(payload, header, "s3cr3t")).toThrow("too old");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("returns false for malformed header", () => {
|
|
177
|
-
expect(verifySignature(Buffer.from("body"), "malformed", "secret")).toBe(
|
|
178
|
-
false,
|
|
179
|
-
);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("Webhooks.verifySignature delegates to module function", () => {
|
|
183
|
-
const payload = Buffer.from("body");
|
|
184
|
-
const header = makeHeader(payload, "secret");
|
|
185
|
-
expect(Webhooks.verifySignature(payload, header, "secret")).toBe(true);
|
|
186
|
-
});
|
|
187
|
-
});
|
package/src/resources/quotas.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Quotas — per-external_user_id resource limits.
|
|
3
|
-
* Corresponds to: GET/PUT/DELETE /api/v1/quotas/external/{external_user_id}
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { HttpClient } from "../http.js";
|
|
7
|
-
|
|
8
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
export interface QuotaData {
|
|
11
|
-
external_user_id: string;
|
|
12
|
-
max_sandboxes?: number | null;
|
|
13
|
-
max_concurrent?: number | null;
|
|
14
|
-
max_storage_gb?: number | null;
|
|
15
|
-
max_credit_cents?: number | null;
|
|
16
|
-
usage?: {
|
|
17
|
-
sandbox_count?: number;
|
|
18
|
-
concurrent_count?: number;
|
|
19
|
-
storage_gb?: number;
|
|
20
|
-
credit_cents?: number;
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
};
|
|
23
|
-
[key: string]: unknown;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface QuotaSetParams {
|
|
27
|
-
max_sandboxes?: number;
|
|
28
|
-
max_concurrent?: number;
|
|
29
|
-
max_storage_gb?: number;
|
|
30
|
-
max_credit_cents?: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
function unwrap<T>(payload: unknown): T {
|
|
36
|
-
if (payload && typeof payload === "object" && "data" in (payload as object)) {
|
|
37
|
-
return (payload as { data: T }).data;
|
|
38
|
-
}
|
|
39
|
-
return payload as T;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function stripUndefined(
|
|
43
|
-
input: Record<string, unknown>,
|
|
44
|
-
): Record<string, unknown> {
|
|
45
|
-
return Object.fromEntries(
|
|
46
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
export class Quotas {
|
|
53
|
-
constructor(private readonly http: HttpClient) {}
|
|
54
|
-
|
|
55
|
-
/** GET /api/v1/quotas/external/{external_user_id} — current limits + usage. */
|
|
56
|
-
async get(externalUserId: string): Promise<QuotaData> {
|
|
57
|
-
return unwrap(
|
|
58
|
-
await this.http.get<unknown>(`/quotas/external/${externalUserId}`),
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** PUT /api/v1/quotas/external/{external_user_id} — set per-user limits. */
|
|
63
|
-
async set(
|
|
64
|
-
externalUserId: string,
|
|
65
|
-
params: QuotaSetParams,
|
|
66
|
-
): Promise<QuotaData> {
|
|
67
|
-
const body = stripUndefined(params as Record<string, unknown>);
|
|
68
|
-
return unwrap(
|
|
69
|
-
await this.http.put<unknown>(`/quotas/external/${externalUserId}`, body),
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** DELETE /api/v1/quotas/external/{external_user_id} — revert to tenant default. */
|
|
74
|
-
async delete(externalUserId: string): Promise<void> {
|
|
75
|
-
await this.http.delete<unknown>(`/quotas/external/${externalUserId}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SandboxProcesses — long-running process management inside a sandbox.
|
|
3
|
-
* Corresponds to: POST/GET/DELETE /api/v1/sandboxes/{id}/processes
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { HttpClient } from "../http.js";
|
|
7
|
-
|
|
8
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
export interface SandboxProcessData {
|
|
11
|
-
pid: number;
|
|
12
|
-
name?: string;
|
|
13
|
-
command: string;
|
|
14
|
-
status: "running" | "stopped" | "failed" | string;
|
|
15
|
-
started_at?: string;
|
|
16
|
-
exit_code?: number | null;
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface SandboxProcessStartParams {
|
|
21
|
-
command: string;
|
|
22
|
-
env?: Record<string, string>;
|
|
23
|
-
name?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface SandboxProcessStreamEvent {
|
|
27
|
-
stream: "stdout" | "stderr";
|
|
28
|
-
line: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
function unwrap<T>(payload: unknown): T {
|
|
34
|
-
if (payload && typeof payload === "object" && "data" in (payload as object)) {
|
|
35
|
-
return (payload as { data: T }).data;
|
|
36
|
-
}
|
|
37
|
-
return payload as T;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function listItems<T>(payload: unknown): T[] {
|
|
41
|
-
if (Array.isArray(payload)) return payload;
|
|
42
|
-
if (!payload || typeof payload !== "object") return [];
|
|
43
|
-
const p = payload as Record<string, unknown>;
|
|
44
|
-
for (const k of ["data", "processes", "items"]) {
|
|
45
|
-
if (Array.isArray(p[k])) return p[k] as T[];
|
|
46
|
-
}
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function stripUndefined(
|
|
51
|
-
input: Record<string, unknown>,
|
|
52
|
-
): Record<string, unknown> {
|
|
53
|
-
return Object.fromEntries(
|
|
54
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
export class SandboxProcesses {
|
|
61
|
-
constructor(
|
|
62
|
-
private readonly http: HttpClient,
|
|
63
|
-
private readonly sandboxId: string,
|
|
64
|
-
) {}
|
|
65
|
-
|
|
66
|
-
private base(): string {
|
|
67
|
-
return `/sandboxes/${this.sandboxId}/processes`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** POST /api/v1/sandboxes/{id}/processes — start a long-running process. */
|
|
71
|
-
async start(params: SandboxProcessStartParams): Promise<SandboxProcessData> {
|
|
72
|
-
const body = stripUndefined({
|
|
73
|
-
command: params.command,
|
|
74
|
-
env: params.env,
|
|
75
|
-
name: params.name,
|
|
76
|
-
});
|
|
77
|
-
return unwrap(await this.http.post<unknown>(this.base(), body));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** GET /api/v1/sandboxes/{id}/processes — list all processes. */
|
|
81
|
-
async list(): Promise<SandboxProcessData[]> {
|
|
82
|
-
const data = await this.http.get<unknown>(this.base());
|
|
83
|
-
return listItems<SandboxProcessData>(data);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** GET /api/v1/sandboxes/{id}/processes/{pid} — get a single process. */
|
|
87
|
-
async get(pid: number): Promise<SandboxProcessData> {
|
|
88
|
-
return unwrap(await this.http.get<unknown>(`${this.base()}/${pid}`));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** DELETE /api/v1/sandboxes/{id}/processes/{pid} — SIGTERM then SIGKILL after 5s. */
|
|
92
|
-
async stop(pid: number): Promise<void> {
|
|
93
|
-
await this.http.delete<unknown>(`${this.base()}/${pid}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** GET /api/v1/sandboxes/{id}/processes/{pid}/logs?tail=N — tail log text. */
|
|
97
|
-
async logs(pid: number, tail = 200): Promise<string> {
|
|
98
|
-
const data = await this.http.get<unknown>(`${this.base()}/${pid}/logs`, {
|
|
99
|
-
tail,
|
|
100
|
-
});
|
|
101
|
-
if (typeof data === "string") return data;
|
|
102
|
-
const d = data as Record<string, unknown>;
|
|
103
|
-
return String(d.logs ?? d.output ?? d.data ?? "");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** GET /api/v1/sandboxes/{id}/processes/{pid}/stream (SSE) — live output. */
|
|
107
|
-
stream(pid: number): AsyncIterableIterator<SandboxProcessStreamEvent> {
|
|
108
|
-
return this.http.stream<SandboxProcessStreamEvent>(
|
|
109
|
-
`${this.base()}/${pid}/stream`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SandboxShares — public read-only share URLs for a sandbox.
|
|
3
|
-
* Corresponds to: POST/GET/DELETE /api/v1/sandboxes/{id}/shares
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { HttpClient } from "../http.js";
|
|
7
|
-
|
|
8
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
export interface SandboxShareData {
|
|
11
|
-
share_id: string;
|
|
12
|
-
share_url: string;
|
|
13
|
-
expires_at?: string | null;
|
|
14
|
-
scope: string;
|
|
15
|
-
[key: string]: unknown;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface SandboxShareCreateParams {
|
|
19
|
-
expires_in?: number;
|
|
20
|
-
scope?: "read";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
function unwrap<T>(payload: unknown): T {
|
|
26
|
-
if (payload && typeof payload === "object" && "data" in (payload as object)) {
|
|
27
|
-
return (payload as { data: T }).data;
|
|
28
|
-
}
|
|
29
|
-
return payload as T;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function listItems<T>(payload: unknown): T[] {
|
|
33
|
-
if (Array.isArray(payload)) return payload;
|
|
34
|
-
if (!payload || typeof payload !== "object") return [];
|
|
35
|
-
const p = payload as Record<string, unknown>;
|
|
36
|
-
for (const k of ["data", "shares", "items"]) {
|
|
37
|
-
if (Array.isArray(p[k])) return p[k] as T[];
|
|
38
|
-
}
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function stripUndefined(
|
|
43
|
-
input: Record<string, unknown>,
|
|
44
|
-
): Record<string, unknown> {
|
|
45
|
-
return Object.fromEntries(
|
|
46
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
export class SandboxShares {
|
|
53
|
-
constructor(
|
|
54
|
-
private readonly http: HttpClient,
|
|
55
|
-
private readonly sandboxId: string,
|
|
56
|
-
) {}
|
|
57
|
-
|
|
58
|
-
private base(): string {
|
|
59
|
-
return `/sandboxes/${this.sandboxId}/shares`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** POST /api/v1/sandboxes/{id}/shares — create a public share URL. */
|
|
63
|
-
async create(
|
|
64
|
-
params: SandboxShareCreateParams = {},
|
|
65
|
-
): Promise<SandboxShareData> {
|
|
66
|
-
const body = stripUndefined({
|
|
67
|
-
expires_in: params.expires_in,
|
|
68
|
-
scope: params.scope ?? "read",
|
|
69
|
-
});
|
|
70
|
-
return unwrap(await this.http.post<unknown>(this.base(), body));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** GET /api/v1/sandboxes/{id}/shares — list all active shares. */
|
|
74
|
-
async list(): Promise<SandboxShareData[]> {
|
|
75
|
-
const data = await this.http.get<unknown>(this.base());
|
|
76
|
-
return listItems<SandboxShareData>(data);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** DELETE /api/v1/sandboxes/{id}/shares/{share_id} — revoke a share. */
|
|
80
|
-
async revoke(shareId: string): Promise<void> {
|
|
81
|
-
await this.http.delete<unknown>(`${this.base()}/${shareId}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TenantEvents — tenant-scoped SSE event stream.
|
|
3
|
-
* Corresponds to: GET /api/v1/events/stream?types=sandbox.*,webhook.delivered
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { HttpClient } from "../http.js";
|
|
7
|
-
|
|
8
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
export interface TenantStreamEvent {
|
|
11
|
-
type: string;
|
|
12
|
-
[key: string]: unknown;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
16
|
-
|
|
17
|
-
export class TenantEvents {
|
|
18
|
-
constructor(private readonly http: HttpClient) {}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* GET /api/v1/events/stream — tenant-scoped SSE event stream.
|
|
22
|
-
*
|
|
23
|
-
* @param types - Event type globs to filter. Accepts a comma-separated string
|
|
24
|
-
* or an array, e.g. `["sandbox.*", "webhook.delivered"]`.
|
|
25
|
-
* Omit to receive all event types.
|
|
26
|
-
*/
|
|
27
|
-
stream(types?: string | string[]): AsyncIterableIterator<TenantStreamEvent> {
|
|
28
|
-
const typesParam = Array.isArray(types) ? types.join(",") : types;
|
|
29
|
-
const query = typesParam ? `?types=${encodeURIComponent(typesParam)}` : "";
|
|
30
|
-
return this.http.stream<TenantStreamEvent>(`/events/stream${query}`);
|
|
31
|
-
}
|
|
32
|
-
}
|