@miosa/sdk 1.1.0 → 1.2.1
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 +1 -1
- package/dist/index.d.ts +23 -234
- package/dist/index.js +15 -280
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +0 -5
- package/src/index.ts +0 -10
- package/src/resources/api-keys.ts +1 -5
- package/src/resources/computer.ts +9 -4
- package/src/resources/cron-jobs.ts +0 -7
- package/src/resources/custom_domains.ts +1 -8
- package/src/resources/deployments.ts +2 -13
- package/src/resources/flat-custom-domains.ts +0 -7
- package/src/resources/functions.ts +0 -7
- package/src/resources/sandboxes.test.ts +10 -7
- package/src/resources/storage.ts +0 -7
- package/src/resources/tenant.ts +3 -82
- package/src/resources/volumes.ts +0 -7
- package/src/resources/webhooks.ts +0 -55
- package/src/types.ts +5 -9
- 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
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -44,7 +44,6 @@ import { Volumes } from "./resources/volumes.js";
|
|
|
44
44
|
import { Webhooks } from "./resources/webhooks.js";
|
|
45
45
|
import { WorkspaceInvites } from "./resources/workspace-invites.js";
|
|
46
46
|
import { WorkspaceMembers } from "./resources/workspace-members.js";
|
|
47
|
-
import { Workspaces } from "./resources/workspaces.js";
|
|
48
47
|
import type { MiosaClientConfig } from "./types.js";
|
|
49
48
|
|
|
50
49
|
const DEFAULT_BASE_URL = "https://api.miosa.ai/api/v1";
|
|
@@ -65,9 +64,6 @@ const DEFAULT_MAX_RETRIES = 3;
|
|
|
65
64
|
* ```
|
|
66
65
|
*/
|
|
67
66
|
export class Miosa {
|
|
68
|
-
/** Workspace CRUD — create, list, get, update, delete, and sub-resource queries. */
|
|
69
|
-
readonly workspaces: Workspaces;
|
|
70
|
-
|
|
71
67
|
/** Per-workspace user roster — list, add, update role, remove. */
|
|
72
68
|
readonly workspaceMembers: WorkspaceMembers;
|
|
73
69
|
|
|
@@ -242,7 +238,6 @@ export class Miosa {
|
|
|
242
238
|
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
243
239
|
});
|
|
244
240
|
|
|
245
|
-
this.workspaces = new Workspaces(this.http);
|
|
246
241
|
this.workspaceMembers = new WorkspaceMembers(this.http);
|
|
247
242
|
this.workspaceInvites = new WorkspaceInvites(this.http);
|
|
248
243
|
this.orgInvites = new OrgInvites(this.http);
|
package/src/index.ts
CHANGED
|
@@ -245,16 +245,6 @@ export type {
|
|
|
245
245
|
ApiKeyCreateParams,
|
|
246
246
|
} from "./resources/api-keys.js";
|
|
247
247
|
|
|
248
|
-
// Workspace CRUD
|
|
249
|
-
export { Workspaces } from "./resources/workspaces.js";
|
|
250
|
-
export type {
|
|
251
|
-
WorkspaceId as TenantWorkspaceId,
|
|
252
|
-
WorkspaceData,
|
|
253
|
-
WorkspaceCreateParams,
|
|
254
|
-
WorkspaceUpdateParams,
|
|
255
|
-
WorkspaceComputerTemplateCreateParams,
|
|
256
|
-
} from "./resources/workspaces.js";
|
|
257
|
-
|
|
258
248
|
// Members & Invites
|
|
259
249
|
export { WorkspaceMembers } from "./resources/workspace-members.js";
|
|
260
250
|
export type {
|
|
@@ -47,9 +47,6 @@ export interface ApiKeyCreateParams {
|
|
|
47
47
|
expires_at?: string;
|
|
48
48
|
expiresAt?: string;
|
|
49
49
|
idempotencyKey?: string;
|
|
50
|
-
/** Scope this key to a single workspace. Omit for a tenant-wide key. */
|
|
51
|
-
workspaceId?: string;
|
|
52
|
-
workspace_id?: string;
|
|
53
50
|
[key: string]: unknown;
|
|
54
51
|
}
|
|
55
52
|
|
|
@@ -103,11 +100,10 @@ export class ApiKeys {
|
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
async create(params: ApiKeyCreateParams): Promise<ApiKeyCreateResult> {
|
|
106
|
-
const { idempotencyKey: ikey, expiresAt,
|
|
103
|
+
const { idempotencyKey: ikey, expiresAt, ...rest } = params;
|
|
107
104
|
const body = stripUndefined({
|
|
108
105
|
...rest,
|
|
109
106
|
expires_at: expiresAt ?? rest.expires_at,
|
|
110
|
-
workspace_id: workspaceId ?? rest.workspace_id,
|
|
111
107
|
});
|
|
112
108
|
const data = await this.http.request<unknown>("/api-keys", {
|
|
113
109
|
method: "POST",
|
|
@@ -176,21 +176,26 @@ export class Computer {
|
|
|
176
176
|
* ```ts
|
|
177
177
|
* await computer.exec.bash("npm run dev &");
|
|
178
178
|
* const url = computer.previewUrl(3000);
|
|
179
|
-
* // => https://3000-<slug>.sandbox
|
|
179
|
+
* // => https://3000-<slug>.sandbox.<tenant-domain>
|
|
180
180
|
* ```
|
|
181
181
|
*
|
|
182
182
|
* Works for any TCP HTTP listener. Public (no auth required); anyone with
|
|
183
183
|
* the URL can see it. Served over the ingress proxy so it inherits the
|
|
184
|
-
*
|
|
184
|
+
* tenant's white-label preview domain — no per-sandbox certs to manage.
|
|
185
185
|
*/
|
|
186
186
|
previewUrl(port: number, path: string = "/"): string {
|
|
187
187
|
const p = path.startsWith("/") ? path : `/${path}`;
|
|
188
|
-
return `https://${port}-${this.slug}.sandbox.
|
|
188
|
+
return `https://${port}-${this.slug}.sandbox.${this.previewDomain}${p}`;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
/** Root preview URL — serves whatever is on the default app port. */
|
|
192
192
|
get publicUrl(): string {
|
|
193
|
-
return `https://${this.slug}.sandbox.
|
|
193
|
+
return `https://${this.slug}.sandbox.${this.previewDomain}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Tenant's preview/base domain, white-label aware. */
|
|
197
|
+
get previewDomain(): string {
|
|
198
|
+
return this.data.preview_domain || "miosa.app";
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
// ─── Lifecycle ─────────────────────────────────────────────────────────────
|
|
@@ -55,13 +55,6 @@ export interface CronJobCreateParams {
|
|
|
55
55
|
name: string;
|
|
56
56
|
schedule: string;
|
|
57
57
|
idempotencyKey?: string;
|
|
58
|
-
// White-label attribution
|
|
59
|
-
externalWorkspaceId?: string;
|
|
60
|
-
external_workspace_id?: string;
|
|
61
|
-
externalUserId?: string;
|
|
62
|
-
external_user_id?: string;
|
|
63
|
-
externalProjectId?: string;
|
|
64
|
-
external_project_id?: string;
|
|
65
58
|
[key: string]: unknown;
|
|
66
59
|
}
|
|
67
60
|
|
|
@@ -27,13 +27,6 @@ export interface CustomDomainData {
|
|
|
27
27
|
|
|
28
28
|
export interface CustomDomainRegisterParams {
|
|
29
29
|
fqdn: string;
|
|
30
|
-
// White-label attribution
|
|
31
|
-
externalWorkspaceId?: string;
|
|
32
|
-
external_workspace_id?: string;
|
|
33
|
-
externalUserId?: string;
|
|
34
|
-
external_user_id?: string;
|
|
35
|
-
externalProjectId?: string;
|
|
36
|
-
external_project_id?: string;
|
|
37
30
|
}
|
|
38
31
|
|
|
39
32
|
// ─── CustomDomains resource ──────────────────────────────────────────────────
|
|
@@ -48,7 +41,7 @@ export interface CustomDomainRegisterParams {
|
|
|
48
41
|
* // 1. Register the domain
|
|
49
42
|
* const domain = await computer.domains.register("app.example.com");
|
|
50
43
|
* console.log(domain.instructions);
|
|
51
|
-
* // => "Add a CNAME record: app.example.com → <slug>.sandbox
|
|
44
|
+
* // => "Add a CNAME record: app.example.com → <slug>.sandbox.<tenant-domain>"
|
|
52
45
|
*
|
|
53
46
|
* // 2. Add the CNAME in your DNS registrar, then...
|
|
54
47
|
*
|
|
@@ -95,7 +95,7 @@ export interface DeploymentData {
|
|
|
95
95
|
* with `source_sandbox_id` on the version row. Will become nullable.
|
|
96
96
|
*/
|
|
97
97
|
repo_url?: string;
|
|
98
|
-
repo_provider?: "github"
|
|
98
|
+
repo_provider?: "github";
|
|
99
99
|
branch?: string;
|
|
100
100
|
build_command?: string | null;
|
|
101
101
|
run_command?: string | null;
|
|
@@ -278,9 +278,6 @@ export interface DeploymentCreateParams extends ExternalAttribution {
|
|
|
278
278
|
autoDeploy?: boolean;
|
|
279
279
|
database?: DeploymentDatabaseRequest;
|
|
280
280
|
metadata?: Record<string, unknown>;
|
|
281
|
-
/** Pin build and runtime VMs to a specific DC. One of: "us-west", "us-east", "us-mia". */
|
|
282
|
-
target_region?: string;
|
|
283
|
-
targetRegion?: string;
|
|
284
281
|
idempotencyKey?: string;
|
|
285
282
|
}
|
|
286
283
|
|
|
@@ -620,7 +617,6 @@ export class Deployments {
|
|
|
620
617
|
auto_deploy: params.autoDeploy ?? params.auto_deploy,
|
|
621
618
|
database: params.database,
|
|
622
619
|
metadata: params.metadata,
|
|
623
|
-
target_region: params.targetRegion ?? params.target_region,
|
|
624
620
|
...attributionBody(params),
|
|
625
621
|
});
|
|
626
622
|
const data = await this.http.request<unknown>("/deployments", {
|
|
@@ -739,16 +735,9 @@ export class Deployments {
|
|
|
739
735
|
return unwrap(data) as DeploymentBuildData;
|
|
740
736
|
}
|
|
741
737
|
|
|
742
|
-
async listEnv(
|
|
743
|
-
deploymentId: string,
|
|
744
|
-
opts: { environment?: string } = {},
|
|
745
|
-
): Promise<Record<string, unknown>[]> {
|
|
746
|
-
const query = stripUndefined({ environment: opts.environment });
|
|
738
|
+
async listEnv(deploymentId: string): Promise<Record<string, unknown>[]> {
|
|
747
739
|
const data = await this.http.get<unknown>(
|
|
748
740
|
`/deployments/${deploymentId}/env`,
|
|
749
|
-
Object.keys(query).length
|
|
750
|
-
? (query as Record<string, string | number | boolean | undefined>)
|
|
751
|
-
: undefined,
|
|
752
741
|
);
|
|
753
742
|
return listItems<Record<string, unknown>>(data);
|
|
754
743
|
}
|
|
@@ -47,13 +47,6 @@ export interface CustomDomainCreateParams {
|
|
|
47
47
|
redirect_policy?: "none" | "www_to_apex" | "apex_to_www";
|
|
48
48
|
redirectPolicy?: "none" | "www_to_apex" | "apex_to_www";
|
|
49
49
|
idempotencyKey?: string;
|
|
50
|
-
// White-label attribution
|
|
51
|
-
externalWorkspaceId?: string;
|
|
52
|
-
external_workspace_id?: string;
|
|
53
|
-
externalUserId?: string;
|
|
54
|
-
external_user_id?: string;
|
|
55
|
-
externalProjectId?: string;
|
|
56
|
-
external_project_id?: string;
|
|
57
50
|
[key: string]: unknown;
|
|
58
51
|
}
|
|
59
52
|
|
|
@@ -46,13 +46,6 @@ export interface FunctionCreateParams {
|
|
|
46
46
|
timeoutSec?: number;
|
|
47
47
|
env?: Record<string, string>;
|
|
48
48
|
idempotencyKey?: string;
|
|
49
|
-
// White-label attribution
|
|
50
|
-
externalWorkspaceId?: string;
|
|
51
|
-
external_workspace_id?: string;
|
|
52
|
-
externalUserId?: string;
|
|
53
|
-
external_user_id?: string;
|
|
54
|
-
externalProjectId?: string;
|
|
55
|
-
external_project_id?: string;
|
|
56
49
|
[key: string]: unknown;
|
|
57
50
|
}
|
|
58
51
|
|
|
@@ -393,10 +393,10 @@ describe("Sandbox handle", () => {
|
|
|
393
393
|
it("pause/resume/deploy use native sandbox lifecycle endpoints", async () => {
|
|
394
394
|
mockPost
|
|
395
395
|
.mockResolvedValueOnce({ data: sandboxData({ state: "paused" }) })
|
|
396
|
-
.mockResolvedValueOnce({ data: sandboxData({ state: "running" }) })
|
|
397
|
-
|
|
396
|
+
.mockResolvedValueOnce({ data: sandboxData({ state: "running" }) });
|
|
397
|
+
mockRequest.mockResolvedValueOnce({
|
|
398
398
|
data: { deployment_id: "dep_1", url: "https://app.miosa.app" },
|
|
399
|
-
|
|
399
|
+
});
|
|
400
400
|
|
|
401
401
|
const sandbox = new Sandbox(makeHttp(), sandboxData());
|
|
402
402
|
await sandbox.pause();
|
|
@@ -413,10 +413,13 @@ describe("Sandbox handle", () => {
|
|
|
413
413
|
"/sandboxes/sbx_123/resume",
|
|
414
414
|
{},
|
|
415
415
|
);
|
|
416
|
-
expect(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
expect(mockRequest).toHaveBeenCalledWith("/sandboxes/sbx_123/deploy", {
|
|
417
|
+
method: "POST",
|
|
418
|
+
body: {
|
|
419
|
+
name: "site",
|
|
420
|
+
output_path: "/workspace/dist",
|
|
421
|
+
custom_domain: "example.com",
|
|
422
|
+
},
|
|
420
423
|
});
|
|
421
424
|
expect(deployment.deployment_id).toBe("dep_1");
|
|
422
425
|
});
|
package/src/resources/storage.ts
CHANGED
|
@@ -49,13 +49,6 @@ export interface BucketCreateParams {
|
|
|
49
49
|
visibility?: "private" | "public";
|
|
50
50
|
quota_bytes?: number;
|
|
51
51
|
public?: boolean;
|
|
52
|
-
// White-label attribution
|
|
53
|
-
externalWorkspaceId?: string;
|
|
54
|
-
external_workspace_id?: string;
|
|
55
|
-
externalUserId?: string;
|
|
56
|
-
external_user_id?: string;
|
|
57
|
-
externalProjectId?: string;
|
|
58
|
-
external_project_id?: string;
|
|
59
52
|
[key: string]: unknown;
|
|
60
53
|
}
|
|
61
54
|
|
package/src/resources/tenant.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tenant — current tenant info
|
|
2
|
+
* Tenant — current tenant info and plan/usage.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { HttpClient } from "../http.js";
|
|
@@ -14,101 +14,22 @@ export interface TenantPlan {
|
|
|
14
14
|
[key: string]: unknown;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export interface PreviewDomainData {
|
|
18
|
-
domain: string;
|
|
19
|
-
verified_at?: string | null;
|
|
20
|
-
cname_target?: string;
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface PreviewDomainVerifyResult {
|
|
25
|
-
verified: boolean;
|
|
26
|
-
target?: string;
|
|
27
|
-
records?: unknown[];
|
|
28
|
-
[key: string]: unknown;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface BrandingData {
|
|
32
|
-
product_name?: string;
|
|
33
|
-
logo_url?: string;
|
|
34
|
-
support_url?: string;
|
|
35
|
-
support_email?: string;
|
|
36
|
-
primary_color?: string;
|
|
37
|
-
background_color?: string;
|
|
38
|
-
[key: string]: unknown;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
17
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
42
18
|
|
|
43
19
|
function unwrap<T>(payload: unknown): T {
|
|
44
20
|
if (payload && typeof payload === "object") {
|
|
45
21
|
const p = payload as Record<string, unknown>;
|
|
46
|
-
for (const k of ["data", "tenant", "
|
|
22
|
+
for (const k of ["data", "tenant", "items"]) {
|
|
47
23
|
if (k in p) return p[k] as T;
|
|
48
24
|
}
|
|
49
25
|
}
|
|
50
26
|
return payload as T;
|
|
51
27
|
}
|
|
52
28
|
|
|
53
|
-
// ── Sub-resources ─────────────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
export class PreviewDomain {
|
|
56
|
-
constructor(private readonly http: HttpClient) {}
|
|
57
|
-
|
|
58
|
-
/** GET /api/v1/tenant/preview-domain → {domain, verified_at, cname_target} */
|
|
59
|
-
async get(): Promise<PreviewDomainData> {
|
|
60
|
-
return unwrap(await this.http.get<unknown>("/tenant/preview-domain"));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** PUT /api/v1/tenant/preview-domain — set the preview domain. */
|
|
64
|
-
async set(domain: string): Promise<PreviewDomainData> {
|
|
65
|
-
return unwrap(
|
|
66
|
-
await this.http.put<unknown>("/tenant/preview-domain", { domain }),
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** POST /api/v1/tenant/preview-domain/verify → {verified, target, records} */
|
|
71
|
-
async verify(): Promise<PreviewDomainVerifyResult> {
|
|
72
|
-
return unwrap(
|
|
73
|
-
await this.http.post<unknown>("/tenant/preview-domain/verify", {}),
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** DELETE /api/v1/tenant/preview-domain */
|
|
78
|
-
async delete(): Promise<void> {
|
|
79
|
-
await this.http.delete<unknown>("/tenant/preview-domain");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export class Branding {
|
|
84
|
-
constructor(private readonly http: HttpClient) {}
|
|
85
|
-
|
|
86
|
-
/** GET /api/v1/tenant/branding */
|
|
87
|
-
async get(): Promise<BrandingData> {
|
|
88
|
-
return unwrap(await this.http.get<unknown>("/tenant/branding"));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** PUT /api/v1/tenant/branding — keys: product_name, logo_url, support_url, support_email, primary_color, background_color */
|
|
92
|
-
async set(branding: BrandingData): Promise<BrandingData> {
|
|
93
|
-
return unwrap(await this.http.put<unknown>("/tenant/branding", branding));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** DELETE /api/v1/tenant/branding */
|
|
97
|
-
async delete(): Promise<void> {
|
|
98
|
-
await this.http.delete<unknown>("/tenant/branding");
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
29
|
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
103
30
|
|
|
104
31
|
export class Tenant {
|
|
105
|
-
readonly
|
|
106
|
-
readonly branding: Branding;
|
|
107
|
-
|
|
108
|
-
constructor(private readonly http: HttpClient) {
|
|
109
|
-
this.preview_domain = new PreviewDomain(http);
|
|
110
|
-
this.branding = new Branding(http);
|
|
111
|
-
}
|
|
32
|
+
constructor(private readonly http: HttpClient) {}
|
|
112
33
|
|
|
113
34
|
/** Get the current tenant's plan, limits, and live usage counters. */
|
|
114
35
|
async current(): Promise<TenantPlan> {
|
package/src/resources/volumes.ts
CHANGED
|
@@ -40,13 +40,6 @@ export interface VolumeCreateParams {
|
|
|
40
40
|
sizeGb?: number;
|
|
41
41
|
region?: string;
|
|
42
42
|
idempotencyKey?: string;
|
|
43
|
-
// White-label attribution
|
|
44
|
-
externalWorkspaceId?: string;
|
|
45
|
-
external_workspace_id?: string;
|
|
46
|
-
externalUserId?: string;
|
|
47
|
-
external_user_id?: string;
|
|
48
|
-
externalProjectId?: string;
|
|
49
|
-
external_project_id?: string;
|
|
50
43
|
[key: string]: unknown;
|
|
51
44
|
}
|
|
52
45
|
|
|
@@ -2,48 +2,10 @@
|
|
|
2
2
|
* Webhooks resource — tenant outgoing event delivery.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
6
5
|
import { randomUUID } from "node:crypto";
|
|
7
6
|
|
|
8
7
|
import type { HttpClient } from "../http.js";
|
|
9
8
|
|
|
10
|
-
const MAX_TIMESTAMP_AGE_MS = 5 * 60 * 1000;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Verify an incoming ``X-Miosa-Signature`` header.
|
|
14
|
-
* Header format: ``t=<unix_ts>,v1=<hex_hmac>``
|
|
15
|
-
*/
|
|
16
|
-
export function verifySignature(
|
|
17
|
-
payload: string | Uint8Array,
|
|
18
|
-
signatureHeader: string,
|
|
19
|
-
secret: string,
|
|
20
|
-
): boolean {
|
|
21
|
-
const parts: Record<string, string> = {};
|
|
22
|
-
for (const part of signatureHeader.split(",")) {
|
|
23
|
-
const idx = part.indexOf("=");
|
|
24
|
-
if (idx > 0) parts[part.slice(0, idx).trim()] = part.slice(idx + 1).trim();
|
|
25
|
-
}
|
|
26
|
-
const tsStr = parts["t"];
|
|
27
|
-
const sig = parts["v1"];
|
|
28
|
-
if (!tsStr || !sig) return false;
|
|
29
|
-
|
|
30
|
-
const ts = parseInt(tsStr, 10);
|
|
31
|
-
if (isNaN(ts)) return false;
|
|
32
|
-
if (Date.now() - ts * 1000 > MAX_TIMESTAMP_AGE_MS) {
|
|
33
|
-
throw new Error(`Webhook timestamp is too old: ${ts}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const body =
|
|
37
|
-
payload instanceof Uint8Array ? payload : Buffer.from(payload, "utf-8");
|
|
38
|
-
const signed = Buffer.concat([Buffer.from(`${tsStr}.`, "utf-8"), body]);
|
|
39
|
-
const expected = createHmac("sha256", secret).update(signed).digest("hex");
|
|
40
|
-
|
|
41
|
-
return timingSafeEqual(
|
|
42
|
-
Buffer.from(expected, "utf-8"),
|
|
43
|
-
Buffer.from(sig, "utf-8"),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
9
|
// ── Branded IDs ──────────────────────────────────────────────────────────────
|
|
48
10
|
|
|
49
11
|
export type WebhookId = string & { readonly __brand: "WebhookId" };
|
|
@@ -206,21 +168,4 @@ export class Webhooks {
|
|
|
206
168
|
"items",
|
|
207
169
|
]);
|
|
208
170
|
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Verify an incoming ``X-Miosa-Signature`` header.
|
|
212
|
-
*
|
|
213
|
-
* Header format: ``t=<unix_ts>,v1=<hex_hmac>``
|
|
214
|
-
* HMAC body: ``<t>.<raw_payload>``
|
|
215
|
-
*
|
|
216
|
-
* Throws if timestamp is older than 5 minutes.
|
|
217
|
-
* Returns true if signature matches.
|
|
218
|
-
*/
|
|
219
|
-
static verifySignature(
|
|
220
|
-
payload: string | Uint8Array,
|
|
221
|
-
signatureHeader: string,
|
|
222
|
-
secret: string,
|
|
223
|
-
): boolean {
|
|
224
|
-
return verifySignature(payload, signatureHeader, secret);
|
|
225
|
-
}
|
|
226
171
|
}
|
package/src/types.ts
CHANGED
|
@@ -66,13 +66,6 @@ export interface ComputerCreateParams {
|
|
|
66
66
|
size?: ComputerSize;
|
|
67
67
|
visibility?: ComputerVisibility;
|
|
68
68
|
metadata?: Record<string, string>;
|
|
69
|
-
// White-label attribution
|
|
70
|
-
externalWorkspaceId?: string;
|
|
71
|
-
external_workspace_id?: string;
|
|
72
|
-
externalUserId?: string;
|
|
73
|
-
external_user_id?: string;
|
|
74
|
-
externalProjectId?: string;
|
|
75
|
-
external_project_id?: string;
|
|
76
69
|
}
|
|
77
70
|
|
|
78
71
|
export interface ComputerUpdateParams {
|
|
@@ -85,7 +78,8 @@ export interface ComputerData {
|
|
|
85
78
|
id: ComputerId;
|
|
86
79
|
name: string;
|
|
87
80
|
/**
|
|
88
|
-
* URL-safe identifier used in preview URLs:
|
|
81
|
+
* URL-safe identifier used in preview URLs:
|
|
82
|
+
* `https://{port}-{slug}.sandbox.{preview_domain}`.
|
|
89
83
|
* Falls back to the computer id when no slug is assigned.
|
|
90
84
|
*/
|
|
91
85
|
slug: string;
|
|
@@ -97,8 +91,10 @@ export interface ComputerData {
|
|
|
97
91
|
metadata: Record<string, string>;
|
|
98
92
|
/** Controls who can access the HTTP preview URL. Defaults to `"public"`. */
|
|
99
93
|
visibility: ComputerVisibility;
|
|
100
|
-
/** Public ingress root, e.g. `https://<slug>.sandbox
|
|
94
|
+
/** Public ingress root, e.g. `https://<slug>.sandbox.<preview_domain>`. */
|
|
101
95
|
sandbox_url?: string;
|
|
96
|
+
/** Tenant's white-label preview/base domain, e.g. `cliniciq.com`. */
|
|
97
|
+
preview_domain?: string;
|
|
102
98
|
/** KasmVNC URL for desktop templates. */
|
|
103
99
|
desktop_url?: string;
|
|
104
100
|
created_at: string;
|