@askexenow/exe-os 0.9.384 → 0.9.389

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 (481) hide show
  1. package/deploy/compose/.env.askexe-control-plane.example +6 -0
  2. package/deploy/compose/.env.customer.example +1 -1
  3. package/deploy/compose/.env.example +1 -1
  4. package/deploy/compose/docker-compose.yml +16 -0
  5. package/deploy/compose/generate-env.ts +1 -1
  6. package/deploy/compose/sso-edge/sso-redirect.conf +18 -3
  7. package/deploy/stack-manifests/v0.9.json +1160 -1
  8. package/dist/.dist-verified.json +3 -3
  9. package/dist/{active-agent-F5V4E4UL.js → active-agent-RZ24KSJN.js} +8 -6
  10. package/dist/{active-agent-YTBB4ZQM.js → active-agent-YWSWPIEA.js} +8 -6
  11. package/dist/{agent-defs-generator-7TFISIJQ.js → agent-defs-generator-GY3UBREJ.js} +8 -6
  12. package/dist/{agent-heartbeat-CQPGJIFF.js → agent-heartbeat-GUVUUQVM.js} +1 -1
  13. package/dist/{agent-loop-EZ7LDAGD.js → agent-loop-HPNCH3HK.js} +1 -1
  14. package/dist/{agentic-ontology-XVTUVV7W.js → agentic-ontology-RHYNXEKE.js} +1 -1
  15. package/dist/{artifact-backup-TF6YWQT2.js → artifact-backup-HGUFSIJH.js} +19 -6
  16. package/dist/{backfill-metadata-J6MXIDCW.js → backfill-metadata-XLFASEOD.js} +11 -9
  17. package/dist/{backup-verify-6DVBQMDP.js → backup-verify-TUQEICWA.js} +1 -1
  18. package/dist/{behavior-injection-XDA7ZCYN.js → behavior-injection-GXO7ERGN.js} +14 -12
  19. package/dist/{behavior-promotion-4VLQ7LXK.js → behavior-promotion-V63WIZ4N.js} +13 -11
  20. package/dist/{behaviors-SCCZP7RZ.js → behaviors-RMLDPLOF.js} +18 -11
  21. package/dist/bin/agentic-ontology-backfill.js +12 -10
  22. package/dist/bin/agentic-reflection-backfill.js +13 -11
  23. package/dist/bin/agentic-semantic-label.js +12 -10
  24. package/dist/bin/backfill-conversations.js +11 -9
  25. package/dist/bin/backfill-responses.js +11 -9
  26. package/dist/bin/backfill-vectors.js +13 -11
  27. package/dist/bin/bulk-sync-postgres.js +20 -18
  28. package/dist/bin/cc-doctor.js +15 -13
  29. package/dist/bin/cleanup-stale-review-tasks.js +44 -32
  30. package/dist/bin/cli.js +109 -30
  31. package/dist/bin/customer-readiness.js +55 -25
  32. package/dist/bin/deferred-daemon-restart.js +111 -108
  33. package/dist/bin/exe-actions.js +7 -1
  34. package/dist/bin/exe-agent-config.js +8 -6
  35. package/dist/bin/exe-agent.js +18 -16
  36. package/dist/bin/exe-assign.js +13 -11
  37. package/dist/bin/exe-boot.js +49 -47
  38. package/dist/bin/exe-call.js +9 -7
  39. package/dist/bin/exe-cicd.js +19 -8
  40. package/dist/bin/exe-cloud.js +59 -25
  41. package/dist/bin/exe-config-dump.js +8 -6
  42. package/dist/bin/exe-dispatch.js +23 -21
  43. package/dist/bin/exe-doctor.js +11 -8
  44. package/dist/bin/exe-export-behaviors.js +16 -14
  45. package/dist/bin/exe-forget.js +14 -12
  46. package/dist/bin/exe-gateway.js +12 -10
  47. package/dist/bin/exe-grant.js +6 -6
  48. package/dist/bin/exe-healthcheck.js +15 -13
  49. package/dist/bin/exe-heartbeat.js +28 -26
  50. package/dist/bin/exe-kill.js +28 -26
  51. package/dist/bin/exe-launch-agent.js +35 -33
  52. package/dist/bin/exe-mcp.js +9 -7
  53. package/dist/bin/exe-memory-reads.js +11 -9
  54. package/dist/bin/exe-new-employee.js +13 -11
  55. package/dist/bin/exe-os-mcp-proxy.js +9 -7
  56. package/dist/bin/exe-pending-messages.js +23 -21
  57. package/dist/bin/exe-pending-notifications.js +22 -20
  58. package/dist/bin/exe-pending-reviews.js +23 -21
  59. package/dist/bin/exe-preflight.js +1 -1
  60. package/dist/bin/exe-rename.js +9 -7
  61. package/dist/bin/exe-review.js +59 -55
  62. package/dist/bin/exe-search-quality.js +16 -3
  63. package/dist/bin/exe-search.js +13 -11
  64. package/dist/bin/exe-session-cleanup.js +29 -27
  65. package/dist/bin/exe-settings.js +22 -20
  66. package/dist/bin/exe-start-codex.js +23 -21
  67. package/dist/bin/exe-start-opencode.js +21 -19
  68. package/dist/bin/exe-status.js +24 -22
  69. package/dist/bin/exe-support.js +3 -3
  70. package/dist/bin/exe-team.js +8 -6
  71. package/dist/bin/exe-verify-running-images.js +644 -17
  72. package/dist/bin/git-sweep.js +24 -22
  73. package/dist/bin/graph-backfill.js +12 -10
  74. package/dist/bin/graph-export.js +12 -10
  75. package/dist/bin/hooks-cli.js +4 -4
  76. package/dist/bin/import-history.js +16 -14
  77. package/dist/bin/install.js +113 -110
  78. package/dist/bin/intercom-check.js +5 -5
  79. package/dist/bin/mcp-sessions.js +2 -2
  80. package/dist/bin/orchestration-metrics.js +8 -6
  81. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  82. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  83. package/dist/bin/pre-build-guard.js +55 -16
  84. package/dist/bin/pre-publish.js +1 -1
  85. package/dist/bin/prune-orphan-chunks.js +154 -28
  86. package/dist/bin/reconcile-backlog.js +5 -3
  87. package/dist/bin/registry-proxy.js +1 -1
  88. package/dist/bin/repair-document-chunks.js +11 -9
  89. package/dist/bin/scan-tasks.js +23 -21
  90. package/dist/bin/setup.js +3 -3
  91. package/dist/bin/shard-migrate.js +12 -10
  92. package/dist/bin/stack-update.js +622 -43
  93. package/dist/bin/verify-stack.js +2 -2
  94. package/dist/bin/vps-health-gate.js +1 -1
  95. package/dist/{boot-generation-Q7LGQ3AO.js → boot-generation-VCQXVYGT.js} +7 -5
  96. package/dist/{boot-review-sections-G5774FR5.js → boot-review-sections-3QO6Q7MP.js} +21 -19
  97. package/dist/{branch-hygiene-44WLSOZR.js → branch-hygiene-DDXXI6ZA.js} +59 -21
  98. package/dist/bug-landed-reconciler-TQVNOUCU.js +353 -0
  99. package/dist/build-manifest.json +1269 -1194
  100. package/dist/{capability-cards-I3RHCF4F.js → capability-cards-XPDLT6LU.js} +6 -4
  101. package/dist/{capacity-monitor-7I7GDONU.js → capacity-monitor-N3AW242L.js} +23 -21
  102. package/dist/{catchup-brief-3T5PARIR.js → catchup-brief-3PKX7FUP.js} +29 -27
  103. package/dist/{chunk-C5ISP5FO.js → chunk-2BKJAOO4.js} +11 -1
  104. package/dist/{chunk-JJTPPAZY.js → chunk-2CUQHG6H.js} +1609 -1356
  105. package/dist/{chunk-HU3VD4MQ.js → chunk-2Q2GGYBX.js} +1 -1
  106. package/dist/{chunk-CHIUS5QB.js → chunk-36LMIOVU.js} +9 -9
  107. package/dist/{chunk-46VSBDUW.js → chunk-3DCBDG4T.js} +2 -2
  108. package/dist/{chunk-W3KPQE7H.js → chunk-3GL3E4TD.js} +3 -3
  109. package/dist/{chunk-GL47M7NY.js → chunk-3UD3OF3X.js} +202 -117
  110. package/dist/{chunk-WNBZJSQT.js → chunk-3VW5ZP2F.js} +1 -1
  111. package/dist/{chunk-JDDM6C57.js → chunk-3X74IQBU.js} +1 -1
  112. package/dist/{chunk-NVFNADTX.js → chunk-3ZL4MDQ6.js} +18 -4
  113. package/dist/{chunk-R5E7WNAC.js → chunk-4ESNDGMB.js} +3 -3
  114. package/dist/{chunk-CYCIX7AW.js → chunk-4FLLIQRQ.js} +2 -2
  115. package/dist/{chunk-3RGTLZNO.js → chunk-4GAKF7KR.js} +99 -74
  116. package/dist/{chunk-RVO5O5SR.js → chunk-4S3A52AV.js} +38 -10
  117. package/dist/{chunk-7J2ZQ4XN.js → chunk-4VQ3QR6B.js} +29 -28
  118. package/dist/{chunk-MROSARM5.js → chunk-5BJBTFFQ.js} +7 -1
  119. package/dist/chunk-5LQZABW5.js +6 -0
  120. package/dist/chunk-5ONMHTNI.js +236 -0
  121. package/dist/{chunk-APL7HUE3.js → chunk-5QQJUO3F.js} +200 -31
  122. package/dist/{chunk-UOPCVFVR.js → chunk-5RK75SWH.js} +205 -278
  123. package/dist/{chunk-T5FEAQES.js → chunk-5TYJYGS5.js} +1 -1
  124. package/dist/{chunk-P3QQ52QK.js → chunk-5U6EOTZV.js} +3 -3
  125. package/dist/{chunk-ALRHGEAJ.js → chunk-637FL62O.js} +1 -1
  126. package/dist/{chunk-PUBA5B4M.js → chunk-6L4VIYLZ.js} +33 -19
  127. package/dist/{chunk-VF2KRROX.js → chunk-754P2UXM.js} +1 -1
  128. package/dist/{chunk-CM7N5MBY.js → chunk-7FZG7WP4.js} +5 -5
  129. package/dist/{chunk-NYLJQ2M2.js → chunk-7NEBFB3L.js} +4 -4
  130. package/dist/{chunk-E7C2WNR5.js → chunk-AEIOFFFX.js} +5 -5
  131. package/dist/{chunk-VOVFUKEB.js → chunk-AIB2EWO2.js} +5 -5
  132. package/dist/{chunk-EGOEB42T.js → chunk-AP2C4OGS.js} +3 -3
  133. package/dist/{chunk-MPZ2AVNS.js → chunk-ATNV3BVZ.js} +1 -1
  134. package/dist/{chunk-F4BBMIPG.js → chunk-B4XJQBQB.js} +2 -2
  135. package/dist/{chunk-BPVFZ6Q4.js → chunk-B5EGDNG6.js} +2 -2
  136. package/dist/{chunk-AGC2V7JS.js → chunk-B6CBN6EF.js} +1 -1
  137. package/dist/{chunk-KTKIFXIP.js → chunk-B7FDRN7G.js} +1 -1
  138. package/dist/{chunk-C5HUMAA7.js → chunk-B7WVYLAQ.js} +67 -15
  139. package/dist/chunk-BGVP5RMT.js +185 -0
  140. package/dist/{chunk-FVLNNNH6.js → chunk-BNI4LIU5.js} +35 -4
  141. package/dist/{chunk-P4XLIPB7.js → chunk-BU5JRLZT.js} +1 -1
  142. package/dist/{chunk-PHUHIVD5.js → chunk-C6U7XWOC.js} +34 -5
  143. package/dist/chunk-C7GHNJPW.js +8 -0
  144. package/dist/{chunk-4AR5XKCH.js → chunk-CL2MU3OX.js} +1 -1
  145. package/dist/{chunk-ZPP63JSQ.js → chunk-CL3AB7Z2.js} +1 -1
  146. package/dist/{chunk-FHIFTSPL.js → chunk-CP7XUSOF.js} +8 -3
  147. package/dist/{chunk-3OX34OC3.js → chunk-CUDNRHHT.js} +30 -10
  148. package/dist/chunk-CYUUNIEL.js +21 -0
  149. package/dist/{chunk-P2UY34XE.js → chunk-D4C57S2H.js} +5 -5
  150. package/dist/{chunk-XSUALA32.js → chunk-DDYFNBYV.js} +3 -3
  151. package/dist/{chunk-LSNBTGW4.js → chunk-DPYZW42H.js} +1 -1
  152. package/dist/{chunk-FMIYFHLE.js → chunk-E2PRXEZK.js} +9 -3
  153. package/dist/{chunk-L63V25HS.js → chunk-E54SPUYJ.js} +3 -3
  154. package/dist/{chunk-DMUOTCDN.js → chunk-E5CKRHIZ.js} +1 -1
  155. package/dist/{chunk-BV2IET6B.js → chunk-EVMOKRBC.js} +15 -2
  156. package/dist/{chunk-YLFCH3VA.js → chunk-FDWI3T6I.js} +133 -31
  157. package/dist/{chunk-PWC3C5ME.js → chunk-FNJTCJGY.js} +4 -4
  158. package/dist/{chunk-TNS63ATU.js → chunk-FWRPDSO3.js} +350 -50
  159. package/dist/{chunk-JA3Y5TV6.js → chunk-GDLB6BPM.js} +141 -74
  160. package/dist/{chunk-IGJMS7C5.js → chunk-GOVGG7HU.js} +2 -2
  161. package/dist/{chunk-MB47DRF5.js → chunk-GP7CTIUK.js} +2 -2
  162. package/dist/{chunk-L5EPGGDP.js → chunk-GWAN2BDB.js} +4 -4
  163. package/dist/{chunk-YRQ26MNP.js → chunk-H3YF7X4J.js} +12 -5
  164. package/dist/{chunk-VLBHZ6TF.js → chunk-H6PLIFUE.js} +2 -2
  165. package/dist/{chunk-GSLFH4AI.js → chunk-HRNM3N4Y.js} +6 -15
  166. package/dist/chunk-IG2ISRTF.js +48 -0
  167. package/dist/{chunk-MWEIBVBS.js → chunk-IJCWBYA5.js} +14 -1
  168. package/dist/{chunk-NKEFYE4R.js → chunk-IPTUKX3N.js} +1 -1
  169. package/dist/{chunk-RB3J2WYQ.js → chunk-IVE7H6KA.js} +1 -1
  170. package/dist/{chunk-HA63SH5W.js → chunk-IWY2OGQG.js} +1 -1
  171. package/dist/{chunk-BQEZPRUC.js → chunk-IZWMWJZT.js} +64 -46
  172. package/dist/{chunk-AHESD3KW.js → chunk-J4UVI652.js} +1 -1
  173. package/dist/{chunk-PLII76NJ.js → chunk-JBUCA4OX.js} +33 -6
  174. package/dist/{chunk-2PZTMSMG.js → chunk-JOF4HQDF.js} +5 -5
  175. package/dist/{chunk-OBBO6EJR.js → chunk-JQN3QGSF.js} +1 -1
  176. package/dist/{chunk-P4PIZOL2.js → chunk-JWYIH5DR.js} +3 -3
  177. package/dist/{chunk-3XY57PKT.js → chunk-KZLKE7I3.js} +210 -48
  178. package/dist/{chunk-DYVLEMKS.js → chunk-L3GXMYBK.js} +1 -1
  179. package/dist/{chunk-72V3S44T.js → chunk-LUA4SLJY.js} +46 -19
  180. package/dist/{chunk-ZT4LFTUJ.js → chunk-LW3JCBXP.js} +20 -3
  181. package/dist/{chunk-YLB365YX.js → chunk-M5XHN4OM.js} +3 -3
  182. package/dist/{chunk-VHHX4UCP.js → chunk-MDTWARSF.js} +22 -93
  183. package/dist/{chunk-JFJP6ATJ.js → chunk-MWKXQSF6.js} +3 -2
  184. package/dist/{chunk-VJEJ7JNB.js → chunk-N32DVIKY.js} +1 -1
  185. package/dist/{chunk-MUCNYSJP.js → chunk-NRLJOV7M.js} +32 -4
  186. package/dist/{chunk-PL4CFSQ7.js → chunk-NSXJOSH7.js} +1 -1
  187. package/dist/{chunk-NF5FOSLD.js → chunk-NVTG66CK.js} +1 -1
  188. package/dist/chunk-O45GBJEH.js +1500 -0
  189. package/dist/{chunk-4AAMFWJT.js → chunk-OHZSTZXN.js} +4 -2
  190. package/dist/chunk-OSIS7ZQW.js +344 -0
  191. package/dist/{chunk-E3ZM32LF.js → chunk-P22VFGFK.js} +14 -5
  192. package/dist/{chunk-KILR4FBR.js → chunk-PAFLR7WD.js} +3 -3
  193. package/dist/{chunk-R3CZWFFC.js → chunk-PEYQ6MWO.js} +15 -3
  194. package/dist/{chunk-RDWIKDMY.js → chunk-PZR54BEI.js} +8 -20
  195. package/dist/{chunk-2FKU4ZEO.js → chunk-QDBKMEYY.js} +9 -9
  196. package/dist/{chunk-7UHZBVP3.js → chunk-QDVLVLXP.js} +97 -31
  197. package/dist/{chunk-PRDDPQLC.js → chunk-QLXEQRHD.js} +5 -5
  198. package/dist/{chunk-MUAWCFW6.js → chunk-QNPDXV5V.js} +5 -2
  199. package/dist/{chunk-LR5O2XWZ.js → chunk-QUZYLDNJ.js} +5 -5
  200. package/dist/chunk-RBQUXHYK.js +48 -0
  201. package/dist/{chunk-3SUPEE6H.js → chunk-RDMBSHGG.js} +3 -3
  202. package/dist/{chunk-I5XIW2BP.js → chunk-RGUM474V.js} +7 -7
  203. package/dist/{chunk-OAFENIHN.js → chunk-RJ67XAJU.js} +76 -28
  204. package/dist/{chunk-LFUBEHFW.js → chunk-RT57D7C4.js} +39 -1
  205. package/dist/{chunk-MKM6D7JM.js → chunk-RWG7DLOG.js} +42 -5
  206. package/dist/{chunk-P3UX5EH4.js → chunk-RWKAHHIX.js} +3 -3
  207. package/dist/{chunk-XEBKIWI7.js → chunk-RYNT3HB7.js} +12 -12
  208. package/dist/{chunk-EUGEFBGV.js → chunk-S2V272KJ.js} +3 -3
  209. package/dist/{chunk-W6BZ4UGA.js → chunk-SGYEM2PD.js} +1 -1
  210. package/dist/{chunk-VROK4X2L.js → chunk-SJP57MQM.js} +1 -1
  211. package/dist/{chunk-5MVKTNZP.js → chunk-SL4JWAMQ.js} +54 -4
  212. package/dist/{chunk-MOANZQXF.js → chunk-SLMKLEU6.js} +1 -1
  213. package/dist/{chunk-RZN2LEAK.js → chunk-SQA7XCYI.js} +3 -3
  214. package/dist/{chunk-4YNA3CM6.js → chunk-SSFS4NCH.js} +262 -56
  215. package/dist/{chunk-6FIC57PH.js → chunk-T4P2G3PO.js} +1 -1
  216. package/dist/{chunk-5U2IJBFV.js → chunk-TA3B4N24.js} +1 -1
  217. package/dist/{chunk-ZOTTYCP5.js → chunk-TEWWBVQP.js} +40 -4
  218. package/dist/{chunk-SDYWZRPN.js → chunk-TLSFGXXE.js} +1 -1
  219. package/dist/{chunk-L3VUW5M4.js → chunk-TQS2YHLB.js} +1 -1
  220. package/dist/{chunk-KXYKSLW4.js → chunk-TU4M757W.js} +399 -116
  221. package/dist/{chunk-MC4IM2CC.js → chunk-TYD4F4XU.js} +6 -6
  222. package/dist/{chunk-TKS5GMYA.js → chunk-U44Y7RC7.js} +2 -0
  223. package/dist/{chunk-B7AMNZTC.js → chunk-UKBPEEEQ.js} +2 -2
  224. package/dist/{chunk-ZBPZDADW.js → chunk-UWVXQEQQ.js} +467 -78
  225. package/dist/{chunk-P2VVMGMI.js → chunk-V4WH3FV7.js} +35 -16
  226. package/dist/{worktree-ownership-guard-O6JQF2CL.js → chunk-V6F4I2XY.js} +13 -14
  227. package/dist/{chunk-JFTUAV4J.js → chunk-VBKUESIW.js} +1 -1
  228. package/dist/{chunk-AZSTIAVL.js → chunk-VBPYYJPC.js} +1 -1
  229. package/dist/{chunk-LKTN22L6.js → chunk-VIP4LKLO.js} +57 -24
  230. package/dist/{chunk-L5CGXEZC.js → chunk-WAAGVFFN.js} +63 -71
  231. package/dist/{chunk-D3LXVAIQ.js → chunk-WC7AE3PT.js} +244 -103
  232. package/dist/{chunk-EOTZSAQL.js → chunk-WUCUDHKR.js} +1 -1
  233. package/dist/{chunk-VF3BZOZK.js → chunk-X7HZHKY4.js} +50 -3
  234. package/dist/{chunk-LFBLQE2P.js → chunk-XCAKYVKI.js} +1 -1
  235. package/dist/{chunk-UXN4BICC.js → chunk-XMX5J52Z.js} +1 -1
  236. package/dist/{chunk-6YDY6ZTD.js → chunk-XTCXGWTV.js} +9 -4
  237. package/dist/{chunk-XUZK23V7.js → chunk-XW2FW6LS.js} +2 -2
  238. package/dist/{chunk-CH7BUBP3.js → chunk-XWVHUDEK.js} +5 -4
  239. package/dist/{chunk-S26CV7BE.js → chunk-YA5LK6KB.js} +45 -8
  240. package/dist/{chunk-ELCRJGCH.js → chunk-YMRQIENV.js} +2 -2
  241. package/dist/{chunk-QNZ4562V.js → chunk-Z3LXWCB7.js} +1 -1
  242. package/dist/{chunk-EYMFVITZ.js → chunk-Z7I6NQMA.js} +2 -2
  243. package/dist/{chunk-6BJCDWSM.js → chunk-ZB4FS2EG.js} +1 -1
  244. package/dist/{chunk-ZAEUEDTN.js → chunk-ZD6FZ3WL.js} +1 -1
  245. package/dist/{chunk-FEU6QEEL.js → chunk-ZM6EZAVY.js} +1 -0
  246. package/dist/{chunk-2KY7WZZF.js → chunk-ZPA4R2UI.js} +1 -1
  247. package/dist/chunk-ZPPPHVSU.js +285 -0
  248. package/dist/{chunk-ELEGH4DX.js → chunk-ZUGQPYUY.js} +51 -4
  249. package/dist/{chunk-UX2EO653.js → chunk-ZZWFZ46U.js} +66 -8
  250. package/dist/{cleanup-graph-garbage-VQAJ2RBT.js → cleanup-graph-garbage-HFUD5ZQT.js} +12 -10
  251. package/dist/{cloud-sync-command-J7NEFUY7.js → cloud-sync-command-3JHJJKPN.js} +25 -3
  252. package/dist/{cloud-sync-db-init-KWEMNMGA.js → cloud-sync-db-init-2LHIQOSF.js} +1 -1
  253. package/dist/{cloud-sync-shutdown-P63AUTUI.js → cloud-sync-shutdown-ZXAXA3Z7.js} +6 -4
  254. package/dist/{cloud-sync-state-QF3TMMJ4.js → cloud-sync-state-VSQFGPP6.js} +1 -1
  255. package/dist/{co-activation-7IHRPBZN.js → co-activation-NX4EH4AP.js} +6 -4
  256. package/dist/{co-occurrence-R5AG4OAU.js → co-occurrence-5LBYCW3U.js} +10 -8
  257. package/dist/{consent-passphrase-5ZBDJ5G7.js → consent-passphrase-65W3DJIJ.js} +2 -2
  258. package/dist/{content-extractor-ZLXC6GNR.js → content-extractor-OGXETBOX.js} +2 -2
  259. package/dist/{conversation-entity-extractor-7FRKLLBD.js → conversation-entity-extractor-FQCOAH4F.js} +5 -4
  260. package/dist/conversations-retention-XTBFJBUM.js +166 -0
  261. package/dist/{crdt-sync-IFRZOEIS.js → crdt-sync-7TDUBTLH.js} +4 -4
  262. package/dist/{crm-webhook-L2S63ASV.js → crm-webhook-776JTY55.js} +2 -2
  263. package/dist/{cto-delegation-gate-3WU7OHX7.js → cto-delegation-gate-ZNUWVYCN.js} +25 -23
  264. package/dist/{daemon-orchestration-3VQYKLOR.js → daemon-orchestration-76BOFDEP.js} +30 -24
  265. package/dist/{doc-graph-extractor-YIR5E7OF.js → doc-graph-extractor-F724KZJX.js} +10 -8
  266. package/dist/{documents-QEHPTYIY.js → documents-Q24V4GRD.js} +13 -11
  267. package/dist/{dreaming-LNFE7DTN.js → dreaming-IY6NQTSP.js} +27 -25
  268. package/dist/{employee-boot-brief-ZTXDYNWM.js → employee-boot-brief-CCTXO5DQ.js} +35 -29
  269. package/dist/{entity-boost-R5N6JWXD.js → entity-boost-XGOB7HDM.js} +1 -1
  270. package/dist/{exe-drift-WYSJVZFO.js → exe-drift-NEPXLNGP.js} +7 -5
  271. package/dist/{exe-export-GOLNAEAV.js → exe-export-ZHJQFKR2.js} +14 -12
  272. package/dist/{exe-import-U4BSI6OH.js → exe-import-7SVNI4FW.js} +14 -12
  273. package/dist/{exe-key-V42G3NGO.js → exe-key-QJKU56WZ.js} +119 -108
  274. package/dist/{exe-org-U4GLQ6OP.js → exe-org-SJXHLV6K.js} +76 -51
  275. package/dist/{exe-snapshot-U7ONBYNY.js → exe-snapshot-EFWPXTXE.js} +45 -36
  276. package/dist/{fast-db-init-W3NGKAEU.js → fast-db-init-BQZOQNT3.js} +1 -1
  277. package/dist/{founder-context-ZSIBRVDM.js → founder-context-KG2ZAOXY.js} +2 -2
  278. package/dist/{founder-monitor-envelope-L3IWZLFY.js → founder-monitor-envelope-NYB5LLU4.js} +4 -4
  279. package/dist/gateway/index.js +20 -18
  280. package/dist/{gateway-client-DJFCVIPT.js → gateway-client-ODMUXUL3.js} +1 -1
  281. package/dist/{git-staleness-OF24QXFZ.js → git-staleness-OPGEAJA7.js} +6 -4
  282. package/dist/{global-procedures-WSIOWO43.js → global-procedures-3KXY4LL6.js} +8 -6
  283. package/dist/{graph-auto-extract-IEVVN3L3.js → graph-auto-extract-I7H2F5NJ.js} +10 -8
  284. package/dist/{graph-query-JDZWAVCU.js → graph-query-E7JBSNY5.js} +1 -1
  285. package/dist/{graph-rag-BKAD2XQA.js → graph-rag-H24CMFP2.js} +5 -4
  286. package/dist/{hook-integrity-TPWDGX2M.js → hook-integrity-PGQ3OX4S.js} +1 -1
  287. package/dist/{hook-prefetch-FD6VGJJA.js → hook-prefetch-7AZJER6Z.js} +23 -21
  288. package/dist/hook-registration-liveness-NGQRFKAA.js +11 -0
  289. package/dist/hooks/bug-report-worker.js +26 -24
  290. package/dist/hooks/codex-stop-task-finalizer.js +28 -26
  291. package/dist/hooks/commit-complete.js +26 -24
  292. package/dist/hooks/error-recall.js +16 -14
  293. package/dist/hooks/exe-heartbeat-hook.js +9 -7
  294. package/dist/hooks/ingest-worker.js +12 -10
  295. package/dist/hooks/ingest.js +22 -17
  296. package/dist/hooks/instructions-loaded.js +11 -9
  297. package/dist/hooks/manifest.json +20 -20
  298. package/dist/hooks/notification.js +10 -8
  299. package/dist/hooks/post-compact.js +9 -7
  300. package/dist/hooks/post-tool-combined.js +14 -14
  301. package/dist/hooks/pre-compact.js +26 -24
  302. package/dist/hooks/pre-tool-use.js +2 -2
  303. package/dist/hooks/prompt-submit.js +47 -41
  304. package/dist/hooks/session-end.js +38 -42
  305. package/dist/hooks/session-start.js +26 -18
  306. package/dist/hooks/stop.js +29 -27
  307. package/dist/hooks/subagent-stop.js +29 -27
  308. package/dist/hooks/summary-worker.js +32 -30
  309. package/dist/index.js +37 -35
  310. package/dist/{installer-OTI5SA43.js → installer-2ZS3LHCU.js} +12 -9
  311. package/dist/{installer-NMOBSEZ2.js → installer-NH3A3HWC.js} +14 -9
  312. package/dist/{installer-SRJGE4GU.js → installer-UJ73PGRD.js} +21 -9
  313. package/dist/{intercom-queue-4FFXCBHO.js → intercom-queue-OE25GJF2.js} +21 -19
  314. package/dist/lib/agent-config.js +2 -2
  315. package/dist/lib/cloud-sync.js +24 -18
  316. package/dist/lib/consolidation.js +13 -11
  317. package/dist/lib/crypto.js +3 -3
  318. package/dist/lib/database.js +6 -4
  319. package/dist/lib/db-backup.js +1 -1
  320. package/dist/lib/db-write-worker-client.js +3 -1
  321. package/dist/lib/db-write-worker.js +65 -89
  322. package/dist/lib/db.js +6 -4
  323. package/dist/lib/delivery-truth.js +5 -1
  324. package/dist/lib/device-registry.js +1 -1
  325. package/dist/lib/embedder.js +2 -2
  326. package/dist/lib/employee-templates.js +9 -7
  327. package/dist/lib/employees.js +6 -4
  328. package/dist/lib/exe-daemon.js +425 -314
  329. package/dist/lib/file-grep.js +15 -21
  330. package/dist/lib/hybrid-search.js +19 -13
  331. package/dist/lib/identity-templates.js +1 -1
  332. package/dist/lib/identity.js +6 -4
  333. package/dist/lib/keychain.js +1 -1
  334. package/dist/lib/license.js +22 -6
  335. package/dist/lib/messaging.js +22 -20
  336. package/dist/lib/post-tool-memory.js +10 -8
  337. package/dist/lib/registry-proxy.js +1 -1
  338. package/dist/lib/reminders.js +7 -5
  339. package/dist/lib/runtime-table.js +1 -1
  340. package/dist/lib/schedules.js +12 -10
  341. package/dist/lib/session-registry.js +8 -6
  342. package/dist/lib/skill-learning.js +13 -11
  343. package/dist/lib/status-brief.js +2 -2
  344. package/dist/lib/store.js +17 -9
  345. package/dist/lib/task-router.js +7 -5
  346. package/dist/lib/tasks.js +23 -21
  347. package/dist/lib/tmux-routing.js +21 -19
  348. package/dist/lib/tmux-status.js +4 -1
  349. package/dist/lib/token-spend.js +7 -5
  350. package/dist/lib/ws-client.js +1 -1
  351. package/dist/{license-gate-R4JWTWM4.js → license-gate-XQ2KN5I5.js} +3 -3
  352. package/dist/{license-recheck-3MBJ4IJX.js → license-recheck-IIBKR2V5.js} +1 -1
  353. package/dist/{license-sync-BVSG3WMW.js → license-sync-76JVI2M5.js} +151 -8
  354. package/dist/{local-secret-inventory-JWNBHZZ4.js → local-secret-inventory-MJT7NJLY.js} +2 -2
  355. package/dist/{mailbox-inbox-MTAT4MXX.js → mailbox-inbox-BPZP4GIZ.js} +1 -1
  356. package/dist/maintenance-worker-54VZTFIK.js +371 -0
  357. package/dist/mcp/register-tools.js +110 -107
  358. package/dist/mcp/server.js +113 -110
  359. package/dist/mcp/tools/complete-reminder.js +8 -6
  360. package/dist/mcp/tools/create-reminder.js +8 -6
  361. package/dist/mcp/tools/create-task.js +26 -24
  362. package/dist/mcp/tools/deactivate-behavior.js +15 -13
  363. package/dist/mcp/tools/list-reminders.js +8 -6
  364. package/dist/mcp/tools/list-tasks.js +26 -24
  365. package/dist/mcp/tools/send-message.js +25 -23
  366. package/dist/mcp/tools/update-task.js +24 -22
  367. package/dist/{mcp-child-registry-TIZERM7O.js → mcp-child-registry-5VUG5ATZ.js} +1 -1
  368. package/dist/{mcp-http-config-VUMSFWFH.js → mcp-http-config-SKXESWHA.js} +9 -7
  369. package/dist/{mcp-session-identity-DZHWEJRV.js → mcp-session-identity-HYMLJXXA.js} +21 -19
  370. package/dist/{memory-cards-JVXTMSAT.js → memory-cards-PAZDUQ3B.js} +16 -32
  371. package/dist/{memory-graph-extractor-PPBJMYFJ.js → memory-graph-extractor-BC6QKBY6.js} +11 -9
  372. package/dist/{memory-poisoning-defense-K5Q74XQ4.js → memory-poisoning-defense-BRGIHF6J.js} +9 -7
  373. package/dist/{memory-pressure-watchdog-YMJH5CSA.js → memory-pressure-watchdog-HR6TPOMT.js} +22 -20
  374. package/dist/{memory-reflection-7BZWIHRE.js → memory-reflection-LO7VFBL3.js} +38 -59
  375. package/dist/memory-write-repository-J25USX63.js +16 -0
  376. package/dist/{monitor-grants-Q7X3MYQP.js → monitor-grants-YQTO6NWR.js} +3 -3
  377. package/dist/{monitor-identity-ZQB4IXEE.js → monitor-identity-POVTG76Y.js} +2 -2
  378. package/dist/{notification-ledger-XRAXL5OI.js → notification-ledger-KLD6GZTG.js} +22 -20
  379. package/dist/{notifications-JE3PX55X.js → notifications-3R6HNHDR.js} +21 -19
  380. package/dist/{orchestration-events-JCQ5F5IJ.js → orchestration-events-CHY3GOJD.js} +7 -5
  381. package/dist/{orchestrator-X65INDTR.js → orchestrator-GAL3HOAD.js} +23 -21
  382. package/dist/{org-brief-session-3PQ22UIX.js → org-brief-session-LQ2DBYVF.js} +17 -15
  383. package/dist/{org-consent-policy-VGELJ7K7.js → org-consent-policy-KFUP7QPG.js} +5 -5
  384. package/dist/{org-join-6TO5ZYUZ.js → org-join-MXZCIQWX.js} +8 -8
  385. package/dist/{org-key-admin-XHYVRRAZ.js → org-key-admin-4UK6QUT7.js} +9 -9
  386. package/dist/{org-key-pin-TTTYGTQL.js → org-key-pin-ZOX4OM3O.js} +3 -3
  387. package/dist/{org-membership-Y7T2BS57.js → org-membership-D5TPN45F.js} +4 -4
  388. package/dist/{org-partner-config-OP4NEUNF.js → org-partner-config-3FXSYPQA.js} +7 -7
  389. package/dist/{org-partner-runtime-YNVUNT2R.js → org-partner-runtime-TH3LMPII.js} +20 -16
  390. package/dist/{org-row-visibility-JBBXNYXD.js → org-row-visibility-Y5R33C5U.js} +1 -1
  391. package/dist/{pipeline-router-EIBB7QNG.js → pipeline-router-E2JZLDK6.js} +7 -5
  392. package/dist/{plan-limits-MEWXYLEN.js → plan-limits-NYI6R4IK.js} +9 -7
  393. package/dist/{platform-procedures-Z7ZGBJWN.js → platform-procedures-RTD5E3BD.js} +5 -3
  394. package/dist/{pre-tool-use-impl-C6KJY22W.js → pre-tool-use-impl-LT4FRXBP.js} +127 -83
  395. package/dist/{project-boot-VEYFCCSL.js → project-boot-LIMXQKAF.js} +25 -17
  396. package/dist/{project-name-UYORSNGE.js → project-name-V62GWALX.js} +7 -5
  397. package/dist/{projection-worker-WYFDWS57.js → projection-worker-KLE3IZQ2.js} +5 -2
  398. package/dist/{prospective-memory-E6XBQFJA.js → prospective-memory-HRN7JFT2.js} +6 -4
  399. package/dist/{push-notifications-M7YU63FY.js → push-notifications-XC6EQEXF.js} +22 -20
  400. package/dist/{reproject-worktree-memories-B5XMGJSD.js → reproject-worktree-memories-4CAVZSFE.js} +21 -19
  401. package/dist/{reranker-SRA4BR7N.js → reranker-2RU6IIX2.js} +1 -1
  402. package/dist/{retrieval-health-P2JPZUPV.js → retrieval-health-4Q5TOV2S.js} +1 -1
  403. package/dist/{review-polling-KQPN7IQE.js → review-polling-WOWIMBCL.js} +22 -20
  404. package/dist/runtime/index.js +30 -28
  405. package/dist/{session-events-6LDDKKY3.js → session-events-OPMS2C5F.js} +22 -20
  406. package/dist/{session-kill-telemetry-5DSRGWCL.js → session-kill-telemetry-3GWATSIX.js} +22 -20
  407. package/dist/{session-scope-XG5R3P3D.js → session-scope-TIRGR4MH.js} +21 -19
  408. package/dist/{setup-wizard-7QFLZIKT.js → setup-wizard-B2JBVGNH.js} +3 -3
  409. package/dist/{shard-manager-HHHHV2KZ.js → shard-manager-C7LYX6E4.js} +5 -1
  410. package/dist/{skill-refinement-I5RMAHSC.js → skill-refinement-FWQFYNBI.js} +7 -5
  411. package/dist/skill-retrieval-PPLFC5RY.js +166 -0
  412. package/dist/skill-usage-NDB67PU2.js +25 -0
  413. package/dist/{stack-generate-manifest-AKMZ3AUX.js → stack-generate-manifest-AXI55MEQ.js} +6 -6
  414. package/dist/{stack-promote-34CE2HHF.js → stack-promote-6N23OGZY.js} +6 -6
  415. package/dist/stack-release-2CXFYVWM.js +2770 -0
  416. package/dist/{stack-update-ZQQ5YYPJ.js → stack-update-U53FBVCQ.js} +13 -5
  417. package/dist/{steward-gate-AQJ6KT2C.js → steward-gate-TUA3QCN7.js} +7 -5
  418. package/dist/{subagent-brief-injection-3GU7YAWY.js → subagent-brief-injection-N52RWYFZ.js} +34 -32
  419. package/dist/{subagent-spawn-ledger-52I66CLW.js → subagent-spawn-ledger-OI23OT4I.js} +1 -1
  420. package/dist/support-bug-actions-XWCJI6S5.js +30 -0
  421. package/dist/support-inbox-MMVTCXRX.js +77 -0
  422. package/dist/{support-outbox-KNI3FR3G.js → support-outbox-2TG2UV6J.js} +2 -2
  423. package/dist/{task-enforcement-CAA6D2MH.js → task-enforcement-JDMBAAOY.js} +22 -20
  424. package/dist/{task-request-inbox-QBZVU66E.js → task-request-inbox-PKJCYKJV.js} +9 -5
  425. package/dist/{task-scope-NHXB4ZRN.js → task-scope-32HQEV6W.js} +27 -19
  426. package/dist/{tasks-crud-JHSWL5NM.js → tasks-crud-CYKUKS3F.js} +21 -19
  427. package/dist/{tasks-notify-7PBE4U7T.js → tasks-notify-YND5MKWJ.js} +22 -20
  428. package/dist/{tasks-review-PPPVEE62.js → tasks-review-CG2A5GM3.js} +23 -19
  429. package/dist/{telemetry-upload-YU2VRNHX.js → telemetry-upload-WLUMEFAB.js} +11 -9
  430. package/dist/{token-budget-QCMWKG3K.js → token-budget-3YHAY5LL.js} +7 -5
  431. package/dist/{tool-capability-index-7AWQAJRP.js → tool-capability-index-2ADOWH43.js} +2 -2
  432. package/dist/{tool-gates-5Q3RSQEG.js → tool-gates-7ENOOYMR.js} +5 -3
  433. package/dist/{tool-gates-QD26D2NE.js → tool-gates-EKJTLVGN.js} +1 -1
  434. package/dist/{tool-telemetry-LTOIC64I.js → tool-telemetry-VUP7S6HC.js} +1 -1
  435. package/dist/tui/App.js +59 -35
  436. package/dist/{tui-data-NDBXD6U7.js → tui-data-BOQTOW2G.js} +25 -23
  437. package/dist/{update-support-BMJM4NG7.js → update-support-WT3S3AQ4.js} +21 -21
  438. package/dist/{whatsapp-QQLDAHUX.js → whatsapp-OZGIF2EM.js} +2 -2
  439. package/dist/{wiki-acl-W6WDCVCP.js → wiki-acl-LTEQJVDA.js} +1 -1
  440. package/dist/{work-claims-6DM32MGO.js → work-claims-CMV2GWIW.js} +8 -6
  441. package/dist/{work-state-heartbeat-HG7D4AWJ.js → work-state-heartbeat-S45TXK4M.js} +1 -1
  442. package/dist/{worker-gate-FTMW7GBB.js → worker-gate-7Z6NMM3G.js} +1 -1
  443. package/dist/{workflow-engine-ZWC6CCXE.js → workflow-engine-ZSQDXZQ7.js} +2 -2
  444. package/dist/{worktree-EMQCPGFE.js → worktree-NMWDVT6N.js} +8 -6
  445. package/dist/worktree-ownership-guard-ZVIWJAOP.js +35 -0
  446. package/dist/{worktree-sweep-XVUJZBXV.js → worktree-sweep-Q62ZIT3U.js} +9 -7
  447. package/package.json +14 -8
  448. package/release-notes.json +7 -41
  449. package/src/commands/exe/afk.md +6 -0
  450. package/src/commands/exe/call.md +6 -0
  451. package/src/commands/exe/employee-heartbeat.md +6 -0
  452. package/src/commands/exe/intercom.md +6 -0
  453. package/src/commands/exe/kill.md +6 -0
  454. package/src/commands/exe/launch.md +6 -0
  455. package/src/commands/exe/logs.md +1 -1
  456. package/src/commands/exe/sessions.md +6 -0
  457. package/src/commands/exe/setup.md +7 -3
  458. package/src/commands/exe/status.md +6 -0
  459. package/src/commands/exe-reference.md +25 -36
  460. package/src/commands/exe.md +19 -28
  461. package/stack.release.json +11 -11
  462. package/dist/chunk-3B6FLNLV.js +0 -30
  463. package/dist/chunk-FXU7JOXK.js +0 -6
  464. package/dist/chunk-QFAFKEB5.js +0 -820
  465. package/dist/conversations-retention-C4BLKFEL.js +0 -54
  466. package/dist/stack-release-5PGWV5D4.js +0 -1607
  467. package/dist/support-inbox-NY5TB3FN.js +0 -51
  468. /package/dist/{chunk-U2D6PJ5T.js → chunk-65G7ZTGE.js} +0 -0
  469. /package/dist/{chunk-DN3ZR3UE.js → chunk-CP7Z35MJ.js} +0 -0
  470. /package/dist/{chunk-XDVOGDBC.js → chunk-KBBWALJV.js} +0 -0
  471. /package/dist/{chunk-JRIH6I7D.js → chunk-LZXYPTY4.js} +0 -0
  472. /package/dist/{chunk-GIRD7O5X.js → chunk-MCWEW2SG.js} +0 -0
  473. /package/dist/{chunk-TKAU7IJ2.js → chunk-MQHUJLCA.js} +0 -0
  474. /package/dist/{chunk-M4XY4JRK.js → chunk-O53LTOXK.js} +0 -0
  475. /package/dist/{chunk-DZN7SDY2.js → chunk-OSVTH3SG.js} +0 -0
  476. /package/dist/{chunk-AJK2PVQW.js → chunk-QXXEXGX3.js} +0 -0
  477. /package/dist/{chunk-AWWDKF22.js → chunk-SFWNTO2F.js} +0 -0
  478. /package/dist/{chunk-2Q2CD6CD.js → chunk-WC5HF2FN.js} +0 -0
  479. /package/dist/{core-memory-O6D5UC3T.js → core-memory-MMMW3TF3.js} +0 -0
  480. /package/dist/{memory-isolation-EBFFC6HF.js → memory-isolation-67ACKQ2M.js} +0 -0
  481. /package/dist/{oauth-server-ZKBUHDFI.js → oauth-server-OILIKR7D.js} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  deployStalenessAlertPath,
