okengine 0.23.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/package.json +1 -1
  2. package/site/content/docs/ai/mcp.mdx +1 -1
  3. package/site/content/docs/elements/channel/index.mdx +4 -4
  4. package/site/content/docs/elements/channel/receipts.mdx +7 -5
  5. package/site/content/docs/elements/channel/sms.mdx +2 -1
  6. package/site/content/docs/elements/flow/index.mdx +22 -18
  7. package/site/content/docs/elements/signal/index.mdx +15 -14
  8. package/site/content/docs/plugins/otp.mdx +3 -3
  9. package/site/content/docs/reference/fx.mdx +12 -4
  10. package/src/auth/tenants.ts +17 -0
  11. package/src/cli/doctor-diff.test.ts +29 -1
  12. package/src/cli/doctor-diff.ts +35 -0
  13. package/src/cli/manifest-pr-diff.ts +16 -0
  14. package/src/compiler/effects-fetch.test.ts +77 -0
  15. package/src/compiler/effects-infer.ts +87 -16
  16. package/src/compiler/effects-join.test.ts +48 -0
  17. package/src/compiler/extract.test.ts +28 -0
  18. package/src/compiler/extract.ts +11 -1
  19. package/src/compiler/fx-index.ts +58 -4
  20. package/src/console/index.ts +1 -1
  21. package/src/console/ui-next/dist/assets/{access-page-DDKkhT9v.js → access-page-C_N4qvai.js} +1 -1
  22. package/src/console/ui-next/dist/assets/{agent-disclosure-62Xts8Xi.js → agent-disclosure-Cq1TxniW.js} +1 -1
  23. package/src/console/ui-next/dist/assets/{cache-glyph-DyeoKHKA.js → cache-glyph-BaI4uxZz.js} +1 -1
  24. package/src/console/ui-next/dist/assets/{call-pii-button-DvfpLXsU.js → call-pii-button-DNyK7IIY.js} +1 -1
  25. package/src/console/ui-next/dist/assets/{collapsible-BY03SeCg.js → collapsible-CRhBJMZR.js} +1 -1
  26. package/src/console/ui-next/dist/assets/{decisions-page-CgkKlA56.js → decisions-page-D9twj8Xy.js} +1 -1
  27. package/src/console/ui-next/dist/assets/{duration-tone-Y6HLjyJ5.js → duration-tone-CsHWlVPU.js} +1 -1
  28. package/src/console/ui-next/dist/assets/{flows-page-DLPfNy-_.js → flows-page-ZaTJAhR9.js} +1 -1
  29. package/src/console/ui-next/dist/assets/{highlighted-json-D7nNzpgB.js → highlighted-json-DQ-EMAfz.js} +1 -1
  30. package/src/console/ui-next/dist/assets/{http-method-DdL19zzh.js → http-method-Cn_Fl79s.js} +1 -1
  31. package/src/console/ui-next/dist/assets/{index-C0lc_s9d.js → index-CmRFeWdT.js} +3 -3
  32. package/src/console/ui-next/dist/assets/{observability-page-CRlcyLCC.js → observability-page-Cn6R_dXz.js} +1 -1
  33. package/src/console/ui-next/dist/assets/{replica-lag-Bb3FWUu9.js → replica-lag-Bx6EZhSe.js} +1 -1
  34. package/src/console/ui-next/dist/assets/{request-meta-DYBMWhX8.js → request-meta-DmdiUrfh.js} +1 -1
  35. package/src/console/ui-next/dist/assets/{store-page-CcE-SXC8.js → store-page-8P6vGXnn.js} +1 -1
  36. package/src/console/ui-next/dist/assets/{trace-detail-sheet-CKMSEQ4s.js → trace-detail-sheet-BfGvpFSv.js} +1 -1
  37. package/src/console/ui-next/dist/assets/{tree-expand-toggle-QOfk0M0H.js → tree-expand-toggle-CWdUHbc0.js} +1 -1
  38. package/src/console/ui-next/dist/assets/{units-page-DKC1CDDd.js → units-page-CZOFZzay.js} +1 -1
  39. package/src/console/ui-next/dist/assets/{vault-page-B54v4Yit.js → vault-page-ki_ishmX.js} +1 -1
  40. package/src/console/ui-next/dist/index.html +1 -1
  41. package/src/drivers/journal-postgres.test.ts +22 -1
  42. package/src/drivers/journal-postgres.ts +223 -12
  43. package/src/drivers/signal-compete.test.ts +234 -0
  44. package/src/drivers/signal-postgres.ts +39 -19
  45. package/src/drivers/signal-redis.ts +167 -11
  46. package/src/drivers/signal-types.ts +12 -0
  47. package/src/elements/ai/mcp-protocol.ts +5 -6
  48. package/src/elements/ai/runtime.ts +20 -19
  49. package/src/elements/channel/runtime.ts +11 -0
  50. package/src/elements/channel/sql-ledger.ts +251 -0
  51. package/src/elements/signal/runtime.ts +9 -0
  52. package/src/elements/signal.test.ts +1 -0
  53. package/src/elements/store/cache.test.ts +120 -15
  54. package/src/elements/store/cache.ts +200 -39
  55. package/src/elements/store/prepare-row.test.ts +6 -2
  56. package/src/elements/store/sql-session.test.ts +2 -0
  57. package/src/elements/store/sql-session.ts +95 -9
  58. package/src/elements/store.ts +1 -0
  59. package/src/kernel/app.ts +32 -16
  60. package/src/kernel/auto-cache.test.ts +228 -0
  61. package/src/kernel/boot-bind/channel.ts +63 -2
  62. package/src/kernel/boot-bind/honor-config.test.ts +40 -10
  63. package/src/kernel/boot-bind/signal.ts +61 -25
  64. package/src/kernel/boot.ts +16 -2
  65. package/src/kernel/errors-compiler.ts +11 -0
  66. package/src/kernel/errors-text.ts +16 -0
  67. package/src/kernel/errors.ts +8 -0
  68. package/src/kernel/flow.ts +33 -3
  69. package/src/kernel/fx-call-types.test.ts +32 -0
  70. package/src/kernel/fx-decide.ts +11 -7
  71. package/src/kernel/fx-sql-handle.ts +46 -7
  72. package/src/kernel/fx.test.ts +1 -3
  73. package/src/kernel/fx.ts +27 -16
  74. package/src/kernel/idempotency.test.ts +2 -1
  75. package/src/kernel/journal.test.ts +114 -0
  76. package/src/kernel/journal.ts +369 -53
  77. package/src/kernel/pipeline-tenant.ts +5 -1
  78. package/src/kernel/pipeline.ts +5 -0
  79. package/src/kernel/signal-tx.ts +28 -0
  80. package/src/kernel/store-transaction.test.ts +95 -0
  81. package/src/mcp/docs-server.ts +19 -2
  82. package/src/mcp/mcp.test.ts +83 -0
  83. package/src/mcp/protocol.ts +8 -2
  84. package/src/mcp/server.ts +23 -2
  85. package/src/mcp/versions.ts +60 -0
  86. package/src/plugins/index.ts +1 -0
  87. package/src/plugins/mena.ts +56 -0
