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.
Files changed (285) hide show
  1. package/package.json +3 -4
  2. package/site/content/docs/elements/ai.mdx +2 -1
  3. package/site/content/docs/elements/channel.mdx +1 -0
  4. package/site/content/docs/elements/clock.mdx +3 -0
  5. package/site/content/docs/elements/flow.mdx +9 -0
  6. package/site/content/docs/elements/gate.mdx +80 -22
  7. package/site/content/docs/elements/signal.mdx +4 -4
  8. package/site/content/docs/elements/store.mdx +84 -27
  9. package/site/content/docs/elements/vault.mdx +4 -0
  10. package/site/content/docs/get-started/basic-usage.mdx +22 -13
  11. package/site/content/docs/get-started/index.mdx +5 -0
  12. package/site/content/docs/get-started/introduction.mdx +11 -3
  13. package/site/content/docs/get-started/meta.json +1 -1
  14. package/site/content/docs/get-started/project-structure.mdx +932 -0
  15. package/site/content/docs/reference/cli.md +13 -10
  16. package/site/content/docs/reference/client.mdx +28 -21
  17. package/site/content/docs/reference/fx.mdx +29 -18
  18. package/site/content/docs/reference/security.md +2 -2
  19. package/src/auth/api-key-sql.test.ts +47 -0
  20. package/src/auth/api-key-sql.ts +254 -0
  21. package/src/auth/api-keys.ts +273 -14
  22. package/src/auth/auth.test.ts +56 -0
  23. package/src/auth/bindings.ts +14 -5
  24. package/src/auth/config.ts +5 -5
  25. package/src/auth/cross-plane.ts +10 -3
  26. package/src/auth/index.ts +25 -1
  27. package/src/auth/schema.ts +8 -0
  28. package/src/cli/ai-setup/ai-setup.test.ts +62 -0
  29. package/src/cli/ai-setup/apply.ts +32 -1
  30. package/src/cli/attach-host-console.test.ts +40 -2
  31. package/src/cli/attach-host-console.ts +9 -0
  32. package/src/cli/build.test.ts +4 -2
  33. package/src/cli/db-seed.ts +48 -15
  34. package/src/cli/db.test.ts +104 -0
  35. package/src/cli/db.ts +122 -10
  36. package/src/cli/dev-controls.test.ts +20 -0
  37. package/src/cli/dev-controls.ts +13 -0
  38. package/src/cli/dev.test.ts +15 -4
  39. package/src/cli/dev.ts +102 -29
  40. package/src/cli/ensure-drizzle-config.ts +2 -1
  41. package/src/cli/tui/DevLive.tsx +12 -2
  42. package/src/cli/tui/keys.test.ts +2 -1
  43. package/src/cli/tui/keys.ts +3 -2
  44. package/src/compiler/aot.test.ts +1 -1
  45. package/src/compiler/effects-infer.ts +17 -2
  46. package/src/compiler/extract.test.ts +409 -33
  47. package/src/compiler/extract.ts +318 -67
  48. package/src/compiler/flow-path.test.ts +96 -0
  49. package/src/compiler/flow-path.ts +173 -0
  50. package/src/compiler/generate-adopt.test.ts +112 -3
  51. package/src/compiler/generate-adopt.ts +280 -26
  52. package/src/compiler/schema-from-ast.ts +609 -0
  53. package/src/console/server/access.test.ts +116 -8
  54. package/src/console/server/access.ts +70 -23
  55. package/src/console/server/app.ts +1 -0
  56. package/src/console/server/bind.ts +2 -2
  57. package/src/console/server/console.test.ts +3 -0
  58. package/src/console/server/dev-identities.test.ts +127 -0
  59. package/src/console/server/dev-identities.ts +229 -0
  60. package/src/console/server/flows-invoke.test.ts +21 -4
  61. package/src/console/server/flows.ts +233 -98
  62. package/src/console/server/gates.test.ts +24 -0
  63. package/src/console/server/gates.ts +8 -21
  64. package/src/console/server/index.ts +1 -0
  65. package/src/console/server/invoke-user-flow.test.ts +3 -1
  66. package/src/console/server/invoke-user-flow.ts +17 -2
  67. package/src/console/server/iso-at.test.ts +9 -0
  68. package/src/console/server/iso-at.ts +11 -0
  69. package/src/console/server/live.test.ts +26 -1
  70. package/src/console/server/live.ts +4 -2
  71. package/src/console/server/operator-db.ts +2 -0
  72. package/src/console/server/security-headers.ts +6 -3
  73. package/src/console/server/spa-proxy.test.ts +1 -0
  74. package/src/console/server/sql-catalog.ts +147 -7
  75. package/src/console/server/sql-rls.test.ts +16 -0
  76. package/src/console/server/state.ts +37 -55
  77. package/src/console/server/store.test.ts +7 -4
  78. package/src/console/server/store.ts +142 -58
  79. package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
  80. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
  81. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
  82. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
  83. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
  84. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
  85. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
  86. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
  87. package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
  88. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
  89. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
  90. package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
  91. package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
  92. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
  93. package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
  94. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
  95. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
  96. package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
  97. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
  98. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
  99. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
  100. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
  101. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
  102. package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
  103. package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
  104. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
  105. package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
  106. package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
  107. package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
  108. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
  109. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
  110. package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
  111. package/src/console/ui-next/dist/index.html +12 -10
  112. package/src/console/ui-next/index.html +3 -3
  113. package/src/console/ui-next/src/client.ts +202 -7
  114. package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
  115. package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
  116. package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
  117. package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
  118. package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
  119. package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
  120. package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
  121. package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
  122. package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
  123. package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
  124. package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
  125. package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
  126. package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
  127. package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
  128. package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
  129. package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
  130. package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
  131. package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
  132. package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
  133. package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
  134. package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
  135. package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
  136. package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
  137. package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
  138. package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
  139. package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
  140. package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
  141. package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
  142. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
  143. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
  144. package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
  145. package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
  146. package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
  147. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
  148. package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
  149. package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
  150. package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
  151. package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
  152. package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
  153. package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
  154. package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
  155. package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
  156. package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
  157. package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
  158. package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
  159. package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
  160. package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
  161. package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
  162. package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
  163. package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
  164. package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
  165. package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
  166. package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
  167. package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
  168. package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
  169. package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
  170. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
  171. package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
  172. package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
  173. package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
  174. package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
  175. package/src/console/ui-next/src/lib/document-meta.ts +2 -1
  176. package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
  177. package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
  178. package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
  179. package/src/console/ui-next/src/lib/shortcut.ts +3 -0
  180. package/src/console/ui-next/src/router.tsx +10 -1
  181. package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
  182. package/src/console/ui-next/ui-next-seed-store.ts +43 -15
  183. package/src/console/xss.gate.test.ts +1 -0
  184. package/src/docker/compose-health.test.ts +27 -0
  185. package/src/docker/compose-health.ts +19 -3
  186. package/src/docker/docker.test.ts +3 -0
  187. package/src/docker/recipes/pgdog.ts +2 -0
  188. package/src/drivers/clock-postgres.ts +13 -9
  189. package/src/drivers/instances-postgres.ts +4 -6
  190. package/src/drivers/journal-postgres.ts +13 -9
  191. package/src/drivers/pg-rls.test.ts +87 -0
  192. package/src/drivers/pg-rls.ts +233 -7
  193. package/src/drivers/pglite.ts +26 -3
  194. package/src/drivers/postgres.test.ts +23 -0
  195. package/src/drivers/postgres.ts +133 -9
  196. package/src/drivers/types.ts +10 -0
  197. package/src/elements/clock/declare.ts +26 -1
  198. package/src/elements/gate/boot.ts +5 -5
  199. package/src/elements/gate/declare.ts +38 -4
  200. package/src/elements/gate/flatten.ts +1 -1
  201. package/src/elements/gate/runtime.ts +1 -1
  202. package/src/elements/store/cache.ts +1 -1
  203. package/src/elements/store/declare.ts +9 -6
  204. package/src/elements/store/emit-drizzle.ts +44 -8
  205. package/src/elements/store/resource.test.ts +1 -1
  206. package/src/elements/store/rls-identity.test.ts +95 -0
  207. package/src/elements/store/rls-identity.ts +116 -0
  208. package/src/elements/store/runtime.ts +18 -0
  209. package/src/elements/store/schema-decl.test.ts +37 -0
  210. package/src/elements/store/schema-decl.ts +260 -41
  211. package/src/elements/store/seed.ts +1 -1
  212. package/src/elements/store/sql-condition.test.ts +12 -0
  213. package/src/elements/store/sql-condition.ts +26 -2
  214. package/src/elements/store/sql-rls-isolation.test.ts +165 -0
  215. package/src/elements/store/sql-rls-stamp.test.ts +117 -0
  216. package/src/elements/store/sql-select.types.test.ts +71 -0
  217. package/src/elements/store/sql-session.ts +135 -35
  218. package/src/elements/store/upsert-app.test.ts +1 -1
  219. package/src/elements/store.ts +17 -0
  220. package/src/http.ts +20 -3
  221. package/src/index.ts +11 -1
  222. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  223. package/src/kernel/adopt-routes.ts +30 -2
  224. package/src/kernel/api-key-host-persist.test.ts +143 -0
  225. package/src/kernel/app.ts +145 -16
  226. package/src/kernel/auth-resolve.ts +10 -5
  227. package/src/kernel/auto-cache.test.ts +8 -8
  228. package/src/kernel/auto-registry.test.ts +49 -9
  229. package/src/kernel/boot.test.ts +5 -5
  230. package/src/kernel/boot.ts +2 -2
  231. package/src/kernel/effects-stamping.test.ts +3 -3
  232. package/src/kernel/element-registries.ts +12 -5
  233. package/src/kernel/errors.ts +24 -0
  234. package/src/kernel/flow-units.ts +64 -0
  235. package/src/kernel/flow.ts +29 -17
  236. package/src/kernel/fx-auth-keys.test.ts +88 -0
  237. package/src/kernel/fx-auth-keys.ts +198 -0
  238. package/src/kernel/fx.test.ts +17 -1
  239. package/src/kernel/fx.ts +59 -18
  240. package/src/kernel/horizontal-child.ts +4 -4
  241. package/src/kernel/http-path-pending.ts +20 -0
  242. package/src/kernel/http-resource.ts +4 -0
  243. package/src/kernel/http-stream.test.ts +4 -4
  244. package/src/kernel/pipeline-api-key.test.ts +192 -7
  245. package/src/kernel/pipeline.test.ts +2 -2
  246. package/src/kernel/pipeline.ts +4 -2
  247. package/src/kernel/plugin-elements.test.ts +1 -1
  248. package/src/kernel/plugin-needs.test.ts +1 -1
  249. package/src/kernel/ready.test.ts +2 -2
  250. package/src/kernel/resource-mount.test.ts +14 -0
  251. package/src/kernel/run-duration.test.ts +2 -2
  252. package/src/kernel/stamp-http.test.ts +79 -0
  253. package/src/kernel/stamp-http.ts +75 -0
  254. package/src/kernel/triggers.ts +52 -27
  255. package/src/manifest/types.ts +22 -3
  256. package/src/release/build-lib.ts +1 -0
  257. package/src/release/measure.ts +2 -2
  258. package/src/runtime/json-code-block.test.ts +2 -2
  259. package/src/runtime/serve.test.ts +4 -4
  260. package/src/term.test.ts +52 -4
  261. package/src/term.ts +125 -17
  262. package/src/test/reset-element-registries.ts +8 -3
  263. package/src/cli/ask-seed.test.ts +0 -96
  264. package/src/cli/ask-seed.ts +0 -82
  265. package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
  266. package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
  267. package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
  268. package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
  269. package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
  270. package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
  271. package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
  272. package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
  273. package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
  274. package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
  275. package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
  276. package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
  277. package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
  278. package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
  279. package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
  280. package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
  281. package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
  282. package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
  283. package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
  284. package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
  285. package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Default invoke-as identities — ten rungs from owner to guest.
