@glassly/cli 2.0.0-dev.0
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 +38 -0
- package/package.json +47 -0
- package/src/api.ts +403 -0
- package/src/config.ts +24 -0
- package/src/credentials.ts +216 -0
- package/src/index.ts +871 -0
- package/src/open-browser.ts +41 -0
- package/src/signing.ts +109 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @glassly/cli
|
|
2
|
+
|
|
3
|
+
The Glassly developer CLI — the `glassly` command. Build, publish, and manage
|
|
4
|
+
Glassly miniapps against the Cloud V2 developer console.
|
|
5
|
+
|
|
6
|
+
It wraps [`@glassly/miniapp-cli`](https://www.npmjs.com/package/@glassly/miniapp-cli)
|
|
7
|
+
(the `dev` / `build` / `pack` author flow) and adds account and store operations:
|
|
8
|
+
`login`, `whoami`, `org`, `miniapps`, `releases`, and `publish`.
|
|
9
|
+
|
|
10
|
+
> **Bun-only.** This CLI ships as TypeScript and runs under [Bun](https://bun.sh)
|
|
11
|
+
> (`#!/usr/bin/env bun`). Use `bun` / `bunx`, not `npx`/Node.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add -g @glassly/cli@dev
|
|
17
|
+
glassly --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or run without installing:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bunx @glassly/cli@dev --help
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Common commands
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
glassly login # sign in to the Glassly Developer Console
|
|
30
|
+
glassly dev # local dev server with a signed Cloud V2 identity
|
|
31
|
+
glassly build # build the current miniapp
|
|
32
|
+
glassly pack # zip dist/ into a submittable release
|
|
33
|
+
glassly publish # upload + publish a release
|
|
34
|
+
glassly miniapps list # miniapps owned by your org
|
|
35
|
+
glassly releases submit # submit an uploaded release for review
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Run `glassly <command> --help` for the full option set.
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glassly/cli",
|
|
3
|
+
"version": "2.0.0-dev.0",
|
|
4
|
+
"description": "Glassly developer CLI — build, publish, and manage Glassly miniapps (Cloud V2). Bun-only.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"glassly": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"bun": ">=1.0.0"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"tag": "dev"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "bun src/index.ts",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@glassly/miniapp-cli": "0.1.0-dev.1",
|
|
25
|
+
"commander": "^14.0.2",
|
|
26
|
+
"qrcode": "^1.5.4"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/bun": "1.3.14",
|
|
30
|
+
"@types/qrcode": "^1.5.5"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"glassly",
|
|
34
|
+
"glassly",
|
|
35
|
+
"cli",
|
|
36
|
+
"miniapp"
|
|
37
|
+
],
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/tetramo-labs/glassly.git",
|
|
41
|
+
"directory": "cloud-v2/packages/cli"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/tetramo-labs/glassly/tree/dev/cloud-v2/packages/cli#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/tetramo-labs/glassly/issues"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import type { CliConfig } from "./config";
|
|
2
|
+
import type { CliCredentials } from "./credentials";
|
|
3
|
+
|
|
4
|
+
const DEVICE_AUTH_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
5
|
+
|
|
6
|
+
export type CliJwk = Record<string, unknown> & {
|
|
7
|
+
kty?: string;
|
|
8
|
+
crv?: string;
|
|
9
|
+
x?: string;
|
|
10
|
+
d?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export interface DeviceAuthorizationResponse {
|
|
14
|
+
device_code: string;
|
|
15
|
+
user_code: string;
|
|
16
|
+
verification_uri: string;
|
|
17
|
+
verification_uri_complete: string;
|
|
18
|
+
expires_in: number;
|
|
19
|
+
interval: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LoginTokenResponse {
|
|
23
|
+
access_token: string;
|
|
24
|
+
refresh_token?: string;
|
|
25
|
+
token_type?: "Bearer";
|
|
26
|
+
expires_in?: number;
|
|
27
|
+
authentication_method?: string;
|
|
28
|
+
organization_id?: string | null;
|
|
29
|
+
user: {
|
|
30
|
+
id: string;
|
|
31
|
+
email: string;
|
|
32
|
+
first_name?: string | null;
|
|
33
|
+
last_name?: string | null;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PendingDeviceAuthorization {
|
|
38
|
+
status: "pending";
|
|
39
|
+
interval?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface SlowDownDeviceAuthorization {
|
|
43
|
+
status: "slow_down";
|
|
44
|
+
interval?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DeveloperApp {
|
|
48
|
+
id: string;
|
|
49
|
+
packageName: string;
|
|
50
|
+
name: string;
|
|
51
|
+
description: string | null;
|
|
52
|
+
status: "active" | "archived" | "suspended";
|
|
53
|
+
activeRelease: DeveloperRelease | null;
|
|
54
|
+
latestRelease: DeveloperRelease | null;
|
|
55
|
+
releaseCount: number;
|
|
56
|
+
createdAt: string | null;
|
|
57
|
+
updatedAt: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface DeveloperRelease {
|
|
61
|
+
id: string;
|
|
62
|
+
version: string;
|
|
63
|
+
status: "draft" | "submitted" | "in_review" | "accepted" | "rejected" | "published" | "suspended";
|
|
64
|
+
releaseBundleAssetId: string | null;
|
|
65
|
+
bundleSha256: string | null;
|
|
66
|
+
bundleSizeBytes: number | null;
|
|
67
|
+
manifestSha256?: string | null;
|
|
68
|
+
signingKeyId?: string | null;
|
|
69
|
+
signedAt?: string | null;
|
|
70
|
+
reviewedBy?: string | null;
|
|
71
|
+
reviewNotes?: string | null;
|
|
72
|
+
createdAt: string | null;
|
|
73
|
+
updatedAt: string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DeveloperOrg {
|
|
77
|
+
id: string;
|
|
78
|
+
ownerUserId: string;
|
|
79
|
+
workosOrgId: string | null;
|
|
80
|
+
name: string;
|
|
81
|
+
packagePrefix: string;
|
|
82
|
+
packagePrefixStatus: "unverified" | "verified" | "rejected";
|
|
83
|
+
createdAt: string | null;
|
|
84
|
+
updatedAt: string | null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface DeveloperSigningKey {
|
|
88
|
+
id: string;
|
|
89
|
+
orgId: string;
|
|
90
|
+
workosUserId: string;
|
|
91
|
+
publicKeyJwk: CliJwk;
|
|
92
|
+
status: "active" | "revoked";
|
|
93
|
+
lastUsedAt: string | null;
|
|
94
|
+
createdAt: string | null;
|
|
95
|
+
updatedAt: string | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface SignedBundleMetadata {
|
|
99
|
+
signingKeyId: string;
|
|
100
|
+
signature: string;
|
|
101
|
+
payload: {
|
|
102
|
+
packageName: string;
|
|
103
|
+
version: string;
|
|
104
|
+
bundleSha256: string;
|
|
105
|
+
manifestSha256: string;
|
|
106
|
+
createdAt: string;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type PreinstallEnvironment = "debug" | "dev" | "staging" | "prod";
|
|
111
|
+
export type PreinstallPolicy = "install_once" | "keep_updated" | "mandatory";
|
|
112
|
+
|
|
113
|
+
export interface AdminUser {
|
|
114
|
+
developerId: string;
|
|
115
|
+
email: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface AdminRegistry {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
environment: PreinstallEnvironment;
|
|
122
|
+
tenantId: string | null;
|
|
123
|
+
status: "draft" | "active" | "archived";
|
|
124
|
+
activeRevisionId: string | null;
|
|
125
|
+
createdAt: string | null;
|
|
126
|
+
updatedAt: string | null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface AdminReleaseSummary {
|
|
130
|
+
id: string;
|
|
131
|
+
miniAppId?: string;
|
|
132
|
+
packageName: string;
|
|
133
|
+
displayName: string;
|
|
134
|
+
version: string;
|
|
135
|
+
status: DeveloperRelease["status"];
|
|
136
|
+
bundleSha256: string | null;
|
|
137
|
+
bundleSizeBytes: number | null;
|
|
138
|
+
createdAt: string | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface AdminRegistryRevision {
|
|
142
|
+
id: string;
|
|
143
|
+
registryId: string;
|
|
144
|
+
status: "draft" | "active" | "archived";
|
|
145
|
+
reason: string | null;
|
|
146
|
+
entries: Array<{
|
|
147
|
+
id: string;
|
|
148
|
+
miniAppId: string;
|
|
149
|
+
releaseId: string;
|
|
150
|
+
required: boolean;
|
|
151
|
+
installPolicy: PreinstallPolicy;
|
|
152
|
+
minMobileVersion: string | null;
|
|
153
|
+
maxMobileVersion: string | null;
|
|
154
|
+
priority: number;
|
|
155
|
+
}>;
|
|
156
|
+
promotedAt: string | null;
|
|
157
|
+
createdAt: string | null;
|
|
158
|
+
updatedAt: string | null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function startLogin(config: CliConfig): Promise<DeviceAuthorizationResponse> {
|
|
162
|
+
assertWorkosClientId(config);
|
|
163
|
+
const body = new URLSearchParams({ client_id: config.workosClientId });
|
|
164
|
+
const response = await fetch(`${config.workosApiBaseUrl}/user_management/authorize/device`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
167
|
+
body,
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) throw new Error(await errorMessage(response));
|
|
170
|
+
return (await response.json()) as DeviceAuthorizationResponse;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export async function pollLoginToken(
|
|
174
|
+
config: CliConfig,
|
|
175
|
+
deviceCode: string,
|
|
176
|
+
): Promise<LoginTokenResponse | PendingDeviceAuthorization | SlowDownDeviceAuthorization> {
|
|
177
|
+
assertWorkosClientId(config);
|
|
178
|
+
const body = new URLSearchParams({
|
|
179
|
+
grant_type: DEVICE_AUTH_GRANT_TYPE,
|
|
180
|
+
device_code: deviceCode,
|
|
181
|
+
client_id: config.workosClientId,
|
|
182
|
+
});
|
|
183
|
+
const response = await fetch(`${config.workosApiBaseUrl}/user_management/authenticate`, {
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
186
|
+
body,
|
|
187
|
+
});
|
|
188
|
+
if (response.status === 400 || response.status === 403) {
|
|
189
|
+
const result = await response.json().catch(() => ({})) as { error?: string; error_description?: string };
|
|
190
|
+
if (result.error === "authorization_pending") return { status: "pending" };
|
|
191
|
+
if (result.error === "slow_down") return { status: "slow_down" };
|
|
192
|
+
throw new Error(result.error_description || result.error || `HTTP ${response.status}`);
|
|
193
|
+
}
|
|
194
|
+
if (!response.ok) throw new Error(await errorMessage(response));
|
|
195
|
+
return (await response.json()) as LoginTokenResponse;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function refreshLoginToken(
|
|
199
|
+
config: CliConfig,
|
|
200
|
+
refreshToken: string,
|
|
201
|
+
organizationId?: string | null,
|
|
202
|
+
): Promise<LoginTokenResponse> {
|
|
203
|
+
assertWorkosClientId(config);
|
|
204
|
+
const body: Record<string, string> = {
|
|
205
|
+
client_id: config.workosClientId,
|
|
206
|
+
grant_type: "refresh_token",
|
|
207
|
+
refresh_token: refreshToken,
|
|
208
|
+
};
|
|
209
|
+
if (organizationId) body.organization_id = organizationId;
|
|
210
|
+
|
|
211
|
+
const response = await fetch(`${config.workosApiBaseUrl}/user_management/authenticate`, {
|
|
212
|
+
method: "POST",
|
|
213
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
214
|
+
body: JSON.stringify(body),
|
|
215
|
+
});
|
|
216
|
+
if (!response.ok) throw new Error(await errorMessage(response));
|
|
217
|
+
return (await response.json()) as LoginTokenResponse;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function listApps(credentials: CliCredentials): Promise<{ apps: DeveloperApp[] }> {
|
|
221
|
+
return coreRequest(credentials, "/api/console/apps");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function getAdminMe(credentials: CliCredentials): Promise<{ authenticated: true; admin: true; user: AdminUser | null }> {
|
|
225
|
+
return coreRequest(credentials, "/api/admin/me");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function listAdminRegistries(credentials: CliCredentials): Promise<{ registries: AdminRegistry[] }> {
|
|
229
|
+
return coreRequest(credentials, "/api/admin/preinstalled/registries");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function ensureAdminRegistry(
|
|
233
|
+
credentials: CliCredentials,
|
|
234
|
+
input: { environment: PreinstallEnvironment; name?: string; tenantId?: string | null },
|
|
235
|
+
): Promise<{ registry: AdminRegistry }> {
|
|
236
|
+
return coreRequest(credentials, "/api/admin/preinstalled/registries", {
|
|
237
|
+
method: "POST",
|
|
238
|
+
body: JSON.stringify(input),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function listAdminPreinstallReleases(
|
|
243
|
+
credentials: CliCredentials,
|
|
244
|
+
): Promise<{ releases: AdminReleaseSummary[] }> {
|
|
245
|
+
return coreRequest(credentials, "/api/admin/preinstalled/releases");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function listAdminRegistryRevisions(
|
|
249
|
+
credentials: CliCredentials,
|
|
250
|
+
registryId: string,
|
|
251
|
+
): Promise<{ revisions: AdminRegistryRevision[] }> {
|
|
252
|
+
return coreRequest(credentials, `/api/admin/preinstalled/registries/${encodeURIComponent(registryId)}/revisions`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export async function createAdminRegistryRevision(
|
|
256
|
+
credentials: CliCredentials,
|
|
257
|
+
registryId: string,
|
|
258
|
+
input: {
|
|
259
|
+
reason?: string | null;
|
|
260
|
+
entries: Array<{
|
|
261
|
+
releaseId: string;
|
|
262
|
+
required?: boolean;
|
|
263
|
+
installPolicy?: PreinstallPolicy;
|
|
264
|
+
minMobileVersion?: string | null;
|
|
265
|
+
maxMobileVersion?: string | null;
|
|
266
|
+
priority?: number;
|
|
267
|
+
}>;
|
|
268
|
+
},
|
|
269
|
+
): Promise<{ revision: AdminRegistryRevision }> {
|
|
270
|
+
return coreRequest(credentials, `/api/admin/preinstalled/registries/${encodeURIComponent(registryId)}/revisions`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
body: JSON.stringify(input),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export async function promoteAdminRegistryRevision(
|
|
277
|
+
credentials: CliCredentials,
|
|
278
|
+
registryId: string,
|
|
279
|
+
revisionId: string,
|
|
280
|
+
): Promise<{ registry: AdminRegistry; revision: AdminRegistryRevision }> {
|
|
281
|
+
return coreRequest(
|
|
282
|
+
credentials,
|
|
283
|
+
`/api/admin/preinstalled/registries/${encodeURIComponent(registryId)}/revisions/${encodeURIComponent(revisionId)}/promote`,
|
|
284
|
+
{ method: "POST" },
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function getOrg(credentials: CliCredentials): Promise<{ org: DeveloperOrg | null }> {
|
|
289
|
+
return coreRequest(credentials, "/api/console/org");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export async function upsertOrg(
|
|
293
|
+
credentials: CliCredentials,
|
|
294
|
+
input: { displayName: string; packagePrefix: string },
|
|
295
|
+
): Promise<{ org: DeveloperOrg }> {
|
|
296
|
+
return coreRequest(credentials, "/api/console/org", {
|
|
297
|
+
method: "PUT",
|
|
298
|
+
body: JSON.stringify(input),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export async function createApp(
|
|
303
|
+
credentials: CliCredentials,
|
|
304
|
+
input: { packageName: string; displayName: string; description?: string | null },
|
|
305
|
+
): Promise<{ app: DeveloperApp }> {
|
|
306
|
+
return coreRequest(credentials, "/api/console/apps", {
|
|
307
|
+
method: "POST",
|
|
308
|
+
body: JSON.stringify(input),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function deleteApp(credentials: CliCredentials, packageName: string): Promise<{ ok: true }> {
|
|
313
|
+
return coreRequest(credentials, `/api/console/apps/${encodeURIComponent(packageName)}`, {
|
|
314
|
+
method: "DELETE",
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export async function listReleases(
|
|
319
|
+
credentials: CliCredentials,
|
|
320
|
+
packageName: string,
|
|
321
|
+
): Promise<{ releases: DeveloperRelease[] }> {
|
|
322
|
+
return coreRequest(credentials, `/api/console/apps/${encodeURIComponent(packageName)}/releases`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export async function createRelease(
|
|
326
|
+
credentials: CliCredentials,
|
|
327
|
+
input: {
|
|
328
|
+
packageName: string;
|
|
329
|
+
version: string;
|
|
330
|
+
manifest: Record<string, unknown>;
|
|
331
|
+
bundleBase64: string;
|
|
332
|
+
fileName?: string;
|
|
333
|
+
signedBundle: SignedBundleMetadata;
|
|
334
|
+
},
|
|
335
|
+
): Promise<{ release: DeveloperRelease }> {
|
|
336
|
+
return coreRequest(credentials, `/api/console/apps/${encodeURIComponent(input.packageName)}/releases`, {
|
|
337
|
+
method: "POST",
|
|
338
|
+
body: JSON.stringify(input),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export async function listSigningKeys(credentials: CliCredentials): Promise<{ keys: DeveloperSigningKey[] }> {
|
|
343
|
+
return coreRequest(credentials, "/api/console/signing-keys");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export async function registerSigningKey(
|
|
347
|
+
credentials: CliCredentials,
|
|
348
|
+
input: { publicKeyJwk: CliJwk },
|
|
349
|
+
): Promise<{ key: DeveloperSigningKey }> {
|
|
350
|
+
return coreRequest(credentials, "/api/console/signing-keys", {
|
|
351
|
+
method: "POST",
|
|
352
|
+
body: JSON.stringify(input),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export async function submitRelease(
|
|
357
|
+
credentials: CliCredentials,
|
|
358
|
+
input: {
|
|
359
|
+
packageName: string;
|
|
360
|
+
releaseId: string;
|
|
361
|
+
},
|
|
362
|
+
): Promise<{ release: DeveloperRelease }> {
|
|
363
|
+
return coreRequest(
|
|
364
|
+
credentials,
|
|
365
|
+
`/api/console/apps/${encodeURIComponent(input.packageName)}/releases/${encodeURIComponent(input.releaseId)}/submit`,
|
|
366
|
+
{ method: "POST" },
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function coreRequest<T>(
|
|
371
|
+
credentials: CliCredentials,
|
|
372
|
+
path: string,
|
|
373
|
+
init?: RequestInit,
|
|
374
|
+
): Promise<T> {
|
|
375
|
+
const response = await fetch(`${credentials.coreUrl}${path}`, {
|
|
376
|
+
...init,
|
|
377
|
+
headers: {
|
|
378
|
+
accept: "application/json",
|
|
379
|
+
authorization: `Bearer ${credentials.token}`,
|
|
380
|
+
...(init?.body ? { "content-type": "application/json" } : {}),
|
|
381
|
+
...init?.headers,
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
if (!response.ok) throw new Error(await errorMessage(response));
|
|
385
|
+
return await response.json() as T;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function assertWorkosClientId(config: CliConfig): void {
|
|
389
|
+
if (!config.workosClientId) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
"WORKOS_CLIENT_ID is not configured. Add the public AuthKit client id to Doppler cloud-v2/dev as WORKOS_CLIENT_ID.",
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function errorMessage(response: Response): Promise<string> {
|
|
397
|
+
try {
|
|
398
|
+
const body = (await response.json()) as { error?: string; error_description?: string };
|
|
399
|
+
return body.error_description || body.error || `HTTP ${response.status}`;
|
|
400
|
+
} catch {
|
|
401
|
+
return `HTTP ${response.status}`;
|
|
402
|
+
}
|
|
403
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface CliConfig {
|
|
2
|
+
coreUrl: string;
|
|
3
|
+
consoleUrl: string;
|
|
4
|
+
workosClientId: string;
|
|
5
|
+
workosApiBaseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getConfig(): CliConfig {
|
|
9
|
+
const workosClientId = process.env.GLASSLY_WORKOS_CLIENT_ID || process.env.WORKOS_CLIENT_ID || "";
|
|
10
|
+
return {
|
|
11
|
+
coreUrl: normalizeUrl(process.env.GLASSLY_CORE_URL || "http://localhost:3000"),
|
|
12
|
+
consoleUrl: normalizeUrl(process.env.GLASSLY_CONSOLE_URL || "http://localhost:5173"),
|
|
13
|
+
workosClientId,
|
|
14
|
+
workosApiBaseUrl: normalizeUrl(process.env.WORKOS_API_BASE_URL || "https://api.workos.com"),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeUrl(value: string): string {
|
|
19
|
+
const url = new URL(value);
|
|
20
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
21
|
+
url.search = "";
|
|
22
|
+
url.hash = "";
|
|
23
|
+
return url.toString().replace(/\/$/, "");
|
|
24
|
+
}
|