okengine 0.19.4 → 0.19.6

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 (36) hide show
  1. package/package.json +1 -1
  2. package/site/content/docs/elements/flow/index.mdx +3 -6
  3. package/site/content/docs/reference/cli.mdx +20 -1
  4. package/site/content/docs/understand/try-it.mdx +1 -1
  5. package/src/cli/dev-app-runner.ts +23 -2
  6. package/src/cli/dev.test.ts +47 -0
  7. package/src/cli/dev.ts +11 -0
  8. package/src/compiler/extract-skip.test.ts +43 -0
  9. package/src/compiler/extract.ts +11 -4
  10. package/src/compiler/flow-path.test.ts +13 -0
  11. package/src/compiler/flow-path.ts +18 -0
  12. package/src/compiler/generate-adopt.ts +12 -2
  13. package/src/console/server/serve-static.test.ts +74 -0
  14. package/src/console/server/serve.ts +17 -3
  15. package/src/console/ui-next/dist/assets/{access-page-rO2HLPab.js → access-page-DZtT3hKL.js} +1 -1
  16. package/src/console/ui-next/dist/assets/{agent-disclosure-ae8w8JLx.js → agent-disclosure-BFnHhgjD.js} +1 -1
  17. package/src/console/ui-next/dist/assets/{cache-glyph-C-uHdh5d.js → cache-glyph-BguHj4PA.js} +1 -1
  18. package/src/console/ui-next/dist/assets/{call-pii-button-BJH54w4s.js → call-pii-button-DJmzLtht.js} +1 -1
  19. package/src/console/ui-next/dist/assets/{collapsible-CaE8cs9p.js → collapsible-BGuiLYBV.js} +1 -1
  20. package/src/console/ui-next/dist/assets/{duration-tone-DdOQkQVK.js → duration-tone-C6X7Gcfr.js} +1 -1
  21. package/src/console/ui-next/dist/assets/{flows-page-DWeBszQQ.js → flows-page-40_zEnuY.js} +1 -1
  22. package/src/console/ui-next/dist/assets/{highlighted-json-B2gBsC4B.js → highlighted-json-DPc-p38T.js} +1 -1
  23. package/src/console/ui-next/dist/assets/{http-method-BnMl0dJt.js → http-method-BaqumPKP.js} +1 -1
  24. package/src/console/ui-next/dist/assets/{index-CBAP48v5.js → index-B0GkEkAT.js} +3 -3
  25. package/src/console/ui-next/dist/assets/{observability-page-DVpcJEnW.js → observability-page-BYG33yY_.js} +1 -1
  26. package/src/console/ui-next/dist/assets/{replica-lag-BUQf2Nf6.js → replica-lag-BufW34k2.js} +1 -1
  27. package/src/console/ui-next/dist/assets/{request-meta-CPkGDQgT.js → request-meta-iGMEf-yI.js} +1 -1
  28. package/src/console/ui-next/dist/assets/{store-page-CwS_cY4V.js → store-page-BhAEVbQw.js} +1 -1
  29. package/src/console/ui-next/dist/assets/{trace-detail-sheet-DNX7Y2bd.js → trace-detail-sheet-HOK-3aLD.js} +1 -1
  30. package/src/console/ui-next/dist/assets/{tree-expand-toggle-Bhdk27aF.js → tree-expand-toggle-BV1Yf8iy.js} +1 -1
  31. package/src/console/ui-next/dist/assets/{units-page-CsBh0XbI.js → units-page-mTT6I9zg.js} +1 -1
  32. package/src/console/ui-next/dist/assets/{vault-page-CsTlPtK9.js → vault-page-Dn0Pqj_8.js} +1 -1
  33. package/src/console/ui-next/dist/index.html +1 -1
  34. package/src/kernel/boot.ts +9 -5
  35. package/src/kernel/effects-stamping.test.ts +44 -0
  36. package/src/kernel/errors.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okengine",
3
- "version": "0.19.4",
3
+ "version": "0.19.6",
4
4
  "description": "One law. Eight elements. One contract. The backend model stays small; operational surfaces are derived from it instead of maintained separately.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -570,12 +570,9 @@ Default is `true` once `gate.auth.tenant` is on.
570
570
  </Accordion>
571
571
 
572
572
  <Accordion title="OKE1020 — no declared effects">
573
- Cause: `Flow "{flow}" has no declared effects and no Manifest to derive them from.` Boot with `oke
574
- build` / `oke dev` so extract can infer. Framework / plugin Flows outside the app tree get an
575
- empty least-privilege stamp automatically when a Manifest is present — do not hand-write `effects:{" "}
576
- {}`. Real `fx` usage still needs inference (app source) or an explicit non-empty `effects` block
577
- when extract cannot see the body. If extract failed (`Manifest extract failed`), install
578
- `oxc-parser` and retry.
573
+ Cause: `Flow "{flow}" has no declared effects and no Manifest to derive them from.` Extract
574
+ failures append `Manifest extract failed — …`. Boot with `oke dev` / `oke build`; install
575
+ `oxc-parser` if extract cannot load. On Windows use `bun run dev` / `bunx oke dev`.
579
576
  </Accordion>
580
577
 
581
578
  </Accordions>
@@ -34,9 +34,11 @@ cd notes
34
34
  ### Run the dev loop
35
35
 
36
36
  ```bash
37
- oke dev
37
+ bun run dev
38
38
  ```
39
39
 
40
+ `bun run dev` is the portable form (`bunx oke dev` is the same). Bare `oke` needs `node_modules/.bin` on PATH — PowerShell does not add it.
41
+
40
42
  Compose comes up (pull / create / start progress streams into the boot status
41
43
  lines), the backend listens on **6530**, Console on **6533**, app MCP on **6535**,
42
44
  docs MCP on **6536**. Open `http://localhost:6533`. Quit with **Ctrl+C**.
@@ -177,6 +179,23 @@ oke db search-backfill notes --batch 500
177
179
 
178
180
  <Accordions>
179
181
 
182
+ <Accordion title="oke is not recognized (Windows PowerShell)">
183
+ PowerShell has no local `oke` on PATH. Use `bun run dev` or `bunx oke dev`. New Cursor/VS Code
184
+ terminals inherit `node_modules/.bin` from `.vscode/settings.json`. Global: `bun install -g
185
+ okengine`.
186
+ </Accordion>
187
+
188
+ <Accordion title="Console shows Shell assets not built">
189
+ `:6533` is up but the SPA files were not found. Published `okengine` ships them. Upgrade past the
190
+ Windows path bug (`file://` `/C:/…` lookup), then restart `bun run dev`. From a framework
191
+ checkout, run `bun run build` or keep `oke dev` so Vite HMR attaches.
192
+ </Accordion>
193
+
194
+ <Accordion title="OKE1020 on oke dev (Compose)">
195
+ The app child had no Manifest to stamp effects (`main.health`). `oke dev` now hands the parent
196
+ extract to the child. A failed extract appends `Manifest extract failed — …` (`oxc-parser`).
197
+ </Accordion>
198
+
180
199
  <Accordion title="oke start: no entry found">
181
200
  Set `package.json` `okengine.entry` or `main`, pass `--entry`, or keep a conventional
182
201
  `src/app.ts`. Production imports that module; the app must call `createBunRuntime().serve` itself.
@@ -18,7 +18,7 @@ docker info
18
18
  ```bash
19
19
  bunx create-oke@latest my-app
20
20
  cd my-app
21
- oke dev
21
+ bun run dev
22
22
  ```
23
23
 
24
24
  Open the Console at the address printed in the terminal and claim it with the code shown there. The Flows listed weren't configured anywhere — they were derived from the code you just scaffolded.
@@ -11,6 +11,9 @@
11
11
  * - `PORT` — listen port (`0` = ephemeral)
12
12
  * - `OKE_HOSTNAME` — listen hostname (default `127.0.0.1`)
13
13
  * - `OKE_READY_PATH` — when set, write bound port here once listening
14
+ * - `OKE_ROOT_DIR` — project root for Manifest extract (defaults to cwd)
15
+ * - `OKE_MANIFEST_PATH` — optional JSON Manifest from the parent `oke dev`
16
+ * extract (avoids a second extract in the child; Windows-safe fallback)
14
17
  *
15
18
  * Soft reload must not clear the TTY — the parent `oke dev` board owns
16
19
  * Docker status and the first-admin claim code.
@@ -19,6 +22,7 @@
19
22
  import { resolve } from "node:path";
20
23
  import { pathToFileURL } from "node:url";
21
24
  import { installGracefulShutdown, type GracefulShutdownApp } from "../kernel/graceful-shutdown.ts";
25
+ import type { Manifest } from "../manifest/types.ts";
22
26
  import { createBunRuntime } from "../runtime/bun.ts";
23
27
  import { APP_PORT, type FetchApp } from "../runtime/types.ts";
24
28
  import { formatAppReadyLine } from "../term.ts";
@@ -28,7 +32,7 @@ export const DEV_APP_SERVE_ID = "oke-dev-app";
28
32
 
29
33
  /** App entry shape — FetchApp plus boot before serve. */
30
34
  type BootableApp = FetchApp & {
31
- boot(): Promise<unknown>;
35
+ boot(overrides?: { readonly rootDir?: string; readonly manifest?: Manifest }): Promise<unknown>;
32
36
  stop(): Promise<void>;
33
37
  readonly bootResult?: GracefulShutdownApp["bootResult"];
34
38
  };
@@ -49,6 +53,20 @@ const readyPath = Bun.env["OKE_READY_PATH"];
49
53
  // already the project root here (Bun.spawn's `cwd` option in dev.ts).
50
54
  process.env["OKE_ROOT_DIR"] ??= process.cwd();
51
55
 
56
+ const manifestPath = Bun.env["OKE_MANIFEST_PATH"];
57
+ let manifest: Manifest | undefined;
58
+ if (manifestPath !== undefined && manifestPath.length > 0) {
59
+ try {
60
+ manifest = (await Bun.file(manifestPath).json()) as Manifest;
61
+ } catch (err) {
62
+ console.error(
63
+ `oke dev-app-runner: OKE_MANIFEST_PATH unreadable — ${
64
+ err instanceof Error ? err.message : String(err)
65
+ }`,
66
+ );
67
+ }
68
+ }
69
+
52
70
  const absoluteEntry = resolve(entry);
