@stackable-labs/cli-app-extension 1.29.0 → 1.31.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.
Files changed (2) hide show
  1. package/dist/index.js +352 -174
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -1053,6 +1053,20 @@ var fetchExtensions = async (token, appId) => {
1053
1053
  ])
1054
1054
  );
1055
1055
  };
1056
+ var requestDevSessionToken = async (token, appId, extensionId) => {
1057
+ const baseUrl = getAdminApiBaseUrl();
1058
+ const res = await fetch(`${baseUrl}/app-extension/${appId}/dev-session`, {
1059
+ method: "POST",
1060
+ headers: authHeaders(token),
1061
+ body: JSON.stringify({ extensionId })
1062
+ });
1063
+ if (!res.ok) {
1064
+ const body = await res.text();
1065
+ throw new Error(`Failed to request dev session token: ${res.status} ${body}`);
1066
+ }
1067
+ const data = await res.json();
1068
+ return data.token;
1069
+ };
1056
1070
  var updateExtension = async (token, appId, extensionId, payload) => {
1057
1071
  const baseUrl = getAdminApiBaseUrl();
1058
1072
  const res = await fetch(`${baseUrl}/app-extension/${appId}/extensions/${extensionId}`, {
@@ -2269,12 +2283,42 @@ var App = ({ command, token, userId, orgId, initialName, initialExtensionId, opt
2269
2283
  };
2270
2284
 
2271
2285
  // src/components/DevApp.tsx
2272
- import { useRef, useState as useState12, useEffect as useEffect6, useCallback as useCallback2 } from "react";
2273
- import { useInput as useInput11, Box as Box18, Text as Text18 } from "ink";
2286
+ import { useRef, useState as useState13, useEffect as useEffect6, useCallback as useCallback3 } from "react";
2287
+ import { useInput as useInput12, Box as Box19, Text as Text19 } from "ink";
2288
+
2289
+ // src/lib/devServer.ts
2290
+ import { spawn } from "child_process";
2291
+ var startDevServer = (projectRoot) => {
2292
+ const child = spawn("pnpm", ["dev"], {
2293
+ cwd: projectRoot,
2294
+ stdio: "pipe"
2295
+ });
2296
+ const stop = () => {
2297
+ child.kill("SIGTERM");
2298
+ };
2299
+ return { process: child, stop };
2300
+ };
2274
2301
 
2275
2302
  // src/lib/tunnel.ts
2276
2303
  import { Tunnel } from "cloudflared";
2277
- var startTunnel = (port) => new Promise((resolve, reject) => {
2304
+ var MAX_RETRIES = 3;
2305
+ var RETRY_DELAY_MS = 2e3;
2306
+ var isValidTunnelUrl = (url) => {
2307
+ try {
2308
+ const { protocol, hostname } = new URL(url);
2309
+ if (protocol !== "https:" || !hostname.endsWith(".trycloudflare.com")) {
2310
+ return false;
2311
+ }
2312
+ const subdomain = hostname.replace(".trycloudflare.com", "");
2313
+ if (!subdomain || subdomain === "api") {
2314
+ return false;
2315
+ }
2316
+ return true;
2317
+ } catch {
2318
+ return false;
2319
+ }
2320
+ };
2321
+ var startTunnelOnce = (port) => new Promise((resolve, reject) => {
2278
2322
  const tunnel = Tunnel.quick(`http://localhost:${port}`);
2279
2323
  const timeout = setTimeout(() => {
2280
2324
  tunnel.stop();
@@ -2282,6 +2326,10 @@ var startTunnel = (port) => new Promise((resolve, reject) => {
2282
2326
  }, 3e4);
2283
2327
  tunnel.once("url", (url) => {
2284
2328
  clearTimeout(timeout);
2329
+ if (!isValidTunnelUrl(url)) {
2330
+ tunnel.stop();
2331
+ return reject(new Error(`Tunnel returned invalid URL: ${url}`));
2332
+ }
2285
2333
  resolve({ url, stop: () => tunnel.stop() });
2286
2334
  });
2287
2335
  tunnel.once("error", (err) => {
@@ -2289,18 +2337,22 @@ var startTunnel = (port) => new Promise((resolve, reject) => {
2289
2337
  reject(err);
2290
2338
  });
2291
2339
  });
2292
-
2293
- // src/lib/devServer.ts
2294
- import { spawn } from "child_process";
2295
- var startDevServer = (projectRoot) => {
2296
- const child = spawn("pnpm", ["dev"], {
2297
- cwd: projectRoot,
2298
- stdio: "pipe"
2299
- });
2300
- const stop = () => {
2301
- child.kill("SIGTERM");
2302
- };
2303
- return { process: child, stop };
2340
+ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2341
+ var startTunnel = async (port) => {
2342
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
2343
+ try {
2344
+ return await startTunnelOnce(port);
2345
+ } catch (err) {
2346
+ const message = err instanceof Error ? err.message : String(err);
2347
+ if (attempt < MAX_RETRIES && message.includes("invalid URL")) {
2348
+ console.log(`[dev] Tunnel returned invalid URL, retrying (attempt ${attempt + 1}/${MAX_RETRIES})...`);
2349
+ await delay(RETRY_DELAY_MS);
2350
+ } else {
2351
+ throw err;
2352
+ }
2353
+ }
2354
+ }
2355
+ throw new Error(`Tunnel failed to return a valid URL after ${MAX_RETRIES} attempts`);
2304
2356
  };
2305
2357
 
2306
2358
  // src/components/DevSetup.tsx
@@ -2352,9 +2404,71 @@ var DevSetup = ({ initialContext, token, onReady }) => {
2352
2404
  };
2353
2405
 
2354
2406
  // src/components/DevDashboard.tsx
2355
- import { Box as Box17, Text as Text17, useInput as useInput10 } from "ink";
2356
- import { useEffect as useEffect5 } from "react";
2407
+ import { Box as Box18, Text as Text18, useInput as useInput11 } from "ink";
2408
+ import { useEffect as useEffect5, useMemo } from "react";
2409
+
2410
+ // src/components/CopyableField.tsx
2411
+ import { useState as useState12, useCallback as useCallback2 } from "react";
2412
+ import { useInput as useInput10, Box as Box17, Text as Text17 } from "ink";
2413
+ import clipboard from "clipboardy";
2357
2414
  import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2415
+ var CopyableField = ({
2416
+ label,
2417
+ value,
2418
+ shortcutKey,
2419
+ shortcutLabel,
2420
+ maxLength = 60,
2421
+ labelColor,
2422
+ children
2423
+ }) => {
2424
+ const [expanded, setExpanded] = useState12(false);
2425
+ const [copied, setCopied] = useState12(false);
2426
+ const handleCopy = useCallback2(async () => {
2427
+ if (expanded) {
2428
+ setExpanded(false);
2429
+ setCopied(false);
2430
+ return;
2431
+ }
2432
+ try {
2433
+ await clipboard.write(value);
2434
+ setExpanded(true);
2435
+ setCopied(true);
2436
+ setTimeout(() => {
2437
+ setCopied(false);
2438
+ setExpanded(false);
2439
+ }, 3e3);
2440
+ } catch {
2441
+ setExpanded(true);
2442
+ }
2443
+ }, [expanded, value]);
2444
+ useInput10((input) => {
2445
+ if (input === shortcutKey) {
2446
+ handleCopy();
2447
+ }
2448
+ });
2449
+ const truncated = value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
2450
+ const keyHint = shortcutLabel || shortcutKey;
2451
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
2452
+ /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2453
+ /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: label }),
2454
+ copied && /* @__PURE__ */ jsx17(Text17, { color: "green", bold: true, children: "Copied!" }),
2455
+ !copied && /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
2456
+ "press ",
2457
+ /* @__PURE__ */ jsxs17(Text17, { bold: true, color: "yellow", children: [
2458
+ "[",
2459
+ keyHint,
2460
+ "]"
2461
+ ] }),
2462
+ " to copy (and see full value)"
2463
+ ] })
2464
+ ] }),
2465
+ /* @__PURE__ */ jsx17(Box17, { paddingLeft: 2, children: expanded ? /* @__PURE__ */ jsx17(Text17, { wrap: "wrap", children: value }) : children || /* @__PURE__ */ jsx17(Text17, { children: labelColor ? /* @__PURE__ */ jsx17(Text17, { color: labelColor, children: truncated }) : truncated }) })
2466
+ ] });
2467
+ };
2468
+
2469
+ // src/components/DevDashboard.tsx
2470
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2471
+ var toBase64Url = (input) => btoa(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2358
2472
  var DevDashboard = ({
2359
2473
  previewTunnelUrl,
2360
2474
  tunnelUrl,
@@ -2367,6 +2481,7 @@ var DevDashboard = ({
2367
2481
  extensionPort,
2368
2482
  previewPort,
2369
2483
  manifestWarnings = [],
2484
+ devSessionToken,
2370
2485
  onQuit
2371
2486
  }) => {
2372
2487
  useEffect5(() => {
@@ -2378,79 +2493,132 @@ var DevDashboard = ({
2378
2493
  process.off("SIGINT", handler);
2379
2494
  };
2380
2495
  }, [onQuit]);
2381
- useInput10((input, key) => {
2496
+ const devParamBlob = useMemo(() => {
2497
+ if (!tunnelUrl || !devSessionToken) return "";
2498
+ return toBase64Url(JSON.stringify({ url: tunnelUrl, token: devSessionToken }));
2499
+ }, [tunnelUrl, devSessionToken]);
2500
+ useInput11((input, key) => {
2382
2501
  if (input === "q" || key.ctrl && input === "c") {
2383
2502
  onQuit();
2384
2503
  }
2385
2504
  });
2386
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
2387
- /* @__PURE__ */ jsx17(Banner, { userId, orgId }),
2388
- /* @__PURE__ */ jsxs17(
2505
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
2506
+ /* @__PURE__ */ jsx18(Banner, { userId, orgId }),
2507
+ /* @__PURE__ */ jsxs18(
2389
2508
  StepShell,
2390
2509
  {
2391
2510
  title: "dev",
2392
- hint: `Live development ${tunnelUrl ? "with" : "w/o"} Tunnel`,
2393
- footer: /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", gap: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Press q or Ctrl-C to quit" }) }),
2511
+ hint: `Live development ${tunnelUrl ? "with" : "w/o"} Tunnel \u2014 ${appName ? `${appName} ` : ""}(${appId})`,
2512
+ footer: /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", gap: 1, children: [
2513
+ devParamBlob && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
2514
+ "Press ",
2515
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: "yellow", children: "[d]" }),
2516
+ " to copy Dev Param, ",
2517
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: "yellow", children: "[s]" }),
2518
+ " to copy Staging Param"
2519
+ ] }),
2520
+ /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
2521
+ "Press ",
2522
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: "yellow", children: "[q]" }),
2523
+ " or Ctrl-C to quit"
2524
+ ] })
2525
+ ] }),
2394
2526
  children: [
2395
- /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
2396
- /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2397
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Extension:" }),
2398
- /* @__PURE__ */ jsxs17(Text17, { children: [
2527
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
2528
+ /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2529
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Extension:" }),
2530
+ /* @__PURE__ */ jsxs18(Text18, { children: [
2399
2531
  extensionName,
2400
2532
  " (",
2401
2533
  extensionId,
2402
2534
  ")"
2403
2535
  ] })
2404
2536
  ] }),
2405
- /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2406
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "App:" }),
2407
- /* @__PURE__ */ jsx17(Text17, { children: `${appName ? `${appName} ` : ""}(${appId})` })
2408
- ] }),
2409
- /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2410
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Bundle URL:" }),
2411
- /* @__PURE__ */ jsx17(Text17, { children: tunnelUrl || `http://localhost:${extensionPort}` })
2537
+ /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2538
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Bundle URL:" }),
2539
+ /* @__PURE__ */ jsx18(Text18, { children: tunnelUrl || `http://localhost:${extensionPort}` })
2412
2540
  ] })
