@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.
- package/README.md +75 -3
- package/dist/index.d.ts +323 -175
- package/dist/index.js +441 -386
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/client.ts +0 -293
- package/src/errors.ts +0 -136
- package/src/http.test.ts +0 -374
- package/src/http.ts +0 -390
- package/src/index.ts +0 -564
- package/src/resources/admin.ts +0 -359
- package/src/resources/analytics.ts +0 -60
- package/src/resources/api-keys.ts +0 -135
- package/src/resources/audit-log.ts +0 -64
- package/src/resources/benchmarks.ts +0 -104
- package/src/resources/builder-sessions.ts +0 -75
- package/src/resources/channels.ts +0 -143
- package/src/resources/checkpoints.ts +0 -225
- package/src/resources/command-center.ts +0 -73
- package/src/resources/community.ts +0 -103
- package/src/resources/completions.ts +0 -104
- package/src/resources/computer-auto-stop.ts +0 -43
- package/src/resources/computer-env.ts +0 -76
- package/src/resources/computer-logs.ts +0 -50
- package/src/resources/computer-osa.ts +0 -76
- package/src/resources/computer-ports.ts +0 -91
- package/src/resources/computer-terminal.ts +0 -61
- package/src/resources/computer-volumes.ts +0 -64
- package/src/resources/computer.ts +0 -551
- package/src/resources/computers.ts +0 -75
- package/src/resources/credits.ts +0 -43
- package/src/resources/cron-jobs.ts +0 -191
- package/src/resources/custom_domains.ts +0 -123
- package/src/resources/dashboard.ts +0 -49
- package/src/resources/databases.ts +0 -218
- package/src/resources/deployments.test.ts +0 -194
- package/src/resources/deployments.ts +0 -1060
- package/src/resources/desktop.ts +0 -134
- package/src/resources/docker-deploy.test.ts +0 -102
- package/src/resources/docker-deploy.ts +0 -183
- package/src/resources/egress.test.ts +0 -318
- package/src/resources/egressAudit.ts +0 -245
- package/src/resources/egressNetwork.ts +0 -450
- package/src/resources/egressSecrets.ts +0 -577
- package/src/resources/email.ts +0 -212
- package/src/resources/embeddings.ts +0 -36
- package/src/resources/events.ts +0 -296
- package/src/resources/exec.ts +0 -319
- package/src/resources/external-keys.ts +0 -79
- package/src/resources/files.test.ts +0 -339
- package/src/resources/files.ts +0 -220
- package/src/resources/flat-custom-domains.ts +0 -127
- package/src/resources/functions.ts +0 -178
- package/src/resources/governance.test.ts +0 -355
- package/src/resources/governance.ts +0 -528
- package/src/resources/health-checks.ts +0 -165
- package/src/resources/integrations.ts +0 -183
- package/src/resources/mcp.ts +0 -70
- package/src/resources/models.ts +0 -45
- package/src/resources/network_policy.ts +0 -73
- package/src/resources/open-computers/agents.ts +0 -91
- package/src/resources/open-computers/apps.ts +0 -102
- package/src/resources/open-computers/clusters.ts +0 -88
- package/src/resources/open-computers/desktop.ts +0 -34
- package/src/resources/open-computers/files.ts +0 -97
- package/src/resources/open-computers/hosts.ts +0 -85
- package/src/resources/open-computers/index.ts +0 -98
- package/src/resources/open-computers/jobs.ts +0 -75
- package/src/resources/open-computers/open_computers.test.ts +0 -288
- package/src/resources/open-computers/secrets.ts +0 -115
- package/src/resources/open-computers/terminal.ts +0 -33
- package/src/resources/open-computers/tunnels.ts +0 -87
- package/src/resources/open-computers/types.ts +0 -343
- package/src/resources/open-computers/workspaces.ts +0 -135
- package/src/resources/org-invites.ts +0 -189
- package/src/resources/phase1.test.ts +0 -187
- package/src/resources/project-auth.ts +0 -142
- package/src/resources/project-integrations.ts +0 -133
- package/src/resources/provider-defaults.ts +0 -89
- package/src/resources/quotas.ts +0 -77
- package/src/resources/regions.ts +0 -94
- package/src/resources/sandbox-processes.ts +0 -112
- package/src/resources/sandbox-shares.ts +0 -83
- package/src/resources/sandbox-templates.ts +0 -195
- package/src/resources/sandboxes.live.test.ts +0 -92
- package/src/resources/sandboxes.test.ts +0 -653
- package/src/resources/sandboxes.ts +0 -1433
- package/src/resources/settings.ts +0 -143
- package/src/resources/snapshots-standalone.ts +0 -51
- package/src/resources/storage.ts +0 -221
- package/src/resources/tenant-events.ts +0 -32
- package/src/resources/tenant.ts +0 -148
- package/src/resources/usage.ts +0 -85
- package/src/resources/volumes.ts +0 -117
- package/src/resources/webhooks.ts +0 -224
- package/src/resources/workspace-invites.ts +0 -188
- package/src/resources/workspace-members.test.ts +0 -121
- package/src/resources/workspace-members.ts +0 -143
- package/src/resources/workspaces.ts +0 -285
- package/src/types.ts +0 -463
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Org Invites — email invite flow for org (tenant) membership.
|
|
3
|
-
*
|
|
4
|
-
* Public endpoints (no auth):
|
|
5
|
-
* GET /invites/:token
|
|
6
|
-
*
|
|
7
|
-
* Authenticated endpoints (admin/owner role required for create/list/revoke):
|
|
8
|
-
* POST /tenants/:id/invites
|
|
9
|
-
* GET /tenants/:id/invites
|
|
10
|
-
* DELETE /tenants/:id/invites/:invite_id
|
|
11
|
-
* POST /invites/:token/accept
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { HttpClient } from "../http.js";
|
|
15
|
-
|
|
16
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
export type OrgRole = "owner" | "admin" | "member";
|
|
19
|
-
|
|
20
|
-
export interface OrgInvite {
|
|
21
|
-
id: string;
|
|
22
|
-
tenant_id: string;
|
|
23
|
-
email: string;
|
|
24
|
-
role: OrgRole;
|
|
25
|
-
invited_by: string | null;
|
|
26
|
-
expires_at: string;
|
|
27
|
-
accepted_at: string | null;
|
|
28
|
-
created_at: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface OrgInviteCreated {
|
|
32
|
-
invite_id: string;
|
|
33
|
-
email: string;
|
|
34
|
-
role: OrgRole;
|
|
35
|
-
expires_at: string;
|
|
36
|
-
/**
|
|
37
|
-
* Full URL for the invite landing page. On white-label tenants this uses
|
|
38
|
-
* the tenant's custom domain.
|
|
39
|
-
*/
|
|
40
|
-
invite_url: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface OrgInvitePreview {
|
|
44
|
-
email: string;
|
|
45
|
-
tenant_name: string;
|
|
46
|
-
role: OrgRole;
|
|
47
|
-
expires_at: string;
|
|
48
|
-
expired: boolean;
|
|
49
|
-
accepted: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface TenantSummary {
|
|
53
|
-
id: string;
|
|
54
|
-
name: string;
|
|
55
|
-
slug: string;
|
|
56
|
-
owner_user_id: string | null;
|
|
57
|
-
plan_id: string | null;
|
|
58
|
-
plan_name: string | null;
|
|
59
|
-
settings: Record<string, unknown>;
|
|
60
|
-
inserted_at: string;
|
|
61
|
-
updated_at: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ── Request payloads ─────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
export interface CreateOrgInviteParams {
|
|
67
|
-
email: string;
|
|
68
|
-
role?: OrgRole;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ── Response shapes ───────────────────────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
export interface OrgInviteCreatedResponse {
|
|
74
|
-
data: OrgInviteCreated;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface OrgInviteListResponse {
|
|
78
|
-
data: OrgInvite[];
|
|
79
|
-
total: number;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface OrgInviteRevokeResponse {
|
|
83
|
-
invite_id: string;
|
|
84
|
-
revoked: boolean;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface OrgInvitePreviewResponse {
|
|
88
|
-
data: OrgInvitePreview;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface AcceptOrgInviteResponse {
|
|
92
|
-
accepted: boolean;
|
|
93
|
-
tenant_id: string;
|
|
94
|
-
tenant: TenantSummary | null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
98
|
-
|
|
99
|
-
export class OrgInvites {
|
|
100
|
-
constructor(private readonly http: HttpClient) {}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create an org invite and dispatch the invite email.
|
|
104
|
-
*
|
|
105
|
-
* The invite URL in the response is host-aware: on white-label tenants it
|
|
106
|
-
* uses the custom domain so the recipient lands on the branded experience.
|
|
107
|
-
* Requires `admin` or `owner` role in the tenant.
|
|
108
|
-
*
|
|
109
|
-
* `POST /tenants/:id/invites`
|
|
110
|
-
*/
|
|
111
|
-
async create(
|
|
112
|
-
tenantId: string,
|
|
113
|
-
params: CreateOrgInviteParams,
|
|
114
|
-
): Promise<OrgInviteCreated> {
|
|
115
|
-
const res = await this.http.post<OrgInviteCreatedResponse>(
|
|
116
|
-
`/tenants/${tenantId}/invites`,
|
|
117
|
-
params,
|
|
118
|
-
);
|
|
119
|
-
return res.data;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* List all pending (non-expired, non-accepted, non-revoked) org invites.
|
|
124
|
-
*
|
|
125
|
-
* Requires `admin` or `owner` role.
|
|
126
|
-
*
|
|
127
|
-
* `GET /tenants/:id/invites`
|
|
128
|
-
*/
|
|
129
|
-
async list(tenantId: string): Promise<OrgInvite[]> {
|
|
130
|
-
const res = await this.http.get<OrgInviteListResponse>(
|
|
131
|
-
`/tenants/${tenantId}/invites`,
|
|
132
|
-
);
|
|
133
|
-
return res.data ?? [];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Revoke a pending org invite.
|
|
138
|
-
*
|
|
139
|
-
* Returns `409` when the invite was already legitimately accepted.
|
|
140
|
-
* Requires `admin` or `owner` role.
|
|
141
|
-
*
|
|
142
|
-
* `DELETE /tenants/:id/invites/:invite_id`
|
|
143
|
-
*/
|
|
144
|
-
async revoke(
|
|
145
|
-
tenantId: string,
|
|
146
|
-
inviteId: string,
|
|
147
|
-
): Promise<OrgInviteRevokeResponse> {
|
|
148
|
-
return this.http.delete<OrgInviteRevokeResponse>(
|
|
149
|
-
`/tenants/${tenantId}/invites/${inviteId}`,
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Preview an org invite by token (no auth required).
|
|
155
|
-
*
|
|
156
|
-
* Returns `null` when the token is unknown or has been revoked.
|
|
157
|
-
*
|
|
158
|
-
* `GET /invites/:token`
|
|
159
|
-
*/
|
|
160
|
-
async preview(token: string): Promise<OrgInvitePreview | null> {
|
|
161
|
-
try {
|
|
162
|
-
const res = await this.http.get<OrgInvitePreviewResponse>(
|
|
163
|
-
`/invites/${token}`,
|
|
164
|
-
);
|
|
165
|
-
return res.data ?? null;
|
|
166
|
-
} catch {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Accept an org invite on behalf of the authenticated user.
|
|
173
|
-
*
|
|
174
|
-
* The caller's JWT email must match the invite email (case-insensitive).
|
|
175
|
-
* On success inserts a `tenant_members` row.
|
|
176
|
-
*
|
|
177
|
-
* Error responses:
|
|
178
|
-
* - `400` — invalid or expired token.
|
|
179
|
-
* - `422 EMAIL_MISMATCH` — JWT email does not match the invite email.
|
|
180
|
-
*
|
|
181
|
-
* `POST /invites/:token/accept`
|
|
182
|
-
*/
|
|
183
|
-
async accept(token: string): Promise<AcceptOrgInviteResponse> {
|
|
184
|
-
return this.http.post<AcceptOrgInviteResponse>(
|
|
185
|
-
`/invites/${token}/accept`,
|
|
186
|
-
{},
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
@@ -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
|
-
});
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project auth — built-in auth for sandboxes and deployments.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { HttpClient } from "../http.js";
|
|
6
|
-
|
|
7
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
export interface ProjectAuthStatus {
|
|
10
|
-
enabled?: boolean;
|
|
11
|
-
state?: string;
|
|
12
|
-
database_id?: string | null;
|
|
13
|
-
config?: Record<string, unknown>;
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// ── Request payloads ─────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
export type ProjectAuthResourceType = "sandbox" | "deployment";
|
|
20
|
-
|
|
21
|
-
export interface ProjectAuthResourceParams {
|
|
22
|
-
resourceType?: ProjectAuthResourceType;
|
|
23
|
-
resource_type?: ProjectAuthResourceType;
|
|
24
|
-
resourceId?: string;
|
|
25
|
-
resource_id?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ProjectAuthEnableParams extends ProjectAuthResourceParams {
|
|
29
|
-
signupEnabled?: boolean;
|
|
30
|
-
signup_enabled?: boolean;
|
|
31
|
-
emailConfirmRequired?: boolean;
|
|
32
|
-
email_confirm_required?: boolean;
|
|
33
|
-
tokenExpirySec?: number;
|
|
34
|
-
token_expiry_sec?: number;
|
|
35
|
-
config?: Record<string, unknown>;
|
|
36
|
-
[key: string]: unknown;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ProjectAuthUpdateParams extends ProjectAuthEnableParams {}
|
|
40
|
-
|
|
41
|
-
export interface ProjectAuthDisableParams extends ProjectAuthResourceParams {
|
|
42
|
-
[key: string]: unknown;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ProjectAuthStatusParams extends ProjectAuthResourceParams {
|
|
46
|
-
[key: string]: unknown;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function resourcePayload(params: ProjectAuthResourceParams): {
|
|
50
|
-
resource_type: ProjectAuthResourceType;
|
|
51
|
-
resource_id: string;
|
|
52
|
-
} {
|
|
53
|
-
const resource_type = params.resource_type ?? params.resourceType;
|
|
54
|
-
const resource_id = params.resource_id ?? params.resourceId;
|
|
55
|
-
|
|
56
|
-
if (!resource_type || !resource_id) {
|
|
57
|
-
throw new Error("project auth requires resourceType/resourceId");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return { resource_type, resource_id };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function authConfig(params: ProjectAuthEnableParams): Record<string, unknown> {
|
|
64
|
-
return stripUndefined({
|
|
65
|
-
...(params.config ?? {}),
|
|
66
|
-
signup_enabled: params.signup_enabled ?? params.signupEnabled,
|
|
67
|
-
email_confirm_required:
|
|
68
|
-
params.email_confirm_required ?? params.emailConfirmRequired,
|
|
69
|
-
token_expiry_sec: params.token_expiry_sec ?? params.tokenExpirySec,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function requestBody(params: ProjectAuthEnableParams): Record<string, unknown> {
|
|
74
|
-
return stripUndefined({
|
|
75
|
-
...resourcePayload(params),
|
|
76
|
-
config: authConfig(params),
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface ProjectAuthLegacyParams {
|
|
81
|
-
provider?: string;
|
|
82
|
-
config?: Record<string, unknown>;
|
|
83
|
-
[key: string]: unknown;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
function unwrap<T>(payload: unknown): T {
|
|
89
|
-
if (payload && typeof payload === "object") {
|
|
90
|
-
const p = payload as Record<string, unknown>;
|
|
91
|
-
for (const k of ["data", "project_auth", "config", "items"]) {
|
|
92
|
-
if (k in p) return p[k] as T;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return payload as T;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function stripUndefined(
|
|
99
|
-
input: Record<string, unknown>,
|
|
100
|
-
): Record<string, unknown> {
|
|
101
|
-
return Object.fromEntries(
|
|
102
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
export class ProjectAuth {
|
|
109
|
-
constructor(private readonly http: HttpClient) {}
|
|
110
|
-
|
|
111
|
-
/** Get the current project-auth status and config. */
|
|
112
|
-
async status(params: ProjectAuthStatusParams): Promise<ProjectAuthStatus> {
|
|
113
|
-
const data = await this.http.get<unknown>(
|
|
114
|
-
"/project-auth/status",
|
|
115
|
-
resourcePayload(params),
|
|
116
|
-
);
|
|
117
|
-
return unwrap<ProjectAuthStatus>(data);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Enable project auth. */
|
|
121
|
-
async enable(params: ProjectAuthEnableParams): Promise<ProjectAuthStatus> {
|
|
122
|
-
const body = requestBody(params);
|
|
123
|
-
const data = await this.http.post<unknown>("/project-auth/enable", body);
|
|
124
|
-
return unwrap<ProjectAuthStatus>(data);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Disable project auth. */
|
|
128
|
-
async disable(params: ProjectAuthDisableParams): Promise<ProjectAuthStatus> {
|
|
129
|
-
const data = await this.http.post<unknown>(
|
|
130
|
-
"/project-auth/disable",
|
|
131
|
-
resourcePayload(params),
|
|
132
|
-
);
|
|
133
|
-
return unwrap<ProjectAuthStatus>(data);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** Update project-auth configuration. */
|
|
137
|
-
async update(params: ProjectAuthUpdateParams): Promise<ProjectAuthStatus> {
|
|
138
|
-
const body = requestBody(params);
|
|
139
|
-
const data = await this.http.patch<unknown>("/project-auth/config", body);
|
|
140
|
-
return unwrap<ProjectAuthStatus>(data);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project integrations — third-party API key connections injected into VMs.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { HttpClient } from "../http.js";
|
|
6
|
-
|
|
7
|
-
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
export interface ProjectIntegrationData {
|
|
10
|
-
id?: string;
|
|
11
|
-
project_id?: string;
|
|
12
|
-
provider?: string;
|
|
13
|
-
status?: string;
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ProjectIntegrationCatalogEntry {
|
|
18
|
-
provider?: string;
|
|
19
|
-
name?: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
schema?: Record<string, unknown>;
|
|
22
|
-
[key: string]: unknown;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ── Request payloads ─────────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
export interface ProjectIntegrationListParams {
|
|
28
|
-
project_id?: string;
|
|
29
|
-
provider?: string;
|
|
30
|
-
[key: string]: string | number | boolean | undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface ProjectIntegrationCreateParams {
|
|
34
|
-
project_id?: string;
|
|
35
|
-
provider?: string;
|
|
36
|
-
credentials?: Record<string, unknown>;
|
|
37
|
-
[key: string]: unknown;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface ProjectIntegrationUpdateParams {
|
|
41
|
-
credentials?: Record<string, unknown>;
|
|
42
|
-
[key: string]: unknown;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
function unwrap<T>(payload: unknown): T {
|
|
48
|
-
if (payload && typeof payload === "object") {
|
|
49
|
-
const p = payload as Record<string, unknown>;
|
|
50
|
-
for (const k of ["data", "project_integrations", "catalog", "items"]) {
|
|
51
|
-
if (k in p) return p[k] as T;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return payload as T;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function listItems<T>(payload: unknown): T[] {
|
|
58
|
-
const result = unwrap<T[] | unknown>(payload);
|
|
59
|
-
if (Array.isArray(result)) return result;
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function stripUndefined(
|
|
64
|
-
input: Record<string, unknown>,
|
|
65
|
-
): Record<string, string | number | boolean | undefined> {
|
|
66
|
-
return Object.fromEntries(
|
|
67
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
68
|
-
) as Record<string, string | number | boolean | undefined>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function stripUndefObj(
|
|
72
|
-
input: Record<string, unknown>,
|
|
73
|
-
): Record<string, unknown> {
|
|
74
|
-
return Object.fromEntries(
|
|
75
|
-
Object.entries(input).filter(([, v]) => v !== undefined),
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
export class ProjectIntegrations {
|
|
82
|
-
constructor(private readonly http: HttpClient) {}
|
|
83
|
-
|
|
84
|
-
/** List project integrations. */
|
|
85
|
-
async list(
|
|
86
|
-
params: ProjectIntegrationListParams = {},
|
|
87
|
-
): Promise<ProjectIntegrationData[]> {
|
|
88
|
-
const query = stripUndefined(params as Record<string, unknown>);
|
|
89
|
-
const data = await this.http.get<unknown>("/project-integrations", query);
|
|
90
|
-
return listItems<ProjectIntegrationData>(data);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** List supported providers and their schemas. */
|
|
94
|
-
async catalog(): Promise<ProjectIntegrationCatalogEntry[]> {
|
|
95
|
-
const data = await this.http.get<unknown>("/project-integrations/catalog");
|
|
96
|
-
return listItems<ProjectIntegrationCatalogEntry>(data);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Get a project integration by id. */
|
|
100
|
-
async get(integrationId: string): Promise<ProjectIntegrationData> {
|
|
101
|
-
const data = await this.http.get<unknown>(
|
|
102
|
-
`/project-integrations/${integrationId}`,
|
|
103
|
-
);
|
|
104
|
-
return unwrap<ProjectIntegrationData>(data);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Create a project integration. */
|
|
108
|
-
async create(
|
|
109
|
-
params: ProjectIntegrationCreateParams,
|
|
110
|
-
): Promise<ProjectIntegrationData> {
|
|
111
|
-
const body = stripUndefObj(params as Record<string, unknown>);
|
|
112
|
-
const data = await this.http.post<unknown>("/project-integrations", body);
|
|
113
|
-
return unwrap<ProjectIntegrationData>(data);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Update a project integration. */
|
|
117
|
-
async update(
|
|
118
|
-
integrationId: string,
|
|
119
|
-
params: ProjectIntegrationUpdateParams,
|
|
120
|
-
): Promise<ProjectIntegrationData> {
|
|
121
|
-
const body = stripUndefObj(params as Record<string, unknown>);
|
|
122
|
-
const data = await this.http.patch<unknown>(
|
|
123
|
-
`/project-integrations/${integrationId}`,
|
|
124
|
-
body,
|
|
125
|
-
);
|
|
126
|
-
return unwrap<ProjectIntegrationData>(data);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Delete a project integration. */
|
|
130
|
-
async delete(integrationId: string): Promise<void> {
|
|
131
|
-
await this.http.delete<unknown>(`/project-integrations/${integrationId}`);
|
|
132
|
-
}
|
|
133
|
-
}
|