@@ -110,6 +110,11 @@ export interface InferEffectsOptions {
110
110
  * never pass `fx` into another function.
111
111
  */
112
112
  readonly resolveCallee?: (callee: AstNode, file: string) => FxCalleeResolution;
113
+ /**
114
+ * Flow name of an imported handle in `file`. Absent when inference does
115
+ * not follow imports (unit tests).
116
+ */
117
+ readonly resolveFlowRef?: (localName: string, file: string) => string | undefined;
113
118
  }
114
119
 
115
120
  /** Result of effect inference for one flow body. */
@@ -129,6 +134,11 @@ export interface InferredEffects {
129
134
  readonly nondeterministic: boolean;
130
135
  /** True when the body references `fx.auth.userId`. */
131
136
  readonly readsUserId: boolean;
137
+ /**
138
+ * `fx.fetch` / `fx.send` calls that are not inside an `fx.step` callback.
139
+ * A `durable: true` flow must not call these directly.
140
+ */
141
+ readonly bareIrreversible: readonly ("fetch" | "send")[];
132
142
  }
133
143
 
134
144
  const READ_METHODS = new Set([
@@ -145,6 +155,10 @@ const READ_METHODS = new Set([
145
155
  "page",
146
156
  "search",
147
157
  "raw",
158
+ "innerJoin",
159
+ "leftJoin",
160
+ "rightJoin",
161
+ "fullJoin",
148
162
  ]);
149
163
 
150
164
  const WRITE_METHODS = new Set([
@@ -175,6 +189,10 @@ const TABLE_ARG_METHODS = new Set([
175
189
  "count",
176
190
  "page",
177
191
  "search",
192
+ "innerJoin",
193
+ "leftJoin",
194
+ "rightJoin",
195
+ "fullJoin",
178
196
  ]);
179
197
 
180
198
  /**
@@ -204,7 +222,8 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
204
222
  name: "liveQuery" | "applySearchEmbedCdc";
205
223
  call: CallExpression;
206
224
  }> = [];
207
- const chains = collectFxChains(options, opaque, userIdRoots, intrinsics);
225
+ const bareIrreversible = new Set<"fetch" | "send">();
226
+ const chains = collectFxChains(options, opaque, userIdRoots, intrinsics, bareIrreversible);
208
227
 
209
228
  for (const { call, chain } of chains) {
210
229
  if (chain.rootMethod === "raw") {
@@ -313,7 +332,13 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
313
332
  }
314
333
 
315
334
  if (chain.rootMethod === "call" && call === chain.rootCall) {
316
- const ref = resolveCallTarget(call.arguments[0], options.bindings);
335
+ const arg = unwrapValueNode(call.arguments[0]);
336
+ const local = identifierName(arg);
337
+ const imported =
338
+ local && options.file && options.resolveFlowRef
339
+ ? options.resolveFlowRef(local, options.file)
340
+ : undefined;
341
+ const ref = imported ?? resolveCallTarget(call.arguments[0], options.bindings);
317
342
  if (ref) calls.add(ref);
318
343
  continue;
319
344
  }
@@ -359,18 +384,28 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
359
384
  // Skip incomplete chains (`select` before `.from`, `insert` before table) —
360
385
  // the sibling call that carries the table arg records the real resource.
361
386
  const leaf = resolved.methods[resolved.methods.length - 1]!;
362
- const hasTable = tableFromStoreChain(call, options.bindings, chain) !== undefined;
387
+ const tables = tablesFromStoreChain(call, options.bindings, chain);
388
+ const hasTable = tables.length > 0;
363
389
  if (!hasTable && (leaf === "select" || leaf === "insert" || leaf === "update")) {
364
390
  continue;
365
391
  }
366
392
  // Intermediate chain links that only forward (where/values/returning)
367
393
  // still carry read/write from earlier methods — record once we have a table
368
394
  // or a terminal key-based op.
369
- if (!hasTable && (leaf === "where" || leaf === "values" || leaf === "returning")) {
395
+ if (
396
+ !hasTable &&
397
+ (leaf === "where" || leaf === "values" || leaf === "returning" || leaf === "groupBy")
398
+ ) {
370
399
  continue;
371
400
  }
372
- if (op === "read" || op === "both") reads.add(resolved.resource);
373
- if (op === "write" || op === "both") writes.add(resolved.resource);
401
+ const resources =
402
+ tables.length > 0
403
+ ? tables.map((table) => sqlTableRef(table) as ResourceRef)
404
+ : [resolved.resource];
405
+ for (const resource of resources) {
406
+ if (op === "read" || op === "both") reads.add(resource);
407
+ if (op === "write" || op === "both") writes.add(resource);
408
+ }
374
409
  }
375
410
  }
376
411
 
@@ -407,6 +442,7 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
407
442
  cacheIneligible: usesRaw && !options.hasExplicitEffects,
408
443
  nondeterministic: asks.size > 0 || embeds.size > 0 || decides.size > 0,
409
444
  readsUserId: userIdRoots.some((node) => containsAuthUserId(node)),
445
+ bareIrreversible: [...bareIrreversible],
410
446
  };
411
447
  }
412
448
 
@@ -610,21 +646,37 @@ function tableFromStoreChain(
610
646
  bindings: ReadonlyMap<string, InferBinding>,
611
647
  chain?: FxChain,
612
648
  ): string | undefined {
649
+ return tablesFromStoreChain(call, bindings, chain)[0];
650
+ }
651
+
652
+ /**
653
+ * Every table argument on a store chain, including joins.
654
+ *
655
+ * @param call - Any call in the chain
656
+ * @param bindings - Scope bindings
657
+ * @param chain - Precomputed chain when already known
658
+ */
659
+ function tablesFromStoreChain(
660
+ call: CallExpression,
661
+ bindings: ReadonlyMap<string, InferBinding>,
662
+ chain?: FxChain,
663
+ ): string[] {
613
664
  const found = tableFromStoreWalk(call, bindings, chain);
614
- if (found) return found;
665
+ if (found.length > 0) return found;
615
666
  if (chain?.aliasLeaf && chain.aliasLeaf !== call) {
616
667
  return tableFromStoreWalk(chain.aliasLeaf, bindings);
617
668
  }
618
- return undefined;
669
+ return [];
619
670
  }
620
671
 
621
672
  function tableFromStoreWalk(
622
673
  call: CallExpression,
623
674
  bindings: ReadonlyMap<string, InferBinding>,
624
675
  chain?: FxChain,
625
- ): string | undefined {
676
+ ): string[] {
626
677
  let current: AstNode | undefined = call;
627
678
  const seen = new Set<AstNode>();
679
+ const names: string[] = [];
628
680
  while (current && current.type === "CallExpression" && !seen.has(current)) {
629
681
  seen.add(current);
630
682
  const c = current as CallExpression;
@@ -644,7 +696,8 @@ function tableFromStoreWalk(
644
696
  const id = identifierName(c.arguments[0]);
645
697
  if (id) {
646
698
  const binding = bindings.get(id);
647
- return binding?.kind === "table" ? binding.ref : id;
699
+ const tableName = binding?.kind === "table" ? binding.ref : id;
700
+ if (!names.includes(tableName)) names.push(tableName);
648
701
  }
649
702
  }
650
703
  }
@@ -658,7 +711,7 @@ function tableFromStoreWalk(
658
711
  }
659
712
  break;
660
713
  }
661
- return undefined;
714
+ return names;
662
715
  }
663
716
 
664
717
  function methodNameOfCall(call: CallExpression): string | undefined {
@@ -908,7 +961,9 @@ function collectFxChains(
908
961
  opaque: string[],
909
962
  userIdRoots: AstNode[],
910
963
  intrinsics: Array<{ name: "liveQuery" | "applySearchEmbedCdc"; call: CallExpression }>,
964
+ bareIrreversible: Set<"fetch" | "send">,
911
965
  ): ChainHit[] {
966
+ const stepBodies = new Set<AstNode>();
912
967
  const hits: ChainHit[] = [];
913
968
  const seen = new Set<AstNode>();
914
969
 
@@ -918,7 +973,7 @@ function collectFxChains(
918
973
  aliases: ReadonlyMap<string, ChainAlias>,
919
974
  locals: ReadonlyMap<string, AstNode>,
920
975
  ): void => {
921
- analyzeFunction(fn, "fx", aliases, locals, file);
976
+ analyzeFunction(fn, "fx", aliases, locals, file, stepBodies.has(fn));
922
977
  };
923
978
 
924
979
  const analyzeFunction = (
@@ -927,13 +982,14 @@ function collectFxChains(
927
982
  outerAliases: ReadonlyMap<string, ChainAlias>,
928
983
  outerLocals: ReadonlyMap<string, AstNode>,
929
984
  file: string,
985
+ insideStep: boolean,
930
986
  ): void => {
931
987
  if (seen.has(fn)) return;
932
988
  seen.add(fn);
933
989
  userIdRoots.push(fn);
934
990
  const body = (fn as AstNode & { body?: AstNode }).body;
935
991
  if (!body) return;
936
- analyzeBody(body, door, outerAliases, outerLocals, file);
992
+ analyzeBody(body, door, outerAliases, outerLocals, file, insideStep);
937
993
  };
938
994
 
939
995
  const analyzeBody = (
@@ -942,6 +998,7 @@ function collectFxChains(
942
998
  outerAliases: ReadonlyMap<string, ChainAlias>,
943
999
  outerLocals: ReadonlyMap<string, AstNode>,
944
1000
  file: string,
1001
+ insideStep: boolean,
945
1002
  ): void => {
946
1003
  const aliases = new Map(outerAliases);
947
1004
  const locals = new Map(outerLocals);
@@ -951,6 +1008,20 @@ function collectFxChains(
951
1008
  });
952
1009
  for (const call of callsSkippingNested(body)) {
953
1010
  const chain = chainFromCall(call, aliases);
1011
+ if (chain?.rootMethod === "step") {
1012
+ for (const arg of call.arguments) {
1013
+ const fn = unwrapValue(arg);
1014
+ if (fn && isFunctionNode(fn)) stepBodies.add(fn);
1015
+ }
1016
+ }
1017
+ if (
1018
+ !insideStep &&
1019
+ chain &&
1020
+ call === chain.rootCall &&
1021
+ (chain.rootMethod === "fetch" || chain.rootMethod === "send")
1022
+ ) {
1023
+ bareIrreversible.add(chain.rootMethod);
1024
+ }
954
1025
  if (chain) {
955
1026
  hits.push({ call, chain });
956
1027
  continue;
@@ -961,7 +1032,7 @@ function collectFxChains(
961
1032
  if (seen.has(nested)) continue;
962
1033
  const shadows = functionParams(nested).some((param) => paramIdentifierName(param) === door);
963
1034
  if (shadows) continue;
964
- analyzeFunction(nested, door, aliases, locals, file);
1035
+ analyzeFunction(nested, door, aliases, locals, file, insideStep || stepBodies.has(nested));
965
1036
  }
966
1037
  };
967
1038
 
@@ -972,9 +1043,9 @@ function collectFxChains(
972
1043
  if (second && paramIdentifierName(second) !== "fx") {
973
1044
  opaque.push("the do callback's second parameter is not named fx");
974
1045
  }
975
- analyzeFunction(options.doNode, "fx", new Map(), new Map(), startFile);
1046
+ analyzeFunction(options.doNode, "fx", new Map(), new Map(), startFile, false);
976
1047
  } else {
977
- analyzeBody(options.doNode, "fx", new Map(), new Map(), startFile);
1048
+ analyzeBody(options.doNode, "fx", new Map(), new Map(), startFile, false);
978
1049
  }
979
1050
 
980
1051
  return hits;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Joins and groupBy list every source table under the flow's reads.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { extractFromSources } from "./extract.ts";
7
+
8
+ describe("extractManifest — joins", () => {
9
+ test("innerJoin and groupBy record every table", async () => {
10
+ const manifest = await extractFromSources({
11
+ "src/app.ts": `
12
+ import { oke } from "okengine";
13
+ export const app = oke({ name: "joins" });
14
+ `,
15
+ "src/flows/orders.ts": `
16
+ import { on, flow, http, store, field } from "okengine";
17
+ import { eq, count } from "drizzle-orm";
18
+
19
+ export const db = store.sql("db");
20
+ export const orders = store.schema.table("orders", {
21
+ id: field.text().primaryKey(),
22
+ customerId: field.text(),
23
+ });
24
+ export const customers = store.schema.table("customers", {
25
+ id: field.text().primaryKey(),
26
+ });
27
+
28
+ export const totals = on(
29
+ http.get("/totals"),
30
+ flow("orders.totals", {
31
+ do: async (_input, fx) => {
32
+ return fx
33
+ .store(db)
34
+ .select({ n: count() })
35
+ .from(orders)
36
+ .innerJoin(customers, eq(orders.customerId, customers.id))
37
+ .groupBy(customers.id);
38
+ },
39
+ }),
40
+ );
41
+ `,
42
+ });
43
+ expect(manifest.flows?.["orders.totals"]?.effects?.reads?.slice().sort()).toEqual([
44
+ "sql:customers",
45
+ "sql:orders",
46
+ ]);
47
+ });
48
+ });
@@ -1577,6 +1577,34 @@ export const ping = flow({ do: () => ({ ok: true }) });
1577
1577
  expect(manifest.flows?.["notes.ping"]).toBeDefined();
1578
1578
  });
1579
1579
 
1580
+ test("fx.call of an imported flow handle records that flow's name", async () => {
1581
+ const tasks = `
1582
+ import { on, flow, http } from "okengine";
1583
+ export const create = on(
1584
+ http.post("/tasks"),
1585
+ flow("tasks.create", { do: async () => ({ id: "1" }) }),
1586
+ );
1587
+ `;
1588
+ const forms = `
1589
+ import { on, flow, http } from "okengine";
1590
+ import { create as createTask } from "../tasks/index.ts";
1591
+ export const submit = on(
1592
+ http.post("/forms/:id/submit"),
1593
+ flow("forms.submit", {
1594
+ do: async (_input, fx) => {
1595
+ await fx.call(createTask, { title: "SSO" });
1596
+ return { ok: true };
1597
+ },
1598
+ }),
1599
+ );
1600
+ `;
1601
+ const manifest = await extractFromSources({
1602
+ "src/flows/tasks/index.ts": tasks,
1603
+ "src/flows/forms/index.ts": forms,
1604
+ });
1605
+ expect(manifest.flows?.["forms.submit"]?.effects?.calls).toEqual(["tasks.create"]);
1606
+ });
1607
+
1580
1608
  test("effects are identical for the same do body in a tree file vs a barrel", async () => {
1581
1609
  const doBody = `
1582
1610
  const rows = await fx.store(db).select().from(notes);
@@ -2194,6 +2194,7 @@ function registerFlow(args: {
2194
2194
  flowName: name,
2195
2195
  file: args.file.path,
2196
2196
  resolveCallee: (callee, file) => args.scope.fxIndex.resolve(file, callee),
2197
+ resolveFlowRef: (localName, file) => args.scope.fxIndex.flowRef(file, localName),
2197
2198
  })
2198
2199
  : {
2199
2200
  effects: {} as Effects,
@@ -2202,6 +2203,7 @@ function registerFlow(args: {
2202
2203
  cacheIneligible: false,
2203
2204
  nondeterministic: false,
2204
2205
  readsUserId: false,
2206
+ bareIrreversible: [] as ("fetch" | "send")[],
2205
2207
  };
2206
2208
 
2207
2209
  let effects: Effects | undefined;
@@ -2262,7 +2264,15 @@ function registerFlow(args: {
2262
2264
  const line = lineAt(args.file.source, args.flowCall.start ?? 0);
2263
2265
  flow.source = `${args.file.path}:${line}`;
2264
2266
 
2265
- if (boolProp(opts, "durable")) flow.durable = true;
2267
+ if (boolProp(opts, "durable")) {
2268
+ flow.durable = true;
2269
+ const bare = inferred.bareIrreversible[0];
2270
+ if (bare !== undefined) {
2271
+ throw new Error(
2272
+ `OKE1901: durable flow "${name}" calls fx.${bare} outside fx.step. Wrap fx.fetch and fx.send in fx.step.`,
2273
+ );
2274
+ }
2275
+ }
2266
2276
  assertDecisionPlacement(name, flow, args.scope);
2267
2277
  if (typeof liveFromTrigger === "string") {
2268
2278
  flow.live = liveFromTrigger;
@@ -26,6 +26,14 @@ export interface FxIndex {
26
26
  * @param callee - Call callee node
27
27
  */
28
28
  resolve(file: string, callee: AstNode): FxCalleeResolution;
29
+ /**
30
+ * Flow name behind an imported handle (`import { create as createTask }`).
31
+ * Absent when `localName` is not a named import of a `flow()` / `on()` export.
32
+ *
33
+ * @param file - File that contains the `fx.call`
34
+ * @param localName - Local binding (`createTask`)
35
+ */
36
+ flowRef(file: string, localName: string): string | undefined;
29
37
  }
30
38
 
31
39
  interface NamedImport {
@@ -61,6 +69,8 @@ interface FileFx {
61
69
  readonly functions: Map<string, AstNode>;
62
70
  readonly imports: Map<string, ImportBinding>;
63
71
  readonly exports: Map<string, LocalExport | Reexport>;
72
+ /** Local binding → `flow("name")` / `on(…, flow("name"))` literal. */
73
+ readonly flowNames: Map<string, string>;
64
74
  }
65
75
 
66
76
  const INTRINSIC_SOURCES: Readonly<
@@ -176,7 +186,29 @@ export function buildFxIndex(
176
186
  return resolveExport(target, imported.imported, seen);
177
187
  }
178
188
 
189
+ function flowExportName(file: string, exportName: string, seen: Set<string>): string | undefined {
190
+ const key = `${file}#flow:${exportName}`;
191
+ if (seen.has(key)) return undefined;
192
+ seen.add(key);
193
+ const indexed = byFile.get(file);
194
+ if (!indexed) return undefined;
195
+ const exported = indexed.exports.get(exportName);
196
+ if (!exported) return indexed.flowNames.get(exportName);
197
+ if (exported.kind === "local") return indexed.flowNames.get(exported.binding);
198
+ const target = resolveModule(file, exported.from);
199
+ if (!target) return undefined;
200
+ return flowExportName(target, exported.imported, seen);
201
+ }
202
+
179
203
  return {
204
+ flowRef(file, localName) {
205
+ const from = normalizePosix(file);
206
+ const imported = byFile.get(from)?.imports.get(localName);
207
+ if (!imported || imported.kind !== "named") return undefined;
208
+ const target = resolveModule(from, imported.from);
209
+ if (!target) return undefined;
210
+ return flowExportName(target, imported.imported, new Set());
211
+ },
180
212
  resolve(file, callee) {
181
213
  const from = normalizePosix(file);
182
214
  const seen = new Set<string>();
@@ -212,9 +244,10 @@ function indexFile(program: AstNode): FileFx {
212
244
  const functions = new Map<string, AstNode>();
213
245
  const imports = new Map<string, ImportBinding>();
214
246
  const exports = new Map<string, LocalExport | Reexport>();
247
+ const flowNames = new Map<string, string>();
215
248
  const body = (program as AstNode & { body?: AstNode[] }).body ?? [];
216
- for (const stmt of body) indexStmt(stmt, functions, imports, exports);
217
- return { functions, imports, exports };
249
+ for (const stmt of body) indexStmt(stmt, functions, imports, exports, flowNames);
250
+ return { functions, imports, exports, flowNames };
218
251
  }
219
252
 
220
253
  function indexStmt(
@@ -222,6 +255,7 @@ function indexStmt(
222
255
  functions: Map<string, AstNode>,
223
256
  imports: Map<string, ImportBinding>,
224
257
  exports: Map<string, LocalExport | Reexport>,
258
+ flowNames: Map<string, string>,
225
259
  ): void {
226
260
  if (stmt.type === "ImportDeclaration") {
227
261
  indexImport(stmt, imports);
@@ -232,7 +266,7 @@ function indexStmt(
232
266
  return;
233
267
  }
234
268
  if (stmt.type === "VariableDeclaration") {
235
- rememberVars(stmt, functions, exports, false);
269
+ rememberVars(stmt, functions, exports, flowNames, false);
236
270
  return;
237
271
  }
238
272
  if (stmt.type === "ExportDefaultDeclaration") {
@@ -247,7 +281,7 @@ function indexStmt(
247
281
  const decl = (stmt as AstNode & { declaration?: AstNode | null }).declaration;
248
282
  const source = stringArg((stmt as AstNode & { source?: AstNode }).source);
249
283
  if (decl?.type === "FunctionDeclaration") rememberFunction(decl, functions, exports, true);
250
- if (decl?.type === "VariableDeclaration") rememberVars(decl, functions, exports, true);
284
+ if (decl?.type === "VariableDeclaration") rememberVars(decl, functions, exports, flowNames, true);
251
285
  const specifiers = (stmt as AstNode & { specifiers?: AstNode[] }).specifiers ?? [];
252
286
  for (const spec of specifiers) {
253
287
  const local = exportedLocalName(spec);
@@ -309,6 +343,7 @@ function rememberVars(
309
343
  decl: AstNode,
310
344
  functions: Map<string, AstNode>,
311
345
  exports: Map<string, LocalExport | Reexport>,
346
+ flowNames: Map<string, string>,
312
347
  exportIt: boolean,
313
348
  ): void {
314
349
  const declarations = (decl as AstNode & { declarations?: AstNode[] }).declarations ?? [];
@@ -317,10 +352,29 @@ function rememberVars(
317
352
  if (!name) continue;
318
353
  const init = (item as AstNode & { init?: AstNode | null }).init;
319
354
  if (init && isFunctionNode(unwrapValue(init))) functions.set(name, unwrapValue(init));
355
+ const flowName = init ? flowLiteralName(unwrapValue(init)) : undefined;
356
+ if (flowName) flowNames.set(name, flowName);
320
357
  if (exportIt) exports.set(name, { kind: "local", binding: name });
321
358
  }
322
359
  }
323
360
 
361
+ /**
362
+ * `flow("name")` / `call("name")` / `on(trigger, flow("name"))` literal.
363
+ *
364
+ * @param node - Initializer
365
+ */
366
+ function flowLiteralName(node: AstNode): string | undefined {
367
+ if (node.type !== "CallExpression") return undefined;
368
+ const call = node as AstNode & { callee: AstNode; arguments?: AstNode[] };
369
+ const callee = identifierName(call.callee);
370
+ if (callee === "flow" || callee === "call") return stringArg(call.arguments?.[0]);
371
+ if (callee === "on") {
372
+ const body = call.arguments?.[1];
373
+ return body ? flowLiteralName(unwrapValue(body)) : undefined;
374
+ }
375
+ return undefined;
376
+ }
377
+
324
378
  function exportedLocalName(spec: AstNode): string | undefined {
325
379
  return (
326
380
  identifierName((spec as AstNode & { local?: AstNode }).local) ??
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Console — operator plane UI + server (port 6533).
2
+ * Console — operator plane UI + server (port 6533). Public import: `okengine/console`.
3
3
  *
4
4
  * Built on `createClient<ConsoleApp>`. Every action is a real flow through `fx`;
5
5
  * the audit log is the trace (docs/spec/console.md).
@@ -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{$n as r,Cr as i,E as a,F as o,I as s,Qn as c,R as l,c as u,i as d,l as f,o as p,rr as m,s as h,tr as g,u as _,x as v}from"./shortcut-keys-DTE1sjTC.js";import{C as y,D as b,E as x,F as S,I as C,L as w,M as ee,N as T,O as E,P as D,T as O,W as k,_ as A,a as j,b as M,c as N,d as P,f as F,g as I,i as L,m as R,o as z,p as te,u as B,w as V,x as H}from"./explorer-chrome-Dslh2yE8.js";import{i as ne,t as re}from"./http-method-DdL19zzh.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-C6t8Vlml.js";import{t as fe}from"./UserIcon-DaE2PB5_.js";import{n as pe,t as W}from"./copy-inline-button-BTjPVjRT.js";import{Mt as G,bt as me,ht as he}from"./index-C0lc_s9d.js";import{t as ge}from"./explorer-empty-CIVg7Jgb.js";import{a as K,c as _e,l as q,n as J,o as ve,r as ye,s as be,t as xe}from"./detail-header-Dk5LipF6.js";import{t as Se}from"./confirm-sheet-DZfuJXP4.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{$n as r,Cr as i,E as a,F as o,I as s,Qn as c,R as l,c as u,i as d,l as f,o as p,rr as m,s as h,tr as g,u as _,x as v}from"./shortcut-keys-DTE1sjTC.js";import{C as y,D as b,E as x,F as S,I as C,L as w,M as ee,N as T,O as E,P as D,T as O,W as k,_ as A,a as j,b as M,c as N,d as P,f as F,g as I,i as L,m as R,o as z,p as te,u as B,w as V,x as H}from"./explorer-chrome-Dslh2yE8.js";import{i as ne,t as re}from"./http-method-Cn_Fl79s.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-C6t8Vlml.js";import{t as fe}from"./UserIcon-DaE2PB5_.js";import{n as pe,t as W}from"./copy-inline-button-BTjPVjRT.js";import{Mt as G,bt as me,ht as he}from"./index-CmRFeWdT.js";import{t as ge}from"./explorer-empty-CIVg7Jgb.js";import{a as K,c as _e,l as q,n as J,o as ve,r as ye,s as be,t as xe}from"./detail-header-Dk5LipF6.js";import{t as Se}from"./confirm-sheet-DZfuJXP4.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:i,onEdit:a,onRefresh:o,onRevoke:s,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`,y=(0,Y.useMemo)(()=>He(e.scopes),[e.scopes]),x=e.creatorScopes.length,S=x>0&&e.scopes.length<x;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:i,icon:(0,Q.jsx)(r,{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)(r,{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)(W,{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:o,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:s,children:`Revoke`})})]})}),(0,Q.jsx)(Ke,{revoked:d,expired:f,unused:e.unused90d,residual:t?.residualAccessNote}),(0,Q.jsxs)(`div`,{className:H,"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:S?`${e.scopes.length} of ${x}`:String(e.scopes.length),children:e.scopes.length>0?(0,Q.jsx)(W,{value:e.scopes.join(`
3
3
  `),label:`Copy scopes`}):null}),y.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:y.map(e=>(0,Q.jsxs)(`li`,{className:A,"aria-label":e.group,children:[(0,Q.jsx)(`span`,{className:c(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:c(H,`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:c(H,`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:c(H,`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:c(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:c(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)(W,{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:c(`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:c(`shrink-0 font-mono text-[10px] font-semibold tracking-[0.08em] uppercase`,E(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)(W,{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:c(H,`items-center gap-2 px-2`),children:[(0,Q.jsx)(`h3`,{className:c(b,`shrink-0 leading-none`),children:e}),t?(0,Q.jsx)(`span`,{className:c(N,!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:c(y,V,`gap-1.5`),children:[(0,Q.jsx)(`span`,{className:`tracking-[0.08em] uppercase`,children:e}),(0,Q.jsx)(`span`,{className:c(`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)(W,{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:c(b,`w-[5.5rem] shrink-0`),children:e}),(0,Q.jsx)(`dd`,{className:c(`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 m({queryKey:$e,queryFn:async()=>{let e=await D();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:te,children:`Loading…`}):null,!r&&e.length===0&&n.trim().length>0?(0,Q.jsx)(`p`,{className:te,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)(r,{icon:e.icon,className:c(F,`text-muted-foreground`),"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:c(z,`flex-1`),children:e.label}),(0,Q.jsx)(`span`,{className:N,children:e.rows.length})]}),e.rows.map(t=>{let n=t.id===e.selectedKey,i=t.revokedAt!==null;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-key-row`,className:c(A,n&&`bg-muted/70 text-foreground`),onClick:()=>e.onSelect(t.id),children:[(0,Q.jsx)(`span`,{className:c(I,n&&`bg-sky-500`)}),(0,Q.jsx)(r,{icon:oe,className:c(F,`text-muted-foreground`)}),(0,Q.jsx)(`span`,{className:c(`min-w-0 truncate`,i&&`text-muted-foreground line-through`),children:t.name}),i?(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:c(`ml-auto tabular-nums text-[10px] text-muted-foreground`),children:t.scopes.length})]},t.id)})]})}function rt({entries:e,rateMax:t,rateCount:n,rateUnit:i,onEntries:u,onRateMax:d,onRateCount:f,onRateUnit:p}){let[m,h]=(0,Y.useState)(``),_=Re(m),v=m.trim().length>0&&_.length===0,x=()=>{if(_.length===0)return;let t=new Set(e),n=[...e];for(let e of _)t.has(e)||(t.add(e),n.push(e));u(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:m,onChange:e=>h(e.target.value),onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),x())},placeholder:`203.0.113.4 or ci.example.com`,autoComplete:`off`,"aria-label":`Add allow entry`,"aria-invalid":v,flat:!0,className:c(J,`min-w-0 flex-1 font-mono`)}),e.length>0?(0,Q.jsx)(`span`,{className:c(N,`flex h-8 items-center pr-2`),children:e.length}):null,(0,Q.jsx)(`button`,{type:`button`,className:c(P,`h-8`),"aria-label":`Add allow entry`,disabled:_.length===0,onClick:x,children:(0,Q.jsx)(r,{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:c(A,`last:border-b-0`),children:[(0,Q.jsx)(`span`,{className:c(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:c(`min-w-0 truncate font-mono text-[11px]`,n==null&&`text-destructive`),children:t}),(0,Q.jsx)(`button`,{type:`button`,className:c(B,`ml-auto opacity-0 group-hover:opacity-100 focus-visible:opacity-100`),"aria-label":`Remove ${t}`,onClick:()=>u(e.filter(e=>e!==t)),children:(0,Q.jsx)(r,{icon:g,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=>d(e.target.value),placeholder:`none`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate max`,flat:!0,className:c(J,`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=>f(e.target.value),placeholder:`1`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate window`,flat:!0,className:c(J,`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)(o,{children:[(0,Q.jsx)(l,{render:t=>(0,Q.jsx)(`button`,{...t,type:`button`,"aria-pressed":i===e,"aria-label":Pe[e],onClick:n=>{t.onClick?.(n),p(e)},className:c(y,`h-8 px-1.5 font-mono font-semibold`,i===e?O:V),children:e})}),(0,Q.jsx)(s,{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)(K,{active:e===`never`,onClick:()=>n(`never`),children:`never`}),(0,Q.jsx)(K,{active:e===`30d`,onClick:()=>n(`30d`),children:`30d`}),(0,Q.jsx)(K,{active:e===`90d`,onClick:()=>n(`90d`),children:`90d`}),(0,Q.jsx)(K,{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:c(J,`font-mono`)})}):null]})}function at({value:e,placeholder:t,label:n,onChange:i}){return(0,Q.jsxs)(`label`,{className:`relative min-w-0 flex-1`,children:[(0,Q.jsx)(r,{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=>i(e.target.value),placeholder:t,"aria-label":n,autoComplete:`off`,flat:!0,className:c(ye)})]})}function ot({scopes:e,selected:t,empty:n,onChange:r}){let[i,a]=(0,Y.useState)(``),o=new Set(t),s=(0,Y.useMemo)(()=>st(e,i),[e,i]),l=(0,Y.useMemo)(()=>He(s),[s]),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}):s.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=o.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:c(`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:o,onOpenChange:s,onSubmit:l}){let[m,g]=(0,Y.useState)(`user`),[v,y]=(0,Y.useState)(``),[b,x]=(0,Y.useState)(``),[S,C]=(0,Y.useState)([]),[w,ee]=(0,Y.useState)(`never`),[T,E]=(0,Y.useState)(``),[D,O]=(0,Y.useState)([]),[k,j]=(0,Y.useState)(``),[M,N]=(0,Y.useState)(``),[P,F]=(0,Y.useState)(`m`),[L,R]=(0,Y.useState)(``),z=t.find(e=>e.id===b)??null,te=(0,Y.useMemo)(()=>{let e=L.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 z&&!n.some(e=>e.id===z.id)?[z,...n]:n},[t,L,z]),B=(0,Y.useMemo)(()=>m===`operator`?r:z==null?[]:z.scopes.length>0?z.scopes:n,[m,n,r,z]);(0,Y.useEffect)(()=>{e||(g(`user`),y(``),x(``),C([]),ee(`never`),E(``),O([]),j(``),N(``),F(`m`),R(``))},[e]),(0,Y.useEffect)(()=>{C(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(T),H=Fe(k,M,P),ne=v.trim().length>0&&S.length>0&&!i&&H!==void 0&&(m===`operator`||b.length>0)&&(w!==`custom`||V>0);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:s,children:(0,Q.jsxs)(p,{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)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Create API key`}),(0,Q.jsx)(h,{className:`text-[11px]`,children:m===`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:v,onChange:e=>y(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:c(J,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(ve,{label:`Plane`,children:[(0,Q.jsx)(K,{active:m===`user`,icon:fe,onClick:()=>{g(`user`)},children:`user`}),(0,Q.jsx)(K,{active:m===`operator`,icon:Ce,onClick:()=>{g(`operator`),x(``),R(``)},children:`operator`})]}),(0,Q.jsx)(it,{expires:w,customExpires:T,onExpires:ee,onCustomExpires:E}),(0,Q.jsx)(rt,{entries:D,rateMax:k,rateCount:M,rateUnit:P,onEntries:O,onRateMax:j,onRateCount:N,onRateUnit:F}),m===`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:L,placeholder:`Search users`,label:`Search users`,onChange:R}):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.`}):te.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:te.map(e=>{let t=e.id===b;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-creator-user`,"aria-pressed":t,className:c(A,t&&`bg-muted/70 text-foreground`),onClick:()=>x(e.id),children:[(0,Q.jsx)(`span`,{className:c(I,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:S,empty:m===`user`&&b.length===0?`Select an issuer to see grantable scopes.`:m===`user`?`This user has no grants.`:`No grantable scopes on this plane.`,onChange:C}),o?(0,Q.jsx)(be,{slot:`access-create-error`,children:o}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>s(!1),disabled:i,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!ne,"data-slot":`access-create-submit`,onClick:()=>{if(H===void 0)return;let e=Oe(w,Date.now(),T);l({plane:m,name:v.trim(),scopes:S,...m===`user`?{creatorUserId:b}:{},...e==null?{}:{expiresAt:e},...H==null?{}:{rateLimit:H},...D.length>0?{ipAllowlist:D}:{}})},children:i?`Creating…`:`Create`})]})]})})}function lt({open:e,keyRow:t,users:n,operators:i,operatorScopes:o,pending:s,error:l,now:m=Date.now(),onOpenChange:g,onSubmit:v}){let y=ke(t.expiresAt,m),[b,x]=(0,Y.useState)(t.name),[S,C]=(0,Y.useState)(t.scopes),[w,ee]=(0,Y.useState)(y.choice),[T,E]=(0,Y.useState)(y.custom),[D,O]=(0,Y.useState)(t.ipAllowlist),k=Ie(t.rateLimit?.per??``),[A,j]=(0,Y.useState)(t.rateLimit?String(t.rateLimit.max):``),[M,N]=(0,Y.useState)(k.count),[P,F]=(0,Y.useState)(k.unit);(0,Y.useEffect)(()=>{if(!e)return;let n=ke(t.expiresAt,m);x(t.name),C(t.scopes),ee(n.choice),E(n.custom),O(t.ipAllowlist);let r=Ie(t.rateLimit?.per??``);j(t.rateLimit?String(t.rateLimit.max):``),N(r.count),F(r.unit)},[e,t.id]);let I=t.creatorId??``,L=(0,Y.useMemo)(()=>I.length===0?null:n.find(e=>e.id===I)??i.find(e=>e.id===I)??null,[I,i,n]),R=L?L.name.trim()||L.email.trim()||L.id:I||`Unknown`,z=(0,Y.useMemo)(()=>{let e=t.creatorScopes??[];return e.length>0?e:t.plane===`operator`?L&&L.scopes.length>0?L.scopes:o:L?.scopes??[]},[L,t.creatorScopes,t.plane,o]),te=Array.from(new Set([...z,...t.scopes])),B=De(T),V=Fe(A,M,P),H=b.trim().length>0&&S.length>0&&!s&&V!==void 0&&(w!==`custom`||B>0);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:g,children:(0,Q.jsxs)(p,{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)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Edit key`}),(0,Q.jsx)(h,{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:b,onChange:e=>x(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:c(J,`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 ${R}`,children:[(0,Q.jsx)(r,{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:R})]}),I.length>0&&I!==R?(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pr-4 font-mono text-[10px] text-muted-foreground`,children:I}):null]}),(0,Q.jsx)(it,{expires:w,customExpires:T,onExpires:ee,onCustomExpires:E}),(0,Q.jsx)(rt,{entries:D,rateMax:A,rateCount:M,rateUnit:P,onEntries:O,onRateMax:j,onRateCount:N,onRateUnit:F}),(0,Q.jsx)(ot,{scopes:te,selected:S,empty:t.plane===`user`&&L==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:C}),l?(0,Q.jsx)(be,{slot:`access-edit-error`,children:l}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>g(!1),disabled:s,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!H,"data-slot":`access-edit-submit`,onClick:()=>{V!==void 0&&v({name:b.trim(),scopes:S,expiresAt:Oe(w,Date.now(),T),rateLimit:V,ipAllowlist:D})},children:s?`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),[m,g]=(0,Y.useState)(s.custom);(0,Y.useEffect)(()=>{if(!e)return;let n=je(t.expiresAt,i);l(n.choice),g(n.custom)},[e,t.id]);let v=De(m);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:a,children:(0,Q.jsxs)(p,{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)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Refresh expiry`}),(0,Q.jsx)(h,{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:m,onExpires:l,onCustomExpires:g}),r?(0,Q.jsx)(be,{slot:`access-refresh-error`,children:r}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>a(!1),disabled:n,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!(!n&&(c!==`custom`||v>0)),"data-slot":`access-refresh-submit`,onClick:()=>{o(Oe(c,Date.now(),m))},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)(d,{open:e!==null,onOpenChange:e=>{e||n()},children:(0,Q.jsxs)(p,{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)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:t}),(0,Q.jsx)(h,{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)(u,{children:[(0,Q.jsx)(q,{split:!0,disabled:!e,onClick:()=>{e&&(navigator.clipboard?.writeText(e),i(!0))},children:r?`Copied`:`Copy (clipboard warning)`}),(0,Q.jsx)(q,{variant:`destructive`,onClick:()=>{i(!1),n()},children:`I have stored this key`})]})]})})}function ft(){let e=et(),t=i(),{query:n,selectedKey:a,action:o,setQuery:s,setSelectedKey:c,setAction:l}=me(),u=ce(),[d]=(0,Y.useState)(()=>Date.now()),[f,p]=(0,Y.useState)(null),[h,g]=(0,Y.useState)(`New API key`),_=(0,Y.useMemo)(()=>{let t=e.data?.userPlane.keys??[],n=e.data?.operatorPlane.keys??[];return[...t,...n]},[e.data]),v=(0,Y.useMemo)(()=>{let e=n.trim().toLowerCase();return e?_.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.scopes.some(t=>t.toLowerCase().includes(e))):_},[_,n]),y=v.find(e=>e.id===a)??_.find(e=>e.id===a),b=m({queryKey:[`console.access.blast`,y?.id],queryFn:async()=>{if(!y)throw Error(`no key`);let e=await T(y.id);if(e.error||!e.data)throw Error(e.error?.message??`blast failed`);return e.data},enabled:y!==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 E=()=>{t.invalidateQueries({queryKey:$e})},D=G({mutationFn:ee,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`create failed`);g(`New API key`),p(e.data.secret),c(e.data.key.id),l(null),E()}}),O=G({mutationFn:e=>C({keyId:y.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`rotate failed`);g(`Rotated API key`),p(e.data.secret),l(null),E()}}),A=G({mutationFn:e=>S({keyId:y.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`revoke failed`);l(null),E()}}),j=G({mutationFn:w,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`update failed`);l(null),E()}}),N=G({mutationFn:e=>w({keyId:y.id,expiresAt:e}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`refresh failed`);l(null),E()}}),F=(0,Q.jsx)(le,{open:u.open,onToggle:u.toggle,noun:`keys`,controlsId:`access-list`,dataSlot:`access-list-toggle`});return(0,Q.jsxs)(`div`,{className:R,"data-slot":`access-page`,children:[(0,Q.jsxs)(ie,{orientation:`horizontal`,className:`min-h-0 flex-1`,children:[(0,Q.jsx)(se,{panelRef:u.panelRef,collapsible:!0,collapsedSize:0,defaultSize:M.start.defaultSize,minSize:M.start.minSize,onResize:u.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:x,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:P,"data-slot":`access-create`,"aria-label":`Create key`,onClick:()=>l(`create`),children:(0,Q.jsx)(r,{icon:pe,className:`size-3.5`})})})]}),(0,Q.jsx)(tt,{rows:v,selectedKey:a,query:n,loading:e.isLoading,onSelect:c})]})}),u.open?(0,Q.jsx)(ue,{withHandle:!0}):null,(0,Q.jsx)(se,{defaultSize:M.end.defaultSize,minSize:M.end.minSize,className:`min-h-0 overflow-hidden`,children:y?(0,Q.jsx)(Ge,{keyRow:y,blast:b.data,now:d,leading:F,onEdit:()=>l(`edit`),onRefresh:()=>l(`refresh`),onRevoke:()=>l(`revoke`),onRotate:()=>l(`rotate`),rotatePending:O.isPending}):(0,Q.jsx)(ge,{icon:de.gate.icon,iconClassName:`border-border/70`,title:e.isLoading?`Loading keys…`:_.length>0?`Select a key`:`No keys yet`,leading:F,description:e.isLoading?`Reading planes and grantable scopes.`:_.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:D.isPending,error:D.error instanceof Error?D.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>D.mutate(e)}),y?(0,Q.jsx)(lt,{open:o===`edit`,keyRow:y,users:e.data?.userPlane.users??[],operators:e.data?.operatorPlane.operators??[],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({keyId:y.id,...e})}):null,y?(0,Q.jsx)(ut,{open:o===`refresh`,keyRow:y,pending:N.isPending,error:N.error instanceof Error?N.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>N.mutate(e)}):null,y?(0,Q.jsx)(Se,{open:o===`revoke`,onOpenChange:e=>{e||l(null)},phrase:`REVOKE`,title:`Revoke ${y.name}`,description:`Irreversible. Residual sessions may continue for the access TTL.`,pending:A.isPending,error:A.error instanceof Error?A.error.message:null,confirmLabel:`Revoke key`,slot:`access-revoke-sheet`,onConfirm:e=>A.mutate({confirmation:`REVOKE`,reason:e.reason})}):null,y?(0,Q.jsx)(Se,{open:o===`rotate`,onOpenChange:e=>{e||l(null)},phrase:`ROTATE`,title:`Rotate ${y.name}`,description:`Mints a new secret once. The current secret stops after residual TTL.`,pending:O.isPending,error:O.error instanceof Error?O.error.message:null,confirmLabel:`Rotate key`,confirmVariant:`ghost`,slot:`access-rotate-sheet`,onConfirm:e=>O.mutate({confirmation:`ROTATE`,reason:e.reason})}):null,(0,Q.jsx)(dt,{secret:f,title:h,onDismiss:()=>p(null)})]})}export{ft as AccessPage};
@@ -1 +1 @@
1
- import{A as e}from"./xyflow-yApv7D4e.js";import{Qn as t}from"./shortcut-keys-DTE1sjTC.js";import{c as n,l as r,st as i}from"./index-C0lc_s9d.js";var a=e();function o({open:e,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=r()??!1;return(0,a.jsx)(n.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:i},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{Qn as t}from"./shortcut-keys-DTE1sjTC.js";import{c as n,l as r,st as i}from"./index-CmRFeWdT.js";var a=e();function o({open:e,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=r()??!1;return(0,a.jsx)(n.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:i},className:t(`overflow-hidden`,s),style:{...c,height:e?o:0,pointerEvents:e?void 0:`none`,transformOrigin:`top`}})}export{o as t};