@noodleseed/one 0.8.0 → 0.11.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 (277) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +16 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/archive-ops.d.ts +3 -0
  5. package/dist/commands/archive-ops.d.ts.map +1 -0
  6. package/dist/commands/archive-ops.js +168 -0
  7. package/dist/commands/archive-ops.js.map +1 -0
  8. package/dist/commands/author-loop.d.ts.map +1 -1
  9. package/dist/commands/author-loop.js +78 -3
  10. package/dist/commands/author-loop.js.map +1 -1
  11. package/dist/commands/deploy-ops.d.ts +1 -30
  12. package/dist/commands/deploy-ops.d.ts.map +1 -1
  13. package/dist/commands/deploy-ops.js +55 -87
  14. package/dist/commands/deploy-ops.js.map +1 -1
  15. package/dist/commands/deploy-output.d.ts +31 -0
  16. package/dist/commands/deploy-output.d.ts.map +1 -0
  17. package/dist/commands/deploy-output.js +86 -0
  18. package/dist/commands/deploy-output.js.map +1 -0
  19. package/dist/commands/deploy-version-resolution.d.ts +20 -0
  20. package/dist/commands/deploy-version-resolution.d.ts.map +1 -0
  21. package/dist/commands/deploy-version-resolution.js +77 -0
  22. package/dist/commands/deploy-version-resolution.js.map +1 -0
  23. package/dist/commands/docs-mcp.d.ts +32 -0
  24. package/dist/commands/docs-mcp.d.ts.map +1 -0
  25. package/dist/commands/docs-mcp.js +66 -0
  26. package/dist/commands/docs-mcp.js.map +1 -0
  27. package/dist/commands/mcp-apps.d.ts.map +1 -1
  28. package/dist/commands/mcp-apps.js +38 -37
  29. package/dist/commands/mcp-apps.js.map +1 -1
  30. package/dist/commands/org-admin.d.ts +17 -0
  31. package/dist/commands/org-admin.d.ts.map +1 -0
  32. package/dist/commands/org-admin.js +241 -0
  33. package/dist/commands/org-admin.js.map +1 -0
  34. package/dist/commands/policy-ops.d.ts.map +1 -1
  35. package/dist/commands/policy-ops.js +94 -1
  36. package/dist/commands/policy-ops.js.map +1 -1
  37. package/dist/commands/project-setup.d.ts.map +1 -1
  38. package/dist/commands/project-setup.js +29 -0
  39. package/dist/commands/project-setup.js.map +1 -1
  40. package/dist/commands/session.d.ts +1 -15
  41. package/dist/commands/session.d.ts.map +1 -1
  42. package/dist/commands/session.js +15 -170
  43. package/dist/commands/session.js.map +1 -1
  44. package/dist/commands/shared.d.ts +1 -0
  45. package/dist/commands/shared.d.ts.map +1 -1
  46. package/dist/commands/shared.js +21 -9
  47. package/dist/commands/shared.js.map +1 -1
  48. package/dist/commands/update-ops.d.ts +29 -0
  49. package/dist/commands/update-ops.d.ts.map +1 -0
  50. package/dist/commands/update-ops.js +315 -0
  51. package/dist/commands/update-ops.js.map +1 -0
  52. package/dist/config.d.ts +2 -0
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js.map +1 -1
  55. package/dist/deploy-version.d.ts +3 -0
  56. package/dist/deploy-version.d.ts.map +1 -0
  57. package/dist/deploy-version.js +28 -0
  58. package/dist/deploy-version.js.map +1 -0
  59. package/dist/deploy.d.ts +5 -1
  60. package/dist/deploy.d.ts.map +1 -1
  61. package/dist/deploy.js +27 -3
  62. package/dist/deploy.js.map +1 -1
  63. package/dist/devtools-chat.d.ts +76 -0
  64. package/dist/devtools-chat.d.ts.map +1 -0
  65. package/dist/devtools-chat.js +114 -0
  66. package/dist/devtools-chat.js.map +1 -0
  67. package/dist/devtools-env.d.ts +16 -0
  68. package/dist/devtools-env.d.ts.map +1 -0
  69. package/dist/devtools-env.js +71 -0
  70. package/dist/devtools-env.js.map +1 -0
  71. package/dist/devtools-harness.d.ts +28 -0
  72. package/dist/devtools-harness.d.ts.map +1 -0
  73. package/dist/devtools-harness.js +387 -0
  74. package/dist/devtools-harness.js.map +1 -0
  75. package/dist/devtools-preview.d.ts +42 -0
  76. package/dist/devtools-preview.d.ts.map +1 -0
  77. package/dist/devtools-preview.js +394 -0
  78. package/dist/devtools-preview.js.map +1 -0
  79. package/dist/preview-session.d.ts +46 -0
  80. package/dist/preview-session.d.ts.map +1 -0
  81. package/dist/preview-session.js +132 -0
  82. package/dist/preview-session.js.map +1 -0
  83. package/dist/project.d.ts +2 -0
  84. package/dist/project.d.ts.map +1 -1
  85. package/dist/project.js +2 -0
  86. package/dist/project.js.map +1 -1
  87. package/dist/react-widget-build.js +3 -1
  88. package/dist/react-widget-build.js.map +1 -1
  89. package/dist/update-binary.d.ts +38 -0
  90. package/dist/update-binary.d.ts.map +1 -0
  91. package/dist/update-binary.js +144 -0
  92. package/dist/update-binary.js.map +1 -0
  93. package/dist/update-check.d.ts +40 -0
  94. package/dist/update-check.d.ts.map +1 -0
  95. package/dist/update-check.js +212 -0
  96. package/dist/update-check.js.map +1 -0
  97. package/dist/update-render.d.ts +17 -0
  98. package/dist/update-render.d.ts.map +1 -0
  99. package/dist/update-render.js +39 -0
  100. package/dist/update-render.js.map +1 -0
  101. package/dist/update.d.ts +10 -9
  102. package/dist/update.d.ts.map +1 -1
  103. package/dist/update.js +21 -89
  104. package/dist/update.js.map +1 -1
  105. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.d.ts.map +1 -1
  106. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +13 -1
  107. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js.map +1 -1
  108. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.d.ts.map +1 -1
  109. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +2 -0
  110. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js.map +1 -1
  111. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  112. package/node_modules/@noodle-borg/connector-defs/dist/compile-expr.d.ts +23 -0
  113. package/node_modules/@noodle-borg/connector-defs/dist/compile-expr.d.ts.map +1 -0
  114. package/node_modules/@noodle-borg/connector-defs/dist/compile-expr.js +41 -0
  115. package/node_modules/@noodle-borg/connector-defs/dist/compile-expr.js.map +1 -0
  116. package/node_modules/@noodle-borg/connector-defs/dist/compile.d.ts +2 -6
  117. package/node_modules/@noodle-borg/connector-defs/dist/compile.d.ts.map +1 -1
  118. package/node_modules/@noodle-borg/connector-defs/dist/compile.js +10 -32
  119. package/node_modules/@noodle-borg/connector-defs/dist/compile.js.map +1 -1
  120. package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +16 -0
  121. package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts.map +1 -1
  122. package/node_modules/@noodle-borg/connector-defs/dist/schema.js +6 -0
  123. package/node_modules/@noodle-borg/connector-defs/dist/schema.js.map +1 -1
  124. package/node_modules/@noodle-borg/connector-http/dist/http-connector.d.ts +7 -0
  125. package/node_modules/@noodle-borg/connector-http/dist/http-connector.d.ts.map +1 -1
  126. package/node_modules/@noodle-borg/connector-http/dist/http-connector.js +14 -5
  127. package/node_modules/@noodle-borg/connector-http/dist/http-connector.js.map +1 -1
  128. package/node_modules/@noodle-borg/module/dist/contract.d.ts +12 -0
  129. package/node_modules/@noodle-borg/module/dist/contract.d.ts.map +1 -1
  130. package/node_modules/@noodle-borg/module/dist/contract.js +41 -0
  131. package/node_modules/@noodle-borg/module/dist/contract.js.map +1 -1
  132. package/node_modules/@noodle-borg/service/dist/archive-sweeper.d.ts +43 -0
  133. package/node_modules/@noodle-borg/service/dist/archive-sweeper.d.ts.map +1 -0
  134. package/node_modules/@noodle-borg/service/dist/archive-sweeper.js +122 -0
  135. package/node_modules/@noodle-borg/service/dist/archive-sweeper.js.map +1 -0
  136. package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.d.ts +20 -2
  137. package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.d.ts.map +1 -1
  138. package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.js +37 -2
  139. package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.js.map +1 -1
  140. package/node_modules/@noodle-borg/service/dist/commercial-plans.d.ts +11 -0
  141. package/node_modules/@noodle-borg/service/dist/commercial-plans.d.ts.map +1 -0
  142. package/node_modules/@noodle-borg/service/dist/commercial-plans.js +119 -0
  143. package/node_modules/@noodle-borg/service/dist/commercial-plans.js.map +1 -0
  144. package/node_modules/@noodle-borg/service/dist/deployment-versioning.d.ts +20 -0
  145. package/node_modules/@noodle-borg/service/dist/deployment-versioning.d.ts.map +1 -0
  146. package/node_modules/@noodle-borg/service/dist/deployment-versioning.js +29 -0
  147. package/node_modules/@noodle-borg/service/dist/deployment-versioning.js.map +1 -0
  148. package/node_modules/@noodle-borg/service/dist/index.d.ts +3 -1
  149. package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
  150. package/node_modules/@noodle-borg/service/dist/index.js +2 -0
  151. package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
  152. package/node_modules/@noodle-borg/service/dist/main.js +7 -0
  153. package/node_modules/@noodle-borg/service/dist/main.js.map +1 -1
  154. package/node_modules/@noodle-borg/service/dist/oauth/app.d.ts.map +1 -1
  155. package/node_modules/@noodle-borg/service/dist/oauth/app.js +2 -1
  156. package/node_modules/@noodle-borg/service/dist/oauth/app.js.map +1 -1
  157. package/node_modules/@noodle-borg/service/dist/oauth/branding.d.ts +30 -0
  158. package/node_modules/@noodle-borg/service/dist/oauth/branding.d.ts.map +1 -0
  159. package/node_modules/@noodle-borg/service/dist/oauth/branding.js +224 -0
  160. package/node_modules/@noodle-borg/service/dist/oauth/branding.js.map +1 -0
  161. package/node_modules/@noodle-borg/service/dist/oauth/consent.d.ts +0 -6
  162. package/node_modules/@noodle-borg/service/dist/oauth/consent.d.ts.map +1 -1
  163. package/node_modules/@noodle-borg/service/dist/oauth/consent.js +23 -34
  164. package/node_modules/@noodle-borg/service/dist/oauth/consent.js.map +1 -1
  165. package/node_modules/@noodle-borg/service/dist/oauth/customer-bridge.d.ts.map +1 -1
  166. package/node_modules/@noodle-borg/service/dist/oauth/customer-bridge.js +13 -28
  167. package/node_modules/@noodle-borg/service/dist/oauth/customer-bridge.js.map +1 -1
  168. package/node_modules/@noodle-borg/service/dist/options.d.ts +9 -0
  169. package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
  170. package/node_modules/@noodle-borg/service/dist/registry-helpers.d.ts +1 -1
  171. package/node_modules/@noodle-borg/service/dist/registry-helpers.d.ts.map +1 -1
  172. package/node_modules/@noodle-borg/service/dist/registry-helpers.js +5 -3
  173. package/node_modules/@noodle-borg/service/dist/registry-helpers.js.map +1 -1
  174. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +25 -0
  175. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts.map +1 -0
  176. package/node_modules/@noodle-borg/service/dist/registry-state.js +119 -0
  177. package/node_modules/@noodle-borg/service/dist/registry-state.js.map +1 -0
  178. package/node_modules/@noodle-borg/service/dist/registry-targets.d.ts +1 -0
  179. package/node_modules/@noodle-borg/service/dist/registry-targets.d.ts.map +1 -1
  180. package/node_modules/@noodle-borg/service/dist/registry-targets.js +4 -0
  181. package/node_modules/@noodle-borg/service/dist/registry-targets.js.map +1 -1
  182. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +59 -0
  183. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts.map +1 -0
  184. package/node_modules/@noodle-borg/service/dist/registry-types.js +2 -0
  185. package/node_modules/@noodle-borg/service/dist/registry-types.js.map +1 -0
  186. package/node_modules/@noodle-borg/service/dist/registry.d.ts +14 -70
  187. package/node_modules/@noodle-borg/service/dist/registry.d.ts.map +1 -1
  188. package/node_modules/@noodle-borg/service/dist/registry.js +137 -119
  189. package/node_modules/@noodle-borg/service/dist/registry.js.map +1 -1
  190. package/node_modules/@noodle-borg/service/dist/routes/access-mode.d.ts +4 -0
  191. package/node_modules/@noodle-borg/service/dist/routes/access-mode.d.ts.map +1 -0
  192. package/node_modules/@noodle-borg/service/dist/routes/access-mode.js +15 -0
  193. package/node_modules/@noodle-borg/service/dist/routes/access-mode.js.map +1 -0
  194. package/node_modules/@noodle-borg/service/dist/routes/archive.d.ts +16 -0
  195. package/node_modules/@noodle-borg/service/dist/routes/archive.d.ts.map +1 -0
  196. package/node_modules/@noodle-borg/service/dist/routes/archive.js +83 -0
  197. package/node_modules/@noodle-borg/service/dist/routes/archive.js.map +1 -0
  198. package/node_modules/@noodle-borg/service/dist/routes/control-plane.d.ts +2 -19
  199. package/node_modules/@noodle-borg/service/dist/routes/control-plane.d.ts.map +1 -1
  200. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +52 -281
  201. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js.map +1 -1
  202. package/node_modules/@noodle-borg/service/dist/routes/deployments.d.ts +17 -0
  203. package/node_modules/@noodle-borg/service/dist/routes/deployments.d.ts.map +1 -0
  204. package/node_modules/@noodle-borg/service/dist/routes/deployments.js +88 -0
  205. package/node_modules/@noodle-borg/service/dist/routes/deployments.js.map +1 -0
  206. package/node_modules/@noodle-borg/service/dist/routes/org-admin.d.ts +40 -0
  207. package/node_modules/@noodle-borg/service/dist/routes/org-admin.d.ts.map +1 -0
  208. package/node_modules/@noodle-borg/service/dist/routes/org-admin.js +353 -0
  209. package/node_modules/@noodle-borg/service/dist/routes/org-admin.js.map +1 -0
  210. package/node_modules/@noodle-borg/service/dist/routes/paths.d.ts +15 -0
  211. package/node_modules/@noodle-borg/service/dist/routes/paths.d.ts.map +1 -1
  212. package/node_modules/@noodle-borg/service/dist/routes/paths.js +47 -0
  213. package/node_modules/@noodle-borg/service/dist/routes/paths.js.map +1 -1
  214. package/node_modules/@noodle-borg/service/dist/routes/plans.d.ts +20 -0
  215. package/node_modules/@noodle-borg/service/dist/routes/plans.d.ts.map +1 -0
  216. package/node_modules/@noodle-borg/service/dist/routes/plans.js +159 -0
  217. package/node_modules/@noodle-borg/service/dist/routes/plans.js.map +1 -0
  218. package/node_modules/@noodle-borg/service/dist/routes/policies.d.ts.map +1 -1
  219. package/node_modules/@noodle-borg/service/dist/routes/policies.js +7 -0
  220. package/node_modules/@noodle-borg/service/dist/routes/policies.js.map +1 -1
  221. package/node_modules/@noodle-borg/service/dist/routes/rollback.d.ts.map +1 -1
  222. package/node_modules/@noodle-borg/service/dist/routes/rollback.js +23 -7
  223. package/node_modules/@noodle-borg/service/dist/routes/rollback.js.map +1 -1
  224. package/node_modules/@noodle-borg/service/dist/serve.d.ts +6 -0
  225. package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
  226. package/node_modules/@noodle-borg/service/dist/serve.js +72 -32
  227. package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
  228. package/node_modules/@noodle-borg/service/dist/server-auth-bindings.d.ts +7 -0
  229. package/node_modules/@noodle-borg/service/dist/server-auth-bindings.d.ts.map +1 -0
  230. package/node_modules/@noodle-borg/service/dist/server-auth-bindings.js +14 -0
  231. package/node_modules/@noodle-borg/service/dist/server-auth-bindings.js.map +1 -0
  232. package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
  233. package/node_modules/@noodle-borg/service/dist/service.js +139 -7
  234. package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
  235. package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts +14 -0
  236. package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts.map +1 -1
  237. package/node_modules/@noodle-borg/service/dist/store/control-plane.js +38 -0
  238. package/node_modules/@noodle-borg/service/dist/store/control-plane.js.map +1 -1
  239. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +25 -0
  240. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts.map +1 -0
  241. package/node_modules/@noodle-borg/service/dist/store/json-file.js +163 -0
  242. package/node_modules/@noodle-borg/service/dist/store/json-file.js.map +1 -0
  243. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts +14 -0
  244. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts.map +1 -1
  245. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js +16 -0
  246. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js.map +1 -1
  247. package/node_modules/@noodle-borg/service/dist/store/postgres-rows.d.ts +60 -0
  248. package/node_modules/@noodle-borg/service/dist/store/postgres-rows.d.ts.map +1 -0
  249. package/node_modules/@noodle-borg/service/dist/store/postgres-rows.js +101 -0
  250. package/node_modules/@noodle-borg/service/dist/store/postgres-rows.js.map +1 -0
  251. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.d.ts.map +1 -1
  252. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js +21 -2
  253. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js.map +1 -1
  254. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts +23 -8
  255. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts.map +1 -1
  256. package/node_modules/@noodle-borg/service/dist/store/postgres.js +112 -128
  257. package/node_modules/@noodle-borg/service/dist/store/postgres.js.map +1 -1
  258. package/node_modules/@noodle-borg/service/dist/store/records.d.ts +35 -0
  259. package/node_modules/@noodle-borg/service/dist/store/records.d.ts.map +1 -0
  260. package/node_modules/@noodle-borg/service/dist/store/records.js +87 -0
  261. package/node_modules/@noodle-borg/service/dist/store/records.js.map +1 -0
  262. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts +19 -0
  263. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts.map +1 -0
  264. package/node_modules/@noodle-borg/service/dist/store/validate.js +61 -0
  265. package/node_modules/@noodle-borg/service/dist/store/validate.js.map +1 -0
  266. package/node_modules/@noodle-borg/service/dist/store.d.ts +75 -45
  267. package/node_modules/@noodle-borg/service/dist/store.d.ts.map +1 -1
  268. package/node_modules/@noodle-borg/service/dist/store.js +61 -224
  269. package/node_modules/@noodle-borg/service/dist/store.js.map +1 -1
  270. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts +1 -0
  271. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts.map +1 -1
  272. package/node_modules/@noodle-borg/transport-http/dist/handler.js +37 -0
  273. package/node_modules/@noodle-borg/transport-http/dist/handler.js.map +1 -1
  274. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -1
  275. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts.map +1 -1
  276. package/node_modules/@noodle-borg/transport-http/dist/index.js.map +1 -1
  277. package/package.json +3 -2