2413
2541
  ] }),
2414
- /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
2415
- previewTunnelUrl && /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2416
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Tunnel Dev - Preview:" }),
2417
- /* @__PURE__ */ jsx17(Text17, { children: previewTunnelUrl })
2542
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
2543
+ previewTunnelUrl && /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2544
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Tunnel Dev - Preview:" }),
2545
+ /* @__PURE__ */ jsx18(Text18, { children: previewTunnelUrl })
2418
2546
  ] }),
2419
- /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2420
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Local Dev - Preview:" }),
2421
- /* @__PURE__ */ jsxs17(Text17, { children: [
2547
+ /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2548
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Local Dev - Preview:" }),
2549
+ /* @__PURE__ */ jsxs18(Text18, { children: [
2422
2550
  "http://localhost:",
2423
2551
  previewPort
2424
2552
  ] })
2425
2553
  ] })
2426
2554
  ] }),
2427
- /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
2428
- tunnelUrl && /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2429
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Tunnel Dev - Extension:" }),
2430
- /* @__PURE__ */ jsx17(Text17, { children: tunnelUrl })
2555
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
2556
+ tunnelUrl && /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2557
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Tunnel Dev - Extension:" }),
2558
+ /* @__PURE__ */ jsx18(Text18, { children: tunnelUrl })
2431
2559
  ] }),
