@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,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
- }