okengine 0.1.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.
Files changed (674) hide show
  1. package/AGENTS.md +91 -0
  2. package/README.md +327 -0
  3. package/docs/spec/complete-example.md +1188 -0
  4. package/docs/spec/console.md +739 -0
  5. package/docs/spec/four-applications.md +1188 -0
  6. package/docs/spec/unified-theory.md +489 -0
  7. package/package.json +152 -0
  8. package/spec/manifest.v1.schema.json +624 -0
  9. package/src/auth/api-keys.ts +168 -0
  10. package/src/auth/attenuation.ts +116 -0
  11. package/src/auth/auth.test.ts +273 -0
  12. package/src/auth/cross-plane.ts +98 -0
  13. package/src/auth/index.ts +112 -0
  14. package/src/auth/invites.ts +66 -0
  15. package/src/auth/invoke-as.ts +49 -0
  16. package/src/auth/operator.ts +178 -0
  17. package/src/auth/planes.ts +102 -0
  18. package/src/auth/plugin.ts +44 -0
  19. package/src/auth/roles.ts +84 -0
  20. package/src/auth/sessions.ts +418 -0
  21. package/src/auth/tables.ts +131 -0
  22. package/src/cli/args.ts +50 -0
  23. package/src/cli/branch.ts +75 -0
  24. package/src/cli/build.ts +86 -0
  25. package/src/cli/client-add.test.ts +140 -0
  26. package/src/cli/client-add.ts +164 -0
  27. package/src/cli/completion.test.ts +58 -0
  28. package/src/cli/completion.ts +296 -0
  29. package/src/cli/dev-app-runner.ts +77 -0
  30. package/src/cli/dev.test.ts +435 -0
  31. package/src/cli/dev.ts +594 -0
  32. package/src/cli/doc-drift.test.ts +146 -0
  33. package/src/cli/doc-drift.ts +397 -0
  34. package/src/cli/docker-cli.test.ts +221 -0
  35. package/src/cli/docker.ts +127 -0
  36. package/src/cli/doctor-diff-examples.ts +92 -0
  37. package/src/cli/doctor-diff.test.ts +91 -0
  38. package/src/cli/doctor-diff.ts +274 -0
  39. package/src/cli/doctor-pii.test.ts +98 -0
  40. package/src/cli/doctor-pii.ts +151 -0
  41. package/src/cli/doctor.test.ts +150 -0
  42. package/src/cli/doctor.ts +223 -0
  43. package/src/cli/eval.ts +122 -0
  44. package/src/cli/exit.ts +19 -0
  45. package/src/cli/gates-list.test.ts +47 -0
  46. package/src/cli/gates-list.ts +84 -0
  47. package/src/cli/images.ts +277 -0
  48. package/src/cli/index.ts +99 -0
  49. package/src/cli/json-out.test.ts +129 -0
  50. package/src/cli/load-config.ts +71 -0
  51. package/src/cli/mcp-from-console.ts +50 -0
  52. package/src/cli/privacy-erase.ts +73 -0
  53. package/src/cli/registry.ts +455 -0
  54. package/src/cli/safe-defaults.test.ts +92 -0
  55. package/src/cli/schema.ts +196 -0
  56. package/src/cli/stack.ts +82 -0
  57. package/src/cli/start.ts +106 -0
  58. package/src/cli/upgrade.ts +105 -0
  59. package/src/cli/vault-cmd.ts +140 -0
  60. package/src/client/budget-entry.ts +34 -0
  61. package/src/client/budget.test.ts +17 -0
  62. package/src/client/create.test.ts +159 -0
  63. package/src/client/create.ts +152 -0
  64. package/src/client/errors.ts +60 -0
  65. package/src/client/index.ts +35 -0
  66. package/src/client/notes-contract.test.ts +289 -0
  67. package/src/client/transport.test.ts +123 -0
  68. package/src/client/transport.ts +254 -0
  69. package/src/client/types.ts +282 -0
  70. package/src/compiler/aot.test.ts +248 -0
  71. package/src/compiler/aot.ts +182 -0
  72. package/src/compiler/differential.test.ts +241 -0
  73. package/src/compiler/dynamic.ts +54 -0
  74. package/src/compiler/effects-infer.ts +541 -0
  75. package/src/compiler/emit.ts +40 -0
  76. package/src/compiler/extract.test.ts +177 -0
  77. package/src/compiler/extract.ts +1368 -0
  78. package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
  79. package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
  80. package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
  81. package/src/compiler/fixtures/skyport/src/app.ts +10 -0
  82. package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
  83. package/src/compiler/fixtures/skyport/src/core.ts +3 -0
  84. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
  85. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
  86. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
  87. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
  88. package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
  89. package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
  90. package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
  91. package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
  92. package/src/compiler/fixtures/skyport.expected.json +241 -0
  93. package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
  94. package/src/compiler/http-parse.ts +214 -0
  95. package/src/compiler/index.ts +67 -0
  96. package/src/compiler/response.ts +89 -0
  97. package/src/compiler/sucrose.ts +238 -0
  98. package/src/config/index.ts +167 -0
  99. package/src/console/budget.test.ts +73 -0
  100. package/src/console/index.ts +30 -0
  101. package/src/console/server/access.test.ts +370 -0
  102. package/src/console/server/access.ts +777 -0
  103. package/src/console/server/ai.test.ts +172 -0
  104. package/src/console/server/ai.ts +632 -0
  105. package/src/console/server/app.ts +294 -0
  106. package/src/console/server/auth-rate.test.ts +81 -0
  107. package/src/console/server/auth-rate.ts +72 -0
  108. package/src/console/server/bind.ts +25 -0
  109. package/src/console/server/channels.test.ts +179 -0
  110. package/src/console/server/channels.ts +469 -0
  111. package/src/console/server/claim.test.ts +66 -0
  112. package/src/console/server/claim.ts +134 -0
  113. package/src/console/server/clock.test.ts +239 -0
  114. package/src/console/server/clock.ts +390 -0
  115. package/src/console/server/console.test.ts +338 -0
  116. package/src/console/server/diff.test.ts +299 -0
  117. package/src/console/server/diff.ts +352 -0
  118. package/src/console/server/dry-run.audit.test.ts +118 -0
  119. package/src/console/server/flows-invoke.test.ts +148 -0
  120. package/src/console/server/flows.ts +3633 -0
  121. package/src/console/server/gates.test.ts +360 -0
  122. package/src/console/server/gates.ts +869 -0
  123. package/src/console/server/index.ts +141 -0
  124. package/src/console/server/live.test.ts +30 -0
  125. package/src/console/server/live.ts +109 -0
  126. package/src/console/server/plugin.ts +50 -0
  127. package/src/console/server/plugins.test.ts +131 -0
  128. package/src/console/server/plugins.ts +429 -0
  129. package/src/console/server/runs-pii.ts +98 -0
  130. package/src/console/server/security-headers.ts +97 -0
  131. package/src/console/server/security.gate.test.ts +286 -0
  132. package/src/console/server/serve.ts +319 -0
  133. package/src/console/server/signals.test.ts +88 -0
  134. package/src/console/server/signals.ts +282 -0
  135. package/src/console/server/state.ts +982 -0
  136. package/src/console/server/store.test.ts +233 -0
  137. package/src/console/server/store.ts +861 -0
  138. package/src/console/server/structural.ts +95 -0
  139. package/src/console/server/vault.test.ts +251 -0
  140. package/src/console/server/vault.ts +430 -0
  141. package/src/console/ui/access/AccessA11yView.tsx +237 -0
  142. package/src/console/ui/access/a11y.test.tsx +122 -0
  143. package/src/console/ui/access/acknowledgement.test.ts +13 -0
  144. package/src/console/ui/access/acknowledgement.ts +35 -0
  145. package/src/console/ui/access/blast-radius.test.ts +26 -0
  146. package/src/console/ui/access/blast-radius.ts +40 -0
  147. package/src/console/ui/access/confirmation.test.ts +38 -0
  148. package/src/console/ui/access/confirmation.ts +36 -0
  149. package/src/console/ui/access/fixture.ts +203 -0
  150. package/src/console/ui/access/grantable.test.ts +17 -0
  151. package/src/console/ui/access/grantable.ts +36 -0
  152. package/src/console/ui/access/hygiene.test.ts +12 -0
  153. package/src/console/ui/access/hygiene.ts +39 -0
  154. package/src/console/ui/access/index.ts +54 -0
  155. package/src/console/ui/access/provenance.test.ts +12 -0
  156. package/src/console/ui/access/provenance.ts +29 -0
  157. package/src/console/ui/access/search.test.ts +17 -0
  158. package/src/console/ui/access/search.ts +72 -0
  159. package/src/console/ui/access/types.ts +111 -0
  160. package/src/console/ui/ai/AiA11yView.tsx +265 -0
  161. package/src/console/ui/ai/a11y.test.tsx +99 -0
  162. package/src/console/ui/ai/findings.ts +41 -0
  163. package/src/console/ui/ai/fixture.ts +182 -0
  164. package/src/console/ui/ai/format.ts +90 -0
  165. package/src/console/ui/ai/group.test.ts +37 -0
  166. package/src/console/ui/ai/group.ts +88 -0
  167. package/src/console/ui/ai/index.ts +66 -0
  168. package/src/console/ui/ai/promotion.test.ts +77 -0
  169. package/src/console/ui/ai/promotion.ts +150 -0
  170. package/src/console/ui/ai/search.test.ts +40 -0
  171. package/src/console/ui/ai/search.ts +89 -0
  172. package/src/console/ui/ai/types.ts +139 -0
  173. package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
  174. package/src/console/ui/architecture/a11y.test.tsx +97 -0
  175. package/src/console/ui/architecture/boundary.ts +42 -0
  176. package/src/console/ui/architecture/declared.ts +165 -0
  177. package/src/console/ui/architecture/fixture.ts +74 -0
  178. package/src/console/ui/architecture/index.ts +84 -0
  179. package/src/console/ui/architecture/layers.ts +64 -0
  180. package/src/console/ui/architecture/layout.ts +68 -0
  181. package/src/console/ui/architecture/pathologies.ts +260 -0
  182. package/src/console/ui/architecture/search.ts +145 -0
  183. package/src/console/ui/architecture/traffic.ts +102 -0
  184. package/src/console/ui/architecture/types.ts +144 -0
  185. package/src/console/ui/architecture/view.test.ts +165 -0
  186. package/src/console/ui/architecture/view.ts +516 -0
  187. package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
  188. package/src/console/ui/channels/a11y.test.tsx +96 -0
  189. package/src/console/ui/channels/confirmation.test.ts +37 -0
  190. package/src/console/ui/channels/confirmation.ts +27 -0
  191. package/src/console/ui/channels/fallback.test.ts +15 -0
  192. package/src/console/ui/channels/fallback.ts +20 -0
  193. package/src/console/ui/channels/findings.ts +38 -0
  194. package/src/console/ui/channels/fixture.ts +134 -0
  195. package/src/console/ui/channels/group.ts +25 -0
  196. package/src/console/ui/channels/index.ts +57 -0
  197. package/src/console/ui/channels/locale.test.ts +17 -0
  198. package/src/console/ui/channels/locale.ts +31 -0
  199. package/src/console/ui/channels/mask.test.ts +14 -0
  200. package/src/console/ui/channels/mask.ts +12 -0
  201. package/src/console/ui/channels/search.ts +58 -0
  202. package/src/console/ui/channels/taxonomy.test.ts +17 -0
  203. package/src/console/ui/channels/taxonomy.ts +49 -0
  204. package/src/console/ui/channels/types.ts +117 -0
  205. package/src/console/ui/clock/ClockA11yView.tsx +182 -0
  206. package/src/console/ui/clock/a11y.test.tsx +99 -0
  207. package/src/console/ui/clock/confirmation.test.ts +32 -0
  208. package/src/console/ui/clock/confirmation.ts +34 -0
  209. package/src/console/ui/clock/findings.ts +40 -0
  210. package/src/console/ui/clock/fixture.ts +111 -0
  211. package/src/console/ui/clock/health.test.ts +25 -0
  212. package/src/console/ui/clock/health.ts +61 -0
  213. package/src/console/ui/clock/index.ts +49 -0
  214. package/src/console/ui/clock/search.test.ts +39 -0
  215. package/src/console/ui/clock/search.ts +76 -0
  216. package/src/console/ui/clock/timeline.test.ts +44 -0
  217. package/src/console/ui/clock/timeline.ts +41 -0
  218. package/src/console/ui/clock/types.ts +78 -0
  219. package/src/console/ui/clock/waiting-on.test.ts +34 -0
  220. package/src/console/ui/clock/waiting-on.ts +83 -0
  221. package/src/console/ui/confirmation.unify.test.ts +92 -0
  222. package/src/console/ui/diff/DiffA11yView.tsx +85 -0
  223. package/src/console/ui/diff/a11y.test.tsx +99 -0
  224. package/src/console/ui/diff/fixture.ts +85 -0
  225. package/src/console/ui/diff/group.test.ts +58 -0
  226. package/src/console/ui/diff/group.ts +97 -0
  227. package/src/console/ui/diff/index.ts +38 -0
  228. package/src/console/ui/diff/search.test.ts +32 -0
  229. package/src/console/ui/diff/search.ts +75 -0
  230. package/src/console/ui/diff/summary.ts +74 -0
  231. package/src/console/ui/diff/types.ts +48 -0
  232. package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
  233. package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
  234. package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
  235. package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
  236. package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
  237. package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
  238. package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
  239. package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
  240. package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
  241. package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
  242. package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
  243. package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
  244. package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
  245. package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
  246. package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
  247. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
  248. package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
  249. package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
  250. package/src/console/ui/dist/index.html +14 -0
  251. package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
  252. package/src/console/ui/flows/a11y.test.tsx +105 -0
  253. package/src/console/ui/flows/buffer.test.ts +91 -0
  254. package/src/console/ui/flows/buffer.ts +118 -0
  255. package/src/console/ui/flows/confirmation.ts +131 -0
  256. package/src/console/ui/flows/contract.test.ts +89 -0
  257. package/src/console/ui/flows/contract.ts +353 -0
  258. package/src/console/ui/flows/fixture.ts +81 -0
  259. package/src/console/ui/flows/graph.test.ts +101 -0
  260. package/src/console/ui/flows/graph.ts +549 -0
  261. package/src/console/ui/flows/index.ts +87 -0
  262. package/src/console/ui/flows/save-as-test.test.ts +47 -0
  263. package/src/console/ui/flows/save-as-test.ts +99 -0
  264. package/src/console/ui/flows/search.ts +255 -0
  265. package/src/console/ui/flows/tiers.ts +140 -0
  266. package/src/console/ui/gates/GatesA11yView.tsx +192 -0
  267. package/src/console/ui/gates/a11y.test.tsx +116 -0
  268. package/src/console/ui/gates/audit.test.ts +40 -0
  269. package/src/console/ui/gates/audit.ts +63 -0
  270. package/src/console/ui/gates/denial.test.ts +36 -0
  271. package/src/console/ui/gates/denial.ts +51 -0
  272. package/src/console/ui/gates/findings.ts +19 -0
  273. package/src/console/ui/gates/fixture.ts +189 -0
  274. package/src/console/ui/gates/group.test.ts +30 -0
  275. package/src/console/ui/gates/group.ts +102 -0
  276. package/src/console/ui/gates/index.ts +47 -0
  277. package/src/console/ui/gates/search.test.ts +49 -0
  278. package/src/console/ui/gates/search.ts +115 -0
  279. package/src/console/ui/gates/types.ts +121 -0
  280. package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
  281. package/src/console/ui/overview/a11y.test.tsx +113 -0
  282. package/src/console/ui/overview/busiest.ts +26 -0
  283. package/src/console/ui/overview/compose.ts +133 -0
  284. package/src/console/ui/overview/cost.test.ts +39 -0
  285. package/src/console/ui/overview/cost.ts +138 -0
  286. package/src/console/ui/overview/fixture.ts +161 -0
  287. package/src/console/ui/overview/golden.ts +57 -0
  288. package/src/console/ui/overview/index.ts +59 -0
  289. package/src/console/ui/overview/rank.test.ts +136 -0
  290. package/src/console/ui/overview/rank.ts +210 -0
  291. package/src/console/ui/overview/slo.test.ts +73 -0
  292. package/src/console/ui/overview/slo.ts +229 -0
  293. package/src/console/ui/overview/types.ts +121 -0
  294. package/src/console/ui/overview/verdict.ts +131 -0
  295. package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
  296. package/src/console/ui/plugins/a11y.test.tsx +97 -0
  297. package/src/console/ui/plugins/command.test.ts +22 -0
  298. package/src/console/ui/plugins/command.ts +27 -0
  299. package/src/console/ui/plugins/findings.ts +39 -0
  300. package/src/console/ui/plugins/fixture.ts +233 -0
  301. package/src/console/ui/plugins/group.test.ts +38 -0
  302. package/src/console/ui/plugins/group.ts +67 -0
  303. package/src/console/ui/plugins/index.ts +40 -0
  304. package/src/console/ui/plugins/search.ts +67 -0
  305. package/src/console/ui/plugins/types.ts +115 -0
  306. package/src/console/ui/runs/RunsA11yView.tsx +267 -0
  307. package/src/console/ui/runs/a11y.test.tsx +99 -0
  308. package/src/console/ui/runs/explain.test.ts +23 -0
  309. package/src/console/ui/runs/explain.ts +32 -0
  310. package/src/console/ui/runs/fixture.ts +189 -0
  311. package/src/console/ui/runs/group.test.ts +24 -0
  312. package/src/console/ui/runs/group.ts +85 -0
  313. package/src/console/ui/runs/histogram.test.ts +31 -0
  314. package/src/console/ui/runs/histogram.ts +85 -0
  315. package/src/console/ui/runs/index.ts +75 -0
  316. package/src/console/ui/runs/project.ts +122 -0
  317. package/src/console/ui/runs/query.test.ts +50 -0
  318. package/src/console/ui/runs/query.ts +283 -0
  319. package/src/console/ui/runs/search.test.ts +45 -0
  320. package/src/console/ui/runs/search.ts +141 -0
  321. package/src/console/ui/runs/trace-link.test.ts +30 -0
  322. package/src/console/ui/runs/trace-link.ts +99 -0
  323. package/src/console/ui/runs/types.ts +137 -0
  324. package/src/console/ui/shell/App.tsx +56 -0
  325. package/src/console/ui/shell/client.ts +1715 -0
  326. package/src/console/ui/shell/components/ui.tsx +110 -0
  327. package/src/console/ui/shell/index.html +12 -0
  328. package/src/console/ui/shell/layout/Shell.tsx +95 -0
  329. package/src/console/ui/shell/main.tsx +281 -0
  330. package/src/console/ui/shell/panels/Access.tsx +5 -0
  331. package/src/console/ui/shell/panels/Ai.tsx +5 -0
  332. package/src/console/ui/shell/panels/Architecture.tsx +5 -0
  333. package/src/console/ui/shell/panels/Channels.tsx +5 -0
  334. package/src/console/ui/shell/panels/Clock.tsx +5 -0
  335. package/src/console/ui/shell/panels/Diff.tsx +5 -0
  336. package/src/console/ui/shell/panels/Flows.tsx +5 -0
  337. package/src/console/ui/shell/panels/Gates.tsx +5 -0
  338. package/src/console/ui/shell/panels/Overview.tsx +6 -0
  339. package/src/console/ui/shell/panels/Plugins.tsx +5 -0
  340. package/src/console/ui/shell/panels/Runs.tsx +5 -0
  341. package/src/console/ui/shell/panels/Signals.tsx +5 -0
  342. package/src/console/ui/shell/panels/Store.tsx +5 -0
  343. package/src/console/ui/shell/panels/Traces.tsx +5 -0
  344. package/src/console/ui/shell/panels/Vault.tsx +5 -0
  345. package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
  346. package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
  347. package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
  348. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
  349. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
  350. package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
  351. package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
  352. package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
  353. package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
  354. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
  355. package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
  356. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
  357. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
  358. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
  359. package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
  360. package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
  361. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
  362. package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
  363. package/src/console/ui/shell/setup/Wizard.tsx +189 -0
  364. package/src/console/ui/shell/styles.css +75 -0
  365. package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
  366. package/src/console/ui/signals/a11y.test.tsx +99 -0
  367. package/src/console/ui/signals/confirmation.test.ts +39 -0
  368. package/src/console/ui/signals/confirmation.ts +63 -0
  369. package/src/console/ui/signals/dry-run.test.ts +18 -0
  370. package/src/console/ui/signals/dry-run.ts +36 -0
  371. package/src/console/ui/signals/durable.test.ts +22 -0
  372. package/src/console/ui/signals/durable.ts +41 -0
  373. package/src/console/ui/signals/findings.ts +33 -0
  374. package/src/console/ui/signals/fixture.ts +242 -0
  375. package/src/console/ui/signals/group.test.ts +32 -0
  376. package/src/console/ui/signals/group.ts +49 -0
  377. package/src/console/ui/signals/index.ts +52 -0
  378. package/src/console/ui/signals/monitor.test.ts +30 -0
  379. package/src/console/ui/signals/monitor.ts +79 -0
  380. package/src/console/ui/signals/schema-form.test.ts +34 -0
  381. package/src/console/ui/signals/schema-form.ts +112 -0
  382. package/src/console/ui/signals/search.ts +102 -0
  383. package/src/console/ui/signals/types.ts +71 -0
  384. package/src/console/ui/store/StoreA11yView.tsx +199 -0
  385. package/src/console/ui/store/a11y.test.tsx +99 -0
  386. package/src/console/ui/store/cache-view.test.ts +13 -0
  387. package/src/console/ui/store/cache-view.ts +36 -0
  388. package/src/console/ui/store/confirmation.test.ts +38 -0
  389. package/src/console/ui/store/confirmation.ts +55 -0
  390. package/src/console/ui/store/dry-run.test.ts +15 -0
  391. package/src/console/ui/store/dry-run.ts +29 -0
  392. package/src/console/ui/store/fixture.ts +163 -0
  393. package/src/console/ui/store/group.test.ts +21 -0
  394. package/src/console/ui/store/group.ts +41 -0
  395. package/src/console/ui/store/index.ts +40 -0
  396. package/src/console/ui/store/search.ts +78 -0
  397. package/src/console/ui/store/types.ts +65 -0
  398. package/src/console/ui/store/will-not-fire.test.ts +19 -0
  399. package/src/console/ui/store/will-not-fire.ts +40 -0
  400. package/src/console/ui/traces/TracesA11yView.tsx +200 -0
  401. package/src/console/ui/traces/a11y.test.tsx +97 -0
  402. package/src/console/ui/traces/chain.test.ts +61 -0
  403. package/src/console/ui/traces/chain.ts +144 -0
  404. package/src/console/ui/traces/critical-path.test.ts +24 -0
  405. package/src/console/ui/traces/critical-path.ts +59 -0
  406. package/src/console/ui/traces/filter.test.ts +78 -0
  407. package/src/console/ui/traces/filter.ts +112 -0
  408. package/src/console/ui/traces/fixture.ts +126 -0
  409. package/src/console/ui/traces/fold.test.ts +67 -0
  410. package/src/console/ui/traces/fold.ts +170 -0
  411. package/src/console/ui/traces/index.ts +64 -0
  412. package/src/console/ui/traces/mini.ts +65 -0
  413. package/src/console/ui/traces/replay.test.ts +24 -0
  414. package/src/console/ui/traces/replay.ts +35 -0
  415. package/src/console/ui/traces/sampling.test.ts +26 -0
  416. package/src/console/ui/traces/sampling.ts +66 -0
  417. package/src/console/ui/traces/search.ts +148 -0
  418. package/src/console/ui/traces/tier.ts +72 -0
  419. package/src/console/ui/traces/types.ts +125 -0
  420. package/src/console/ui/tsconfig.json +20 -0
  421. package/src/console/ui/vault/VaultA11yView.tsx +177 -0
  422. package/src/console/ui/vault/a11y.test.tsx +97 -0
  423. package/src/console/ui/vault/blast-radius.test.ts +35 -0
  424. package/src/console/ui/vault/blast-radius.ts +55 -0
  425. package/src/console/ui/vault/confirmation.test.ts +41 -0
  426. package/src/console/ui/vault/confirmation.ts +39 -0
  427. package/src/console/ui/vault/dormant.test.ts +37 -0
  428. package/src/console/ui/vault/dormant.ts +28 -0
  429. package/src/console/ui/vault/export-safe.test.ts +30 -0
  430. package/src/console/ui/vault/export-safe.ts +65 -0
  431. package/src/console/ui/vault/fixture.ts +75 -0
  432. package/src/console/ui/vault/group.test.ts +25 -0
  433. package/src/console/ui/vault/group.ts +50 -0
  434. package/src/console/ui/vault/index.ts +45 -0
  435. package/src/console/ui/vault/search.ts +62 -0
  436. package/src/console/ui/vault/types.ts +57 -0
  437. package/src/console/ui/vite.config.ts +84 -0
  438. package/src/console/xss.gate.test.ts +118 -0
  439. package/src/docker/compose.ts +226 -0
  440. package/src/docker/credentials.ts +25 -0
  441. package/src/docker/derive.ts +83 -0
  442. package/src/docker/docker.test.ts +185 -0
  443. package/src/docker/dockerfile.integration.test.ts +170 -0
  444. package/src/docker/dockerfile.ts +39 -0
  445. package/src/docker/helpers.ts +103 -0
  446. package/src/docker/index.ts +52 -0
  447. package/src/docker/pin.ts +90 -0
  448. package/src/docker/recipes/index.ts +33 -0
  449. package/src/docker/recipes/postgres.ts +18 -0
  450. package/src/docker/recipes/redis.ts +18 -0
  451. package/src/docker/stack.integration.test.ts +140 -0
  452. package/src/docker/stack.ts +62 -0
  453. package/src/docker/types.ts +134 -0
  454. package/src/drivers/ai-anthropic.ts +128 -0
  455. package/src/drivers/ai-mock.ts +82 -0
  456. package/src/drivers/ai-openai-compatible.ts +139 -0
  457. package/src/drivers/ai-providers.test.ts +163 -0
  458. package/src/drivers/ai-types.ts +83 -0
  459. package/src/drivers/bun-native-completeness.test.ts +155 -0
  460. package/src/drivers/channel-console.ts +118 -0
  461. package/src/drivers/channel-fcm.ts +96 -0
  462. package/src/drivers/channel-resend.ts +27 -0
  463. package/src/drivers/channel-smtp.ts +33 -0
  464. package/src/drivers/channel-types.ts +160 -0
  465. package/src/drivers/channel-unifonic.ts +79 -0
  466. package/src/drivers/channel-wa-cloud.ts +84 -0
  467. package/src/drivers/channel-webpush.ts +317 -0
  468. package/src/drivers/conformance.test.ts +89 -0
  469. package/src/drivers/conformance.ts +162 -0
  470. package/src/drivers/fs.ts +73 -0
  471. package/src/drivers/index.ts +194 -0
  472. package/src/drivers/kv-lua.ts +222 -0
  473. package/src/drivers/memory.ts +472 -0
  474. package/src/drivers/pgvector.ts +130 -0
  475. package/src/drivers/postgres.ts +221 -0
  476. package/src/drivers/redis.ts +254 -0
  477. package/src/drivers/s3.ts +129 -0
  478. package/src/drivers/signal-engine.ts +690 -0
  479. package/src/drivers/signal-memory.ts +27 -0
  480. package/src/drivers/signal-nats.ts +131 -0
  481. package/src/drivers/signal-postgres.ts +1089 -0
  482. package/src/drivers/signal-redis.ts +275 -0
  483. package/src/drivers/signal-types.ts +328 -0
  484. package/src/drivers/sqlite.ts +53 -0
  485. package/src/drivers/types.ts +334 -0
  486. package/src/drivers/vault-dotenv-parse.ts +56 -0
  487. package/src/drivers/vault-env.ts +101 -0
  488. package/src/drivers/vault-infisical.ts +61 -0
  489. package/src/drivers/vault-managed.ts +42 -0
  490. package/src/drivers/vault-memory.ts +38 -0
  491. package/src/drivers/vault-openbao.ts +80 -0
  492. package/src/drivers/vault-sops.ts +252 -0
  493. package/src/drivers/vault-types.ts +84 -0
  494. package/src/elements/ai/declare.ts +206 -0
  495. package/src/elements/ai/eval.ts +113 -0
  496. package/src/elements/ai/pii.ts +108 -0
  497. package/src/elements/ai/runtime.ts +540 -0
  498. package/src/elements/ai/schema.ts +194 -0
  499. package/src/elements/ai.test.ts +393 -0
  500. package/src/elements/ai.ts +56 -0
  501. package/src/elements/channel/consent.ts +81 -0
  502. package/src/elements/channel/costs.test.ts +62 -0
  503. package/src/elements/channel/costs.ts +130 -0
  504. package/src/elements/channel/declare.ts +141 -0
  505. package/src/elements/channel/email-auth.test.ts +47 -0
  506. package/src/elements/channel/email-auth.ts +109 -0
  507. package/src/elements/channel/locale.ts +97 -0
  508. package/src/elements/channel/mask.test.ts +19 -0
  509. package/src/elements/channel/mask.ts +60 -0
  510. package/src/elements/channel/mime.ts +23 -0
  511. package/src/elements/channel/outcomes.test.ts +40 -0
  512. package/src/elements/channel/outcomes.ts +126 -0
  513. package/src/elements/channel/receipts.ts +121 -0
  514. package/src/elements/channel/runtime.ts +500 -0
  515. package/src/elements/channel/suppression.ts +136 -0
  516. package/src/elements/channel.test.ts +232 -0
  517. package/src/elements/channel.ts +109 -0
  518. package/src/elements/clock/actions.ts +170 -0
  519. package/src/elements/clock/declare.ts +58 -0
  520. package/src/elements/clock/dst.ts +197 -0
  521. package/src/elements/clock/durable.ts +140 -0
  522. package/src/elements/clock/duration.ts +29 -0
  523. package/src/elements/clock/health.test.ts +103 -0
  524. package/src/elements/clock/health.ts +112 -0
  525. package/src/elements/clock/leader.ts +127 -0
  526. package/src/elements/clock/reconcile.ts +241 -0
  527. package/src/elements/clock/runtime.ts +243 -0
  528. package/src/elements/clock/schedule.ts +262 -0
  529. package/src/elements/clock/time-travel.ts +53 -0
  530. package/src/elements/clock.test.ts +365 -0
  531. package/src/elements/clock.ts +80 -0
  532. package/src/elements/gate/declare.ts +134 -0
  533. package/src/elements/gate/permissions.ts +104 -0
  534. package/src/elements/gate/runtime.ts +177 -0
  535. package/src/elements/gate/strategies.ts +388 -0
  536. package/src/elements/gate.test.ts +145 -0
  537. package/src/elements/gate.ts +41 -0
  538. package/src/elements/index.ts +169 -0
  539. package/src/elements/signal/chaos-child.ts +47 -0
  540. package/src/elements/signal/declare.ts +77 -0
  541. package/src/elements/signal/dry-run-replay.test.ts +95 -0
  542. package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
  543. package/src/elements/signal/reconcile.test.ts +23 -0
  544. package/src/elements/signal/reconcile.ts +122 -0
  545. package/src/elements/signal/runtime.ts +94 -0
  546. package/src/elements/signal.test.ts +457 -0
  547. package/src/elements/signal.ts +26 -0
  548. package/src/elements/store/cache.ts +197 -0
  549. package/src/elements/store/classify.ts +125 -0
  550. package/src/elements/store/declare.ts +200 -0
  551. package/src/elements/store/files-policy.test.ts +31 -0
  552. package/src/elements/store/files-policy.ts +70 -0
  553. package/src/elements/store/replica.ts +68 -0
  554. package/src/elements/store/runtime.ts +393 -0
  555. package/src/elements/store/sql-condition.ts +205 -0
  556. package/src/elements/store/sql-session.ts +579 -0
  557. package/src/elements/store/table.ts +282 -0
  558. package/src/elements/store.test.ts +370 -0
  559. package/src/elements/store.ts +99 -0
  560. package/src/elements/vault/declare.ts +143 -0
  561. package/src/elements/vault/fingerprint.ts +30 -0
  562. package/src/elements/vault/redact.ts +52 -0
  563. package/src/elements/vault/runtime.ts +461 -0
  564. package/src/elements/vault.test.ts +296 -0
  565. package/src/elements/vault.ts +44 -0
  566. package/src/index.ts +183 -0
  567. package/src/kernel/adopt-routes.ts +194 -0
  568. package/src/kernel/app.ts +1068 -0
  569. package/src/kernel/auth-resolve.ts +93 -0
  570. package/src/kernel/boot.test.ts +173 -0
  571. package/src/kernel/boot.ts +439 -0
  572. package/src/kernel/budget-entry.ts +55 -0
  573. package/src/kernel/budget.test.ts +19 -0
  574. package/src/kernel/call.test.ts +83 -0
  575. package/src/kernel/capability.ts +113 -0
  576. package/src/kernel/dry-run.test.ts +64 -0
  577. package/src/kernel/dry-run.ts +171 -0
  578. package/src/kernel/effects.test.ts +89 -0
  579. package/src/kernel/effects.ts +140 -0
  580. package/src/kernel/errors.registry.test.ts +39 -0
  581. package/src/kernel/errors.ts +225 -0
  582. package/src/kernel/flow.test.ts +154 -0
  583. package/src/kernel/flow.ts +290 -0
  584. package/src/kernel/fx.test.ts +245 -0
  585. package/src/kernel/fx.ts +996 -0
  586. package/src/kernel/hook-timing.test.ts +72 -0
  587. package/src/kernel/hook-timing.ts +190 -0
  588. package/src/kernel/hooks.test.ts +181 -0
  589. package/src/kernel/hooks.ts +275 -0
  590. package/src/kernel/index.ts +278 -0
  591. package/src/kernel/journal.ts +405 -0
  592. package/src/kernel/journey.ts +75 -0
  593. package/src/kernel/on.ts +74 -0
  594. package/src/kernel/pipeline.test.ts +349 -0
  595. package/src/kernel/pipeline.ts +245 -0
  596. package/src/kernel/plug.ts +82 -0
  597. package/src/kernel/plugin/capabilities.test.ts +84 -0
  598. package/src/kernel/plugin/conflicts.test.ts +106 -0
  599. package/src/kernel/plugin/decorate.test.ts +111 -0
  600. package/src/kernel/plugin/scoping.test.ts +138 -0
  601. package/src/kernel/plugin.ts +485 -0
  602. package/src/kernel/rate-limit.ts +25 -0
  603. package/src/kernel/registry.ts +398 -0
  604. package/src/kernel/router.test.ts +114 -0
  605. package/src/kernel/router.ts +556 -0
  606. package/src/kernel/routing-budget.test.ts +19 -0
  607. package/src/kernel/run-telemetry.ts +70 -0
  608. package/src/kernel/triggers.ts +324 -0
  609. package/src/kernel/unit.ts +88 -0
  610. package/src/manifest/diff.test.ts +866 -0
  611. package/src/manifest/diff.ts +1670 -0
  612. package/src/manifest/fixtures/base.manifest.json +112 -0
  613. package/src/manifest/fixtures/skyport.excerpt.json +51 -0
  614. package/src/manifest/fixtures/skyport.manifest.json +153 -0
  615. package/src/manifest/index.ts +27 -0
  616. package/src/manifest/types.ts +358 -0
  617. package/src/manifest/undeclared.test.ts +82 -0
  618. package/src/manifest/undeclared.ts +65 -0
  619. package/src/manifest/validate.test.ts +105 -0
  620. package/src/manifest/validate.ts +174 -0
  621. package/src/mcp/authorization.ts +340 -0
  622. package/src/mcp/confirmation.ts +174 -0
  623. package/src/mcp/data.ts +90 -0
  624. package/src/mcp/index.ts +80 -0
  625. package/src/mcp/injection.gate.test.ts +176 -0
  626. package/src/mcp/mcp.test.ts +348 -0
  627. package/src/mcp/protocol.ts +172 -0
  628. package/src/mcp/server.ts +283 -0
  629. package/src/mcp/session.ts +139 -0
  630. package/src/mcp/tools.ts +391 -0
  631. package/src/plugins/catalogue.test.ts +48 -0
  632. package/src/plugins/catalogue.ts +113 -0
  633. package/src/plugins/node-import-scan.test.ts +28 -0
  634. package/src/plugins/node-import-scan.ts +98 -0
  635. package/src/plugins/supply-chain.test.ts +48 -0
  636. package/src/plugins/supply-chain.ts +460 -0
  637. package/src/release/index.ts +30 -0
  638. package/src/release/limits.ts +20 -0
  639. package/src/release/measure.ts +443 -0
  640. package/src/release/publish.ts +56 -0
  641. package/src/runs/bench.test.ts +93 -0
  642. package/src/runs/collect.ts +144 -0
  643. package/src/runs/drivers/clickhouse.ts +108 -0
  644. package/src/runs/drivers/files.ts +201 -0
  645. package/src/runs/drivers/memory.ts +94 -0
  646. package/src/runs/drivers/postgres.ts +110 -0
  647. package/src/runs/duckdb.ts +99 -0
  648. package/src/runs/index.ts +82 -0
  649. package/src/runs/outlier.ts +180 -0
  650. package/src/runs/parquet.ts +210 -0
  651. package/src/runs/privacy.ts +49 -0
  652. package/src/runs/runs.test.ts +324 -0
  653. package/src/runs/runtime.ts +229 -0
  654. package/src/runs/shred.ts +243 -0
  655. package/src/runs/telemetry.ts +10 -0
  656. package/src/runs/types.ts +179 -0
  657. package/src/runtime/bun.ts +140 -0
  658. package/src/runtime/cold-start.bench.ts +20 -0
  659. package/src/runtime/index.ts +46 -0
  660. package/src/runtime/primitives.ts +242 -0
  661. package/src/runtime/security.test.ts +170 -0
  662. package/src/runtime/security.ts +168 -0
  663. package/src/runtime/serve.test.ts +223 -0
  664. package/src/runtime/types.ts +217 -0
  665. package/src/runtime/web-standard.ts +77 -0
  666. package/src/test/create-test-app.test.ts +161 -0
  667. package/src/test/create-test-app.ts +533 -0
  668. package/src/test/index.ts +21 -0
  669. package/src/test/provisions.integration.test.ts +174 -0
  670. package/src/upgrade/codemods.test.ts +58 -0
  671. package/src/upgrade/codemods.ts +83 -0
  672. package/src/validation/index.ts +23 -0
  673. package/src/validation/standard-schema.test.ts +96 -0
  674. package/src/validation/standard-schema.ts +316 -0
