okengine 0.13.0 → 0.15.2

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 (277) hide show
  1. package/manifest.v1.schema.json +7 -1
  2. package/package.json +4 -5
  3. package/site/content/docs/ai/mcp.mdx +27 -2
  4. package/site/content/docs/elements/ai.mdx +59 -11
  5. package/site/content/docs/elements/channel.mdx +1 -0
  6. package/site/content/docs/elements/clock.mdx +30 -12
  7. package/site/content/docs/elements/flow.mdx +9 -3
  8. package/site/content/docs/elements/gate.mdx +25 -22
  9. package/site/content/docs/elements/signal.mdx +71 -25
  10. package/site/content/docs/elements/store.mdx +87 -28
  11. package/site/content/docs/elements/vault.mdx +1 -0
  12. package/site/content/docs/get-started/basic-usage.mdx +22 -13
  13. package/site/content/docs/get-started/index.mdx +5 -0
  14. package/site/content/docs/get-started/installation.mdx +2 -2
  15. package/site/content/docs/get-started/introduction.mdx +11 -3
  16. package/site/content/docs/get-started/meta.json +1 -1
  17. package/site/content/docs/get-started/project-structure.mdx +932 -0
  18. package/site/content/docs/reference/cli.md +13 -10
  19. package/site/content/docs/reference/client.mdx +25 -21
  20. package/site/content/docs/reference/fx.mdx +27 -22
  21. package/site/content/docs/reference/security.md +1 -1
  22. package/src/auth/bindings.ts +14 -5
  23. package/src/auth/cross-plane.ts +10 -3
  24. package/src/cli/ai-setup/ai-setup.test.ts +62 -0
  25. package/src/cli/ai-setup/apply.ts +32 -1
  26. package/src/cli/ai-setup/recommend.test.ts +25 -0
  27. package/src/cli/ai-setup/recommend.ts +8 -3
  28. package/src/cli/attach-host-console.test.ts +3 -2
  29. package/src/cli/attach-host-console.ts +3 -0
  30. package/src/cli/build.test.ts +4 -2
  31. package/src/cli/db-seed.ts +48 -15
  32. package/src/cli/db.test.ts +104 -0
  33. package/src/cli/db.ts +122 -10
  34. package/src/cli/dev-controls.test.ts +20 -0
  35. package/src/cli/dev-controls.ts +13 -0
  36. package/src/cli/dev.test.ts +15 -4
  37. package/src/cli/dev.ts +102 -29
  38. package/src/cli/ensure-drizzle-config.ts +2 -1
  39. package/src/cli/tui/DevLive.tsx +12 -2
  40. package/src/cli/tui/keys.test.ts +2 -1
  41. package/src/cli/tui/keys.ts +3 -2
  42. package/src/compiler/aot.test.ts +1 -1
  43. package/src/compiler/effects-infer.ts +58 -3
  44. package/src/compiler/extract.test.ts +458 -29
  45. package/src/compiler/extract.ts +387 -69
  46. package/src/compiler/flow-path.test.ts +96 -0
  47. package/src/compiler/flow-path.ts +173 -0
  48. package/src/compiler/generate-adopt.test.ts +112 -3
  49. package/src/compiler/generate-adopt.ts +280 -26
  50. package/src/compiler/response.ts +45 -1
  51. package/src/compiler/schema-from-ast.ts +609 -0
  52. package/src/console/server/ai.ts +5 -2
  53. package/src/console/server/bind.ts +2 -2
  54. package/src/console/server/console.test.ts +3 -0
  55. package/src/console/server/flows.ts +121 -40
  56. package/src/console/server/invoke-user-flow.test.ts +3 -1
  57. package/src/console/server/invoke-user-flow.ts +17 -2
  58. package/src/console/server/iso-at.test.ts +9 -0
  59. package/src/console/server/iso-at.ts +11 -0
  60. package/src/console/server/security-headers.ts +6 -3
  61. package/src/console/server/serve.ts +17 -2
  62. package/src/console/server/spa-proxy.test.ts +1 -0
  63. package/src/console/server/sql-catalog.ts +147 -7
  64. package/src/console/server/sql-rls.test.ts +16 -0
  65. package/src/console/server/state.ts +8 -0
  66. package/src/console/server/store.test.ts +7 -4
  67. package/src/console/server/store.ts +142 -58
  68. package/src/console/ui-next/dist/assets/agent-disclosure-C0X1fbWF.js +1 -0
  69. package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +1 -0
  70. package/src/console/ui-next/dist/assets/{call-pii-button-bqkxMrJH.js → call-pii-button-DEDCl_j3.js} +1 -1
  71. package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +1 -0
  72. package/src/console/ui-next/dist/assets/{confirm-sheet-DDCRmG62.js → confirm-sheet-3ptDzXbz.js} +1 -1
  73. package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
  74. package/src/console/ui-next/dist/assets/{explorer-empty-HjTnVQoR.js → explorer-empty-BvYrygyO.js} +1 -1
  75. package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +1 -0
  76. package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-CS_O8L-r.js} +1 -1
  77. package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +1 -0
  78. package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +66 -0
  79. package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +2 -0
  80. package/src/console/ui-next/dist/assets/{link-DF7SZ9Ek.js → link-COeyggt-.js} +1 -1
  81. package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +4 -0
  82. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
  83. package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +18 -0
  84. package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +1 -0
  85. package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +1 -0
  86. package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +1 -0
  87. package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +41 -0
  88. package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +2 -0
  89. package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +54 -0
  90. package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +1 -0
  91. package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-BLbZhzyF.js} +1 -1
  92. package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +2 -0
  93. package/src/console/ui-next/dist/index.html +6 -6
  94. package/src/console/ui-next/seed-invoke-host.ts +2 -0
  95. package/src/console/ui-next/src/client.ts +35 -7
  96. package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
  97. package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
  98. package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
  99. package/src/console/ui-next/src/features/flows/graph/build-flow-graph.test.ts +18 -0
  100. package/src/console/ui-next/src/features/flows/graph/build-flow-graph.ts +14 -1
  101. package/src/console/ui-next/src/features/flows/graph/neighborhood.test.ts +17 -0
  102. package/src/console/ui-next/src/features/flows/graph/neighborhood.ts +16 -3
  103. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +3 -1
  104. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
  105. package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
  106. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +9 -3
  107. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +29 -2
  108. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
  109. package/src/console/ui-next/src/features/observability/lib/ask-count.test.ts +25 -0
  110. package/src/console/ui-next/src/features/observability/lib/ask-count.ts +4 -1
  111. package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
  112. package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
  113. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
  114. package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
  115. package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
  116. package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
  117. package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
  118. package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
  119. package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
  120. package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
  121. package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
  122. package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
  123. package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
  124. package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
  125. package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
  126. package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
  127. package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
  128. package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
  129. package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
  130. package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
  131. package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
  132. package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
  133. package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
  134. package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
  135. package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
  136. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
  137. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +12 -4
  138. package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
  139. package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
  140. package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
  141. package/src/docker/compose-health.test.ts +27 -0
  142. package/src/docker/compose-health.ts +19 -3
  143. package/src/docker/docker.test.ts +4 -1
  144. package/src/docker/dockerfile.ts +1 -1
  145. package/src/docker/recipes/pgdog.ts +2 -0
  146. package/src/drivers/ai-anthropic.ts +5 -0
  147. package/src/drivers/ai-ollama.ts +49 -30
  148. package/src/drivers/ai-openai-compatible.ts +57 -46
  149. package/src/drivers/ai-providers.test.ts +3 -0
  150. package/src/drivers/bun-native-completeness.test.ts +7 -9
  151. package/src/drivers/clock-postgres.ts +13 -9
  152. package/src/drivers/instances-postgres.ts +4 -6
  153. package/src/drivers/journal-postgres.ts +13 -9
  154. package/src/drivers/pg-rls.test.ts +87 -0
  155. package/src/drivers/pg-rls.ts +233 -7
  156. package/src/drivers/pglite.ts +26 -3
  157. package/src/drivers/postgres.test.ts +23 -0
  158. package/src/drivers/postgres.ts +133 -9
  159. package/src/drivers/redis.ts +11 -4
  160. package/src/drivers/signal-redis.ts +24 -14
  161. package/src/drivers/signal-types.ts +2 -1
  162. package/src/drivers/types.ts +10 -0
  163. package/src/elements/ai/declare.ts +109 -0
  164. package/src/elements/ai/errors.test.ts +5 -1
  165. package/src/elements/ai/errors.ts +30 -2
  166. package/src/elements/ai/eval.ts +4 -6
  167. package/src/elements/ai/mcp-client.test.ts +206 -0
  168. package/src/elements/ai/mcp-client.ts +362 -0
  169. package/src/elements/ai/mcp-http.ts +159 -0
  170. package/src/elements/ai/mcp-mock.ts +134 -0
  171. package/src/elements/ai/mcp-protocol.ts +234 -0
  172. package/src/elements/ai/mcp-stdio.test.ts +50 -0
  173. package/src/elements/ai/mcp-stdio.ts +212 -0
  174. package/src/elements/ai/mcp-transport.ts +70 -0
  175. package/src/elements/ai/runtime.ts +159 -29
  176. package/src/elements/ai.test.ts +139 -0
  177. package/src/elements/ai.ts +16 -0
  178. package/src/elements/clock/declare.ts +26 -1
  179. package/src/elements/clock/health.test.ts +43 -0
  180. package/src/elements/clock/runtime.ts +55 -2
  181. package/src/elements/clock/schedule.ts +71 -142
  182. package/src/elements/clock.test.ts +9 -40
  183. package/src/elements/clock.ts +6 -1
  184. package/src/elements/gate/boot.ts +5 -5
  185. package/src/elements/gate/declare.ts +36 -4
  186. package/src/elements/gate/flatten.ts +1 -1
  187. package/src/elements/gate/runtime.ts +1 -1
  188. package/src/elements/index.ts +2 -0
  189. package/src/elements/signal/declare.ts +2 -1
  190. package/src/elements/signal/runtime.ts +16 -1
  191. package/src/elements/signal.ts +1 -0
  192. package/src/elements/store/cache.test.ts +2 -0
  193. package/src/elements/store/cache.ts +3 -3
  194. package/src/elements/store/declare.ts +9 -6
  195. package/src/elements/store/emit-drizzle.ts +44 -8
  196. package/src/elements/store/resource.test.ts +1 -1
  197. package/src/elements/store/rls-identity.test.ts +95 -0
  198. package/src/elements/store/rls-identity.ts +116 -0
  199. package/src/elements/store/runtime.ts +4 -0
  200. package/src/elements/store/schema-decl.test.ts +37 -0
  201. package/src/elements/store/schema-decl.ts +161 -0
  202. package/src/elements/store/seed.ts +1 -1
  203. package/src/elements/store/sql-condition.test.ts +12 -0
  204. package/src/elements/store/sql-condition.ts +26 -2
  205. package/src/elements/store/sql-rls-isolation.test.ts +165 -0
  206. package/src/elements/store/sql-rls-stamp.test.ts +117 -0
  207. package/src/elements/store/sql-session.ts +81 -3
  208. package/src/elements/store/upsert-app.test.ts +1 -1
  209. package/src/elements/store.ts +14 -0
  210. package/src/elements/vault/builtin-adapter.ts +17 -0
  211. package/src/full.ts +2 -0
  212. package/src/http.ts +20 -3
  213. package/src/index.ts +14 -1
  214. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  215. package/src/kernel/adopt-routes.ts +30 -2
  216. package/src/kernel/app.ts +227 -77
  217. package/src/kernel/auto-cache.test.ts +8 -8
  218. package/src/kernel/auto-registry.test.ts +54 -9
  219. package/src/kernel/boot-bind/ai.ts +24 -0
  220. package/src/kernel/boot-bind/clock.ts +2 -0
  221. package/src/kernel/boot.test.ts +4 -4
  222. package/src/kernel/boot.ts +5 -3
  223. package/src/kernel/effects-stamping.test.ts +3 -3
  224. package/src/kernel/element-registries.ts +15 -5
  225. package/src/kernel/errors.ts +24 -0
  226. package/src/kernel/flow-units.ts +64 -0
  227. package/src/kernel/flow.ts +29 -17
  228. package/src/kernel/fx-dead-letters.test.ts +77 -0
  229. package/src/kernel/fx.test.ts +22 -0
  230. package/src/kernel/fx.ts +135 -6
  231. package/src/kernel/horizontal-child.ts +4 -4
  232. package/src/kernel/http-path-pending.ts +20 -0
  233. package/src/kernel/http-resource.ts +4 -0
  234. package/src/kernel/http-stream.test.ts +174 -0
  235. package/src/kernel/index.ts +2 -0
  236. package/src/kernel/pipeline.test.ts +2 -2
  237. package/src/kernel/plugin-elements.test.ts +1 -1
  238. package/src/kernel/plugin-needs.test.ts +1 -1
  239. package/src/kernel/ready.test.ts +2 -2
  240. package/src/kernel/resource-mount.test.ts +14 -0
  241. package/src/kernel/run-duration.test.ts +2 -2
  242. package/src/kernel/stamp-http.test.ts +79 -0
  243. package/src/kernel/stamp-http.ts +75 -0
  244. package/src/kernel/triggers.ts +52 -27
  245. package/src/manifest/mcp-ref.ts +88 -0
  246. package/src/manifest/types.ts +41 -2
  247. package/src/manifest/validate.test.ts +20 -0
  248. package/src/mcp/docs-server.ts +1 -1
  249. package/src/mcp/server.ts +1 -1
  250. package/src/plugins/compression.test.ts +21 -0
  251. package/src/plugins/compression.ts +1 -0
  252. package/src/release/measure.ts +2 -2
  253. package/src/runtime/bun.ts +41 -4
  254. package/src/runtime/json-code-block.test.ts +2 -2
  255. package/src/runtime/serve.test.ts +4 -4
  256. package/src/term.test.ts +52 -4
  257. package/src/term.ts +125 -17
  258. package/src/test/reset-element-registries.ts +10 -3
  259. package/src/cli/ask-seed.test.ts +0 -96
  260. package/src/cli/ask-seed.ts +0 -82
  261. package/src/console/ui-next/dist/assets/cache-glyph-CLPBqZeb.js +0 -1
  262. package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
  263. package/src/console/ui-next/dist/assets/flows-page-Dg8CTE29.js +0 -1
  264. package/src/console/ui-next/dist/assets/http-method-BJ92Z_ke.js +0 -1
  265. package/src/console/ui-next/dist/assets/index-Bp-R7jtM.js +0 -66
  266. package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
  267. package/src/console/ui-next/dist/assets/observability-page-DAnpEaq1.js +0 -4
  268. package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
  269. package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
  270. package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
  271. package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
  272. package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
  273. package/src/console/ui-next/dist/assets/skeleton-CL_X0GCj.js +0 -1
  274. package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
  275. package/src/console/ui-next/dist/assets/trace-detail-sheet-D16lWQMt.js +0 -2
  276. package/src/console/ui-next/dist/assets/units-page-BtQ0bqMe.js +0 -1
  277. package/src/console/ui-next/dist/assets/vault-page-Ca-MvcmJ.js +0 -2
