okengine 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -4
- package/site/content/docs/elements/ai.mdx +2 -1
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +3 -0
- package/site/content/docs/elements/flow.mdx +9 -0
- package/site/content/docs/elements/gate.mdx +80 -22
- package/site/content/docs/elements/signal.mdx +4 -4
- package/site/content/docs/elements/store.mdx +84 -27
- package/site/content/docs/elements/vault.mdx +4 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +28 -21
- package/site/content/docs/reference/fx.mdx +29 -18
- package/site/content/docs/reference/security.md +2 -2
- package/src/auth/api-key-sql.test.ts +47 -0
- package/src/auth/api-key-sql.ts +254 -0
- package/src/auth/api-keys.ts +273 -14
- package/src/auth/auth.test.ts +56 -0
- package/src/auth/bindings.ts +14 -5
- package/src/auth/config.ts +5 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/auth/index.ts +25 -1
- package/src/auth/schema.ts +8 -0
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/attach-host-console.test.ts +40 -2
- package/src/cli/attach-host-console.ts +9 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +17 -2
- package/src/compiler/extract.test.ts +409 -33
- package/src/compiler/extract.ts +318 -67
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/access.test.ts +116 -8
- package/src/console/server/access.ts +70 -23
- package/src/console/server/app.ts +1 -0
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/dev-identities.test.ts +127 -0
- package/src/console/server/dev-identities.ts +229 -0
- package/src/console/server/flows-invoke.test.ts +21 -4
- package/src/console/server/flows.ts +233 -98
- package/src/console/server/gates.test.ts +24 -0
- package/src/console/server/gates.ts +8 -21
- package/src/console/server/index.ts +1 -0
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/live.test.ts +26 -1
- package/src/console/server/live.ts +4 -2
- package/src/console/server/operator-db.ts +2 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +37 -55
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
- package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
- package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
- package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
- package/src/console/ui-next/dist/index.html +12 -10
- package/src/console/ui-next/index.html +3 -3
- package/src/console/ui-next/src/client.ts +202 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
- package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
- package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
- package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
- package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
- package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
- package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
- package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
- package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
- package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
- package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
- package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
- package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
- package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
- package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
- package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
- package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
- package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
- package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
- package/src/console/ui-next/src/lib/document-meta.ts +2 -1
- package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
- package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
- package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
- package/src/console/ui-next/src/lib/shortcut.ts +3 -0
- package/src/console/ui-next/src/router.tsx +10 -1
- package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
- package/src/console/ui-next/ui-next-seed-store.ts +43 -15
- package/src/console/xss.gate.test.ts +1 -0
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +3 -0
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/types.ts +10 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +38 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/store/cache.ts +1 -1
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +18 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +260 -41
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-select.types.test.ts +71 -0
- package/src/elements/store/sql-session.ts +135 -35
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +17 -0
- package/src/http.ts +20 -3
- package/src/index.ts +11 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/api-key-host-persist.test.ts +143 -0
- package/src/kernel/app.ts +145 -16
- package/src/kernel/auth-resolve.ts +10 -5
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +49 -9
- package/src/kernel/boot.test.ts +5 -5
- package/src/kernel/boot.ts +2 -2
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +12 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-auth-keys.test.ts +88 -0
- package/src/kernel/fx-auth-keys.ts +198 -0
- package/src/kernel/fx.test.ts +17 -1
- package/src/kernel/fx.ts +59 -18
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +4 -4
- package/src/kernel/pipeline-api-key.test.ts +192 -7
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/pipeline.ts +4 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/types.ts +22 -3
- package/src/release/build-lib.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +8 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
|
@@ -56,6 +56,12 @@ import type { InstanceStore, InstancesList } from "../../kernel/instances.ts";
|
|
|
56
56
|
import type { CronStore } from "../../elements/clock/reconcile.ts";
|
|
57
57
|
import type { ResourceRef } from "../../manifest/types.ts";
|
|
58
58
|
import type { ConsoleClockList } from "./clock.ts";
|
|
59
|
+
import {
|
|
60
|
+
defaultDevIdentities,
|
|
61
|
+
isKeelManifest,
|
|
62
|
+
refreshSeededIdentities,
|
|
63
|
+
seedKeelAccessRoles,
|
|
64
|
+
} from "./dev-identities.ts";
|
|
59
65
|
import {
|
|
60
66
|
createDefaultGateAuthStores,
|
|
61
67
|
createManifestGateRuntime,
|
|
@@ -89,6 +95,8 @@ import {
|
|
|
89
95
|
requireConsoleAuthStore,
|
|
90
96
|
} from "./auth-store.ts";
|
|
91
97
|
|
|
98
|
+
export { refreshSeededIdentities } from "./dev-identities.ts";
|
|
99
|
+
|
|
92
100
|
/** How long a built-in Vault backend probe is reused across list polls. */
|
|
93
101
|
const VAULT_BACKEND_TTL_MS = 15_000;
|
|
94
102
|
|
|
@@ -125,8 +133,8 @@ export interface ConsoleState {
|
|
|
125
133
|
readonly constructedPanels: ReadonlySet<ConsolePanelId>;
|
|
126
134
|
/** Role store (data, not code) — Gates · Access. */
|
|
127
135
|
readonly roles: RoleStore;
|
|
128
|
-
/** API key store — first-class principals. */
|
|
129
|
-
|
|
136
|
+
/** API key store — first-class principals. Shared with the host when attached. */
|
|
137
|
+
apiKeys: ApiKeyStore;
|
|
130
138
|
/** Operator invitations — Access hygiene. */
|
|
131
139
|
readonly invites: OperatorInviteStore;
|
|
132
140
|
/** Access-token TTL (ms) from auth config — residual revoke delay. */
|
|
@@ -159,6 +167,15 @@ export interface ConsoleState {
|
|
|
159
167
|
accessRotateKey: (
|
|
160
168
|
keyId: string,
|
|
161
169
|
) => Promise<{ readonly row: AccessKeyRow; readonly secret: string } | null>;
|
|
170
|
+
/** Update key metadata (name, scopes, expiry, allowlist, rate). */
|
|
171
|
+
accessUpdateKey: (input: {
|
|
172
|
+
readonly keyId: string;
|
|
173
|
+
readonly name?: string;
|
|
174
|
+
readonly scopes?: readonly string[];
|
|
175
|
+
readonly expiresAt?: number | null;
|
|
176
|
+
readonly rateLimit?: { max: number; per: string } | null;
|
|
177
|
+
readonly ipAllowlist?: readonly string[];
|
|
178
|
+
}) => Promise<AccessKeyRow | null>;
|
|
162
179
|
/** Replace role grants (grantable scopes only). */
|
|
163
180
|
accessSetRoleGrants: (input: {
|
|
164
181
|
readonly roleId: string;
|
|
@@ -267,13 +284,21 @@ export interface ConsoleState {
|
|
|
267
284
|
readonly revealPii?: boolean;
|
|
268
285
|
readonly tenant?: string;
|
|
269
286
|
readonly asGate?: string;
|
|
287
|
+
readonly asUserId?: string;
|
|
288
|
+
readonly rls?: import("../../drivers/pg-rls.ts").RlsIdentity;
|
|
270
289
|
},
|
|
271
290
|
) => Promise<{
|
|
272
291
|
readonly rows: readonly Record<string, unknown>[];
|
|
273
292
|
readonly masked: boolean;
|
|
274
293
|
readonly routedRole: "primary" | "replica";
|
|
275
294
|
readonly asGate: string | null;
|
|
295
|
+
readonly asUserId: string | null;
|
|
276
296
|
readonly gateApplied: boolean;
|
|
297
|
+
readonly rls: {
|
|
298
|
+
readonly gate: string | null;
|
|
299
|
+
readonly userId: string;
|
|
300
|
+
readonly applied: boolean;
|
|
301
|
+
};
|
|
277
302
|
}>;
|
|
278
303
|
/** Engine-native pg_stat_statements + KPIs. */
|
|
279
304
|
queryStoreSqlStats: (ref: ResourceRef) => Promise<import("./store-stats.ts").StoreSqlStatsResult>;
|
|
@@ -577,6 +602,8 @@ export interface CreateConsoleStateOptions {
|
|
|
577
602
|
readonly persistOperator?: (operatorId: string) => void | Promise<void>;
|
|
578
603
|
/** Persist hook after session issue / revoke. */
|
|
579
604
|
readonly persistSessions?: () => void | Promise<void>;
|
|
605
|
+
/** Shared host API key store (`oke dev` attach). */
|
|
606
|
+
readonly apiKeys?: ApiKeyStore;
|
|
580
607
|
/**
|
|
581
608
|
* Shared secret for host → Console runs ingest (`oke dev` bridge).
|
|
582
609
|
* When omitted, ingest is disabled (404).
|
|
@@ -644,7 +671,7 @@ export function createConsoleState(options: CreateConsoleStateOptions = {}): Con
|
|
|
644
671
|
return constructed;
|
|
645
672
|
},
|
|
646
673
|
roles: gateAuth.roles,
|
|
647
|
-
apiKeys: gateAuth.apiKeys,
|
|
674
|
+
apiKeys: options.apiKeys ?? gateAuth.apiKeys,
|
|
648
675
|
invites,
|
|
649
676
|
accessTtlMs: options.accessTtlMs ?? ACCESS_TTL_MS,
|
|
650
677
|
roleMembers: gateAuth.roleMembers,
|
|
@@ -709,6 +736,10 @@ export function createConsoleState(options: CreateConsoleStateOptions = {}): Con
|
|
|
709
736
|
const access = await markPanel<typeof import("./access.ts")>("access");
|
|
710
737
|
return access.accessRotateKey(state.apiKeys, keyId);
|
|
711
738
|
},
|
|
739
|
+
accessUpdateKey: async (input) => {
|
|
740
|
+
const access = await markPanel<typeof import("./access.ts")>("access");
|
|
741
|
+
return access.accessUpdateKey(state.apiKeys, input);
|
|
742
|
+
},
|
|
712
743
|
accessSetRoleGrants: async (input) => {
|
|
713
744
|
const access = await markPanel<typeof import("./access.ts")>("access");
|
|
714
745
|
access.accessSetRoleGrants({
|
|
@@ -1254,6 +1285,8 @@ export function setManifest(state: ConsoleState, manifest: Manifest): void {
|
|
|
1254
1285
|
const before = state.manifest;
|
|
1255
1286
|
if (before) state.previousManifest = before;
|
|
1256
1287
|
state.manifest = manifest;
|
|
1288
|
+
refreshSeededIdentities(state.identities, manifest);
|
|
1289
|
+
if (isKeelManifest(manifest)) seedKeelAccessRoles(state.roles, state.roleMembers);
|
|
1257
1290
|
publishLive(state, { type: "manifest", manifest });
|
|
1258
1291
|
if (before) {
|
|
1259
1292
|
publishLive(state, { type: "manifest.diff", before, after: manifest });
|
|
@@ -1378,39 +1411,6 @@ export async function bindManifestGateRuntime(state: ConsoleState): Promise<void
|
|
|
1378
1411
|
state.gateRuntime = runtime;
|
|
1379
1412
|
}
|
|
1380
1413
|
|
|
1381
|
-
/**
|
|
1382
|
-
* Default development identities for the invoke-as picker.
|
|
1383
|
-
* Demo scopes follow Manifest gate `scopes` when present so Call API
|
|
1384
|
-
* matches the seeded app (keel `issue:write`, skyport `booking:create`).
|
|
1385
|
-
*
|
|
1386
|
-
* @param manifest - Optional Manifest used to derive application scopes
|
|
1387
|
-
*/
|
|
1388
|
-
function defaultDevIdentities(manifest?: Manifest | null): ConsoleIdentity[] {
|
|
1389
|
-
const scopes = new Set<string>(["member"]);
|
|
1390
|
-
for (const gate of Object.values(manifest?.gates ?? {})) {
|
|
1391
|
-
for (const scope of gate.scopes ?? []) {
|
|
1392
|
-
scopes.add(scope);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
if (scopes.size === 1) scopes.add("booking:create");
|
|
1396
|
-
return [
|
|
1397
|
-
{
|
|
1398
|
-
id: "user_demo",
|
|
1399
|
-
email: "demo@example.com",
|
|
1400
|
-
name: "Demo User",
|
|
1401
|
-
status: "active",
|
|
1402
|
-
scopes: [...scopes].sort(),
|
|
1403
|
-
},
|
|
1404
|
-
{
|
|
1405
|
-
id: "user_member",
|
|
1406
|
-
email: "member@example.com",
|
|
1407
|
-
name: "Member",
|
|
1408
|
-
status: "active",
|
|
1409
|
-
scopes: ["member"],
|
|
1410
|
-
},
|
|
1411
|
-
];
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
1414
|
function accessCatalog(state: ConsoleState): string[] {
|
|
1415
1415
|
if (state.manifest) return deriveModuleActions(state.manifest);
|
|
1416
1416
|
const set = new Set<string>();
|
|
@@ -1428,29 +1428,11 @@ function accessCatalog(state: ConsoleState): string[] {
|
|
|
1428
1428
|
* Never-signed-in operators appear once real invites land with lastSeenAt null.
|
|
1429
1429
|
*/
|
|
1430
1430
|
function seedAccessDemoData(
|
|
1431
|
-
|
|
1431
|
+
_apiKeys: ApiKeyStore,
|
|
1432
1432
|
invites: OperatorInviteStore,
|
|
1433
1433
|
now: () => number,
|
|
1434
1434
|
): void {
|
|
1435
1435
|
const t = now();
|
|
1436
|
-
const staleCreated = t - 100 * 24 * 60 * 60 * 1000;
|
|
1437
|
-
if (!apiKeys.keys.has("key_stale")) {
|
|
1438
|
-
apiKeys.keys.set("key_stale", {
|
|
1439
|
-
id: "key_stale",
|
|
1440
|
-
plane: "user",
|
|
1441
|
-
hash: "stale",
|
|
1442
|
-
name: "Stale unused key",
|
|
1443
|
-
scopes: ["member"],
|
|
1444
|
-
expiresAt: null,
|
|
1445
|
-
rateLimit: null,
|
|
1446
|
-
ipAllowlist: [],
|
|
1447
|
-
creatorId: "user_demo",
|
|
1448
|
-
creatorScopes: ["member"],
|
|
1449
|
-
createdAt: staleCreated,
|
|
1450
|
-
lastUsedAt: null,
|
|
1451
|
-
revokedAt: null,
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
1436
|
if (!invites.invites.has("invite_expired")) {
|
|
1455
1437
|
invites.invites.set("invite_expired", {
|
|
1456
1438
|
id: "invite_expired",
|
|
@@ -578,10 +578,11 @@ describe("queryStore SQL catalog", () => {
|
|
|
578
578
|
declaredFingerprint: "x",
|
|
579
579
|
appliedFingerprint: "x",
|
|
580
580
|
});
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
).toBe(true);
|
|
581
|
+
const listedBookings = listedOn.stores
|
|
582
|
+
.find((s) => s.ref === "sql:db")
|
|
583
|
+
?.children.find((c) => c.name === "bookings");
|
|
584
|
+
expect(listedBookings?.rls).toBe(true);
|
|
585
|
+
expect(listedBookings?.rlsPolicyCount).toBe(1);
|
|
585
586
|
|
|
586
587
|
await editStore(
|
|
587
588
|
runtime,
|
|
@@ -1097,7 +1098,9 @@ describe("runStoreSql", () => {
|
|
|
1097
1098
|
asGate: "public",
|
|
1098
1099
|
});
|
|
1099
1100
|
expect(result.asGate).toBe("public");
|
|
1101
|
+
expect(result.asUserId).toBeNull();
|
|
1100
1102
|
expect(result.gateApplied).toBe(false);
|
|
1103
|
+
expect(result.rls.applied).toBe(false);
|
|
1101
1104
|
expect(result.rows).toHaveLength(1);
|
|
1102
1105
|
await runtime.close();
|
|
1103
1106
|
});
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
type SqlStoreHandle,
|
|
30
30
|
type StoreRuntime,
|
|
31
31
|
} from "../../elements/store.ts";
|
|
32
|
+
import { RLS_CONTEXT_DRIVERS, type RlsIdentity } from "../../drivers/pg-rls.ts";
|
|
32
33
|
import { DryRunWriteIsolationError, withDryRun } from "../../kernel/dry-run.ts";
|
|
33
34
|
import { addPiiFieldName, piiNameAliases, PII_MASK } from "../../elements/store/classify.ts";
|
|
34
35
|
import { rankIndexHits } from "./index-search.ts";
|
|
@@ -54,6 +55,7 @@ import {
|
|
|
54
55
|
dropSqlPolicy,
|
|
55
56
|
applySqlTableRls,
|
|
56
57
|
listSqlCatalog,
|
|
58
|
+
listSqlTablePolicyCounts,
|
|
57
59
|
listSqlTableRls,
|
|
58
60
|
setSqlExtension,
|
|
59
61
|
setSqlRowSecurity,
|
|
@@ -107,6 +109,8 @@ export interface ConsoleStoreChild {
|
|
|
107
109
|
readonly columnDescriptions: Readonly<Record<string, string>>;
|
|
108
110
|
/** Live RLS (`pg_class.relrowsecurity`) when the engine can report it. */
|
|
109
111
|
readonly rls?: boolean;
|
|
112
|
+
/** Live + declared RLS policy count when the child is a SQL table. */
|
|
113
|
+
readonly rlsPolicyCount?: number;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
/** One row in `console.store.list`. */
|
|
@@ -183,6 +187,7 @@ export async function projectStoresList(options: ProjectStoresOptions): Promise<
|
|
|
183
187
|
ref,
|
|
184
188
|
facet,
|
|
185
189
|
childrenOf(manifest!, name, facet, store),
|
|
190
|
+
store.tables,
|
|
186
191
|
);
|
|
187
192
|
const replicaLagMs = facet === "sql" ? latestReplicaLag(options.runs ?? [], children) : null;
|
|
188
193
|
|
|
@@ -291,14 +296,54 @@ async function withSqlTableRls(
|
|
|
291
296
|
ref: ResourceRef,
|
|
292
297
|
facet: StoreFacet,
|
|
293
298
|
children: readonly ConsoleStoreChild[],
|
|
299
|
+
tables?: NonNullable<Manifest["stores"]>[string]["tables"],
|
|
294
300
|
): Promise<readonly ConsoleStoreChild[]> {
|
|
295
|
-
|
|
301
|
+
const declared = declaredTableRls(tables);
|
|
302
|
+
const declaredCounts = declaredTablePolicyCounts(tables);
|
|
303
|
+
if (facet !== "sql" || runtime === null) {
|
|
304
|
+
return applySqlTableRls(children, new Map(), declared, declaredCounts);
|
|
305
|
+
}
|
|
296
306
|
try {
|
|
297
307
|
const sql = (await runtime.openRef(ref, { effects: { reads: [ref] } })) as SqlStoreHandle;
|
|
298
|
-
|
|
308
|
+
const policyCounts = mergePolicyCounts(await listSqlTablePolicyCounts(sql), declaredCounts);
|
|
309
|
+
return applySqlTableRls(children, await listSqlTableRls(sql), declared, policyCounts);
|
|
299
310
|
} catch {
|
|
300
|
-
return children;
|
|
311
|
+
return applySqlTableRls(children, new Map(), declared, declaredCounts);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function declaredTableRls(
|
|
316
|
+
tables: NonNullable<Manifest["stores"]>[string]["tables"] | undefined,
|
|
317
|
+
): ReadonlyMap<string, boolean> {
|
|
318
|
+
const out = new Map<string, boolean>();
|
|
319
|
+
for (const [name, table] of Object.entries(tables ?? {})) {
|
|
320
|
+
if (table.rls === true || Object.keys(table.policies ?? {}).length > 0) {
|
|
321
|
+
out.set(name, true);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return out;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function declaredTablePolicyCounts(
|
|
328
|
+
tables: NonNullable<Manifest["stores"]>[string]["tables"] | undefined,
|
|
329
|
+
): ReadonlyMap<string, number> {
|
|
330
|
+
const out = new Map<string, number>();
|
|
331
|
+
for (const [name, table] of Object.entries(tables ?? {})) {
|
|
332
|
+
const n = Object.keys(table.policies ?? {}).length;
|
|
333
|
+
if (n > 0) out.set(name, n);
|
|
301
334
|
}
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function mergePolicyCounts(
|
|
339
|
+
live: ReadonlyMap<string, number>,
|
|
340
|
+
declared: ReadonlyMap<string, number>,
|
|
341
|
+
): Map<string, number> {
|
|
342
|
+
const out = new Map(declared);
|
|
343
|
+
for (const [name, n] of live) {
|
|
344
|
+
out.set(name, Math.max(n, out.get(name) ?? 0));
|
|
345
|
+
}
|
|
346
|
+
return out;
|
|
302
347
|
}
|
|
303
348
|
|
|
304
349
|
function columnDescriptionsFor(
|
|
@@ -411,6 +456,9 @@ export interface StoreQueryInput {
|
|
|
411
456
|
readonly q?: string;
|
|
412
457
|
readonly topK?: number;
|
|
413
458
|
readonly revealPii?: boolean;
|
|
459
|
+
readonly asGate?: string;
|
|
460
|
+
readonly asUserId?: string;
|
|
461
|
+
readonly rls?: RlsIdentity;
|
|
414
462
|
}
|
|
415
463
|
|
|
416
464
|
/** Query result shape. */
|
|
@@ -436,6 +484,11 @@ export interface StoreQueryResult {
|
|
|
436
484
|
readonly facetDistribution?: Record<string, Record<string, number>>;
|
|
437
485
|
readonly masked: boolean;
|
|
438
486
|
readonly routedRole?: "primary" | "replica";
|
|
487
|
+
readonly rls?: {
|
|
488
|
+
readonly gate: string | null;
|
|
489
|
+
readonly userId: string;
|
|
490
|
+
readonly applied: boolean;
|
|
491
|
+
};
|
|
439
492
|
}
|
|
440
493
|
|
|
441
494
|
const KV_TTL_RE = /^(\d+)(ms|s|m|h|d)$/;
|
|
@@ -508,18 +561,27 @@ export async function queryStore(
|
|
|
508
561
|
): Promise<StoreQueryResult> {
|
|
509
562
|
const [facet] = input.ref.split(":") as [StoreFacet, string];
|
|
510
563
|
const effects: Effects = { reads: [input.ref] };
|
|
564
|
+
const catalog = input.child ? sqlCatalogKind(input.child) : null;
|
|
565
|
+
const rls = catalog ? undefined : input.rls;
|
|
511
566
|
const handle = await runtime.openRef(input.ref, {
|
|
512
567
|
effects,
|
|
513
568
|
revealPii: input.revealPii === true,
|
|
569
|
+
...(rls ? { rls } : {}),
|
|
514
570
|
});
|
|
515
571
|
|
|
516
572
|
if (facet === "sql") {
|
|
517
573
|
const sql = handle as SqlStoreHandle;
|
|
518
574
|
const table = input.child;
|
|
575
|
+
const stamped = rlsStamp(sql.driverId, rls);
|
|
519
576
|
if (!table) {
|
|
520
|
-
return {
|
|
577
|
+
return {
|
|
578
|
+
facet,
|
|
579
|
+
rows: [],
|
|
580
|
+
masked: !input.revealPii,
|
|
581
|
+
routedRole: sql.routedRole,
|
|
582
|
+
...(stamped ? { rls: stamped } : {}),
|
|
583
|
+
};
|
|
521
584
|
}
|
|
522
|
-
const catalog = sqlCatalogKind(table);
|
|
523
585
|
if (catalog) {
|
|
524
586
|
const storeName = input.ref.split(":")[1] ?? "";
|
|
525
587
|
const rows = await listSqlCatalog(sql, catalog, manifest, storeName, input.limit ?? 200);
|
|
@@ -540,6 +602,7 @@ export async function queryStore(
|
|
|
540
602
|
rows,
|
|
541
603
|
masked: !input.revealPii,
|
|
542
604
|
routedRole: sql.routedRole,
|
|
605
|
+
...(stamped ? { rls: stamped } : {}),
|
|
543
606
|
};
|
|
544
607
|
} catch {
|
|
545
608
|
// Table may not exist yet — empty browse.
|
|
@@ -548,6 +611,7 @@ export async function queryStore(
|
|
|
548
611
|
rows: [],
|
|
549
612
|
masked: !input.revealPii,
|
|
550
613
|
routedRole: sql.routedRole,
|
|
614
|
+
...(stamped ? { rls: stamped } : {}),
|
|
551
615
|
};
|
|
552
616
|
}
|
|
553
617
|
}
|
|
@@ -765,6 +829,9 @@ export interface StoreEditInput {
|
|
|
765
829
|
readonly confirmation?: string;
|
|
766
830
|
readonly reason?: string;
|
|
767
831
|
readonly confirmed?: boolean;
|
|
832
|
+
readonly asGate?: string;
|
|
833
|
+
readonly asUserId?: string;
|
|
834
|
+
readonly rls?: RlsIdentity;
|
|
768
835
|
}
|
|
769
836
|
|
|
770
837
|
/** Result of a direct store edit (preview or applied). */
|
|
@@ -777,6 +844,11 @@ export interface StoreEditResult {
|
|
|
777
844
|
readonly kind: "send" | "ask";
|
|
778
845
|
readonly resource: string;
|
|
779
846
|
}>;
|
|
847
|
+
readonly rls?: {
|
|
848
|
+
readonly gate: string | null;
|
|
849
|
+
readonly userId: string;
|
|
850
|
+
readonly applied: boolean;
|
|
851
|
+
};
|
|
780
852
|
}
|
|
781
853
|
|
|
782
854
|
/**
|
|
@@ -818,6 +890,15 @@ export async function editStore(
|
|
|
818
890
|
willNotFire,
|
|
819
891
|
applied: false,
|
|
820
892
|
wouldHaveFired,
|
|
893
|
+
...(input.rls
|
|
894
|
+
? {
|
|
895
|
+
rls: {
|
|
896
|
+
gate: input.rls.gate,
|
|
897
|
+
userId: input.rls.userId,
|
|
898
|
+
applied: false,
|
|
899
|
+
},
|
|
900
|
+
}
|
|
901
|
+
: {}),
|
|
821
902
|
};
|
|
822
903
|
} catch (err) {
|
|
823
904
|
await restoreEditTarget(runtime, snapshot);
|
|
@@ -828,7 +909,12 @@ export async function editStore(
|
|
|
828
909
|
}
|
|
829
910
|
}
|
|
830
911
|
|
|
831
|
-
await applyEdit(
|
|
912
|
+
const appliedRls = await applyEdit(
|
|
913
|
+
runtime,
|
|
914
|
+
input,
|
|
915
|
+
{ revealPii: false, persistMeta: true },
|
|
916
|
+
manifest,
|
|
917
|
+
);
|
|
832
918
|
// Direct edit: invalidate cache for written resource (effects-derived).
|
|
833
919
|
runtime.onWriteEffects({ writes: [effectRef] });
|
|
834
920
|
void options.production;
|
|
@@ -838,6 +924,7 @@ export async function editStore(
|
|
|
838
924
|
willNotFire,
|
|
839
925
|
applied: true,
|
|
840
926
|
wouldHaveFired: [],
|
|
927
|
+
...(appliedRls ? { rls: appliedRls } : {}),
|
|
841
928
|
};
|
|
842
929
|
}
|
|
843
930
|
|
|
@@ -1125,11 +1212,14 @@ async function applyEdit(
|
|
|
1125
1212
|
input: StoreEditInput,
|
|
1126
1213
|
ctx: { readonly revealPii: boolean; readonly persistMeta?: boolean },
|
|
1127
1214
|
manifest?: Manifest | null,
|
|
1128
|
-
): Promise<
|
|
1215
|
+
): Promise<StoreEditResult["rls"] | undefined> {
|
|
1129
1216
|
const [facet] = input.ref.split(":") as [StoreFacet, string];
|
|
1217
|
+
const catalog = input.child ? sqlCatalogKind(input.child) : null;
|
|
1218
|
+
const rls = catalog ? undefined : input.rls;
|
|
1130
1219
|
const handle = await runtime.openRef(input.ref, {
|
|
1131
1220
|
effects: { writes: [input.ref] },
|
|
1132
1221
|
revealPii: ctx.revealPii,
|
|
1222
|
+
...(rls ? { rls } : {}),
|
|
1133
1223
|
});
|
|
1134
1224
|
|
|
1135
1225
|
if (facet === "files") {
|
|
@@ -1242,12 +1332,12 @@ async function applyEdit(
|
|
|
1242
1332
|
`INSERT INTO "${table}" (${columns}) VALUES (${placeholders})`,
|
|
1243
1333
|
sets.map((c) => input.patch[c]),
|
|
1244
1334
|
);
|
|
1245
|
-
return;
|
|
1335
|
+
return rlsStamp(sql.driverId, rls);
|
|
1246
1336
|
}
|
|
1247
1337
|
const assignments = sets.map((c) => `"${c}" = ?`).join(", ");
|
|
1248
1338
|
const params = [...sets.map((c) => input.patch[c]), id];
|
|
1249
1339
|
await sql.raw(`UPDATE "${table}" SET ${assignments} WHERE "id" = ?`, params);
|
|
1250
|
-
return;
|
|
1340
|
+
return rlsStamp(sql.driverId, rls);
|
|
1251
1341
|
}
|
|
1252
1342
|
|
|
1253
1343
|
throw new Error(`direct edit not supported for facet ${facet}`);
|
|
@@ -1383,7 +1473,23 @@ export function isKnownConsoleSqlGate(manifest: Manifest | null, name: string):
|
|
|
1383
1473
|
return gate.kind !== "rate" && !name.startsWith("rate:");
|
|
1384
1474
|
}
|
|
1385
1475
|
|
|
1386
|
-
|
|
1476
|
+
function rlsStamp(
|
|
1477
|
+
driverId: string,
|
|
1478
|
+
identity: RlsIdentity | undefined,
|
|
1479
|
+
):
|
|
1480
|
+
| {
|
|
1481
|
+
readonly gate: string | null;
|
|
1482
|
+
readonly userId: string;
|
|
1483
|
+
readonly applied: boolean;
|
|
1484
|
+
}
|
|
1485
|
+
| undefined {
|
|
1486
|
+
if (!identity) return undefined;
|
|
1487
|
+
return {
|
|
1488
|
+
gate: identity.gate,
|
|
1489
|
+
userId: identity.userId,
|
|
1490
|
+
applied: RLS_CONTEXT_DRIVERS.has(driverId),
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1387
1493
|
|
|
1388
1494
|
/** DML / DDL heads — keep in sync with `isSqlWrite` in the query console. */
|
|
1389
1495
|
const STORE_SQL_WRITE_HEADS = new Set([
|
|
@@ -1434,72 +1540,50 @@ export async function runStoreSql(
|
|
|
1434
1540
|
readonly allowWrite: boolean;
|
|
1435
1541
|
readonly revealPii?: boolean;
|
|
1436
1542
|
readonly tenant?: string;
|
|
1437
|
-
/** View rows as this Gate (`oke.gate`
|
|
1543
|
+
/** View rows as this Gate (`oke.gate` on postgres / pglite). */
|
|
1438
1544
|
readonly asGate?: string;
|
|
1545
|
+
readonly asUserId?: string;
|
|
1546
|
+
readonly rls?: RlsIdentity;
|
|
1439
1547
|
},
|
|
1440
1548
|
): Promise<{
|
|
1441
1549
|
readonly rows: readonly Record<string, unknown>[];
|
|
1442
1550
|
readonly masked: boolean;
|
|
1443
1551
|
readonly routedRole: "primary" | "replica";
|
|
1444
1552
|
readonly asGate: string | null;
|
|
1553
|
+
readonly asUserId: string | null;
|
|
1445
1554
|
readonly gateApplied: boolean;
|
|
1555
|
+
readonly rls: {
|
|
1556
|
+
readonly gate: string | null;
|
|
1557
|
+
readonly userId: string;
|
|
1558
|
+
readonly applied: boolean;
|
|
1559
|
+
};
|
|
1446
1560
|
}> {
|
|
1447
1561
|
const trimmed = sqlText.trim().replace(/;+\s*$/, "");
|
|
1448
1562
|
const isWrite = isStoreSqlWrite(trimmed);
|
|
1449
1563
|
if (isWrite && !options.allowWrite) {
|
|
1450
1564
|
throw new Error("SQL console is read-only by default — write requires console:store.sql:write");
|
|
1451
1565
|
}
|
|
1566
|
+
const rls = options.rls;
|
|
1452
1567
|
const handle = (await runtime.openRef(ref, {
|
|
1453
1568
|
effects: isWrite ? { writes: [ref] } : { reads: [ref] },
|
|
1454
1569
|
revealPii: options.revealPii === true,
|
|
1570
|
+
...(rls ? { rls } : {}),
|
|
1455
1571
|
})) as SqlStoreHandle;
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
try {
|
|
1472
|
-
await handle.raw("ROLLBACK");
|
|
1473
|
-
} catch {
|
|
1474
|
-
// Connection may already be idle after a failed SET.
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
throw err;
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
/**
|
|
1482
|
-
* Open a transaction and set `oke.gate` + `row_security` for RLS simulation.
|
|
1483
|
-
* No-op on memory SQL.
|
|
1484
|
-
*
|
|
1485
|
-
* @param handle - Open SQL handle
|
|
1486
|
-
* @param gate - Validated Gate name
|
|
1487
|
-
*/
|
|
1488
|
-
async function applySqlGateContext(handle: SqlStoreHandle, gate: string): Promise<boolean> {
|
|
1489
|
-
if (!GATE_CONTEXT_DRIVERS.has(handle.driverId)) return false;
|
|
1490
|
-
try {
|
|
1491
|
-
await handle.raw("BEGIN");
|
|
1492
|
-
await handle.raw("SET LOCAL row_security = on");
|
|
1493
|
-
await handle.raw("SELECT set_config('oke.gate', ?, true)", [gate]);
|
|
1494
|
-
return true;
|
|
1495
|
-
} catch {
|
|
1496
|
-
try {
|
|
1497
|
-
await handle.raw("ROLLBACK");
|
|
1498
|
-
} catch {
|
|
1499
|
-
// ignore
|
|
1500
|
-
}
|
|
1501
|
-
return false;
|
|
1502
|
-
}
|
|
1572
|
+
const stamped = rlsStamp(handle.driverId, rls) ?? {
|
|
1573
|
+
gate: rls?.gate ?? options.asGate?.trim() ?? null,
|
|
1574
|
+
userId: rls?.userId ?? options.asUserId?.trim() ?? "",
|
|
1575
|
+
applied: false,
|
|
1576
|
+
};
|
|
1577
|
+
const rows = await handle.raw(trimmed);
|
|
1578
|
+
return {
|
|
1579
|
+
rows,
|
|
1580
|
+
masked: !options.revealPii,
|
|
1581
|
+
routedRole: handle.routedRole,
|
|
1582
|
+
asGate: stamped.gate,
|
|
1583
|
+
asUserId: stamped.userId.length > 0 ? stamped.userId : options.asUserId?.trim() || null,
|
|
1584
|
+
gateApplied: stamped.applied,
|
|
1585
|
+
rls: stamped,
|
|
1586
|
+
};
|
|
1503
1587
|
}
|
|
1504
1588
|
|
|
1505
1589
|
/**
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{$n as t,Bi as n,Br as r,Cr as i,E as a,F as o,Fo as s,L as c,No as l,P as u,Qn as d,Sa as f,Sr as p,Ta as m,Ui as h,Yr as g,_a as _,_r as v,ar as y,ba as b,br as x,c as S,ca as C,cr as w,dr as T,er as E,hr as D,i as O,ir as k,l as A,mr as j,nr as M,o as N,or as P,s as F,sr as I,u as ee,ur as L,va as R,vr as z,wr as B,x as V,xa as te,xi as H,xr as U,ya as W,yr as ne}from"./shortcut-keys-DKxNTe_m.js";import{t as G}from"./useMutation-B8EO02Ej.js";import{r as re}from"./react-dom-Bph1y7z7.js";import{A as ie}from"./xyflow-D7n4g6go.js";import{n as ae}from"./index-CGoZkILK.js";import{a as oe,c as se,i as ce,n as le,o as ue,r as de,s as fe,t as pe}from"./element-icons-BVXtRyd3.js";import{t as me}from"./explorer-empty-2uIhBu0_.js";import{a as K,c as he,l as q,n as J,o as ge,r as _e,s as ve,t as ye}from"./detail-header-DhHM1iaZ.js";import{n as be,t as xe}from"./copy-inline-button-DBHhgHKP.js";import{i as Se,t as Ce}from"./http-method-_UHM2ODJ.js";var Y=e(re(),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=ie(),Ue=`GET /secure
|
|
2
|
+
Authorization: Bearer <secret>`,We=`{ "ok": true }`;function Ge({keyRow:e,blast:t,now:n,leading:r,onEdit:a,onRefresh:o,onRevoke:s,onRotate:c,rotatePending:l}){let u=e.revokedAt!==null,d=e.expiresAt!==null&&e.expiresAt<=n,f=t?.lastUsedAt??e.lastUsedAt,p=t?.callVolume??0,m=Te(e.expiresAt,n),h=we(f,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)(ye,{dataSlot:`access-detail-header`,leading:r,icon:(0,Q.jsx)(B,{icon:H,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)(B,{icon:e.plane===`operator`?g:C,className:`size-3`,"aria-hidden":!0}),e.plane]}),u?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-destructive`,children:`revoked`}):null,d&&!u?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-amber-700 dark:text-amber-400`,children:`expired`}):null,e.unused90d&&!u&&!d?(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)(xe,{value:e.id,label:`Copy ${e.id}`})]}),actions:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(fe,{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:u,onClick:a,children:`Edit`})}),(0,Q.jsx)(fe,{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:u,onClick:o,children:`Refresh`})}),(0,Q.jsx)(fe,{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:l||u,onClick:c,children:`Rotate`})}),(0,Q.jsx)(fe,{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:u,onClick:s,children:`Revoke`})})]})}),(0,Q.jsx)(Ke,{revoked:u,expired:d,unused:e.unused90d,residual:t?.residualAccessNote}),(0,Q.jsxs)(`div`,{className:D,"data-slot":`access-briefing`,role:`group`,"aria-label":`Usage`,children:[(0,Q.jsx)(Xe,{label:`calls`,value:String(p)}),(0,Q.jsx)(Xe,{label:`used`,value:h,hint:f?new Date(f).toISOString():`No recorded call`,warn:f==null&&!u}),(0,Q.jsx)(Xe,{label:`expires`,value:m,hint:e.expiresAt?new Date(e.expiresAt).toISOString():`No expiry`,warn:d&&!u}),(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)(xe,{value:e.scopes.join(`
|
|
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:T,"aria-label":e.group,children:[(0,Q.jsx)(`span`,{className:i(U,`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
|
+
`),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:m,hint:e.expiresAt?new Date(e.expiresAt).toISOString():void 0,warn:d&&!u}),(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:f,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:i(D,`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:i(D,`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:i(D,`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:T,children:[(0,Q.jsx)(`span`,{className:i(U,`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:T,children:[(0,Q.jsx)(`span`,{className:i(U,`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)(xe,{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:i(`w-1 shrink-0 self-stretch`,Se(`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:i(`shrink-0 font-mono text-[10px] font-semibold tracking-[0.08em] uppercase`,p(Ce(`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)(xe,{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:i(D,`items-center gap-2 px-2`),children:[(0,Q.jsx)(`h3`,{className:i(U,`shrink-0 leading-none`),children:e}),t?(0,Q.jsx)(`span`,{className:i(M,!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:i(v,z,`gap-1.5`),children:[(0,Q.jsx)(`span`,{className:`tracking-[0.08em] uppercase`,children:e}),(0,Q.jsx)(`span`,{className:i(`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)(xe,{value:e,label:t})})}function Qe({label:e,value:t,hint:n,copy:r,copyLabel:a,warn:o=!1}){return(0,Q.jsxs)(`div`,{className:T,children:[(0,Q.jsx)(`dt`,{className:i(U,`w-[5.5rem] shrink-0`),children:e}),(0,Q.jsx)(`dd`,{className:i(`min-w-0 flex-1 truncate font-mono text-[11px]`,o&&`text-amber-800 dark:text-amber-400`),title:n,children:t}),r?(0,Q.jsx)(Ze,{value:r,label:a??`Copy ${e}`}):null]})}var $e=[`console.access.list`];function et(){return l({queryKey:$e,queryFn:async()=>{let e=await W();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:I,children:`Loading…`}):null,!r&&e.length===0&&n.trim().length>0?(0,Q.jsx)(`p`,{className:I,children:`No keys match.`}):null,(0,Q.jsx)(nt,{label:`User`,icon:C,rows:a,selectedKey:t,onSelect:i}),(0,Q.jsx)(nt,{label:`Operator`,icon:g,rows:o,selectedKey:t,onSelect:i})]})}function nt(e){return(0,Q.jsxs)(`section`,{className:d,"aria-label":e.label,children:[(0,Q.jsxs)(`div`,{className:t,children:[(0,Q.jsx)(B,{icon:e.icon,className:i(P,`text-muted-foreground`),"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:i(E,`flex-1`),children:e.label}),(0,Q.jsx)(`span`,{className:M,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:i(T,n&&`bg-muted/70 text-foreground`),onClick:()=>e.onSelect(t.id),children:[(0,Q.jsx)(`span`,{className:i(L,n&&`bg-sky-500`)}),(0,Q.jsx)(B,{icon:H,className:i(P,`text-muted-foreground`)}),(0,Q.jsx)(`span`,{className:i(`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:i(`ml-auto tabular-nums text-[10px] text-muted-foreground`),children:t.scopes.length})]},t.id)})]})}function rt({entries:e,rateMax:t,rateCount:s,rateUnit:l,onEntries:d,onRateMax:f,onRateCount:p,onRateUnit:m}){let[h,g]=(0,Y.useState)(``),_=Re(h),b=h.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));d(n),g(``)};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:h,onChange:e=>g(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":b,flat:!0,className:i(J,`min-w-0 flex-1 font-mono`)}),e.length>0?(0,Q.jsx)(`span`,{className:i(M,`flex h-8 items-center pr-2`),children:e.length}):null,(0,Q.jsx)(`button`,{type:`button`,className:i(y,`h-8`),"aria-label":`Add allow entry`,disabled:_.length===0,onClick:x,children:(0,Q.jsx)(B,{icon:n,className:`size-3.5`,"aria-hidden":!0})})]}),b?(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:i(T,`last:border-b-0`),children:[(0,Q.jsx)(`span`,{className:i(U,`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:i(`min-w-0 truncate font-mono text-[11px]`,n==null&&`text-destructive`),children:t}),(0,Q.jsx)(`button`,{type:`button`,className:i(k,`ml-auto opacity-0 group-hover:opacity-100 focus-visible:opacity-100`),"aria-label":`Remove ${t}`,onClick:()=>d(e.filter(e=>e!==t)),children:(0,Q.jsx)(B,{icon:r,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=>f(e.target.value),placeholder:`none`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate max`,flat:!0,className:i(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:s,onChange:e=>p(e.target.value),placeholder:`1`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate window`,flat:!0,className:i(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)(u,{children:[(0,Q.jsx)(c,{render:t=>(0,Q.jsx)(`button`,{...t,type:`button`,"aria-pressed":l===e,"aria-label":Pe[e],onClick:n=>{t.onClick?.(n),m(e)},className:i(v,`h-8 px-1.5 font-mono font-semibold`,l===e?ne:z),children:e})}),(0,Q.jsx)(o,{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)(ge,{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)(he,{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:i(J,`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)(B,{icon:h,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:i(_e)})]})}function ot({scopes:e,selected:t,empty:n,onChange:r}){let[a,o]=(0,Y.useState)(``),s=new Set(t),c=(0,Y.useMemo)(()=>st(e,a),[e,a]),l=(0,Y.useMemo)(()=>He(c),[c]),u=e.join(`\0`);return(0,Y.useEffect)(()=>{o(``)},[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:a,placeholder:`Search scopes`,label:`Search scopes`,onChange:o}):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:i(`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:o,error:s,onOpenChange:c,onSubmit:l}){let[u,d]=(0,Y.useState)(`user`),[f,p]=(0,Y.useState)(``),[m,h]=(0,Y.useState)(``),[_,v]=(0,Y.useState)([]),[y,b]=(0,Y.useState)(`never`),[x,w]=(0,Y.useState)(``),[E,D]=(0,Y.useState)([]),[k,j]=(0,Y.useState)(``),[M,P]=(0,Y.useState)(``),[I,R]=(0,Y.useState)(`m`),[z,B]=(0,Y.useState)(``),V=t.find(e=>e.id===m)??null,te=(0,Y.useMemo)(()=>{let e=z.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 V&&!n.some(e=>e.id===V.id)?[V,...n]:n},[t,z,V]),H=(0,Y.useMemo)(()=>u===`operator`?r:V==null?[]:V.scopes.length>0?V.scopes:n,[u,n,r,V]);(0,Y.useEffect)(()=>{e||(d(`user`),p(``),h(``),v([]),b(`never`),w(``),D([]),j(``),P(``),R(`m`),B(``))},[e]),(0,Y.useEffect)(()=>{v(e=>{let t=e.filter(e=>H.includes(e));if(t.length>0)return t;let n=H[0];return n?[n]:[]})},[H]);let U=De(x),W=Fe(k,M,I),ne=f.trim().length>0&&_.length>0&&!o&&W!==void 0&&(u===`operator`||m.length>0)&&(y!==`custom`||U>0);return(0,Q.jsx)(O,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:c,children:(0,Q.jsxs)(N,{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)(A,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(ee,{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:i(J,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(ge,{label:`Plane`,children:[(0,Q.jsx)(K,{active:u===`user`,icon:C,onClick:()=>{d(`user`)},children:`user`}),(0,Q.jsx)(K,{active:u===`operator`,icon:g,onClick:()=>{d(`operator`),h(``),B(``)},children:`operator`})]}),(0,Q.jsx)(it,{expires:y,customExpires:x,onExpires:b,onCustomExpires:w}),(0,Q.jsx)(rt,{entries:E,rateMax:k,rateCount:M,rateUnit:I,onEntries:D,onRateMax:j,onRateCount:P,onRateUnit:R}),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:z,placeholder:`Search users`,label:`Search users`,onChange:B}):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===m;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-creator-user`,"aria-pressed":t,className:i(T,t&&`bg-muted/70 text-foreground`),onClick:()=>h(e.id),children:[(0,Q.jsx)(`span`,{className:i(L,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:H,selected:_,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:v}),s?(0,Q.jsx)(ve,{slot:`access-create-error`,children:s}):null]}),(0,Q.jsxs)(S,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>c(!1),disabled:o,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!ne,"data-slot":`access-create-submit`,onClick:()=>{if(W===void 0)return;let e=Oe(y,Date.now(),x);l({plane:u,name:f.trim(),scopes:_,...u===`user`?{creatorUserId:m}:{},...e==null?{}:{expiresAt:e},...W==null?{}:{rateLimit:W},...E.length>0?{ipAllowlist:E}:{}})},children:o?`Creating…`:`Create`})]})]})})}function lt({open:e,keyRow:t,users:n,operators:r,operatorScopes:o,pending:s,error:c,now:l=Date.now(),onOpenChange:u,onSubmit:d}){let f=ke(t.expiresAt,l),[p,m]=(0,Y.useState)(t.name),[h,_]=(0,Y.useState)(t.scopes),[v,y]=(0,Y.useState)(f.choice),[b,x]=(0,Y.useState)(f.custom),[w,T]=(0,Y.useState)(t.ipAllowlist),E=Ie(t.rateLimit?.per??``),[D,k]=(0,Y.useState)(t.rateLimit?String(t.rateLimit.max):``),[j,M]=(0,Y.useState)(E.count),[P,I]=(0,Y.useState)(E.unit);(0,Y.useEffect)(()=>{if(!e)return;let n=ke(t.expiresAt,l);m(t.name),_(t.scopes),y(n.choice),x(n.custom),T(t.ipAllowlist);let r=Ie(t.rateLimit?.per??``);k(t.rateLimit?String(t.rateLimit.max):``),M(r.count),I(r.unit)},[e,t.id]);let L=t.creatorId??``,R=(0,Y.useMemo)(()=>L.length===0?null:n.find(e=>e.id===L)??r.find(e=>e.id===L)??null,[L,r,n]),z=R?R.name.trim()||R.email.trim()||R.id:L||`Unknown`,V=(0,Y.useMemo)(()=>{let e=t.creatorScopes??[];return e.length>0?e:t.plane===`operator`?R&&R.scopes.length>0?R.scopes:o:R?.scopes??[]},[R,t.creatorScopes,t.plane,o]),te=Array.from(new Set([...V,...t.scopes])),H=De(b),U=Fe(D,j,P),W=p.trim().length>0&&h.length>0&&!s&&U!==void 0&&(v!==`custom`||H>0);return(0,Q.jsx)(O,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:u,children:(0,Q.jsxs)(N,{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)(A,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(ee,{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:p,onChange:e=>m(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:i(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 ${z}`,children:[(0,Q.jsx)(B,{icon:t.plane===`operator`?g:C,className:`size-3 shrink-0`,"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:`min-w-0 truncate text-foreground`,children:z})]}),L.length>0&&L!==z?(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pr-4 font-mono text-[10px] text-muted-foreground`,children:L}):null]}),(0,Q.jsx)(it,{expires:v,customExpires:b,onExpires:y,onCustomExpires:x}),(0,Q.jsx)(rt,{entries:w,rateMax:D,rateCount:j,rateUnit:P,onEntries:T,onRateMax:k,onRateCount:M,onRateUnit:I}),(0,Q.jsx)(ot,{scopes:te,selected:h,empty:t.plane===`user`&&R==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?(0,Q.jsx)(ve,{slot:`access-edit-error`,children:c}):null]}),(0,Q.jsxs)(S,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>u(!1),disabled:s,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!W,"data-slot":`access-edit-submit`,onClick:()=>{U!==void 0&&d({name:p.trim(),scopes:h,expiresAt:Oe(v,Date.now(),b),rateLimit:U,ipAllowlist:w})},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),[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)(O,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:a,children:(0,Q.jsxs)(N,{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)(A,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(ee,{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)(ve,{slot:`access-refresh-error`,children:r}):null]}),(0,Q.jsxs)(S,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>a(!1),disabled:n,children:`Cancel`}),(0,Q.jsx)(q,{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)(O,{open:e!==null,onOpenChange:e=>{e||n()},children:(0,Q.jsxs)(N,{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)(A,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(ee,{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)(S,{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=s(),{query:r,selectedKey:i,action:a,setQuery:o,setSelectedKey:c,setAction:u}=ae(),d=le(),[p]=(0,Y.useState)(()=>Date.now()),[h,g]=(0,Y.useState)(null),[v,S]=(0,Y.useState)(`New API key`),C=(0,Y.useMemo)(()=>{let t=e.data?.userPlane.keys??[],n=e.data?.operatorPlane.keys??[];return[...t,...n]},[e.data]),T=(0,Y.useMemo)(()=>{let e=r.trim().toLowerCase();return e?C.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.scopes.some(t=>t.toLowerCase().includes(e))):C},[C,r]),E=T.find(e=>e.id===i)??C.find(e=>e.id===i),D=l({queryKey:[`console.access.blast`,E?.id],queryFn:async()=>{if(!E)throw Error(`no key`);let e=await R(E.id);if(e.error||!e.data)throw Error(e.error?.message??`blast failed`);return e.data},enabled:E!==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 O=()=>{t.invalidateQueries({queryKey:$e})},k=G({mutationFn:_,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?m(e.error):`create failed`);S(`New API key`),g(e.data.secret),c(e.data.key.id),u(null),O()}}),A=G({mutationFn:e=>te({keyId:E.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?m(e.error):`rotate failed`);S(`Rotated API key`),g(e.data.secret),u(null),O()}}),M=G({mutationFn:e=>b({keyId:E.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?m(e.error):`revoke failed`);u(null),O()}}),N=G({mutationFn:f,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?m(e.error):`update failed`);u(null),O()}}),P=G({mutationFn:e=>f({keyId:E.id,expiresAt:e}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?m(e.error):`refresh failed`);u(null),O()}}),F=(0,Q.jsx)(ue,{open:d.open,onToggle:d.toggle,noun:`keys`,controlsId:`access-list`,dataSlot:`access-list-toggle`});return(0,Q.jsxs)(`div`,{className:w,"data-slot":`access-page`,children:[(0,Q.jsxs)(oe,{orientation:`horizontal`,className:`min-h-0 flex-1`,children:[(0,Q.jsx)(ce,{panelRef:d.panelRef,collapsible:!0,collapsedSize:0,defaultSize:j.start.defaultSize,minSize:j.start.minSize,onResize:d.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)(se,{"data-slot":`access-search-input`,placeholder:`Search keys`,"aria-label":`Search keys`,value:r,onChange:e=>o(e.target.value)}),(0,Q.jsx)(fe,{label:`Create key`,className:`flex self-stretch`,children:(0,Q.jsx)(`button`,{type:`button`,className:y,"data-slot":`access-create`,"aria-label":`Create key`,onClick:()=>u(`create`),children:(0,Q.jsx)(B,{icon:n,className:`size-3.5`})})})]}),(0,Q.jsx)(tt,{rows:T,selectedKey:i,query:r,loading:e.isLoading,onSelect:c})]})}),d.open?(0,Q.jsx)(de,{withHandle:!0}):null,(0,Q.jsx)(ce,{defaultSize:j.end.defaultSize,minSize:j.end.minSize,className:`min-h-0 overflow-hidden`,children:E?(0,Q.jsx)(Ge,{keyRow:E,blast:D.data,now:p,leading:F,onEdit:()=>u(`edit`),onRefresh:()=>u(`refresh`),onRevoke:()=>u(`revoke`),onRotate:()=>u(`rotate`),rotatePending:A.isPending}):(0,Q.jsx)(me,{icon:pe.gate.icon,iconClassName:`border-border/70`,title:e.isLoading?`Loading keys…`:C.length>0?`Select a key`:`No keys yet`,leading:F,description:e.isLoading?`Reading planes and grantable scopes.`:C.length>0&&r.trim().length>0?(0,Q.jsxs)(Q.Fragment,{children:[`Nothing matches `,(0,Q.jsx)(`span`,{className:`font-mono`,children:r}),`.`,` `,(0,Q.jsx)(`button`,{type:`button`,className:`underline underline-offset-2 hover:text-foreground`,onClick:()=>o(``),children:`Clear search`})]}):`Create one to mint a secret once.`})})]}),(0,Q.jsx)(ct,{open:a===`create`,users:e.data?.userPlane.users??[],userScopes:e.data?.userPlane.grantableScopes??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:k.isPending,error:k.error instanceof Error?k.error.message:null,onOpenChange:e=>{e||u(null)},onSubmit:e=>k.mutate(e)}),E?(0,Q.jsx)(lt,{open:a===`edit`,keyRow:E,users:e.data?.userPlane.users??[],operators:e.data?.operatorPlane.operators??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:N.isPending,error:N.error instanceof Error?N.error.message:null,onOpenChange:e=>{e||u(null)},onSubmit:e=>N.mutate({keyId:E.id,...e})}):null,E?(0,Q.jsx)(ut,{open:a===`refresh`,keyRow:E,pending:P.isPending,error:P.error instanceof Error?P.error.message:null,onOpenChange:e=>{e||u(null)},onSubmit:e=>P.mutate(e)}):null,E?(0,Q.jsx)(be,{open:a===`revoke`,onOpenChange:e=>{e||u(null)},phrase:`REVOKE`,title:`Revoke ${E.name}`,description:`Irreversible. Residual sessions may continue for the access TTL.`,pending:M.isPending,error:M.error instanceof Error?M.error.message:null,confirmLabel:`Revoke key`,slot:`access-revoke-sheet`,onConfirm:e=>M.mutate({confirmation:`REVOKE`,reason:e.reason})}):null,E?(0,Q.jsx)(be,{open:a===`rotate`,onOpenChange:e=>{e||u(null)},phrase:`ROTATE`,title:`Rotate ${E.name}`,description:`Mints a new secret once. The current secret stops after residual TTL.`,pending:A.isPending,error:A.error instanceof Error?A.error.message:null,confirmLabel:`Rotate key`,confirmVariant:`ghost`,slot:`access-rotate-sheet`,onConfirm:e=>A.mutate({confirmation:`ROTATE`,reason:e.reason})}):null,(0,Q.jsx)(dt,{secret:h,title:v,onDismiss:()=>g(null)})]})}export{ft as AccessPage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Cr as e}from"./shortcut-keys-DKxNTe_m.js";import{A as t}from"./xyflow-D7n4g6go.js";import{V as n,n as r,t as i}from"./react-D8E3mtu1.js";var a=t();function o({open:t,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=r()??!1;return(0,a.jsx)(i.div,{...u,"aria-hidden":!t,inert:!t||void 0,initial:!1,animate:d?{opacity:+!!t}:{opacity:+!!t,clipPath:t?`inset(0 0 0% 0)`:`inset(0 0 100% 0)`,y:t?0:-4},transition:l??{duration:d?0:t?.22:.14,ease:n},className:e(`overflow-hidden`,s),style:{...c,height:t?o:0,pointerEvents:t?void 0:`none`,transformOrigin:`top`}})}export{o as t};
|