2432
- /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2433
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Local Dev - Extension:" }),
2434
- /* @__PURE__ */ jsxs17(Text17, { children: [
2560
+ /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2561
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Local Dev - Extension:" }),
2562
+ /* @__PURE__ */ jsxs18(Text18, { children: [
2435
2563
  "http://localhost:",
2436
2564
  extensionPort
2437
2565
  ] })
2438
2566
  ] })
2439
2567
  ] }),
2440
- /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", children: tunnelUrl && /* @__PURE__ */ jsxs17(Box17, { gap: 2, children: [
2441
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Host Dev - Query Param:" }),
2442
- /* @__PURE__ */ jsxs17(Text17, { children: [
2443
- "?_stackable_dev=",
2444
- encodeURIComponent(`${extensionId}:${tunnelUrl}`)
2568
+ tunnelUrl && devSessionToken && /* @__PURE__ */ jsxs18(Fragment2, { children: [
2569
+ /* @__PURE__ */ jsx18(
2570
+ CopyableField,
2571
+ {
2572
+ label: "Dev Param (no encryption):",
2573
+ value: `?_stackable_dev=${extensionId}:${devParamBlob}`,
2574
+ shortcutKey: "d",
2575
+ children: /* @__PURE__ */ jsxs18(Text18, { children: [
2576
+ "?",
2577
+ /* @__PURE__ */ jsx18(Text18, { color: "yellow", children: "_stackable_dev" }),
2578
+ "=",
2579
+ /* @__PURE__ */ jsx18(Text18, { color: "cyan", children: extensionId }),
2580
+ ":",
2581
+ devParamBlob.slice(0, 20),
2582
+ "..."
2583
+ ] })
2584
+ }
2585
+ ),
2586
+ /* @__PURE__ */ jsx18(
2587
+ CopyableField,
2588
+ {
2589
+ label: "Staging Param (with encryption):",
2590
+ value: `?_stackable_staging=${extensionId}:${devParamBlob}`,
2591
+ shortcutKey: "s",
2592
+ children: /* @__PURE__ */ jsxs18(Text18, { children: [
2593
+ "?",
2594
+ /* @__PURE__ */ jsx18(Text18, { color: "yellow", children: "_stackable_staging" }),
2595
+ "=",
2596
+ /* @__PURE__ */ jsx18(Text18, { color: "cyan", children: extensionId }),
2597
+ ":",
2598
+ devParamBlob.slice(0, 20),
2599
+ "..."
2600
+ ] })
2601
+ }
2602
+ )
2603
+ ] }),
2604
+ tunnelUrl && !devSessionToken && /* @__PURE__ */ jsxs18(Box18, { gap: 2, children: [
2605
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Host Dev - Query Param:" }),
2606
+ /* @__PURE__ */ jsxs18(Text18, { children: [
2607
+ "?",
2608
+ /* @__PURE__ */ jsx18(Text18, { color: "yellow", children: "_stackable_dev" }),
2609
+ "=",
2610
+ /* @__PURE__ */ jsx18(Text18, { color: "cyan", children: extensionId }),
2611
+ ":",
2612
+ tunnelUrl
2445
2613
  ] })
2446
- ] }) }),
2447
- manifestWarnings.length > 0 && /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2448
- /* @__PURE__ */ jsx17(Text17, { color: "yellow", bold: true, children: "Local manifest differs from registry:" }),
2449
- manifestWarnings.map((w, i) => /* @__PURE__ */ jsxs17(Text17, { color: "yellow", children: [
2614
+ ] }),
2615
+ manifestWarnings.length > 0 && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2616
+ /* @__PURE__ */ jsx18(Text18, { color: "yellow", bold: true, children: "Local manifest differs from registry:" }),
2617
+ manifestWarnings.map((w, i) => /* @__PURE__ */ jsxs18(Text18, { color: "yellow", children: [
2450
2618
  " ",
2451
2619
  w
2452
2620
  ] }, i)),
2453
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " Run `pnpm --config.dlx-cache-max-age=0 dlx @stackable-labs/cli-app-extension@latest update` to review and sync." })
2621
+ /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: " Run `pnpm --config.dlx-cache-max-age=0 dlx @stackable-labs/cli-app-extension@latest update` to review and sync." })
2454
2622
  ] })
2455
2623
  ]
2456
2624
  }