@@ -0,0 +1,197 @@
1
+ /**
2
+ * DST ambiguity detection — expression + zone together (console §9.6).
3
+ *
4
+ * A daily 02:00 job in a DST-observing zone runs twice or never on
5
+ * transition days. The warning appears only when the schedule actually
6
+ * falls in the ambiguous window.
7
+ */
8
+
9
+ /** Result when a schedule hits a DST gap or overlap. */
10
+ export interface DstAmbiguity {
11
+ /** `"gap"` — spring forward (time skipped); `"overlap"` — fall back (time repeats). */
12
+ readonly kind: "gap" | "overlap";
13
+ /** IANA timezone. */
14
+ readonly timezone: string;
15
+ /** Cron expression that was checked. */
16
+ readonly cron: string;
17
+ /** Local wall time (`HH:MM`) that is ambiguous. */
18
+ readonly localTime: string;
19
+ /** Example transition day (ISO date) where the issue occurs. */
20
+ readonly on: string;
21
+ /** Human-readable reason. */
22
+ readonly reason: string;
23
+ }
24
+
25
+ /**
26
+ * Detect whether a cron expression + timezone falls in a DST ambiguous window.
27
+ *
28
+ * Supports simple forms `M H * * *` (and `M H * * DOW`). Complex lists /
29
+ * steps / ranges return `null` (no spurious always-on warning).
30
+ *
31
+ * @param cron - Five-field cron expression
32
+ * @param timezone - IANA timezone
33
+ * @param from - Optional epoch-ms scan start (defaults to now)
34
+ */
35
+ export function detectDstAmbiguity(
36
+ cron: string,
37
+ timezone: string,
38
+ from: number = Date.now(),
39
+ ): DstAmbiguity | null {
40
+ if (timezone === "UTC" || timezone === "Etc/UTC" || timezone === "GMT") {
41
+ return null;
42
+ }
43
+
44
+ const parsed = parseSimpleDaily(cron);
45
+ if (!parsed) return null;
46
+
47
+ const { minute, hour } = parsed;
48
+ const start = new Date(from);
49
+ let prevOffset: number | null = null;
50
+
51
+ // Walk day-by-day; only classify on days where the noon offset jumps
52
+ // (DST transition). Keeps the warning precise without a full-year grind.
53
+ for (let day = 0; day < 450; day++) {
54
+ const probe = Date.UTC(
55
+ start.getUTCFullYear(),
56
+ start.getUTCMonth(),
57
+ start.getUTCDate() + day,
58
+ 12,
59
+ 0,
60
+ 0,
61
+ );
62
+ const offset = offsetMinutesAt(probe, timezone);
63
+ if (prevOffset !== null && offset !== prevOffset) {
64
+ // Transition occurred between yesterday noon and today noon —
65
+ // check both civil dates around the jump.
66
+ for (const delta of [-1, 0] as const) {
67
+ const t = new Date(probe + delta * 86_400_000);
68
+ const y = t.getUTCFullYear();
69
+ const mo = t.getUTCMonth() + 1;
70
+ const d = t.getUTCDate();
71
+ const dateStr = `${y}-${pad(mo)}-${pad(d)}`;
72
+ const kind = classifyLocalInstant(dateStr, hour, minute, timezone);
73
+ if (kind === "gap" || kind === "overlap") {
74
+ const localTime = `${pad(hour)}:${pad(minute)}`;
75
+ return {
76
+ kind,
77
+ timezone,
78
+ cron: cron.trim(),
79
+ localTime,
80
+ on: dateStr,
81
+ reason:
82
+ kind === "gap"
83
+ ? `${localTime} in ${timezone} is skipped on ${dateStr} (DST spring forward)`
84
+ : `${localTime} in ${timezone} occurs twice on ${dateStr} (DST fall back)`,
85
+ };
86
+ }
87
+ }
88
+ }
89
+ prevOffset = offset;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ /** Parsed simple daily cron. */
95
+ interface SimpleDaily {
96
+ readonly minute: number;
97
+ readonly hour: number;
98
+ }
99
+
100
+ function parseSimpleDaily(cron: string): SimpleDaily | null {
101
+ const parts = cron.trim().split(/\s+/);
102
+ if (parts.length < 5) return null;
103
+ const minute = parts[0]!;
104
+ const hour = parts[1]!;
105
+ const dom = parts[2]!;
106
+ const mon = parts[3]!;
107
+ // Only flag when the schedule pins a specific local wall-clock time.
108
+ if (!/^\d+$/.test(minute) || !/^\d+$/.test(hour)) return null;
109
+ if (dom !== "*" && !/^\d+$/.test(dom)) return null;
110
+ if (mon !== "*" && !/^\d+$/.test(mon)) return null;
111
+ const m = Number(minute);
112
+ const h = Number(hour);
113
+ if (m < 0 || m > 59 || h < 0 || h > 23) return null;
114
+ return { minute: m, hour: h };
115
+ }
116
+
117
+ /**
118
+ * Classify a local civil time in `timezone` on `YYYY-MM-DD`.
119
+ *
120
+ * - `ok` — exactly one UTC instant
121
+ * - `gap` — no UTC instant (spring forward)
122
+ * - `overlap` — two UTC instants (fall back)
123
+ */
124
+ function classifyLocalInstant(
125
+ dateStr: string,
126
+ hour: number,
127
+ minute: number,
128
+ timezone: string,
129
+ ): "ok" | "gap" | "overlap" {
130
+ const matches: number[] = [];
131
+ const base = Date.parse(`${dateStr}T00:00:00.000Z`);
132
+ if (Number.isNaN(base)) return "ok";
133
+
134
+ for (let offsetMin = -14 * 60; offsetMin <= 14 * 60; offsetMin += 15) {
135
+ const utc = base + hour * 3_600_000 + minute * 60_000 - offsetMin * 60_000;
136
+ const local = formatInZone(utc, timezone);
137
+ if (
138
+ local.date === dateStr &&
139
+ local.hour === hour &&
140
+ local.minute === minute
141
+ ) {
142
+ if (!matches.includes(utc)) matches.push(utc);
143
+ }
144
+ }
145
+
146
+ if (matches.length === 0) return "gap";
147
+ if (matches.length >= 2) return "overlap";
148
+ return "ok";
149
+ }
150
+
151
+ /** UTC offset (minutes) of `timezone` at `utcMs`. */
152
+ function offsetMinutesAt(utcMs: number, timezone: string): number {
153
+ const local = formatInZone(utcMs, timezone);
154
+ // Reconstruct a UTC ms for the same wall clock as if it were UTC, then diff.
155
+ const asUtc = Date.UTC(
156
+ Number(local.date.slice(0, 4)),
157
+ Number(local.date.slice(5, 7)) - 1,
158
+ Number(local.date.slice(8, 10)),
159
+ local.hour,
160
+ local.minute,
161
+ 0,
162
+ );
163
+ return Math.round((asUtc - utcMs) / 60_000);
164
+ }
165
+
166
+ interface ZonedParts {
167
+ readonly date: string;
168
+ readonly hour: number;
169
+ readonly minute: number;
170
+ }
171
+
172
+ function formatInZone(utcMs: number, timezone: string): ZonedParts {
173
+ const fmt = new Intl.DateTimeFormat("en-US", {
174
+ timeZone: timezone,
175
+ year: "numeric",
176
+ month: "2-digit",
177
+ day: "2-digit",
178
+ hour: "2-digit",
179
+ minute: "2-digit",
180
+ hourCycle: "h23",
181
+ });
182
+ const parts = fmt.formatToParts(new Date(utcMs));
183
+ const get = (type: Intl.DateTimeFormatPartTypes): string =>
184
+ parts.find((p) => p.type === type)?.value ?? "0";
185
+ const year = get("year");
186
+ const month = get("month");
187
+ const day = get("day");
188
+ return {
189
+ date: `${year}-${month}-${day}`,
190
+ hour: Number(get("hour")),
191
+ minute: Number(get("minute")),
192
+ };
193
+ }
194
+
195
+ function pad(n: number): string {
196
+ return n < 10 ? `0${n}` : String(n);
197
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Durable flow runner — journals every fx call when `durable: true`.
3
+ *
4
+ * Workflows are not a separate API: an ordinary flow with one option.
5
+ * A process killed between steps resumes at the failed step; the prior
6
+ * step never re-runs (four-applications · Provisions).
7
+ */
8
+
9
+ import type { AnyFlowDef } from "../../kernel/flow.ts";
10
+ import {
11
+ createFxContext,
12
+ type CreateFxOptions,
13
+ type Fx,
14
+ } from "../../kernel/fx.ts";
15
+ import {
16
+ createJournal,
17
+ isJournalSuspend,
18
+ type Journal,
19
+ type JournalSession,
20
+ type JournalStore,
21
+ } from "../../kernel/journal.ts";
22
+ /** Outcome of one durable attempt. */
23
+ export type DurableResult<O = unknown> =
24
+ | {
25
+ readonly status: "completed";
26
+ readonly runId: string;
27
+ readonly output: O;
28
+ }
29
+ | {
30
+ readonly status: "sleeping";
31
+ readonly runId: string;
32
+ readonly wakeAt: number;
33
+ readonly label: string;
34
+ }
35
+ | {
36
+ readonly status: "failed";
37
+ readonly runId: string;
38
+ readonly error: string;
39
+ };
40
+
41
+ /** Options for {@link runDurable}. */
42
+ export interface RunDurableOptions {
43
+ /** Flow with `durable: true` (or forced via this runner). */
44
+ readonly flow: AnyFlowDef;
45
+ /** Input payload. */
46
+ readonly input?: unknown;
47
+ /** Journal store (memory or file). */
48
+ readonly journalStore: JournalStore;
49
+ /** Resume an existing run id (crash recovery). */
50
+ readonly runId?: string;
51
+ /** Injectable clock. */
52
+ readonly now?: () => number;
53
+ /** Extra fx options (secrets, store runtime, …). */
54
+ readonly fx?: Omit<
55
+ CreateFxOptions,
56
+ "flow" | "effects" | "capability" | "now" | "journal"
57
+ >;
58
+ }
59
+
60
+ /**
61
+ * Execute (or resume) a durable flow against a journal.
62
+ *
63
+ * @param options - Flow, input, journal, clock
64
+ */
65
+ export async function runDurable<O = unknown>(
66
+ options: RunDurableOptions,
67
+ ): Promise<DurableResult<O>> {
68
+ const now = options.now ?? (() => Date.now());
69
+ const journal: Journal = createJournal({
70
+ store: options.journalStore,
71
+ now,
72
+ });
73
+
74
+ if (options.runId) {
75
+ const existing = await options.journalStore.get(options.runId);
76
+ if (
77
+ existing?.wakeAt !== undefined &&
78
+ now() < existing.wakeAt
79
+ ) {
80
+ const sleepEntry = [...existing.entries]
81
+ .reverse()
82
+ .find((e) => e.kind === "sleep");
83
+ return {
84
+ status: "sleeping",
85
+ runId: existing.id,
86
+ wakeAt: existing.wakeAt,
87
+ label: sleepEntry && sleepEntry.kind === "sleep" ? sleepEntry.label : "",
88
+ };
89
+ }
90
+ }
91
+
92
+ const session: JournalSession = options.runId
93
+ ? await journal.resume(options.runId)
94
+ : await journal.start(options.flow.name, options.input);
95
+
96
+ if (session.run.status === "sleeping") {
97
+ session.run.status = "running";
98
+ }
99
+
100
+ const { fx } = createFxContext({
101
+ ...options.fx,
102
+ flow: options.flow.name,
103
+ effects: options.flow.effects,
104
+ now,
105
+ journal: session,
106
+ durable: true,
107
+ });
108
+
109
+ try {
110
+ const output = await options.flow.do(options.input as never, fx);
111
+ await session.commit("completed", { output });
112
+ return {
113
+ status: "completed",
114
+ runId: session.runId,
115
+ output: output as O,
116
+ };
117
+ } catch (err) {
118
+ if (isJournalSuspend(err)) {
119
+ return {
120
+ status: "sleeping",
121
+ runId: session.runId,
122
+ wakeAt: err.wakeAt,
123
+ label: err.label,
124
+ };
125
+ }
126
+ const message = err instanceof Error ? err.message : String(err);
127
+ await session.commit("failed", { error: message });
128
+ return {
129
+ status: "failed",
130
+ runId: session.runId,
131
+ error: message,
132
+ };
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Bind a journal session onto an existing {@link Fx} factory path —
138
+ * re-exported type helper for tests that build fx directly.
139
+ */
140
+ export type { JournalSession, Fx };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Duration / interval parsing for the Clock element.
3
+ */
4
+
5
+ /**
6
+ * Parse a short duration string (`"7d"`, `"2m"`, `"30s"`, `"1h"`, `"200ms"`)
7
+ * to milliseconds. Returns `0` when the string is not a duration.
8
+ *
9
+ * @param duration - Duration string
10
+ */
11
+ export function parseDurationMs(duration: string): number {
12
+ const match = /^(\d+)(ms|s|m|h|d)$/.exec(duration.trim());
13
+ if (!match) return 0;
14
+ const n = Number(match[1]);
15
+ switch (match[2]) {
16
+ case "ms":
17
+ return n;
18
+ case "s":
19
+ return n * 1000;
20
+ case "m":
21
+ return n * 60_000;
22
+ case "h":
23
+ return n * 3_600_000;
24
+ case "d":
25
+ return n * 86_400_000;
26
+ default:
27
+ return 0;
28
+ }
29
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Cron health + next-occurrence helpers.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { cronHealth } from "./health.ts";
7
+ import { nextOccurrences } from "./schedule.ts";
8
+ import type { CronRow } from "./reconcile.ts";
9
+
10
+ function row(partial: Partial<CronRow> & { name: string }): CronRow {
11
+ return {
12
+ timezone: "UTC",
13
+ overridable: false,
14
+ status: "active",
15
+ ...partial,
16
+ };
17
+ }
18
+
19
+ describe("cronHealth", () => {
20
+ test("every — overdue with missed runs and catchUp one", () => {
21
+ const now = 10_000;
22
+ const h = cronHealth(
23
+ row({
24
+ name: "tick",
25
+ effectiveEvery: "1s",
26
+ declaredEvery: "1s",
27
+ lastRunAt: 0,
28
+ nextRunAt: 1_000,
29
+ }),
30
+ now,
31
+ );
32
+ expect(h.overdue).toBe(true);
33
+ expect(h.missedRuns).toBe(10);
34
+ expect(h.catchUp).toBe("one");
35
+ });
36
+
37
+ test("not overdue when nextRunAt is in the future", () => {
38
+ const now = 1_000;
39
+ const h = cronHealth(
40
+ row({
41
+ name: "ok",
42
+ effectiveEvery: "1h",
43
+ lastRunAt: 0,
44
+ nextRunAt: 3_600_000,
45
+ }),
46
+ now,
47
+ );
48
+ expect(h.overdue).toBe(false);
49
+ expect(h.missedRuns).toBe(0);
50
+ });
51
+
52
+ test("projects lease holder fields", () => {
53
+ const h = cronHealth(
54
+ row({
55
+ name: "leased",
56
+ effectiveEvery: "1h",
57
+ leaderInstanceId: "inst-a",
58
+ leaderLeaseUntil: 99_000,
59
+ lastRunAt: 0,
60
+ nextRunAt: 3_600_000,
61
+ }),
62
+ 1_000,
63
+ );
64
+ expect(h.leaderInstanceId).toBe("inst-a");
65
+ expect(h.leaderLeaseUntil).toBe(99_000);
66
+ });
67
+ });
68
+
69
+ describe("nextOccurrences", () => {
70
+ test("every — next 24h of fires", () => {
71
+ const from = 0;
72
+ const until = 10_000;
73
+ const fires = nextOccurrences(
74
+ row({
75
+ name: "tick",
76
+ effectiveEvery: "2s",
77
+ lastRunAt: 0,
78
+ }),
79
+ from,
80
+ until,
81
+ );
82
+ expect(fires[0]).toBe(2_000);
83
+ expect(fires.length).toBe(4);
84
+ expect(fires[fires.length - 1]).toBe(8_000);
85
+ });
86
+
87
+ test("simple daily cron in UTC", () => {
88
+ // 2024-01-01 00:00 UTC
89
+ const from = Date.UTC(2024, 0, 1, 0, 0, 0);
90
+ const until = from + 48 * 3_600_000;
91
+ const fires = nextOccurrences(
92
+ row({
93
+ name: "nightly",
94
+ effectiveCron: "0 2 * * *",
95
+ timezone: "UTC",
96
+ }),
97
+ from,
98
+ until,
99
+ );
100
+ expect(fires.length).toBeGreaterThanOrEqual(1);
101
+ expect(fires.every((t) => t >= from && t < until)).toBe(true);
102
+ });
103
+ });
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Cron health — four numbers for the Console Clock panel (console §9.6).
3
+ *
4
+ * Catch-up policy is `"one"`: the runtime fires at most once per lease when
5
+ * overdue (`ClockRuntime.tick` / `fire`). Not a Store column.
6
+ */
7
+
8
+ import { parseDurationMs } from "./duration.ts";
9
+ import { effectiveSchedule, type CronRow } from "./reconcile.ts";
10
+ import {
11
+ countMissedOccurrences,
12
+ previousOccurrence,
13
+ } from "./schedule.ts";
14
+
15
+ /** Catch-up policy matching ClockRuntime physics. */
16
+ export type CatchUpPolicy = "one";
17
+
18
+ /** Four-number cron health (+ lease holder). */
19
+ export interface CronHealth {
20
+ /** Scheduled vs actual (`lastRunAt - expected`); `null` when unknown. */
21
+ readonly driftMs: number | null;
22
+ /** Whether the schedule is past due. */
23
+ readonly overdue: boolean;
24
+ /** Runs missed while the app was down (slots between last fire and now). */
25
+ readonly missedRuns: number;
26
+ /** Runtime catch-up policy (`"one"`). */
27
+ readonly catchUp: CatchUpPolicy;
28
+ /** Instance holding the leader lease, if any. */
29
+ readonly leaderInstanceId?: string;
30
+ /** Lease expiry epoch-ms. */
31
+ readonly leaderLeaseUntil?: number;
32
+ }
33
+
34
+ /**
35
+ * Compute cron health from a reconciled Store row.
36
+ *
37
+ * @param row - `oke_crons` row
38
+ * @param now - Current epoch-ms
39
+ */
40
+ export function cronHealth(row: CronRow, now: number): CronHealth {
41
+ const overdue = isOverdue(row, now);
42
+ const missedRuns = overdue
43
+ ? countMissedFromRow(row, now)
44
+ : 0;
45
+ const driftMs = computeDrift(row, now);
46
+
47
+ return {
48
+ driftMs,
49
+ overdue,
50
+ missedRuns,
51
+ catchUp: "one",
52
+ leaderInstanceId: row.leaderInstanceId,
53
+ leaderLeaseUntil: row.leaderLeaseUntil,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * @param row - Store row
59
+ * @param now - Clock
60
+ */
61
+ function isOverdue(row: CronRow, now: number): boolean {
62
+ if (row.status !== "active") return false;
63
+ if (row.nextRunAt !== undefined) return row.nextRunAt < now;
64
+ const sched = effectiveSchedule(row);
65
+ if (sched.every) {
66
+ const interval = parseDurationMs(sched.every);
67
+ if (interval <= 0) return false;
68
+ if (row.lastRunAt === undefined) return true;
69
+ return row.lastRunAt + interval < now;
70
+ }
71
+ return false;
72
+ }
73
+
74
+ function countMissedFromRow(row: CronRow, now: number): number {
75
+ const sched = effectiveSchedule(row);
76
+ if (sched.every) {
77
+ const interval = parseDurationMs(sched.every);
78
+ if (interval <= 0) return 0;
79
+ if (row.lastRunAt === undefined) return 1;
80
+ const elapsed = now - row.lastRunAt;
81
+ return Math.max(0, Math.floor(elapsed / interval));
82
+ }
83
+ if (row.lastRunAt !== undefined) {
84
+ return countMissedOccurrences(row, row.lastRunAt, now);
85
+ }
86
+ if (row.nextRunAt !== undefined && row.nextRunAt < now) return 1;
87
+ return 0;
88
+ }
89
+
90
+ function computeDrift(row: CronRow, now: number): number | null {
91
+ if (row.lastRunAt === undefined) return null;
92
+ const expected = previousOccurrence(row, row.lastRunAt);
93
+ if (expected === null) {
94
+ // Fallback: for `every`, expected is lastRunAt floored to interval from origin.
95
+ const sched = effectiveSchedule(row);
96
+ if (sched.every) {
97
+ const interval = parseDurationMs(sched.every);
98
+ if (interval <= 0) return null;
99
+ // If nextRunAt is set, expected previous = nextRunAt - interval.
100
+ if (row.nextRunAt !== undefined) {
101
+ return row.lastRunAt - (row.nextRunAt - interval);
102
+ }
103
+ // No better anchor — drift unknown unless overdue.
104
+ if (row.lastRunAt + interval < now) {
105
+ return now - (row.lastRunAt + interval);
106
+ }
107
+ return 0;
108
+ }
109
+ return null;
110
+ }
111
+ return row.lastRunAt - expected;
112
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Cron leader election — N instances, one run (unified-theory · Clone axis).
3
+ *
4
+ * Lease lives on the reconciled cron row so the Console can show which
5
+ * instance holds it (console §9.6).
6
+ */
7
+
8
+ /** Cron row fields the lease mutates. */
9
+ export interface LeaseTarget {
10
+ /** Schedule name. */
11
+ readonly name: string;
12
+ /** Instance currently holding the lease, if any. */
13
+ leaderInstanceId?: string;
14
+ /** Absolute lease expiry epoch-ms. */
15
+ leaderLeaseUntil?: number;
16
+ }
17
+
18
+ /** Store surface required by {@link tryAcquireLease}. */
19
+ export interface LeaseStore {
20
+ /**
21
+ * @param name - Cron name
22
+ */
23
+ get(name: string): Promise<LeaseTarget | undefined>;
24
+ /**
25
+ * @param row - Updated row
26
+ */
27
+ put(row: LeaseTarget): Promise<void>;
28
+ /**
29
+ * Optional atomic acquire — preferred when present (memory / sql).
30
+ *
31
+ * @param name - Cron name
32
+ * @param instanceId - Candidate instance
33
+ * @param now - Current epoch-ms
34
+ * @param leaseMs - Lease TTL
35
+ */
36
+ acquireLease?(
37
+ name: string,
38
+ instanceId: string,
39
+ now: number,
40
+ leaseMs: number,
41
+ ): Promise<boolean>;
42
+ }
43
+
44
+ /** Options for {@link tryAcquireLease}. */
45
+ export interface AcquireLeaseOptions {
46
+ /** Cron name. */
47
+ readonly name: string;
48
+ /** This instance's id. */
49
+ readonly instanceId: string;
50
+ /** Current epoch-ms. */
51
+ readonly now: number;
52
+ /** Lease TTL in milliseconds. */
53
+ readonly leaseMs: number;
54
+ /** Backing store. */
55
+ readonly store: LeaseStore;
56
+ }
57
+
58
+ /**
59
+ * Try to acquire (or renew) the leader lease for a cron.
60
+ *
61
+ * Returns `true` when this instance holds the lease after the call —
62
+ * only then may it execute the schedule.
63
+ *
64
+ * @param options - Name, instance, clock, store
65
+ */
66
+ export async function tryAcquireLease(
67
+ options: AcquireLeaseOptions,
68
+ ): Promise<boolean> {
69
+ if (options.store.acquireLease) {
70
+ return options.store.acquireLease(
71
+ options.name,
72
+ options.instanceId,
73
+ options.now,
74
+ options.leaseMs,
75
+ );
76
+ }
77
+
78
+ const row = await options.store.get(options.name);
79
+ if (!row) return false;
80
+
81
+ const held =
82
+ row.leaderLeaseUntil !== undefined &&
83
+ row.leaderLeaseUntil > options.now &&
84
+ row.leaderInstanceId !== undefined &&
85
+ row.leaderInstanceId !== options.instanceId;
86
+
87
+ if (held) return false;
88
+
89
+ const next: LeaseTarget = {
90
+ ...row,
91
+ leaderInstanceId: options.instanceId,
92
+ leaderLeaseUntil: options.now + options.leaseMs,
93
+ };
94
+ await options.store.put(next);
95
+
96
+ // Re-read: lose the race if another writer won.
97
+ const confirmed = await options.store.get(options.name);
98
+ return (
99
+ confirmed?.leaderInstanceId === options.instanceId &&
100
+ (confirmed.leaderLeaseUntil ?? 0) > options.now
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Release the lease when held by `instanceId` (best-effort).
106
+ *
107
+ * @param store - Lease store
108
+ * @param name - Cron name
109
+ * @param instanceId - Instance that should release
110
+ * @param now - Current epoch-ms
111
+ */
112
+ export async function releaseLease(
113
+ store: LeaseStore,
114
+ name: string,
115
+ instanceId: string,
116
+ now: number,
117
+ ): Promise<void> {
118
+ const row = await store.get(name);
119
+ if (!row) return;
120
+ if (row.leaderInstanceId !== instanceId) return;
121
+ if (row.leaderLeaseUntil !== undefined && row.leaderLeaseUntil <= now) return;
122
+ await store.put({
123
+ ...row,
124
+ leaderInstanceId: undefined,
125
+ leaderLeaseUntil: undefined,
126
+ });
127
+ }