package/src/term.test.ts CHANGED
@@ -6,6 +6,7 @@ import { describe, expect, test } from "bun:test";
6
6
  import {
7
7
  formatAppReadyLine,
8
8
  formatBootWarn,
9
+ formatBoundSurfaces,
9
10
  formatClaimNote,
10
11
  formatDevNote,
11
12
  formatCliChrome,
@@ -14,6 +15,7 @@ import {
14
15
  formatDevLogSeparator,
15
16
  formatOkeWordmark,
16
17
  formatServiceLine,
18
+ formatSurfaceBox,
17
19
  formatRequestLine,
18
20
  formatStackSummary,
19
21
  countTermLines,
@@ -61,8 +63,8 @@ describe("term", () => {
61
63
  expect(out).toContain("elements");
62
64
  expect(out).toContain("store");
63
65
  expect(out).toContain("postgres");
64
- expect(out).toContain("● flow");
65
- expect(out).toContain("● ai");
66
+ expect(out).toContain("● flow");
67
+ expect(out).toContain("● ai");
66
68
  expect(out).toContain("Starting");
67
69
  expect(out).not.toContain("Ready");
68
70
  expect(out).not.toContain("on(Trigger)");
@@ -134,6 +136,7 @@ describe("term", () => {
134
136
  appUrl: "http://127.0.0.1:6530",
135
137
  consoleUrl: "http://127.0.0.1:6533",
136
138
  mcpUrl: "http://127.0.0.1:6535",
139
+ docsMcpUrl: "http://127.0.0.1:6536",
137
140
  profile: "local",
138
141
  runtimeEnv: "local",
139
142
  system: "darwin 25.4.0 · bun 1.3.14",
@@ -145,11 +148,38 @@ describe("term", () => {
145
148
  expect(out).toContain("Backend");
146
149
  expect(out).toContain("Console");
147
150
  expect(out).toContain("MCP");
151
+ expect(out).toContain("Docs MCP");
148
152
  expect(out).toContain("http://127.0.0.1:6530");
153
+ expect(out).toContain("Your flows, served from the Manifest.");
149
154
  expect(out).toContain("└");
150
155
  expect(out).toContain("Logs");
151
156
  });
152
157
 
158
+ test("formatSurfaceBox prints url and purpose", () => {
159
+ const out = formatSurfaceBox({
160
+ label: "Backend",
161
+ url: "http://127.0.0.1:6530",
162
+ color: false,
163
+ });
164
+ expect(out).toContain("Backend");
165
+ expect(out).toContain("http://127.0.0.1:6530");
166
+ expect(out).toContain("Your flows, served from the Manifest.");
167
+ expect(out).toMatch(/● {2}Backend\n│ {2}http:\/\/127\.0\.0\.1:6530\n│ {2}Your flows/);
168
+ });
169
+
170
+ test("formatBoundSurfaces skips unbound MCP and Docs MCP", () => {
171
+ const out = formatBoundSurfaces({
172
+ appUrl: "http://127.0.0.1:6530",
173
+ consoleUrl: "http://127.0.0.1:6533",
174
+ color: false,
175
+ });
176
+ expect(out).toContain("Backend");
177
+ expect(out).toContain("Console");
178
+ expect(out).toContain("Panels, traces, and the effect graph.");
179
+ expect(out).not.toContain("Docs MCP");
180
+ expect(out).not.toContain("The same Manifest, for agents.");
181
+ });
182
+
153
183
  test("formatDevLogSeparator closes hero and titles Logs", () => {
154
184
  const out = formatDevLogSeparator(false);
155
185
  expect(out).toContain("│");
@@ -280,10 +310,28 @@ describe("term", () => {
280
310
  expect(out).toContain("Docker");
281
311
  expect(out).toContain(":15975");
282
312
  expect(out).toContain("gemma4:e4b-q4_K_M");
283
- expect(out).toContain("● postgres");
284
- expect(out).toContain("● ai");
313
+ expect(out).toContain("● postgres");
314
+ expect(out).toContain("● ai");
285
315
  // Blank │ row separates the Docker header from whatever sits above.
286
316
  expect(out).toMatch(/│\n◇ {2}Docker/);
287
317
  expect(out).not.toMatch(/\u001b\[/);
288
318
  });
319
+
320
+ test("formatStackSummary aligns name and port columns", () => {
321
+ const out = formatStackSummary({
322
+ project: "oke-dev-a3f791",
323
+ services: [
324
+ { label: "ai", hostPort: 23850, status: "ready" },
325
+ { label: "store.index", hostPort: 8550, status: "ready" },
326
+ { label: "postgres", hostPort: 15850, status: "ready" },
327
+ ],
328
+ color: false,
329
+ });
330
+ const portCols = out
331
+ .split("\n")
332
+ .filter((line) => /●\s+\S/.test(line) && line.includes(":"))
333
+ .map((line) => line.search(/:\d/));
334
+ expect(portCols.length).toBe(3);
335
+ expect(new Set(portCols).size).toBe(1);
336
+ });
289
337
  });
package/src/term.ts CHANGED
@@ -112,6 +112,32 @@ export type DevHeroElement = {
112
112
  readonly status?: DevStatus;
113
113
  };
114
114
 
115
+ /** Name column for Ready / Docker / bind rows — fits `store.index`. */
116
+ const HERO_NAME_MIN = 12;
117
+
118
+ /**
119
+ * Widest name column that still fits every label (never below {@link HERO_NAME_MIN}).
120
+ *
121
+ * @param names - Row labels in the table
122
+ */
123
+ function heroNameWidth(names: readonly string[]): number {
124
+ let width = HERO_NAME_MIN;
125
+ for (const name of names) {
126
+ if (name.length > width) width = name.length;
127
+ }
128
+ return width;
129
+ }
130
+
131
+ /**
132
+ * Left-align a table cell.
133
+ *
134
+ * @param value - Cell text
135
+ * @param width - Column width
136
+ */
137
+ function padHeroCol(value: string, width: number): string {
138
+ return value.padEnd(width);
139
+ }
140
+
115
141
  /** Shared options for the `oke dev` hero / banner. */
116
142
  export type DevHeroMeta = {
117
143
  /** `local` · `docker` · `test` · `production` */
@@ -158,18 +184,24 @@ export function formatOkeWordmark(color: boolean = termColorEnabled()): string {
158
184
  export function formatDevHeroDetails(options: DevHeroMeta = {}): string {
159
185
  const s = termStyle(options.color ?? termColorEnabled());
160
186
  const bar = `${s.dim}│${s.reset}`;
161
- const label = (name: string) => `${s.dim}${name.padEnd(9)}${s.reset}`;
187
+ const elements = options.elements ?? [];
188
+ const nameWidth = heroNameWidth([
189
+ ...(options.profile ? ["profile"] : []),
190
+ ...(options.runtimeEnv ? ["env"] : []),
191
+ ...(options.system ? ["system"] : []),
192
+ ...elements.map((row) => row.element),
193
+ ]);
194
+ const label = (name: string) => `${s.dim}${padHeroCol(name, nameWidth)}${s.reset}`;
162
195
  const lines: string[] = [];
163
196
  if (options.profile) {
164
- lines.push(`${bar} ${label("profile")} ${s.cyan}${options.profile}${s.reset}`);
197
+ lines.push(`${bar} ${label("profile")} ${s.cyan}${options.profile}${s.reset}`);
165
198
  }
166
199
  if (options.runtimeEnv) {
167
- lines.push(`${bar} ${label("env")} ${s.cyan}${options.runtimeEnv}${s.reset}`);
200
+ lines.push(`${bar} ${label("env")} ${s.cyan}${options.runtimeEnv}${s.reset}`);
168
201
  }
169
202
  if (options.system) {
170
- lines.push(`${bar} ${label("system")} ${s.dim}${options.system}${s.reset}`);
203
+ lines.push(`${bar} ${label("system")} ${s.dim}${options.system}${s.reset}`);
171
204
  }
172
- const elements = options.elements ?? [];
173
205
  if (elements.length > 0) {
174
206
  lines.push(`${bar} ${s.dim}elements${s.reset}`);
175
207
  const color = options.color ?? termColorEnabled();
@@ -182,8 +214,10 @@ export function formatDevHeroDetails(options: DevHeroMeta = {}): string {
182
214
  ? ""
183
215
  : `${s.dim}—${s.reset}`
184
216
  : `${s.cyan}${row.detail}${s.reset}`;
185
- const detailPart = detail ? ` ${detail}` : "";
186
- lines.push(`${bar} ${dot} ${s.dim}${row.element.padEnd(9)}${s.reset}${detailPart}`);
217
+ const detailPart = detail ? ` ${detail}` : "";
218
+ lines.push(
219
+ `${bar} ${dot} ${s.dim}${padHeroCol(row.element, nameWidth)}${s.reset}${detailPart}`,
220
+ );
187
221
  }
188
222
  }
189
223
  return lines.length > 0 ? `${lines.join("\n")}\n` : "";
@@ -454,7 +488,19 @@ export function createAnchoredBoard(
454
488
  }
455
489
 
456
490
  /**
457
- * One service URL line (Console / MCP after bind).
491
+ * One-line purpose for each `oke dev` bind surface.
492
+ *
493
+ * Same wording as the site PORTS mnemonic — keep them in lockstep.
494
+ */
495
+ export const DEV_SURFACE_DETAILS: Readonly<Record<string, string>> = {
496
+ Backend: "Your flows, served from the Manifest.",
497
+ Console: "Panels, traces, and the effect graph.",
498
+ MCP: "The same Manifest, for agents.",
499
+ "Docs MCP": "Read-only docs search for agents.",
500
+ };
501
+
502
+ /**
503
+ * One service URL line (Console / MCP after bind, before the ready board).
458
504
  *
459
505
  * @param label - Surface name
460
506
  * @param url - Absolute URL
@@ -466,19 +512,73 @@ export function formatServiceLine(
466
512
  color: boolean = termColorEnabled(),
467
513
  ): string {
468
514
  const s = termStyle(color);
469
- const pad = label.padEnd(7);
515
+ const pad = padHeroCol(label, HERO_NAME_MIN);
470
516
  return `${s.dim}│${s.reset} ${s.dim}${pad}${s.reset} ${s.cyan}${url}${s.reset}\n`;
471
517
  }
472
518
 
473
519
  /**
474
520
  * Backend child ready line (`bun --hot` soft reload).
475
521
  *
522
+ * Stays one line so Logs do not fill with surface boxes on every reload.
523
+ *
476
524
  * @param url - Backend base URL
477
525
  * @param color - Color on/off
478
526
  */
479
527
  export function formatAppReadyLine(url: string, color: boolean = termColorEnabled()): string {
480
528
  const s = termStyle(color);
481
- return `${s.green}●${s.reset} ${s.dim}${"Backend".padEnd(7)}${s.reset} ${s.cyan}${url}${s.reset}\n`;
529
+ return `${s.green}●${s.reset} ${s.dim}${padHeroCol("Backend", HERO_NAME_MIN)}${s.reset} ${s.cyan}${url}${s.reset}\n`;
530
+ }
531
+
532
+ /**
533
+ * One bound-surface box on the ready board — title, URL, purpose.
534
+ *
535
+ * @param options - Label, URL, optional detail override
536
+ */
537
+ export function formatSurfaceBox(options: {
538
+ readonly label: string;
539
+ readonly url: string;
540
+ readonly detail?: string;
541
+ readonly color?: boolean;
542
+ }): string {
543
+ const s = termStyle(options.color ?? termColorEnabled());
544
+ const bar = `${s.dim}│${s.reset}`;
545
+ const detail = (options.detail ?? DEV_SURFACE_DETAILS[options.label] ?? "").trim();
546
+ const lines = [
547
+ `${s.green}●${s.reset} ${s.bold}${options.label}${s.reset}`,
548
+ `${bar} ${s.cyan}${options.url}${s.reset}`,
549
+ ];
550
+ if (detail.length > 0) {
551
+ lines.push(`${bar} ${s.dim}${detail}${s.reset}`);
552
+ }
553
+ lines.push(bar);
554
+ return `${lines.join("\n")}\n`;
555
+ }
556
+
557
+ /**
558
+ * Backend · Console · MCP · Docs MCP boxes after bind.
559
+ *
560
+ * Pass `mcpUrl` / `docsMcpUrl` only when that surface came up — omitted
561
+ * URLs skip the box.
562
+ *
563
+ * @param options - Bound surface URLs
564
+ */
565
+ export function formatBoundSurfaces(options: {
566
+ readonly appUrl: string;
567
+ readonly consoleUrl: string;
568
+ readonly mcpUrl?: string | null;
569
+ readonly docsMcpUrl?: string | null;
570
+ readonly color?: boolean;
571
+ }): string {
572
+ const color = options.color ?? termColorEnabled();
573
+ let out = formatSurfaceBox({ label: "Backend", url: options.appUrl, color });
574
+ out += formatSurfaceBox({ label: "Console", url: options.consoleUrl, color });
575
+ if (options.mcpUrl) {
576
+ out += formatSurfaceBox({ label: "MCP", url: options.mcpUrl, color });
577
+ }
578
+ if (options.docsMcpUrl) {
579
+ out += formatSurfaceBox({ label: "Docs MCP", url: options.docsMcpUrl, color });
580
+ }
581
+ return out;
482
582
  }
483
583
 
484
584
  /**
@@ -499,6 +599,7 @@ export function formatDevHero(
499
599
  readonly appUrl: string;
500
600
  readonly consoleUrl: string;
501
601
  readonly mcpUrl: string;
602
+ readonly docsMcpUrl?: string;
502
603
  },
503
604
  ): string {
504
605
  const color = options.color ?? termColorEnabled();
@@ -511,9 +612,13 @@ export function formatDevHero(
511
612
  `\n${bar}\n` +
512
613
  formatDevHeroDetails({ ...options, color }) +
513
614
  `${bar}\n` +
514
- formatAppReadyLine(options.appUrl, color) +
515
- formatServiceLine("Console", options.consoleUrl, color) +
516
- formatServiceLine("MCP", options.mcpUrl, color) +
615
+ formatBoundSurfaces({
616
+ appUrl: options.appUrl,
617
+ consoleUrl: options.consoleUrl,
618
+ mcpUrl: options.mcpUrl,
619
+ docsMcpUrl: options.docsMcpUrl,
620
+ color,
621
+ }) +
517
622
  formatDevLogSeparator(color)
518
623
  );
519
624
  }
@@ -767,25 +872,28 @@ export function formatStackSummary(options: {
767
872
  }): string {
768
873
  const s = termStyle(options.color ?? termColorEnabled());
769
874
  const bar = `${s.dim}│${s.reset}`;
770
- const pad = (label: string) => label.padEnd(8);
875
+ const nameWidth = heroNameWidth(["Docker", "app", ...options.services.map((svc) => svc.label)]);
876
+ const portWidth = Math.max(6, ...options.services.map((svc) => `:${svc.hostPort}`.length));
877
+ const pad = (label: string) => padHeroCol(label, nameWidth);
771
878
  const color = options.color ?? termColorEnabled();
772
879
  const lines: string[] = [
773
880
  `${s.dim}│${s.reset}`,
774
- `${s.green}◇${s.reset} ${s.bold}Docker${s.reset} ${s.cyan}${options.project}${s.reset}`,
881
+ `${s.green}◇${s.reset} ${s.bold}${pad("Docker")}${s.reset} ${s.cyan}${options.project}${s.reset}`,
775
882
  ];
776
883
  for (const svc of options.services) {
777
884
  const detail = svc.detail?.trim();
778
885
  const status = svc.status ?? "pending";
779
886
  const dot = formatStatusDot(status, color);
887
+ const port = `:${svc.hostPort}`.padEnd(portWidth);
780
888
  lines.push(
781
- `${bar} ${dot} ${s.dim}${pad(svc.label)}${s.reset} ${s.cyan}:${svc.hostPort}${s.reset}` +
889
+ `${bar} ${dot} ${s.dim}${pad(svc.label)}${s.reset} ${s.cyan}${port}${s.reset}` +
782
890
  (detail ? ` ${s.dim}${detail}${s.reset}` : ""),
783
891
  );
784
892
  }
785
893
  const drivers = options.appDrivers ?? [];
786
894
  const appDetail = drivers.length > 0 ? `host Bun · ${drivers.join(" + ")}` : "host Bun";
787
895
  const appDot = formatStatusDot("ready", color);
788
- lines.push(`${bar} ${appDot} ${s.dim}${pad("app")}${s.reset} ${s.dim}${appDetail}${s.reset}`);
896
+ lines.push(`${bar} ${appDot} ${s.dim}${pad("app")}${s.reset} ${s.dim}${appDetail}${s.reset}`);
789
897
  lines.push(bar);
790
898
  return `${lines.join("\n")}\n`;
791
899
  }
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Global safety net for the `store.sql` / `store.files` / `vault.secret` /
3
- * `signal()` / `channel.<medium>().template()` / `ai.model`·prompt·embed·agent
4
- * auto-registries (`src/kernel/element-registries.ts`).
2
+ * Global safety net for the `store.*` / `vault.secret` / `signal()` /
3
+ * `clock()` / `gate.policy`·scope·rate / `channel.<medium>().template()` /
4
+ * `ai.model`·prompt·embed·agent auto-registries
5
+ * (`src/kernel/element-registries.ts`).
5
6
  *
6
7
  * Unlike `on()` bindings — created almost exclusively to wire a real app —
7
8
  * these factories are called throughout the suite as bare value constructors,
@@ -19,9 +20,12 @@ import { afterEach } from "bun:test";
19
20
  import {
20
21
  aiAgentRegistry,
21
22
  aiEmbedRegistry,
23
+ aiMcpServerRegistry,
22
24
  aiModelRegistry,
23
25
  aiPromptRegistry,
24
26
  channelTemplateRegistry,
27
+ clockRegistry,
28
+ gateRegistry,
25
29
  requiredEnvRegistry,
26
30
  secretRegistry,
27
31
  signalRegistry,
@@ -33,9 +37,12 @@ afterEach(() => {
33
37
  secretRegistry.length = 0;
34
38
  requiredEnvRegistry.length = 0;
35
39
  signalRegistry.length = 0;
40
+ clockRegistry.length = 0;
41
+ gateRegistry.length = 0;
36
42
  channelTemplateRegistry.length = 0;
37
43
  aiModelRegistry.length = 0;
38
44
  aiPromptRegistry.length = 0;
39
45
  aiEmbedRegistry.length = 0;
40
46
  aiAgentRegistry.length = 0;
47
+ aiMcpServerRegistry.length = 0;
41
48
  });
@@ -1,96 +0,0 @@
1
- /**
2
- * Tests for the one-shot seed prompt after `oke db push`.
3
- */
4
-
5
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
6
- import { tmpdir } from "node:os";
7
- import { join } from "node:path";
8
- import { describe, expect, test } from "bun:test";
9
- import { maybeAskSeed } from "./ask-seed.ts";
10
- import { PROJECT_STATE_REL } from "./project-state.ts";
11
-
12
- describe("maybeAskSeed", () => {
13
- test("skips when non-TTY", async () => {
14
- const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
15
- try {
16
- mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
17
- writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
18
- let seeded = false;
19
- await maybeAskSeed({
20
- cwd: dir,
21
- env: "test",
22
- stdinIsTTY: false,
23
- confirmFn: async () => true,
24
- seedFn: async () => {
25
- seeded = true;
26
- return 0;
27
- },
28
- });
29
- expect(seeded).toBe(false);
30
- } finally {
31
- rmSync(dir, { recursive: true, force: true });
32
- }
33
- });
34
-
35
- test("re-asks when the seed identity changes", async () => {
36
- const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
37
- try {
38
- mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
39
- writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
40
- mkdirSync(join(dir, ".oke"), { recursive: true });
41
- writeFileSync(
42
- join(dir, PROJECT_STATE_REL),
43
- `${JSON.stringify({ seededAt: "2026-01-01T00:00:00.000Z", seed: "notes" }, null, 2)}\n`,
44
- );
45
- let calls = 0;
46
- await maybeAskSeed({
47
- cwd: dir,
48
- env: "test",
49
- stdinIsTTY: true,
50
- confirmFn: async () => true,
51
- seedFn: async () => {
52
- calls += 1;
53
- return 0;
54
- },
55
- });
56
- expect(calls).toBe(1);
57
- } finally {
58
- rmSync(dir, { recursive: true, force: true });
59
- }
60
- });
61
-
62
- test("asks once then writes .oke/state.json seededAt", async () => {
63
- const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
64
- try {
65
- mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
66
- writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
67
- let calls = 0;
68
- await maybeAskSeed({
69
- cwd: dir,
70
- env: "test",
71
- stdinIsTTY: true,
72
- confirmFn: async () => true,
73
- seedFn: async () => {
74
- calls += 1;
75
- return 0;
76
- },
77
- });
78
- expect(calls).toBe(1);
79
- expect(existsSync(join(dir, PROJECT_STATE_REL))).toBe(true);
80
-
81
- await maybeAskSeed({
82
- cwd: dir,
83
- env: "test",
84
- stdinIsTTY: true,
85
- confirmFn: async () => true,
86
- seedFn: async () => {
87
- calls += 1;
88
- return 0;
89
- },
90
- });
91
- expect(calls).toBe(1);
92
- } finally {
93
- rmSync(dir, { recursive: true, force: true });
94
- }
95
- });
96
- });
@@ -1,82 +0,0 @@
1
- /**
2
- * One-time interactive prompt: run `oke db seed` after first successful push.
3
- */
4
-
5
- import { confirm, isCancel } from "@clack/prompts";
6
- import type { ConfigEnv } from "../config/index.ts";
7
- import { resolveSeedIdentity, seedPromptMessage } from "../elements/store/seed.ts";
8
- import { formatCliChrome } from "../term.ts";
9
- import { loadSeedDef, resolveSeedModulePath, runSeed } from "./db-seed.ts";
10
- import { isProjectSeeded, markProjectSeeded } from "./project-state.ts";
11
-
12
- /** @deprecated Use `.oke/state.json` `seededAt` via {@link isProjectSeeded}. */
13
- export { LEGACY_SEEDED_MARKER as SEEDED_MARKER } from "./project-state.ts";
14
-
15
- /** Options for {@link maybeAskSeed}. */
16
- export interface AskSeedOptions {
17
- readonly cwd: string;
18
- readonly env: ConfigEnv;
19
- readonly write?: (text: string) => void;
20
- readonly stdinIsTTY?: boolean;
21
- /** Skip prompt (tests / `--yes` / non-interactive). */
22
- readonly skip?: boolean;
23
- /** Injectable confirm (tests). */
24
- readonly confirmFn?: () => Promise<boolean>;
25
- /** Injectable seed runner (tests). */
26
- readonly seedFn?: (cwd: string, env: ConfigEnv) => Promise<number>;
27
- }
28
-
29
- /**
30
- * After a successful `oke db push`, ask once whether to seed this app
31
- * (`defineSeed({ name })` or the project folder). Skips when non-TTY,
32
- * already seeded for that identity, or no seed module.
33
- *
34
- * @param options - Project / env / injectables
35
- */
36
- export async function maybeAskSeed(options: AskSeedOptions): Promise<void> {
37
- if (options.skip) return;
38
- const tty = options.stdinIsTTY ?? Boolean(process.stdin.isTTY);
39
- if (!tty) return;
40
-
41
- const cwd = options.cwd;
42
- const seedPath = await resolveSeedModulePath(cwd);
43
- if (!(await Bun.file(seedPath).exists())) return;
44
-
45
- const def = await loadSeedDef(cwd).catch(() => undefined);
46
- const identity = resolveSeedIdentity(def, cwd);
47
- if (await isProjectSeeded(cwd, identity.name)) return;
48
-
49
- const write = options.write ?? ((t) => process.stdout.write(t));
50
- const chromeWrite = (t: string) => write(formatCliChrome(t));
51
- const ok =
52
- options.confirmFn !== undefined
53
- ? await options.confirmFn()
54
- : await (async () => {
55
- const value = await confirm({
56
- message: seedPromptMessage(identity),
57
- initialValue: true,
58
- });
59
- if (isCancel(value)) return false;
60
- return Boolean(value);
61
- })();
62
-
63
- if (!ok) {
64
- chromeWrite("oke db seed: skipped (run `oke db seed` later)\n");
65
- return;
66
- }
67
-
68
- const seed =
69
- options.seedFn ??
70
- ((projectCwd, env) =>
71
- runSeed({
72
- cwd: projectCwd,
73
- env,
74
- write: chromeWrite,
75
- force: true,
76
- stdinIsTTY: false,
77
- }));
78
- const code = await seed(cwd, options.env);
79
- if (code === 0) {
80
- await markProjectSeeded(cwd, { seed: identity.name });
81
- }
82
- }
@@ -1 +0,0 @@
1
- import{Cr as e,F as t,L as n,P as r,wr as i}from"./shortcut-keys-DO4IsVqv.js";import{A as a,f as o}from"./xyflow-D7n4g6go.js";import{t as s}from"./dagre.esm-ZwcdTuZZ.js";import{C as c,S as l,_ as u,o as d,v as f}from"./http-method-BJ92Z_ke.js";function p(e){let t=e.indexOf(`.`);return t===-1?e:e.slice(0,t)}function m(e){let t=e.indexOf(`.`);return t===-1?e:e.slice(t+1)}function h(e){let t=e?.flows??{},n=new Map;for(let e of Object.keys(t)){let r=t[e]?.effects?.calls??[];for(let t of r){let r=n.get(t)??[];r.push(e),n.set(t,r)}}for(let[e,t]of n)t.sort((e,t)=>e.localeCompare(t)),n.set(e,t);return n}function g(e,t){return h(e).get(t)??[]}function _(e){return e.startsWith(`sql:`)?{id:e,kind:`store`,label:e.slice(4),facet:`sql`}:e.startsWith(`kv:`)?{id:e,kind:`store`,label:e.slice(3),facet:`kv`}:e.startsWith(`files:`)?{id:e,kind:`store`,label:e.slice(6),facet:`files`}:e.startsWith(`index:`)?{id:e,kind:`store`,label:e.slice(6),facet:`index`}:e.startsWith(`signal:`)?{id:e,kind:`signal`,label:e.slice(7)}:e.startsWith(`ai:`)?{id:e,kind:`ai`,label:e.slice(3)}:e.startsWith(`vault:`)?{id:e,kind:`vault`,label:e.slice(6)}:e.startsWith(`channel:`)?{id:e,kind:`channel`,label:e.slice(8)}:e.startsWith(`gate:`)?{id:e,kind:`gate`,label:e.slice(5)}:e.startsWith(`clock:`)?{id:e,kind:`clock`,label:e.slice(6)}:null}function v(e,t,n,r,i=!1){let a=u[r];return{id:e,source:t,target:n,type:`smoothstep`,animated:i,data:{kind:r},style:{stroke:a,strokeWidth:r===`calls`?2.5:2.25},pathOptions:{borderRadius:28},zIndex:f.edge,markerEnd:{type:o.ArrowClosed,width:14,height:14,color:a}}}function y(e){let t=e?.flows??{},n=new Set(Object.keys(t)),r=[],i=new Map;for(let e of[...n].sort()){let t=p(e),n=i.get(t)??[];n.push(e),i.set(t,n)}let a=new Map,o=new Map;for(let e of n){let n=t[e];if(!n)continue;if(n.trigger?.signal){let t=`signal:${n.trigger.signal}`,i=_(t);i&&a.set(t,i),r.push(v(`e:${t}->flow:${e}`,t,`flow:${e}`,`trigger`))}if(n.trigger?.cron||n.trigger?.every){let t=`clock:${e}`,i=n.trigger.cron??n.trigger.every??e;a.set(t,{id:t,kind:`clock`,label:i,facet:n.trigger.cron?`cron`:`every`}),r.push(v(`e:${t}->flow:${e}`,t,`flow:${e}`,`trigger`))}for(let t of n.gates??[]){let n=`gate:${t}`;a.set(n,{id:n,kind:`gate`,label:t}),o.set(n,(o.get(n)??0)+1),r.push(v(`e:flow:${e}-gates->${n}`,`flow:${e}`,n,`gates`))}let i=n.effects;if(i){for(let t of i.reads??[]){let n=_(t);n&&(a.set(t,n),o.set(t,(o.get(t)??0)+1),r.push(v(`e:flow:${e}-reads->${t}`,`flow:${e}`,t,`reads`)))}for(let t of i.writes??[]){let n=_(t);n&&(a.set(t,n),o.set(t,(o.get(t)??0)+1),r.push(v(`e:flow:${e}-writes->${t}`,`flow:${e}`,t,`writes`)))}for(let t of i.emits??[]){let n=`signal:${t}`,i=_(n);i&&(a.set(n,i),o.set(n,(o.get(n)??0)+1),r.push(v(`e:flow:${e}->${n}`,`flow:${e}`,n,`emits`)))}for(let t of i.asks??[]){let n=`ai:${t}`,i=_(n);i&&(a.set(n,i),o.set(n,(o.get(n)??0)+1),r.push(v(`e:flow:${e}->${n}`,`flow:${e}`,n,`asks`)))}for(let t of i.sends??[]){let n=`channel:${t}`,i=_(n);i&&(a.set(n,i),o.set(n,(o.get(n)??0)+1),r.push(v(`e:flow:${e}->${n}`,`flow:${e}`,n,`sends`)))}for(let t of i.secrets??[]){let n=`vault:${t}`,i=_(n);i&&(a.set(n,i),o.set(n,(o.get(n)??0)+1),r.push(v(`e:flow:${e}->${n}`,`flow:${e}`,n,`secrets`)))}for(let t of i.calls??[])r.push(v(`e:flow:${e}->flow:${t}`,`flow:${e}`,`flow:${t}`,`calls`,!0))}}return{nodes:b(i,t,a,o),edges:r,flowIds:n}}function b(e,t,n,r){let i=new s.graphlib.Graph({compound:!0});i.setGraph({rankdir:`LR`,align:`UL`,nodesep:16,ranksep:88,edgesep:12,marginx:8,marginy:8}),i.setDefaultEdgeLabel(()=>({}));for(let[t,n]of[...e.entries()].sort()){i.setNode(`unit:${t}`,{});for(let e of n)i.setNode(`flow:${e}`,{...l.flow}),i.setParent(`flow:${e}`,`unit:${t}`)}for(let e of n.values())i.setNode(e.id,{...l[e.kind]});let a=new Set;for(let n of e.values())for(let e of n){let n=t[e];if(!n)continue;let r=(e,t)=>{let n=`${e}->${t}`;a.has(n)||!i.hasNode(e)||!i.hasNode(t)||(a.add(n),i.setEdge(e,t))};n.trigger?.signal&&r(`signal:${n.trigger.signal}`,`flow:${e}`),(n.trigger?.cron||n.trigger?.every)&&r(`clock:${e}`,`flow:${e}`);for(let t of n.gates??[])r(`flow:${e}`,`gate:${t}`);let o=n.effects;if(o){for(let t of o.reads??[])r(`flow:${e}`,t);for(let t of o.writes??[])r(`flow:${e}`,t);for(let t of o.emits??[])r(`flow:${e}`,`signal:${t}`);for(let t of o.asks??[])r(`flow:${e}`,`ai:${t}`);for(let t of o.sends??[])r(`flow:${e}`,`channel:${t}`);for(let t of o.secrets??[])r(`flow:${e}`,`vault:${t}`);for(let t of o.calls??[])r(`flow:${e}`,`flow:${t}`)}}s.layout(i);let o=[],{headerH:u,padX:d,padBottom:p}=c;for(let[n,r]of[...e.entries()].sort()){let e=`unit:${n}`,a=i.node(e);if(!a)continue;let s=a.x??0,c=a.y??0,h=r.length*l.flow.height+Math.max(0,r.length-1)*10,g=l.flow.width+d*2,_=u+h+p,v=s-g/2,y=c-_/2;o.push({id:e,type:`unit`,position:{x:v,y},data:{kind:`unit`,label:n,refId:e,badge:String(r.length)},selectable:!1,draggable:!1,zIndex:f.unit,width:g,height:_,style:{width:g,height:_}}),r.forEach((r,i)=>{let a=t[r];a&&o.push({id:`flow:${r}`,type:`flow`,position:{x:d,y:u+i*(l.flow.height+10)},parentId:e,extent:`parent`,zIndex:f.leaf,data:{kind:`flow`,label:m(r),refId:r,unit:n,plane:a.plane??`user`,badge:a.plane??`user`},draggable:!1,width:l.flow.width,height:l.flow.height,style:{width:l.flow.width,height:l.flow.height}})})}let h=[...n.values()].sort((e,t)=>e.kind.localeCompare(t.kind)||e.label.localeCompare(t.label));for(let e of h){let t=i.node(e.id),n=l[e.kind],a=r.get(e.id)??0;o.push({id:e.id,type:e.kind,position:{x:(t?.x??0)-n.width/2,y:(t?.y??0)-n.height/2},data:{kind:e.kind,label:e.label,refId:e.id,...e.facet===void 0?{}:{facet:e.facet},badge:e.facet??(a>0?String(a):e.kind)},draggable:!1,zIndex:f.leaf,width:n.width,height:n.height,style:{width:n.width,height:n.height}})}return o}function x(e,t,n,r={}){let i=t.size>0||n.size>0;return e.map(e=>{if(e.data.kind===`unit`||e.data.kind===`law`)return e;let a=e.data.kind===`flow`?t.has(e.data.refId):n.has(e.id);return{...e,data:{...e.data,highlighted:a,dimmed:i&&!a,active:r.activeNodeId!=null&&e.id===r.activeNodeId,focused:r.focusedNodeId!=null&&e.id===r.focusedNodeId}}})}function S(e,t){let n=new Map(t.map(e=>[e.id,e]));return t.some(e=>e.data.highlighted===!0)?e.map(e=>{let t=n.get(e.source),r=n.get(e.target),i=t?.data.highlighted===!0&&r?.data.highlighted===!0,a=u[e.data?.kind??`reads`];return{...e,animated:i?!0:e.animated,style:{...e.style,stroke:a,opacity:i?1:.32,strokeWidth:i?2.75:1.75},markerEnd:e.markerEnd&&typeof e.markerEnd==`object`?{...e.markerEnd,color:a}:e.markerEnd}}):e.map(e=>({...e,style:{...e.style,opacity:1}}))}var C=100;function w(e,t){return e.filter(e=>t.has(e.flow)).sort((e,t)=>t.startedAt-e.startedAt).slice(0,C)}var T=a();function E({cache:a,dataSlot:o}){let s=d(a);return(0,T.jsxs)(r,{children:[(0,T.jsx)(n,{render:t=>(0,T.jsx)(`span`,{...t,className:e(`flex w-4 shrink-0 items-center justify-center`,s.className),"data-slot":o,"data-cache":a,"aria-label":s.label,children:(0,T.jsx)(i,{icon:s.icon,className:`size-3`,"aria-hidden":!0})})}),(0,T.jsx)(t,{side:`top`,children:s.label})]})}export{S as a,p as c,x as i,w as n,y as o,m as r,g as s,E as t};