@@ -2459,17 +2627,18 @@ var DevDashboard = ({
2459
2627
  };
2460
2628
 
2461
2629
  // src/components/DevApp.tsx
2462
- import { jsx as jsx18 } from "react/jsx-runtime";
2630
+ import { jsx as jsx19 } from "react/jsx-runtime";
2463
2631
  var DevApp = ({ token, userId, orgId, options = {} }) => {
2464
- const [state, setState] = useState12("setup");
2465
- const [devContext, setDevContext] = useState12(null);
2466
- const [resolvedContext, setResolvedContext] = useState12(null);
2467
- const [tunnelUrl, setTunnelUrl] = useState12(null);
2468
- const [previewTunnelUrl, setPreviewTunnelUrl] = useState12(null);
2469
- const [tunnelHandle, setTunnelHandle] = useState12(null);
2470
- const [previewTunnelHandle, setPreviewTunnelHandle] = useState12(null);
2471
- const [devServerHandle, setDevServerHandle] = useState12(null);
2472
- const [manifestWarnings, setManifestWarnings] = useState12([]);
2632
+ const [state, setState] = useState13("setup");
2633
+ const [devContext, setDevContext] = useState13(null);
2634
+ const [resolvedContext, setResolvedContext] = useState13(null);
2635
+ const [tunnelUrl, setTunnelUrl] = useState13(null);
2636
+ const [previewTunnelUrl, setPreviewTunnelUrl] = useState13(null);
2637
+ const [tunnelHandle, setTunnelHandle] = useState13(null);
2638
+ const [previewTunnelHandle, setPreviewTunnelHandle] = useState13(null);
2639
+ const [devServerHandle, setDevServerHandle] = useState13(null);
2640
+ const [manifestWarnings, setManifestWarnings] = useState13([]);
2641
+ const [devSessionToken, setDevSessionToken] = useState13(null);
2473
2642
  const shuttingDown = useRef(false);
2474
2643
  const useTunnel = options.tunnel !== false;
2475
2644
  useEffect6(() => {
@@ -2480,7 +2649,7 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2480
2649
  setDevContext(ctx);
2481
2650
  });
2482
2651
  }, [options.dir]);