@@ -0,0 +1,83 @@
1
+ import { sendForbidden, sendJson } from '../http-util.js';
2
+ import { authorizeControlPlane } from './control-plane.js';
3
+ import { canManageMembers } from './org-admin.js';
4
+ const DAY_MS = 24 * 60 * 60 * 1000;
5
+ export async function handleAppArchive(req, res, registry, gate, controlPlane, audit, ref, options) {
6
+ const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
7
+ if (identity === false)
8
+ return;
9
+ if (!(await canManageMembers(controlPlane, ref.org, identity))) {
10
+ return sendForbidden(res, 'forbidden');
11
+ }
12
+ const at = (options.clock?.() ?? new Date()).toISOString();
13
+ const result = await registry.archiveApp(ref.org, ref.app, at);
14
+ if (result === undefined)
15
+ return sendJson(res, 404, { error: 'app not found' });
16
+ await audit.emit({
17
+ eventType: 'app.archived',
18
+ org: ref.org,
19
+ app: ref.app,
20
+ decision: 'allow',
21
+ status: 200,
22
+ actorSubject: identity.subject,
23
+ ...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
24
+ details: {
25
+ archivedAt: result.archivedAt,
26
+ archivedDeployments: result.archivedDeployments,
27
+ alreadyArchived: result.alreadyArchived,
28
+ },
29
+ });
30
+ return sendJson(res, 200, {
31
+ ok: true,
32
+ target: { org: ref.org, app: ref.app },
33
+ archive: result,
34
+ });
35
+ }
36
+ export async function handleAppRestore(req, res, registry, gate, controlPlane, audit, ref, options, retentionDays) {
37
+ const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
38
+ if (identity === false)
39
+ return;
40
+ if (!(await canManageMembers(controlPlane, ref.org, identity))) {
41
+ return sendForbidden(res, 'forbidden');
42
+ }
43
+ const now = options.clock?.() ?? new Date();
44
+ const archivedAt = await registry.getAppArchivedAt(ref.org, ref.app);
45
+ // Deterministic on the window, not on sweeper timing (ADR 0117 §5): past-retention restores are
46
+ // 410 Gone even before the sweeper hard-deletes; after the sweep the app is a plain 404.
47
+ if (archivedAt !== undefined && now.getTime() - Date.parse(archivedAt) > retentionDays * DAY_MS) {
48
+ await audit.emit({
49
+ eventType: 'app.restore.rejected',
50
+ org: ref.org,
51
+ app: ref.app,
52
+ decision: 'deny',
53
+ status: 410,
54
+ reasonCode: 'retention_elapsed',
55
+ actorSubject: identity.subject,
56
+ ...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
57
+ details: { archivedAt, retentionDays },
58
+ });
59
+ return sendJson(res, 410, {
60
+ error: 'archive retention window elapsed; the app is pending permanent deletion',
61
+ });
62
+ }
63
+ const result = await registry.restoreApp(ref.org, ref.app);
64
+ if (result === undefined)
65
+ return sendJson(res, 404, { error: 'app not found' });
66
+ const alreadyActive = result.restoredDeployments === 0;
67
+ await audit.emit({
68
+ eventType: 'app.restored',
69
+ org: ref.org,
70
+ app: ref.app,
71
+ decision: 'allow',
72
+ status: 200,
73
+ actorSubject: identity.subject,
74
+ ...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
75
+ details: { restoredDeployments: result.restoredDeployments, alreadyActive },
76
+ });
77
+ return sendJson(res, 200, {
78
+ ok: true,
79
+ target: { org: ref.org, app: ref.app },
80
+ restore: { restoredDeployments: result.restoredDeployments, alreadyActive },
81
+ });
82
+ }
83
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/routes/archive.ts"],"names":[],"mappings":"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"}
@@ -3,31 +3,14 @@ import type { ControlPlaneIdentity, DeployAuthGate } from '../auth/deploy-gate.j
3
3
  import type { ServiceOptions } from '../options.js';