53
71
  const mod = (await import(pathToFileURL(absoluteEntry).href)) as {
54
72
  app?: BootableApp;
@@ -63,7 +81,10 @@ if (
63
81
  process.exit(1);
64
82
  }
65
83
 
66
- await mod.app.boot();
84
+ await mod.app.boot({
85
+ rootDir: process.env["OKE_ROOT_DIR"],
86
+ ...(manifest !== undefined ? { manifest } : {}),
87
+ });
67
88
  const handle = createBunRuntime().serve(mod.app, {
68
89
  port,
69
90
  hostname,
@@ -1070,8 +1070,55 @@ export const posts = store.schema.table("posts", {
1070
1070
  });
1071
1071
 
1072
1072
  describe("oke dev Console vault config", () => {
1073
+ let session: DevSession | undefined;
1074
+
1075
+ afterEach(() => {
1076
+ session?.stop();
1077
+ session = undefined;
1078
+ });
1079
+
1073
1080
  test("hands loaded oke.config to Console so drivers.vault is not the env default", async () => {
1074
1081
  const src = await Bun.file(new URL("./dev.ts", import.meta.url)).text();
1075
1082
  expect(src).toMatch(/okeConfig:\s*loadedConfig/);
1076
1083
  });
1084
+
1085
+ test("writes OKE_MANIFEST_PATH for the app child when a Manifest is available", async () => {
1086
+ const runner = await Bun.file(new URL("./dev-app-runner.ts", import.meta.url)).text();
1087
+ expect(runner).toContain("OKE_MANIFEST_PATH");
1088
+ expect(runner).toMatch(/mod\.app\.boot\(\{/);
1089
+
1090
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-manifest-handoff-"));
1091
+ await Bun.write(join(dir, "src/app.ts"), "export {}\n");
1092
+ await Bun.write(join(dir, "oke.manifest.json"), JSON.stringify(LIVE_MANIFEST));
1093
+ let seen: Record<string, string> | undefined;
1094
+ const result = await runDev({
1095
+ stdinIsTTY: false,
1096
+ cwd: dir,
1097
+ silentClaim: true,
1098
+ keepAlive: false,
1099
+ appPort: 0,
1100
+ consolePort: 0,
1101
+ mcpPort: 0,
1102
+ docsMcpPort: 0,
1103
+ ...stubCompose(),
1104
+ startApp: async (_entry, env) => {
1105
+ seen = env;
1106
+ return { stop() {} };
1107
+ },
1108
+ regenClient: async () => {},
1109
+ write: () => {},
1110
+ serveConsole: async () => ({ stop() {} }),
1111
+ serveMcp: async () => ({ stop() {} }),
1112
+ serveDocsMcp: async () => ({
1113
+ stop() {},
1114
+ port: 1,
1115
+ url: new URL("http://127.0.0.1:1"),
1116
+ }),
1117
+ });
1118
+ session = result.session;
1119
+ expect(result.code).toBe(0);
1120
+ expect(seen?.["OKE_MANIFEST_PATH"]).toBeTruthy();
1121
+ const dumped = JSON.parse(await Bun.file(seen!["OKE_MANIFEST_PATH"]!).text()) as Manifest;
1122
+ expect(dumped.app).toBe("dev-live");
1123
+ });
1077
1124
  });
package/src/cli/dev.ts CHANGED
@@ -1067,6 +1067,13 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
1067
1067
  const seedManifest =
1068
1068
  options.manifest !== undefined ? options.manifest : await tryLoadProjectManifest(cwd);
1069
1069
 
1070
+ let seedManifestPath: string | undefined;
1071
+ if (seedManifest) {
1072
+ seedManifestPath = join(tmpdir(), `oke-dev-manifest-${crypto.randomUUID()}.json`);
1073
+ await Bun.write(seedManifestPath, `${JSON.stringify(seedManifest)}\n`);
1074
+ env.OKE_MANIFEST_PATH = seedManifestPath;
1075
+ }
1076
+
1070
1077
  async function refreshManifestInto(state: ConsoleState | null): Promise<void> {
1071
1078
  if (!state) return;
1072
1079
  try {
@@ -1401,6 +1408,10 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
1401
1408
  consoleVite = null;
1402
1409
  void vite?.stop();
1403
1410
  void clearDevSessionLock(cwd);
1411
+ if (seedManifestPath) {
1412
+ void unlink(seedManifestPath).catch(() => {});
1413
+ seedManifestPath = undefined;
1414
+ }
1404
1415
  if (dockerStarted) {
1405
1416
  const started = dockerStarted;
1406
1417
  dockerStarted = null;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Manifest extract must not walk `node_modules` — Windows glob paths use `\`
3
+ * and a naive `includes("node_modules/")` miss lets framework fixtures throw
4
+ * during extract, which Docker-first `oke dev` surfaces as **OKE1020**.
5
+ */
6
+
7
+ import { describe, expect, test } from "bun:test";
8
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { extractManifest } from "./extract.ts";
12
+
13
+ describe("extractManifest — skip dependency trees", () => {
14
+ test("does not parse node_modules even when a poison file would fail extract", async () => {
15
+ const dir = await mkdtemp(join(tmpdir(), "oke-extract-skip-"));
16
+ try {
17
+ await mkdir(join(dir, "src/flows/main"), { recursive: true });
18
+ await writeFile(
19
+ join(dir, "src/flows/main/health.ts"),
20
+ `
21
+ import { on, flow, http } from "okengine/http";
22
+ export const health = on(
23
+ http.get("/health").public(),
24
+ flow({ do: () => ({ ok: true as const }) }),
25
+ );
26
+ `,
27
+ );
28
+ await mkdir(join(dir, "node_modules/okengine"), { recursive: true });
29
+ // Nameless Signal consumer — **OKE1072** if this file is scanned.
30
+ await writeFile(
31
+ join(dir, "node_modules/okengine/poison.ts"),
32
+ `
33
+ import { on, flow, signal } from "okengine";
34
+ export const inbound = on(signal.once("x"), flow({ do: () => ({}) }));
35
+ `,
36
+ );
37
+ const manifest = await extractManifest({ rootDir: dir });
38
+ expect(manifest.flows?.["main.health"]).toBeDefined();
39
+ } finally {
40
+ await rm(dir, { recursive: true, force: true });
41
+ }
42
+ });
43
+ });
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { parseSync } from "oxc-parser";
10
+ import { join } from "node:path";
10
11
 
11
12
  import {
12
13
  AI_NATIVE_DRIVER_IDS,
@@ -55,7 +56,13 @@ import {
55
56
  type InferBinding,
56
57
  type Literal,
57
58
  } from "./effects-infer.ts";
58
- import { isFlowsTreeFile, nameFromFlowFile, pathFromFlowFile } from "./flow-path.ts";
59
+ import {
60
+ isFlowsTreeFile,
61
+ isSkippedExtractSource,
62
+ nameFromFlowFile,
63
+ pathFromFlowFile,
64
+ toPosixPath,
65
+ } from "./flow-path.ts";
59
66
  import {
60
67
  defaultListInSchema,
61
68
  jsonSchemaFromAst,
@@ -295,9 +302,9 @@ async function readSources(rootDir: string, pattern: string): Promise<SourceFile
295
302
  cwd: rootDir,
296
303
  onlyFiles: true,
297
304
  })) {
298
- if (path.includes("node_modules/") || path.endsWith(".test.ts")) continue;
299
- const abs = `${rootDir.replace(/\/$/, "")}/${path}`;
300
- files.push({ path, source: await Bun.file(abs).text() });
305
+ if (isSkippedExtractSource(path)) continue;
306
+ const abs = join(rootDir, path);
307
+ files.push({ path: toPosixPath(path), source: await Bun.file(abs).text() });
301
308
  }
302
309
  files.sort((a, b) => a.path.localeCompare(b.path));
303
310
  return files;
@@ -6,6 +6,7 @@ import { describe, expect, test } from "bun:test";
6
6
  import {
7
7
  importSpecifierFromWalked,
8
8
  isFlowsTreeFile,
9
+ isSkippedExtractSource,
9
10
  nameFromFlowFile,
10
11
  pathFromFlowFile,
11
12
  toPosixPath,
@@ -117,3 +118,15 @@ describe("isFlowsTreeFile", () => {
117
118
  expect(isFlowsTreeFile("lib/inbound.ts")).toBe(false);
118
119
  });
119
120
  });
121
+
122
+ describe("isSkippedExtractSource", () => {
123
+ test("skips node_modules as a path segment, including Windows separators", () => {
124
+ expect(isSkippedExtractSource("node_modules/okengine/src/index.ts")).toBe(true);
125
+ expect(isSkippedExtractSource("node_modules\\okengine\\src\\index.ts")).toBe(true);
126
+ expect(isSkippedExtractSource("web/node_modules/vite/index.ts")).toBe(true);
127
+ expect(isSkippedExtractSource(".git/hooks/pre-commit.ts")).toBe(true);
128
+ expect(isSkippedExtractSource("src/flows/main/health.test.ts")).toBe(true);
129
+ expect(isSkippedExtractSource("src/flows/main/health.ts")).toBe(false);
130
+ expect(isSkippedExtractSource("src/core.ts")).toBe(false);
131
+ });
132
+ });
@@ -148,6 +148,24 @@ export function nameFromFlowFile(
148
148
  return `${unit}.${exportName}`;
149
149
  }
150
150
 
151
+ /**
152
+ * True when a walked source should be omitted from Manifest extract.
153
+ *
154
+ * `node_modules` / `.git` are path *segments* (POSIX-normalized) so Windows
155
+ * `node_modules\okengine\…` is skipped the same as `node_modules/okengine/…`.
156
+ * Scanning dependency trees would otherwise parse framework fixtures and
157
+ * throw during extract — Docker-first `oke dev` then hard-fails **OKE1020**.
158
+ *
159
+ * @param relPath - Glob-relative path (any separator)
160
+ */
161
+ export function isSkippedExtractSource(relPath: string): boolean {
162
+ const posix = toPosixPath(relPath);
163
+ const parts = posix.split("/").filter(Boolean);
164
+ if (parts.includes("node_modules") || parts.includes(".git")) return true;
165
+ const base = parts[parts.length - 1] ?? "";
166
+ return base.endsWith(".test.ts") || base.endsWith(".test.tsx");
167
+ }
168
+
151
169
  /**
152
170
  * True when the path is under a flows tree (`src/flows/…` or `…/flows/…`).
153
171
  *
@@ -14,7 +14,7 @@
14
14
  * `index` is also a reserved leaf).
15
15
  */
16
16
 
17
- import { mkdir, readdir, readFile, rename, writeFile } from "node:fs/promises";
17
+ import { mkdir, readdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
18
18
  import { join } from "node:path";
19
19
 
20
20
  import {
@@ -110,7 +110,17 @@ export async function writeAdoptBarrel(
110
110
  // missing — write
111
111
  }
112
112
  await writeFile(tmp, source);
113
- await rename(tmp, target);
113
+ try {
114
+ await rename(tmp, target);
115
+ } catch {
116
+ // Windows cannot rename onto an existing path (EEXIST / EPERM).
117
+ await writeFile(target, source);
118
+ try {
119
+ await unlink(tmp);
120
+ } catch {
121
+ // tmp already replaced or removed
122
+ }
123
+ }
114
124
  return { written: true };
115
125
  }
116
126
 
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Console SPA static files — filesystem paths, not `file://` URL pathnames.
3
+ *
4
+ * Windows `new URL(...).pathname` is `/C:/…`; concatenating that with
5
+ * `index.html` misses the built SPA and serves the fallback shell.
6
+ */
7
+
8
+ import { afterAll, describe, expect, test } from "bun:test";
9
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { resolveConsoleStaticDir, serveConsole, type ConsoleServerHandle } from "./serve.ts";
14
+
15
+ describe("resolveConsoleStaticDir", () => {
16
+ test("is a filesystem path next to this module, not a file URL pathname", () => {
17
+ const dir = resolveConsoleStaticDir();
18
+ const fromMeta = join(import.meta.dir, "../ui-next/dist");
19
+ const fromFileUrl = fileURLToPath(new URL("../ui-next/dist/", import.meta.url));
20
+ expect(dir).toBe(fromMeta);
21
+ expect(dir).toBe(fromFileUrl.replace(/[/\\]+$/, ""));
22
+ expect(dir.startsWith("file:")).toBe(false);
23
+ // URL.pathname on Windows is `/C:/Users/…` — not a Win32 path.
24
+ expect(dir).not.toMatch(/^\/[A-Za-z]:/);
25
+ });
26
+
27
+ test("keeps an explicit override", () => {
28
+ expect(resolveConsoleStaticDir("/tmp/console-spa")).toBe("/tmp/console-spa");
29
+ });
30
+ });
31
+
32
+ describe("serveConsole staticDir", () => {
33
+ let server: ConsoleServerHandle | undefined;
34
+ let cwd: string | undefined;
35
+ let staticDir: string | undefined;
36
+
37
+ afterAll(async () => {
38
+ await Promise.resolve(server?.stop(true));
39
+ if (cwd) await rm(cwd, { recursive: true, force: true });
40
+ if (staticDir) await rm(staticDir, { recursive: true, force: true });
41
+ });
42
+
43
+ test("serves index.html from a directory without a trailing separator", async () => {
44
+ cwd = await mkdtemp(join(tmpdir(), "oke-console-static-cwd-"));
45
+ staticDir = await mkdtemp(join(tmpdir(), "oke-console-static-spa-"));
46
+ await writeFile(join(staticDir, "index.html"), "<!doctype html><title>console-spa</title>");
47
+ await writeFile(join(staticDir, "app.js"), "export {}\n");
48
+
49
+ server = await serveConsole({
50
+ port: 0,
51
+ hostname: "127.0.0.1",
52
+ cwd,
53
+ secret: "static-dir-secret",
54
+ silentClaim: true,
55
+ env: "test",
56
+ persist: false,
57
+ staticDir,
58
+ });
59
+
60
+ const html = await server.fetch(
61
+ new Request(String(server.url), { headers: { host: "127.0.0.1" } }),
62
+ );
63
+ expect(html.status).toBe(200);
64
+ const body = await html.text();
65
+ expect(body).toContain("console-spa");
66
+ expect(body).not.toContain("Shell assets not built");
67
+
68
+ const asset = await server.fetch(
69
+ new Request(String(new URL("/app.js", server.url)), { headers: { host: "127.0.0.1" } }),
70
+ );
71
+ expect(asset.status).toBe(200);
72
+ expect(await asset.text()).toContain("export");
73
+ });
74
+ });
@@ -5,6 +5,7 @@
5
5
  * SameSite=Strict cookies are applied from the first response.
6
6
  */
7
7
 
8
+ import { join } from "node:path";
8
9
  import {
9
10
  checkRequestSecurity,
10
11
  forbiddenResponse,
@@ -65,6 +66,19 @@ export function isConsoleKernelPath(pathname: string): boolean {
65
66
  return pathname.startsWith("/console/") || pathname.startsWith("/plugin-frame/");
66
67
  }
67
68
 
69
+ /**
70
+ * Filesystem directory of the prebuilt Console SPA (`ui-next/dist`).
71
+ *
72
+ * Must be a real OS path. `new URL(...).pathname` on Windows is `/C:/…`, which
73
+ * `Bun.file` cannot open — the kernel then serves the "Shell assets not built"
74
+ * fallback even when the SPA is on disk.
75
+ *
76
+ * @param override - Explicit directory from {@link ServeConsoleOptions.staticDir}
77
+ */
78
+ export function resolveConsoleStaticDir(override?: string): string {
79
+ return override ?? join(import.meta.dir, "../ui-next/dist");
80
+ }
81
+
68
82
  /**
69
83
  * Use the caller's `oke.config` when given; otherwise load it from `cwd`
70
84
  * so Console sees `drivers.vault` (built-in vs the env default).
@@ -166,7 +180,7 @@ export async function serveConsole(
166
180
  await bindManifestStoreRuntime(handle.state);
167
181
  await bindManifestVaultRuntime(handle.state);
168
182
 
169
- const staticDir = options.staticDir ?? new URL("../ui-next/dist/", import.meta.url).pathname;
183
+ const staticDir = resolveConsoleStaticDir(options.staticDir);
170
184
 
171
185
  const live = createLiveWebsocket(handle.state);
172
186
  const allowed = resolveAllowedHosts(hostname, options.allowedHosts);
@@ -210,7 +224,7 @@ export async function serveConsole(
210
224
  }
211
225
 
212
226
  const spa = isConsoleSpaPath(url.pathname);
213
- const index = Bun.file(`${staticDir}index.html`);
227
+ const index = Bun.file(join(staticDir, "index.html"));
214
228
  if (await index.exists()) {
215
229
  return withConsoleSecurityHeaders(
216
230
  new Response(index, {
@@ -430,7 +444,7 @@ export async function proxySpa(origin: string, request: Request): Promise<Respon
430
444
  async function serveStatic(staticDir: string, pathname: string): Promise<Response | null> {
431
445
  if (pathname.includes("..")) return null;
432
446
  const path = pathname === "/" ? "index.html" : pathname.replace(/^\//, "");
433
- const file = Bun.file(`${staticDir}${path}`);
447
+ const file = Bun.file(join(staticDir, path));
434
448
  if (!(await file.exists())) return null;
435
449
  return new Response(file);
436
450
  }
@@ -1,4 +1,4 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{r as t}from"./react-dom-Ddte4I-Q.js";import{A as n}from"./xyflow-yApv7D4e.js";import{Cr as r,Dr as i,E as a,Er as o,F as s,Fr as c,I as l,Ir as u,Lr as d,Nr as f,Pr as p,R as m,Rr as h,Sr as g,Tr as _,Vr as v,Yi as y,_r as b,ar as x,br as S,c as C,cr as w,dr as T,ha as ee,i as E,kr as D,l as O,lr as k,mr as A,nr as j,o as M,pr as N,rr as P,s as F,sr as I,tr as L,u as te,ur as R,vr as z,wr as B,x as V,xr as H}from"./shortcut-keys-3ILd8oGn.js";import{i as ne,t as re}from"./http-method-BnMl0dJt.js";import{a as ie,c as ae,h as oe,i as se,n as ce,o as le,r as ue,s as U,t as de}from"./element-icons-BI8cJgdh.js";import{t as fe}from"./UserIcon-DaE2PB5_.js";import{t as pe}from"./PlusSignIcon-CwG3nxfu.js";import{bt as W,ct as me,it as he}from"./index-CBAP48v5.js";import{t as ge}from"./explorer-empty-CJs5A-wm.js";import{a as G,c as _e,l as K,n as q,o as ve,r as ye,s as be,t as xe}from"./detail-header-DVWNjWjg.js";import{n as Se,t as J}from"./copy-inline-button-CAYD18cr.js";var Ce=[[`path`,{d:`M5 21H19`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`0`}],[`path`,{d:`M12.125 12.75H12M12.25 12.75C12.25 12.8881 12.1381 13 12 13C11.8619 13 11.75 12.8881 11.75 12.75C11.75 12.6119 11.8619 12.5 12 12.5C12.1381 12.5 12.25 12.6119 12.25 12.75Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`1`}],[`path`,{d:`M14.9152 7.61089L13.8078 5.38179C13.019 3.79393 12.6246 3 12 3C11.3754 3 10.981 3.79393 10.1922 5.38179L9.08483 7.61089C8.58107 8.62494 8.32919 9.13197 7.87976 9.24608C7.8485 9.25401 7.81689 9.26043 7.78503 9.26533C7.32682 9.3357 6.89919 8.96678 6.04393 8.22895C4.0124 6.47635 2.99663 5.60004 2.38034 5.94899C2.34045 5.97157 2.30213 5.99686 2.26565 6.02467C1.70197 6.45439 2.09541 7.74136 2.88229 10.3153L4.04783 14.1279C4.47098 15.5121 4.68255 16.2042 5.21787 16.6021C5.75318 17 6.47261 17 7.91147 17L16.0886 16.9999C17.5274 16.9999 18.2468 16.9999 18.7821 16.602C19.3175 16.2041 19.529 15.512 19.9522 14.1279L21.1177 10.3153C21.9046 7.74137 22.298 6.4544 21.7344 6.02468C21.6979 5.99687 21.6595 5.97158 21.6197 5.94899C21.0034 5.60006 19.9876 6.47636 17.9561 8.22896C17.1008 8.96679 16.6732 9.3357 16.215 9.26533C16.1831 9.26043 16.1515 9.25401 16.1202 9.24607C15.6708 9.13197 15.4189 8.62494 14.9152 7.61089Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`2`}]],Y=e(t(),1);function we(e,t){if(e==null)return`never`;let n=e-t;if(Math.abs(n)<6e4)return`just now`;let r=Ee(Math.abs(n));return n<0?`${r} ago`:`in ${r}`}function Te(e,t){return e==null?`never`:e<=t?`expired`:we(e,t)}function Ee(e){if(e<36e5)return`${Math.floor(e/6e4)}m`;if(e<864e5)return`${Math.floor(e/36e5)}h`;let t=Math.floor(e/864e5);return t<100?`${t}d`:`${Math.floor(t/30)}mo`}var X=864e5;function De(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());if(!t)return 0;let n=Number(t[1]);switch(t[2]){case`ms`:return n;case`s`:return n*1e3;case`m`:return n*6e4;case`h`:return n*36e5;case`d`:return n*X;default:return 0}}function Oe(e,t,n=``){if(e===`never`)return null;if(e===`30d`)return t+30*X;if(e===`90d`)return t+90*X;let r=De(n);return r>0?t+r:null}var Z=36e5;function ke(e,t){if(e==null)return{choice:`never`,custom:``};let n=e-t;return n<=0?{choice:`never`,custom:``}:Math.abs(n-30*X)<Z?{choice:`30d`,custom:``}:Math.abs(n-90*X)<Z?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}var Ae=7*X;function je(e,t){if(e==null||e<=t)return{choice:`90d`,custom:``};let n=e-t;return Math.abs(n-30*X)<=Ae?{choice:`30d`,custom:``}:Math.abs(n-90*X)<=Ae?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}function Me(e){return e<=0?``:e>=X&&e%X===0?`${e/X}d`:e>=Z&&e%Z===0?`${e/Z}h`:e>=6e4&&e%6e4==0?`${e/6e4}m`:e>=X?`${Math.max(1,Math.round(e/X))}d`:e>=Z?`${Math.max(1,Math.round(e/Z))}h`:`${Math.max(1,Math.round(e/6e4))}m`}var Ne=[`s`,`m`,`h`,`d`,`ms`],Pe={s:`Seconds`,m:`Minutes`,h:`Hours`,d:`Days`,ms:`Milliseconds`};function Fe(e,t,n){let r=e.trim(),i=t.trim();if(r.length===0&&i.length===0)return null;if(r.length===0||i.length===0||!/^\d+$/.test(r)||!/^\d+$/.test(i))return;let a=Number(r),o=Number(i);if(a<=0||o<=0)return;let s=`${o}${n}`;if(!(De(s)<=0))return{max:a,per:s}}function Ie(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());return t?{count:t[1],unit:t[2]}:{count:``,unit:`m`}}function Le(e){let t=ze(e);return t.length===0?null:Be(t)?`ip`:Ve(t)?`host`:null}function Re(e){let t=new Set,n=[];for(let r of e.split(/[\s,]+/)){let e=ze(r);e.length===0||t.has(e)||Le(e)!=null&&(t.add(e),n.push(e))}return n}function ze(e){let t=e.trim().toLowerCase();if(t=t.replace(/^https?:\/\//,``),t.startsWith(`[`)&&t.includes(`]`)){let e=t.indexOf(`]`),n=t.slice(1,e),r=t.slice(e+1);t=r.startsWith(`:`)||r.startsWith(`/`)?n:`${n}${r}`}let n=t.indexOf(`/`);return n>=0&&(t=t.slice(0,n)),t.endsWith(`.`)&&(t=t.slice(0,-1)),t}function Be(e){return/^\d{1,3}(?:\.\d{1,3}){3}$/.test(e)?e.split(`.`).every(e=>Number(e)<=255):!e.includes(`:`)||!/^[0-9a-f:]+$/.test(e)||e.split(`::`).length>2?!1:e.split(`:`).filter(e=>e.length>0).length>=1}function Ve(e){if(e===`localhost`)return!0;if(e.length>253||e.includes(`:`))return!1;let t=e.split(`.`);return t.length<2?!1:t.every((e,n)=>!(e.length===0||e.length>63||!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(e)||n===t.length-1&&!/^[a-z]{2,63}$/.test(e)))}function He(e){let t=[],n=new Map;for(let r of e){let e=r.indexOf(`:`),i=e<=0?r:r.slice(0,e),a=e<=0?r:r.slice(e+1),o=n.get(i);if(o){o.push({scope:r,action:a});continue}t.push(i),n.set(i,[{scope:r,action:a}])}return t.map(e=>({group:e,items:n.get(e)??[]}))}var Q=n(),Ue=`GET /secure
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{r as t}from"./react-dom-Ddte4I-Q.js";import{A as n}from"./xyflow-yApv7D4e.js";import{Cr as r,Dr as i,E as a,Er as o,F as s,Fr as c,I as l,Ir as u,Lr as d,Nr as f,Pr as p,R as m,Rr as h,Sr as g,Tr as _,Vr as v,Yi as y,_r as b,ar as x,br as S,c as C,cr as w,dr as T,ha as ee,i as E,kr as D,l as O,lr as k,mr as A,nr as j,o as M,pr as N,rr as P,s as F,sr as I,tr as L,u as te,ur as R,vr as z,wr as B,x as V,xr as H}from"./shortcut-keys-3ILd8oGn.js";import{i as ne,t as re}from"./http-method-BaqumPKP.js";import{a as ie,c as ae,h as oe,i as se,n as ce,o as le,r as ue,s as U,t as de}from"./element-icons-BI8cJgdh.js";import{t as fe}from"./UserIcon-DaE2PB5_.js";import{t as pe}from"./PlusSignIcon-CwG3nxfu.js";import{bt as W,ct as me,it as he}from"./index-B0GkEkAT.js";import{t as ge}from"./explorer-empty-CJs5A-wm.js";import{a as G,c as _e,l as K,n as q,o as ve,r as ye,s as be,t as xe}from"./detail-header-DVWNjWjg.js";import{n as Se,t as J}from"./copy-inline-button-CAYD18cr.js";var Ce=[[`path`,{d:`M5 21H19`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`0`}],[`path`,{d:`M12.125 12.75H12M12.25 12.75C12.25 12.8881 12.1381 13 12 13C11.8619 13 11.75 12.8881 11.75 12.75C11.75 12.6119 11.8619 12.5 12 12.5C12.1381 12.5 12.25 12.6119 12.25 12.75Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`1`}],[`path`,{d:`M14.9152 7.61089L13.8078 5.38179C13.019 3.79393 12.6246 3 12 3C11.3754 3 10.981 3.79393 10.1922 5.38179L9.08483 7.61089C8.58107 8.62494 8.32919 9.13197 7.87976 9.24608C7.8485 9.25401 7.81689 9.26043 7.78503 9.26533C7.32682 9.3357 6.89919 8.96678 6.04393 8.22895C4.0124 6.47635 2.99663 5.60004 2.38034 5.94899C2.34045 5.97157 2.30213 5.99686 2.26565 6.02467C1.70197 6.45439 2.09541 7.74136 2.88229 10.3153L4.04783 14.1279C4.47098 15.5121 4.68255 16.2042 5.21787 16.6021C5.75318 17 6.47261 17 7.91147 17L16.0886 16.9999C17.5274 16.9999 18.2468 16.9999 18.7821 16.602C19.3175 16.2041 19.529 15.512 19.9522 14.1279L21.1177 10.3153C21.9046 7.74137 22.298 6.4544 21.7344 6.02468C21.6979 5.99687 21.6595 5.97158 21.6197 5.94899C21.0034 5.60006 19.9876 6.47636 17.9561 8.22896C17.1008 8.96679 16.6732 9.3357 16.215 9.26533C16.1831 9.26043 16.1515 9.25401 16.1202 9.24607C15.6708 9.13197 15.4189 8.62494 14.9152 7.61089Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`2`}]],Y=e(t(),1);function we(e,t){if(e==null)return`never`;let n=e-t;if(Math.abs(n)<6e4)return`just now`;let r=Ee(Math.abs(n));return n<0?`${r} ago`:`in ${r}`}function Te(e,t){return e==null?`never`:e<=t?`expired`:we(e,t)}function Ee(e){if(e<36e5)return`${Math.floor(e/6e4)}m`;if(e<864e5)return`${Math.floor(e/36e5)}h`;let t=Math.floor(e/864e5);return t<100?`${t}d`:`${Math.floor(t/30)}mo`}var X=864e5;function De(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());if(!t)return 0;let n=Number(t[1]);switch(t[2]){case`ms`:return n;case`s`:return n*1e3;case`m`:return n*6e4;case`h`:return n*36e5;case`d`:return n*X;default:return 0}}function Oe(e,t,n=``){if(e===`never`)return null;if(e===`30d`)return t+30*X;if(e===`90d`)return t+90*X;let r=De(n);return r>0?t+r:null}var Z=36e5;function ke(e,t){if(e==null)return{choice:`never`,custom:``};let n=e-t;return n<=0?{choice:`never`,custom:``}:Math.abs(n-30*X)<Z?{choice:`30d`,custom:``}:Math.abs(n-90*X)<Z?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}var Ae=7*X;function je(e,t){if(e==null||e<=t)return{choice:`90d`,custom:``};let n=e-t;return Math.abs(n-30*X)<=Ae?{choice:`30d`,custom:``}:Math.abs(n-90*X)<=Ae?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}function Me(e){return e<=0?``:e>=X&&e%X===0?`${e/X}d`:e>=Z&&e%Z===0?`${e/Z}h`:e>=6e4&&e%6e4==0?`${e/6e4}m`:e>=X?`${Math.max(1,Math.round(e/X))}d`:e>=Z?`${Math.max(1,Math.round(e/Z))}h`:`${Math.max(1,Math.round(e/6e4))}m`}var Ne=[`s`,`m`,`h`,`d`,`ms`],Pe={s:`Seconds`,m:`Minutes`,h:`Hours`,d:`Days`,ms:`Milliseconds`};function Fe(e,t,n){let r=e.trim(),i=t.trim();if(r.length===0&&i.length===0)return null;if(r.length===0||i.length===0||!/^\d+$/.test(r)||!/^\d+$/.test(i))return;let a=Number(r),o=Number(i);if(a<=0||o<=0)return;let s=`${o}${n}`;if(!(De(s)<=0))return{max:a,per:s}}function Ie(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());return t?{count:t[1],unit:t[2]}:{count:``,unit:`m`}}function Le(e){let t=ze(e);return t.length===0?null:Be(t)?`ip`:Ve(t)?`host`:null}function Re(e){let t=new Set,n=[];for(let r of e.split(/[\s,]+/)){let e=ze(r);e.length===0||t.has(e)||Le(e)!=null&&(t.add(e),n.push(e))}return n}function ze(e){let t=e.trim().toLowerCase();if(t=t.replace(/^https?:\/\//,``),t.startsWith(`[`)&&t.includes(`]`)){let e=t.indexOf(`]`),n=t.slice(1,e),r=t.slice(e+1);t=r.startsWith(`:`)||r.startsWith(`/`)?n:`${n}${r}`}let n=t.indexOf(`/`);return n>=0&&(t=t.slice(0,n)),t.endsWith(`.`)&&(t=t.slice(0,-1)),t}function Be(e){return/^\d{1,3}(?:\.\d{1,3}){3}$/.test(e)?e.split(`.`).every(e=>Number(e)<=255):!e.includes(`:`)||!/^[0-9a-f:]+$/.test(e)||e.split(`::`).length>2?!1:e.split(`:`).filter(e=>e.length>0).length>=1}function Ve(e){if(e===`localhost`)return!0;if(e.length>253||e.includes(`:`))return!1;let t=e.split(`.`);return t.length<2?!1:t.every((e,n)=>!(e.length===0||e.length>63||!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(e)||n===t.length-1&&!/^[a-z]{2,63}$/.test(e)))}function He(e){let t=[],n=new Map;for(let r of e){let e=r.indexOf(`:`),i=e<=0?r:r.slice(0,e),a=e<=0?r:r.slice(e+1),o=n.get(i);if(o){o.push({scope:r,action:a});continue}t.push(i),n.set(i,[{scope:r,action:a}])}return t.map(e=>({group:e,items:n.get(e)??[]}))}var Q=n(),Ue=`GET /secure
2
2
  Authorization: Bearer <secret>`,We=`{ "ok": true }`;function Ge({keyRow:e,blast:t,now:n,leading:r,onEdit:a,onRefresh:s,onRevoke:c,onRotate:l,rotatePending:u}){let d=e.revokedAt!==null,f=e.expiresAt!==null&&e.expiresAt<=n,p=t?.lastUsedAt??e.lastUsedAt,m=t?.callVolume??0,h=Te(e.expiresAt,n),g=we(p,n),_=e.rateLimit?`${e.rateLimit.max} / ${e.rateLimit.per}`:`none`,v=(0,Y.useMemo)(()=>He(e.scopes),[e.scopes]),y=e.creatorScopes.length,b=y>0&&e.scopes.length<y;return(0,Q.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col overflow-y-auto`,"data-slot":`access-detail`,"aria-label":`Access key detail`,"aria-live":`polite`,children:[(0,Q.jsx)(xe,{dataSlot:`access-detail-header`,leading:r,icon:(0,Q.jsx)(i,{icon:oe,className:`size-4`}),title:e.name,badge:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`span`,{className:`inline-flex items-center gap-1 font-mono text-[10px] font-medium tracking-[0.08em] text-muted-foreground uppercase`,children:[(0,Q.jsx)(i,{icon:e.plane===`operator`?Ce:fe,className:`size-3`,"aria-hidden":!0}),e.plane]}),d?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-destructive`,children:`revoked`}):null,f&&!d?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-amber-700 dark:text-amber-400`,children:`expired`}):null,e.unused90d&&!d&&!f?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-amber-700 dark:text-amber-400`,children:`unused`}):null]}),subtitle:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(`code`,{className:`min-w-0 truncate font-mono text-[11px] leading-none text-foreground/80`,children:e.id}),(0,Q.jsx)(J,{value:e.id,label:`Copy ${e.id}`})]}),actions:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{label:`Rename or re-attenuate scopes`,className:`flex self-stretch`,children:(0,Q.jsx)(V,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,disabled:d,onClick:a,children:`Edit`})}),(0,Q.jsx)(U,{label:`Reset expiry from now. The secret stays the same.`,className:`flex self-stretch`,children:(0,Q.jsx)(V,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,"data-slot":`access-refresh`,disabled:d,onClick:s,children:`Refresh`})}),(0,Q.jsx)(U,{label:`Mint a new secret. The current secret stops working after residual TTL.`,className:`flex self-stretch`,children:(0,Q.jsx)(V,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,disabled:u||d,onClick:l,children:`Rotate`})}),(0,Q.jsx)(U,{label:`Irreversible. Residual sessions may continue for the access TTL.`,className:`flex self-stretch`,children:(0,Q.jsx)(V,{type:`button`,variant:`destructive`,size:`sm`,className:`h-full rounded-none`,disabled:d,onClick:c,children:`Revoke`})})]})}),(0,Q.jsx)(Ke,{revoked:d,expired:f,unused:e.unused90d,residual:t?.residualAccessNote}),(0,Q.jsxs)(`div`,{className:z,"data-slot":`access-briefing`,role:`group`,"aria-label":`Usage`,children:[(0,Q.jsx)(Xe,{label:`calls`,value:String(m)}),(0,Q.jsx)(Xe,{label:`used`,value:g,hint:p?new Date(p).toISOString():`No recorded call`,warn:p==null&&!d}),(0,Q.jsx)(Xe,{label:`expires`,value:h,hint:e.expiresAt?new Date(e.expiresAt).toISOString():`No expiry`,warn:f&&!d}),(0,Q.jsx)(Xe,{label:`rate`,value:_})]}),(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Scopes`,children:[(0,Q.jsx)($,{title:`Scopes`,meta:b?`${e.scopes.length} of ${y}`:String(e.scopes.length),children:e.scopes.length>0?(0,Q.jsx)(J,{value:e.scopes.join(`
3
3
  `),label:`Copy scopes`}):null}),v.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`No scopes granted.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-scopes`,children:v.map(e=>(0,Q.jsxs)(`li`,{className:A,"aria-label":e.group,children:[(0,Q.jsx)(`span`,{className:o(B,`w-[5.5rem] shrink-0`),children:e.group}),(0,Q.jsx)(`div`,{className:`flex min-w-0 flex-1 flex-wrap items-center gap-x-2`,children:e.items.map(e=>(0,Q.jsx)(`span`,{title:e.scope,className:`font-mono text-[11px] text-foreground`,children:e.action},e.scope))}),(0,Q.jsx)(Ze,{value:e.items.map(e=>e.scope).join(`
4
4
  `),label:`Copy ${e.group} scopes`})]},e.group))})]}),(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Key`,children:[(0,Q.jsx)($,{title:`Key`}),(0,Q.jsxs)(`dl`,{"data-slot":`access-key-facts`,children:[(0,Q.jsx)(Qe,{label:`Issuer`,value:e.creatorId,copy:e.creatorId,copyLabel:`Copy ${e.creatorId}`}),(0,Q.jsx)(Qe,{label:`Created`,value:we(e.createdAt,n),hint:new Date(e.createdAt).toISOString()}),(0,Q.jsx)(Qe,{label:`Expires`,value:h,hint:e.expiresAt?new Date(e.expiresAt).toISOString():void 0,warn:f&&!d}),(0,Q.jsx)(Qe,{label:`Rate`,value:_})]})]}),(0,Q.jsx)(qe,{entries:e.ipAllowlist}),(0,Q.jsx)(Je,{sourceAddresses:t?.sourceAddresses??[],residual:t?.residualAccessNote,lastUsed:p,now:n}),(0,Q.jsx)(Ye,{scopes:e.scopes})]})}function Ke({revoked:e,expired:t,unused:n,residual:r}){return e?(0,Q.jsx)(`section`,{className:o(z,`border-destructive/25 bg-destructive/5`),"aria-label":`Revoked`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-destructive`,children:[`Revoked`,(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[` `,`· `,r??`This secret no longer authenticates.`]})]})}):t?(0,Q.jsx)(`section`,{className:o(z,`border-amber-500/25 bg-amber-500/5`),"aria-label":`Expired`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-amber-900 dark:text-amber-300`,children:[`Expired`,r?(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[` · `,r]}):null]})}):n?(0,Q.jsx)(`section`,{className:o(z,`border-amber-500/25 bg-amber-500/5`),"aria-label":`Unused`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-amber-900 dark:text-amber-300`,children:[`No calls in 90 days`,(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:` · rotate or revoke`})]})}):null}function qe({entries:e}){return(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Allow`,children:[(0,Q.jsx)($,{title:`Allow`,meta:e.length>0?String(e.length):`any`}),e.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`Any origin.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-allow`,children:e.map(e=>{let t=Le(e);return(0,Q.jsxs)(`li`,{className:A,children:[(0,Q.jsx)(`span`,{className:o(B,`w-[5.5rem] shrink-0`),children:t===`ip`?`IP`:t===`host`?`Host`:`—`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px]`,children:e}),(0,Q.jsx)(Ze,{value:e,label:`Copy ${e}`})]},e)})})]})}function Je({sourceAddresses:e,residual:t,lastUsed:n,now:r}){return(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Usage`,children:[(0,Q.jsx)($,{title:`Usage`,meta:n?we(n,r):`never`}),e.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`No observed source IPs.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-sources`,children:e.map(e=>(0,Q.jsxs)(`li`,{className:A,children:[(0,Q.jsx)(`span`,{className:o(B,`w-[5.5rem] shrink-0`),children:`IP`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px]`,children:e}),(0,Q.jsx)(Ze,{value:e,label:`Copy ${e}`})]},e))}),t?(0,Q.jsx)(`p`,{className:`border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground`,children:t}):null]})}function Ye({scopes:e}){return(0,Q.jsxs)(`section`,{"data-slot":`access-call-example`,children:[(0,Q.jsx)($,{title:`Request`,children:(0,Q.jsx)(J,{value:Ue,label:`Copy request`})}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 border-b border-border/60`,children:[(0,Q.jsx)(`div`,{className:o(`w-1 shrink-0 self-stretch`,ne(`GET`)),"aria-hidden":!0}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 px-2.5 py-2`,children:[(0,Q.jsx)(`span`,{className:o(`shrink-0 font-mono text-[10px] font-semibold tracking-[0.08em] uppercase`,_(re(`GET`))),children:`GET`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-xs text-foreground select-all`,children:`/secure`})]}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 border-t border-border/60 px-2.5 py-2`,children:[(0,Q.jsx)(`span`,{className:`w-[7.5rem] shrink-0 truncate font-mono text-[11px] font-medium text-sky-600 dark:text-sky-400`,children:`Authorization`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px] text-muted-foreground`,children:`Bearer <secret>`})]})]})]}),(0,Q.jsx)($,{title:`Response`,children:(0,Q.jsx)(J,{value:We,label:`Copy response`})}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 border-b border-border/60`,children:[(0,Q.jsx)(`div`,{className:`w-1 shrink-0 self-stretch bg-emerald-500`,"aria-hidden":!0}),(0,Q.jsx)(`p`,{className:`min-w-0 flex-1 px-2.5 py-2 font-mono text-[11px] text-foreground/80 select-all`,children:We})]}),(0,Q.jsxs)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:[`Seed the Bearer secret from create / rotate. The key is the issuer; gates see`,` `,e.join(`, `)||`its scopes`,`.`]})]})}function $({title:e,meta:t,children:n}){return(0,Q.jsxs)(`header`,{className:o(z,`items-center gap-2 px-2`),children:[(0,Q.jsx)(`h3`,{className:o(B,`shrink-0 leading-none`),children:e}),t?(0,Q.jsx)(`span`,{className:o(x,!n&&`ml-auto`),children:t}):null,n?(0,Q.jsx)(`div`,{className:`ml-auto flex items-center`,children:n}):null]})}function Xe({label:e,value:t,hint:n,warn:r=!1}){return(0,Q.jsxs)(`span`,{title:n,className:o(S,H,`gap-1.5`),children:[(0,Q.jsx)(`span`,{className:`tracking-[0.08em] uppercase`,children:e}),(0,Q.jsx)(`span`,{className:o(`font-mono tabular-nums text-foreground`,r&&`text-amber-800 dark:text-amber-400`),children:t})]})}function Ze({value:e,label:t}){return(0,Q.jsx)(`div`,{className:`opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100`,children:(0,Q.jsx)(J,{value:e,label:t})})}function Qe({label:e,value:t,hint:n,copy:r,copyLabel:i,warn:a=!1}){return(0,Q.jsxs)(`div`,{className:A,children:[(0,Q.jsx)(`dt`,{className:o(B,`w-[5.5rem] shrink-0`),children:e}),(0,Q.jsx)(`dd`,{className:o(`min-w-0 flex-1 truncate font-mono text-[11px]`,a&&`text-amber-800 dark:text-amber-400`),title:n,children:t}),r?(0,Q.jsx)(Ze,{value:r,label:i??`Copy ${e}`}):null]})}var $e=[`console.access.list`];function et(){return y({queryKey:$e,queryFn:async()=>{let e=await c();if(e.error||!e.data)throw Error(e.error?.message??`Empty access list`);return e.data}})}function tt({rows:e,selectedKey:t,query:n,loading:r=!1,onSelect:i}){let a=e.filter(e=>e.plane===`user`),o=e.filter(e=>e.plane===`operator`);return(0,Q.jsxs)(`div`,{id:`access-list`,"data-slot":`access-list`,className:`min-h-0 flex-1 overflow-y-auto`,children:[r&&e.length===0?(0,Q.jsx)(`p`,{className:R,children:`Loading…`}):null,!r&&e.length===0&&n.trim().length>0?(0,Q.jsx)(`p`,{className:R,children:`No keys match.`}):null,(0,Q.jsx)(nt,{label:`User`,icon:fe,rows:a,selectedKey:t,onSelect:i}),(0,Q.jsx)(nt,{label:`Operator`,icon:Ce,rows:o,selectedKey:t,onSelect:i})]})}function nt(e){return(0,Q.jsxs)(`section`,{className:L,"aria-label":e.label,children:[(0,Q.jsxs)(`div`,{className:j,children:[(0,Q.jsx)(i,{icon:e.icon,className:o(k,`text-muted-foreground`),"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:o(P,`flex-1`),children:e.label}),(0,Q.jsx)(`span`,{className:x,children:e.rows.length})]}),e.rows.map(t=>{let n=t.id===e.selectedKey,r=t.revokedAt!==null;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-key-row`,className:o(A,n&&`bg-muted/70 text-foreground`),onClick:()=>e.onSelect(t.id),children:[(0,Q.jsx)(`span`,{className:o(N,n&&`bg-sky-500`)}),(0,Q.jsx)(i,{icon:oe,className:o(k,`text-muted-foreground`)}),(0,Q.jsx)(`span`,{className:o(`min-w-0 truncate`,r&&`text-muted-foreground line-through`),children:t.name}),r?(0,Q.jsx)(`span`,{className:`ml-auto text-[10px] text-muted-foreground`,children:`revoked`}):t.unused90d?(0,Q.jsx)(`span`,{className:`ml-auto text-[10px] text-amber-700 dark:text-amber-400`,children:`unused`}):(0,Q.jsx)(`span`,{className:o(`ml-auto tabular-nums text-[10px] text-muted-foreground`),children:t.scopes.length})]},t.id)})]})}function rt({entries:e,rateMax:t,rateCount:n,rateUnit:r,onEntries:c,onRateMax:u,onRateCount:d,onRateUnit:f}){let[p,h]=(0,Y.useState)(``),_=Re(p),v=p.trim().length>0&&_.length===0,y=()=>{if(_.length===0)return;let t=new Set(e),n=[...e];for(let e of _)t.has(e)||(t.add(e),n.push(e));c(n),h(``)};return(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-allow-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Allow`}),(0,Q.jsx)(a,{value:p,onChange:e=>h(e.target.value),onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),y())},placeholder:`203.0.113.4 or ci.example.com`,autoComplete:`off`,"aria-label":`Add allow entry`,"aria-invalid":v,flat:!0,className:o(q,`min-w-0 flex-1 font-mono`)}),e.length>0?(0,Q.jsx)(`span`,{className:o(x,`flex h-8 items-center pr-2`),children:e.length}):null,(0,Q.jsx)(`button`,{type:`button`,className:o(w,`h-8`),"aria-label":`Add allow entry`,disabled:_.length===0,onClick:y,children:(0,Q.jsx)(i,{icon:pe,className:`size-3.5`,"aria-hidden":!0})})]}),v?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-2 text-[11px] text-destructive`,children:`Need an IP or host — 203.0.113.4, ::1, localhost, or ci.example.com.`}):e.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-2 text-[11px] text-muted-foreground`,children:`Any client. Add an IP or host.`}):(0,Q.jsx)(`div`,{role:`list`,"aria-label":`Allow list`,children:e.map(t=>{let n=Le(t);return(0,Q.jsxs)(`div`,{role:`listitem`,"data-slot":`access-allow-entry`,"data-kind":n??`invalid`,className:o(A,`last:border-b-0`),children:[(0,Q.jsx)(`span`,{className:o(B,`w-[5.5rem] shrink-0 px-1.5`,n==null&&`text-destructive`),children:n===`ip`?`IP`:n===`host`?`Host`:`Bad`}),(0,Q.jsx)(`span`,{className:o(`min-w-0 truncate font-mono text-[11px]`,n==null&&`text-destructive`),children:t}),(0,Q.jsx)(`button`,{type:`button`,className:o(I,`ml-auto opacity-0 group-hover:opacity-100 focus-visible:opacity-100`),"aria-label":`Remove ${t}`,onClick:()=>c(e.filter(e=>e!==t)),children:(0,Q.jsx)(i,{icon:D,className:`size-3`,"aria-hidden":!0})})]},t)})})]}),(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-rate-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Rate`}),(0,Q.jsxs)(`label`,{className:`flex min-w-0 flex-1 items-stretch border-r border-border/50`,children:[(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pl-1 text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:`Max`}),(0,Q.jsx)(a,{value:t,onChange:e=>u(e.target.value),placeholder:`none`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate max`,flat:!0,className:o(q,`min-w-0 flex-1 px-2 font-mono tabular-nums`)})]}),(0,Q.jsxs)(`label`,{className:`flex min-w-0 flex-1 items-stretch`,children:[(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pl-2 text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:`Every`}),(0,Q.jsx)(a,{value:n,onChange:e=>d(e.target.value),placeholder:`1`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate window`,flat:!0,className:o(q,`min-w-0 flex-1 px-2 font-mono tabular-nums`)})]}),(0,Q.jsx)(`div`,{className:`flex h-8 shrink-0 items-stretch`,role:`group`,"aria-label":`Rate unit`,children:Ne.map(e=>(0,Q.jsxs)(s,{children:[(0,Q.jsx)(m,{render:t=>(0,Q.jsx)(`button`,{...t,type:`button`,"aria-pressed":r===e,"aria-label":Pe[e],onClick:n=>{t.onClick?.(n),f(e)},className:o(S,`h-8 px-1.5 font-mono font-semibold`,r===e?g:H),children:e})}),(0,Q.jsx)(l,{side:`bottom`,className:`text-[11px]`,children:Pe[e]})]},e))})]})]})}function it({expires:e,customExpires:t,onExpires:n,onCustomExpires:r}){return(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(ve,{label:`Expires`,children:[(0,Q.jsx)(G,{active:e===`never`,onClick:()=>n(`never`),children:`never`}),(0,Q.jsx)(G,{active:e===`30d`,onClick:()=>n(`30d`),children:`30d`}),(0,Q.jsx)(G,{active:e===`90d`,onClick:()=>n(`90d`),children:`90d`}),(0,Q.jsx)(G,{active:e===`custom`,onClick:()=>n(`custom`),children:`custom`})]}),e===`custom`?(0,Q.jsx)(_e,{label:`Duration`,hint:`7d · 12h · 30m`,children:(0,Q.jsx)(a,{value:t,onChange:e=>r(e.target.value),placeholder:`7d`,autoComplete:`off`,"aria-label":`Custom expiry duration`,flat:!0,className:o(q,`font-mono`)})}):null]})}function at({value:e,placeholder:t,label:n,onChange:r}){return(0,Q.jsxs)(`label`,{className:`relative min-w-0 flex-1`,children:[(0,Q.jsx)(i,{icon:he,className:`pointer-events-none absolute top-1/2 left-3 size-3.5 -translate-y-1/2 text-muted-foreground`,"aria-hidden":!0}),(0,Q.jsx)(a,{value:e,onChange:e=>r(e.target.value),placeholder:t,"aria-label":n,autoComplete:`off`,flat:!0,className:o(ye)})]})}function ot({scopes:e,selected:t,empty:n,onChange:r}){let[i,a]=(0,Y.useState)(``),s=new Set(t),c=(0,Y.useMemo)(()=>st(e,i),[e,i]),l=(0,Y.useMemo)(()=>He(c),[c]),u=e.join(`\0`);return(0,Y.useEffect)(()=>{a(``)},[u]),(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-scope-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Scopes`}),e.length>0?(0,Q.jsx)(at,{value:i,placeholder:`Search scopes`,label:`Search scopes`,onChange:a}):null]}),e.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:n}):c.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No scopes match.`}):(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto`,role:`group`,"aria-label":`Scopes`,children:l.map(e=>(0,Q.jsxs)(`div`,{className:`flex items-stretch border-t border-border/50`,role:`group`,"aria-label":e.group,children:[(0,Q.jsx)(`p`,{className:`flex w-[5.5rem] shrink-0 items-center px-4 font-mono text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:e.group}),(0,Q.jsx)(`div`,{className:`flex min-w-0 flex-1 flex-wrap items-stretch`,children:e.items.map(e=>{let n=s.has(e.scope);return(0,Q.jsx)(`button`,{type:`button`,"aria-pressed":n,"aria-label":e.scope,"data-slot":`access-scope-chip`,title:e.scope,onClick:()=>{r(n?t.filter(t=>t!==e.scope):[...t,e.scope])},className:o(`inline-flex h-8 min-w-0 items-center px-2 font-mono text-[10px] font-semibold outline-none select-none`,`focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-inset`,n?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`),children:e.action},e.scope)})})]},e.group))})]})}function st(e,t){let n=t.trim().toLowerCase();return n.length===0?e:e.filter(e=>e.toLowerCase().includes(n))}function ct({open:e,users:t,userScopes:n,operatorScopes:r,pending:i,error:s,onOpenChange:c,onSubmit:l}){let[u,d]=(0,Y.useState)(`user`),[f,p]=(0,Y.useState)(``),[m,h]=(0,Y.useState)(``),[g,_]=(0,Y.useState)([]),[v,y]=(0,Y.useState)(`never`),[b,x]=(0,Y.useState)(``),[S,w]=(0,Y.useState)([]),[T,ee]=(0,Y.useState)(``),[D,k]=(0,Y.useState)(``),[j,P]=(0,Y.useState)(`m`),[I,L]=(0,Y.useState)(``),R=t.find(e=>e.id===m)??null,z=(0,Y.useMemo)(()=>{let e=I.trim().toLowerCase(),n=e.length===0?t:t.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.email.toLowerCase().includes(e));return R&&!n.some(e=>e.id===R.id)?[R,...n]:n},[t,I,R]),B=(0,Y.useMemo)(()=>u===`operator`?r:R==null?[]:R.scopes.length>0?R.scopes:n,[u,n,r,R]);(0,Y.useEffect)(()=>{e||(d(`user`),p(``),h(``),_([]),y(`never`),x(``),w([]),ee(``),k(``),P(`m`),L(``))},[e]),(0,Y.useEffect)(()=>{_(e=>{let t=e.filter(e=>B.includes(e));if(t.length>0)return t;let n=B[0];return n?[n]:[]})},[B]);let V=De(b),H=Fe(T,D,j),ne=f.trim().length>0&&g.length>0&&!i&&H!==void 0&&(u===`operator`||m.length>0)&&(v!==`custom`||V>0);return(0,Q.jsx)(E,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:c,children:(0,Q.jsxs)(M,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-create-sheet`,children:[(0,Q.jsxs)(O,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(te,{className:`text-sm`,children:`Create API key`}),(0,Q.jsx)(F,{className:`text-[11px]`,children:u===`user`?`User-plane keys require an issuer. The key cannot exceed that user's grants.`:`Operator keys inherit the actor ceiling. Absence is impossible — only grantable scopes appear.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-name-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Name`}),(0,Q.jsx)(a,{value:f,onChange:e=>p(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:o(q,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(ve,{label:`Plane`,children:[(0,Q.jsx)(G,{active:u===`user`,icon:fe,onClick:()=>{d(`user`)},children:`user`}),(0,Q.jsx)(G,{active:u===`operator`,icon:Ce,onClick:()=>{d(`operator`),h(``),L(``)},children:`operator`})]}),(0,Q.jsx)(it,{expires:v,customExpires:b,onExpires:y,onCustomExpires:x}),(0,Q.jsx)(rt,{entries:S,rateMax:T,rateCount:D,rateUnit:j,onEntries:w,onRateMax:ee,onRateCount:k,onRateUnit:P}),u===`user`?(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-issuer-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Issuer`}),t.length>0?(0,Q.jsx)(at,{value:I,placeholder:`Search users`,label:`Search users`,onChange:L}):null]}),t.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No users on this plane.`}):z.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No users match.`}):(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto`,children:z.map(e=>{let t=e.id===m;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-creator-user`,"aria-pressed":t,className:o(A,t&&`bg-muted/70 text-foreground`),onClick:()=>h(e.id),children:[(0,Q.jsx)(`span`,{className:o(N,t&&`bg-sky-500`)}),(0,Q.jsx)(`span`,{className:`min-w-0 truncate`,children:e.name}),(0,Q.jsx)(`span`,{className:`ml-auto shrink-0 font-mono text-[10px] text-muted-foreground`,children:e.id})]},e.id)})})]}):null,(0,Q.jsx)(ot,{scopes:B,selected:g,empty:u===`user`&&m.length===0?`Select an issuer to see grantable scopes.`:u===`user`?`This user has no grants.`:`No grantable scopes on this plane.`,onChange:_}),s?(0,Q.jsx)(be,{slot:`access-create-error`,children:s}):null]}),(0,Q.jsxs)(C,{children:[(0,Q.jsx)(K,{split:!0,onClick:()=>c(!1),disabled:i,children:`Cancel`}),(0,Q.jsx)(K,{variant:`default`,disabled:!ne,"data-slot":`access-create-submit`,onClick:()=>{if(H===void 0)return;let e=Oe(v,Date.now(),b);l({plane:u,name:f.trim(),scopes:g,...u===`user`?{creatorUserId:m}:{},...e==null?{}:{expiresAt:e},...H==null?{}:{rateLimit:H},...S.length>0?{ipAllowlist:S}:{}})},children:i?`Creating…`:`Create`})]})]})})}function lt({open:e,keyRow:t,users:n,operators:r,operatorScopes:s,pending:c,error:l,now:u=Date.now(),onOpenChange:d,onSubmit:f}){let p=ke(t.expiresAt,u),[m,h]=(0,Y.useState)(t.name),[g,_]=(0,Y.useState)(t.scopes),[v,y]=(0,Y.useState)(p.choice),[b,x]=(0,Y.useState)(p.custom),[S,w]=(0,Y.useState)(t.ipAllowlist),T=Ie(t.rateLimit?.per??``),[ee,D]=(0,Y.useState)(t.rateLimit?String(t.rateLimit.max):``),[k,A]=(0,Y.useState)(T.count),[j,N]=(0,Y.useState)(T.unit);(0,Y.useEffect)(()=>{if(!e)return;let n=ke(t.expiresAt,u);h(t.name),_(t.scopes),y(n.choice),x(n.custom),w(t.ipAllowlist);let r=Ie(t.rateLimit?.per??``);D(t.rateLimit?String(t.rateLimit.max):``),A(r.count),N(r.unit)},[e,t.id]);let P=t.creatorId??``,I=(0,Y.useMemo)(()=>P.length===0?null:n.find(e=>e.id===P)??r.find(e=>e.id===P)??null,[P,r,n]),L=I?I.name.trim()||I.email.trim()||I.id:P||`Unknown`,R=(0,Y.useMemo)(()=>{let e=t.creatorScopes??[];return e.length>0?e:t.plane===`operator`?I&&I.scopes.length>0?I.scopes:s:I?.scopes??[]},[I,t.creatorScopes,t.plane,s]),z=Array.from(new Set([...R,...t.scopes])),B=De(b),V=Fe(ee,k,j),H=m.trim().length>0&&g.length>0&&!c&&V!==void 0&&(v!==`custom`||B>0);return(0,Q.jsx)(E,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:d,children:(0,Q.jsxs)(M,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-edit-sheet`,children:[(0,Q.jsxs)(O,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(te,{className:`text-sm`,children:`Edit key`}),(0,Q.jsx)(F,{className:`text-[11px]`,children:`Change name, expiry, allow, rate, and scopes. Issuer stays frozen.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-name-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Name`}),(0,Q.jsx)(a,{value:m,onChange:e=>h(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:o(q,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-issuer-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Issuer`}),(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center gap-1.5 px-4 text-[12px] text-muted-foreground`,"aria-label":`Issuer ${L}`,children:[(0,Q.jsx)(i,{icon:t.plane===`operator`?Ce:fe,className:`size-3 shrink-0`,"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:`min-w-0 truncate text-foreground`,children:L})]}),P.length>0&&P!==L?(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pr-4 font-mono text-[10px] text-muted-foreground`,children:P}):null]}),(0,Q.jsx)(it,{expires:v,customExpires:b,onExpires:y,onCustomExpires:x}),(0,Q.jsx)(rt,{entries:S,rateMax:ee,rateCount:k,rateUnit:j,onEntries:w,onRateMax:D,onRateCount:A,onRateUnit:N}),(0,Q.jsx)(ot,{scopes:z,selected:g,empty:t.plane===`user`&&I==null?`Issuer is gone. Only scopes already on this key remain.`:t.plane===`user`?`This issuer has no grants.`:`No grantable scopes on this plane.`,onChange:_}),l?(0,Q.jsx)(be,{slot:`access-edit-error`,children:l}):null]}),(0,Q.jsxs)(C,{children:[(0,Q.jsx)(K,{split:!0,onClick:()=>d(!1),disabled:c,children:`Cancel`}),(0,Q.jsx)(K,{variant:`default`,disabled:!H,"data-slot":`access-edit-submit`,onClick:()=>{V!==void 0&&f({name:m.trim(),scopes:g,expiresAt:Oe(v,Date.now(),b),rateLimit:V,ipAllowlist:S})},children:c?`Saving…`:`Save`})]})]})})}function ut({open:e,keyRow:t,pending:n,error:r,now:i=Date.now(),onOpenChange:a,onSubmit:o}){let s=je(t.expiresAt,i),[c,l]=(0,Y.useState)(s.choice),[u,d]=(0,Y.useState)(s.custom);(0,Y.useEffect)(()=>{if(!e)return;let n=je(t.expiresAt,i);l(n.choice),d(n.custom)},[e,t.id]);let f=De(u);return(0,Q.jsx)(E,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:a,children:(0,Q.jsxs)(M,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-refresh-sheet`,children:[(0,Q.jsxs)(O,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(te,{className:`text-sm`,children:`Refresh expiry`}),(0,Q.jsx)(F,{className:`text-[11px]`,children:`Reset the deadline from now. The secret does not change.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsx)(it,{expires:c,customExpires:u,onExpires:l,onCustomExpires:d}),r?(0,Q.jsx)(be,{slot:`access-refresh-error`,children:r}):null]}),(0,Q.jsxs)(C,{children:[(0,Q.jsx)(K,{split:!0,onClick:()=>a(!1),disabled:n,children:`Cancel`}),(0,Q.jsx)(K,{variant:`default`,disabled:!(!n&&(c!==`custom`||f>0)),"data-slot":`access-refresh-submit`,onClick:()=>{o(Oe(c,Date.now(),u))},children:n?`Refreshing…`:`Refresh`})]})]})})}function dt({secret:e,title:t=`New API key`,onDismiss:n}){let[r,i]=(0,Y.useState)(!1);return(0,Q.jsx)(E,{open:e!==null,onOpenChange:e=>{e||n()},children:(0,Q.jsxs)(M,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-secret-sheet`,children:[(0,Q.jsxs)(O,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(te,{className:`text-sm`,children:t}),(0,Q.jsx)(F,{className:`text-[11px]`,children:`Shown once. Store it offline. This Console process does not keep it for you.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsx)(`p`,{className:`border-b border-border/50 px-4 py-3 font-mono text-[11px] break-all`,children:e}),(0,Q.jsx)(`p`,{className:`px-4 py-3 text-[11px] text-muted-foreground`,role:`status`,children:`Copying puts the secret on the clipboard. Anyone with this device can read it until you clear it.`})]}),(0,Q.jsxs)(C,{children:[(0,Q.jsx)(K,{split:!0,disabled:!e,onClick:()=>{e&&(navigator.clipboard?.writeText(e),i(!0))},children:r?`Copied`:`Copy (clipboard warning)`}),(0,Q.jsx)(K,{variant:`destructive`,onClick:()=>{i(!1),n()},children:`I have stored this key`})]})]})})}function ft(){let e=et(),t=ee(),{query:n,selectedKey:a,action:o,setQuery:s,setSelectedKey:c,setAction:l}=me(),m=ce(),[g]=(0,Y.useState)(()=>Date.now()),[_,x]=(0,Y.useState)(null),[S,C]=(0,Y.useState)(`New API key`),E=(0,Y.useMemo)(()=>{let t=e.data?.userPlane.keys??[],n=e.data?.operatorPlane.keys??[];return[...t,...n]},[e.data]),D=(0,Y.useMemo)(()=>{let e=n.trim().toLowerCase();return e?E.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.scopes.some(t=>t.toLowerCase().includes(e))):E},[E,n]),O=D.find(e=>e.id===a)??E.find(e=>e.id===a),k=y({queryKey:[`console.access.blast`,O?.id],queryFn:async()=>{if(!O)throw Error(`no key`);let e=await p(O.id);if(e.error||!e.data)throw Error(e.error?.message??`blast failed`);return e.data},enabled:O!==void 0});(0,Y.useEffect)(()=>{let e=e=>{if(e.key!==`/`||e.metaKey||e.ctrlKey||e.altKey)return;let t=e.target;if(t instanceof HTMLElement){let e=t.tagName;if(e===`INPUT`||e===`TEXTAREA`||t.isContentEditable)return}e.preventDefault(),document.querySelector(`[data-slot='access-search-input']`)?.focus()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let A=()=>{t.invalidateQueries({queryKey:$e})},j=W({mutationFn:f,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?v(e.error):`create failed`);C(`New API key`),x(e.data.secret),c(e.data.key.id),l(null),A()}}),M=W({mutationFn:e=>d({keyId:O.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?v(e.error):`rotate failed`);C(`Rotated API key`),x(e.data.secret),l(null),A()}}),N=W({mutationFn:e=>u({keyId:O.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?v(e.error):`revoke failed`);l(null),A()}}),P=W({mutationFn:h,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?v(e.error):`update failed`);l(null),A()}}),F=W({mutationFn:e=>h({keyId:O.id,expiresAt:e}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?v(e.error):`refresh failed`);l(null),A()}}),I=(0,Q.jsx)(le,{open:m.open,onToggle:m.toggle,noun:`keys`,controlsId:`access-list`,dataSlot:`access-list-toggle`});return(0,Q.jsxs)(`div`,{className:T,"data-slot":`access-page`,children:[(0,Q.jsxs)(ie,{orientation:`horizontal`,className:`min-h-0 flex-1`,children:[(0,Q.jsx)(se,{panelRef:m.panelRef,collapsible:!0,collapsedSize:0,defaultSize:b.start.defaultSize,minSize:b.start.minSize,onResize:m.onResize,className:`min-h-0 overflow-hidden`,children:(0,Q.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col overflow-hidden`,children:[(0,Q.jsxs)(`div`,{className:r,children:[(0,Q.jsx)(ae,{"data-slot":`access-search-input`,placeholder:`Search keys`,"aria-label":`Search keys`,value:n,onChange:e=>s(e.target.value)}),(0,Q.jsx)(U,{label:`Create key`,className:`flex self-stretch`,children:(0,Q.jsx)(`button`,{type:`button`,className:w,"data-slot":`access-create`,"aria-label":`Create key`,onClick:()=>l(`create`),children:(0,Q.jsx)(i,{icon:pe,className:`size-3.5`})})})]}),(0,Q.jsx)(tt,{rows:D,selectedKey:a,query:n,loading:e.isLoading,onSelect:c})]})}),m.open?(0,Q.jsx)(ue,{withHandle:!0}):null,(0,Q.jsx)(se,{defaultSize:b.end.defaultSize,minSize:b.end.minSize,className:`min-h-0 overflow-hidden`,children:O?(0,Q.jsx)(Ge,{keyRow:O,blast:k.data,now:g,leading:I,onEdit:()=>l(`edit`),onRefresh:()=>l(`refresh`),onRevoke:()=>l(`revoke`),onRotate:()=>l(`rotate`),rotatePending:M.isPending}):(0,Q.jsx)(ge,{icon:de.gate.icon,iconClassName:`border-border/70`,title:e.isLoading?`Loading keys…`:E.length>0?`Select a key`:`No keys yet`,leading:I,description:e.isLoading?`Reading planes and grantable scopes.`:E.length>0&&n.trim().length>0?(0,Q.jsxs)(Q.Fragment,{children:[`Nothing matches `,(0,Q.jsx)(`span`,{className:`font-mono`,children:n}),`.`,` `,(0,Q.jsx)(`button`,{type:`button`,className:`underline underline-offset-2 hover:text-foreground`,onClick:()=>s(``),children:`Clear search`})]}):`Create one to mint a secret once.`})})]}),(0,Q.jsx)(ct,{open:o===`create`,users:e.data?.userPlane.users??[],userScopes:e.data?.userPlane.grantableScopes??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:j.isPending,error:j.error instanceof Error?j.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>j.mutate(e)}),O?(0,Q.jsx)(lt,{open:o===`edit`,keyRow:O,users:e.data?.userPlane.users??[],operators:e.data?.operatorPlane.operators??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:P.isPending,error:P.error instanceof Error?P.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>P.mutate({keyId:O.id,...e})}):null,O?(0,Q.jsx)(ut,{open:o===`refresh`,keyRow:O,pending:F.isPending,error:F.error instanceof Error?F.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>F.mutate(e)}):null,O?(0,Q.jsx)(Se,{open:o===`revoke`,onOpenChange:e=>{e||l(null)},phrase:`REVOKE`,title:`Revoke ${O.name}`,description:`Irreversible. Residual sessions may continue for the access TTL.`,pending:N.isPending,error:N.error instanceof Error?N.error.message:null,confirmLabel:`Revoke key`,slot:`access-revoke-sheet`,onConfirm:e=>N.mutate({confirmation:`REVOKE`,reason:e.reason})}):null,O?(0,Q.jsx)(Se,{open:o===`rotate`,onOpenChange:e=>{e||l(null)},phrase:`ROTATE`,title:`Rotate ${O.name}`,description:`Mints a new secret once. The current secret stops after residual TTL.`,pending:M.isPending,error:M.error instanceof Error?M.error.message:null,confirmLabel:`Rotate key`,confirmVariant:`ghost`,slot:`access-rotate-sheet`,onConfirm:e=>M.mutate({confirmation:`ROTATE`,reason:e.reason})}):null,(0,Q.jsx)(dt,{secret:_,title:S,onDismiss:()=>x(null)})]})}export{ft as AccessPage};
@@ -1 +1 @@
1
- import{A as e}from"./xyflow-yApv7D4e.js";import{Er as t}from"./shortcut-keys-3ILd8oGn.js";import{X as n,c as r,l as i}from"./index-CBAP48v5.js";var a=e();function o({open:e,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=i()??!1;return(0,a.jsx)(r.div,{...u,"aria-hidden":!e,inert:!e||void 0,initial:!1,animate:d?{opacity:+!!e}:{opacity:+!!e,clipPath:e?`inset(0 0 0% 0)`:`inset(0 0 100% 0)`,y:e?0:-4},transition:l??{duration:d?0:e?.22:.14,ease:n},className:t(`overflow-hidden`,s),style:{...c,height:e?o:0,pointerEvents:e?void 0:`none`,transformOrigin:`top`}})}export{o as t};
1
+ import{A as e}from"./xyflow-yApv7D4e.js";import{Er as t}from"./shortcut-keys-3ILd8oGn.js";import{X as n,c as r,l as i}from"./index-B0GkEkAT.js";var a=e();function o({open:e,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=i()??!1;return(0,a.jsx)(r.div,{...u,"aria-hidden":!e,inert:!e||void 0,initial:!1,animate:d?{opacity:+!!e}:{opacity:+!!e,clipPath:e?`inset(0 0 0% 0)`:`inset(0 0 100% 0)`,y:e?0:-4},transition:l??{duration:d?0:e?.22:.14,ease:n},className:t(`overflow-hidden`,s),style:{...c,height:e?o:0,pointerEvents:e?void 0:`none`,transformOrigin:`top`}})}export{o as t};