okengine 0.18.3 → 0.18.4
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/AGENTS.md +5 -3
- package/README.md +5 -5
- package/package.json +2 -2
- package/site/content/docs/ai/index.mdx +1 -1
- package/site/content/docs/ai/mcp.mdx +10 -1
- package/site/content/docs/ai/skills.mdx +9 -9
- package/site/content/docs/elements/ai.mdx +1 -1
- package/site/content/docs/elements/gate.mdx +3 -2
- package/site/content/docs/elements/store.mdx +2 -0
- package/site/content/docs/get-started/basic-usage.mdx +3 -10
- package/site/content/docs/get-started/index.mdx +1 -1
- package/site/content/docs/get-started/installation.mdx +1 -1
- package/site/content/docs/get-started/introduction.mdx +6 -13
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +4 -11
- package/site/content/docs/get-started/why.mdx +12 -14
- package/site/content/docs/index.mdx +45 -12
- package/site/content/docs/meta.json +8 -5
- package/site/content/docs/providers/index.mdx +2 -0
- package/site/content/docs/recipes/index.mdx +2 -0
- package/src/console/ui-next/dist/assets/{access-page-DY4N6nnk.js → access-page-De7Lc2JC.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-CsPDMrVM.js → flows-page-RGy7VEA_.js} +1 -1
- package/src/console/ui-next/dist/assets/{index-CcTDXHuz.js → index-_rgpdVzo.js} +3 -3
- package/src/console/ui-next/dist/assets/{observability-page-CKR595wP.js → observability-page-Ds6pcnh-.js} +1 -1
- package/src/console/ui-next/dist/assets/{units-page-CpPFFKyE.js → units-page-4rHOePuE.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-BsMf-9_W.js → vault-page-DISPgxLM.js} +1 -1
- package/src/console/ui-next/dist/index.html +1 -1
- package/src/index.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# OKE — Agent Contract
|
|
2
2
|
|
|
3
|
+
One law. Eight elements. One contract. The backend model stays small; operational surfaces are derived from it instead of maintained separately.
|
|
4
|
+
|
|
3
5
|
This file is loaded by every later session. It prevents drift. Product documentation lives in `site/content/docs/`. **If the documentation is silent, stop and ask.**
|
|
4
6
|
|
|
5
7
|
## The one law
|
|
@@ -27,13 +29,13 @@ There are no separate species called endpoints, handlers, consumers, jobs, subsc
|
|
|
27
29
|
|
|
28
30
|
An element earns its place only if it has irreducible physics. New infrastructure becomes a new **driver** for an existing element — never a ninth element.
|
|
29
31
|
|
|
30
|
-
##
|
|
32
|
+
## Core programming vocabulary
|
|
31
33
|
|
|
32
34
|
```typescript
|
|
33
35
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
34
36
|
```
|
|
35
37
|
|
|
36
|
-
That is the
|
|
38
|
+
That is the core programming vocabulary. Everything else is derived.
|
|
37
39
|
|
|
38
40
|
## Governing rule
|
|
39
41
|
|
|
@@ -80,7 +82,7 @@ Published packages:
|
|
|
80
82
|
- `okengine` — framework. Subpath exports: `.`, `./client`, `./test`, `./config`, `./auth`, `./plugins`, `./drivers/*`. `"sideEffects": false`. CLI binary: `oke`.
|
|
81
83
|
- `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke` and ships Notes starters from `packages/create-oke/templates/{standard,advanced}`.
|
|
82
84
|
|
|
83
|
-
Engine: Bun `>=1.
|
|
85
|
+
Engine: Bun `>=1.4.0`.
|
|
84
86
|
|
|
85
87
|
## Documentation authority
|
|
86
88
|
|
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
<picture>
|
|
3
3
|
<source
|
|
4
4
|
media="(prefers-color-scheme: dark)"
|
|
5
|
-
srcset="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+
|
|
5
|
+
srcset="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+One+contract.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-W.svg&theme=zinc&size=banner&mode=dark&font=geist&align=left"
|
|
6
6
|
/>
|
|
7
7
|
<img
|
|
8
|
-
alt="okengine — One law. Eight elements.
|
|
9
|
-
src="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+
|
|
8
|
+
alt="okengine — One law. Eight elements. One contract."
|
|
9
|
+
src="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+One+contract.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-B.svg&theme=zinc&size=banner&mode=light&font=geist&align=left"
|
|
10
10
|
width="750"
|
|
11
11
|
/>
|
|
12
12
|
</picture>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
24
|
<p align="center">
|
|
25
|
-
<em>
|
|
25
|
+
<em>Modern backends became a pile of disconnected systems. One law collapses them — client, Console, and infra derived from one contract. Yours to host.</em>
|
|
26
26
|
</p>
|
|
27
27
|
|
|
28
28
|
<p align="center">
|
|
@@ -104,7 +104,7 @@ Every backend behavior is a Flow: `on(Trigger) → Effects`. Endpoints, jobs, co
|
|
|
104
104
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
That is the
|
|
107
|
+
That is the core programming vocabulary. World access goes through `fx` — effects are inferred, so the Manifest can derive the typed client, Console, and MCP without a second source of truth.
|
|
108
108
|
|
|
109
109
|
| Element | Essence |
|
|
110
110
|
| ----------- | ----------------------------- |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okengine",
|
|
3
|
-
"version": "0.18.
|
|
4
|
-
"description": "One law. Eight elements.
|
|
3
|
+
"version": "0.18.4",
|
|
4
|
+
"description": "One law. Eight elements. One contract. The backend model stays small; operational surfaces are derived from it instead of maintained separately.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -5,7 +5,7 @@ icon: "Bot"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Resources for AI agents that read or operate an OKE app — not the `ai` element itself.
|
|
8
|
+
Resources for AI agents that read or operate an OKE app — not the `ai` element itself. In OKE, **MCP is a surface of the model, not the model itself**. Gate governs agents calling in; the AI element governs apps calling out.
|
|
9
9
|
|
|
10
10
|
## Pages
|
|
11
11
|
|
|
@@ -5,7 +5,16 @@ icon: "Plug"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Two directions, same
|
|
8
|
+
## Two directions, same execution model
|
|
9
|
+
|
|
10
|
+
> **MCP is a surface of the model, not the model itself.**
|
|
11
|
+
|
|
12
|
+
Two genuinely distinct things share the protocol in OKE:
|
|
13
|
+
|
|
14
|
+
1. **Gate Element (MCP Provider role):** Your app exposes Flows as MCP tools on port **6535** (or via `mcp.tool()` + OAuth 2.1 Authorization Server) so external AI agents (Claude, ChatGPT) can read the Manifest and call declared Flows.
|
|
15
|
+
2. **AI Element (MCP Client role):** Your app consumes _external_ MCP tool servers via `ai.mcpServer(...)`, routing tool calls through `fx.call` inside prompts and agents.
|
|
16
|
+
|
|
17
|
+
OKE does not create separate security models for users, operators, and agents. They enter the same execution model through different triggers and planes.
|
|
9
18
|
|
|
10
19
|
The server on **6535** speaks JSON-RPC over HTTP (MCP protocol `2024-11-05`), requires a Bearer token **even on localhost**, and never forwards that token upstream — adapters receive structured operator ids instead.
|
|
11
20
|
|
|
@@ -9,18 +9,18 @@ Tools alone don't make a good operator — an agent also needs to know the _voca
|
|
|
9
9
|
|
|
10
10
|
## The layers
|
|
11
11
|
|
|
12
|
-
| Layer | Path | Loaded when | Teaches
|
|
13
|
-
| ------------------ | ---------------------------- | ------------------------------------------------- |
|
|
14
|
-
| **Agent contract** | `AGENTS.md` (repo root) | Every agent session, automatically | The one law, eight elements,
|
|
15
|
-
| **Element skill** | `.agents/skills/oke/` | Building or changing an okengine app | The element contract in depth — declaration patterns per element
|
|
16
|
-
| **Docs skill** | `.agents/skills/oke-docs/` | Writing or editing docs under `site/content/docs` | The documentation information-architecture standard and its gates
|
|
17
|
-
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under `## Unreleased` + docs sync via `oke-docs`
|
|
18
|
-
| **Deps skill** | `.agents/skills/oke-deps/` | Updating `package.json` dependencies | Scoped bumps, Bun install, pins (Drizzle RC, fumadocs alias, …)
|
|
19
|
-
| **Images skill** | `.agents/skills/oke-images/` | Updating Compose image pins | Registry probe, pin style, catalog + recipes + Keel + docs lockstep
|
|
12
|
+
| Layer | Path | Loaded when | Teaches |
|
|
13
|
+
| ------------------ | ---------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
14
|
+
| **Agent contract** | `AGENTS.md` (repo root) | Every agent session, automatically | The one law, eight elements, one contract, the fx rule, ports, budgets |
|
|
15
|
+
| **Element skill** | `.agents/skills/oke/` | Building or changing an okengine app | The element contract in depth — declaration patterns per element |
|
|
16
|
+
| **Docs skill** | `.agents/skills/oke-docs/` | Writing or editing docs under `site/content/docs` | The documentation information-architecture standard and its gates |
|
|
17
|
+
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under `## Unreleased` + docs sync via `oke-docs` |
|
|
18
|
+
| **Deps skill** | `.agents/skills/oke-deps/` | Updating `package.json` dependencies | Scoped bumps, Bun install, pins (Drizzle RC, fumadocs alias, …) |
|
|
19
|
+
| **Images skill** | `.agents/skills/oke-images/` | Updating Compose image pins | Registry probe, pin style, catalog + recipes + Keel + docs lockstep |
|
|
20
20
|
|
|
21
21
|
## AGENTS.md — the root contract
|
|
22
22
|
|
|
23
|
-
Every OKE app's repo carries an `AGENTS.md` that agents (Cursor, Claude Code, and peers) read at session start. It is deliberately short and absolute: every backend behavior is a Flow (`on(Trigger) → Effects`), there are eight elements
|
|
23
|
+
Every OKE app's repo carries an `AGENTS.md` that agents (Cursor, Claude Code, and peers) read at session start. It is deliberately short and absolute: every backend behavior is a Flow (`on(Trigger) → Effects`), there are eight elements bound to one contract, all world access goes through `fx`, drivers are named after protocols, and the ports/budgets are fixed. Its closing rule is the one that keeps agents honest: **if the documentation is silent, stop and ask** — never invent the API.
|
|
24
24
|
|
|
25
25
|
## Skills — installable know-how
|
|
26
26
|
|
|
@@ -129,7 +129,7 @@ const result = await fx.ask(triage, input, {
|
|
|
129
129
|
});
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
External MCP tools take the same path. Declare a server with a **required** allowlist, then pass `server.tool("…")` — never whatever `tools/list` happens to expose:
|
|
132
|
+
External MCP tools take the same path. In OKE, **MCP is a surface of the model, not the model itself** — `ai.mcpServer` models the client role (consuming external tools), while `mcp.tool()` + Gate models the provider role (exposing your flows to agents). Declare a server with a **required** allowlist, then pass `server.tool("…")` — never whatever `tools/list` happens to expose:
|
|
133
133
|
|
|
134
134
|
```typescript
|
|
135
135
|
export const github = ai.mcpServer("github", {
|
|
@@ -9,6 +9,8 @@ Gate answers **"may this happen?"** before it happens: is this a verified member
|
|
|
9
9
|
burned its minute quota? Attach gates to the HTTP trigger — the pipeline checks them before a
|
|
10
10
|
store write, emit, or channel send runs.
|
|
11
11
|
|
|
12
|
+
> **Same Semantics Across Planes:** OKE does not create separate security models for users, operators, and agents. They enter the same execution model through different triggers and planes: users via sessions, operators via Console policies, and agents via `mcp.tool()` + OAuth 2.1 AS.
|
|
13
|
+
|
|
12
14
|
<Callout title="The one rule">
|
|
13
15
|
Every HTTP trigger declares posture: attach a real gate, or attach `.public()`. Omitting both
|
|
14
16
|
fails boot. Denial is a typed error value — never a thrown exception mid-`do`.
|
|
@@ -63,8 +65,7 @@ import { member, canBook, fair } from "../../gates";
|
|
|
63
65
|
export const health = on(
|
|
64
66
|
http.get("/health").public(),
|
|
65
67
|
flow("health.check", {
|
|
66
|
-
|
|
67
|
-
do: () => ({ ok: true as const }),
|
|
68
|
+
do: () => ({ ok: true }),
|
|
68
69
|
}),
|
|
69
70
|
);
|
|
70
71
|
|
|
@@ -129,6 +129,8 @@ A request that filters on a forbidden column — `?secret=eq.x` — fails with *
|
|
|
129
129
|
|
|
130
130
|
### Live queries — `live: true` and `.live(table)`
|
|
131
131
|
|
|
132
|
+
> **Realtime did not introduce a new subsystem. It composed existing Store, Signal, Gate/RLS, Flow, and Manifest semantics.**
|
|
133
|
+
|
|
132
134
|
Pass `live: true` to also mount `GET <path>/live` — an SSE feed of per-subscriber classified events (`upsert` / `revoked` / `delete`), gated the same way as the CRUD verbs:
|
|
133
135
|
|
|
134
136
|
```typescript
|
|
@@ -46,16 +46,9 @@ lists HTTP routes — static GET paths are links. `curl` still gets `{ data, err
|
|
|
46
46
|
The standard starter already exports a named health Flow:
|
|
47
47
|
|
|
48
48
|
```typescript title="flows/main/health.ts"
|
|
49
|
-
import { on, flow, http } from "okengine";
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export const health = on(
|
|
53
|
-
http.get().public(),
|
|
54
|
-
flow({
|
|
55
|
-
out: z.object({ ok: z.literal(true) }),
|
|
56
|
-
do: () => ({ ok: true as const }),
|
|
57
|
-
}),
|
|
58
|
-
);
|
|
49
|
+
import { on, flow, http } from "okengine/http";
|
|
50
|
+
|
|
51
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
59
52
|
```
|
|
60
53
|
|
|
61
54
|
`main` omits the URL prefix, so this file is `GET /health` named `main.health`. See [Project structure](/docs/get-started/project-structure).
|
|
@@ -12,7 +12,7 @@ Learn the shape once, install on Bun, then write your first Flow from the standa
|
|
|
12
12
|
<Cards>
|
|
13
13
|
<Card
|
|
14
14
|
title="Introduction"
|
|
15
|
-
description="One law, eight elements,
|
|
15
|
+
description="One law, eight elements, one contract."
|
|
16
16
|
href="/docs/get-started/introduction"
|
|
17
17
|
/>
|
|
18
18
|
<Card
|
|
@@ -209,7 +209,7 @@ Rename the env keys in `oke.config.ts` to `dev` / `test` / `prod`.
|
|
|
209
209
|
/>
|
|
210
210
|
<Card
|
|
211
211
|
title="Introduction"
|
|
212
|
-
description="The one law, eight elements,
|
|
212
|
+
description="The one law, eight elements, one contract."
|
|
213
213
|
href="/docs/get-started/introduction"
|
|
214
214
|
/>
|
|
215
215
|
<Card
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Introduction
|
|
3
|
-
description: Learn the one idea behind okengine — then the eight elements and
|
|
3
|
+
description: Learn the one idea behind okengine — then the eight elements and core programming vocabulary you will use everywhere.
|
|
4
4
|
source: docs/spec/unified-theory.md
|
|
5
5
|
icon: BookOpen
|
|
6
6
|
---
|
|
@@ -19,16 +19,9 @@ A booking API, a nightly cleanup job, a receipt email, a row-change hook — in
|
|
|
19
19
|
Four contracts plus a `do`. This is the standard starter's health check:
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
|
-
import { on, flow, http } from "okengine";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export const health = on(
|
|
26
|
-
http.get().public(),
|
|
27
|
-
flow({
|
|
28
|
-
out: z.object({ ok: z.literal(true) }),
|
|
29
|
-
do: () => ({ ok: true as const }),
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
22
|
+
import { on, flow, http } from "okengine/http";
|
|
23
|
+
|
|
24
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
32
25
|
```
|
|
33
26
|
|
|
34
27
|
`main` omits the URL prefix, so this file is `GET /health` named `main.health`. See [Project structure](/docs/get-started/project-structure). Explicit `http.get("/health")` and `flow("main.health")` still win.
|
|
@@ -105,9 +98,9 @@ An element earns its place only if it has **irreducible physics**. New infrastru
|
|
|
105
98
|
| **Channel** | Reaching humans | email · SMS · WhatsApp · push |
|
|
106
99
|
| **AI** | Reaching machine intelligence | model calls · prompts · embeddings · agents · RAG |
|
|
107
100
|
|
|
108
|
-
##
|
|
101
|
+
## Core Programming Vocabulary
|
|
109
102
|
|
|
110
|
-
The
|
|
103
|
+
The core programming vocabulary fits in one import. Everything else in the docs is derived from these names:
|
|
111
104
|
|
|
112
105
|
```ts
|
|
113
106
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
@@ -561,10 +561,7 @@ export const archive = on(
|
|
|
561
561
|
<Tab value="(public)">
|
|
562
562
|
|
|
563
563
|
```typescript title="flows/main/(public)/health.ts"
|
|
564
|
-
export const health = on(
|
|
565
|
-
http.get().public(),
|
|
566
|
-
flow({ out: z.object({ ok: z.literal(true) }), do: () => ({ ok: true as const }) }),
|
|
567
|
-
);
|
|
564
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
568
565
|
// still GET /health named main.health
|
|
569
566
|
```
|
|
570
567
|
|
|
@@ -635,13 +632,9 @@ Do not put `index.ts` next to `health.ts` — that mix is a generate error. Use
|
|
|
635
632
|
<Tab value="health.ts">
|
|
636
633
|
|
|
637
634
|
```typescript title="flows/main/health.ts"
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
out: z.object({ ok: z.literal(true) }),
|
|
642
|
-
do: () => ({ ok: true as const }),
|
|
643
|
-
}),
|
|
644
|
-
);
|
|
635
|
+
import { on, flow, http } from "okengine/http";
|
|
636
|
+
|
|
637
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
645
638
|
// GET /health name main.health
|
|
646
639
|
```
|
|
647
640
|
|
|
@@ -12,6 +12,11 @@ A booking API, a nightly cleanup job, a receipt email, a row-change hook — in
|
|
|
12
12
|
> **The one rule**
|
|
13
13
|
> Every backend behavior is a **Flow**: `on(Trigger) → Effects`. There are no separate species called endpoints, handlers, consumers, jobs, or workflows.
|
|
14
14
|
|
|
15
|
+
<Callout title="A note on the count">
|
|
16
|
+
'Forty' is a representative illustration of the number of backend concerns developers commonly
|
|
17
|
+
assemble across modern stacks, not a literal count of backend technologies.
|
|
18
|
+
</Callout>
|
|
19
|
+
|
|
15
20
|
## The pain we solve
|
|
16
21
|
|
|
17
22
|
Every TypeScript backend works on day one. The bill arrives later. None of these are router problems — they are **gaps** between what your code knows and what your tooling knows.
|
|
@@ -58,7 +63,7 @@ Every gap above is the same shape: knowledge the code already has, duplicated so
|
|
|
58
63
|
|
|
59
64
|
## One Manifest feeds every surface
|
|
60
65
|
|
|
61
|
-
At build time OKE extracts a **Manifest** —
|
|
66
|
+
At build time OKE extracts a **Manifest** — the compiled, versioned contract between your backend model and everything derived from it. The source of truth is your model expressed in code (`on(Trigger) → Flow → fx → Effects`); the Manifest freezes that model into a machine-readable, diffable contract. You do not write or hand-maintain the Manifest:
|
|
62
67
|
|
|
63
68
|
| Surface | Port | You maintain? |
|
|
64
69
|
| --------------------------------- | ------------------ | ----------------------------- |
|
|
@@ -68,7 +73,7 @@ At build time OKE extracts a **Manifest** — a machine-readable description of
|
|
|
68
73
|
| Architecture diagram | Console | No — it _is_ the effect graph |
|
|
69
74
|
| Capability matrix + cache keys | compiler / runtime | No — inferred from `fx` |
|
|
70
75
|
|
|
71
|
-
Because every surface reads the same Manifest, they **cannot disagree** — there is only one source of truth.
|
|
76
|
+
Because every surface reads the same Manifest contract, they **cannot disagree** — there is only one source of truth.
|
|
72
77
|
|
|
73
78
|
## The `fx` rule — one door to the world
|
|
74
79
|
|
|
@@ -109,16 +114,9 @@ Drivers are named after **protocols** (`postgres`, `redis`, `s3`) — never vend
|
|
|
109
114
|
One pipeline. Only the trigger changes between an endpoint, a job, a consumer, and a row hook:
|
|
110
115
|
|
|
111
116
|
```ts
|
|
112
|
-
import { on, flow, http } from "okengine";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
export const health = on(
|
|
116
|
-
http.get().public(),
|
|
117
|
-
flow({
|
|
118
|
-
out: z.object({ ok: z.literal(true) }),
|
|
119
|
-
do: () => ({ ok: true as const }),
|
|
120
|
-
}),
|
|
121
|
-
);
|
|
117
|
+
import { on, flow, http } from "okengine/http";
|
|
118
|
+
|
|
119
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
122
120
|
```
|
|
123
121
|
|
|
124
122
|
| Piece | Role |
|
|
@@ -138,7 +136,7 @@ Consequence: **one documentation path, one trace shape, one Flow model, one thin
|
|
|
138
136
|
|
|
139
137
|
## Learn more
|
|
140
138
|
|
|
141
|
-
- [Introduction](/docs/get-started/introduction) — the one law, eight elements,
|
|
139
|
+
- [Introduction](/docs/get-started/introduction) — the one law, eight elements, one contract
|
|
142
140
|
- [Flow](/docs/elements/flow) — how effects are recorded and inferred
|
|
143
141
|
- [Vault](/docs/elements/vault) — fail-loud secret contracts
|
|
144
142
|
- [Plugins](/docs/reference/plugins) — the official HTTP glue set
|
|
@@ -158,7 +156,7 @@ Consequence: **one documentation path, one trace shape, one Flow model, one thin
|
|
|
158
156
|
/>
|
|
159
157
|
<Card
|
|
160
158
|
title="Introduction"
|
|
161
|
-
description="The one law, eight elements,
|
|
159
|
+
description="The one law, eight elements, one contract."
|
|
162
160
|
href="/docs/get-started/introduction"
|
|
163
161
|
/>
|
|
164
162
|
</Cards>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Documentation"
|
|
3
|
-
description: "One law. Eight elements.
|
|
3
|
+
description: "A new programming model for backends. One law. Eight elements. One contract."
|
|
4
4
|
icon: "BookOpen"
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Welcome to the okengine handbook.
|
|
8
|
+
Welcome to the okengine handbook. The backend model stays small; operational surfaces are derived from it instead of maintained separately.
|
|
9
9
|
|
|
10
10
|
<Callout title="The one law">
|
|
11
11
|
Every backend behavior is a Flow: `on(Trigger) → Effects`. One species; triggers are typed values.
|
|
@@ -17,29 +17,62 @@ on(every("10m"), expireStale);
|
|
|
17
17
|
on(orderPlaced, sendReceipt);
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Understand OKE (Core Model)
|
|
21
21
|
|
|
22
22
|
<Cards>
|
|
23
23
|
<Card
|
|
24
|
-
title="
|
|
25
|
-
description="
|
|
24
|
+
title="Why OKE"
|
|
25
|
+
description="From forty tools to one species — the problem, the law, and the Manifest contract."
|
|
26
|
+
href="/docs/get-started/why"
|
|
27
|
+
/>
|
|
28
|
+
<Card
|
|
29
|
+
title="Elements"
|
|
30
|
+
description="The eight closed core elements: Flow, Signal, Store, Clock, Gate, Vault, Channel, AI."
|
|
31
|
+
href="/docs/elements"
|
|
32
|
+
/>
|
|
33
|
+
<Card
|
|
34
|
+
title="Introduction"
|
|
35
|
+
description="The one law, eight elements, and one contract."
|
|
26
36
|
href="/docs/get-started/introduction"
|
|
27
37
|
/>
|
|
28
|
-
|
|
38
|
+
</Cards>
|
|
39
|
+
|
|
40
|
+
## Build with OKE (Surfaces & Infrastructure)
|
|
41
|
+
|
|
42
|
+
<Cards>
|
|
43
|
+
<Card
|
|
44
|
+
title="Get Started"
|
|
45
|
+
description="Installation, basic usage, project structure, and testing."
|
|
46
|
+
href="/docs/get-started"
|
|
47
|
+
/>
|
|
29
48
|
<Card
|
|
30
49
|
title="Plugins"
|
|
31
|
-
description="
|
|
50
|
+
description="22 official extensions — authentication, OAuth, security, ops, performance."
|
|
32
51
|
href="/docs/plugins"
|
|
33
52
|
/>
|
|
34
53
|
<Card
|
|
35
|
-
title="
|
|
36
|
-
description="
|
|
37
|
-
href="/docs/
|
|
54
|
+
title="Providers"
|
|
55
|
+
description="12 managed cloud infrastructure options behind protocol-named drivers."
|
|
56
|
+
href="/docs/providers"
|
|
57
|
+
/>
|
|
58
|
+
<Card
|
|
59
|
+
title="Recipes"
|
|
60
|
+
description="20 self-hosted infrastructure recipes via Docker compose."
|
|
61
|
+
href="/docs/recipes"
|
|
38
62
|
/>
|
|
39
63
|
<Card
|
|
40
64
|
title="Reference"
|
|
41
|
-
description="
|
|
65
|
+
description="Configuration, fx ledger, environment variables, errors, client, and security."
|
|
42
66
|
href="/docs/reference"
|
|
43
67
|
/>
|
|
44
|
-
<Card
|
|
68
|
+
<Card
|
|
69
|
+
title="AI Resources & MCP"
|
|
70
|
+
description="Runtime MCP (:6535), docs MCP (:6536), skills, and /llms.txt."
|
|
71
|
+
href="/docs/ai"
|
|
72
|
+
/>
|
|
73
|
+
<Card
|
|
74
|
+
title="Deployment"
|
|
75
|
+
description="Docker, Swarm, Kubernetes, and reverse proxy deployment."
|
|
76
|
+
href="/docs/deployment"
|
|
77
|
+
/>
|
|
45
78
|
</Cards>
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
"icon": "BookOpen",
|
|
4
4
|
"pages": [
|
|
5
5
|
"index",
|
|
6
|
-
"
|
|
6
|
+
"---Understand OKE---",
|
|
7
7
|
"elements",
|
|
8
|
-
"
|
|
8
|
+
"---Build with OKE---",
|
|
9
|
+
"get-started",
|
|
10
|
+
"ai",
|
|
9
11
|
"deployment",
|
|
10
|
-
"recipes",
|
|
11
|
-
"providers",
|
|
12
12
|
"reference",
|
|
13
|
-
"
|
|
13
|
+
"plugins",
|
|
14
|
+
"---Drivers---",
|
|
15
|
+
"providers",
|
|
16
|
+
"recipes"
|
|
14
17
|
]
|
|
15
18
|
}
|
|
@@ -8,6 +8,8 @@ source: "docs/spec/unified-theory.md"
|
|
|
8
8
|
Every provider below speaks a protocol oke already drives — Postgres wire or Redis wire.
|
|
9
9
|
Hand the connection string to that driver; no new driver id, no Flow code changes.
|
|
10
10
|
|
|
11
|
+
These managed providers are real, working infrastructure choices behind the same two templates (`standard`, `advanced`) and the same eight elements — not alternative products or separate frameworks.
|
|
12
|
+
|
|
11
13
|
<Callout title="The one rule">
|
|
12
14
|
Vendor choice lives in a connection URL (`DATABASE_URL` / `REDIS_URL`) — never in
|
|
13
15
|
`drivers.store.sql` / `drivers.store.kv`, which only ever say `postgres` or `redis`.
|
|
@@ -8,6 +8,8 @@ source: "docs/spec/unified-theory.md"
|
|
|
8
8
|
Every recipe below is already wired into `oke docker` — pin an image in `oke.config.ts`
|
|
9
9
|
and get env, healthcheck, and a connection URL for free.
|
|
10
10
|
|
|
11
|
+
These recipes are real, working infrastructure choices behind the same two templates (`standard`, `advanced`) and the same eight elements — not alternative products or separate frameworks.
|
|
12
|
+
|
|
11
13
|
<Callout title="The one rule">
|
|
12
14
|
Vendor choice lives in `images[…]` — never in `drivers.*`, which only ever say protocol ids
|
|
13
15
|
(`postgres`, `redis`, `s3`, `smtp`, `meilisearch`, `openai-compatible`, `ollama`, …).
|
|
@@ -1,4 +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-
|
|
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-_rgpdVzo.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
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
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
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};
|