4
4
  import type { ServerRegistry } from '../registry.js';
5
5
  import type { AuditSink } from '../store/audit.js';
6
- import { type ConfigStore, type ControlPlaneStore, type TenantRef } from '../store.js';
6
+ import type { ConfigStore, ControlPlaneStore, TenantRef } from '../store.js';
7
7
  import type { ConfigRouteRef } from './paths.js';
8
8
  export declare function handleDeploy(req: IncomingMessage, res: ServerResponse, registry: ServerRegistry, options: ServiceOptions, maxBody: number, tenant: TenantRef, gate: DeployAuthGate, controlPlane: ControlPlaneStore, audit: AuditSink): Promise<void>;
9
9
  export declare function handleWhoami(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, options?: ServiceOptions): Promise<void>;
10
- export declare function handleOrgs(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, maxBody: number): Promise<void>;
11
- export declare function handleMembers(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, maxBody: number, ref: {
12
- org: string;
13
- subject?: string;
14
- }): Promise<void>;
15
- export declare function handleInvitations(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, maxBody: number, ref: {
16
- org: string;
17
- token?: string;
18
- action?: 'accept';
19
- }, now?: () => Date): Promise<void>;
20
10
  export declare function handleConfigValues(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, configStore: ConfigStore, maxBody: number, ref: ConfigRouteRef, audit: AuditSink): Promise<void>;
