@alneas/eaos-server 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.ja.md +39 -0
  4. package/README.md +40 -2
  5. package/dist/agentic/agent-jobs.d.ts +329 -0
  6. package/dist/agentic/agent-jobs.js +15 -0
  7. package/dist/agentic/agent-jobs.js.map +1 -0
  8. package/dist/agentic/agent-node.d.ts +40 -0
  9. package/dist/agentic/agent-node.js +7 -0
  10. package/dist/agentic/agent-node.js.map +1 -0
  11. package/dist/agentic/agent-runner.d.ts +33 -0
  12. package/dist/agentic/agent-runner.js +7 -0
  13. package/dist/agentic/agent-runner.js.map +1 -0
  14. package/dist/agentic/ai-broker.d.ts +169 -0
  15. package/dist/agentic/ai-broker.js +8 -0
  16. package/dist/agentic/ai-broker.js.map +1 -0
  17. package/dist/agentic/atomic-checkpointer.d.ts +137 -0
  18. package/dist/agentic/atomic-checkpointer.js +9 -0
  19. package/dist/agentic/atomic-checkpointer.js.map +1 -0
  20. package/dist/agentic/autonomous-worker.d.ts +95 -0
  21. package/dist/agentic/autonomous-worker.js +9 -0
  22. package/dist/agentic/autonomous-worker.js.map +1 -0
  23. package/dist/agentic/event-bridge.d.ts +344 -0
  24. package/dist/agentic/event-bridge.js +9 -0
  25. package/dist/agentic/event-bridge.js.map +1 -0
  26. package/dist/agentic/executor.d.ts +91 -0
  27. package/dist/agentic/executor.js +12 -0
  28. package/dist/agentic/executor.js.map +1 -0
  29. package/dist/agentic/graph-projection.d.ts +119 -0
  30. package/dist/agentic/graph-projection.js +7 -0
  31. package/dist/agentic/graph-projection.js.map +1 -0
  32. package/dist/agentic/graph.d.ts +524 -0
  33. package/dist/agentic/graph.js +7 -0
  34. package/dist/agentic/graph.js.map +1 -0
  35. package/dist/agentic/index.d.ts +24 -0
  36. package/dist/agentic/index.js +86 -0
  37. package/dist/agentic/index.js.map +1 -0
  38. package/dist/agentic/local-runner.d.ts +102 -0
  39. package/dist/agentic/local-runner.js +7 -0
  40. package/dist/agentic/local-runner.js.map +1 -0
  41. package/dist/agentic/remote-executor.d.ts +67 -0
  42. package/dist/agentic/remote-executor.js +8 -0
  43. package/dist/agentic/remote-executor.js.map +1 -0
  44. package/dist/agentic/types.d.ts +157 -0
  45. package/dist/agentic/types.js +7 -0
  46. package/dist/agentic/types.js.map +1 -0
  47. package/dist/agentic/workflow-catalog.d.ts +152 -0
  48. package/dist/agentic/workflow-catalog.js +7 -0
  49. package/dist/agentic/workflow-catalog.js.map +1 -0
  50. package/dist/agentic/workflow-compiler.d.ts +44 -0
  51. package/dist/agentic/workflow-compiler.js +8 -0
  52. package/dist/agentic/workflow-compiler.js.map +1 -0
  53. package/dist/agentic/workflow-coordinator.d.ts +170 -0
  54. package/dist/agentic/workflow-coordinator.js +8 -0
  55. package/dist/agentic/workflow-coordinator.js.map +1 -0
  56. package/dist/agentic/workflow-manager.d.ts +91 -0
  57. package/dist/agentic/workflow-manager.js +7 -0
  58. package/dist/agentic/workflow-manager.js.map +1 -0
  59. package/dist/agentic/workflow-registry.d.ts +334 -0
  60. package/dist/agentic/workflow-registry.js +7 -0
  61. package/dist/agentic/workflow-registry.js.map +1 -0
  62. package/dist/artifacts-CteHmCMI.d.ts +614 -0
  63. package/dist/auth-hooks/api-key.d.ts +75 -0
  64. package/dist/auth-hooks/api-key.js +7 -0
  65. package/dist/auth-hooks/api-key.js.map +1 -0
  66. package/dist/auth-hooks/index.d.ts +80 -0
  67. package/dist/auth-hooks/index.js +28 -0
  68. package/dist/auth-hooks/index.js.map +1 -0
  69. package/dist/browser-auth/account-memberships.d.ts +213 -0
  70. package/dist/browser-auth/account-memberships.js +13 -0
  71. package/dist/browser-auth/account-memberships.js.map +1 -0
  72. package/dist/browser-auth/index.d.ts +8 -0
  73. package/dist/browser-auth/index.js +59 -0
  74. package/dist/browser-auth/index.js.map +1 -0
  75. package/dist/browser-auth/kratos-session.d.ts +37 -0
  76. package/dist/browser-auth/kratos-session.js +7 -0
  77. package/dist/browser-auth/kratos-session.js.map +1 -0
  78. package/dist/browser-auth/platform-session.d.ts +120 -0
  79. package/dist/browser-auth/platform-session.js +7 -0
  80. package/dist/browser-auth/platform-session.js.map +1 -0
  81. package/dist/browser-auth/public-memberships.d.ts +22 -0
  82. package/dist/browser-auth/public-memberships.js +7 -0
  83. package/dist/browser-auth/public-memberships.js.map +1 -0
  84. package/dist/cache/index.d.ts +181 -0
  85. package/dist/cache/index.js +197 -0
  86. package/dist/cache/index.js.map +1 -0
  87. package/dist/chunk-32746UQZ.js +131 -0
  88. package/dist/chunk-32746UQZ.js.map +1 -0
  89. package/dist/chunk-3NLSWP2Y.js +223 -0
  90. package/dist/chunk-3NLSWP2Y.js.map +1 -0
  91. package/dist/chunk-3P4XTTQH.js +346 -0
  92. package/dist/chunk-3P4XTTQH.js.map +1 -0
  93. package/dist/chunk-3VITSHN3.js +15 -0
  94. package/dist/chunk-3VITSHN3.js.map +1 -0
  95. package/dist/chunk-46UWPEW2.js +226 -0
  96. package/dist/chunk-46UWPEW2.js.map +1 -0
  97. package/dist/chunk-4VORUFKR.js +45 -0
  98. package/dist/chunk-4VORUFKR.js.map +1 -0
  99. package/dist/chunk-55D4ERWO.js +1 -0
  100. package/dist/chunk-55D4ERWO.js.map +1 -0
  101. package/dist/chunk-5TBBZIND.js +18 -0
  102. package/dist/chunk-5TBBZIND.js.map +1 -0
  103. package/dist/chunk-5WOZWPZA.js +211 -0
  104. package/dist/chunk-5WOZWPZA.js.map +1 -0
  105. package/dist/chunk-6CBVZ6CT.js +168 -0
  106. package/dist/chunk-6CBVZ6CT.js.map +1 -0
  107. package/dist/chunk-6KV5CJBW.js +221 -0
  108. package/dist/chunk-6KV5CJBW.js.map +1 -0
  109. package/dist/chunk-6XZXYNJR.js +74 -0
  110. package/dist/chunk-6XZXYNJR.js.map +1 -0
  111. package/dist/chunk-753HWDSC.js +222 -0
  112. package/dist/chunk-753HWDSC.js.map +1 -0
  113. package/dist/chunk-7AHSS7SO.js +81 -0
  114. package/dist/chunk-7AHSS7SO.js.map +1 -0
  115. package/dist/chunk-7FTJN4SA.js +1 -0
  116. package/dist/chunk-7FTJN4SA.js.map +1 -0
  117. package/dist/chunk-AVDMLTPF.js +24 -0
  118. package/dist/chunk-AVDMLTPF.js.map +1 -0
  119. package/dist/chunk-BKAHUEIT.js +95 -0
  120. package/dist/chunk-BKAHUEIT.js.map +1 -0
  121. package/dist/chunk-CJ7OFEF7.js +134 -0
  122. package/dist/chunk-CJ7OFEF7.js.map +1 -0
  123. package/dist/chunk-DHQGNP4T.js +770 -0
  124. package/dist/chunk-DHQGNP4T.js.map +1 -0
  125. package/dist/chunk-EJKWDZIU.js +111 -0
  126. package/dist/chunk-EJKWDZIU.js.map +1 -0
  127. package/dist/chunk-F3O7CELI.js +23 -0
  128. package/dist/chunk-F3O7CELI.js.map +1 -0
  129. package/dist/chunk-FD6VC2NA.js +66 -0
  130. package/dist/chunk-FD6VC2NA.js.map +1 -0
  131. package/dist/chunk-FRRRWAHO.js +14 -0
  132. package/dist/chunk-FRRRWAHO.js.map +1 -0
  133. package/dist/chunk-HCKAGLLB.js +17 -0
  134. package/dist/chunk-HCKAGLLB.js.map +1 -0
  135. package/dist/chunk-HVC7FG6B.js +191 -0
  136. package/dist/chunk-HVC7FG6B.js.map +1 -0
  137. package/dist/chunk-HXFVOR7Z.js +496 -0
  138. package/dist/chunk-HXFVOR7Z.js.map +1 -0
  139. package/dist/chunk-IJWYS2PF.js +122 -0
  140. package/dist/chunk-IJWYS2PF.js.map +1 -0
  141. package/dist/chunk-INCHXAFK.js +1395 -0
  142. package/dist/chunk-INCHXAFK.js.map +1 -0
  143. package/dist/chunk-JT6BRGQM.js +1 -0
  144. package/dist/chunk-JT6BRGQM.js.map +1 -0
  145. package/dist/chunk-L6PMAEMB.js +115 -0
  146. package/dist/chunk-L6PMAEMB.js.map +1 -0
  147. package/dist/chunk-LOUGW6MU.js +126 -0
  148. package/dist/chunk-LOUGW6MU.js.map +1 -0
  149. package/dist/chunk-LYPZPE53.js +129 -0
  150. package/dist/chunk-LYPZPE53.js.map +1 -0
  151. package/dist/chunk-NNCCN7JP.js +186 -0
  152. package/dist/chunk-NNCCN7JP.js.map +1 -0
  153. package/dist/chunk-PBC3MZUL.js +212 -0
  154. package/dist/chunk-PBC3MZUL.js.map +1 -0
  155. package/dist/chunk-R4ADP2DC.js +20 -0
  156. package/dist/chunk-R4ADP2DC.js.map +1 -0
  157. package/dist/chunk-RHN4Y6VS.js +44 -0
  158. package/dist/chunk-RHN4Y6VS.js.map +1 -0
  159. package/dist/chunk-THYUY5ZZ.js +204 -0
  160. package/dist/chunk-THYUY5ZZ.js.map +1 -0
  161. package/dist/chunk-UGNTCOVR.js +28 -0
  162. package/dist/chunk-UGNTCOVR.js.map +1 -0
  163. package/dist/chunk-VC2PCK2S.js +31 -0
  164. package/dist/chunk-VC2PCK2S.js.map +1 -0
  165. package/dist/chunk-VF5YDZJC.js +305 -0
  166. package/dist/chunk-VF5YDZJC.js.map +1 -0
  167. package/dist/chunk-VISRDVTK.js +675 -0
  168. package/dist/chunk-VISRDVTK.js.map +1 -0
  169. package/dist/chunk-W6O3LVBY.js +102 -0
  170. package/dist/chunk-W6O3LVBY.js.map +1 -0
  171. package/dist/chunk-WFEAKJLR.js +47 -0
  172. package/dist/chunk-WFEAKJLR.js.map +1 -0
  173. package/dist/chunk-XIEY2OF4.js +67 -0
  174. package/dist/chunk-XIEY2OF4.js.map +1 -0
  175. package/dist/chunk-XITLHDG3.js +37 -0
  176. package/dist/chunk-XITLHDG3.js.map +1 -0
  177. package/dist/chunk-XMWPYZWZ.js +61 -0
  178. package/dist/chunk-XMWPYZWZ.js.map +1 -0
  179. package/dist/chunk-XUP6CNOM.js +34 -0
  180. package/dist/chunk-XUP6CNOM.js.map +1 -0
  181. package/dist/chunk-YABTQZDJ.js +25 -0
  182. package/dist/chunk-YABTQZDJ.js.map +1 -0
  183. package/dist/chunk-YMJ7BRKU.js +510 -0
  184. package/dist/chunk-YMJ7BRKU.js.map +1 -0
  185. package/dist/chunk-ZC3XWU7B.js +28 -0
  186. package/dist/chunk-ZC3XWU7B.js.map +1 -0
  187. package/dist/chunk-ZHMTD5KV.js +90 -0
  188. package/dist/chunk-ZHMTD5KV.js.map +1 -0
  189. package/dist/chunk-ZWZDLOE7.js +160 -0
  190. package/dist/chunk-ZWZDLOE7.js.map +1 -0
  191. package/dist/db-package/db/local-first-sqlite-adapter.d.ts +432 -0
  192. package/dist/db-package/db/local-first-sqlite-adapter.js +7 -0
  193. package/dist/db-package/db/local-first-sqlite-adapter.js.map +1 -0
  194. package/dist/db-package/index.d.ts +7 -0
  195. package/dist/db-package/index.js +16 -0
  196. package/dist/db-package/index.js.map +1 -0
  197. package/dist/db-package/migration/aggregator.d.ts +86 -0
  198. package/dist/db-package/migration/aggregator.js +7 -0
  199. package/dist/db-package/migration/aggregator.js.map +1 -0
  200. package/dist/db-package/migration/index.d.ts +3 -0
  201. package/dist/db-package/migration/index.js +8 -0
  202. package/dist/db-package/migration/index.js.map +1 -0
  203. package/dist/engine.d.ts +205 -0
  204. package/dist/engine.js +20 -0
  205. package/dist/engine.js.map +1 -0
  206. package/dist/entitlements/index.d.ts +313 -0
  207. package/dist/entitlements/index.js +23 -0
  208. package/dist/entitlements/index.js.map +1 -0
  209. package/dist/execution/index.d.ts +50 -0
  210. package/dist/execution/index.js +57 -0
  211. package/dist/execution/index.js.map +1 -0
  212. package/dist/extensions/index.d.ts +172 -0
  213. package/dist/extensions/index.js +95 -0
  214. package/dist/extensions/index.js.map +1 -0
  215. package/dist/health/index.d.ts +35 -0
  216. package/dist/health/index.js +7 -0
  217. package/dist/health/index.js.map +1 -0
  218. package/dist/index.d.ts +39 -0
  219. package/dist/index.js +160 -0
  220. package/dist/index.js.map +1 -0
  221. package/dist/lifecycle/index.d.ts +133 -0
  222. package/dist/lifecycle/index.js +13 -0
  223. package/dist/lifecycle/index.js.map +1 -0
  224. package/dist/lifecycle-engine/backend-migration.d.ts +2 -0
  225. package/dist/lifecycle-engine/backend-migration.js +11 -0
  226. package/dist/lifecycle-engine/backend-migration.js.map +1 -0
  227. package/dist/lifecycle-engine/baseline.d.ts +2 -0
  228. package/dist/lifecycle-engine/baseline.js +9 -0
  229. package/dist/lifecycle-engine/baseline.js.map +1 -0
  230. package/dist/lifecycle-engine/index.d.ts +1497 -0
  231. package/dist/lifecycle-engine/index.js +41 -0
  232. package/dist/lifecycle-engine/index.js.map +1 -0
  233. package/dist/lifecycle-engine/platform/artifacts.d.ts +4 -0
  234. package/dist/lifecycle-engine/platform/artifacts.js +7 -0
  235. package/dist/lifecycle-engine/platform/artifacts.js.map +1 -0
  236. package/dist/lifecycle-engine/platform/control-plane.d.ts +12 -0
  237. package/dist/lifecycle-engine/platform/control-plane.js +2 -0
  238. package/dist/lifecycle-engine/platform/control-plane.js.map +1 -0
  239. package/dist/lifecycle-engine/platform/database-router.d.ts +118 -0
  240. package/dist/lifecycle-engine/platform/database-router.js +7 -0
  241. package/dist/lifecycle-engine/platform/database-router.js.map +1 -0
  242. package/dist/lifecycle-engine/platform/deployment-placements.d.ts +464 -0
  243. package/dist/lifecycle-engine/platform/deployment-placements.js +16 -0
  244. package/dist/lifecycle-engine/platform/deployment-placements.js.map +1 -0
  245. package/dist/lifecycle-engine/platform/deployment.d.ts +4 -0
  246. package/dist/lifecycle-engine/platform/deployment.js +17 -0
  247. package/dist/lifecycle-engine/platform/deployment.js.map +1 -0
  248. package/dist/lifecycle-engine/platform/index.d.ts +737 -0
  249. package/dist/lifecycle-engine/platform/index.js +60 -0
  250. package/dist/lifecycle-engine/platform/index.js.map +1 -0
  251. package/dist/lifecycle-engine/platform/runtime.d.ts +80 -0
  252. package/dist/lifecycle-engine/platform/runtime.js +11 -0
  253. package/dist/lifecycle-engine/platform/runtime.js.map +1 -0
  254. package/dist/lifecycle-engine/platform/site-publication.d.ts +102 -0
  255. package/dist/lifecycle-engine/platform/site-publication.js +7 -0
  256. package/dist/lifecycle-engine/platform/site-publication.js.map +1 -0
  257. package/dist/lifecycle-engine/platform/user-directory.d.ts +139 -0
  258. package/dist/lifecycle-engine/platform/user-directory.js +13 -0
  259. package/dist/lifecycle-engine/platform/user-directory.js.map +1 -0
  260. package/dist/mcp.d.ts +92 -0
  261. package/dist/mcp.js +10 -0
  262. package/dist/mcp.js.map +1 -0
  263. package/dist/middleware/auth.d.ts +118 -0
  264. package/dist/middleware/auth.js +7 -0
  265. package/dist/middleware/auth.js.map +1 -0
  266. package/dist/middleware/error-handler.d.ts +35 -0
  267. package/dist/middleware/error-handler.js +7 -0
  268. package/dist/middleware/error-handler.js.map +1 -0
  269. package/dist/middleware/module-guard.d.ts +33 -0
  270. package/dist/middleware/module-guard.js +7 -0
  271. package/dist/middleware/module-guard.js.map +1 -0
  272. package/dist/platform-admin/index.d.ts +37 -0
  273. package/dist/platform-admin/index.js +11 -0
  274. package/dist/platform-admin/index.js.map +1 -0
  275. package/dist/platform-admin/store.d.ts +346 -0
  276. package/dist/platform-admin/store.js +7 -0
  277. package/dist/platform-admin/store.js.map +1 -0
  278. package/dist/plugin-data/annotations.d.ts +6 -0
  279. package/dist/plugin-data/annotations.js +12 -0
  280. package/dist/plugin-data/annotations.js.map +1 -0
  281. package/dist/plugin-data/atomic-context.d.ts +41 -0
  282. package/dist/plugin-data/atomic-context.js +9 -0
  283. package/dist/plugin-data/atomic-context.js.map +1 -0
  284. package/dist/plugin-data/atomic.d.ts +6 -0
  285. package/dist/plugin-data/atomic.js +26 -0
  286. package/dist/plugin-data/atomic.js.map +1 -0
  287. package/dist/plugin-data/audit.d.ts +6 -0
  288. package/dist/plugin-data/audit.js +12 -0
  289. package/dist/plugin-data/audit.js.map +1 -0
  290. package/dist/plugin-data/commands.d.ts +6 -0
  291. package/dist/plugin-data/commands.js +8 -0
  292. package/dist/plugin-data/commands.js.map +1 -0
  293. package/dist/plugin-data/composite.d.ts +6 -0
  294. package/dist/plugin-data/composite.js +8 -0
  295. package/dist/plugin-data/composite.js.map +1 -0
  296. package/dist/plugin-data/delivery.d.ts +6 -0
  297. package/dist/plugin-data/delivery.js +16 -0
  298. package/dist/plugin-data/delivery.js.map +1 -0
  299. package/dist/plugin-data/index.d.ts +2798 -0
  300. package/dist/plugin-data/index.js +102 -0
  301. package/dist/plugin-data/index.js.map +1 -0
  302. package/dist/plugin-data/json-documents.d.ts +6 -0
  303. package/dist/plugin-data/json-documents.js +16 -0
  304. package/dist/plugin-data/json-documents.js.map +1 -0
  305. package/dist/plugin-data/outbox.d.ts +6 -0
  306. package/dist/plugin-data/outbox.js +16 -0
  307. package/dist/plugin-data/outbox.js.map +1 -0
  308. package/dist/plugin-data/scoped-records.d.ts +6 -0
  309. package/dist/plugin-data/scoped-records.js +10 -0
  310. package/dist/plugin-data/scoped-records.js.map +1 -0
  311. package/dist/plugin-sdk.d.ts +41 -0
  312. package/dist/plugin-sdk.js +10 -0
  313. package/dist/plugin-sdk.js.map +1 -0
  314. package/dist/sql-lifecycle/index.d.ts +138 -0
  315. package/dist/sql-lifecycle/index.js +167 -0
  316. package/dist/sql-lifecycle/index.js.map +1 -0
  317. package/dist/storage/index.d.ts +2 -0
  318. package/dist/storage/index.js +13 -0
  319. package/dist/storage/index.js.map +1 -0
  320. package/dist/storage/s3-compatible.d.ts +139 -0
  321. package/dist/storage/s3-compatible.js +11 -0
  322. package/dist/storage/s3-compatible.js.map +1 -0
  323. package/dist/tenant.d.ts +108 -0
  324. package/dist/tenant.js +9 -0
  325. package/dist/tenant.js.map +1 -0
  326. package/dist/workspace-context.d.ts +119 -0
  327. package/dist/workspace-context.js +9 -0
  328. package/dist/workspace-context.js.map +1 -0
  329. package/docs/api.md +29753 -0
  330. package/docs/db/README.md +49 -0
  331. package/docs/db/docs/api.md +1613 -0
  332. package/docs/db/docs/guide.md +47 -0
  333. package/docs/entitlements.md +60 -0
  334. package/docs/guide.md +48 -0
  335. package/docs/index.md +53 -0
  336. package/docs/ja/db/README.md +46 -0
  337. package/docs/ja/db/docs/guide.md +53 -0
  338. package/docs/ja/guide.md +46 -0
  339. package/docs/ja/lifecycle/README.md +46 -0
  340. package/docs/ja/lifecycle/docs/guide.md +45 -0
  341. package/docs/ja/storage/README.md +46 -0
  342. package/docs/ja/storage/docs/guide.md +43 -0
  343. package/docs/lifecycle/README.md +49 -0
  344. package/docs/lifecycle/docs/api.md +3770 -0
  345. package/docs/lifecycle/docs/baseline.md +50 -0
  346. package/docs/lifecycle/docs/guide.md +47 -0
  347. package/docs/storage/README.md +49 -0
  348. package/docs/storage/docs/api.md +470 -0
  349. package/docs/storage/docs/guide.md +47 -0
  350. package/package.json +345 -4