3
+ *
4
+ * `user_demo` (owner) and `user_member` keep their historic ids so Call API
5
+ * tests and Access fixtures stay stable. Owner scopes follow the Manifest;
6
+ * lower rungs intersect a named profile with that catalog. Keel (`app ===
7
+ * "keel"`) always unions {@link KEEL_SCOPES} so seeding is not missing a
8
+ * declared Gate. No leftover app catalog is invented for other apps.
9
+ */
10
+
11
+ import { setRoleGrants, upsertRole, type RoleStore } from "../../auth/index.ts";
12
+ import type { Manifest } from "../../manifest/types.ts";
13
+
14
+ /**
15
+ * Every Keel user-plane scope — `gate.policy("member")` plus `gate.scope`.
16
+ * Seeded onto the owner → guest ladder when the Manifest app is `keel`.
17
+ */
18
+ export const KEEL_SCOPES = [
19
+ "member",
20
+ "task:write",
21
+ "comment:write",
22
+ "files:write",
23
+ "project:admin",
24
+ "member:admin",
25
+ "webhook:admin",
26
+ ] as const;
27
+
28
+ /** One seeded development identity. */
29
+ export interface SeededDevIdentity {
30
+ readonly id: string;
31
+ readonly email: string;
32
+ readonly name: string;
33
+ readonly status: "active";
34
+ readonly scopes: readonly string[];
35
+ }
36
+
37
+ /** Stable person for one authorization rung. */
38
+ export interface DevIdentityPerson {
39
+ readonly id: string;
40
+ readonly email: string;
41
+ readonly name: string;
42
+ /** Workspace / Access role label, owner → guest. */
43
+ readonly role: string;
44
+ }
45
+
46
+ /**
47
+ * Ten people, owner → guest. Demo and Member keep historic ids.
48
+ */
49
+ export const DEV_IDENTITY_LADDER = [
50
+ { id: "user_demo", email: "demo@example.com", name: "Demo User", role: "owner" },
51
+ { id: "user_admin", email: "admin@example.com", name: "Admin", role: "admin" },
52
+ {
53
+ id: "user_workspace",
54
+ email: "workspace@example.com",
55
+ name: "Workspace Admin",
56
+ role: "workspace_admin",
57
+ },
58
+ { id: "user_pm", email: "pm@example.com", name: "Project Manager", role: "project_manager" },
59
+ { id: "user_lead", email: "lead@example.com", name: "Lead", role: "lead" },
60
+ { id: "user_dev", email: "developer@example.com", name: "Developer", role: "developer" },
61
+ { id: "user_writer", email: "writer@example.com", name: "Contributor", role: "contributor" },
62
+ { id: "user_member", email: "member@example.com", name: "Member", role: "member" },
63
+ { id: "user_commenter", email: "commenter@example.com", name: "Commenter", role: "commenter" },
64
+ { id: "user_guest", email: "guest@example.com", name: "Guest", role: "guest" },
65
+ ] as const satisfies readonly DevIdentityPerson[];
66
+
67
+ /** One Keel ladder role and the scopes it holds. */
68
+ export type KeelLadderRole = (typeof DEV_IDENTITY_LADDER)[number]["role"];
69
+
70
+ /**
71
+ * Owner → guest grants. Owner and admin hold {@link KEEL_SCOPES}.
72
+ */
73
+ export const KEEL_LADDER_GRANTS: Readonly<Record<KeelLadderRole, readonly string[]>> = {
74
+ owner: KEEL_SCOPES,
75
+ admin: KEEL_SCOPES,
76
+ workspace_admin: [
77
+ "member",
78
+ "task:write",
79
+ "comment:write",
80
+ "files:write",
81
+ "project:admin",
82
+ "member:admin",
83
+ ],
84
+ project_manager: ["member", "task:write", "comment:write", "files:write", "project:admin"],
85
+ lead: ["member", "task:write", "comment:write", "project:admin"],
86
+ developer: ["member", "task:write", "comment:write", "files:write"],
87
+ contributor: ["member", "task:write", "comment:write"],
88
+ member: ["member"],
89
+ commenter: ["member", "comment:write"],
90
+ guest: [],
91
+ };
92
+
93
+ /** `*` = every catalog scope (owner). Other rungs follow {@link KEEL_LADDER_GRANTS}. */
94
+ const LADDER_SCOPE_PROFILES: readonly (readonly string[] | "*")[] = [
95
+ "*",
96
+ ...DEV_IDENTITY_LADDER.slice(1).map((person) => KEEL_LADDER_GRANTS[person.role]),
97
+ ];
98
+
99
+ /**
100
+ * Whether this Manifest is the Keel example (full scope seed applies).
101
+ *
102
+ * @param manifest - Optional Manifest
103
+ */
104
+ export function isKeelManifest(manifest?: Manifest | null): boolean {
105
+ return manifest?.app === "keel";
106
+ }
107
+
108
+ /**
109
+ * Application scopes implied by a Manifest — gate `scopes`, scoped gate
110
+ * ids, and flow gate refs. Always includes `member`.
111
+ *
112
+ * @param manifest - Optional Manifest
113
+ */
114
+ export function demoScopesFromManifest(manifest?: Manifest | null): string[] {
115
+ const scopes = new Set<string>(["member"]);
116
+ if (isKeelManifest(manifest)) {
117
+ for (const scope of KEEL_SCOPES) scopes.add(scope);
118
+ }
119
+ if (!manifest) return [...scopes].sort();
120
+ for (const [gateId, gate] of Object.entries(manifest.gates ?? {})) {
121
+ for (const scope of gate.scopes ?? []) scopes.add(scope);
122
+ if (gateId.includes(":") && !gateId.startsWith("rate:")) scopes.add(gateId);
123
+ }
124
+ for (const flow of Object.values(manifest.flows ?? {})) {
125
+ for (const gate of flow.gates ?? []) {
126
+ if (gate.includes(":") && !gate.startsWith("rate:")) scopes.add(gate);
127
+ }
128
+ }
129
+ return [...scopes].sort();
130
+ }
131
+
132
+ /**
133
+ * Scopes for one ladder rung — owner is the full catalog; others intersect
134
+ * their profile. `member` stays on every signed-in rung.
135
+ *
136
+ * @param rung - 0 (owner) … 9 (guest)
137
+ * @param catalog - Manifest-derived scopes
138
+ */
139
+ export function scopesForDevIdentityRung(rung: number, catalog: readonly string[]): string[] {
140
+ const profile = LADDER_SCOPE_PROFILES[rung];
141
+ if (profile === undefined) return [];
142
+ if (profile === "*") return [...catalog].sort((a, b) => a.localeCompare(b));
143
+ const allowed = new Set(catalog);
144
+ return [...new Set(profile.filter((scope) => scope === "member" || allowed.has(scope)))].sort(
145
+ (a, b) => a.localeCompare(b),
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Default development identities for the invoke-as picker.
151
+ *
152
+ * @param manifest - Optional Manifest used to derive application scopes
153
+ */
154
+ export function defaultDevIdentities(manifest?: Manifest | null): SeededDevIdentity[] {
155
+ const catalog = demoScopesFromManifest(manifest);
156
+ return DEV_IDENTITY_LADDER.map((person, rung) => ({
157
+ id: person.id,
158
+ email: person.email,
159
+ name: person.name,
160
+ status: "active",
161
+ scopes: scopesForDevIdentityRung(rung, catalog),
162
+ }));
163
+ }
164
+
165
+ /**
166
+ * Whether the live list is still the built-in ladder (safe to refresh).
167
+ *
168
+ * @param identities - Live identity list
169
+ */
170
+ export function isDefaultIdentitySeed(identities: readonly { readonly id: string }[]): boolean {
171
+ if (identities.length !== DEV_IDENTITY_LADDER.length) return false;
172
+ const ids = new Set(identities.map((row) => row.id));
173
+ return DEV_IDENTITY_LADDER.every((person) => ids.has(person.id));
174
+ }
175
+
176
+ /**
177
+ * Signed-in ladder ids (guest excluded) — default `role_member` holders.
178
+ */
179
+ export function defaultMemberIdentityIds(): string[] {
180
+ return DEV_IDENTITY_LADDER.filter((person) => person.role !== "guest").map((person) => person.id);
181
+ }
182
+
183
+ /**
184
+ * Replace the default ladder when a Manifest arrives.
185
+ *
186
+ * @param identities - Live identity list
187
+ * @param manifest - New Manifest
188
+ */
189
+ export function refreshSeededIdentities(
190
+ identities: Array<{
191
+ id: string;
192
+ email: string;
193
+ name: string;
194
+ status: "active" | "disabled";
195
+ scopes: readonly string[];
196
+ }>,
197
+ manifest: Manifest | null,
198
+ ): void {
199
+ if (!isDefaultIdentitySeed(identities)) return;
200
+ const next = defaultDevIdentities(manifest);
201
+ identities.splice(0, identities.length, ...next);
202
+ }
203
+
204
+ /**
205
+ * Seed Access roles for Keel — every {@link KEEL_SCOPES} pair lands on the
206
+ * owner / admin rung; lower rungs use {@link KEEL_LADDER_GRANTS}.
207
+ *
208
+ * @param roles - Role store
209
+ * @param roleMembers - roleId → principal ids
210
+ */
211
+ export function seedKeelAccessRoles(roles: RoleStore, roleMembers: Map<string, string[]>): void {
212
+ for (const person of DEV_IDENTITY_LADDER) {
213
+ const roleId = `role_${person.role}`;
214
+ upsertRole(roles, {
215
+ id: roleId,
216
+ name: person.role,
217
+ plane: "user",
218
+ description:
219
+ person.role === "owner"
220
+ ? "Full workspace authorization"
221
+ : person.role === "guest"
222
+ ? "Least authorization — no write scopes"
223
+ : `Keel ${person.role.replaceAll("_", " ")}`,
224
+ });
225
+ setRoleGrants(roles, roleId, KEEL_LADDER_GRANTS[person.role]);
226
+ roleMembers.set(roleId, person.role === "guest" ? [] : [person.id]);
227
+ }
228
+ roleMembers.set("role_member", defaultMemberIdentityIds());
229
+ }
@@ -15,12 +15,29 @@ import { http } from "../../kernel/triggers.ts";
15
15
  import { field, store } from "../../elements/store.ts";
16
16
  import { PII_MASK } from "../../elements/store/classify.ts";
17
17
  import { createTestApp } from "../../test/create-test-app.ts";
18
+ import type { Manifest } from "../../manifest/types.ts";
18
19
  import { FLOWS_TEST_MANIFEST } from "../ui-next/src/features/flows/fixture.ts";
19
20
  import { bindHostInvokeUserFlow } from "./invoke-user-flow.ts";
20
21
  import { startConsoleApp } from "./serve.ts";
21
22
  import { setManifest } from "./state.ts";
22
23
  import type { InvokeUserFlowInput } from "./invoke-user-flow.ts";
23
24
 
25
+ /** Fixture plus the booking gate the host actually enforces. */
26
+ const BOOKING_INVOKE_MANIFEST: Manifest = {
27
+ ...FLOWS_TEST_MANIFEST,
28
+ gates: {
29
+ member: { kind: "policy", scopes: ["member"] },
30
+ "booking:create": { kind: "policy", scopes: ["booking:create"] },
31
+ },
32
+ flows: {
33
+ ...FLOWS_TEST_MANIFEST.flows,
34
+ "bookings.create": {
35
+ ...FLOWS_TEST_MANIFEST.flows!["bookings.create"]!,
36
+ gates: ["member", "booking:create"],
37
+ },
38
+ },
39
+ };
40
+
24
41
  const memoryDrivers = {
25
42
  store: { kv: { dev: "memory", test: "memory", prod: "memory" } },
26
43
  signal: { dev: "memory", test: "memory", prod: "memory" },
@@ -147,11 +164,11 @@ describe("console flows invoke", () => {
147
164
  secret: "test-secret-flows",
148
165
  silentClaim: true,
149
166
  production: false,
150
- manifest: FLOWS_TEST_MANIFEST,
167
+ manifest: BOOKING_INVOKE_MANIFEST,
151
168
  });
152
169
  const host = await bootInvokeHost();
153
170
  try {
154
- setManifest(handle.state, FLOWS_TEST_MANIFEST);
171
+ setManifest(handle.state, BOOKING_INVOKE_MANIFEST);
155
172
  handle.state.invokeUserFlow = bindHostInvokeUserFlow(host);
156
173
  const auth = await claimOperator(handle);
157
174
 
@@ -214,12 +231,12 @@ describe("console flows invoke", () => {
214
231
  secret: "test-secret-flows-adv",
215
232
  silentClaim: true,
216
233
  production: false,
217
- manifest: FLOWS_TEST_MANIFEST,
234
+ manifest: BOOKING_INVOKE_MANIFEST,
218
235
  });
219
236
  const host = await bootInvokeHost();
220
237
  const seen: InvokeUserFlowInput[] = [];
221
238
  try {
222
- setManifest(handle.state, FLOWS_TEST_MANIFEST);
239
+ setManifest(handle.state, BOOKING_INVOKE_MANIFEST);
223
240
  const bound = bindHostInvokeUserFlow(host);
224
241
  handle.state.invokeUserFlow = async (input) => {
225
242
  seen.push(input);