21
11
  export declare function authorizeTenantControl(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, org: string): Promise<ControlPlaneIdentity | false>;
22
- export declare function canManageMembers(store: ControlPlaneStore, org: string, identity: ControlPlaneIdentity): Promise<boolean>;
23
- export declare function handleDeploymentStatus(req: IncomingMessage, res: ServerResponse, registry: ServerRegistry, gate: DeployAuthGate, controlPlane: ControlPlaneStore, tenant: TenantRef, options: ServiceOptions): Promise<void>;
12
+ export declare function handleDeploymentStatus(req: IncomingMessage, res: ServerResponse, registry: ServerRegistry, gate: DeployAuthGate, controlPlane: ControlPlaneStore, tenant: TenantRef, options: ServiceOptions, url?: URL): Promise<void>;
24
13
  export declare function handleAccessUpdate(req: IncomingMessage, res: ServerResponse, registry: ServerRegistry, gate: DeployAuthGate, controlPlane: ControlPlaneStore, maxBody: number, tenant: TenantRef): Promise<void>;
25
- export declare function handleDeployments(req: IncomingMessage, res: ServerResponse, registry: ServerRegistry, gate: DeployAuthGate, controlPlane: ControlPlaneStore, ref: {
26
- org: string;
27
- }, url: URL): Promise<void>;
28
- export declare function handleAuditEvents(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, controlPlane: ControlPlaneStore, audit: AuditSink, ref: {
29
- org: string;
30
- }, url: URL): Promise<void>;
31
14
  export declare function authorizeControlPlane(req: IncomingMessage, res: ServerResponse, gate: DeployAuthGate, options: {
32
15
  requireIdentity: true;
33
16
  }): Promise<ControlPlaneIdentity | false>;