2483
- const handleSetupReady = useCallback2(async (resolved) => {
2652
+ const handleSetupReady = useCallback3(async (resolved) => {
2484
2653
  if (!devContext) return;
2485
2654
  setResolvedContext(resolved);
2486
2655
  console.log(`[dev] Saving context: appId=${resolved.appId} extensionId=${resolved.extensionId}`);
@@ -2538,6 +2707,14 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2538
2707
  } else {
2539
2708
  console.log("[dev] Tunnel disabled (--no-tunnel)");
2540
2709
  }
2710
+ try {
2711
+ console.log("[dev] Requesting dev session token...");
2712
+ const sessionToken = await requestDevSessionToken(token, resolved.appId, resolved.extensionId);
2713
+ console.log("[dev] Dev session token received");
2714
+ setDevSessionToken(sessionToken);
2715
+ } catch (err) {
2716
+ console.warn("[dev] Failed to obtain dev session token \u2014 dev/staging mode URLs will not include auth token:", err);
2717
+ }
2541
2718
  console.log(`[dev] Starting dev server in ${devContext.projectRoot}`);
2542
2719
  const serverHandle = startDevServer(devContext.projectRoot);
2543
2720
  setDevServerHandle(serverHandle);
@@ -2579,7 +2756,7 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2579
2756
  console.log("[dev] Done");
2580
2757
  process.exit(0);
2581
2758
  };
2582
- useInput11((input, key) => {
2759
+ useInput12((input, key) => {
2583
2760
  if (input === "c" && key.ctrl) {
2584
2761
  if (state === "running") {
2585
2762
  handleQuit();
@@ -2590,7 +2767,7 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2590
2767
  });
2591
2768
  if (state === "setup" && devContext) {
2592
2769
  if (!devContext.appId || !devContext.extensionId) {
2593
- return /* @__PURE__ */ jsx18(
2770
+ return /* @__PURE__ */ jsx19(
2594
2771
  DevSetup,
2595
2772
  {
2596
2773
  token,
@@ -2602,7 +2779,7 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2602
2779
  return null;
2603
2780
  }
2604
2781
  if (state === "running" && devContext && resolvedContext) {
2605
- return /* @__PURE__ */ jsx18(
2782
+ return /* @__PURE__ */ jsx19(
2606
2783
  DevDashboard,
2607
2784
  {
2608
2785
  previewTunnelUrl,
@@ -2616,6 +2793,7 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2616
2793
  extensionPort: options.extensionPort ? parseInt(options.extensionPort, 10) : devContext.extensionPort,
2617
2794
  previewPort: options.previewPort ? parseInt(options.previewPort, 10) : devContext.previewPort,
2618
2795
  manifestWarnings,
2796
+ devSessionToken,
2619
2797
  onQuit: handleQuit
2620
2798
  }
2621
2799
  );
@@ -2623,13 +2801,13 @@ var DevApp = ({ token, userId, orgId, options = {} }) => {
2623
2801
  if (state === "stopping") {
2624
2802
  return null;
2625
2803
  }
2626
- return /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsx18(Text18, { children: "Loading..." }) });
2804
+ return /* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsx19(Text19, { children: "Loading..." }) });
2627
2805
  };
2628
2806
 
2629
2807
  // src/components/AIScaffold.tsx
2630
- import { Box as Box19, Text as Text19, useApp as useApp2 } from "ink";
2808
+ import { Box as Box20, Text as Text20, useApp as useApp2 } from "ink";
2631
2809
  import Spinner4 from "ink-spinner";
2632
- import { useState as useState13, useEffect as useEffect7 } from "react";
2810
+ import { useState as useState14, useEffect as useEffect7 } from "react";
2633
2811
 
2634
2812
  // src/lib/aiDocs.ts
2635
2813
  import { existsSync, readFileSync } from "fs";
@@ -2682,12 +2860,12 @@ var downloadAndExtractAiDocs = async (targetDir, version2) => {
2682
2860
  };
2683
2861
 
2684
2862
  // src/components/AIScaffold.tsx
2685
- import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
2863
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
2686
2864
  var AIScaffold = ({ version: version2 }) => {
2687
2865
  const { exit } = useApp2();
2688
- const [state, setState] = useState13("validating");
2689
- const [files, setFiles] = useState13([]);
2690
- const [errorMessage, setErrorMessage] = useState13("");
2866
+ const [state, setState] = useState14("validating");
2867
+ const [files, setFiles] = useState14([]);
2868
+ const [errorMessage, setErrorMessage] = useState14("");
2691
2869
  useEffect7(() => {
2692
2870
  const run = async () => {
2693
2871
  const projectDir = process.cwd();
@@ -2711,35 +2889,35 @@ var AIScaffold = ({ version: version2 }) => {
2711
2889
  };
2712
2890
  run();
2713
2891
  }, []);
2714
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", children: [
2715
- /* @__PURE__ */ jsx19(Banner, {}),
2716
- /* @__PURE__ */ jsxs18(StepShell, { title: "AI Editor Config", children: [
2717
- state === "validating" && /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
2718
- /* @__PURE__ */ jsx19(Text19, { color: "cyan", children: /* @__PURE__ */ jsx19(Spinner4, { type: "dots" }) }),
2719
- /* @__PURE__ */ jsx19(Text19, { children: "Checking project..." })
2892
+ return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
2893
+ /* @__PURE__ */ jsx20(Banner, {}),
2894
+ /* @__PURE__ */ jsxs19(StepShell, { title: "AI Editor Config", children: [
2895
+ state === "validating" && /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2896
+ /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: /* @__PURE__ */ jsx20(Spinner4, { type: "dots" }) }),
2897
+ /* @__PURE__ */ jsx20(Text20, { children: "Checking project..." })
2720
2898
  ] }),
2721
- state === "downloading" && /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
2722
- /* @__PURE__ */ jsx19(Text19, { color: "cyan", children: /* @__PURE__ */ jsx19(Spinner4, { type: "dots" }) }),
2723
- /* @__PURE__ */ jsxs18(Text19, { children: [
2899
+ state === "downloading" && /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2900
+ /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: /* @__PURE__ */ jsx20(Spinner4, { type: "dots" }) }),
2901
+ /* @__PURE__ */ jsxs19(Text20, { children: [
2724
2902
  "Downloading AI editor configs (",
2725
2903
  version2,
2726
2904
  ")..."
2727
2905
  ] })
2728
2906
  ] }),
2729
- state === "done" && /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", gap: 1, children: [
2730
- /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
2731
- /* @__PURE__ */ jsx19(Text19, { color: "green", bold: true, children: "\u2714" }),
2732
- /* @__PURE__ */ jsxs18(Text19, { bold: true, children: [
2907
+ state === "done" && /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", gap: 1, children: [
2908
+ /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2909
+ /* @__PURE__ */ jsx20(Text20, { color: "green", bold: true, children: "\u2714" }),
2910
+ /* @__PURE__ */ jsxs19(Text20, { bold: true, children: [
2733
2911
  "AI editor configs installed (",
2734
2912
  files.length,
2735
2913
  " files)"
2736
2914
  ] })
2737
2915
  ] }),
2738
- /* @__PURE__ */ jsx19(Box19, { flexDirection: "column", marginLeft: 2, children: files.map((f) => /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: f }, f)) })
2916
+ /* @__PURE__ */ jsx20(Box20, { flexDirection: "column", marginLeft: 2, children: files.map((f) => /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: f }, f)) })
2739
2917
  ] }),
2740
- state === "error" && /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
2741
- /* @__PURE__ */ jsx19(Text19, { color: "red", children: "\u2716" }),
2742
- /* @__PURE__ */ jsx19(Text19, { children: errorMessage })
2918
+ state === "error" && /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2919
+ /* @__PURE__ */ jsx20(Text20, { color: "red", children: "\u2716" }),
2920
+ /* @__PURE__ */ jsx20(Text20, { children: errorMessage })
2743
2921
  ] })
2744
2922
  ] })
2745
2923
  ] });
@@ -2747,10 +2925,10 @@ var AIScaffold = ({ version: version2 }) => {
2747
2925
 
2748
2926
  // src/components/AuthLogin.tsx
2749
2927
  import { createServer } from "http";
2750
- import { Box as Box20, Text as Text20, useApp as useApp3 } from "ink";
2928
+ import { Box as Box21, Text as Text21, useApp as useApp3 } from "ink";
2751
2929
  import Spinner5 from "ink-spinner";
2752
2930
  import open from "open";
2753
- import { useState as useState14, useEffect as useEffect8 } from "react";
2931
+ import { useState as useState15, useEffect as useEffect8 } from "react";
2754
2932
 
2755
2933
  // src/lib/auth.ts
2756
2934
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir2, unlink } from "fs/promises";
@@ -2803,7 +2981,7 @@ var getToken = async () => {
2803
2981
  };
2804
2982
 
2805
2983
  // src/components/AuthLogin.tsx
2806
- import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
2984
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
2807
2985
  var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