7
7
  evaluateDeployStaleness,
8
8
  writeDeployStalenessAlert
9
- } from "./chunk-C5HUMAA7.js";
9
+ } from "./chunk-B7WVYLAQ.js";
10
10
  import {
11
11
  REVISION_LABEL,
12
12
  VERSION_LABEL,
@@ -15,13 +15,13 @@ import {
15
15
  } from "./chunk-L25OAFOU.js";
16
16
  import {
17
17
  declaredArtifactsFromManifest
18
- } from "./chunk-LFUBEHFW.js";
18
+ } from "./chunk-RT57D7C4.js";
19
19
  import {
20
20
  loadLicense
21
- } from "./chunk-TNS63ATU.js";
21
+ } from "./chunk-FWRPDSO3.js";
22
22
 
23
23
  // src/lib/stack-update.ts
24
- import { execFileSync, spawnSync } from "child_process";
24
+ import { execFileSync, spawnSync as spawnSync2 } from "child_process";
25
25
  import { createVerify, randomBytes, verify as verifySignature } from "crypto";
26
26
  import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
27
27
  import http from "http";
@@ -119,6 +119,240 @@ ${remedy}
119
119
  return gaps;
120
120
  }
121
121
 
122
+ // src/lib/stack-lock-derive.ts
123
+ import { spawnSync } from "child_process";
124
+ var GIT_SHA_LABEL = "org.exe.git_sha";
125
+ var GIT_REF_LABEL = "org.exe.git_ref";
126
+ function normalizeProbeResult(observed) {
127
+ if (observed === null) return null;
128
+ if (Array.isArray(observed)) return { containers: observed, uninspectable: [] };
129
+ return { containers: observed.containers ?? [], uninspectable: observed.uninspectable ?? [] };
130
+ }
131
+ function deriveStackLockFromContainers(lock, observed, now = () => /* @__PURE__ */ new Date()) {
132
+ const base = { ...lock ?? {} };
133
+ const services = { ...lock?.services ?? {} };
134
+ const probed = normalizeProbeResult(observed);
135
+ if (probed === null) {
136
+ return {
137
+ lock: base,
138
+ observations: [],
139
+ drifted: [],
140
+ unobserved: Object.keys(services),
141
+ uninspectable: [],
142
+ unobservable: true,
143
+ incomplete: true
144
+ };
145
+ }
146
+ const unreadable = new Set(probed.uninspectable.map((s) => s.trim()).filter(Boolean));
147
+ const byComposeService = /* @__PURE__ */ new Map();
148
+ for (const container of probed.containers) {
149
+ const key = container.composeService.trim();
150
+ if (!key) continue;
151
+ const existing = byComposeService.get(key);
152
+ if (existing) existing.push(container);
153
+ else byComposeService.set(key, [container]);
154
+ }
155
+ const at = now().toISOString();
156
+ const observations = [];
157
+ const unobserved = [];
158
+ const uninspectable = [];
159
+ const claimedUninspectable = /* @__PURE__ */ new Set();
160
+ const lockComposeServices = new Set(
161
+ Object.entries(services).map(
162
+ ([name, entry]) => (entry.composeService ?? name).trim() || name
163
+ )
164
+ );
165
+ for (const [serviceName, entry] of Object.entries(services)) {
166
+ const composeService = (entry.composeService ?? serviceName).trim() || serviceName;
167
+ if (unreadable.has(composeService)) {
168
+ uninspectable.push(serviceName);
169
+ claimedUninspectable.add(composeService);
170
+ continue;
171
+ }
172
+ const replicas = byComposeService.get(composeService) ?? [];
173
+ const container = replicas[0];
174
+ if (!container) {
175
+ unobserved.push(serviceName);
176
+ continue;
177
+ }
178
+ const recordedImage = typeof entry.image === "string" ? entry.image : void 0;
179
+ const recordedGitSha = typeof entry.gitSha === "string" ? entry.gitSha : void 0;
180
+ const observedGitSha = container.gitSha?.trim() || void 0;
181
+ const imageDrifted = normalizeImage(recordedImage) !== normalizeImage(container.image) || Boolean(entry.imageId && container.imageId && entry.imageId !== container.imageId);
182
+ const shaDrifted = observedGitSha !== void 0 && (recordedGitSha ?? "").toLowerCase() !== observedGitSha.toLowerCase();
183
+ const derived = {
184
+ ...entry,
185
+ image: container.image,
186
+ observedAt: at,
187
+ imageId: container.imageId
188
+ };
189
+ if (observedGitSha) {
190
+ derived.gitSha = observedGitSha;
191
+ derived.provenance = "container-label";
192
+ } else {
193
+ derived.provenance = "container-image";
194
+ if (imageDrifted) {
195
+ delete derived.gitSha;
196
+ derived.gitShaUnknown = true;
197
+ }
198
+ }
199
+ if (container.gitRef?.trim()) derived.gitRef = container.gitRef.trim();
200
+ else if (imageDrifted) delete derived.gitRef;
201
+ const staleContainers = [
202
+ ...replicas.slice(1),
203
+ ...probed.containers.filter(
204
+ (c) => isSidecarOf(composeService, c.composeService.trim()) && !lockComposeServices.has(c.composeService.trim())
205
+ )
206
+ ].filter((c) => c !== container && runsSomethingElse(container, c)).map((c) => ({
207
+ composeService: c.composeService.trim(),
208
+ image: c.image,
209
+ ...c.imageId ? { imageId: c.imageId } : {},
210
+ gitSha: c.gitSha?.trim() || void 0
211
+ }));
212
+ if (staleContainers.length > 0) {
213
+ derived.staleContainers = staleContainers;
214
+ derived.inconsistent = true;
215
+ } else {
216
+ delete derived.staleContainers;
217
+ delete derived.inconsistent;
218
+ }
219
+ services[serviceName] = derived;
220
+ observations.push({
221
+ service: serviceName,
222
+ composeService,
223
+ recordedImage,
224
+ recordedGitSha,
225
+ observedImage: container.image,
226
+ observedGitSha,
227
+ observedGitRef: container.gitRef?.trim() || void 0,
228
+ ...staleContainers.length > 0 ? { staleContainers } : {},
229
+ drifted: imageDrifted || shaDrifted || staleContainers.length > 0
230
+ });
231
+ }
232
+ for (const composeService of unreadable) {
233
+ if (!claimedUninspectable.has(composeService)) uninspectable.push(composeService);
234
+ }
235
+ const inconsistent = observations.filter((o) => o.staleContainers?.length).map((o) => o.service);
236
+ const incomplete = uninspectable.length > 0 || unobserved.length > 0 || observations.length === 0 || inconsistent.length > 0;
237
+ base.services = services;
238
+ base.derivedAt = at;
239
+ base.derivedFrom = "container-labels";
240
+ if (incomplete) base.derivationUnverified = [...uninspectable, ...unobserved, ...inconsistent];
241
+ else delete base.derivationUnverified;
242
+ return {
243
+ lock: base,
244
+ observations,
245
+ drifted: observations.filter((o) => o.drifted),
246
+ unobserved,
247
+ uninspectable,
248
+ unobservable: false,
249
+ incomplete
250
+ };
251
+ }
252
+ function formatStackLockDrift(drifted) {
253
+ return drifted.map((d) => {
254
+ const was = `${d.recordedImage ?? "(none)"}${d.recordedGitSha ? ` / ${d.recordedGitSha.slice(0, 12)}` : ""}`;
255
+ const is = `${d.observedImage}${d.observedGitSha ? ` / ${d.observedGitSha.slice(0, 12)}` : ""}`;
256
+ const siblings = (d.staleContainers ?? []).map((c) => `
257
+ also running: ${c.composeService} -> ${c.image}${c.gitSha ? ` / ${c.gitSha.slice(0, 12)}` : ""}`).join("");
258
+ return ` - ${d.service} (${d.composeService}): lock recorded ${was}; container is running ${is}${siblings}`;
259
+ }).join("\n");
260
+ }
261
+ function isSidecarOf(composeService, candidate) {
262
+ if (!composeService || !candidate || candidate === composeService) return false;
263
+ return candidate.startsWith(`${composeService}-`) || candidate.startsWith(`${composeService}_`);
264
+ }
265
+ function imageRepo(image) {
266
+ const trimmed = image.trim();
267
+ const at = trimmed.lastIndexOf("@");
268
+ const base = at === -1 ? trimmed : trimmed.slice(0, at);
269
+ const slash = base.lastIndexOf("/");
270
+ const colon = base.lastIndexOf(":");
271
+ return colon > slash ? base.slice(0, colon) : base;
272
+ }
273
+ function runsSomethingElse(primary, sidecar) {
274
+ if ((primary.composeService.trim() === sidecar.composeService.trim() || imageRepo(primary.image) === imageRepo(sidecar.image)) && primary.imageId && sidecar.imageId && primary.imageId !== sidecar.imageId) return true;
275
+ if ((primary.composeService.trim() === sidecar.composeService.trim() || imageRepo(primary.image) === imageRepo(sidecar.image)) && normalizeImage(primary.image) !== normalizeImage(sidecar.image)) return true;
276
+ const primarySha = primary.gitSha?.trim().toLowerCase();
277
+ const sidecarSha = sidecar.gitSha?.trim().toLowerCase();
278
+ if (primarySha && sidecarSha) return primarySha !== sidecarSha;
279
+ if (imageRepo(primary.image) !== imageRepo(sidecar.image)) return false;
280
+ return normalizeImage(primary.image) !== normalizeImage(sidecar.image);
281
+ }
282
+ function normalizeImage(image) {
283
+ if (!image) return "";
284
+ const trimmed = image.trim();
285
+ const at = trimmed.lastIndexOf("@");
286
+ return at === -1 ? trimmed : trimmed.slice(at + 1);
287
+ }
288
+ var defaultContainerProvenanceProbe = (composeArgs) => {
289
+ const ps = spawnSync("docker", [...composeArgs, "ps", "--format", "json"], { encoding: "utf8" });
290
+ if (ps.status !== 0 || typeof ps.stdout !== "string") return null;
291
+ const records = parseComposePsRecords(ps.stdout);
292
+ if (records.length === 0) return null;
293
+ const containers = [];
294
+ const uninspectable = [];
295
+ for (const { service, name } of records) {
296
+ const inspect = spawnSync("docker", ["inspect", name, "--format", '{"Config":{{json .Config}},"Image":{{json .Image}}}'], { encoding: "utf8" });
297
+ if (inspect.status !== 0 || typeof inspect.stdout !== "string") {
298
+ uninspectable.push(service);
299
+ continue;
300
+ }
301
+ let inspected;
302
+ try {
303
+ inspected = JSON.parse(inspect.stdout.trim());
304
+ } catch {
305
+ uninspectable.push(service);
306
+ continue;
307
+ }
308
+ const config = inspected?.Config ?? {};
309
+ const imageId = typeof inspected?.Image === "string" ? inspected.Image : "";
310
+ const image = typeof config.Image === "string" ? config.Image : "";
311
+ if (!image || !imageId) {
312
+ uninspectable.push(service);
313
+ continue;
314
+ }
315
+ const labels = config.Labels ?? {};
316
+ containers.push({
317
+ composeService: service,
318
+ image,
319
+ imageId,
320
+ gitSha: typeof labels[GIT_SHA_LABEL] === "string" ? labels[GIT_SHA_LABEL] : void 0,
321
+ gitRef: typeof labels[GIT_REF_LABEL] === "string" ? labels[GIT_REF_LABEL] : void 0
322
+ });
323
+ }
324
+ return { containers, uninspectable };
325
+ };
326
+ function parseComposePsRecords(stdout) {
327
+ const trimmed = stdout.trim().replace(/^/, "");
328
+ if (!trimmed) return [];
329
+ const records = [];
330
+ if (trimmed.startsWith("[")) {
331
+ try {
332
+ const parsed = JSON.parse(trimmed);
333
+ if (Array.isArray(parsed)) records.push(...parsed);
334
+ } catch {
335
+ }
336
+ }
337
+ if (records.length === 0) {
338
+ for (const line of trimmed.split("\n")) {
339
+ const l = line.trim();
340
+ if (!l) continue;
341
+ try {
342
+ records.push(JSON.parse(l));
343
+ } catch {
344
+ }
345
+ }
346
+ }
347
+ const out = [];
348
+ for (const obj of records) {
349
+ const service = String(obj.Service ?? "").trim();
350
+ const name = String(obj.Name ?? "").trim();
351
+ if (service && name) out.push({ service, name });
352
+ }
353
+ return out;
354
+ }
355
+
122
356
  // src/lib/stack-update.ts