@@ -1 +1 @@
1
- {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/routes/control-plane.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/routes/control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AASnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,IAAI,CAAC,CAuOf;AAWD,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,IAAI,CAAC,CAgDf;AAwBD,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,GAAG,KAAK,CAAC,CAWvC;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,GAAG,CAAC,EAAE,GAAG,GACR,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,iBAAiB,EAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE;IAAE,eAAe,EAAE,IAAI,CAAA;CAAE,GACjC,OAAO,CAAC,oBAAoB,GAAG,KAAK,CAAC,CAAC;AACzC,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE;IAAE,eAAe,EAAE,KAAK,CAAA;CAAE,GAClC,OAAO,CAAC,oBAAoB,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC"}
@@ -1,10 +1,10 @@
1
+ import { normalizeServerVersion } from '@noodle-borg/module';
1
2
  import { noopLogger } from '@noodle-borg/transport-http';
2
3
  import { baseFromRequest, readBody, readJsonBody, sendForbidden, sendJson, sendUnauthorized, } from '../http-util.js';
3
- import { hashToken, randomToken } from '../oauth/tokens.js';
4
4
  import { ensurePersonalWorkspace } from '../personal-workspace.js';
5
- import { validateOrgRole, } from '../store.js';
5
+ import { tenantMcpUrl } from '../registry-helpers.js';
6
+ import { isAccessMode, isIdentityAccessMode } from './access-mode.js';
6
7
  import { parseHostedAssets } from './assets.js';
7
- const ORG_INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
8
8
  export async function handleDeploy(req, res, registry, options, maxBody, tenant, gate, controlPlane, audit) {
9
9
  const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: false });
10
10
  if (identity === false)
@@ -17,6 +17,24 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
17
17
  if (!member)
18
18
  return sendForbidden(res, 'forbidden');
19
19
  }
20
+ // Archived apps are soft-deleted (ADR 0117): refuse new deploys so the all-records-archived
21
+ // invariant can't be broken sideways. Restore first, then deploy.
22
+ const appArchivedAt = await registry.getAppArchivedAt(tenant.org, tenant.app);
23
+ if (appArchivedAt !== undefined) {
24
+ await audit.emit({
25
+ eventType: 'deploy.rejected',
26
+ org: tenant.org,
27
+ app: tenant.app,
28
+ env: tenant.env,
29
+ decision: 'deny',
30
+ status: 409,
31
+ reasonCode: 'app_archived',
32
+ ...(identity?.subject !== undefined ? { actorSubject: identity.subject } : {}),
33
+ ...(identity?.email !== undefined ? { actorEmail: identity.email } : {}),
34
+ details: { archivedAt: appArchivedAt },
35
+ });
36
+ return sendJson(res, 409, { error: 'app is archived; restore it before deploying' });
37
+ }
20
38
  const body = await readBody(req, maxBody);
21
39
  if (!body.ok)
22
40
  return sendJson(res, 413, { error: 'request body too large' });
@@ -24,6 +42,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
24
42
  let connectors;
25
43
  let hostedAssets;
26
44
  let accessMode = 'owner-only';
