@stackable-labs/cli-app-extension 1.91.2 → 1.92.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/dist/index.js +241 -91
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import { render, useApp, Box, Text, useInput, useFocus, useFocusManager } from 'ink';
|
|
5
|
-
import { readFile, writeFile, mkdir, readdir, rm } from 'fs/promises';
|
|
5
|
+
import { unlink, readFile, writeFile, mkdir, readdir, rm } from 'fs/promises';
|
|
6
6
|
import { join, dirname } from 'path';
|
|
7
7
|
import Spinner5 from 'ink-spinner';
|
|
8
8
|
import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import TextInput from 'ink-text-input';
|
|
11
11
|
import { SURFACE_TARGET } from '@stackable-labs/sdk-extension-contracts';
|
|
12
|
-
import {
|
|
12
|
+
import { homedir } from 'os';
|
|
13
13
|
import { execFile, spawn } from 'child_process';
|
|
14
14
|
import { promisify } from 'util';
|
|
15
15
|
import { installDependencies } from 'nypm';
|
|
@@ -22,6 +22,45 @@ import { createServer } from 'http';
|
|
|
22
22
|
import open from 'open';
|
|
23
23
|
import https from 'https';
|
|
24
24
|
|
|
25
|
+
// ../../lib/utils-js/src/crypto.ts
|
|
26
|
+
var getCrypto = () => {
|
|
27
|
+
if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
28
|
+
return globalThis.crypto;
|
|
29
|
+
}
|
|
30
|
+
throw new Error("Web Crypto API not available \u2014 requires Node.js 22+ or a modern browser");
|
|
31
|
+
};
|
|
32
|
+
var getSubtle = () => {
|
|
33
|
+
const crypto = getCrypto();
|
|
34
|
+
if (crypto.subtle) {
|
|
35
|
+
return crypto.subtle;
|
|
36
|
+
}
|
|
37
|
+
throw new Error("SubtleCrypto not available \u2014 requires a secure context (HTTPS) or Node.js 22+");
|
|
38
|
+
};
|
|
39
|
+
var encodeBytes = (bytes, encoding) => {
|
|
40
|
+
if (encoding === "hex") {
|
|
41
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
42
|
+
}
|
|
43
|
+
const base64 = btoa(String.fromCharCode(...bytes));
|
|
44
|
+
if (encoding === "base64url") {
|
|
45
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
46
|
+
}
|
|
47
|
+
return base64;
|
|
48
|
+
};
|
|
49
|
+
var getRandomBytes = (length, encoding = "base64") => {
|
|
50
|
+
const bytes = new Uint8Array(length);
|
|
51
|
+
getCrypto().getRandomValues(bytes);
|
|
52
|
+
return encodeBytes(bytes, encoding);
|
|
53
|
+
};
|
|
54
|
+
var getDigest = async (input, encoding = "hex") => {
|
|
55
|
+
const digest = await getSubtle().digest(
|
|
56
|
+
"SHA-256",
|
|
57
|
+
new TextEncoder().encode(input)
|
|
58
|
+
);
|
|
59
|
+
return encodeBytes(new Uint8Array(digest), encoding);
|
|
60
|
+
};
|
|
61
|
+
var getNonce = () => getRandomBytes(16, "base64url");
|
|
62
|
+
var getVerifier = () => getRandomBytes(32, "base64url");
|
|
63
|
+
|
|
25
64
|
// ../../lib/utils-js/src/format.ts
|
|
26
65
|
var toKebabCase = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
27
66
|
|
|
@@ -1127,6 +1166,92 @@ var TemplateSelect = ({ onSubmit, onBack }) => {
|
|
|
1127
1166
|
}
|
|
1128
1167
|
);
|
|
1129
1168
|
};
|
|
1169
|
+
|
|
1170
|
+
// ../../lib/contracts/src/base.ts
|
|
1171
|
+
var asClerkUserId = (value) => value;
|
|
1172
|
+
var asClerkOrgId = (value) => value;
|
|
1173
|
+
|
|
1174
|
+
// ../../lib/contracts/src/permissions.ts
|
|
1175
|
+
var SUPER_ROLE = {
|
|
1176
|
+
ADMIN: "org:super_admin"
|
|
1177
|
+
};
|
|
1178
|
+
var ORG_ROLE = {
|
|
1179
|
+
ADMIN: "org:admin",
|
|
1180
|
+
OWNER: "org:owner"};
|
|
1181
|
+
var EDITOR_ROLES = [
|
|
1182
|
+
SUPER_ROLE.ADMIN,
|
|
1183
|
+
ORG_ROLE.ADMIN,
|
|
1184
|
+
ORG_ROLE.OWNER
|
|
1185
|
+
];
|
|
1186
|
+
[
|
|
1187
|
+
...Object.values(SUPER_ROLE),
|
|
1188
|
+
...Object.values(EDITOR_ROLES)
|
|
1189
|
+
];
|
|
1190
|
+
|
|
1191
|
+
// ../../lib/utils-auth/src/constants.ts
|
|
1192
|
+
var STANDALONE_CLIENT_DATA = {
|
|
1193
|
+
CLI: { name: "@stackable-labs/cli-app-extension", authFile: "cli-auth.json" },
|
|
1194
|
+
MCP: { name: "@stackable-labs/mcp-app-extension", authFile: "mcp-auth.json" }
|
|
1195
|
+
};
|
|
1196
|
+
var STANDALONE_CLIENT = Object.fromEntries(
|
|
1197
|
+
Object.entries(STANDALONE_CLIENT_DATA).map(([k, v]) => [k, v.name])
|
|
1198
|
+
);
|
|
1199
|
+
var STANDALONE_CLIENT_AUTH_FILE = Object.fromEntries(
|
|
1200
|
+
Object.entries(STANDALONE_CLIENT_DATA).map(([k, v]) => [k, v.authFile])
|
|
1201
|
+
);
|
|
1202
|
+
Object.values(STANDALONE_CLIENT);
|
|
1203
|
+
|
|
1204
|
+
// ../../lib/utils-auth/src/index.ts
|
|
1205
|
+
var deriveClientId = async (clientName) => (await getDigest(clientName)).slice(0, 32);
|
|
1206
|
+
var AUTH_DIR = join(homedir(), ".stackable");
|
|
1207
|
+
join(AUTH_DIR, STANDALONE_CLIENT_AUTH_FILE.CLI);
|
|
1208
|
+
join(AUTH_DIR, STANDALONE_CLIENT_AUTH_FILE.MCP);
|
|
1209
|
+
var resolveAuthFile = (filename) => join(AUTH_DIR, STANDALONE_CLIENT_AUTH_FILE.CLI);
|
|
1210
|
+
var readAuthState = async (filename) => {
|
|
1211
|
+
try {
|
|
1212
|
+
const content = await readFile(resolveAuthFile(filename), "utf8");
|
|
1213
|
+
return JSON.parse(content);
|
|
1214
|
+
} catch {
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
var writeAuthState = async (state, filename) => {
|
|
1219
|
+
await mkdir(AUTH_DIR, { recursive: true, mode: 448 });
|
|
1220
|
+
await writeFile(resolveAuthFile(), JSON.stringify(state, null, 2), { mode: 384 });
|
|
1221
|
+
};
|
|
1222
|
+
var clearAuthState = async (filename) => {
|
|
1223
|
+
try {
|
|
1224
|
+
await unlink(resolveAuthFile(filename));
|
|
1225
|
+
} catch {
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
var decodeJwtPayload = (token) => {
|
|
1229
|
+
try {
|
|
1230
|
+
const [, payload] = token.split(".");
|
|
1231
|
+
if (!payload) {
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
const json = Buffer.from(payload, "base64url").toString("utf8");
|
|
1235
|
+
return JSON.parse(json);
|
|
1236
|
+
} catch {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
var getToken = async (filename) => {
|
|
1241
|
+
const state = await readAuthState(filename);
|
|
1242
|
+
if (!state) {
|
|
1243
|
+
throw new Error("Not authenticated. Run `stackable-app-extension auth login` first.");
|
|
1244
|
+
}
|
|
1245
|
+
const payload = decodeJwtPayload(state.token);
|
|
1246
|
+
if (payload?.exp && typeof payload.exp === "number") {
|
|
1247
|
+
if (Date.now() >= payload.exp * 1e3) {
|
|
1248
|
+
throw new Error("Session expired. Run `stackable-app-extension auth login` to re-authenticate.");
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return state.token;
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
// src/lib/api.ts
|
|
1130
1255
|
var DEFAULT_ADMIN_API_URL = "https://api-use1.stackablelabs.io/admin";
|
|
1131
1256
|
var DEFAULT_PUBLIC_API_URL = "https://api.stackablelabs.io/app-extension/latest";
|
|
1132
1257
|
var getAdminApiBaseUrl = () => process.env.ADMIN_API_BASE_URL ?? DEFAULT_ADMIN_API_URL;
|
|
@@ -1526,13 +1651,15 @@ export const appStore = createStore<AppState>({
|
|
|
1526
1651
|
{
|
|
1527
1652
|
path: "surfaces/Content.tsx",
|
|
1528
1653
|
title: "Content Surface with Loading State",
|
|
1529
|
-
code: `import { ui, useStore, useContextData,
|
|
1654
|
+
code: `import { ui, useStore, useContextData, Surface } from '@stackable-labs/sdk-extension-react'
|
|
1530
1655
|
import { appStore } from '../store'
|
|
1531
1656
|
|
|
1532
1657
|
export function Content() {
|
|
1533
1658
|
const viewState = useStore(appStore, (s) => s.viewState)
|
|
1534
1659
|
const { loading } = useContextData()
|
|
1535
|
-
|
|
1660
|
+
// Non-secret settings from settingsSchema (add settingsSchema to manifest.json to use). Ex:
|
|
1661
|
+
// const settings = useSettings()
|
|
1662
|
+
// const apiEndpoint = settings.apiEndpoint as string
|
|
1536
1663
|
|
|
1537
1664
|
if (loading) {
|
|
1538
1665
|
return (
|
|
@@ -1661,29 +1788,6 @@ export function createFetchApi(fetch: FetchFn) {
|
|
|
1661
1788
|
}`
|
|
1662
1789
|
}
|
|
1663
1790
|
];
|
|
1664
|
-
|
|
1665
|
-
// ../../lib/contracts/src/base.ts
|
|
1666
|
-
var asClerkUserId = (value) => value;
|
|
1667
|
-
var asClerkOrgId = (value) => value;
|
|
1668
|
-
|
|
1669
|
-
// ../../lib/contracts/src/permissions.ts
|
|
1670
|
-
var SUPER_ROLE = {
|
|
1671
|
-
ADMIN: "org:super_admin"
|
|
1672
|
-
};
|
|
1673
|
-
var ORG_ROLE = {
|
|
1674
|
-
ADMIN: "org:admin",
|
|
1675
|
-
OWNER: "org:owner"};
|
|
1676
|
-
var EDITOR_ROLES = [
|
|
1677
|
-
SUPER_ROLE.ADMIN,
|
|
1678
|
-
ORG_ROLE.ADMIN,
|
|
1679
|
-
ORG_ROLE.OWNER
|
|
1680
|
-
];
|
|
1681
|
-
[
|
|
1682
|
-
...Object.values(SUPER_ROLE),
|
|
1683
|
-
...Object.values(EDITOR_ROLES)
|
|
1684
|
-
];
|
|
1685
|
-
|
|
1686
|
-
// src/lib/devContext.ts
|
|
1687
1791
|
var parseEnvFile = (content) => {
|
|
1688
1792
|
const lines = content.split("\n");
|
|
1689
1793
|
const env = {};
|
|
@@ -3381,7 +3485,102 @@ var callbackPage = (heading, sub, redirectUrl) => `<!DOCTYPE html>
|
|
|
3381
3485
|
</head><body><div class="card"><h2>${heading}</h2><p>${sub}</p><p class="hint" id="h"></p></div>
|
|
3382
3486
|
${redirectUrl ? `<script>(function(){var s=3,el=document.getElementById('h');function t(){if(s<=0){location.href='${redirectUrl}';return}el.textContent='Redirecting in '+s+'s\u2026';s--;setTimeout(t,1000)}t()})()</script>` : ""}
|
|
3383
3487
|
</body></html>`;
|
|
3384
|
-
var
|
|
3488
|
+
var performCLIOAuthFlow = async ({ dashboardUrl, adminApiBaseUrl }) => {
|
|
3489
|
+
const clientId = await deriveClientId(STANDALONE_CLIENT.CLI);
|
|
3490
|
+
const codeVerifier = getVerifier();
|
|
3491
|
+
const codeChallenge = await getDigest(codeVerifier, "base64url");
|
|
3492
|
+
const state = getNonce();
|
|
3493
|
+
let resolveCode;
|
|
3494
|
+
let rejectCode;
|
|
3495
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
3496
|
+
resolveCode = resolve;
|
|
3497
|
+
rejectCode = reject;
|
|
3498
|
+
});
|
|
3499
|
+
let server;
|
|
3500
|
+
server = createServer((req, res) => {
|
|
3501
|
+
const url = new URL(req.url, "http://localhost");
|
|
3502
|
+
if (url.pathname === "/callback") {
|
|
3503
|
+
const error = url.searchParams.get("error");
|
|
3504
|
+
if (error) {
|
|
3505
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
3506
|
+
res.end(callbackPage("Authentication failed", "You can close this tab."));
|
|
3507
|
+
rejectCode(new Error(url.searchParams.get("error_description") ?? error));
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
const code2 = url.searchParams.get("code");
|
|
3511
|
+
const returnedState = url.searchParams.get("state");
|
|
3512
|
+
if (!code2) {
|
|
3513
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
3514
|
+
res.end("Missing authorization code");
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
if (returnedState !== state) {
|
|
3518
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
3519
|
+
res.end("State mismatch");
|
|
3520
|
+
rejectCode(new Error("OAuth state mismatch \u2014 possible CSRF attack"));
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
3524
|
+
res.end(callbackPage("CLI authenticated", "You can return to your terminal.", dashboardUrl));
|
|
3525
|
+
resolveCode(code2);
|
|
3526
|
+
} else {
|
|
3527
|
+
res.writeHead(404);
|
|
3528
|
+
res.end();
|
|
3529
|
+
}
|
|
3530
|
+
});
|
|
3531
|
+
await new Promise((r) => server.listen(0, "127.0.0.1", r));
|
|
3532
|
+
const port = server.address().port;
|
|
3533
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
3534
|
+
const authUrl = new URL(`${dashboardUrl}/oauth/authorize`);
|
|
3535
|
+
authUrl.searchParams.set("client_id", clientId);
|
|
3536
|
+
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
3537
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
3538
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
3539
|
+
authUrl.searchParams.set("state", state);
|
|
3540
|
+
authUrl.searchParams.set("response_type", "code");
|
|
3541
|
+
const loginUrl = authUrl.toString();
|
|
3542
|
+
await open(loginUrl);
|
|
3543
|
+
const timeout = setTimeout(() => {
|
|
3544
|
+
server.close();
|
|
3545
|
+
rejectCode(new Error("Authentication timed out. Please try again."));
|
|
3546
|
+
}, LOGIN_TIMEOUT_MS);
|
|
3547
|
+
let code;
|
|
3548
|
+
try {
|
|
3549
|
+
code = await codePromise;
|
|
3550
|
+
} catch (err) {
|
|
3551
|
+
clearTimeout(timeout);
|
|
3552
|
+
server.close();
|
|
3553
|
+
throw err;
|
|
3554
|
+
}
|
|
3555
|
+
clearTimeout(timeout);
|
|
3556
|
+
server.close();
|
|
3557
|
+
const tokenRes = await fetch(`${adminApiBaseUrl}/oauth/token`, {
|
|
3558
|
+
method: "POST",
|
|
3559
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
3560
|
+
body: new URLSearchParams({
|
|
3561
|
+
grant_type: "authorization_code",
|
|
3562
|
+
code,
|
|
3563
|
+
code_verifier: codeVerifier,
|
|
3564
|
+
client_id: clientId,
|
|
3565
|
+
redirect_uri: redirectUri
|
|
3566
|
+
}).toString()
|
|
3567
|
+
});
|
|
3568
|
+
if (!tokenRes.ok) {
|
|
3569
|
+
const error = await tokenRes.json().catch(() => ({}));
|
|
3570
|
+
throw new Error(error.error_description ?? error.error ?? `Token exchange failed: ${tokenRes.status}`);
|
|
3571
|
+
}
|
|
3572
|
+
const tokenData = await tokenRes.json();
|
|
3573
|
+
const [, payloadB64] = tokenData.access_token.split(".");
|
|
3574
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
3575
|
+
const authState = {
|
|
3576
|
+
token: tokenData.access_token,
|
|
3577
|
+
userId: asClerkUserId(payload.sub),
|
|
3578
|
+
orgId: asClerkOrgId(payload.orgId)
|
|
3579
|
+
};
|
|
3580
|
+
await writeAuthState(authState);
|
|
3581
|
+
return { authState, loginUrl };
|
|
3582
|
+
};
|
|
3583
|
+
var AuthLogin = ({ dashboardUrl, adminApiBaseUrl }) => {
|
|
3385
3584
|
const { exit } = useApp();
|
|
3386
3585
|
const [state, setState] = useState("waiting");
|
|
3387
3586
|
const [loginUrl, setLoginUrl] = useState("");
|
|
@@ -3389,78 +3588,29 @@ var AuthLogin = ({ dashboardUrl }) => {
|
|
|
3389
3588
|
const [orgIdLabel, setOrgIdLabel] = useState("");
|
|
3390
3589
|
const [errorMessage, setErrorMessage] = useState("");
|
|
3391
3590
|
useEffect(() => {
|
|
3392
|
-
let
|
|
3393
|
-
let timeout;
|
|
3591
|
+
let cancelled = false;
|
|
3394
3592
|
const run = async () => {
|
|
3395
|
-
let resolveToken;
|
|
3396
|
-
let rejectToken;
|
|
3397
|
-
const tokenPromise = new Promise((resolve, reject) => {
|
|
3398
|
-
resolveToken = resolve;
|
|
3399
|
-
rejectToken = reject;
|
|
3400
|
-
});
|
|
3401
|
-
server = createServer((req, res) => {
|
|
3402
|
-
const url2 = new URL(req.url, "http://localhost");
|
|
3403
|
-
if (url2.pathname === "/callback") {
|
|
3404
|
-
const error = url2.searchParams.get("error");
|
|
3405
|
-
if (error) {
|
|
3406
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
3407
|
-
res.end(callbackPage("Authentication failed", "You can close this tab."));
|
|
3408
|
-
rejectToken(new Error(error));
|
|
3409
|
-
return;
|
|
3410
|
-
}
|
|
3411
|
-
const token2 = url2.searchParams.get("token");
|
|
3412
|
-
if (token2) {
|
|
3413
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
3414
|
-
res.end(callbackPage("CLI authenticated", "You can return to your terminal.", dashboardUrl));
|
|
3415
|
-
resolveToken(token2);
|
|
3416
|
-
} else {
|
|
3417
|
-
res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
|
|
3418
|
-
res.end("Missing token");
|
|
3419
|
-
}
|
|
3420
|
-
} else {
|
|
3421
|
-
res.writeHead(404);
|
|
3422
|
-
res.end();
|
|
3423
|
-
}
|
|
3424
|
-
});
|
|
3425
|
-
await new Promise((r) => server.listen(0, "127.0.0.1", r));
|
|
3426
|
-
const port = server.address().port;
|
|
3427
|
-
const callbackUrl = `http://localhost:${port}/callback`;
|
|
3428
|
-
const url = `${dashboardUrl}/cli-auth?callback=${encodeURIComponent(callbackUrl)}`;
|
|
3429
|
-
setLoginUrl(url);
|
|
3430
|
-
await open(url);
|
|
3431
|
-
timeout = setTimeout(() => {
|
|
3432
|
-
server.close();
|
|
3433
|
-
rejectToken(new Error("Login timed out. Please try again."));
|
|
3434
|
-
}, LOGIN_TIMEOUT_MS);
|
|
3435
|
-
let token;
|
|
3436
3593
|
try {
|
|
3437
|
-
|
|
3594
|
+
const result = await performCLIOAuthFlow({ dashboardUrl, adminApiBaseUrl });
|
|
3595
|
+
if (cancelled) {
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3598
|
+
setLoginUrl(result.loginUrl);
|
|
3599
|
+
setUserIdLabel(result.authState.userId);
|
|
3600
|
+
setOrgIdLabel(result.authState.orgId);
|
|
3601
|
+
setState("success");
|
|
3438
3602
|
} catch (err) {
|
|
3439
|
-
|
|
3440
|
-
|
|
3603
|
+
if (cancelled) {
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3441
3606
|
setErrorMessage(err instanceof Error ? err.message : String(err));
|
|
3442
3607
|
setState("error");
|
|
3443
|
-
exit();
|
|
3444
|
-
return;
|
|
3445
3608
|
}
|
|
3446
|
-
clearTimeout(timeout);
|
|
3447
|
-
server.close();
|
|
3448
|
-
const [, payloadB64] = token.split(".");
|
|
3449
|
-
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
3450
|
-
await writeAuthState({
|
|
3451
|
-
token,
|
|
3452
|
-
userId: asClerkUserId(payload.sub),
|
|
3453
|
-
orgId: asClerkOrgId(payload.orgId)
|
|
3454
|
-
});
|
|
3455
|
-
setUserIdLabel(payload.sub);
|
|
3456
|
-
setOrgIdLabel(payload.orgId);
|
|
3457
|
-
setState("success");
|
|
3458
3609
|
exit();
|
|
3459
3610
|
};
|
|
3460
3611
|
run();
|
|
3461
3612
|
return () => {
|
|
3462
|
-
|
|
3463
|
-
server?.close();
|
|
3613
|
+
cancelled = true;
|
|
3464
3614
|
};
|
|
3465
3615
|
}, []);
|
|
3466
3616
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
@@ -3708,7 +3858,7 @@ program.command("dev" /* DEV */).description("Start dev servers with a public tu
|
|
|
3708
3858
|
var DASHBOARD_URL = process.env.ADMIN_DASHBOARD_URL ?? "https://admin.stackablelabs.com";
|
|
3709
3859
|
var auth = program.command("auth").description("Manage CLI authentication");
|
|
3710
3860
|
auth.command("login").description("Authenticate with Stackable via browser").action(async () => {
|
|
3711
|
-
render(/* @__PURE__ */ jsx(AuthLogin, { dashboardUrl: DASHBOARD_URL }));
|
|
3861
|
+
render(/* @__PURE__ */ jsx(AuthLogin, { dashboardUrl: DASHBOARD_URL, adminApiBaseUrl: getAdminApiBaseUrl() }));
|
|
3712
3862
|
});
|
|
3713
3863
|
auth.command("logout").description("Clear stored CLI credentials").action(async () => {
|
|
3714
3864
|
await clearAuthState();
|