@miosa/sdk 1.2.1 → 1.2.3
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 +43 -1
- package/dist/index.d.ts +224 -8
- package/dist/index.js +398 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +5 -0
- package/src/index.ts +24 -2
- package/src/resources/admin.ts +11 -0
- package/src/resources/api-keys.ts +16 -0
- package/src/resources/custom_domains.ts +1 -1
- package/src/resources/deployments.test.ts +121 -0
- package/src/resources/deployments.ts +231 -1
- package/src/resources/docker-deploy.test.ts +102 -0
- package/src/resources/docker-deploy.ts +183 -0
- package/src/resources/governance.test.ts +355 -0
- package/src/resources/governance.ts +528 -0
- package/src/resources/phase1.test.ts +187 -0
- package/src/resources/quotas.ts +77 -0
- package/src/resources/sandbox-processes.ts +112 -0
- package/src/resources/sandbox-shares.ts +83 -0
- package/src/resources/sandboxes.test.ts +26 -0
- package/src/resources/sandboxes.ts +31 -0
- package/src/resources/tenant-events.ts +32 -0
- package/src/resources/tenant.ts +111 -2
- package/src/resources/webhooks.ts +54 -1
- package/src/resources/workspaces.ts +285 -0
- package/src/types.ts +5 -5
package/src/resources/tenant.ts
CHANGED
|
@@ -14,26 +14,135 @@ export interface TenantPlan {
|
|
|
14
14
|
[key: string]: unknown;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface PreviewDomainData {
|
|
18
|
+
preview_domain?: string | null;
|
|
19
|
+
default_domain?: string;
|
|
20
|
+
status?: string;
|
|
21
|
+
dns_status?: string;
|
|
22
|
+
cname_target?: string | null;
|
|
23
|
+
dns_instructions?: unknown;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TenantBrandingUpdateParams {
|
|
28
|
+
product_name?: string;
|
|
29
|
+
logo_url?: string;
|
|
30
|
+
support_url?: string;
|
|
31
|
+
support_email?: string;
|
|
32
|
+
primary_color?: string;
|
|
33
|
+
background_color?: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type BrandingData = TenantBrandingUpdateParams;
|
|
38
|
+
|
|
17
39
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
18
40
|
|
|
19
41
|
function unwrap<T>(payload: unknown): T {
|
|
20
42
|
if (payload && typeof payload === "object") {
|
|
21
43
|
const p = payload as Record<string, unknown>;
|
|
22
|
-
for (const k of ["data", "tenant", "items"]) {
|
|
44
|
+
for (const k of ["data", "tenant", "branding", "items"]) {
|
|
23
45
|
if (k in p) return p[k] as T;
|
|
24
46
|
}
|
|
25
47
|
}
|
|
26
48
|
return payload as T;
|
|
27
49
|
}
|
|
28
50
|
|
|
51
|
+
// ── Sub-resources ────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
class PreviewDomain {
|
|
54
|
+
constructor(private readonly http: HttpClient) {}
|
|
55
|
+
|
|
56
|
+
/** Get the tenant's white-label preview domain settings. */
|
|
57
|
+
async get(): Promise<PreviewDomainData> {
|
|
58
|
+
const data = await this.http.get<unknown>("/tenant/preview-domain");
|
|
59
|
+
return unwrap<PreviewDomainData>(data);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Set the tenant's white-label preview domain. */
|
|
63
|
+
async set(domain: string): Promise<PreviewDomainData> {
|
|
64
|
+
const data = await this.http.put<unknown>("/tenant/preview-domain", {
|
|
65
|
+
preview_domain: domain,
|
|
66
|
+
});
|
|
67
|
+
return unwrap<PreviewDomainData>(data);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Re-run DNS verification for the configured preview domain. */
|
|
71
|
+
async verify(): Promise<PreviewDomainData> {
|
|
72
|
+
const data = await this.http.post<unknown>(
|
|
73
|
+
"/tenant/preview-domain/verify",
|
|
74
|
+
{},
|
|
75
|
+
);
|
|
76
|
+
return unwrap<PreviewDomainData>(data);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Remove the tenant's custom preview domain. */
|
|
80
|
+
async delete(): Promise<void> {
|
|
81
|
+
await this.http.delete<unknown>("/tenant/preview-domain");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class Branding {
|
|
86
|
+
constructor(private readonly http: HttpClient) {}
|
|
87
|
+
|
|
88
|
+
/** Get tenant branding used by white-label hosted surfaces. */
|
|
89
|
+
async get(): Promise<BrandingData> {
|
|
90
|
+
const data = await this.http.get<unknown>("/tenant/branding");
|
|
91
|
+
return unwrap<BrandingData>(data);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Update tenant branding used by white-label hosted surfaces. */
|
|
95
|
+
async set(params: TenantBrandingUpdateParams): Promise<BrandingData> {
|
|
96
|
+
const body = Object.fromEntries(
|
|
97
|
+
Object.entries(params).filter(([, v]) => v !== undefined),
|
|
98
|
+
);
|
|
99
|
+
const data = await this.http.put<unknown>("/tenant/branding", {
|
|
100
|
+
branding: body,
|
|
101
|
+
});
|
|
102
|
+
return unwrap<BrandingData>(data);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Reset tenant branding to platform defaults. */
|
|
106
|
+
async delete(): Promise<void> {
|
|
107
|
+
await this.http.delete<unknown>("/tenant/branding");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
29
111
|
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
30
112
|
|
|
31
113
|
export class Tenant {
|
|
32
|
-
|
|
114
|
+
private readonly http: HttpClient;
|
|
115
|
+
readonly preview_domain: PreviewDomain;
|
|
116
|
+
readonly branding: Branding;
|
|
117
|
+
|
|
118
|
+
/** camelCase alias for SDK consumers that avoid snake_case properties. */
|
|
119
|
+
readonly previewDomain: PreviewDomain;
|
|
120
|
+
|
|
121
|
+
constructor(http: HttpClient) {
|
|
122
|
+
this.http = http;
|
|
123
|
+
this.preview_domain = new PreviewDomain(http);
|
|
124
|
+
this.previewDomain = this.preview_domain;
|
|
125
|
+
this.branding = new Branding(http);
|
|
126
|
+
}
|
|
33
127
|
|
|
34
128
|
/** Get the current tenant's plan, limits, and live usage counters. */
|
|
35
129
|
async current(): Promise<TenantPlan> {
|
|
36
130
|
const data = await this.http.get<unknown>("/tenant/plan");
|
|
37
131
|
return unwrap<TenantPlan>(data);
|
|
38
132
|
}
|
|
133
|
+
|
|
134
|
+
/** Convenience alias for `tenant.branding.get()`. */
|
|
135
|
+
async getBranding(): Promise<BrandingData> {
|
|
136
|
+
return this.branding.get();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Convenience alias for `tenant.branding.set(...)`. */
|
|
140
|
+
async setBranding(params: TenantBrandingUpdateParams): Promise<BrandingData> {
|
|
141
|
+
return this.branding.set(params);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Convenience alias for `tenant.branding.delete()`. */
|
|
145
|
+
async deleteBranding(): Promise<void> {
|
|
146
|
+
await this.branding.delete();
|
|
147
|
+
}
|
|
39
148
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Webhooks resource — tenant outgoing event delivery.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
6
6
|
|
|
7
7
|
import type { HttpClient } from "../http.js";
|
|
8
8
|
|
|
@@ -101,11 +101,64 @@ function idempotencyKey(key?: string): string {
|
|
|
101
101
|
return key ?? randomUUID();
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function bodyBuffer(body: Buffer | Uint8Array | string): Buffer {
|
|
105
|
+
if (Buffer.isBuffer(body)) return body;
|
|
106
|
+
if (typeof body === "string") return Buffer.from(body, "utf8");
|
|
107
|
+
return Buffer.from(body);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Verify the `Miosa-Signature` webhook header.
|
|
112
|
+
*
|
|
113
|
+
* Header format: `t=<unix_seconds>,v1=<hex_hmac>`.
|
|
114
|
+
* Signed payload: `<timestamp>.<raw_body>`.
|
|
115
|
+
*/
|
|
116
|
+
export function verifySignature(
|
|
117
|
+
body: Buffer | Uint8Array | string,
|
|
118
|
+
header: string,
|
|
119
|
+
secret: string,
|
|
120
|
+
toleranceSec = 300,
|
|
121
|
+
): boolean {
|
|
122
|
+
const parts = Object.fromEntries(
|
|
123
|
+
header
|
|
124
|
+
.split(",")
|
|
125
|
+
.map((chunk) => chunk.split("=", 2))
|
|
126
|
+
.filter(([key, value]) => key && value),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const timestamp = parts.t;
|
|
130
|
+
const received = parts.v1;
|
|
131
|
+
if (!timestamp || !received || !secret) return false;
|
|
132
|
+
|
|
133
|
+
const unixSeconds = Number(timestamp);
|
|
134
|
+
if (!Number.isFinite(unixSeconds)) return false;
|
|
135
|
+
|
|
136
|
+
const ageSec = Math.abs(Date.now() / 1000 - unixSeconds);
|
|
137
|
+
if (ageSec > toleranceSec) {
|
|
138
|
+
throw new Error("webhook timestamp too old");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const rawBody = bodyBuffer(body);
|
|
142
|
+
const signed = Buffer.concat([Buffer.from(`${timestamp}.`), rawBody]);
|
|
143
|
+
const expected = createHmac("sha256", secret).update(signed).digest("hex");
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
return timingSafeEqual(
|
|
147
|
+
Buffer.from(expected, "utf8"),
|
|
148
|
+
Buffer.from(received, "utf8"),
|
|
149
|
+
);
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
104
155
|
// ── Main resource ─────────────────────────────────────────────────────────────
|
|
105
156
|
|
|
106
157
|
export class Webhooks {
|
|
107
158
|
constructor(private readonly http: HttpClient) {}
|
|
108
159
|
|
|
160
|
+
static verifySignature = verifySignature;
|
|
161
|
+
|
|
109
162
|
async list(params: WebhookListParams = {}): Promise<WebhookData[]> {
|
|
110
163
|
const query = stripUndefined({ ...params }) as Record<
|
|
111
164
|
string,
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspaces resource — top-level tenant workspaces grouping computers.
|
|
3
|
+
*
|
|
4
|
+
* A workspace is a logical bucket of computers (handy for teams / projects).
|
|
5
|
+
* Accessed via `miosa.workspaces` on the top-level client.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const ws = await miosa.workspaces.create({ name: "prod" });
|
|
10
|
+
* const computers = await miosa.workspaces.listComputers(ws.id);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { HttpClient } from "../http.js";
|
|
15
|
+
import type { ComputerData } from "../types.js";
|
|
16
|
+
import { Computer } from "./computer.js";
|
|
17
|
+
|
|
18
|
+
// ── Branded IDs ──────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export type WorkspaceId = string & { readonly __brand: "WorkspaceId" };
|
|
21
|
+
|
|
22
|
+
// ── Resource shapes ──────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export interface WorkspaceData {
|
|
25
|
+
id: WorkspaceId;
|
|
26
|
+
tenant_id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
slug?: string | null;
|
|
29
|
+
description?: string | null;
|
|
30
|
+
metadata?: Record<string, unknown> | null;
|
|
31
|
+
settings?: Record<string, unknown> | null;
|
|
32
|
+
created_at?: string;
|
|
33
|
+
updated_at?: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Request payloads ─────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export interface WorkspaceCreateParams {
|
|
40
|
+
name: string;
|
|
41
|
+
slug?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface WorkspaceUpdateParams {
|
|
47
|
+
name?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface WorkspaceComputerTemplateCreateParams {
|
|
53
|
+
name: string;
|
|
54
|
+
templateType?: string;
|
|
55
|
+
template_type?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
function unwrap<T>(payload: unknown): T {
|
|
63
|
+
if (payload && typeof payload === "object" && "data" in (payload as object)) {
|
|
64
|
+
return (payload as { data: T }).data;
|
|
65
|
+
}
|
|
66
|
+
return payload as T;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function listItems<T>(
|
|
70
|
+
payload: unknown,
|
|
71
|
+
candidateKeys: string[] = ["data", "items"],
|
|
72
|
+
): T[] {
|
|
73
|
+
if (Array.isArray(payload)) return payload;
|
|
74
|
+
if (!payload || typeof payload !== "object") return [];
|
|
75
|
+
const p = payload as Record<string, unknown>;
|
|
76
|
+
if (Array.isArray(p.data)) return p.data as T[];
|
|
77
|
+
for (const key of candidateKeys) {
|
|
78
|
+
if (Array.isArray(p[key])) return p[key] as T[];
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── Workspaces resource ───────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
export class Workspaces {
|
|
86
|
+
private readonly http: HttpClient;
|
|
87
|
+
|
|
88
|
+
constructor(http: HttpClient) {
|
|
89
|
+
this.http = http;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create a new workspace.
|
|
94
|
+
*/
|
|
95
|
+
async create(params: WorkspaceCreateParams): Promise<WorkspaceData> {
|
|
96
|
+
const payload = await this.http.post<unknown>("/workspaces", params);
|
|
97
|
+
return unwrap<WorkspaceData>(payload);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* List all workspaces visible to the current credential.
|
|
102
|
+
*/
|
|
103
|
+
async list(): Promise<WorkspaceData[]> {
|
|
104
|
+
const payload = await this.http.get<unknown>("/workspaces");
|
|
105
|
+
if (
|
|
106
|
+
payload &&
|
|
107
|
+
typeof payload === "object" &&
|
|
108
|
+
"workspaces" in (payload as object)
|
|
109
|
+
) {
|
|
110
|
+
const p = payload as Record<string, unknown>;
|
|
111
|
+
return (p["workspaces"] as WorkspaceData[]) ?? [];
|
|
112
|
+
}
|
|
113
|
+
return listItems<WorkspaceData>(payload, ["data", "workspaces", "items"]);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get a single workspace by ID.
|
|
118
|
+
*/
|
|
119
|
+
async get(id: WorkspaceId | string): Promise<WorkspaceData> {
|
|
120
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}`);
|
|
121
|
+
return unwrap<WorkspaceData>(payload);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Update a workspace's metadata.
|
|
126
|
+
*/
|
|
127
|
+
async update(
|
|
128
|
+
id: WorkspaceId | string,
|
|
129
|
+
params: WorkspaceUpdateParams,
|
|
130
|
+
): Promise<WorkspaceData> {
|
|
131
|
+
const payload = await this.http.patch<unknown>(`/workspaces/${id}`, params);
|
|
132
|
+
return unwrap<WorkspaceData>(payload);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Delete a workspace. Does not delete member computers.
|
|
137
|
+
*/
|
|
138
|
+
async delete(id: WorkspaceId | string): Promise<void> {
|
|
139
|
+
await this.http.delete<void>(`/workspaces/${id}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Update workspace-level settings.
|
|
144
|
+
*/
|
|
145
|
+
async updateSettings(
|
|
146
|
+
id: WorkspaceId | string,
|
|
147
|
+
settings: Record<string, unknown>,
|
|
148
|
+
): Promise<WorkspaceData> {
|
|
149
|
+
const payload = await this.http.put<unknown>(
|
|
150
|
+
`/workspaces/${id}/settings`,
|
|
151
|
+
settings,
|
|
152
|
+
);
|
|
153
|
+
return unwrap<WorkspaceData>(payload);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* List all computers that belong to the given workspace.
|
|
158
|
+
*/
|
|
159
|
+
async listComputers(id: WorkspaceId | string): Promise<Computer[]> {
|
|
160
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/computers`);
|
|
161
|
+
const items = listItems<ComputerData>(payload, [
|
|
162
|
+
"data",
|
|
163
|
+
"computers",
|
|
164
|
+
"items",
|
|
165
|
+
]);
|
|
166
|
+
return items.map((d) => new Computer(this.http, d));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* List all sandboxes that belong to this workspace.
|
|
171
|
+
*/
|
|
172
|
+
async listSandboxes(
|
|
173
|
+
id: WorkspaceId | string,
|
|
174
|
+
): Promise<Record<string, unknown>[]> {
|
|
175
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/sandboxes`);
|
|
176
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
177
|
+
"data",
|
|
178
|
+
"sandboxes",
|
|
179
|
+
"items",
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* List all deployments that belong to this workspace.
|
|
185
|
+
*/
|
|
186
|
+
async listDeployments(
|
|
187
|
+
id: WorkspaceId | string,
|
|
188
|
+
): Promise<Record<string, unknown>[]> {
|
|
189
|
+
const payload = await this.http.get<unknown>(
|
|
190
|
+
`/workspaces/${id}/deployments`,
|
|
191
|
+
);
|
|
192
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
193
|
+
"data",
|
|
194
|
+
"deployments",
|
|
195
|
+
"items",
|
|
196
|
+
]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* List all managed databases that belong to this workspace.
|
|
201
|
+
*/
|
|
202
|
+
async listDatabases(
|
|
203
|
+
id: WorkspaceId | string,
|
|
204
|
+
): Promise<Record<string, unknown>[]> {
|
|
205
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/databases`);
|
|
206
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
207
|
+
"data",
|
|
208
|
+
"databases",
|
|
209
|
+
"items",
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* List all projects that belong to this workspace.
|
|
215
|
+
*/
|
|
216
|
+
async listProjects(
|
|
217
|
+
id: WorkspaceId | string,
|
|
218
|
+
): Promise<Record<string, unknown>[]> {
|
|
219
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/projects`);
|
|
220
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
221
|
+
"data",
|
|
222
|
+
"projects",
|
|
223
|
+
"items",
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Return aggregate resource stats for this workspace.
|
|
229
|
+
*/
|
|
230
|
+
async stats(id: WorkspaceId | string): Promise<Record<string, unknown>> {
|
|
231
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/stats`);
|
|
232
|
+
return unwrap<Record<string, unknown>>(payload);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Return metered usage data for this workspace.
|
|
237
|
+
*/
|
|
238
|
+
async usage(id: WorkspaceId | string): Promise<Record<string, unknown>> {
|
|
239
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/usage`);
|
|
240
|
+
return unwrap<Record<string, unknown>>(payload);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Return activity feed for this workspace.
|
|
245
|
+
*/
|
|
246
|
+
async activity(id: WorkspaceId | string): Promise<Record<string, unknown>[]> {
|
|
247
|
+
const payload = await this.http.get<unknown>(`/workspaces/${id}/activity`);
|
|
248
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
249
|
+
"data",
|
|
250
|
+
"activity",
|
|
251
|
+
"events",
|
|
252
|
+
"items",
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* List computer templates available in this workspace.
|
|
258
|
+
*/
|
|
259
|
+
async listComputerTemplates(
|
|
260
|
+
id: WorkspaceId | string,
|
|
261
|
+
): Promise<Record<string, unknown>[]> {
|
|
262
|
+
const payload = await this.http.get<unknown>(
|
|
263
|
+
`/workspaces/${id}/computer-templates`,
|
|
264
|
+
);
|
|
265
|
+
return listItems<Record<string, unknown>>(payload, [
|
|
266
|
+
"data",
|
|
267
|
+
"templates",
|
|
268
|
+
"items",
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Create a computer template scoped to this workspace.
|
|
274
|
+
*/
|
|
275
|
+
async createComputerTemplate(
|
|
276
|
+
id: WorkspaceId | string,
|
|
277
|
+
params: WorkspaceComputerTemplateCreateParams,
|
|
278
|
+
): Promise<Record<string, unknown>> {
|
|
279
|
+
const payload = await this.http.post<unknown>(
|
|
280
|
+
`/workspaces/${id}/computer-templates`,
|
|
281
|
+
params,
|
|
282
|
+
);
|
|
283
|
+
return unwrap<Record<string, unknown>>(payload);
|
|
284
|
+
}
|
|
285
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -78,9 +78,9 @@ export interface ComputerData {
|
|
|
78
78
|
id: ComputerId;
|
|
79
79
|
name: string;
|
|
80
80
|
/**
|
|
81
|
-
* URL-safe identifier used in preview URLs:
|
|
82
|
-
*
|
|
83
|
-
*
|
|
81
|
+
* URL-safe identifier used in preview URLs: `https://{port}-{slug}.sandbox.{preview_domain}`.
|
|
82
|
+
* Falls back to the computer id when no slug is assigned. The domain is the
|
|
83
|
+
* tenant's white-label `preview_domain` (server-provided) — never hardcode it.
|
|
84
84
|
*/
|
|
85
85
|
slug: string;
|
|
86
86
|
status: ComputerStatus;
|
|
@@ -91,9 +91,9 @@ export interface ComputerData {
|
|
|
91
91
|
metadata: Record<string, string>;
|
|
92
92
|
/** Controls who can access the HTTP preview URL. Defaults to `"public"`. */
|
|
93
93
|
visibility: ComputerVisibility;
|
|
94
|
-
/** Public ingress root, e.g. `https://<slug>.sandbox.<preview_domain
|
|
94
|
+
/** Public ingress root, e.g. `https://<slug>.sandbox.<preview_domain>` (server-provided). */
|
|
95
95
|
sandbox_url?: string;
|
|
96
|
-
/** Tenant's white-label preview/base domain
|
|
96
|
+
/** Tenant's white-label preview/base domain (e.g. `cliniciq.com`). Use to build preview URLs. */
|
|
97
97
|
preview_domain?: string;
|
|
98
98
|
/** KasmVNC URL for desktop templates. */
|
|
99
99
|
desktop_url?: string;
|