45
+ let serverVersion;
27
46
  try {
28
47
  const parsed = JSON.parse(body.text);
29
48
  if (typeof parsed.manifest !== 'string')
@@ -49,6 +68,8 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
49
68
  if (parsed.hostedAssets !== undefined) {
50
69
  hostedAssets = parseHostedAssets(parsed.hostedAssets);
51
70
  }
71
+ serverVersion =
72
+ parsed.serverVersion === undefined ? '1' : normalizeParsedServerVersion(parsed.serverVersion);
52
73
  }
53
74
  catch (error) {
54
75
  return sendJson(res, 400, { error: `invalid deploy request: ${error.message}` });
@@ -103,7 +124,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
103
124
  }
104
125
  hostedAssets = verified.assets;
105
126
  }
106
- const result = await registry.deploy(tenant, manifest, connectors, identity || undefined, accessMode, hostedAssets);
127
+ const result = await registry.deploy(tenant, manifest, connectors, identity || undefined, accessMode, hostedAssets, serverVersion);
107
128
  if (!result.ok) {
108
129
  // Codes are safe enum strings; never a path-with-value. No secret name or value is logged.
109
130
  const codes = result.errors.map((error) => error.code).join(',');
@@ -130,6 +151,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
130
151
  // Counts only — never secret names or values.
131
152
  logger.info('deploy.ok', {
132
153
  deploymentId: result.deploymentId,
154
+ serverVersion: result.serverVersion,
133
155
  org: tenant.org,
134
156
  app: tenant.app,
135
157
  env: tenant.env,
@@ -147,6 +169,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
147
169
  ...(identity?.email !== undefined ? { actorEmail: identity.email } : {}),
148
170
  details: {
149
171
  accessMode: result.accessMode ?? accessMode,
172
+ serverVersion: result.serverVersion,
150
173
  hasConnectors: connectors !== undefined,
151
174
  },
152
175
  });
@@ -159,7 +182,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
159
182
  app: tenant.app,
160
183
  env: tenant.env,
161
184
  ...(result.deploymentId !== undefined ? { deploymentId: result.deploymentId } : {}),
162
- details: { accessMode: result.accessMode ?? accessMode },
185
+ details: { accessMode: result.accessMode ?? accessMode, serverVersion: result.serverVersion },
163
186
  });
164
187
  if (result.ok && hostedAssets !== undefined && hostedAssets.length > 0) {
165
188
  await audit.emit({
@@ -185,31 +208,23 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
185
208
  });
186
209
  }
187
210
  const base = options.publicBaseUrl ?? baseFromRequest(req, options.tls ?? {});
211
+ const defaultUrl = tenantMcpUrl(base, tenant);
188
212
  return sendJson(res, 201, {
189
213
  ok: true,
190
214
  org: tenant.org,
191
215
  app: tenant.app,
192
216
  env: tenant.env,
193
217
  deploymentId: result.deploymentId,
218
+ serverVersion: result.serverVersion,
194
219
  accessMode: result.accessMode,
195
- url: tenant.env === 'prod'
196
- ? `${base}/o/${tenant.org}/${tenant.app}/mcp`
197
- : `${base}/o/${tenant.org}/${tenant.app}/${tenant.env}/mcp`,
220
+ url: tenantMcpUrl(base, tenant, result.serverVersion),
221
+ defaultUrl,
198
222
  });
199
223
  }