123
357
  var SLOW_SERVICE_HEALTH_DEFAULTS = {
124
358
  // Frappe `bench migrate` on every boot can take well over the 180s default.
@@ -862,6 +1096,7 @@ async function rollbackStackUpdate(options) {
862
1096
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
863
1097
  });
864
1098
  }
1099
+ reconcileDeployRecordNonFatal("a rollback", lockFilePath, composeArgs, () => /* @__PURE__ */ new Date());
865
1100
  console.log("[stack-update] Rollback complete.");
866
1101
  return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: lockFilePath, serviceStatus: lockData?.serviceStatus };
867
1102
  }
@@ -908,7 +1143,7 @@ function isExecFormMigration(migrationCommand) {
908
1143
  }
909
1144
  function isComposeServiceRunning(composeArgs, composeServiceName) {
910
1145
  try {
911
- const result = spawnSync("docker", [...composeArgs, "ps", "--quiet", composeServiceName], {
1146
+ const result = spawnSync2("docker", [...composeArgs, "ps", "--quiet", composeServiceName], {
912
1147
  encoding: "utf8",
913
1148
  stdio: ["pipe", "pipe", "pipe"],
914
1149
  timeout: 1e4
@@ -978,6 +1213,72 @@ function writeStackUpdateLock(lockFile, data) {
978
1213
  writeFileSync(lockFile, JSON.stringify(data, null, 2) + "\n", { mode: 420 });
979
1214
  chmodSync(lockFile, 420);
980
1215
  }
1216
+ function reconcileStackLockFromContainers(lockFile, composeArgs, options = {}) {
1217
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
1218
+ const probe = options.probe ?? defaultContainerProvenanceProbe;
1219
+ const lock = readStackUpdateLock(lockFile);
1220
+ if (!lock) {
1221
+ return { drifted: [], unobserved: [], uninspectable: [], observed: 0, incomplete: true, unobservable: true, written: false };
1222
+ }
1223
+ let observed;
1224
+ try {
1225
+ observed = probe(composeArgs);
1226
+ } catch {
1227
+ observed = null;
1228
+ }
1229
+ const derivation = deriveStackLockFromContainers(lock, observed, now);
1230
+ if (derivation.unobservable) {
1231
+ console.warn(
1232
+ `[stack-update] \u26A0 Could not observe the running containers, so ${path.basename(lockFile)} was NOT derived. It still reports whatever was last written to it \u2014 treat it as unverified.`
1233
+ );
1234
+ return {
1235
+ drifted: [],
1236
+ unobserved: derivation.unobserved,
1237
+ uninspectable: [],
1238
+ observed: 0,
1239
+ incomplete: true,
1240
+ unobservable: true,
1241
+ written: false
1242
+ };
1243
+ }
1244
+ if (derivation.drifted.length > 0) {
1245
+ console.warn(
1246
+ `[stack-update] \u26A0 Deploy record drift (bug 32920edc): ${derivation.drifted.length} service(s) were delivered by a route that never updated ${path.basename(lockFile)}:
1247
+ ${formatStackLockDrift(derivation.drifted)}`
1248
+ );
1249
+ }
1250
+ if (derivation.incomplete) {
1251
+ const inconsistent = derivation.observations.filter((o) => o.staleContainers?.length).map((o) => o.service);
1252
+ console.warn(
1253
+ `[stack-update] \u26A0 ${path.basename(lockFile)} derivation is INCOMPLETE (${derivation.observations.length} service(s) observed` + (derivation.uninspectable.length > 0 ? `; could not read: ${derivation.uninspectable.join(", ")}` : "") + (derivation.unobserved.length > 0 ? `; no running container: ${derivation.unobserved.join(", ")}` : "") + (inconsistent.length > 0 ? `; inconsistent containers: ${inconsistent.join(", ")}` : "") + `). Those entries are UNVERIFIED; observed conflicts remain in the record.`
1254
+ );
1255
+ }
1256
+ const result = {
1257
+ drifted: derivation.drifted,
1258
+ unobserved: derivation.unobserved,
1259
+ uninspectable: derivation.uninspectable,
1260
+ observed: derivation.observations.length,
1261
+ incomplete: derivation.incomplete,
1262
+ unobservable: false,
1263
+ written: false
1264
+ };
1265
+ if (options.dryRun) return result;
1266
+ writeStackUpdateLock(lockFile, derivation.lock);
1267
+ return { ...result, written: true };
1268
+ }
1269
+ function reconcileDeployRecordNonFatal(stage, lockFile, composeArgs, now) {
1270
+ try {
1271
+ const result = reconcileStackLockFromContainers(lockFile, composeArgs, { now });
1272
+ if (result.unobservable || result.incomplete) {
1273
+ console.warn(
1274
+ `[stack-update] \u26A0 Deploy record after ${stage} is NOT fully verified (${result.observed} service(s) read from containers). Treat ${path.basename(lockFile)} as partial evidence.`
1275
+ );
1276
+ }
1277
+ } catch (deriveErr) {
1278
+ const reason = deriveErr instanceof Error ? deriveErr.message : String(deriveErr);
1279
+ console.warn(`[stack-update] \u26A0 Deploy-record derivation failed after ${stage} (non-fatal): ${reason}`);
1280
+ }
1281
+ }
981
1282
  function composeOverrideFileFor(composeFile) {
982
1283
  return path.join(path.dirname(composeFile), "docker-compose.override.yml");
983
1284
  }
@@ -1568,7 +1869,7 @@ function collectImageAvailabilityIssues(plan) {
1568
1869
  let inspectOutput = "";
1569
1870
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1570
1871
  try {
1571
- const result = spawnSync("docker", ["manifest", "inspect", manifestInspectRef(image), "--verbose"], {
1872
+ const result = spawnSync2("docker", ["manifest", "inspect", manifestInspectRef(image), "--verbose"], {
1572
1873
  stdio: ["ignore", "pipe", "pipe"],
1573
1874
  timeout: 3e4
1574
1875
  });
@@ -1583,7 +1884,7 @@ function collectImageAvailabilityIssues(plan) {
1583
1884
  console.log(
1584
1885
  `[stack-update] Image ${image} not yet available on proxy (attempt ${attempt}/${maxAttempts}), retrying in ${delayMs / 1e3}s\u2026 (bug d11c9809)`
1585
1886
  );
1586
- spawnSync("sleep", [String(delayMs / 1e3)], { stdio: "ignore" });
1887
+ spawnSync2("sleep", [String(delayMs / 1e3)], { stdio: "ignore" });
1587
1888
  }
1588
1889
  } catch {
1589
1890
  inspected = true;
@@ -1888,41 +2189,21 @@ async function resolveStackImageCredentials(options, release) {
1888
2189
  }
1889
2190
  return null;
1890
2191
  }
1891
- async function verifyManifestImagesAvailable(plan, options) {
1892
- const exec = options.exec ?? defaultExec;
1893
- const images = manifestImagesForPull(plan.release);
1894
- const maxAttempts = Math.max(1, Math.floor(options.verifyImageMaxAttempts ?? 3));
1895
- const baseDelayMs = Math.max(0, Math.floor(options.verifyImageRetryBaseDelayMs ?? 1e3));
2192
+ async function withStackImageCredentials(plan, options, verify) {
1896
2193
  let registryForLogout;
1897
- const creds = await resolveStackImageCredentials(options, plan.release);
1898
- if (creds) {
1899
- (options.dockerLogin ?? defaultDockerLogin)(creds);
1900
- registryForLogout = creds.registry;
1901
- }
1902
2194
  try {
1903
- const results = [];
1904
- for (const image of images) {
1905
- let lastError = "";
1906
- let ok = false;
1907
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1908
- try {
1909
- exec("docker", ["manifest", "inspect", manifestInspectRef(image)]);
1910
- ok = true;
1911
- break;
1912
- } catch (err) {
1913
- lastError = err instanceof Error ? err.message : String(err);
1914
- if (attempt < maxAttempts) {
1915
- const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
1916
- console.warn(
1917
- `[stack-update] manifest inspect failed for ${image} (attempt ${attempt}/${maxAttempts}): ${lastError}. Retrying in ${delayMs}ms.`
1918
- );
1919
- if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
1920
- }
1921
- }
1922
- }
1923
- results.push(ok ? { image, ok: true } : { image, ok: false, error: `manifest inspect failed after ${maxAttempts} attempt(s): ${lastError}` });
2195
+ const creds = await resolveStackImageCredentials(options, plan.release);
2196
+ if (creds) {
2197
+ (options.dockerLogin ?? defaultDockerLogin)(creds);
2198
+ registryForLogout = creds.registry;
1924
2199
  }
1925
- return results;
2200
+ } catch (err) {
2201
+ throw new Error(
2202
+ `Pre-deploy registry authentication check failed before any production service was changed. Aborting stack update for service(s) ${allServiceList(plan.release)}: ${errorMessage(err)}`
2203
+ );
2204
+ }
2205
+ try {
2206
+ return await verify();
1926
2207
  } finally {
1927
2208
  if (registryForLogout) {
1928
2209
  try {
@@ -1932,6 +2213,38 @@ async function verifyManifestImagesAvailable(plan, options) {
1932
2213
  }
1933
2214
  }
1934
2215
  }
2216
+ async function inspectManifestImagesAvailable(plan, options) {
2217
+ const exec = options.exec ?? defaultExec;
2218
+ const images = manifestImagesForPull(plan.release);
2219
+ const maxAttempts = Math.max(1, Math.floor(options.verifyImageMaxAttempts ?? 3));
2220
+ const baseDelayMs = Math.max(0, Math.floor(options.verifyImageRetryBaseDelayMs ?? 1e3));
2221
+ const results = [];
2222
+ for (const image of images) {
2223
+ let lastError = "";
2224
+ let ok = false;
2225
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2226
+ try {
2227
+ exec("docker", ["manifest", "inspect", manifestInspectRef(image)]);
2228
+ ok = true;
2229
+ break;
2230
+ } catch (err) {
2231
+ lastError = err instanceof Error ? err.message : String(err);
2232
+ if (attempt < maxAttempts) {
2233
+ const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
2234
+ console.warn(
2235
+ `[stack-update] manifest inspect failed for ${image} (attempt ${attempt}/${maxAttempts}): ${lastError}. Retrying in ${delayMs}ms.`
2236
+ );
2237
+ if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
2238
+ }
2239
+ }
2240
+ }
2241
+ results.push(ok ? { image, ok: true } : { image, ok: false, error: `manifest inspect failed after ${maxAttempts} attempt(s): ${lastError}` });
2242
+ }
2243
+ return results;
2244
+ }
2245
+ async function verifyManifestImagesAvailable(plan, options) {
2246
+ return withStackImageCredentials(plan, options, () => inspectManifestImagesAvailable(plan, options));
2247
+ }
1935
2248
  function servicesForPullImage(release, image) {
1936
2249
  return Object.entries(release.services).filter(([, service]) => imagePullRef(service.image) === image).map(([serviceName, service]) => {
1937
2250
  const composeService = service.composeService ?? serviceName;
@@ -1962,7 +2275,7 @@ function readStagedComposeTemplate(options, currentComposeRaw) {
1962
2275
  async function assertPreDeployImagesPullable(plan, options) {
1963
2276
  let results;
1964
2277
  try {
1965
- results = await verifyManifestImagesAvailable(plan, options);
2278
+ results = await inspectManifestImagesAvailable(plan, options);
1966
2279
  } catch (err) {
1967
2280
  throw new Error(
1968
2281
  `Pre-deploy registry authentication check failed before any production service was changed. Aborting stack update for service(s) ${allServiceList(plan.release)}: ${errorMessage(err)}`
@@ -1980,8 +2293,21 @@ ${details}
1980
2293
  Fix registry authentication or publish the missing image(s), then re-run stack-update.`
1981
2294
  );
1982
2295
  }
2296
+ async function verifyPreDeployImages(plan, options) {
2297
+ await withStackImageCredentials(plan, options, async () => {
2298
+ console.log("[stack-update] Pre-deploy image auth/pullability check started");
2299
+ await assertPreDeployImagesPullable(plan, options);
2300
+ console.log("[stack-update] \u2713 Pre-deploy image auth/pullability check passed");
2301
+ console.log("[stack-update] Deploy-time image-provenance check (G4) started");
2302
+ assertImageProvenance(plan, options);
2303
+ console.log("[stack-update] \u2713 Deploy-time image-provenance check (G4) passed");
2304
+ console.log("[stack-update] Deploy-time image version-label check (G4b) started");
2305
+ assertImageVersionLabels(plan, options);
2306
+ console.log("[stack-update] \u2713 Deploy-time image version-label check (G4b) passed");
2307
+ });
2308
+ }
1983
2309
  var defaultContainerCountSnapshot = () => {
1984
- const snapshot = spawnSync("docker", ["ps", "--format", "json"], { encoding: "utf8", timeout: 1e4 });
2310
+ const snapshot = spawnSync2("docker", ["ps", "--format", "json"], { encoding: "utf8", timeout: 1e4 });
1985
2311
  return snapshot.stdout?.split("\n").filter(Boolean).length ?? 0;
1986
2312
  };
1987
2313
  var defaultImageLabelReader = (image, label) => {
@@ -2097,7 +2423,7 @@ A first-party image whose version label does not match its tag cannot be traced
2097
2423
  }
2098
2424
  var PROBE_TIMEOUT_MS = 1e4;
2099
2425
  function commandSucceeds(cmd, args = []) {
2100
- const res = spawnSync(cmd, args, {
2426
+ const res = spawnSync2(cmd, args, {
2101
2427
  stdio: "ignore",
2102
2428
  timeout: PROBE_TIMEOUT_MS,
2103
2429
  killSignal: "SIGKILL"
@@ -2105,7 +2431,7 @@ function commandSucceeds(cmd, args = []) {
2105
2431
  return res.status === 0;
2106
2432
  }
2107
2433
  function shellSucceeds(command) {
2108
- const res = spawnSync("sh", ["-lc", command], {
2434
+ const res = spawnSync2("sh", ["-lc", command], {
2109
2435
  stdio: "ignore",
2110
2436
  timeout: PROBE_TIMEOUT_MS,
2111
2437
  killSignal: "SIGKILL"
@@ -2536,7 +2862,7 @@ function hardenHost(exec) {
2536
2862
  return result;
2537
2863
  }
2538
2864
  function hardenUfw(exec, result) {
2539
- const whichUfw = spawnSync("which", ["ufw"], { stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 });
2865
+ const whichUfw = spawnSync2("which", ["ufw"], { stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 });
2540
2866
  const ufwInstalled = whichUfw.status === 0;
2541
2867
  if (!ufwInstalled) {
2542
2868
  if (!existsSync("/etc/os-release")) {
@@ -2561,7 +2887,7 @@ function hardenUfw(exec, result) {
2561
2887
  return;
2562
2888
  }
2563
2889
  }
2564
- const statusResult = spawnSync("ufw", ["status", "verbose"], {
2890
+ const statusResult = spawnSync2("ufw", ["status", "verbose"], {
2565
2891
  encoding: "utf8",
2566
2892
  stdio: ["pipe", "pipe", "pipe"],
2567
2893
  timeout: 1e4
@@ -2713,7 +3039,7 @@ function hardenSsh(exec, result) {
2713
3039
  }
2714
3040
  function areStackContainersRunning(composeFile, envFile) {
2715
3041
  try {
2716
- const result = spawnSync("docker", [...composeArgsForStack(composeFile, envFile), "ps", "-q"], {
3042
+ const result = spawnSync2("docker", [...composeArgsForStack(composeFile, envFile), "ps", "-q"], {
2717
3043
  stdio: ["pipe", "pipe", "pipe"],
2718
3044
  timeout: 15e3
2719
3045
  });
@@ -2740,7 +3066,7 @@ var CRITICAL_VOLUMES = [
2740
3066
  "clickhouse_data",
2741
3067
  "erp_sites"
2742
3068
  ];
2743
- var defaultVolumeExists = (volumeName) => spawnSync(
3069
+ var defaultVolumeExists = (volumeName) => spawnSync2(
2744
3070
  "docker",
2745
3071
  ["volume", "inspect", volumeName],
2746
3072
  {
@@ -3034,15 +3360,7 @@ ${lines.join("\n")}`
3034
3360
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
3035
3361
  }
3036
3362
  if (!options.skipImageAvailabilityCheck) {
3037
- console.log("[stack-update] Pre-deploy image auth/pullability check started");
3038
- await assertPreDeployImagesPullable(plan, options);
3039
- console.log("[stack-update] \u2713 Pre-deploy image auth/pullability check passed");
3040
- console.log("[stack-update] Deploy-time image-provenance check (G4) started");
3041
- assertImageProvenance(plan, options);
3042
- console.log("[stack-update] \u2713 Deploy-time image-provenance check (G4) passed");
3043
- console.log("[stack-update] Deploy-time image version-label check (G4b) started");
3044
- assertImageVersionLabels(plan, options);
3045
- console.log("[stack-update] \u2713 Deploy-time image version-label check (G4b) passed");
3363
+ await verifyPreDeployImages(plan, options);
3046
3364
  }
3047
3365
  await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
3048
3366
  try {
@@ -3388,7 +3706,7 @@ Push the missing image(s) to the registry, or re-run with --break-glass <reason>
3388
3706
  if (bundledIsolation && (failedComposeServices.has(service) || failedManifestServices.has(resolveOwningManifestService(service, plan.release.services)))) {
3389
3707
  continue;
3390
3708
  }
3391
- const serviceDefined = options.composeServiceExists ? options.composeServiceExists(service) : spawnSync("docker", [...composeArgs, "ps", "--quiet", service], {
3709
+ const serviceDefined = options.composeServiceExists ? options.composeServiceExists(service) : spawnSync2("docker", [...composeArgs, "ps", "--quiet", service], {
3392
3710
  encoding: "utf8",
3393
3711
  stdio: ["pipe", "pipe", "pipe"],
3394
3712
  timeout: 1e4
@@ -3396,7 +3714,7 @@ Push the missing image(s) to the registry, or re-run with --break-glass <reason>
3396
3714
  if (!serviceDefined) continue;
3397
3715
  progress.logServiceStep(restartIndex, service, "Restarting");
3398
3716
  if (service === "exe-wiki") {
3399
- const guard = options.runWikiSchemaGuard ? { status: options.runWikiSchemaGuard() } : spawnSync(
3717
+ const guard = options.runWikiSchemaGuard ? { status: options.runWikiSchemaGuard() } : spawnSync2(
3400
3718
  "docker",
3401
3719
  [...composeArgs, "run", "--rm", "--no-deps", "exe-wiki-schema-guard"],
3402
3720
  { encoding: "utf8", stdio: ["pipe", "inherit", "inherit"], timeout: 12e4 }
@@ -3412,7 +3730,7 @@ Push the missing image(s) to the registry, or re-run with --break-glass <reason>
3412
3730
  updateServiceStatus(lockFile, manifestServiceName, "restarted", now);
3413
3731
  const maxWait = resolveServiceHealthTimeout(service, plan.release, options.healthTimeoutSecs);
3414
3732
  const probeHealth = options.inspectContainerHealth ?? ((svc) => {
3415
- const inspectResult = spawnSync(
3733
+ const inspectResult = spawnSync2(
3416
3734
  "docker",
3417
3735
  ["inspect", "--format", "{{.State.Health.Status}}", svc],
3418
3736
  { encoding: "utf8", timeout: 5e3 }
@@ -3567,6 +3885,7 @@ Push the missing image(s) to the registry, or re-run with --break-glass <reason>
3567
3885
  preUpdateImageDefaults: partialLock.preUpdateImageDefaults ?? configBackup.preUpdateImageDefaults,
3568
3886
  services: { ...fullTargetPlan.release.services, ...partialLock.services ?? {}, ...plan.release.services }
3569
3887
  });
3888
+ reconcileDeployRecordNonFatal("a partial update", lockFile, composeArgs, now);
3570
3889
  const partialStaleness = evaluateDeployStaleness(readStackUpdateLock(lockFile), now);
3571
3890
  if (partialStaleness) writeDeployStalenessAlert(partialStaleness);
3572
3891
  await postDeployAudit(
@@ -3672,6 +3991,7 @@ Fix these before the stack is safe to operate.`);
3672
3991
  preUpdateImageDefaults: finalLock.preUpdateImageDefaults ?? configBackup.preUpdateImageDefaults,
3673
3992
  services: { ...fullTargetPlan.release.services, ...finalLock.services ?? {}, ...plan.release.services }
3674
3993
  });
3994
+ reconcileDeployRecordNonFatal("a successful update", lockFile, composeArgs, now);
3675
3995
  clearDeployStalenessAlert();
3676
3996
  await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
3677
3997
  return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile, serviceStatus: readStackUpdateLock(lockFile)?.serviceStatus };
@@ -3838,7 +4158,7 @@ async function verifyReleaseHealth(release, retries, delayMs, env, skipServices)
3838
4158
  var defaultComposePsCapture = (composeArgs) => {
3839
4159
  let result;
3840
4160
  try {
3841
- result = spawnSync("docker", [...composeArgs, "ps", "--all", "--format", "json"], {
4161
+ result = spawnSync2("docker", [...composeArgs, "ps", "--all", "--format", "json"], {
3842
4162
  encoding: "utf8",
3843
4163
  stdio: ["pipe", "pipe", "pipe"],
3844
4164
  timeout: 2e4
@@ -3907,29 +4227,38 @@ function snapshotComposeServiceStates(composeArgs, wantedServices, capture) {
3907
4227
  if (records.length === 0) return null;
3908
4228
  const byService = /* @__PURE__ */ new Map();
3909
4229
  for (const svc of wantedServices) {
3910
- byService.set(svc, { service: svc, state: "", health: "", restartCount: 0, missing: true });
4230
+ byService.set(svc, { service: svc, state: "", health: "", restartCount: 0, exitCode: null, missing: true });
3911
4231
  }
3912
4232
  for (const obj of records) {
3913
4233
  const service = String(obj.Service ?? obj.Name ?? "");
3914
4234
  if (!service || !byService.has(service)) continue;
3915
4235
  const restartRaw = obj.RestartCount;
4236
+ const exitRaw = obj.ExitCode;
3916
4237
  byService.set(service, {
3917
4238
  service,
3918
4239
  state: String(obj.State ?? "").toLowerCase(),
3919
4240
  health: String(obj.Health ?? "").toLowerCase(),
3920
4241
  restartCount: typeof restartRaw === "number" ? restartRaw : Number(restartRaw) || 0,
4242
+ // Absent / unparsable stays null. Compose omits ExitCode on some versions
4243
+ // and reports it as a string on others; only a real number is an answer.
4244
+ exitCode: typeof exitRaw === "number" ? exitRaw : typeof exitRaw === "string" && exitRaw.trim() !== "" && Number.isFinite(Number(exitRaw)) ? Number(exitRaw) : null,
3921
4245
  missing: false
3922
4246
  });
3923
4247
  }
3924
4248
  return byService;
3925
4249
  }
3926
- async function verifyComposeServicesRunning(composeArgs, wantedServices, timeoutSecs = 180, capture) {
4250
+ async function verifyComposeServicesRunning(composeArgs, wantedServices, timeoutSecs = 180, capture, oneShotServices, requireObservableState = false) {
3927
4251
  if (wantedServices.length === 0) return;
3928
4252
  const psCapture = capture ?? defaultComposePsCapture;
3929
4253
  const deadline = Date.now() + timeoutSecs * 1e3;
3930
4254
  for (; ; ) {
3931
4255
  const states = snapshotComposeServiceStates(composeArgs, wantedServices, psCapture);
3932
4256
  if (states === null) {
4257
+ if (requireObservableState) {
4258
+ throw new Error(
4259
+ "Post-deploy health verification failed closed (Phase 3): container runtime state unavailable (docker compose ps returned nothing usable)"
4260
+ );
4261
+ }
3933
4262
  console.warn("[stack-update] Phase 3 skipped: container runtime state unavailable (docker compose ps returned nothing)");
3934
4263
  return;
3935
4264
  }
@@ -3941,6 +4270,21 @@ async function verifyComposeServicesRunning(composeArgs, wantedServices, timeout
3941
4270
  hardFailures.push(`${svc}: container missing (never created)`);
3942
4271
  continue;
3943
4272
  }
4273
+ if (oneShotServices?.has(svc)) {
4274
+ if (s.state === "exited") {
4275
+ if (s.exitCode === 0) continue;
4276
+ hardFailures.push(
4277
+ s.exitCode === null ? `${svc}: one-shot container exited but compose reported no exit status, so completion could not be confirmed` : `${svc}: one-shot container failed (exit code ${s.exitCode})`
4278
+ );
4279
+ continue;
4280
+ }
4281
+ if (s.state === "dead") {
4282
+ hardFailures.push(`${svc}: container dead`);
4283
+ continue;
4284
+ }
4285
+ pending.push(`${svc}: one-shot still running (no exit status yet)`);
4286
+ continue;
4287
+ }
3944
4288
  if (s.state === "exited" || s.state === "dead") {
3945
4289
  hardFailures.push(`${svc}: container ${s.state} (restarts: ${s.restartCount})`);
3946
4290
  continue;
@@ -3981,7 +4325,7 @@ var INGRESS_PROBE_TIMEOUT_MS = 4e3;
3981
4325
  var defaultDockerCapture = (args) => {
3982
4326
  let result;
3983
4327
  try {
3984
- result = spawnSync("docker", args, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 2e4 });
4328
+ result = spawnSync2("docker", args, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 2e4 });
3985
4329
  } catch (err) {
3986
4330
  return { unavailable: true, status: null, stdout: "", stderr: err instanceof Error ? err.message : String(err) };
3987
4331
  }
@@ -4135,16 +4479,57 @@ async function defaultFetchText(ref, authToken) {
4135
4479
  if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
4136
4480
  return res.text();
4137
4481
  }
4482
+ function assertCredentialSafeAuditUrl(url) {
4483
+ let parsed;
4484
+ try {
4485
+ parsed = new URL(url);
4486
+ } catch {
4487
+ throw new Error(`Refusing to post a deploy audit to an unparseable URL: ${url}`);
4488
+ }
4489
+ if (parsed.protocol === "https:") return;
4490
+ const host = parsed.hostname.toLowerCase();
4491
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]") return;
4492
+ throw new Error(
4493
+ `Refusing to post a deploy audit over ${parsed.protocol}// to ${parsed.host}: the request carries the license key in its body and a bearer token in its headers, which plain HTTP exposes to anything on the network path. Use an https:// endpoint (the default is https://api.askexe.com), or point it at localhost.`
4494
+ );
4495
+ }
4496
+ var MAX_AUDIT_REDIRECTS = 5;
4138
4497
  async function defaultPostJson(url, body, authToken) {
4139
- const res = await fetch(url, {
4140
- method: "POST",
4141
- headers: {
4142
- "content-type": "application/json",
4143
- ...authToken ? { authorization: `Bearer ${authToken}` } : {}
4144
- },
4145
- body: JSON.stringify(body)
4146
- });
4147
- if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
4498
+ const payload = JSON.stringify(body);
4499
+ let target = url;
4500
+ let originOrigin;
4501
+ for (let hop = 0; ; hop++) {
4502
+ assertCredentialSafeAuditUrl(target);
4503
+ const targetOrigin = new URL(target).origin;
4504
+ if (originOrigin === void 0) {
4505
+ originOrigin = targetOrigin;
4506
+ } else if (targetOrigin !== originOrigin) {
4507
+ throw new Error(
4508
+ `Refusing to follow a deploy-audit redirect from ${originOrigin} to ${targetOrigin}: this request carries the license key in its body and a bearer token in its Authorization header, and this loop re-attaches both to every hop itself, so the Fetch spec's cross-origin credential stripping never applies. A redirect to a different origin \u2014 even one that is independently https \u2014 is refused rather than forwarded with credentials attached.`
4509
+ );
4510
+ }
4511
+ const res = await fetch(target, {
4512
+ method: "POST",
4513
+ headers: {
4514
+ "content-type": "application/json",
4515
+ ...authToken ? { authorization: `Bearer ${authToken}` } : {}
4516
+ },
4517
+ body: payload,
4518
+ redirect: "manual"
4519
+ });
4520
+ if (res.status < 300 || res.status > 399) {
4521
+ if (!res.ok) throw new Error(`Failed to POST ${target}: HTTP ${res.status}`);
4522
+ return;
4523
+ }
4524
+ if (hop >= MAX_AUDIT_REDIRECTS) {
4525
+ throw new Error(`Failed to POST ${url}: more than ${MAX_AUDIT_REDIRECTS} redirects (last: ${target})`);
4526
+ }
4527
+ const location = res.headers.get("location");
4528
+ if (!location) {
4529
+ throw new Error(`Failed to POST ${target}: HTTP ${res.status} redirect with no Location header`);
4530
+ }
4531
+ target = new URL(location, target).toString();
4532
+ }
4148
4533
  }
4149
4534
  function defaultStackPaths() {
4150
4535
  const cwdCompose = path.resolve("docker-compose.yml");
@@ -4201,7 +4586,7 @@ function parseComposeVolumeNames(composeRaw) {
4201
4586
  return names;
4202
4587
  }
4203
4588
  function volumeHasComposeProjectLabel(volumeName, projectName) {
4204
- const res = spawnSync(
4589
+ const res = spawnSync2(
4205
4590
  "docker",
4206
4591
  ["volume", "inspect", volumeName, "--format", '{{ index .Labels "com.docker.compose.project" }}'],
4207
4592
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
@@ -4230,7 +4615,7 @@ function detectDanglingVolumes(scope) {
4230
4615
  byName.set(name, { name, driver: driver ?? "local", size: "" });
4231
4616
  }
4232
4617
  };
4233
- const labeled = spawnSync(
4618
+ const labeled = spawnSync2(
4234
4619
  "docker",
4235
4620
  [
4236
4621
  "volume",
@@ -4245,7 +4630,7 @@ function detectDanglingVolumes(scope) {
4245
4630
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
4246
4631
  );
4247
4632
  if (labeled.status === 0 && labeled.stdout?.trim()) ingest(labeled.stdout, false);
4248
- const all = spawnSync(
4633
+ const all = spawnSync2(
4249
4634
  "docker",
4250
4635
  ["volume", "ls", "--filter", "dangling=true", "--format", "{{.Name}} {{.Driver}}"],
4251
4636
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
@@ -4260,14 +4645,14 @@ function detectDanglingVolumes(scope) {
4260
4645
  }
4261
4646
  function getVolumeSize(volumeName) {
4262
4647
  try {
4263
- const inspectResult = spawnSync(
4648
+ const inspectResult = spawnSync2(
4264
4649
  "docker",
4265
4650
  ["volume", "inspect", volumeName, "--format", "{{.Mountpoint}}"],
4266
4651
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
4267
4652
  );
4268
4653
  const mountpoint = inspectResult.stdout?.trim();
4269
4654
  if (!mountpoint || inspectResult.status !== 0) return "unknown";
4270
- const duResult = spawnSync(
4655
+ const duResult = spawnSync2(
4271
4656
  "du",
4272
4657
  ["-sh", mountpoint],
4273
4658
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
@@ -4296,7 +4681,7 @@ function removeDanglingVolumes(volumes, scope) {
4296
4681
  );
4297
4682
  continue;
4298
4683
  }
4299
- const checkResult = spawnSync(
4684
+ const checkResult = spawnSync2(
4300
4685
  "docker",
4301
4686
  ["volume", "ls", "--filter", "dangling=true", "--filter", `name=^${vol.name}$`, "--format", "{{.Name}}"],
4302
4687
  { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
@@ -4403,6 +4788,7 @@ export {
4403
4788
  isExecFormMigration,
4404
4789
  expandComposeServicesWithSidecars,
4405
4790
  resolveOwningManifestService,
4791
+ reconcileStackLockFromContainers,
4406
4792
  composeArgsForStack,
4407
4793
  parseStackManifest,
4408
4794
  loadStackManifest,
@@ -4444,6 +4830,7 @@ export {
4444
4830
  assertRolloutStageAllowed,
4445
4831
  assertBreakingChangesAllowed,
4446
4832
  verifyManifestImagesAvailable,
4833
+ verifyPreDeployImages,
4447
4834
  collectImageProvenanceIssues,
4448
4835
  assertImageProvenance,
4449
4836
  collectImageVersionLabelIssues,
@@ -4473,6 +4860,8 @@ export {
4473
4860
  verifyIngressTargetsReachable,
4474
4861
  waitForHttpOk,
4475
4862
  httpStatus,
4863
+ assertCredentialSafeAuditUrl,
4864
+ defaultPostJson,
4476
4865
  defaultStackPaths,
4477
4866
  resolveComposeProjectName,
4478
4867
  parseComposeVolumeNames,