2808
2986
  var callbackPage = (heading, sub, redirectUrl) => `<!DOCTYPE html>
2809
2987
  <html><head><meta charset="utf-8"><title>Stackable CLI</title>
@@ -2814,11 +2992,11 @@ ${redirectUrl ? `<script>(function(){var s=3,el=document.getElementById('h');fun
2814
2992
  </body></html>`;
2815
2993
  var AuthLogin = ({ dashboardUrl }) => {
2816
2994
  const { exit } = useApp3();
2817
- const [state, setState] = useState14("waiting");
2818
- const [loginUrl, setLoginUrl] = useState14("");
2819
- const [userIdLabel, setUserIdLabel] = useState14("");
2820
- const [orgIdLabel, setOrgIdLabel] = useState14("");
2821
- const [errorMessage, setErrorMessage] = useState14("");
2995
+ const [state, setState] = useState15("waiting");
2996
+ const [loginUrl, setLoginUrl] = useState15("");
2997
+ const [userIdLabel, setUserIdLabel] = useState15("");
2998
+ const [orgIdLabel, setOrgIdLabel] = useState15("");
2999
+ const [errorMessage, setErrorMessage] = useState15("");
2822
3000
  useEffect8(() => {
2823
3001
  let server;
2824
3002
  let timeout;
@@ -2894,38 +3072,38 @@ var AuthLogin = ({ dashboardUrl }) => {
2894
3072
  server?.close();
2895
3073
  };
2896
3074
  }, []);
2897
- return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
2898
- /* @__PURE__ */ jsx20(Banner, {}),
2899
- /* @__PURE__ */ jsxs19(StepShell, { title: "Authenticate with Stackable", children: [
2900
- state === "waiting" && /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", gap: 1, children: [
2901
- /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2902
- /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: /* @__PURE__ */ jsx20(Spinner5, { type: "dots" }) }),
2903
- /* @__PURE__ */ jsx20(Text20, { children: "Waiting for browser authentication..." })
3075
+ return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", children: [
3076
+ /* @__PURE__ */ jsx21(Banner, {}),
3077
+ /* @__PURE__ */ jsxs20(StepShell, { title: "Authenticate with Stackable", children: [
3078
+ state === "waiting" && /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", gap: 1, children: [
3079
+ /* @__PURE__ */ jsxs20(Box21, { gap: 1, children: [
3080
+ /* @__PURE__ */ jsx21(Text21, { color: "cyan", children: /* @__PURE__ */ jsx21(Spinner5, { type: "dots" }) }),
3081
+ /* @__PURE__ */ jsx21(Text21, { children: "Waiting for browser authentication..." })
2904
3082
  ] }),
2905
- loginUrl && /* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
3083
+ loginUrl && /* @__PURE__ */ jsxs20(Text21, { dimColor: true, children: [
2906
3084
  " ",
2907
3085
  loginUrl
2908
3086
  ] })
2909
3087
  ] }),
2910
- state === "success" && /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", gap: 1, children: [
2911
- /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
2912
- /* @__PURE__ */ jsxs19(Box20, { gap: 2, children: [
2913
- /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "User:" }),
2914
- /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: userIdLabel })
3088
+ state === "success" && /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", gap: 1, children: [
3089
+ /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", children: [
3090
+ /* @__PURE__ */ jsxs20(Box21, { gap: 2, children: [
3091
+ /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "User:" }),
3092
+ /* @__PURE__ */ jsx21(Text21, { color: "cyan", children: userIdLabel })
2915
3093
  ] }),
2916
- /* @__PURE__ */ jsxs19(Box20, { gap: 2, children: [
2917
- /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "Org: " }),
2918
- /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: orgIdLabel })
3094
+ /* @__PURE__ */ jsxs20(Box21, { gap: 2, children: [
3095
+ /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Org: " }),
3096
+ /* @__PURE__ */ jsx21(Text21, { color: "cyan", children: orgIdLabel })
2919
3097
  ] })
2920
3098
  ] }),
2921
- /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2922
- /* @__PURE__ */ jsx20(Text20, { color: "green", bold: true, children: "\u2714" }),
2923
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: "Authenticated" })
3099
+ /* @__PURE__ */ jsxs20(Box21, { gap: 1, children: [
3100
+ /* @__PURE__ */ jsx21(Text21, { color: "green", bold: true, children: "\u2714" }),
3101
+ /* @__PURE__ */ jsx21(Text21, { bold: true, children: "Authenticated" })
2924
3102
  ] })
2925
3103
  ] }),
2926
- state === "error" && /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
2927
- /* @__PURE__ */ jsx20(Text20, { color: "red", children: "\u2716" }),
2928
- /* @__PURE__ */ jsx20(Text20, { children: errorMessage })
3104
+ state === "error" && /* @__PURE__ */ jsxs20(Box21, { gap: 1, children: [
3105
+ /* @__PURE__ */ jsx21(Text21, { color: "red", children: "\u2716" }),
3106
+ /* @__PURE__ */ jsx21(Text21, { children: errorMessage })
2929
3107
  ] })
2930
3108
  ] })
2931
3109
  ] });
@@ -2933,70 +3111,70 @@ var AuthLogin = ({ dashboardUrl }) => {
2933
3111
 
2934
3112
  // src/components/AuthLogout.tsx
2935
3113
  import { useEffect as useEffect9 } from "react";
2936
- import { Box as Box21, Text as Text21, useApp as useApp4 } from "ink";
2937
- import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
3114
+ import { Box as Box22, Text as Text22, useApp as useApp4 } from "ink";
3115
+ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
2938
3116
  var AuthLogout = () => {
2939
3117
  const { exit } = useApp4();
2940
3118
  useEffect9(() => {
2941
3119
  exit();
2942
3120
  }, [exit]);
2943
- return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", children: [
2944
- /* @__PURE__ */ jsx21(Banner, {}),
2945
- /* @__PURE__ */ jsx21(StepShell, { title: "Authenticate with Stackable", children: /* @__PURE__ */ jsxs20(Box21, { gap: 1, children: [
2946
- /* @__PURE__ */ jsx21(Text21, { color: "green", bold: true, children: "\u2714" }),
2947
- /* @__PURE__ */ jsx21(Text21, { bold: true, children: "Logged out" })
3121
+ return /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", children: [
3122
+ /* @__PURE__ */ jsx22(Banner, {}),
3123
+ /* @__PURE__ */ jsx22(StepShell, { title: "Authenticate with Stackable", children: /* @__PURE__ */ jsxs21(Box22, { gap: 1, children: [
3124
+ /* @__PURE__ */ jsx22(Text22, { color: "green", bold: true, children: "\u2714" }),
3125
+ /* @__PURE__ */ jsx22(Text22, { bold: true, children: "Logged out" })
2948
3126
  ] }) })
2949
3127
  ] });
2950
3128
  };
2951
3129
 
2952
3130
  // src/components/AuthStatus.tsx
2953
3131
  import { useEffect as useEffect10 } from "react";
2954
- import { useApp as useApp5, Box as Box22, Text as Text22 } from "ink";
2955
- import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
3132
+ import { useApp as useApp5, Box as Box23, Text as Text23 } from "ink";
3133
+ import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
2956
3134
  var AuthStatus = ({ state, userId, orgId, expiry }) => {
2957
3135
  const { exit } = useApp5();
2958
3136
  useEffect10(() => {
2959
3137
  exit();
2960
3138
  }, [exit]);
2961
- return /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", children: [
2962
- /* @__PURE__ */ jsx22(Banner, {}),
2963
- /* @__PURE__ */ jsxs21(StepShell, { title: "Authenticate with Stackable", children: [
2964
- state === "authenticated" && /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", gap: 1, children: [
2965
- /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", children: [
2966
- /* @__PURE__ */ jsxs21(Box22, { gap: 2, children: [
2967
- /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: "User:" }),
2968
- /* @__PURE__ */ jsx22(Text22, { color: "cyan", children: userId })
3139
+ return /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", children: [
3140
+ /* @__PURE__ */ jsx23(Banner, {}),
3141
+ /* @__PURE__ */ jsxs22(StepShell, { title: "Authenticate with Stackable", children: [
3142
+ state === "authenticated" && /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", gap: 1, children: [
3143
+ /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", children: [
3144
+ /* @__PURE__ */ jsxs22(Box23, { gap: 2, children: [
3145
+ /* @__PURE__ */ jsx23(Text23, { dimColor: true, children: "User:" }),
3146
+ /* @__PURE__ */ jsx23(Text23, { color: "cyan", children: userId })
2969
3147
  ] }),
2970
- /* @__PURE__ */ jsxs21(Box22, { gap: 2, children: [
2971
- /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: "Org: " }),
2972
- /* @__PURE__ */ jsx22(Text22, { color: "cyan", children: orgId })
3148
+ /* @__PURE__ */ jsxs22(Box23, { gap: 2, children: [
3149
+ /* @__PURE__ */ jsx23(Text23, { dimColor: true, children: "Org: " }),
3150
+ /* @__PURE__ */ jsx23(Text23, { color: "cyan", children: orgId })
2973
3151
  ] }),
2974
- expiry && /* @__PURE__ */ jsxs21(Box22, { gap: 2, children: [
2975
- /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: "Exp: " }),
2976
- /* @__PURE__ */ jsx22(Text22, { color: "cyan", children: expiry.toLocaleDateString() })
3152
+ expiry && /* @__PURE__ */ jsxs22(Box23, { gap: 2, children: [
3153
+ /* @__PURE__ */ jsx23(Text23, { dimColor: true, children: "Exp: " }),
3154
+ /* @__PURE__ */ jsx23(Text23, { color: "cyan", children: expiry.toLocaleDateString() })
2977
3155
  ] })
2978
3156
  ] }),
2979
- /* @__PURE__ */ jsxs21(Box22, { gap: 1, children: [
2980
- /* @__PURE__ */ jsx22(Text22, { color: "green", bold: true, children: "\u2714" }),
2981
- /* @__PURE__ */ jsx22(Text22, { bold: true, children: "Authenticated" })
3157
+ /* @__PURE__ */ jsxs22(Box23, { gap: 1, children: [
3158
+ /* @__PURE__ */ jsx23(Text23, { color: "green", bold: true, children: "\u2714" }),
3159
+ /* @__PURE__ */ jsx23(Text23, { bold: true, children: "Authenticated" })
2982
3160
  ] })
2983
3161
  ] }),
2984
- state === "expired" && /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", gap: 1, children: [
2985
- /* @__PURE__ */ jsxs21(Box22, { gap: 1, children: [
2986
- /* @__PURE__ */ jsx22(Text22, { color: "red", children: "\u2716" }),
2987
- /* @__PURE__ */ jsxs21(Text22, { children: [
3162
+ state === "expired" && /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", gap: 1, children: [
3163
+ /* @__PURE__ */ jsxs22(Box23, { gap: 1, children: [
3164
+ /* @__PURE__ */ jsx23(Text23, { color: "red", children: "\u2716" }),
3165
+ /* @__PURE__ */ jsxs22(Text23, { children: [
2988
3166
  "Session expired",
2989
3167
  expiry ? ` (${expiry.toLocaleDateString()})` : ""
2990
3168
  ] })
2991
3169
  ] }),
2992
- /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: "Run `stackable-app-extension auth login` to re-authenticate." })
3170
+ /* @__PURE__ */ jsx23(Text23, { dimColor: true, children: "Run `stackable-app-extension auth login` to re-authenticate." })
2993
3171
  ] }),
2994
- state === "not-logged-in" && /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", gap: 1, children: [
2995
- /* @__PURE__ */ jsxs21(Box22, { gap: 1, children: [
2996
- /* @__PURE__ */ jsx22(Text22, { color: "red", children: "\u2716" }),
2997
- /* @__PURE__ */ jsx22(Text22, { children: "Not logged in" })
3172
+ state === "not-logged-in" && /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", gap: 1, children: [
3173
+ /* @__PURE__ */ jsxs22(Box23, { gap: 1, children: [
3174
+ /* @__PURE__ */ jsx23(Text23, { color: "red", children: "\u2716" }),
3175
+ /* @__PURE__ */ jsx23(Text23, { children: "Not logged in" })
2998
3176
  ] }),
2999
- /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: "Run `stackable-app-extension auth login`" })
3177
+ /* @__PURE__ */ jsx23(Text23, { dimColor: true, children: "Run `stackable-app-extension auth login`" })
3000
3178
  ] })
3001
3179
  ] })
3002
3180
  ] });
@@ -3050,7 +3228,7 @@ var checkForUpdate = (currentVersion) => {
3050
3228
  };
3051
3229
 
3052
3230
  // src/index.tsx
3053
- import { jsx as jsx23 } from "react/jsx-runtime";
3231
+ import { jsx as jsx24 } from "react/jsx-runtime";
3054
3232
  var require2 = createRequire(import.meta.url);
3055
3233
  var { version } = require2("../package.json");
3056
3234
  checkForUpdate(version);
@@ -3063,7 +3241,7 @@ var ensureToken = async () => {
3063
3241
  const message = err instanceof Error ? err.message : String(err);
3064
3242
  const isExpired = message.toLowerCase().includes("expired");
3065
3243
  render(
3066
- /* @__PURE__ */ jsx23(AuthStatus, { state: isExpired ? "expired" : "not-logged-in" })
3244
+ /* @__PURE__ */ jsx24(AuthStatus, { state: isExpired ? "expired" : "not-logged-in" })
3067
3245
  );
3068
3246
  return null;
3069
3247
  }
@@ -3076,7 +3254,7 @@ program.command("create" /* CREATE */).description("Create a new Extension proje
3076
3254
  }
3077
3255
  const { token, userId, orgId } = auth2;
3078
3256
  render(
3079
- /* @__PURE__ */ jsx23(
3257
+ /* @__PURE__ */ jsx24(
3080
3258
  App,
3081
3259
  {
3082
3260
  command: "create" /* CREATE */,
@@ -3096,7 +3274,7 @@ program.command("scaffold" /* SCAFFOLD */).description("Scaffold a local project
3096
3274
  }
3097
3275
  const { token, userId, orgId } = auth2;
3098
3276
  render(
3099
- /* @__PURE__ */ jsx23(
3277
+ /* @__PURE__ */ jsx24(
3100
3278
  App,
3101
3279
  {
3102
3280
  command: "scaffold" /* SCAFFOLD */,
@@ -3115,7 +3293,7 @@ program.command("update" /* UPDATE */).description("Update an existing Extension
3115
3293
  }
3116
3294
  const { token, userId, orgId } = auth2;
3117
3295
  render(
3118
- /* @__PURE__ */ jsx23(
3296
+ /* @__PURE__ */ jsx24(
3119
3297
  App,
3120
3298
  {
3121
3299
  command: "update" /* UPDATE */,
@@ -3135,7 +3313,7 @@ program.command("dev" /* DEV */).description("Start dev servers with a public tu
3135
3313
  }
3136
3314
  const { token, userId, orgId } = auth2;
3137
3315
  render(
3138
- /* @__PURE__ */ jsx23(
3316
+ /* @__PURE__ */ jsx24(
3139
3317
  DevApp,
3140
3318
  {
3141
3319
  options,
@@ -3150,17 +3328,17 @@ program.command("dev" /* DEV */).description("Start dev servers with a public tu
3150
3328
  var DASHBOARD_URL = process.env.ADMIN_DASHBOARD_URL ?? "https://admin.stackablelabs.io";
3151
3329
  var auth = program.command("auth").description("Manage CLI authentication");
3152
3330
  auth.command("login").description("Authenticate with Stackable via browser").action(async () => {
3153
- render(/* @__PURE__ */ jsx23(AuthLogin, { dashboardUrl: DASHBOARD_URL }));
3331
+ render(/* @__PURE__ */ jsx24(AuthLogin, { dashboardUrl: DASHBOARD_URL }));
3154
3332
  });
3155
3333
  auth.command("logout").description("Clear stored CLI credentials").action(async () => {
3156
3334
  await clearAuthState();
3157
- render(/* @__PURE__ */ jsx23(AuthLogout, {}));
3335
+ render(/* @__PURE__ */ jsx24(AuthLogout, {}));
3158
3336
  });
3159
3337
  auth.command("status").description("Show current authentication status").action(async () => {
3160
3338
  const state = await readAuthState();
3161
3339
  if (!state) {
3162
3340
  render(
3163
- /* @__PURE__ */ jsx23(AuthStatus, { state: "not-logged-in" })
3341
+ /* @__PURE__ */ jsx24(AuthStatus, { state: "not-logged-in" })
3164
3342
  );
3165
3343
  return;
3166
3344
  }
@@ -3168,11 +3346,11 @@ auth.command("status").description("Show current authentication status").action(
3168
3346
  const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
3169
3347
  const expiry = payload.exp ? new Date(payload.exp * 1e3) : null;
3170
3348
  if (expiry && Date.now() >= expiry.getTime()) {
3171
- render(/* @__PURE__ */ jsx23(AuthStatus, { state: "expired", expiry }));
3349
+ render(/* @__PURE__ */ jsx24(AuthStatus, { state: "expired", expiry }));
3172
3350
  return;
3173
3351
  }
3174
3352
  render(
3175
- /* @__PURE__ */ jsx23(
3353
+ /* @__PURE__ */ jsx24(
3176
3354
  AuthStatus,
3177
3355
  {
3178
3356
  state: "authenticated",
@@ -3185,6 +3363,6 @@ auth.command("status").description("Show current authentication status").action(
3185
3363
  });
3186
3364
  var ai = program.command("ai").description("AI editor configuration tools");
3187
3365
  ai.command("scaffold").description("Download AI editor config files into your Extension project").option("--version <version>", 'AI docs version (semver or "latest")', "latest").action(async (options) => {
3188
- render(/* @__PURE__ */ jsx23(AIScaffold, { version: options.version }));
3366
+ render(/* @__PURE__ */ jsx24(AIScaffold, { version: options.version }));
3189
3367
  });
3190
3368
  program.parse(process.argv.filter((arg) => arg !== "--"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackable-labs/cli-app-extension",
3
- "version": "1.29.0",
3
+ "version": "1.31.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  ],
14
14
  "dependencies": {
15
15
  "adm-zip": "0.x",
16
+ "clipboardy": "5.x",
16
17
  "cloudflared": "0.x",
17
18
  "commander": "12.x",
18
19
  "giget": "3.x",