200
- function isAccessMode(value) {
201
- return (value === 'owner-only' ||
202
- value === 'org-members' ||
203
- value === 'public' ||
204
- value === 'mixed' ||
205
- value === 'customers' ||
206
- value === 'authenticated');
207
- }
208
- function isIdentityAccessMode(accessMode) {
209
- return (accessMode === 'owner-only' ||
210
- accessMode === 'org-members' ||
211
- accessMode === 'customers' ||
212
- accessMode === 'authenticated');
224
+ function normalizeParsedServerVersion(value) {
225
+ if (typeof value !== 'string')
226
+ throw new Error('"serverVersion" must be a version string');
227
+ return normalizeServerVersion(value);
213
228
  }
214
229
  function manifestUsesUserRoot(manifest) {
215
230
  return /\$\{\s*user(?:[.[]|\s*\})/.test(manifest);
@@ -226,154 +241,6 @@ export async function handleWhoami(req, res, gate, controlPlane, options = {}) {
226
241
  : await controlPlane.listOrgsForSubject(identity.subject);
227
242
  return sendJson(res, 200, { ok: true, identity, orgs });
228
243
  }
229
- export async function handleOrgs(req, res, gate, controlPlane, maxBody) {
230
- const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
231
- if (identity === false)
232
- return;
233
- if (req.method === 'GET') {
234
- const orgs = identity.superAdmin
235
- ? await controlPlane.listOrgs()
236
- : await controlPlane.listOrgsForSubject(identity.subject);
237
- return sendJson(res, 200, { ok: true, orgs });
238
- }
239
- if (!identity.superAdmin)
240
- return sendForbidden(res, 'super-admin required');
241
- const body = await readJsonBody(req, maxBody);
242
- if (!body.ok)
243
- return sendJson(res, body.status, { error: body.error });
244
- const parsed = body.value;
245
- if (typeof parsed.slug !== 'string')
246
- return sendJson(res, 400, { error: '"slug" must be a string' });
247
- if (parsed.displayName !== undefined && typeof parsed.displayName !== 'string') {
248
- return sendJson(res, 400, { error: '"displayName" must be a string' });
249
- }
250
- try {
251
- const org = await controlPlane.createOrg({
252
- slug: parsed.slug,
253
- ...(typeof parsed.displayName === 'string' ? { displayName: parsed.displayName } : {}),
254
- });
255
- return sendJson(res, 201, { ok: true, org });
256
- }
257
- catch (error) {
258
- return sendJson(res, 400, { error: error.message });
259
- }
260
- }
261
- export async function handleMembers(req, res, gate, controlPlane, maxBody, ref) {
262
- const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
263
- if (identity === false)
264
- return;
265
- if (req.method === 'GET' && ref.subject === undefined) {
266
- if (!(await canViewMembers(controlPlane, ref.org, identity)))
267
- return sendForbidden(res, 'forbidden');
268
- try {
269
- return sendJson(res, 200, { ok: true, members: await controlPlane.listOrgMembers(ref.org) });
270
- }
271
- catch (error) {
272
- return sendJson(res, 400, { error: error.message });
273
- }
274
- }
275
- if (req.method === 'POST' && ref.subject === undefined) {
276
- if (!(await canManageMembers(controlPlane, ref.org, identity)))
277
- return sendForbidden(res, 'forbidden');
278
- const body = await readJsonBody(req, maxBody);
279
- if (!body.ok)
280
- return sendJson(res, body.status, { error: body.error });
281
- const parsed = body.value;
282
- if (typeof parsed.subject !== 'string') {
283
- return sendJson(res, 400, { error: '"subject" must be a string' });
284
- }
285
- if (typeof parsed.email !== 'string') {
286
- return sendJson(res, 400, { error: '"email" must be a string' });
287
- }
288
- let role;
289
- try {
290
- role = validateOrgRole(typeof parsed.role === 'string' ? parsed.role : 'developer');
291
- }
292
- catch (error) {
293
- return sendJson(res, 400, { error: error.message });
294
- }
295
- try {
296
- const member = await controlPlane.addOrgMember({
297
- org: ref.org,
298
- subject: parsed.subject,
299
- email: parsed.email,
300
- role,
301
- });
302
- return sendJson(res, 201, { ok: true, member });
303
- }
304
- catch (error) {
305
- return sendJson(res, 400, { error: error.message });
306
- }
307
- }
308
- if (req.method === 'DELETE' && ref.subject !== undefined) {
309
- if (!(await canManageMembers(controlPlane, ref.org, identity)))
310
- return sendForbidden(res, 'forbidden');
311
- await controlPlane.removeOrgMember({ org: ref.org, subject: ref.subject });
312
- res.writeHead(204);
313
- res.end();
314
- return;
315
- }
316
- return sendJson(res, 404, { error: 'not found' });
317
- }
318
- export async function handleInvitations(req, res, gate, controlPlane, maxBody, ref, now = () => new Date()) {
319
- const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
320
- if (identity === false)
321
- return;
322
- if (req.method === 'POST' && ref.token === undefined) {
323
- if (!(await canManageMembers(controlPlane, ref.org, identity)))
324
- return sendForbidden(res, 'forbidden');
325
- const body = await readJsonBody(req, maxBody);
326
- if (!body.ok)
327
- return sendJson(res, body.status, { error: body.error });
328
- const parsed = body.value;
329
- if (typeof parsed.email !== 'string')
330
- return sendJson(res, 400, { error: '"email" must be a string' });
331
- let role;
332
- try {
333
- role = validateOrgRole(typeof parsed.role === 'string' ? parsed.role : 'developer');
334
- }
335
- catch (error) {
336
- return sendJson(res, 400, { error: error.message });
337
- }
338
- const rawToken = randomToken();
339
- const expiresAt = new Date(now().getTime() + ORG_INVITATION_TTL_MS);
340
- const invitation = await controlPlane.createOrgInvitation({
341
- org: ref.org,
342
- email: parsed.email,
343
- role,
344
- tokenHash: hashToken(rawToken),
345
- createdBySubject: identity.subject,
346
- createdByEmail: identity.email,
347
- expiresAt,
348
- });
349
- return sendJson(res, 201, {
350
- ok: true,
351
- invitation: publicInvitation(invitation),
352
- acceptPath: invitationAcceptPath(ref.org, rawToken),
353
- });
354
- }
355
- if (req.method === 'POST' && ref.token !== undefined && ref.action === 'accept') {
356
- const tokenHash = hashToken(ref.token);
357
- const invitation = await controlPlane.getOrgInvitation({ tokenHash });
358
- if (invitation === undefined || invitation.orgSlug !== ref.org) {
359
- return sendJson(res, 404, { error: 'not found' });
360
- }
361
- if (identity.email.toLowerCase() !== invitation.email) {
362
- return sendForbidden(res, 'forbidden');
363
- }
364
- const consumed = await controlPlane.consumeOrgInvitation({ tokenHash });
365
- if (consumed === undefined)
366
- return sendJson(res, 404, { error: 'not found' });
367
- const member = await controlPlane.addOrgMember({
368
- org: consumed.orgSlug,
369
- subject: identity.subject,
370
- email: identity.email,
371
- role: consumed.role,
372
- });
373
- return sendJson(res, 201, { ok: true, member });
374
- }
375
- return sendJson(res, 404, { error: 'not found' });
376
- }
377
244
  export async function handleConfigValues(req, res, gate, controlPlane, configStore, maxBody, ref, audit) {
378
245
  const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
379
246
  if (identity === false)
@@ -457,39 +324,17 @@ export async function authorizeTenantControl(req, res, gate, controlPlane, org)
457
324
  }
458
325
  return identity;
459
326
  }
460
- async function canViewMembers(store, org, identity) {
461
- if (identity.superAdmin)
462
- return true;
463
- return (await store.getOrgMember({ org, subject: identity.subject })) !== undefined;
464
- }
465
- export async function canManageMembers(store, org, identity) {
466
- if (identity.superAdmin)
467
- return true;
468
- return (await store.getOrgMember({ org, subject: identity.subject }))?.role === 'owner';
469
- }
470
- function publicInvitation(invitation) {
471
- return {
472
- orgSlug: invitation.orgSlug,
473
- email: invitation.email,
474
- role: invitation.role,
475
- createdAt: invitation.createdAt,
476
- expiresAt: invitation.expiresAt,
477
- createdBySubject: invitation.createdBySubject,
478
- ...(invitation.createdByEmail !== undefined
479
- ? { createdByEmail: invitation.createdByEmail }
480
- : {}),
481
- };
482
- }
483
- function invitationAcceptPath(org, token) {
484
- return `/v1/orgs/${encodeURIComponent(org)}/invitations/${encodeURIComponent(token)}/accept`;
485
- }
486
- export async function handleDeploymentStatus(req, res, registry, gate, controlPlane, tenant, options) {
327
+ export async function handleDeploymentStatus(req, res, registry, gate, controlPlane, tenant, options, url) {
487
328
  const identity = await authorizeTenantControl(req, res, gate, controlPlane, tenant.org);
488
329
  if (identity === false)
489
330
  return;
490
331
  try {
491
332
  const base = options.publicBaseUrl ?? baseFromRequest(req, options.tls ?? {});
492
- const status = await registry.getStatus(tenant, base);
333
+ const rawVersion = url?.searchParams.get('version') ?? undefined;
334
+ const serverVersion = rawVersion !== undefined && rawVersion !== ''
335
+ ? normalizeServerVersion(rawVersion)
336
+ : undefined;
337
+ const status = await registry.getStatus(tenant, base, serverVersion);
493
338
  if (status === undefined)
494
339
  return sendJson(res, 404, { error: 'no active deployment' });
495
340
  return sendJson(res, 200, { ok: true, ...status });
@@ -513,7 +358,15 @@ export async function handleAccessUpdate(req, res, registry, gate, controlPlane,
513
358
  return sendJson(res, 400, { error: message });
514
359
  }
515
360
  try {
516
- const record = await registry.updateAccess(tenant, parsed.accessMode);
361
+ const serverVersion = parsed.serverVersion === undefined
362
+ ? undefined
363
+ : typeof parsed.serverVersion === 'string'
364
+ ? normalizeServerVersion(parsed.serverVersion)
365
+ : undefined;
366
+ if (parsed.serverVersion !== undefined && serverVersion === undefined) {
367
+ return sendJson(res, 400, { error: '"serverVersion" must be a version string' });
368
+ }
369
+ const record = await registry.updateAccess(tenant, parsed.accessMode, serverVersion);
517
370
  if (record === undefined)
518
371
  return sendJson(res, 404, { error: 'no active deployment' });
519
372
  return sendJson(res, 200, {
@@ -521,6 +374,7 @@ export async function handleAccessUpdate(req, res, registry, gate, controlPlane,
521
374
  target: tenant,
522
375
  deployment: {
523
376
  deploymentId: record.deploymentId,
377
+ ...(record.serverVersion !== undefined ? { serverVersion: record.serverVersion } : {}),
524
378
  accessMode: record.accessMode ?? 'owner-only',
525
379
  },
526
380
  });
@@ -529,89 +383,6 @@ export async function handleAccessUpdate(req, res, registry, gate, controlPlane,
529
383
  return sendJson(res, 400, { error: error.message });
530
384
  }
531
385
  }
532
- export async function handleDeployments(req, res, registry, gate, controlPlane, ref, url) {
533
- const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
534
- if (identity === false)
535
- return;
536
- if (!identity.superAdmin) {
537
- const member = await controlPlane.isOrgMember({ org: ref.org, subject: identity.subject });
538
- if (!member)
539
- return sendForbidden(res, 'forbidden');
540
- }
541
- const app = url.searchParams.get('app') ?? undefined;
542
- const env = url.searchParams.get('env') ?? undefined;
543
- try {
544
- const deployments = await registry.listDeployments({
545
- org: ref.org,
546
- ...(app !== undefined ? { app } : {}),
547
- ...(env !== undefined ? { env } : {}),
548
- });
549
- return sendJson(res, 200, { ok: true, deployments });
550
- }
551
- catch (error) {
552
- return sendJson(res, 400, { error: error.message });
553
- }
554
- }
555
- export async function handleAuditEvents(req, res, gate, controlPlane, audit, ref, url) {
556
- const identity = await authorizeTenantControl(req, res, gate, controlPlane, ref.org);
557
- if (identity === false)
558
- return;
559
- if (!isAuditStore(audit)) {
560
- return sendJson(res, 409, {
561
- ok: false,
562
- error: 'audit events require an audit store capability',
563
- });
564
- }
565
- const limit = parseAuditLimit(url.searchParams.get('limit'));
566
- if (limit === false)
567
- return sendJson(res, 400, { error: '"limit" must be an integer from 1 to 100' });
568
- try {
569
- const app = optionalQuery(url, 'app');
570
- const env = optionalQuery(url, 'env');
571
- const eventType = optionalQuery(url, 'eventType');
572
- const events = await audit.list({
573
- org: ref.org,
574
- ...(app !== undefined ? { app } : {}),
575
- ...(env !== undefined ? { env } : {}),
576
- ...(eventType !== undefined ? { eventType } : {}),
577
- ...(limit !== undefined ? { limit } : {}),
578
- });
579
- await audit.emit({
580
- eventType: 'audit.events.queried',
581
- org: ref.org,
582
- ...(app !== undefined ? { app } : {}),
583
- ...(env !== undefined ? { env } : {}),
584
- decision: 'allow',
585
- status: 200,
586
- actorSubject: identity.subject,
587
- ...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
588
- details: {
589
- ...(eventType !== undefined ? { eventType } : {}),
590
- ...(limit !== undefined ? { limit } : {}),
591
- resultCount: events.length,
592
- },
593
- });
594
- return sendJson(res, 200, { ok: true, events });
595
- }
596
- catch (error) {
597
- return sendJson(res, 400, { error: error.message });
598
- }
599
- }
600
- function isAuditStore(audit) {
601
- return 'list' in audit && typeof audit.list === 'function';
602
- }
603
- function optionalQuery(url, key) {
604
- const value = url.searchParams.get(key);
605
- return value === null || value.trim() === '' ? undefined : value;
606
- }
607
- function parseAuditLimit(value) {
608
- if (value === null || value.trim() === '')
609
- return undefined;
610
- const parsed = Number(value);
611
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100)
612
- return false;
613
- return parsed;
614
- }
615
386
  export async function authorizeControlPlane(req, res, gate, options) {
616
387
  const auth = await gate.authorize(req);
617
388
  if (!auth.ok) {