@@ -0,0 +1,34 @@
1
+ // src/browser-auth/platform-session.ts
2
+ function createPlatformSessionResolver(options) {
3
+ const requests = /* @__PURE__ */ new WeakMap();
4
+ return (context, resolution) => {
5
+ const cached = requests.get(context);
6
+ if (cached && !resolution?.refresh) return cached;
7
+ const result = (async () => {
8
+ const bearer = context.req.header("Authorization");
9
+ if (bearer && !/^Bearer [^\s]+$/.test(bearer)) return null;
10
+ const identity = await options.verify({ cookie: context.req.header("Cookie"), sessionToken: bearer?.slice(7) ?? context.req.header("X-Session-Token") });
11
+ if (!identity) return null;
12
+ const tenantId = context.req.header("X-Tenant-Id");
13
+ if (tenantId && !/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(tenantId)) return null;
14
+ const matches = await options.memberships(identity.userId, tenantId, identity.identity);
15
+ if (matches.length !== 1) return null;
16
+ const member = matches[0];
17
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(member.tenantId) || tenantId && member.tenantId !== tenantId) return null;
18
+ if (!Array.isArray(member.roles) || !Array.isArray(member.enabledModules)) throw new Error("Invalid membership");
19
+ if (![...member.roles, ...member.enabledModules].every((x) => typeof x === "string" && x.length > 0)) throw new Error("Invalid membership");
20
+ const state = await options.store.read();
21
+ if (!state.platform.tenants[member.tenantId]?.active) return null;
22
+ const expected = options.targetFor(member.tenantId);
23
+ if (state.platform.placements[member.tenantId]?.backendId !== expected.id) throw new Error("Tenant backend placement mismatch");
24
+ return { userId: identity.userId, tenantId: member.tenantId, roles: [...member.roles], enabledModules: [...member.enabledModules] };
25
+ })();
26
+ requests.set(context, result);
27
+ return result;
28
+ };
29
+ }
30
+
31
+ export {
32
+ createPlatformSessionResolver
33
+ };
34
+ //# sourceMappingURL=chunk-XUP6CNOM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser-auth/platform-session.ts"],"sourcesContent":["import type { Context } from 'hono'\nimport { createKratosSessionVerifier, type BrowserSession } from './index'\nimport type { LifecycleStore, BackendTarget } from '@alneas/eaos-server/lifecycle-engine'\n\n/**\n * Verified tenant membership candidate supplied to {@link createPlatformSessionResolver}.\n */\nexport interface Membership {\n /** Tenant ID matching the selected header, if provided; validated as a 1–128-character scope identifier. */\n tenantId: string\n /** Verified role names, each a nonempty string. */\n roles: string[]\n /** Verified entitlement names, each a nonempty string. */\n enabledModules: string[]\n}\n/**\n * Combines a verified Kratos identity, unambiguous membership, active tenant, and expected backend placement.\n * @param options - verify checks credentials; memberships receives verified userId, optional selected tenant ID, and provider identity and resolves candidate memberships; store supplies lifecycle state; targetFor synchronously resolves the expected backend for a selected tenant.\n * @returns A resolver accepting Hono context and optional refresh flag. It resolves a BrowserSession or null for invalid credentials/selection, ambiguous membership, or inactive tenant. Each context caches the in-flight/result Promise unless refresh is true.\n * @throws The resolver rejects verifier, membership, store, and targetFor errors, malformed membership fields, or backend placement mismatch. Rejected Promises are cached until refresh.\n * @remarks Authorization accepts only the exact Bearer syntax with one space and a nonwhitespace token. Bearer token takes precedence over X-Session-Token; the verifier gives session tokens precedence over cookies. X-Tenant-Id selects a candidate but never grants membership. No default tenant or role is invented. The WeakMap cache is per resolver/context; refresh starts another lookup without cancelling previous work.\n * @example\n * ```typescript\n * import { createPlatformSessionResolver } from \"@alneas/eaos-server/browser-auth/platform-session\";\n * export function hostSessionResolver(options: Parameters<typeof createPlatformSessionResolver>[0]) {\n * // Supply verified membership reads, lifecycle store, and backend routing.\n * return createPlatformSessionResolver(options);\n * }\n * ```\n */\nexport function createPlatformSessionResolver(options: {\n /**\n * Server whoami verifier receiving cookie/session-token credentials; token transport takes precedence over cookies.\n */\n verify: ReturnType<typeof createKratosSessionVerifier>\n /**\n * Host callback receiving verified user ID, optional requested tenant, and provider identity; must resolve exactly one authorized membership or resolution returns null.\n */\n memberships: (userId: string, tenantId: string | undefined, identity: Record<string, unknown>) => Promise<Membership[]>\n /**\n * Authoritative lifecycle store used to require an active tenant and its expected backend placement.\n */\n store: LifecycleStore\n /**\n * Synchronous host backend selector for the verified membership tenant; an ID differing from lifecycle placement rejects resolution.\n */\n targetFor: (tenantId: string) => BackendTarget\n}) {\n const requests = new WeakMap<Context, Promise<BrowserSession | null>>()\n return (context: Context, resolution?: { refresh?: boolean }): Promise<BrowserSession | null> => {\n const cached = requests.get(context)\n if (cached && !resolution?.refresh) return cached\n const result = (async () => {\n const bearer = context.req.header('Authorization')\n if (bearer && !/^Bearer [^\\s]+$/.test(bearer)) return null\n const identity = await options.verify({ cookie: context.req.header('Cookie'), sessionToken: bearer?.slice(7) ?? context.req.header('X-Session-Token') })\n if (!identity) return null\n const tenantId = context.req.header('X-Tenant-Id')\n if (tenantId && !/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(tenantId)) return null\n const matches = await options.memberships(identity.userId, tenantId, identity.identity)\n if (matches.length !== 1) return null // No default tenant or role for ambiguous/unregistered users.\n const member = matches[0]\n if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(member.tenantId) || (tenantId && member.tenantId !== tenantId)) return null\n if (!Array.isArray(member.roles) || !Array.isArray(member.enabledModules)) throw new Error('Invalid membership')\n if (![...member.roles, ...member.enabledModules].every(x => typeof x === 'string' && x.length > 0)) throw new Error('Invalid membership')\n const state = await options.store.read()\n if (!state.platform.tenants[member.tenantId]?.active) return null\n const expected = options.targetFor(member.tenantId)\n if (state.platform.placements[member.tenantId]?.backendId !== expected.id) throw new Error('Tenant backend placement mismatch')\n return { userId: identity.userId, tenantId: member.tenantId, roles: [...member.roles], enabledModules: [...member.enabledModules] }\n })()\n requests.set(context, result)\n return result\n }\n}\n"],"mappings":";AA8BO,SAAS,8BAA8B,SAiB3C;AACD,QAAM,WAAW,oBAAI,QAAiD;AACtE,SAAO,CAAC,SAAkB,eAAuE;AAC/F,UAAM,SAAS,SAAS,IAAI,OAAO;AACnC,QAAI,UAAU,CAAC,YAAY,QAAS,QAAO;AAC3C,UAAM,UAAU,YAAY;AAC1B,YAAM,SAAS,QAAQ,IAAI,OAAO,eAAe;AACjD,UAAI,UAAU,CAAC,kBAAkB,KAAK,MAAM,EAAG,QAAO;AACtD,YAAM,WAAW,MAAM,QAAQ,OAAO,EAAE,QAAQ,QAAQ,IAAI,OAAO,QAAQ,GAAG,cAAc,QAAQ,MAAM,CAAC,KAAK,QAAQ,IAAI,OAAO,iBAAiB,EAAE,CAAC;AACvJ,UAAI,CAAC,SAAU,QAAO;AACtB,YAAM,WAAW,QAAQ,IAAI,OAAO,aAAa;AACjD,UAAI,YAAY,CAAC,oCAAoC,KAAK,QAAQ,EAAG,QAAO;AAC5E,YAAM,UAAU,MAAM,QAAQ,YAAY,SAAS,QAAQ,UAAU,SAAS,QAAQ;AACtF,UAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,YAAM,SAAS,QAAQ,CAAC;AACxB,UAAI,CAAC,oCAAoC,KAAK,OAAO,QAAQ,KAAM,YAAY,OAAO,aAAa,SAAW,QAAO;AACrH,UAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,CAAC,MAAM,QAAQ,OAAO,cAAc,EAAG,OAAM,IAAI,MAAM,oBAAoB;AAC/G,UAAI,CAAC,CAAC,GAAG,OAAO,OAAO,GAAG,OAAO,cAAc,EAAE,MAAM,OAAK,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC,EAAG,OAAM,IAAI,MAAM,oBAAoB;AACxI,YAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK;AACvC,UAAI,CAAC,MAAM,SAAS,QAAQ,OAAO,QAAQ,GAAG,OAAQ,QAAO;AAC7D,YAAM,WAAW,QAAQ,UAAU,OAAO,QAAQ;AAClD,UAAI,MAAM,SAAS,WAAW,OAAO,QAAQ,GAAG,cAAc,SAAS,GAAI,OAAM,IAAI,MAAM,mCAAmC;AAC9H,aAAO,EAAE,QAAQ,SAAS,QAAQ,UAAU,OAAO,UAAU,OAAO,CAAC,GAAG,OAAO,KAAK,GAAG,gBAAgB,CAAC,GAAG,OAAO,cAAc,EAAE;AAAA,IACpI,GAAG;AACH,aAAS,IAAI,SAAS,MAAM;AAC5B,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,25 @@
1
+ // src/agentic/types.ts
2
+ var ServerAgentExecutionError = class extends Error {
3
+ /** Classification used by runner normalization and durable retry handling. */
4
+ failureCode;
5
+ /** Host retry advice; true does not itself retry or make execution idempotent. */
6
+ retryable;
7
+ /**
8
+ * Creates a runtime failure retaining an optional underlying cause.
9
+ * @param message - Human-readable failure description.
10
+ * @param failureCode - Stable classification for caller retry policy.
11
+ * @param retryable - Whether the caller may consider retrying this failure.
12
+ * @param options - Optional cause; omission does not attach an error cause.
13
+ */
14
+ constructor(message, failureCode, retryable, options) {
15
+ super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
16
+ this.name = "ServerAgentExecutionError";
17
+ this.failureCode = failureCode;
18
+ this.retryable = retryable;
19
+ }
20
+ };
21
+
22
+ export {
23
+ ServerAgentExecutionError
24
+ };
25
+ //# sourceMappingURL=chunk-YABTQZDJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agentic/types.ts"],"sourcesContent":["import type { AgentJobKind, AgentRuntimeResourcePolicy, ApprovalLevel, RiskLevel, WorkflowAgentFailureCode, WorkflowAgentToolMode } from '@alneas/eaos-core'\n\n/**\n * Runtime selector used to dispatch server tasks to built-in or host-provided executors.\n * @see ServerAgentExecutor for the host adapter contract.\n */\nexport type ServerAgentWorkerType = 'goose' | 'hermes_agent' | 'litellm' | 'local_cli' | 'local_mlx' | 'external_no_training' | 'custom'\n/**\n * Selects server-side queue execution or authenticated local runner pull delivery.\n * @see ServerAgentExecutionContext.\n */\nexport type ServerAgentDispatchMode = 'server-queue' | 'local-pull'\n\n/**\n * Host-admitted task passed to an agent runtime; tenant/user claims must already be verified.\n * @see ServerAgentExecutor for a complete adapter example.\n */\nexport interface ServerAgentTaskRequest {\n /** Stable task identifier used for result and checkpoint binding. */\n id: string\n /** Verified tenant ID; never accepted directly as an authenticated request-body claim. */\n tenantId: string\n /** Verified submitting user ID. */\n userId: string\n /** Nonblank task label; ServerAutonomousWorker limits this to 500 characters. */\n title: string\n /** Nonblank runtime instructions; ServerAutonomousWorker limits this to 1000000 characters. */\n instructions: string\n /** Selected built-in or host runtime adapter key. */\n workerType: ServerAgentWorkerType\n /** Why the agent is running; not limited to task-graph workflows. */\n jobKind?: AgentJobKind\n /** Optional domain context; interpretation belongs to the executor. */\n context?: Record<string, unknown>\n /** Host-verified workflow context propagated to the external runtime. */\n executionContext?: ServerAgentExecutionContext\n /** Framework-selected resource policy. Callers cannot loosen this policy. */\n runtimePolicy?: AgentRuntimeResourcePolicy\n /** Optional execution timeout in milliseconds; built-in executors default to 60000 and accept safe integers from 1 to 3600000. */\n timeoutMs?: number\n}\n\n/**\n * Host-selected workflow, correlation, tool scope, and local-runner bindings sent to the runtime.\n * @see ServerAgentTaskRequest.\n */\nexport interface ServerAgentExecutionContext {\n /** Optional workflow/session association. */\n workflowId?: string\n /** Optional host tracing identifier. */\n correlationId?: string\n /** Optional host deduplication key; propagation alone does not enforce idempotency. */\n idempotencyKey?: string\n /** Optional workflow tool-execution mode admitted by the host. */\n toolMode?: WorkflowAgentToolMode\n /** Optional allowlisted tool names; runtime adapters must enforce the intended boundary. */\n allowedToolNames?: readonly string[]\n /** Selected by the server broker; clients cannot override it. */\n routeId?: string\n /** Optional delivery mode; local-pull jobs are claimed by authenticated local runners. */\n dispatchMode?: ServerAgentDispatchMode\n /** User/runner binding for local subscription or MLX execution. */\n runnerId?: string\n}\n\n/**\n * Normalized runtime outcome; FAILED and PENDING_APPROVAL are returned data rather than successful output.\n * @see ServerAgentExecutor.\n */\nexport interface ServerAgentTaskResult {\n /** Task identifier to which this result belongs. */\n taskId: string\n /** COMPLETED has runtime output; PENDING_APPROVAL awaits a host decision; FAILED records a rejected or failed run. */\n status: 'COMPLETED' | 'PENDING_APPROVAL' | 'FAILED'\n /** Runtime text when available; omitted on failures and pending approval. */\n output?: string\n /** Optional failure message for FAILED results. */\n error?: string\n /** Stable failure classification for durable retry/circuit-breaker handling. */\n failureCode?: WorkflowAgentFailureCode\n /** Optional retry advice; absence leaves retry policy to the caller. */\n retryable?: boolean\n /** Optional generated approval binding for PENDING_APPROVAL. */\n approvalTicketId?: string\n /** Optional human-approval level returned by the gatekeeper. */\n approvalLevel?: ApprovalLevel\n /** Optional risk classification returned by the gatekeeper. */\n riskLevel?: RiskLevel\n /** Elapsed wall-clock milliseconds for this attempt. */\n durationMs: number\n}\n\n/**\n * Carries stable failure classification and retry advice from runtime adapters to queue coordination.\n * @example\n * ```typescript\n * import { ServerAgentExecutionError } from \"@alneas/eaos-server/agentic/types\";\n * const error = new ServerAgentExecutionError(\"Runtime unavailable\", \"AGENT_UNAVAILABLE\", true);\n * console.log(error.failureCode, error.retryable);\n * ```\n */\nexport class ServerAgentExecutionError extends Error {\n /** Classification used by runner normalization and durable retry handling. */\n readonly failureCode: WorkflowAgentFailureCode\n /** Host retry advice; true does not itself retry or make execution idempotent. */\n readonly retryable: boolean\n\n /**\n * Creates a runtime failure retaining an optional underlying cause.\n * @param message - Human-readable failure description.\n * @param failureCode - Stable classification for caller retry policy.\n * @param retryable - Whether the caller may consider retrying this failure.\n * @param options - Optional cause; omission does not attach an error cause.\n */\n constructor(message: string, failureCode: WorkflowAgentFailureCode, retryable: boolean, options?: { \n /**\n * Optional underlying error attached through the standard Error cause option; omission leaves the cause unset.\n */\n cause?: unknown }) {\n super(message, options?.cause === undefined ? undefined : { cause: options.cause })\n this.name = 'ServerAgentExecutionError'\n this.failureCode = failureCode\n this.retryable = retryable\n }\n}\n\n/**\n * Host- or plugin-owned agent execution adapter.\n *\n * The server owns admission, authorization and result normalization; the\n * adapter owns the configured runtime (OpenCode, Codex, Goose, LiteLLM, etc.).\n * @param request - Verified task and execution context; adapters must enforce host runtime/tool boundaries.\n * @returns Runtime text output, which is not permission to perform business side effects.\n * @throws Reject for unavailable, failed, timed-out, or invalid runtime work; ServerAgentExecutionError preserves classification and retry advice.\n * @example\n * ```typescript\n * import type { ServerAgentExecutor } from \"@alneas/eaos-server/agentic/types\";\n * export function instrument(execute: ServerAgentExecutor): ServerAgentExecutor {\n * return async request => execute(structuredClone(request));\n * }\n * ```\n */\nexport type ServerAgentExecutor = (request: ServerAgentTaskRequest) => Promise<string>\n\n/**\n * Host-verified approval decision submitted against a pending task checkpoint.\n * @remarks The host authenticates the approver and verifies role authority before passing this payload to a coordinator.\n * @see ServerAgentTaskResult.\n */\nexport interface ServerApprovalCallbackPayload {\n /** Task checkpoint identifier whose approval is being decided. */\n taskId: string\n /** Optional external ticket binding; when supplied it must match the checkpoint. */\n approvalId?: string\n /** Optional binding hash for clients that persist the approval payload. */\n bindingHash?: string\n /** True to approve; false to reject the pending work. */\n approved: boolean\n /** Authenticated host approver identifier. */\n approverId: string\n /** Verified role used for approval authorization. */\n approverRole: string\n /** Optional human decision note. */\n comment?: string\n}\n"],"mappings":";AAqGO,IAAM,4BAAN,cAAwC,MAAM;AAAA;AAAA,EAE1C;AAAA;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,YAAY,SAAiB,aAAuC,WAAoB,SAIrE;AACjB,UAAM,SAAS,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;AAClF,SAAK,OAAO;AACZ,SAAK,cAAc;AACnB,SAAK,YAAY;AAAA,EACnB;AACF;","names":[]}
@@ -0,0 +1,510 @@
1
+ // src/platform-admin/store.ts
2
+ import { assertBackendMigrationCutover, canonical, normalizeLifecycleState, planBackendMigration } from "@alneas/eaos-server/lifecycle-engine";
3
+ var key = (...parts) => JSON.stringify(parts);
4
+ var identifier = (value, name) => {
5
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(value)) throw new Error(`Invalid ${name}`);
6
+ return value;
7
+ };
8
+ var text = (value, name, max = 256) => {
9
+ if (!value.trim() || value.length > max) throw new Error(`Invalid ${name}`);
10
+ return value;
11
+ };
12
+ var timestamp = (value, name) => {
13
+ if (!Number.isFinite(Date.parse(value))) throw new Error(`Invalid ${name}`);
14
+ return value;
15
+ };
16
+ function materializeBackend(state, backendId) {
17
+ const backend = state.backends[backendId];
18
+ if (!backend) return null;
19
+ return { ...backend, ...state.platform.backendMetadata[backendId] };
20
+ }
21
+ function targetOf(backend) {
22
+ return { id: backend.id, databaseId: backend.databaseId, dialect: backend.dialect, mode: backend.mode, ...backend.tenantId === void 0 ? {} : { tenantId: backend.tenantId } };
23
+ }
24
+ function placement(state, tenantId) {
25
+ const record = state.platform.placements[tenantId];
26
+ if (!record) return null;
27
+ const backend = materializeBackend(state, record.backendId);
28
+ if (!backend) throw new Error(`Tenant placement references unknown backend: ${record.backendId}`);
29
+ return { tenantId, backend, routingRevision: record.routingRevision, changedAt: record.changedAt };
30
+ }
31
+ function validateBackend(backend) {
32
+ identifier(backend.id, "backend id");
33
+ identifier(backend.databaseId, "database id");
34
+ if (!["sqlite", "postgres", "oracle"].includes(backend.dialect) || !["shared", "dedicated"].includes(backend.mode)) throw new Error("Invalid backend target");
35
+ if (backend.mode === "dedicated") identifier(backend.tenantId ?? "", "dedicated backend tenant");
36
+ if (backend.mode === "shared" && backend.tenantId !== void 0) throw new Error("Shared backend cannot bind a tenant");
37
+ }
38
+ var LifecyclePlatformControlStore = class {
39
+ /**
40
+ * Captures the shared authoritative lifecycle ledger and clock.
41
+ * @param lifecycle - Host store providing consistent reads and exclusive writer callbacks.
42
+ * @param now - Clock for mutation timestamps; defaults to a fresh Date.
43
+ */
44
+ constructor(lifecycle, now = () => /* @__PURE__ */ new Date()) {
45
+ this.lifecycle = lifecycle;
46
+ this.now = now;
47
+ }
48
+ lifecycle;
49
+ now;
50
+ async read() {
51
+ return normalizeLifecycleState(await this.lifecycle.read());
52
+ }
53
+ async mutate(fn) {
54
+ return this.lifecycle.withLock(async (writer) => {
55
+ const state = normalizeLifecycleState(await writer.read());
56
+ const result = await fn(state);
57
+ state.revision += 1;
58
+ await writer.write(state);
59
+ return result;
60
+ });
61
+ }
62
+ /**
63
+ * Lists tenant records from the authoritative normalized lifecycle snapshot.
64
+ * @returns Tenant records with cloned domains arrays, or an empty array.
65
+ * @throws Host read/normalization failure.
66
+ * @example
67
+ * ```typescript
68
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
69
+ * export async function listTenants(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["listTenants"]>) {
70
+ * return store.listTenants(...args);
71
+ * }
72
+ * ```
73
+ */
74
+ async listTenants() {
75
+ const state = await this.read();
76
+ return Object.values(state.platform.tenants).map((tenant) => ({ ...tenant, domains: [...tenant.domains] }));
77
+ }
78
+ /**
79
+ * Creates an active tenant and its initial backend routing record under the lifecycle writer lock.
80
+ * @param input - Valid scope ID, nonblank name, nonempty domain strings, and registered initial backendId; a dedicated backend must bind the same tenant.
81
+ * @returns Created tenant record with clock timestamps; initial routing revision is one.
82
+ * @throws Invalid input, duplicate tenant, missing/mismatched backend, or host lock/write/clock failure.
83
+ * @example
84
+ * ```typescript
85
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
86
+ * export async function createTenant(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["createTenant"]>) {
87
+ * return store.createTenant(...args);
88
+ * }
89
+ * ```
90
+ */
91
+ async createTenant(input) {
92
+ const id = identifier(input.id, "tenant id");
93
+ const name = text(input.name, "tenant name");
94
+ const backendId = identifier(input.backendId, "initial backend id");
95
+ if (!Array.isArray(input.domains) || input.domains.length === 0 || input.domains.some((domain) => typeof domain !== "string" || !domain.trim())) throw new Error("Invalid tenant domains");
96
+ return this.mutate((state) => {
97
+ if (state.platform.tenants[id]) throw new Error("Tenant already exists");
98
+ const backend = materializeBackend(state, backendId);
99
+ if (!backend) throw new Error("Initial backend not found");
100
+ if (backend.mode === "dedicated" && backend.tenantId !== id) throw new Error("Initial backend tenant mismatch");
101
+ const at = this.now().toISOString();
102
+ const tenant = { id, name, active: true, domains: [...input.domains], createdAt: at, updatedAt: at };
103
+ state.platform.tenants[id] = tenant;
104
+ state.platform.placements[id] = { tenantId: id, backendId, routingRevision: 1, changedAt: at };
105
+ return tenant;
106
+ });
107
+ }
108
+ /**
109
+ * Updates an existing tenant name, active flag, or domains and refreshes updatedAt.
110
+ * @param tenantId - Existing valid tenant scope ID.
111
+ * @param patch - Optional nonblank name, boolean active, and nonempty nonblank domains; omitted fields remain unchanged.
112
+ * @returns Updated tenant with cloned domains.
113
+ * @throws Missing tenant, invalid supplied fields, or host lock/write/clock failure.
114
+ * @example
115
+ * ```typescript
116
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
117
+ * export async function updateTenant(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["updateTenant"]>) {
118
+ * return store.updateTenant(...args);
119
+ * }
120
+ * ```
121
+ */
122
+ async updateTenant(tenantId, patch) {
123
+ identifier(tenantId, "tenant id");
124
+ return this.mutate((state) => {
125
+ const current = state.platform.tenants[tenantId];
126
+ if (!current) throw new Error("Tenant not found");
127
+ if (patch.name !== void 0) current.name = text(patch.name, "tenant name");
128
+ if (patch.active !== void 0) {
129
+ if (typeof patch.active !== "boolean") throw new Error("Invalid tenant active state");
130
+ current.active = patch.active;
131
+ }
132
+ if (patch.domains !== void 0) {
133
+ if (!Array.isArray(patch.domains) || patch.domains.length === 0 || patch.domains.some((domain) => typeof domain !== "string" || !domain.trim())) throw new Error("Invalid tenant domains");
134
+ current.domains = [...patch.domains];
135
+ }
136
+ current.updatedAt = this.now().toISOString();
137
+ return { ...current, domains: [...current.domains] };
138
+ });
139
+ }
140
+ /**
141
+ * Registers an immutable backend placement identity and replaceable public metadata.
142
+ * @param backend - Valid backend/database IDs, dialect/mode, optional dedicated tenant binding, and metadata.
143
+ * @returns Materialized registered target with metadata.
144
+ * @throws Invalid target, changed placement for an existing ID, database dialect conflict, or host store failure.
145
+ * @example
146
+ * ```typescript
147
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
148
+ * export async function registerBackend(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["registerBackend"]>) {
149
+ * return store.registerBackend(...args);
150
+ * }
151
+ * ```
152
+ */
153
+ async registerBackend(backend) {
154
+ validateBackend(backend);
155
+ return this.mutate((state) => {
156
+ const existing = state.backends[backend.id];
157
+ const target = { id: backend.id, databaseId: backend.databaseId, dialect: backend.dialect, mode: backend.mode, ...backend.tenantId === void 0 ? {} : { tenantId: backend.tenantId } };
158
+ if (existing && canonical(existing) !== canonical(target)) throw new Error("Backend placement is immutable; register a new backend ID");
159
+ for (const other of Object.values(state.backends)) if (other.id !== backend.id && other.databaseId === backend.databaseId && other.dialect !== backend.dialect) throw new Error("Database dialect conflict");
160
+ state.backends[backend.id] = target;
161
+ const { id: _id, databaseId: _databaseId, dialect: _dialect, mode: _mode, tenantId: _tenantId, ...metadata } = backend;
162
+ state.platform.backendMetadata[backend.id] = metadata;
163
+ return { ...target, ...metadata };
164
+ });
165
+ }
166
+ /**
167
+ * Materializes registered backend targets with their public metadata.
168
+ * @returns Backend list, or an empty array.
169
+ * @throws Host read/normalization failure.
170
+ * @example
171
+ * ```typescript
172
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
173
+ * export async function listBackends(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["listBackends"]>) {
174
+ * return store.listBackends(...args);
175
+ * }
176
+ * ```
177
+ */
178
+ async listBackends() {
179
+ const state = await this.read();
180
+ return Object.keys(state.backends).map((id) => materializeBackend(state, id)).filter(Boolean);
181
+ }
182
+ /**
183
+ * Reads a registered backend target and its public metadata.
184
+ * @param backendId - Valid registered backend scope ID.
185
+ * @returns Materialized backend, or null when unregistered.
186
+ * @throws Invalid ID or host read failure.
187
+ * @example
188
+ * ```typescript
189
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
190
+ * export async function getBackend(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["getBackend"]>) {
191
+ * return store.getBackend(...args);
192
+ * }
193
+ * ```
194
+ */
195
+ async getBackend(backendId) {
196
+ identifier(backendId, "backend id");
197
+ return materializeBackend(await this.read(), backendId);
198
+ }
199
+ /**
200
+ * Resolves a tenant routing record into its registered backend target.
201
+ * @param tenantId - Valid tenant scope ID.
202
+ * @returns Placement with routing revision/time and backend, or null when absent.
203
+ * @throws Invalid ID, dangling backend reference, or host read failure.
204
+ * @example
205
+ * ```typescript
206
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
207
+ * export async function getTenantPlacement(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["getTenantPlacement"]>) {
208
+ * return store.getTenantPlacement(...args);
209
+ * }
210
+ * ```
211
+ */
212
+ async getTenantPlacement(tenantId) {
213
+ identifier(tenantId, "tenant id");
214
+ return placement(await this.read(), tenantId);
215
+ }
216
+ /**
217
+ * Persists an immutable migration plan after checking registered backend identities.
218
+ * @param plan - Reviewed plan matching currently registered source/target; identical saved plans may be accepted before completion.
219
+ * @returns No value; resolves after storing the plan and initial planned record if absent.
220
+ * @throws Unregistered/changed backend, conflicting reused plan ID, already completed plan, clone or host store failure.
221
+ * @example
222
+ * ```typescript
223
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
224
+ * export async function saveMigrationPlan(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["saveMigrationPlan"]>) {
225
+ * return store.saveMigrationPlan(...args);
226
+ * }
227
+ * ```
228
+ */
229
+ async saveMigrationPlan(plan) {
230
+ await this.mutate((state) => {
231
+ if (!state.backends[plan.request.source.id] || !state.backends[plan.request.target.id]) throw new Error("Migration backend is not registered");
232
+ if (canonical(targetOf(state.backends[plan.request.source.id])) !== canonical(targetOf(plan.request.source)) || canonical(targetOf(state.backends[plan.request.target.id])) !== canonical(targetOf(plan.request.target))) throw new Error("Migration backend changed since planning");
233
+ const existing = state.platform.migrationPlans[plan.id];
234
+ if (existing && canonical(existing) !== canonical(plan)) throw new Error("Migration plan ID already contains a different plan");
235
+ const current = state.platform.migrationRecords[plan.id];
236
+ if (current?.status === "cutover" || current?.status === "rolled-back") throw new Error("Migration plan has already completed");
237
+ state.platform.migrationPlans[plan.id] = structuredClone(plan);
238
+ state.platform.migrationRecords[plan.id] ??= { status: "planned", updatedAt: plan.request.requestedAt };
239
+ });
240
+ }
241
+ /**
242
+ * Reads a cloned persisted migration plan by ID.
243
+ * @param migrationId - Valid migration scope ID.
244
+ * @returns Plan clone, or null when absent.
245
+ * @throws Invalid ID or host read/clone failure.
246
+ * @example
247
+ * ```typescript
248
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
249
+ * export async function getMigrationPlan(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["getMigrationPlan"]>) {
250
+ * return store.getMigrationPlan(...args);
251
+ * }
252
+ * ```
253
+ */
254
+ async getMigrationPlan(migrationId) {
255
+ identifier(migrationId, "migration id");
256
+ const state = await this.read();
257
+ const plan = state.platform.migrationPlans[migrationId];
258
+ return plan ? structuredClone(plan) : null;
259
+ }
260
+ /**
261
+ * Reads the persisted migration execution/reconciliation record.
262
+ * @param migrationId - Valid migration scope ID.
263
+ * @returns Cloned status record, or null when absent.
264
+ * @throws Invalid ID or host read/clone failure.
265
+ * @example
266
+ * ```typescript
267
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
268
+ * export async function getMigrationStatus(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["getMigrationStatus"]>) {
269
+ * return store.getMigrationStatus(...args);
270
+ * }
271
+ * ```
272
+ */
273
+ async getMigrationStatus(migrationId) {
274
+ identifier(migrationId, "migration id");
275
+ const record = (await this.read()).platform.migrationRecords[migrationId];
276
+ return record ? structuredClone(record) : null;
277
+ }
278
+ /**
279
+ * Binds an execution token and transitions a tenant migration record to running.
280
+ * @param input - Verified tenantId, persisted migrationId, and unique host executionId; plan scope must include the tenant.
281
+ * @returns Cloned running record, or completed cutover/rolled-back record unchanged.
282
+ * @throws Missing plan, tenant mismatch, conflicting active token, reconciling state, invalid IDs, or host store failure.
283
+ * @example
284
+ * ```typescript
285
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
286
+ * export async function beginMigrationExecution(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["beginMigrationExecution"]>) {
287
+ * return store.beginMigrationExecution(...args);
288
+ * }
289
+ * ```
290
+ */
291
+ async beginMigrationExecution(input) {
292
+ identifier(input.tenantId, "tenant id");
293
+ identifier(input.migrationId, "migration id");
294
+ identifier(input.executionId, "execution id");
295
+ return this.mutate((state) => {
296
+ const plan = state.platform.migrationPlans[input.migrationId];
297
+ const record = state.platform.migrationRecords[input.migrationId];
298
+ if (!plan || !record) throw new Error("Migration plan not found");
299
+ if (plan.request.scope.kind !== "tenants" || !plan.request.scope.tenantIds.includes(input.tenantId)) throw new Error("Migration plan tenant mismatch");
300
+ if (record.status === "cutover" || record.status === "rolled-back") return structuredClone(record);
301
+ if ((record.status === "running" || record.status === "verified") && record.executionId !== input.executionId) throw new Error("Migration execution already in progress");
302
+ if (record.status === "reconciling") throw new Error("Migration requires reconciliation before retry");
303
+ record.status = "running";
304
+ record.executionId = input.executionId;
305
+ record.error = void 0;
306
+ record.updatedAt = this.now().toISOString();
307
+ return structuredClone(record);
308
+ });
309
+ }
310
+ /**
311
+ * Records verified migration evidence and cutover data for the active execution token.
312
+ * @param input - Tenant, exact persisted plan, active executionId, verification report, and matching validated cutover.
313
+ * @returns Cloned verified migration record.
314
+ * @throws Invalid cutover/report, nonpersisted/changed plan, tenant/token/state mismatch, changed retry verification, or host store failure.
315
+ * @example
316
+ * ```typescript
317
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
318
+ * export async function recordMigrationVerification(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["recordMigrationVerification"]>) {
319
+ * return store.recordMigrationVerification(...args);
320
+ * }
321
+ * ```
322
+ */
323
+ async recordMigrationVerification(input) {
324
+ identifier(input.tenantId, "tenant id");
325
+ identifier(input.executionId, "execution id");
326
+ assertBackendMigrationCutover(input.plan, input.verification, input.cutover);
327
+ return this.mutate((state) => {
328
+ const saved = state.platform.migrationPlans[input.plan.id];
329
+ const record = state.platform.migrationRecords[input.plan.id];
330
+ if (!saved || canonical(saved) !== canonical(input.plan) || !record) throw new Error("Migration plan is not the persisted plan");
331
+ if (input.plan.request.scope.kind !== "tenants" || !input.plan.request.scope.tenantIds.includes(input.tenantId)) throw new Error("Migration plan tenant mismatch");
332
+ if (record.executionId !== input.executionId || !["running", "verified"].includes(record.status)) throw new Error("Migration execution is not active");
333
+ if (record.status === "verified" && canonical(record.verification) !== canonical(input.verification)) throw new Error("Migration verification changed during retry");
334
+ record.status = "verified";
335
+ record.verification = structuredClone(input.verification);
336
+ record.cutover = structuredClone(input.cutover);
337
+ record.updatedAt = this.now().toISOString();
338
+ return structuredClone(record);
339
+ });
340
+ }
341
+ /**
342
+ * Records a failed or reconciling migration status with host diagnostic text.
343
+ * @param input - Persisted migrationId, optional expected executionId, failure status, and nonblank message up to 2048 characters.
344
+ * @returns Cloned updated failure record.
345
+ * @throws Missing/completed migration, mismatched token, invalid input, or host store/clock failure.
346
+ * @example
347
+ * ```typescript
348
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
349
+ * export async function markMigrationFailure(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["markMigrationFailure"]>) {
350
+ * return store.markMigrationFailure(...args);
351
+ * }
352
+ * ```
353
+ */
354
+ async markMigrationFailure(input) {
355
+ identifier(input.migrationId, "migration id");
356
+ text(input.message, "migration failure", 2048);
357
+ return this.mutate((state) => {
358
+ const record = state.platform.migrationRecords[input.migrationId];
359
+ if (!record) throw new Error("Migration plan not found");
360
+ if (input.executionId && record.executionId !== input.executionId) throw new Error("Migration execution token mismatch");
361
+ if (record.status === "cutover" || record.status === "rolled-back") throw new Error("Completed migration cannot be marked failed");
362
+ record.status = input.status;
363
+ record.error = { message: input.message, occurredAt: this.now().toISOString() };
364
+ record.updatedAt = this.now().toISOString();
365
+ return structuredClone(record);
366
+ });
367
+ }
368
+ /**
369
+ * Applies an operator reconciliation status action and records its note.
370
+ * @param input - Persisted migrationId, action, and nonblank note up to 2048 characters; retry only permits failed/reconciling state and clears execution/verification/cutover.
371
+ * @returns Cloned updated migration record.
372
+ * @throws Missing record, retry from an ineligible state, completed record for mark-failed/mark-reconciling, invalid text, or host store failure.
373
+ * @example
374
+ * ```typescript
375
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
376
+ * export async function reconcileMigration(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["reconcileMigration"]>) {
377
+ * return store.reconcileMigration(...args);
378
+ * }
379
+ * ```
380
+ */
381
+ async reconcileMigration(input) {
382
+ identifier(input.migrationId, "migration id");
383
+ text(input.note, "reconciliation note", 2048);
384
+ return this.mutate((state) => {
385
+ const record = state.platform.migrationRecords[input.migrationId];
386
+ if (!record) throw new Error("Migration plan not found");
387
+ if (input.action === "retry") {
388
+ if (!["failed", "reconciling"].includes(record.status)) throw new Error("Only failed or reconciling migrations can be retried");
389
+ record.status = "planned";
390
+ record.executionId = void 0;
391
+ record.verification = void 0;
392
+ record.cutover = void 0;
393
+ } else if (input.action === "mark-failed") {
394
+ if (record.status === "cutover" || record.status === "rolled-back") throw new Error("Completed migration cannot be marked failed");
395
+ record.status = "failed";
396
+ } else if (input.action === "mark-reconciling") {
397
+ if (record.status === "cutover" || record.status === "rolled-back") throw new Error("Completed migration cannot be reconciled");
398
+ record.status = "reconciling";
399
+ }
400
+ record.error = { message: input.note, occurredAt: this.now().toISOString() };
401
+ record.updatedAt = this.now().toISOString();
402
+ return structuredClone(record);
403
+ });
404
+ }
405
+ /**
406
+ * Creates a reverse migration plan for a completed cutover without executing rollback.
407
+ * @param input - Verified tenantId, completed migrationId, nonblank reason up to 1024 characters, and parseable requestedAt timestamp.
408
+ * @returns New planned reverse migration linked to the original record.
409
+ * @throws Incomplete/already rollback-planned migration, tenant no longer on migrated target, invalid input/planning, or host store failure.
410
+ * @example
411
+ * ```typescript
412
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
413
+ * export async function createRollbackPlan(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["createRollbackPlan"]>) {
414
+ * return store.createRollbackPlan(...args);
415
+ * }
416
+ * ```
417
+ */
418
+ async createRollbackPlan(input) {
419
+ identifier(input.tenantId, "tenant id");
420
+ identifier(input.migrationId, "migration id");
421
+ text(input.reason, "rollback reason", 1024);
422
+ timestamp(input.requestedAt, "rollback requestedAt");
423
+ return this.mutate((state) => {
424
+ const original = state.platform.migrationRecords[input.migrationId];
425
+ const originalPlan = state.platform.migrationPlans[input.migrationId];
426
+ if (!original || !originalPlan || original.status !== "cutover") throw new Error("Only a completed migration can be rolled back");
427
+ if (original.rollbackPlanId) throw new Error("Migration rollback already exists");
428
+ const current = placement(state, input.tenantId);
429
+ if (!current || current.backend.id !== originalPlan.request.target.id) throw new Error("Current tenant placement is not the migrated target");
430
+ const rollback = planBackendMigration({
431
+ migrationId: `rollback-${crypto.randomUUID()}`,
432
+ source: originalPlan.request.target,
433
+ target: originalPlan.request.source,
434
+ scope: originalPlan.request.scope,
435
+ pluginIds: originalPlan.request.pluginIds,
436
+ requestedAt: input.requestedAt,
437
+ reason: input.reason
438
+ });
439
+ state.platform.migrationPlans[rollback.id] = structuredClone(rollback);
440
+ state.platform.migrationRecords[rollback.id] = { status: "planned", updatedAt: input.requestedAt, rollbackOf: input.migrationId };
441
+ original.rollbackPlanId = rollback.id;
442
+ original.updatedAt = this.now().toISOString();
443
+ return rollback;
444
+ });
445
+ }
446
+ /**
447
+ * Commits a verified migration routing change and transfers active plugin activations to the target backend.
448
+ * @param input - Tenant included by the exact persisted plan and its previously recorded cutover proof.
449
+ * @returns New target placement, or current placement for an already-cutover record.
450
+ * @throws Unverified/stale/changed plan, invalid cutover or nonadvancing routing revision, target tenant mismatch, missing planned active plugin or target installation, or host lock/write failure.
451
+ * @example
452
+ * ```typescript
453
+ * import { LifecyclePlatformControlStore } from "@alneas/eaos-server/platform-admin/store";
454
+ * export async function recordTenantPlacement(store: LifecyclePlatformControlStore, ...args: Parameters<LifecyclePlatformControlStore["recordTenantPlacement"]>) {
455
+ * return store.recordTenantPlacement(...args);
456
+ * }
457
+ * ```
458
+ */
459
+ async recordTenantPlacement(input) {
460
+ identifier(input.tenantId, "tenant id");
461
+ return this.mutate((state) => {
462
+ const { plan, cutover } = input;
463
+ const saved = state.platform.migrationPlans[plan.id];
464
+ const record = state.platform.migrationRecords[plan.id];
465
+ if (!saved || canonical(saved) !== canonical(plan) || !record) throw new Error("Migration plan is not the persisted plan");
466
+ if (plan.request.scope.kind !== "tenants" || !plan.request.scope.tenantIds.includes(input.tenantId)) throw new Error("Migration plan tenant mismatch");
467
+ if (record.status === "cutover") return placement(state, input.tenantId);
468
+ if (record.status !== "verified" || !record.executionId || !record.verification || !record.cutover || canonical(record.cutover) !== canonical(cutover)) throw new Error("Migration must be verified before cutover");
469
+ if (cutover.migrationId !== plan.request.migrationId || cutover.targetBackendId !== plan.request.target.id || cutover.sourceRetained !== true || !Number.isSafeInteger(cutover.routingRevision) || cutover.routingRevision < 0 || !Number.isFinite(Date.parse(cutover.cutoverAt))) throw new Error("Invalid migration cutover");
470
+ const current = placement(state, input.tenantId);
471
+ if (!current || current.backend.id !== plan.request.source.id || current.backend.databaseId !== plan.request.source.databaseId) throw new Error("Migration plan is stale");
472
+ if (cutover.routingRevision <= current.routingRevision) throw new Error("Migration routing revision must advance");
473
+ const target = materializeBackend(state, plan.request.target.id);
474
+ if (!target || canonical(targetOf(target)) !== canonical(targetOf(plan.request.target))) throw new Error("Migration target backend changed");
475
+ if (target.mode === "dedicated" && target.tenantId !== input.tenantId) throw new Error("Migration target tenant mismatch");
476
+ const active = Object.entries(state.activations).filter(([, activation]) => activation.backendId === plan.request.source.id && activation.tenantId === input.tenantId && activation.enabled);
477
+ const planned = new Set(plan.request.pluginIds);
478
+ for (const [, activation] of active) {
479
+ if (!planned.has(activation.pluginId)) throw new Error(`Migration plan does not include active plugin: ${activation.pluginId}`);
480
+ const installation = state.installations[key(plan.request.target.id, activation.pluginId)];
481
+ if (!installation || installation.status !== "installed") throw new Error(`Target backend is missing installed plugin: ${activation.pluginId}`);
482
+ }
483
+ for (const [activationKey, activation] of active) {
484
+ activation.enabled = false;
485
+ const targetKey = key(plan.request.target.id, input.tenantId, activation.pluginId);
486
+ const installation = state.installations[key(plan.request.target.id, activation.pluginId)];
487
+ state.activations[targetKey] = { ...activation, backendId: plan.request.target.id, digest: installation.digest, enabled: true };
488
+ state.activations[activationKey] = activation;
489
+ }
490
+ state.platform.placements[input.tenantId] = { tenantId: input.tenantId, backendId: plan.request.target.id, routingRevision: cutover.routingRevision, changedAt: timestamp(cutover.cutoverAt, "cutover time") };
491
+ record.status = "cutover";
492
+ record.updatedAt = this.now().toISOString();
493
+ record.cutover = structuredClone(cutover);
494
+ if (record.rollbackOf) {
495
+ const original = state.platform.migrationRecords[record.rollbackOf];
496
+ if (original) {
497
+ original.status = "rolled-back";
498
+ original.updatedAt = this.now().toISOString();
499
+ original.rollbackPlanId = plan.id;
500
+ }
501
+ }
502
+ return placement(state, input.tenantId);
503
+ });
504
+ }
505
+ };
506
+
507
+ export {
508
+ LifecyclePlatformControlStore
509
+ };
510
+ //# sourceMappingURL=chunk-YMJ7BRKU.js.map