@pleri/olam-cli 0.1.201 → 0.1.205

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 (833) hide show
  1. package/README.md +102 -169
  2. package/dist/agent-stream/agent-sdk-to-chunks.js +1 -1
  3. package/dist/agent-stream/driver-runner.js +73 -7
  4. package/dist/agent-stream/host-driver-launch.js +14 -1
  5. package/dist/agent-stream/prototype-gen-launch.js +113 -0
  6. package/dist/agent-stream/question-broker-bridge.js +335 -0
  7. package/dist/agent-stream/sdk-env-merge.demo.js +35 -0
  8. package/dist/agent-stream/sdk-env-merge.js +98 -0
  9. package/dist/image-digests.json +8 -8
  10. package/dist/index.js +9533 -6258
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-server.js +24721 -12934
  13. package/hermes-bundle/version.json +1 -1
  14. package/hooks/__tests__/_loader.py +26 -0
  15. package/hooks/__tests__/prompts.py +63 -0
  16. package/hooks/__tests__/test_classify_bench.py +94 -0
  17. package/hooks/__tests__/test_classify_pins.py +78 -0
  18. package/hooks/model-router.py +17 -4
  19. package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
  20. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
  21. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
  22. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
  23. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
  24. package/host-cp/src/op-side-longpoll.mjs +212 -0
  25. package/host-cp/src/plan-chat-proxy-headers.mjs +53 -0
  26. package/host-cp/src/plan-chat-service.mjs +100 -0
  27. package/host-cp/src/plan-orchestrator.mjs +100 -6
  28. package/host-cp/src/server.mjs +607 -45
  29. package/memory-hooks/agentmemory-classify-queue.mjs +363 -0
  30. package/memory-hooks/agentmemory-recall-trigger.mjs +233 -0
  31. package/memory-hooks/agentmemory-reflect-cite.mjs +332 -0
  32. package/memory-hooks/agentmemory-session-recall.js +332 -0
  33. package/memory-hooks/recall-log.mjs +185 -0
  34. package/package.json +9 -4
  35. package/dist/ask/checkout.d.ts +0 -19
  36. package/dist/ask/checkout.d.ts.map +0 -1
  37. package/dist/ask/checkout.js +0 -40
  38. package/dist/ask/checkout.js.map +0 -1
  39. package/dist/ask/knowledge-pack-builder.d.ts +0 -72
  40. package/dist/ask/knowledge-pack-builder.d.ts.map +0 -1
  41. package/dist/ask/knowledge-pack-builder.js +0 -96
  42. package/dist/ask/knowledge-pack-builder.js.map +0 -1
  43. package/dist/ask/knowledge-pack.generated.d.ts +0 -8
  44. package/dist/ask/knowledge-pack.generated.d.ts.map +0 -1
  45. package/dist/ask/knowledge-pack.generated.js +0 -2362
  46. package/dist/ask/knowledge-pack.generated.js.map +0 -1
  47. package/dist/ask/one-shot.d.ts +0 -21
  48. package/dist/ask/one-shot.d.ts.map +0 -1
  49. package/dist/ask/one-shot.js +0 -50
  50. package/dist/ask/one-shot.js.map +0 -1
  51. package/dist/ask/repl.d.ts +0 -30
  52. package/dist/ask/repl.d.ts.map +0 -1
  53. package/dist/ask/repl.js +0 -109
  54. package/dist/ask/repl.js.map +0 -1
  55. package/dist/ask/sdk-client.d.ts +0 -87
  56. package/dist/ask/sdk-client.d.ts.map +0 -1
  57. package/dist/ask/sdk-client.js +0 -118
  58. package/dist/ask/sdk-client.js.map +0 -1
  59. package/dist/ask/system-prompt.d.ts +0 -30
  60. package/dist/ask/system-prompt.d.ts.map +0 -1
  61. package/dist/ask/system-prompt.js +0 -31
  62. package/dist/ask/system-prompt.js.map +0 -1
  63. package/dist/cli-version.d.ts +0 -16
  64. package/dist/cli-version.d.ts.map +0 -1
  65. package/dist/cli-version.js +0 -39
  66. package/dist/cli-version.js.map +0 -1
  67. package/dist/commands/ask.d.ts +0 -27
  68. package/dist/commands/ask.d.ts.map +0 -1
  69. package/dist/commands/ask.js +0 -63
  70. package/dist/commands/ask.js.map +0 -1
  71. package/dist/commands/auth-list-json.d.ts +0 -87
  72. package/dist/commands/auth-list-json.d.ts.map +0 -1
  73. package/dist/commands/auth-list-json.js +0 -71
  74. package/dist/commands/auth-list-json.js.map +0 -1
  75. package/dist/commands/auth-migrate.d.ts +0 -212
  76. package/dist/commands/auth-migrate.d.ts.map +0 -1
  77. package/dist/commands/auth-migrate.js +0 -465
  78. package/dist/commands/auth-migrate.js.map +0 -1
  79. package/dist/commands/auth-status.d.ts +0 -51
  80. package/dist/commands/auth-status.d.ts.map +0 -1
  81. package/dist/commands/auth-status.js +0 -250
  82. package/dist/commands/auth-status.js.map +0 -1
  83. package/dist/commands/auth-upgrade.d.ts +0 -88
  84. package/dist/commands/auth-upgrade.d.ts.map +0 -1
  85. package/dist/commands/auth-upgrade.js +0 -431
  86. package/dist/commands/auth-upgrade.js.map +0 -1
  87. package/dist/commands/auth.d.ts +0 -31
  88. package/dist/commands/auth.d.ts.map +0 -1
  89. package/dist/commands/auth.js +0 -784
  90. package/dist/commands/auth.js.map +0 -1
  91. package/dist/commands/begin.d.ts +0 -27
  92. package/dist/commands/begin.d.ts.map +0 -1
  93. package/dist/commands/begin.js +0 -45
  94. package/dist/commands/begin.js.map +0 -1
  95. package/dist/commands/bootstrap.d.ts +0 -111
  96. package/dist/commands/bootstrap.d.ts.map +0 -1
  97. package/dist/commands/bootstrap.js +0 -485
  98. package/dist/commands/bootstrap.js.map +0 -1
  99. package/dist/commands/clean.d.ts +0 -41
  100. package/dist/commands/clean.d.ts.map +0 -1
  101. package/dist/commands/clean.js +0 -382
  102. package/dist/commands/clean.js.map +0 -1
  103. package/dist/commands/completion.d.ts +0 -30
  104. package/dist/commands/completion.d.ts.map +0 -1
  105. package/dist/commands/completion.js +0 -50
  106. package/dist/commands/completion.js.map +0 -1
  107. package/dist/commands/config.d.ts +0 -3
  108. package/dist/commands/config.d.ts.map +0 -1
  109. package/dist/commands/config.js +0 -146
  110. package/dist/commands/config.js.map +0 -1
  111. package/dist/commands/create.d.ts +0 -8
  112. package/dist/commands/create.d.ts.map +0 -1
  113. package/dist/commands/create.js +0 -775
  114. package/dist/commands/create.js.map +0 -1
  115. package/dist/commands/crystallize.d.ts +0 -18
  116. package/dist/commands/crystallize.d.ts.map +0 -1
  117. package/dist/commands/crystallize.js +0 -123
  118. package/dist/commands/crystallize.js.map +0 -1
  119. package/dist/commands/destroy.d.ts +0 -59
  120. package/dist/commands/destroy.d.ts.map +0 -1
  121. package/dist/commands/destroy.js +0 -148
  122. package/dist/commands/destroy.js.map +0 -1
  123. package/dist/commands/diagnose.d.ts +0 -36
  124. package/dist/commands/diagnose.d.ts.map +0 -1
  125. package/dist/commands/diagnose.js +0 -177
  126. package/dist/commands/diagnose.js.map +0 -1
  127. package/dist/commands/dispatch-resolve.d.ts +0 -54
  128. package/dist/commands/dispatch-resolve.d.ts.map +0 -1
  129. package/dist/commands/dispatch-resolve.js +0 -105
  130. package/dist/commands/dispatch-resolve.js.map +0 -1
  131. package/dist/commands/dispatch.d.ts +0 -18
  132. package/dist/commands/dispatch.d.ts.map +0 -1
  133. package/dist/commands/dispatch.js +0 -159
  134. package/dist/commands/dispatch.js.map +0 -1
  135. package/dist/commands/doctor.d.ts +0 -258
  136. package/dist/commands/doctor.d.ts.map +0 -1
  137. package/dist/commands/doctor.js +0 -1073
  138. package/dist/commands/doctor.js.map +0 -1
  139. package/dist/commands/enter.d.ts +0 -63
  140. package/dist/commands/enter.d.ts.map +0 -1
  141. package/dist/commands/enter.js +0 -230
  142. package/dist/commands/enter.js.map +0 -1
  143. package/dist/commands/flywheel/check-persona-skeleton.d.ts +0 -35
  144. package/dist/commands/flywheel/check-persona-skeleton.d.ts.map +0 -1
  145. package/dist/commands/flywheel/check-persona-skeleton.js +0 -151
  146. package/dist/commands/flywheel/check-persona-skeleton.js.map +0 -1
  147. package/dist/commands/flywheel/diversity-check.d.ts +0 -17
  148. package/dist/commands/flywheel/diversity-check.d.ts.map +0 -1
  149. package/dist/commands/flywheel/diversity-check.js +0 -64
  150. package/dist/commands/flywheel/diversity-check.js.map +0 -1
  151. package/dist/commands/flywheel/emit-breadcrumb.d.ts +0 -20
  152. package/dist/commands/flywheel/emit-breadcrumb.d.ts.map +0 -1
  153. package/dist/commands/flywheel/emit-breadcrumb.js +0 -137
  154. package/dist/commands/flywheel/emit-breadcrumb.js.map +0 -1
  155. package/dist/commands/flywheel/index.d.ts +0 -27
  156. package/dist/commands/flywheel/index.d.ts.map +0 -1
  157. package/dist/commands/flywheel/index.js +0 -54
  158. package/dist/commands/flywheel/index.js.map +0 -1
  159. package/dist/commands/flywheel/install-sessionstart-hook.d.ts +0 -64
  160. package/dist/commands/flywheel/install-sessionstart-hook.d.ts.map +0 -1
  161. package/dist/commands/flywheel/install-sessionstart-hook.js +0 -197
  162. package/dist/commands/flywheel/install-sessionstart-hook.js.map +0 -1
  163. package/dist/commands/flywheel/install-shims.d.ts +0 -41
  164. package/dist/commands/flywheel/install-shims.d.ts.map +0 -1
  165. package/dist/commands/flywheel/install-shims.js +0 -126
  166. package/dist/commands/flywheel/install-shims.js.map +0 -1
  167. package/dist/commands/flywheel/k10-measure.d.ts +0 -17
  168. package/dist/commands/flywheel/k10-measure.d.ts.map +0 -1
  169. package/dist/commands/flywheel/k10-measure.js +0 -63
  170. package/dist/commands/flywheel/k10-measure.js.map +0 -1
  171. package/dist/commands/flywheel/k5-score.d.ts +0 -14
  172. package/dist/commands/flywheel/k5-score.d.ts.map +0 -1
  173. package/dist/commands/flywheel/k5-score.js +0 -59
  174. package/dist/commands/flywheel/k5-score.js.map +0 -1
  175. package/dist/commands/flywheel/k5-validate.d.ts +0 -46
  176. package/dist/commands/flywheel/k5-validate.d.ts.map +0 -1
  177. package/dist/commands/flywheel/k5-validate.js +0 -246
  178. package/dist/commands/flywheel/k5-validate.js.map +0 -1
  179. package/dist/commands/flywheel/migrate-overlays.d.ts +0 -116
  180. package/dist/commands/flywheel/migrate-overlays.d.ts.map +0 -1
  181. package/dist/commands/flywheel/migrate-overlays.js +0 -792
  182. package/dist/commands/flywheel/migrate-overlays.js.map +0 -1
  183. package/dist/commands/flywheel/ping.d.ts +0 -21
  184. package/dist/commands/flywheel/ping.d.ts.map +0 -1
  185. package/dist/commands/flywheel/ping.js +0 -79
  186. package/dist/commands/flywheel/ping.js.map +0 -1
  187. package/dist/commands/flywheel/sanitize-persona-output.d.ts +0 -38
  188. package/dist/commands/flywheel/sanitize-persona-output.d.ts.map +0 -1
  189. package/dist/commands/flywheel/sanitize-persona-output.js +0 -102
  190. package/dist/commands/flywheel/sanitize-persona-output.js.map +0 -1
  191. package/dist/commands/flywheel/session-start.d.ts +0 -26
  192. package/dist/commands/flywheel/session-start.d.ts.map +0 -1
  193. package/dist/commands/flywheel/session-start.js +0 -119
  194. package/dist/commands/flywheel/session-start.js.map +0 -1
  195. package/dist/commands/hermes-kg-hook.d.ts +0 -36
  196. package/dist/commands/hermes-kg-hook.d.ts.map +0 -1
  197. package/dist/commands/hermes-kg-hook.js +0 -80
  198. package/dist/commands/hermes-kg-hook.js.map +0 -1
  199. package/dist/commands/hermes.d.ts +0 -46
  200. package/dist/commands/hermes.d.ts.map +0 -1
  201. package/dist/commands/hermes.js +0 -320
  202. package/dist/commands/hermes.js.map +0 -1
  203. package/dist/commands/host-cp.d.ts +0 -216
  204. package/dist/commands/host-cp.d.ts.map +0 -1
  205. package/dist/commands/host-cp.js +0 -913
  206. package/dist/commands/host-cp.js.map +0 -1
  207. package/dist/commands/implode.d.ts +0 -86
  208. package/dist/commands/implode.d.ts.map +0 -1
  209. package/dist/commands/implode.js +0 -468
  210. package/dist/commands/implode.js.map +0 -1
  211. package/dist/commands/init.d.ts +0 -86
  212. package/dist/commands/init.d.ts.map +0 -1
  213. package/dist/commands/init.js +0 -357
  214. package/dist/commands/init.js.map +0 -1
  215. package/dist/commands/install.d.ts +0 -22
  216. package/dist/commands/install.d.ts.map +0 -1
  217. package/dist/commands/install.js +0 -203
  218. package/dist/commands/install.js.map +0 -1
  219. package/dist/commands/keys-list-json.d.ts +0 -55
  220. package/dist/commands/keys-list-json.d.ts.map +0 -1
  221. package/dist/commands/keys-list-json.js +0 -54
  222. package/dist/commands/keys-list-json.js.map +0 -1
  223. package/dist/commands/keys.d.ts +0 -26
  224. package/dist/commands/keys.d.ts.map +0 -1
  225. package/dist/commands/keys.js +0 -157
  226. package/dist/commands/keys.js.map +0 -1
  227. package/dist/commands/kg-build.d.ts +0 -80
  228. package/dist/commands/kg-build.d.ts.map +0 -1
  229. package/dist/commands/kg-build.js +0 -282
  230. package/dist/commands/kg-build.js.map +0 -1
  231. package/dist/commands/kg-classify.d.ts +0 -30
  232. package/dist/commands/kg-classify.d.ts.map +0 -1
  233. package/dist/commands/kg-classify.js +0 -88
  234. package/dist/commands/kg-classify.js.map +0 -1
  235. package/dist/commands/kg-doctor.d.ts +0 -76
  236. package/dist/commands/kg-doctor.d.ts.map +0 -1
  237. package/dist/commands/kg-doctor.js +0 -262
  238. package/dist/commands/kg-doctor.js.map +0 -1
  239. package/dist/commands/kg-install-hook.d.ts +0 -20
  240. package/dist/commands/kg-install-hook.d.ts.map +0 -1
  241. package/dist/commands/kg-install-hook.js +0 -208
  242. package/dist/commands/kg-install-hook.js.map +0 -1
  243. package/dist/commands/kg-mirror.d.ts +0 -72
  244. package/dist/commands/kg-mirror.d.ts.map +0 -1
  245. package/dist/commands/kg-mirror.js +0 -397
  246. package/dist/commands/kg-mirror.js.map +0 -1
  247. package/dist/commands/kg-savings.d.ts +0 -20
  248. package/dist/commands/kg-savings.d.ts.map +0 -1
  249. package/dist/commands/kg-savings.js +0 -77
  250. package/dist/commands/kg-savings.js.map +0 -1
  251. package/dist/commands/kg-service-container.d.ts +0 -68
  252. package/dist/commands/kg-service-container.d.ts.map +0 -1
  253. package/dist/commands/kg-service-container.js +0 -191
  254. package/dist/commands/kg-service-container.js.map +0 -1
  255. package/dist/commands/kg-status.d.ts +0 -59
  256. package/dist/commands/kg-status.d.ts.map +0 -1
  257. package/dist/commands/kg-status.js +0 -344
  258. package/dist/commands/kg-status.js.map +0 -1
  259. package/dist/commands/kg-uninstall-hook.d.ts +0 -12
  260. package/dist/commands/kg-uninstall-hook.d.ts.map +0 -1
  261. package/dist/commands/kg-uninstall-hook.js +0 -121
  262. package/dist/commands/kg-uninstall-hook.js.map +0 -1
  263. package/dist/commands/kg-watch.d.ts +0 -49
  264. package/dist/commands/kg-watch.d.ts.map +0 -1
  265. package/dist/commands/kg-watch.js +0 -172
  266. package/dist/commands/kg-watch.js.map +0 -1
  267. package/dist/commands/lanes-list-json.d.ts +0 -69
  268. package/dist/commands/lanes-list-json.d.ts.map +0 -1
  269. package/dist/commands/lanes-list-json.js +0 -42
  270. package/dist/commands/lanes-list-json.js.map +0 -1
  271. package/dist/commands/lanes.d.ts +0 -18
  272. package/dist/commands/lanes.d.ts.map +0 -1
  273. package/dist/commands/lanes.js +0 -133
  274. package/dist/commands/lanes.js.map +0 -1
  275. package/dist/commands/list.d.ts +0 -33
  276. package/dist/commands/list.d.ts.map +0 -1
  277. package/dist/commands/list.js +0 -87
  278. package/dist/commands/list.js.map +0 -1
  279. package/dist/commands/logs.d.ts +0 -52
  280. package/dist/commands/logs.d.ts.map +0 -1
  281. package/dist/commands/logs.js +0 -180
  282. package/dist/commands/logs.js.map +0 -1
  283. package/dist/commands/mcp/add.d.ts +0 -9
  284. package/dist/commands/mcp/add.d.ts.map +0 -1
  285. package/dist/commands/mcp/add.js +0 -87
  286. package/dist/commands/mcp/add.js.map +0 -1
  287. package/dist/commands/mcp/client.d.ts +0 -60
  288. package/dist/commands/mcp/client.d.ts.map +0 -1
  289. package/dist/commands/mcp/client.js +0 -70
  290. package/dist/commands/mcp/client.js.map +0 -1
  291. package/dist/commands/mcp/complete.d.ts +0 -36
  292. package/dist/commands/mcp/complete.d.ts.map +0 -1
  293. package/dist/commands/mcp/complete.js +0 -66
  294. package/dist/commands/mcp/complete.js.map +0 -1
  295. package/dist/commands/mcp/import-discovery.d.ts +0 -25
  296. package/dist/commands/mcp/import-discovery.d.ts.map +0 -1
  297. package/dist/commands/mcp/import-discovery.js +0 -135
  298. package/dist/commands/mcp/import-discovery.js.map +0 -1
  299. package/dist/commands/mcp/import-validate.d.ts +0 -15
  300. package/dist/commands/mcp/import-validate.d.ts.map +0 -1
  301. package/dist/commands/mcp/import-validate.js +0 -55
  302. package/dist/commands/mcp/import-validate.js.map +0 -1
  303. package/dist/commands/mcp/import.d.ts +0 -12
  304. package/dist/commands/mcp/import.d.ts.map +0 -1
  305. package/dist/commands/mcp/import.js +0 -126
  306. package/dist/commands/mcp/import.js.map +0 -1
  307. package/dist/commands/mcp/index.d.ts +0 -14
  308. package/dist/commands/mcp/index.d.ts.map +0 -1
  309. package/dist/commands/mcp/index.js +0 -39
  310. package/dist/commands/mcp/index.js.map +0 -1
  311. package/dist/commands/mcp/install-shared.d.ts +0 -24
  312. package/dist/commands/mcp/install-shared.d.ts.map +0 -1
  313. package/dist/commands/mcp/install-shared.js +0 -42
  314. package/dist/commands/mcp/install-shared.js.map +0 -1
  315. package/dist/commands/mcp/install.d.ts +0 -20
  316. package/dist/commands/mcp/install.d.ts.map +0 -1
  317. package/dist/commands/mcp/install.js +0 -59
  318. package/dist/commands/mcp/install.js.map +0 -1
  319. package/dist/commands/mcp/list.d.ts +0 -6
  320. package/dist/commands/mcp/list.d.ts.map +0 -1
  321. package/dist/commands/mcp/list.js +0 -56
  322. package/dist/commands/mcp/list.js.map +0 -1
  323. package/dist/commands/mcp/login.d.ts +0 -6
  324. package/dist/commands/mcp/login.d.ts.map +0 -1
  325. package/dist/commands/mcp/login.js +0 -38
  326. package/dist/commands/mcp/login.js.map +0 -1
  327. package/dist/commands/mcp/remove.d.ts +0 -6
  328. package/dist/commands/mcp/remove.d.ts.map +0 -1
  329. package/dist/commands/mcp/remove.js +0 -21
  330. package/dist/commands/mcp/remove.js.map +0 -1
  331. package/dist/commands/mcp/revoke.d.ts +0 -11
  332. package/dist/commands/mcp/revoke.d.ts.map +0 -1
  333. package/dist/commands/mcp/revoke.js +0 -51
  334. package/dist/commands/mcp/revoke.js.map +0 -1
  335. package/dist/commands/mcp/serve.d.ts +0 -23
  336. package/dist/commands/mcp/serve.d.ts.map +0 -1
  337. package/dist/commands/mcp/serve.js +0 -55
  338. package/dist/commands/mcp/serve.js.map +0 -1
  339. package/dist/commands/mcp/status.d.ts +0 -6
  340. package/dist/commands/mcp/status.d.ts.map +0 -1
  341. package/dist/commands/mcp/status.js +0 -57
  342. package/dist/commands/mcp/status.js.map +0 -1
  343. package/dist/commands/mcp/uninstall.d.ts +0 -20
  344. package/dist/commands/mcp/uninstall.d.ts.map +0 -1
  345. package/dist/commands/mcp/uninstall.js +0 -60
  346. package/dist/commands/mcp/uninstall.js.map +0 -1
  347. package/dist/commands/memory/_paths.d.ts +0 -25
  348. package/dist/commands/memory/_paths.d.ts.map +0 -1
  349. package/dist/commands/memory/_paths.js +0 -57
  350. package/dist/commands/memory/_paths.js.map +0 -1
  351. package/dist/commands/memory/bridge.d.ts +0 -57
  352. package/dist/commands/memory/bridge.d.ts.map +0 -1
  353. package/dist/commands/memory/bridge.js +0 -152
  354. package/dist/commands/memory/bridge.js.map +0 -1
  355. package/dist/commands/memory/index.d.ts +0 -20
  356. package/dist/commands/memory/index.d.ts.map +0 -1
  357. package/dist/commands/memory/index.js +0 -47
  358. package/dist/commands/memory/index.js.map +0 -1
  359. package/dist/commands/memory/install-hooks.d.ts +0 -22
  360. package/dist/commands/memory/install-hooks.d.ts.map +0 -1
  361. package/dist/commands/memory/install-hooks.js +0 -156
  362. package/dist/commands/memory/install-hooks.js.map +0 -1
  363. package/dist/commands/memory/install.d.ts +0 -57
  364. package/dist/commands/memory/install.d.ts.map +0 -1
  365. package/dist/commands/memory/install.js +0 -114
  366. package/dist/commands/memory/install.js.map +0 -1
  367. package/dist/commands/memory/logs.d.ts +0 -19
  368. package/dist/commands/memory/logs.d.ts.map +0 -1
  369. package/dist/commands/memory/logs.js +0 -50
  370. package/dist/commands/memory/logs.js.map +0 -1
  371. package/dist/commands/memory/mode.d.ts +0 -47
  372. package/dist/commands/memory/mode.d.ts.map +0 -1
  373. package/dist/commands/memory/mode.js +0 -185
  374. package/dist/commands/memory/mode.js.map +0 -1
  375. package/dist/commands/memory/reclassify.d.ts +0 -56
  376. package/dist/commands/memory/reclassify.d.ts.map +0 -1
  377. package/dist/commands/memory/reclassify.js +0 -177
  378. package/dist/commands/memory/reclassify.js.map +0 -1
  379. package/dist/commands/memory/secret.d.ts +0 -16
  380. package/dist/commands/memory/secret.d.ts.map +0 -1
  381. package/dist/commands/memory/secret.js +0 -80
  382. package/dist/commands/memory/secret.js.map +0 -1
  383. package/dist/commands/memory/start.d.ts +0 -25
  384. package/dist/commands/memory/start.d.ts.map +0 -1
  385. package/dist/commands/memory/start.js +0 -83
  386. package/dist/commands/memory/start.js.map +0 -1
  387. package/dist/commands/memory/stats.d.ts +0 -69
  388. package/dist/commands/memory/stats.d.ts.map +0 -1
  389. package/dist/commands/memory/stats.js +0 -164
  390. package/dist/commands/memory/stats.js.map +0 -1
  391. package/dist/commands/memory/status.d.ts +0 -45
  392. package/dist/commands/memory/status.d.ts.map +0 -1
  393. package/dist/commands/memory/status.js +0 -134
  394. package/dist/commands/memory/status.js.map +0 -1
  395. package/dist/commands/memory/stop.d.ts +0 -13
  396. package/dist/commands/memory/stop.d.ts.map +0 -1
  397. package/dist/commands/memory/stop.js +0 -52
  398. package/dist/commands/memory/stop.js.map +0 -1
  399. package/dist/commands/memory/uninstall.d.ts +0 -19
  400. package/dist/commands/memory/uninstall.d.ts.map +0 -1
  401. package/dist/commands/memory/uninstall.js +0 -60
  402. package/dist/commands/memory/uninstall.js.map +0 -1
  403. package/dist/commands/memory-service-container.d.ts +0 -130
  404. package/dist/commands/memory-service-container.d.ts.map +0 -1
  405. package/dist/commands/memory-service-container.js +0 -251
  406. package/dist/commands/memory-service-container.js.map +0 -1
  407. package/dist/commands/observe.d.ts +0 -9
  408. package/dist/commands/observe.d.ts.map +0 -1
  409. package/dist/commands/observe.js +0 -42
  410. package/dist/commands/observe.js.map +0 -1
  411. package/dist/commands/plans-list-json.d.ts +0 -77
  412. package/dist/commands/plans-list-json.d.ts.map +0 -1
  413. package/dist/commands/plans-list-json.js +0 -61
  414. package/dist/commands/plans-list-json.js.map +0 -1
  415. package/dist/commands/plans.d.ts +0 -3
  416. package/dist/commands/plans.d.ts.map +0 -1
  417. package/dist/commands/plans.js +0 -221
  418. package/dist/commands/plans.js.map +0 -1
  419. package/dist/commands/policy-check.d.ts +0 -14
  420. package/dist/commands/policy-check.d.ts.map +0 -1
  421. package/dist/commands/policy-check.js +0 -76
  422. package/dist/commands/policy-check.js.map +0 -1
  423. package/dist/commands/pr.d.ts +0 -17
  424. package/dist/commands/pr.d.ts.map +0 -1
  425. package/dist/commands/pr.js +0 -148
  426. package/dist/commands/pr.js.map +0 -1
  427. package/dist/commands/ps.d.ts +0 -57
  428. package/dist/commands/ps.d.ts.map +0 -1
  429. package/dist/commands/ps.js +0 -202
  430. package/dist/commands/ps.js.map +0 -1
  431. package/dist/commands/refresh-helpers.d.ts +0 -25
  432. package/dist/commands/refresh-helpers.d.ts.map +0 -1
  433. package/dist/commands/refresh-helpers.js +0 -56
  434. package/dist/commands/refresh-helpers.js.map +0 -1
  435. package/dist/commands/refresh.d.ts +0 -23
  436. package/dist/commands/refresh.d.ts.map +0 -1
  437. package/dist/commands/refresh.js +0 -237
  438. package/dist/commands/refresh.js.map +0 -1
  439. package/dist/commands/rekey.d.ts +0 -84
  440. package/dist/commands/rekey.d.ts.map +0 -1
  441. package/dist/commands/rekey.js +0 -209
  442. package/dist/commands/rekey.js.map +0 -1
  443. package/dist/commands/repos-list-json.d.ts +0 -58
  444. package/dist/commands/repos-list-json.d.ts.map +0 -1
  445. package/dist/commands/repos-list-json.js +0 -45
  446. package/dist/commands/repos-list-json.js.map +0 -1
  447. package/dist/commands/repos.d.ts +0 -11
  448. package/dist/commands/repos.d.ts.map +0 -1
  449. package/dist/commands/repos.js +0 -102
  450. package/dist/commands/repos.js.map +0 -1
  451. package/dist/commands/restart.d.ts +0 -18
  452. package/dist/commands/restart.d.ts.map +0 -1
  453. package/dist/commands/restart.js +0 -113
  454. package/dist/commands/restart.js.map +0 -1
  455. package/dist/commands/resume.d.ts +0 -63
  456. package/dist/commands/resume.d.ts.map +0 -1
  457. package/dist/commands/resume.js +0 -174
  458. package/dist/commands/resume.js.map +0 -1
  459. package/dist/commands/runbooks.d.ts +0 -45
  460. package/dist/commands/runbooks.d.ts.map +0 -1
  461. package/dist/commands/runbooks.js +0 -313
  462. package/dist/commands/runbooks.js.map +0 -1
  463. package/dist/commands/seed.d.ts +0 -27
  464. package/dist/commands/seed.d.ts.map +0 -1
  465. package/dist/commands/seed.js +0 -303
  466. package/dist/commands/seed.js.map +0 -1
  467. package/dist/commands/services-tls.d.ts +0 -120
  468. package/dist/commands/services-tls.d.ts.map +0 -1
  469. package/dist/commands/services-tls.js +0 -489
  470. package/dist/commands/services-tls.js.map +0 -1
  471. package/dist/commands/services.d.ts +0 -218
  472. package/dist/commands/services.d.ts.map +0 -1
  473. package/dist/commands/services.js +0 -830
  474. package/dist/commands/services.js.map +0 -1
  475. package/dist/commands/setup-linux-gate.d.ts +0 -26
  476. package/dist/commands/setup-linux-gate.d.ts.map +0 -1
  477. package/dist/commands/setup-linux-gate.js +0 -40
  478. package/dist/commands/setup-linux-gate.js.map +0 -1
  479. package/dist/commands/setup-metrics.d.ts +0 -26
  480. package/dist/commands/setup-metrics.d.ts.map +0 -1
  481. package/dist/commands/setup-metrics.js +0 -56
  482. package/dist/commands/setup-metrics.js.map +0 -1
  483. package/dist/commands/setup-phase-5a-skill-source.d.ts +0 -84
  484. package/dist/commands/setup-phase-5a-skill-source.d.ts.map +0 -1
  485. package/dist/commands/setup-phase-5a-skill-source.js +0 -259
  486. package/dist/commands/setup-phase-5a-skill-source.js.map +0 -1
  487. package/dist/commands/setup-phase-5b-project-sweep.d.ts +0 -38
  488. package/dist/commands/setup-phase-5b-project-sweep.d.ts.map +0 -1
  489. package/dist/commands/setup-phase-5b-project-sweep.js +0 -175
  490. package/dist/commands/setup-phase-5b-project-sweep.js.map +0 -1
  491. package/dist/commands/setup-phase-8-kg-hook.d.ts +0 -48
  492. package/dist/commands/setup-phase-8-kg-hook.d.ts.map +0 -1
  493. package/dist/commands/setup-phase-8-kg-hook.js +0 -93
  494. package/dist/commands/setup-phase-8-kg-hook.js.map +0 -1
  495. package/dist/commands/setup-phase-9-memory-bridge.d.ts +0 -36
  496. package/dist/commands/setup-phase-9-memory-bridge.d.ts.map +0 -1
  497. package/dist/commands/setup-phase-9-memory-bridge.js +0 -59
  498. package/dist/commands/setup-phase-9-memory-bridge.js.map +0 -1
  499. package/dist/commands/setup.d.ts +0 -231
  500. package/dist/commands/setup.d.ts.map +0 -1
  501. package/dist/commands/setup.js +0 -1374
  502. package/dist/commands/setup.js.map +0 -1
  503. package/dist/commands/skills-100x.d.ts +0 -34
  504. package/dist/commands/skills-100x.d.ts.map +0 -1
  505. package/dist/commands/skills-100x.js +0 -405
  506. package/dist/commands/skills-100x.js.map +0 -1
  507. package/dist/commands/skills-doctor.d.ts +0 -14
  508. package/dist/commands/skills-doctor.d.ts.map +0 -1
  509. package/dist/commands/skills-doctor.js +0 -126
  510. package/dist/commands/skills-doctor.js.map +0 -1
  511. package/dist/commands/skills-hook.d.ts +0 -19
  512. package/dist/commands/skills-hook.d.ts.map +0 -1
  513. package/dist/commands/skills-hook.js +0 -99
  514. package/dist/commands/skills-hook.js.map +0 -1
  515. package/dist/commands/skills-install-model-router.d.ts +0 -20
  516. package/dist/commands/skills-install-model-router.d.ts.map +0 -1
  517. package/dist/commands/skills-install-model-router.js +0 -55
  518. package/dist/commands/skills-install-model-router.js.map +0 -1
  519. package/dist/commands/skills-migrate-back.d.ts +0 -21
  520. package/dist/commands/skills-migrate-back.d.ts.map +0 -1
  521. package/dist/commands/skills-migrate-back.js +0 -222
  522. package/dist/commands/skills-migrate-back.js.map +0 -1
  523. package/dist/commands/skills-migrate-hooks-back.d.ts +0 -19
  524. package/dist/commands/skills-migrate-hooks-back.d.ts.map +0 -1
  525. package/dist/commands/skills-migrate-hooks-back.js +0 -83
  526. package/dist/commands/skills-migrate-hooks-back.js.map +0 -1
  527. package/dist/commands/skills-migrate-hooks.d.ts +0 -40
  528. package/dist/commands/skills-migrate-hooks.d.ts.map +0 -1
  529. package/dist/commands/skills-migrate-hooks.js +0 -178
  530. package/dist/commands/skills-migrate-hooks.js.map +0 -1
  531. package/dist/commands/skills-migrate.d.ts +0 -33
  532. package/dist/commands/skills-migrate.d.ts.map +0 -1
  533. package/dist/commands/skills-migrate.js +0 -216
  534. package/dist/commands/skills-migrate.js.map +0 -1
  535. package/dist/commands/skills-onboard.d.ts +0 -26
  536. package/dist/commands/skills-onboard.d.ts.map +0 -1
  537. package/dist/commands/skills-onboard.js +0 -230
  538. package/dist/commands/skills-onboard.js.map +0 -1
  539. package/dist/commands/skills-shadow-backups.d.ts +0 -15
  540. package/dist/commands/skills-shadow-backups.d.ts.map +0 -1
  541. package/dist/commands/skills-shadow-backups.js +0 -132
  542. package/dist/commands/skills-shadow-backups.js.map +0 -1
  543. package/dist/commands/skills-source.d.ts +0 -61
  544. package/dist/commands/skills-source.d.ts.map +0 -1
  545. package/dist/commands/skills-source.js +0 -895
  546. package/dist/commands/skills-source.js.map +0 -1
  547. package/dist/commands/skills.d.ts +0 -59
  548. package/dist/commands/skills.d.ts.map +0 -1
  549. package/dist/commands/skills.js +0 -461
  550. package/dist/commands/skills.js.map +0 -1
  551. package/dist/commands/status.d.ts +0 -65
  552. package/dist/commands/status.d.ts.map +0 -1
  553. package/dist/commands/status.js +0 -249
  554. package/dist/commands/status.js.map +0 -1
  555. package/dist/commands/stop.d.ts +0 -10
  556. package/dist/commands/stop.d.ts.map +0 -1
  557. package/dist/commands/stop.js +0 -17
  558. package/dist/commands/stop.js.map +0 -1
  559. package/dist/commands/substrate-audit-log.d.ts +0 -51
  560. package/dist/commands/substrate-audit-log.d.ts.map +0 -1
  561. package/dist/commands/substrate-audit-log.js +0 -161
  562. package/dist/commands/substrate-audit-log.js.map +0 -1
  563. package/dist/commands/substrate.d.ts +0 -87
  564. package/dist/commands/substrate.d.ts.map +0 -1
  565. package/dist/commands/substrate.js +0 -194
  566. package/dist/commands/substrate.js.map +0 -1
  567. package/dist/commands/update.d.ts +0 -89
  568. package/dist/commands/update.d.ts.map +0 -1
  569. package/dist/commands/update.js +0 -331
  570. package/dist/commands/update.js.map +0 -1
  571. package/dist/commands/upgrade-history.d.ts +0 -15
  572. package/dist/commands/upgrade-history.d.ts.map +0 -1
  573. package/dist/commands/upgrade-history.js +0 -34
  574. package/dist/commands/upgrade-history.js.map +0 -1
  575. package/dist/commands/upgrade-lock.d.ts +0 -93
  576. package/dist/commands/upgrade-lock.d.ts.map +0 -1
  577. package/dist/commands/upgrade-lock.js +0 -225
  578. package/dist/commands/upgrade-lock.js.map +0 -1
  579. package/dist/commands/upgrade-log.d.ts +0 -86
  580. package/dist/commands/upgrade-log.d.ts.map +0 -1
  581. package/dist/commands/upgrade-log.js +0 -146
  582. package/dist/commands/upgrade-log.js.map +0 -1
  583. package/dist/commands/upgrade.d.ts +0 -445
  584. package/dist/commands/upgrade.d.ts.map +0 -1
  585. package/dist/commands/upgrade.js +0 -1718
  586. package/dist/commands/upgrade.js.map +0 -1
  587. package/dist/commands/workspace-list-json.d.ts +0 -73
  588. package/dist/commands/workspace-list-json.d.ts.map +0 -1
  589. package/dist/commands/workspace-list-json.js +0 -59
  590. package/dist/commands/workspace-list-json.js.map +0 -1
  591. package/dist/commands/workspace.d.ts +0 -23
  592. package/dist/commands/workspace.d.ts.map +0 -1
  593. package/dist/commands/workspace.js +0 -203
  594. package/dist/commands/workspace.js.map +0 -1
  595. package/dist/commands/world-snapshot.d.ts +0 -32
  596. package/dist/commands/world-snapshot.d.ts.map +0 -1
  597. package/dist/commands/world-snapshot.js +0 -531
  598. package/dist/commands/world-snapshot.js.map +0 -1
  599. package/dist/commands/world-upgrade.d.ts +0 -33
  600. package/dist/commands/world-upgrade.d.ts.map +0 -1
  601. package/dist/commands/world-upgrade.js +0 -82
  602. package/dist/commands/world-upgrade.js.map +0 -1
  603. package/dist/commands/world.d.ts +0 -12
  604. package/dist/commands/world.d.ts.map +0 -1
  605. package/dist/commands/world.js +0 -18
  606. package/dist/commands/world.js.map +0 -1
  607. package/dist/commands/worldspec/compile.d.ts +0 -20
  608. package/dist/commands/worldspec/compile.d.ts.map +0 -1
  609. package/dist/commands/worldspec/compile.js +0 -130
  610. package/dist/commands/worldspec/compile.js.map +0 -1
  611. package/dist/commands/worldspec/index.d.ts +0 -12
  612. package/dist/commands/worldspec/index.d.ts.map +0 -1
  613. package/dist/commands/worldspec/index.js +0 -23
  614. package/dist/commands/worldspec/index.js.map +0 -1
  615. package/dist/commands/worldspec/init.d.ts +0 -15
  616. package/dist/commands/worldspec/init.d.ts.map +0 -1
  617. package/dist/commands/worldspec/init.js +0 -166
  618. package/dist/commands/worldspec/init.js.map +0 -1
  619. package/dist/commands/worldspec/schema.d.ts +0 -11
  620. package/dist/commands/worldspec/schema.d.ts.map +0 -1
  621. package/dist/commands/worldspec/schema.js +0 -55
  622. package/dist/commands/worldspec/schema.js.map +0 -1
  623. package/dist/commands/worldspec/validate.d.ts +0 -15
  624. package/dist/commands/worldspec/validate.d.ts.map +0 -1
  625. package/dist/commands/worldspec/validate.js +0 -66
  626. package/dist/commands/worldspec/validate.js.map +0 -1
  627. package/dist/commands/yolo.d.ts +0 -95
  628. package/dist/commands/yolo.d.ts.map +0 -1
  629. package/dist/commands/yolo.js +0 -377
  630. package/dist/commands/yolo.js.map +0 -1
  631. package/dist/context.d.ts +0 -30
  632. package/dist/context.d.ts.map +0 -1
  633. package/dist/context.js +0 -56
  634. package/dist/context.js.map +0 -1
  635. package/dist/docker-host.d.ts +0 -18
  636. package/dist/docker-host.d.ts.map +0 -1
  637. package/dist/docker-host.js +0 -17
  638. package/dist/docker-host.js.map +0 -1
  639. package/dist/exit-codes.d.ts +0 -67
  640. package/dist/exit-codes.d.ts.map +0 -1
  641. package/dist/exit-codes.js +0 -67
  642. package/dist/exit-codes.js.map +0 -1
  643. package/dist/from-manifest.d.ts +0 -53
  644. package/dist/from-manifest.d.ts.map +0 -1
  645. package/dist/from-manifest.js +0 -95
  646. package/dist/from-manifest.js.map +0 -1
  647. package/dist/image-presence.d.ts +0 -40
  648. package/dist/image-presence.d.ts.map +0 -1
  649. package/dist/image-presence.js +0 -39
  650. package/dist/image-presence.js.map +0 -1
  651. package/dist/index.d.ts +0 -9
  652. package/dist/index.d.ts.map +0 -1
  653. package/dist/install-root.d.ts +0 -74
  654. package/dist/install-root.d.ts.map +0 -1
  655. package/dist/install-root.js +0 -98
  656. package/dist/install-root.js.map +0 -1
  657. package/dist/lib/anthropic-base-url-file.d.ts +0 -37
  658. package/dist/lib/anthropic-base-url-file.d.ts.map +0 -1
  659. package/dist/lib/anthropic-base-url-file.js +0 -46
  660. package/dist/lib/anthropic-base-url-file.js.map +0 -1
  661. package/dist/lib/auth-backend.d.ts +0 -168
  662. package/dist/lib/auth-backend.d.ts.map +0 -1
  663. package/dist/lib/auth-backend.js +0 -172
  664. package/dist/lib/auth-backend.js.map +0 -1
  665. package/dist/lib/auth-list-cache.d.ts +0 -67
  666. package/dist/lib/auth-list-cache.d.ts.map +0 -1
  667. package/dist/lib/auth-list-cache.js +0 -84
  668. package/dist/lib/auth-list-cache.js.map +0 -1
  669. package/dist/lib/auth-list.d.ts +0 -107
  670. package/dist/lib/auth-list.d.ts.map +0 -1
  671. package/dist/lib/auth-list.js +0 -123
  672. package/dist/lib/auth-list.js.map +0 -1
  673. package/dist/lib/auth-login.d.ts +0 -92
  674. package/dist/lib/auth-login.d.ts.map +0 -1
  675. package/dist/lib/auth-login.js +0 -124
  676. package/dist/lib/auth-login.js.map +0 -1
  677. package/dist/lib/auth-mutator-backend.d.ts +0 -54
  678. package/dist/lib/auth-mutator-backend.d.ts.map +0 -1
  679. package/dist/lib/auth-mutator-backend.js +0 -62
  680. package/dist/lib/auth-mutator-backend.js.map +0 -1
  681. package/dist/lib/auth-refresh-kubernetes.d.ts +0 -65
  682. package/dist/lib/auth-refresh-kubernetes.d.ts.map +0 -1
  683. package/dist/lib/auth-refresh-kubernetes.js +0 -125
  684. package/dist/lib/auth-refresh-kubernetes.js.map +0 -1
  685. package/dist/lib/auth-remote.d.ts +0 -172
  686. package/dist/lib/auth-remote.d.ts.map +0 -1
  687. package/dist/lib/auth-remote.js +0 -394
  688. package/dist/lib/auth-remote.js.map +0 -1
  689. package/dist/lib/bootstrap-kubernetes.d.ts +0 -164
  690. package/dist/lib/bootstrap-kubernetes.d.ts.map +0 -1
  691. package/dist/lib/bootstrap-kubernetes.js +0 -1002
  692. package/dist/lib/bootstrap-kubernetes.js.map +0 -1
  693. package/dist/lib/build-if-stale.d.ts +0 -33
  694. package/dist/lib/build-if-stale.d.ts.map +0 -1
  695. package/dist/lib/build-if-stale.js +0 -156
  696. package/dist/lib/build-if-stale.js.map +0 -1
  697. package/dist/lib/bundle-freshness.d.ts +0 -57
  698. package/dist/lib/bundle-freshness.d.ts.map +0 -1
  699. package/dist/lib/bundle-freshness.js +0 -223
  700. package/dist/lib/bundle-freshness.js.map +0 -1
  701. package/dist/lib/bundle-source.d.ts +0 -52
  702. package/dist/lib/bundle-source.d.ts.map +0 -1
  703. package/dist/lib/bundle-source.js +0 -83
  704. package/dist/lib/bundle-source.js.map +0 -1
  705. package/dist/lib/cf-access-token.d.ts +0 -32
  706. package/dist/lib/cf-access-token.d.ts.map +0 -1
  707. package/dist/lib/cf-access-token.js +0 -51
  708. package/dist/lib/cf-access-token.js.map +0 -1
  709. package/dist/lib/completion-generator.d.ts +0 -107
  710. package/dist/lib/completion-generator.d.ts.map +0 -1
  711. package/dist/lib/completion-generator.js +0 -226
  712. package/dist/lib/completion-generator.js.map +0 -1
  713. package/dist/lib/config.d.ts +0 -114
  714. package/dist/lib/config.d.ts.map +0 -1
  715. package/dist/lib/config.js +0 -246
  716. package/dist/lib/config.js.map +0 -1
  717. package/dist/lib/flywheel-probes.d.ts +0 -58
  718. package/dist/lib/flywheel-probes.d.ts.map +0 -1
  719. package/dist/lib/flywheel-probes.js +0 -163
  720. package/dist/lib/flywheel-probes.js.map +0 -1
  721. package/dist/lib/health-probes.d.ts +0 -267
  722. package/dist/lib/health-probes.d.ts.map +0 -1
  723. package/dist/lib/health-probes.js +0 -933
  724. package/dist/lib/health-probes.js.map +0 -1
  725. package/dist/lib/help-groups.d.ts +0 -36
  726. package/dist/lib/help-groups.d.ts.map +0 -1
  727. package/dist/lib/help-groups.js +0 -124
  728. package/dist/lib/help-groups.js.map +0 -1
  729. package/dist/lib/host-side-proxy.d.ts +0 -67
  730. package/dist/lib/host-side-proxy.d.ts.map +0 -1
  731. package/dist/lib/host-side-proxy.js +0 -177
  732. package/dist/lib/host-side-proxy.js.map +0 -1
  733. package/dist/lib/instrumentation.d.ts +0 -85
  734. package/dist/lib/instrumentation.d.ts.map +0 -1
  735. package/dist/lib/instrumentation.js +0 -104
  736. package/dist/lib/instrumentation.js.map +0 -1
  737. package/dist/lib/k8s-bootstrap.d.ts +0 -126
  738. package/dist/lib/k8s-bootstrap.d.ts.map +0 -1
  739. package/dist/lib/k8s-bootstrap.js +0 -218
  740. package/dist/lib/k8s-bootstrap.js.map +0 -1
  741. package/dist/lib/k8s-context-discovery.d.ts +0 -80
  742. package/dist/lib/k8s-context-discovery.d.ts.map +0 -1
  743. package/dist/lib/k8s-context-discovery.js +0 -102
  744. package/dist/lib/k8s-context-discovery.js.map +0 -1
  745. package/dist/lib/k8s-secret-render.d.ts +0 -141
  746. package/dist/lib/k8s-secret-render.d.ts.map +0 -1
  747. package/dist/lib/k8s-secret-render.js +0 -318
  748. package/dist/lib/k8s-secret-render.js.map +0 -1
  749. package/dist/lib/kg-caps.d.ts +0 -19
  750. package/dist/lib/kg-caps.d.ts.map +0 -1
  751. package/dist/lib/kg-caps.js +0 -19
  752. package/dist/lib/kg-caps.js.map +0 -1
  753. package/dist/lib/kubectl-context.d.ts +0 -87
  754. package/dist/lib/kubectl-context.d.ts.map +0 -1
  755. package/dist/lib/kubectl-context.js +0 -105
  756. package/dist/lib/kubectl-context.js.map +0 -1
  757. package/dist/lib/kubectl-wrap.d.ts +0 -65
  758. package/dist/lib/kubectl-wrap.d.ts.map +0 -1
  759. package/dist/lib/kubectl-wrap.js +0 -135
  760. package/dist/lib/kubectl-wrap.js.map +0 -1
  761. package/dist/lib/manifest-refresh.d.ts +0 -136
  762. package/dist/lib/manifest-refresh.d.ts.map +0 -1
  763. package/dist/lib/manifest-refresh.js +0 -298
  764. package/dist/lib/manifest-refresh.js.map +0 -1
  765. package/dist/lib/memory-host-process-migration.d.ts +0 -56
  766. package/dist/lib/memory-host-process-migration.d.ts.map +0 -1
  767. package/dist/lib/memory-host-process-migration.js +0 -156
  768. package/dist/lib/memory-host-process-migration.js.map +0 -1
  769. package/dist/lib/memory-secret.d.ts +0 -83
  770. package/dist/lib/memory-secret.d.ts.map +0 -1
  771. package/dist/lib/memory-secret.js +0 -147
  772. package/dist/lib/memory-secret.js.map +0 -1
  773. package/dist/lib/peripheral-registry.d.ts +0 -53
  774. package/dist/lib/peripheral-registry.d.ts.map +0 -1
  775. package/dist/lib/peripheral-registry.js +0 -73
  776. package/dist/lib/peripheral-registry.js.map +0 -1
  777. package/dist/lib/plans-client.d.ts +0 -69
  778. package/dist/lib/plans-client.d.ts.map +0 -1
  779. package/dist/lib/plans-client.js +0 -140
  780. package/dist/lib/plans-client.js.map +0 -1
  781. package/dist/lib/port-forward.d.ts +0 -168
  782. package/dist/lib/port-forward.d.ts.map +0 -1
  783. package/dist/lib/port-forward.js +0 -393
  784. package/dist/lib/port-forward.js.map +0 -1
  785. package/dist/lib/shell-rc.d.ts +0 -90
  786. package/dist/lib/shell-rc.d.ts.map +0 -1
  787. package/dist/lib/shell-rc.js +0 -91
  788. package/dist/lib/shell-rc.js.map +0 -1
  789. package/dist/lib/shim-generator.d.ts +0 -51
  790. package/dist/lib/shim-generator.d.ts.map +0 -1
  791. package/dist/lib/shim-generator.js +0 -88
  792. package/dist/lib/shim-generator.js.map +0 -1
  793. package/dist/lib/skills-apply-overlays.d.ts +0 -35
  794. package/dist/lib/skills-apply-overlays.d.ts.map +0 -1
  795. package/dist/lib/skills-apply-overlays.js +0 -243
  796. package/dist/lib/skills-apply-overlays.js.map +0 -1
  797. package/dist/lib/symlink-reconcile.d.ts +0 -32
  798. package/dist/lib/symlink-reconcile.d.ts.map +0 -1
  799. package/dist/lib/symlink-reconcile.js +0 -80
  800. package/dist/lib/symlink-reconcile.js.map +0 -1
  801. package/dist/lib/upgrade-check.d.ts +0 -60
  802. package/dist/lib/upgrade-check.d.ts.map +0 -1
  803. package/dist/lib/upgrade-check.js +0 -169
  804. package/dist/lib/upgrade-check.js.map +0 -1
  805. package/dist/lib/upgrade-kubernetes.d.ts +0 -193
  806. package/dist/lib/upgrade-kubernetes.d.ts.map +0 -1
  807. package/dist/lib/upgrade-kubernetes.js +0 -1014
  808. package/dist/lib/upgrade-kubernetes.js.map +0 -1
  809. package/dist/lib/world-mcp-register.d.ts +0 -98
  810. package/dist/lib/world-mcp-register.d.ts.map +0 -1
  811. package/dist/lib/world-mcp-register.js +0 -117
  812. package/dist/lib/world-mcp-register.js.map +0 -1
  813. package/dist/output.d.ts +0 -10
  814. package/dist/output.d.ts.map +0 -1
  815. package/dist/output.js +0 -31
  816. package/dist/output.js.map +0 -1
  817. package/dist/pleri-config.d.ts +0 -22
  818. package/dist/pleri-config.d.ts.map +0 -1
  819. package/dist/pleri-config.js +0 -42
  820. package/dist/pleri-config.js.map +0 -1
  821. package/dist/protocol-version.d.ts +0 -79
  822. package/dist/protocol-version.d.ts.map +0 -1
  823. package/dist/protocol-version.js +0 -133
  824. package/dist/protocol-version.js.map +0 -1
  825. package/dist/registry-allowlist.d.ts +0 -47
  826. package/dist/registry-allowlist.d.ts.map +0 -1
  827. package/dist/registry-allowlist.js +0 -67
  828. package/dist/registry-allowlist.js.map +0 -1
  829. package/dist/spawn/home-override.d.ts +0 -82
  830. package/dist/spawn/home-override.d.ts.map +0 -1
  831. package/dist/spawn/home-override.js +0 -107
  832. package/dist/spawn/home-override.js.map +0 -1
  833. package/host-cp/src/linear-sync.mjs +0 -43
@@ -1,1718 +0,0 @@
1
- /**
2
- * `olam upgrade` — self-upgrade the local Olam dev stack.
3
- *
4
- * Runs the full manual sequence in one command:
5
- * git fetch + git pull --ff-only
6
- * npm install
7
- * npm run build
8
- * vite build (SPA)
9
- * bash build-host-cp.sh (image)
10
- * docker compose up -d --force-recreate
11
- * wait for /health
12
- */
13
- import * as fs from 'node:fs';
14
- import * as path from 'node:path';
15
- import { spawnSync } from 'node:child_process';
16
- import ora from 'ora';
17
- import pc from 'picocolors';
18
- import { printError, printSuccess, printInfo, printWarning, printHeader } from '../output.js';
19
- import { buildComposeEnv, captureGhToken, findComposeFile, readAuthSecret, runCompose } from './host-cp.js';
20
- import { acquireLock, releaseLock, formatRefusalMessage, LOCK_FILE_PATH } from './upgrade-lock.js';
21
- import { appendUpgradeLog } from './upgrade-log.js';
22
- import { handleHistory, parseHistoryOpts } from './upgrade-history.js';
23
- import { AuthContainerController } from '@olam/core/src/auth/index.js';
24
- import { loadImageDigests, pullImageWithRetry, realDocker, } from './bootstrap.js';
25
- import { EXIT_BOOTSTRAP_PULL_FAILED, EXIT_GENERIC_ERROR, EXIT_PROTOCOL_MISMATCH, } from '../exit-codes.js';
26
- import { checkProtocolOverlap, parseProtocolVersionsLabel } from '../protocol-version.js';
27
- import { isDevMode, MissingBuildScriptError } from '../install-root.js';
28
- import { readConfig } from '../lib/config.js';
29
- import { runUpgradeKubernetes } from '../lib/upgrade-kubernetes.js';
30
- const AUTH_HEALTH_URL = 'http://127.0.0.1:9999/health';
31
- /**
32
- * Check whether node_modules is in sync with package-lock.json.
33
- *
34
- * npm writes node_modules/.package-lock.json after every successful install.
35
- * If its mtime is >= package-lock.json's mtime, the last install ran after
36
- * the last lockfile change — node_modules is current.
37
- *
38
- * Returns true → skip install (node_modules reflects the current lockfile).
39
- * Returns false → run install (lockfile changed or marker absent).
40
- */
41
- export function isNodeModulesInSync(cwd) {
42
- const lockPath = path.join(cwd, 'package-lock.json');
43
- const markerPath = path.join(cwd, 'node_modules', '.package-lock.json');
44
- if (!fs.existsSync(lockPath) || !fs.existsSync(markerPath))
45
- return false;
46
- try {
47
- const lockStat = fs.statSync(lockPath);
48
- const markerStat = fs.statSync(markerPath);
49
- return markerStat.mtimeMs >= lockStat.mtimeMs;
50
- }
51
- catch {
52
- return false;
53
- }
54
- }
55
- /**
56
- * Decide whether the npm install step should be skipped.
57
- *
58
- * Priority:
59
- * 1. --skip-install flag → always skip
60
- * 2. node_modules in sync with lockfile → skip (log reason)
61
- * 3. otherwise → run install
62
- */
63
- export function shouldSkipInstall(opts, cwd) {
64
- if (opts.skipInstall) {
65
- return { skip: true, reason: '--skip-install flag set' };
66
- }
67
- if (isNodeModulesInSync(cwd)) {
68
- return { skip: true, reason: 'node_modules in sync with package-lock.json' };
69
- }
70
- return { skip: false };
71
- }
72
- /** Check cwd looks like the olam repo root. */
73
- export function validateRepoRoot(cwd) {
74
- const marker = path.join(cwd, 'packages/host-cp/compose.yaml');
75
- if (!fs.existsSync(marker)) {
76
- return {
77
- ok: false,
78
- error: `Not an olam repo root (expected ${marker}).\n` +
79
- 'Run `olam upgrade` from the root of your olam checkout.',
80
- };
81
- }
82
- return { ok: true };
83
- }
84
- /** Normalise raw Commander option object into typed opts. */
85
- export function parseUpgradeOpts(raw) {
86
- const rawN = raw.n;
87
- const historyN = typeof rawN === 'number'
88
- ? rawN
89
- : typeof rawN === 'string'
90
- ? Number.parseInt(rawN, 10)
91
- : 10;
92
- return {
93
- yes: raw.yes === true,
94
- skipImage: raw.skipImage === true,
95
- skipInstall: raw.skipInstall === true,
96
- branch: raw.branch ?? null,
97
- rollback: raw.rollback === true,
98
- force: raw.force === true,
99
- noCache: raw.noCache === true,
100
- history: raw.history === true,
101
- historyN: Number.isFinite(historyN) && historyN > 0 ? historyN : 10,
102
- historyJson: raw.json === true,
103
- fromSource: raw.fromSource === true,
104
- forceRefreshManifests: raw.forceRefreshManifests === true,
105
- acceptSecurityRegression: raw.acceptSecurityRegression === true,
106
- };
107
- }
108
- /**
109
- * Extract the JS bundle hash from index.html.
110
- * Looks for the canonical Vite output pattern: `/assets/index-<hash>.js`.
111
- */
112
- export function extractBundleHash(indexHtml) {
113
- const match = indexHtml.match(/\/assets\/index-([A-Za-z0-9_-]+)\.js/);
114
- return match ? (match[1] ?? null) : null;
115
- }
116
- function runStep(label, cmd, args, opts = {}) {
117
- const start = Date.now();
118
- process.stdout.write(` ${pc.dim(label.padEnd(34))}`);
119
- const result = spawnSync(cmd, [...args], {
120
- encoding: 'utf-8',
121
- stdio: ['ignore', 'pipe', 'pipe'],
122
- cwd: opts.cwd ?? process.cwd(),
123
- env: { ...process.env, ...(opts.env ?? {}) },
124
- });
125
- const durationMs = Date.now() - start;
126
- const ok = result.status === 0 && result.error === undefined;
127
- const dur = `${(durationMs / 1000).toFixed(1)}s`;
128
- process.stdout.write(`${ok ? pc.green('✓') : pc.red('✗')} ${dur}\n`);
129
- return {
130
- ok,
131
- stdout: result.stdout ?? '',
132
- stderr: result.stderr ?? '',
133
- durationMs,
134
- };
135
- }
136
- function isGitDirty(cwd) {
137
- const result = spawnSync('git', ['status', '--porcelain'], {
138
- encoding: 'utf-8',
139
- stdio: ['ignore', 'pipe', 'pipe'],
140
- cwd,
141
- });
142
- return (result.stdout ?? '').trim().length > 0;
143
- }
144
- function hasGitUpstream(cwd) {
145
- const result = spawnSync('git', ['rev-parse', '--abbrev-ref', '@{u}'], {
146
- encoding: 'utf-8',
147
- stdio: ['ignore', 'pipe', 'pipe'],
148
- cwd,
149
- });
150
- return result.status === 0;
151
- }
152
- /**
153
- * Capture HEAD SHA via `git rev-parse HEAD`. Returns null on failure.
154
- *
155
- * Phase 2a — A2: must be invoked AFTER `git pull --ff-only` so the captured
156
- * SHA reflects the state we're upgrading TO (not the pre-pull state). The
157
- * pull's whole purpose is to advance HEAD; capturing before would refuse the
158
- * CLI's own pull as drift at A6's swap-boundary check.
159
- *
160
- * The returned SHA is sticky for the rest of the run (no per-step re-reads);
161
- * A6 / B4 re-read once at the swap boundary to detect operator-driven mid-flight
162
- * `git checkout` / `git reset` that happen DURING the build window.
163
- */
164
- export function captureHeadSha(cwd) {
165
- const result = spawnSync('git', ['rev-parse', 'HEAD'], {
166
- encoding: 'utf-8',
167
- stdio: ['ignore', 'pipe', 'pipe'],
168
- cwd,
169
- });
170
- if (result.status !== 0)
171
- return null;
172
- const sha = (result.stdout ?? '').trim();
173
- // git rev-parse HEAD returns 40-char lowercase hex; defensive validation.
174
- if (!/^[0-9a-f]{40}$/.test(sha))
175
- return null;
176
- return sha;
177
- }
178
- /** Abbreviate a 40-char SHA to 8 chars for human-readable output. */
179
- export function abbreviateSha(sha) {
180
- return sha.slice(0, 8);
181
- }
182
- /**
183
- * Check whether a docker image tag exists locally (Phase 2b — B1).
184
- *
185
- * Uses `docker image inspect` which exits 0 only when ALL specified
186
- * images exist locally. Single-image variant for the rollback pre-flight.
187
- */
188
- export function imageExists(tag) {
189
- try {
190
- const result = spawnSync('docker', ['image', 'inspect', '--format', '{{.Id}}', tag], {
191
- encoding: 'utf-8',
192
- stdio: ['ignore', 'pipe', 'ignore'],
193
- });
194
- return result.status === 0;
195
- }
196
- catch {
197
- return false;
198
- }
199
- }
200
- /**
201
- * Pre-flight check for `olam upgrade --rollback` (Phase 2b — B1).
202
- *
203
- * Verifies that all three `:olam-rollback` tags exist. Returns an error
204
- * message naming the missing image(s) when any are absent — typically
205
- * the first-upgrade case where no prior canonical existed for one or
206
- * more components, leaving the rollback set incoherent (see audit A6-001).
207
- *
208
- * Returns null when all three are present (rollback is safe to proceed).
209
- */
210
- export function checkRollbackSetExists(plan) {
211
- const missing = plan.filter((p) => !imageExists(p.rollback)).map((p) => p.rollback);
212
- if (missing.length === 0)
213
- return null;
214
- return missing.join(', ');
215
- }
216
- /**
217
- * Run docker create + docker inspect for a single image.
218
- *
219
- * Returns ok=true when:
220
- * - `docker create <image>` exits 0 (image manifest valid, layers downloadable).
221
- * - `docker inspect <image> --format '{{index .Config.Labels "olam.build.sha"}}'`
222
- * returns the expected `targetSha`.
223
- *
224
- * The container created by `docker create` is removed via `docker rm` even on
225
- * failure paths (best-effort cleanup; orphans are harmless and pruned by the
226
- * daemon's GC eventually).
227
- */
228
- // Cap each docker shellout. The smoke path expects the image to already
229
- // be present locally (post-pull), so docker create / inspect / rm are
230
- // sub-second on the happy path. The cap protects two real-world hangs:
231
- // (1) a wedged dockerd not responding, and (2) a missing image where
232
- // docker tries to pull from Docker Hub and the registry's 404 is slow
233
- // enough to blow vitest's default test budget. 30s is generous for the
234
- // happy path and bounded enough for production callers.
235
- const SMOKE_DOCKER_TIMEOUT_MS = 30_000;
236
- export function smokeImage(image, targetSha) {
237
- // 1. docker create — allocates the container; doesn't start the entrypoint.
238
- const createResult = spawnSync('docker', ['create', '--name', `olam-smoke-${Date.now()}`, image], {
239
- encoding: 'utf-8',
240
- stdio: ['ignore', 'pipe', 'pipe'],
241
- timeout: SMOKE_DOCKER_TIMEOUT_MS,
242
- });
243
- if (createResult.status !== 0) {
244
- return {
245
- image,
246
- ok: false,
247
- bakedSha: null,
248
- error: `docker create failed: ${(createResult.stderr ?? '').trim()}`,
249
- };
250
- }
251
- const containerId = (createResult.stdout ?? '').trim();
252
- // 2. docker inspect — read the OLAM_BUILD_SHA label.
253
- const inspectResult = spawnSync('docker', ['inspect', '--format', '{{index .Config.Labels "olam.build.sha"}}', image], {
254
- encoding: 'utf-8',
255
- stdio: ['ignore', 'pipe', 'pipe'],
256
- timeout: SMOKE_DOCKER_TIMEOUT_MS,
257
- });
258
- // 3. Cleanup — best-effort; ignore exit code.
259
- if (containerId.length > 0) {
260
- spawnSync('docker', ['rm', '-f', containerId], {
261
- encoding: 'utf-8',
262
- stdio: ['ignore', 'ignore', 'ignore'],
263
- timeout: SMOKE_DOCKER_TIMEOUT_MS,
264
- });
265
- }
266
- if (inspectResult.status !== 0) {
267
- return {
268
- image,
269
- ok: false,
270
- bakedSha: null,
271
- error: `docker inspect failed: ${(inspectResult.stderr ?? '').trim()}`,
272
- };
273
- }
274
- const bakedSha = (inspectResult.stdout ?? '').trim();
275
- // Empty output means the label is absent — that's a build-corrupt signal.
276
- if (bakedSha.length === 0) {
277
- return {
278
- image,
279
- ok: false,
280
- bakedSha: null,
281
- error: 'olam.build.sha label is missing or empty',
282
- };
283
- }
284
- // Allow either full 40-char SHA or "unknown" (build-host-cp.sh writes
285
- // "unknown" when git rev-parse fails). Match against targetSha for
286
- // success.
287
- if (bakedSha !== targetSha) {
288
- return {
289
- image,
290
- ok: false,
291
- bakedSha,
292
- error: `baked SHA ${abbreviateSha(bakedSha)} ≠ target SHA ${abbreviateSha(targetSha)}`,
293
- };
294
- }
295
- return { image, ok: true, bakedSha };
296
- }
297
- export const PRODUCTION_SWAP_PLAN = [
298
- { transient: 'olam-auth:olam-next', canonical: 'olam-auth:local', rollback: 'olam-auth:olam-rollback' },
299
- { transient: 'olam-devbox:olam-next', canonical: 'olam-devbox:latest', rollback: 'olam-devbox:olam-rollback' },
300
- { transient: 'olam-host-cp:olam-next', canonical: 'olam-host-cp:latest', rollback: 'olam-host-cp:olam-rollback' },
301
- { transient: 'olam-mcp-auth:olam-next', canonical: 'olam-mcp-auth:local', rollback: 'olam-mcp-auth:olam-rollback' },
302
- { transient: 'olam-kg-service:olam-next', canonical: 'olam-kg-service:local', rollback: 'olam-kg-service:olam-rollback' },
303
- ];
304
- /**
305
- * Run `docker tag <source> <dest>`. Returns ok=false with stderr trimmed
306
- * on failure (e.g. source image absent). No retry — caller decides.
307
- *
308
- * Per audit A6-003: spawnSync may throw synchronously under fork pressure
309
- * (libuv clone(2) failures). The try/catch ensures performAtomicSwap can
310
- * always proceed to its summary phase — a thrown exception escaping
311
- * dockerTag would leak the upgrade lock and produce no SwapResult, which
312
- * confuses both the operator AND Phase 2b's --rollback recovery path.
313
- */
314
- export function dockerTag(source, dest) {
315
- try {
316
- const result = spawnSync('docker', ['tag', source, dest], {
317
- encoding: 'utf-8',
318
- stdio: ['ignore', 'ignore', 'pipe'],
319
- });
320
- if (result.status === 0 && result.error === undefined)
321
- return { ok: true };
322
- return {
323
- ok: false,
324
- error: (result.stderr ?? '').trim() || result.error?.message || 'docker tag failed',
325
- };
326
- }
327
- catch (err) {
328
- return {
329
- ok: false,
330
- error: err instanceof Error ? `spawnSync threw: ${err.message}` : 'spawnSync threw',
331
- };
332
- }
333
- }
334
- /**
335
- * Atomic-ish 3-image set swap.
336
- *
337
- * Six sequential `docker tag` ops in two phases:
338
- *
339
- * Phase 1 (rollback-save):
340
- * 1. canonical → :olam-rollback (image 1)
341
- * 2. canonical → :olam-rollback (image 2)
342
- * 3. canonical → :olam-rollback (image 3)
343
- *
344
- * Phase 2 (canonical-advance):
345
- * 4. :olam-next → canonical (image 1)
346
- * 5. :olam-next → canonical (image 2)
347
- * 6. :olam-next → canonical (image 3)
348
- *
349
- * Invariants:
350
- *
351
- * - **First-upgrade tolerance**: any of steps 1-3 may fail with "no such
352
- * image" if the operator has never had a canonical-tagged image. Those
353
- * failures are NON-FATAL (recorded in rollbackError but not aborted) —
354
- * `:olam-rollback` simply doesn't exist for that image; Phase 2b's
355
- * `--rollback` pre-flight detects this and refuses.
356
- *
357
- * - **Canonical-advance fatality**: any failure in steps 4-6 is fatal.
358
- * The swap is partially advanced; canonical tags are now mixed (some
359
- * at SHA-Y, some at SHA-X). Operator runs `olam upgrade --rollback`
360
- * (Phase 2b) which uses the FULL `:olam-rollback` set written in Phase 1
361
- * to restore coherent prior state.
362
- *
363
- * - **SIGKILL recovery**: if killed during Phase 1, partial `:olam-rollback`
364
- * exists but canonical is intact — operator's next `olam upgrade` succeeds
365
- * normally (the partial `:olam-rollback` is overwritten by the next
366
- * successful run). If killed during Phase 2, canonical is mixed —
367
- * operator must `olam upgrade --rollback` to recover.
368
- *
369
- * The "atomic-ish" qualifier: `docker tag` is per-image atomic (POSIX rename
370
- * of a symbolic name), but the SET of 3 canonical tags is updated sequentially
371
- * across ~1s wall-clock. Sub-second window is acceptable for solo-dev/dogfood
372
- * per the plan's local-dev/dogfood priority axis.
373
- */
374
- export function performAtomicSwap(plan) {
375
- const steps = plan.map((p) => ({
376
- image: p.canonical,
377
- rollbackSaved: false,
378
- canonicalAdvanced: false,
379
- }));
380
- // Phase 1: preserve previous-good as :olam-rollback (steps 1-3).
381
- // Non-fatal failures: missing canonical (first-upgrade) is acceptable.
382
- for (let i = 0; i < plan.length; i++) {
383
- const p = plan[i];
384
- const r = dockerTag(p.canonical, p.rollback);
385
- steps[i] = {
386
- ...steps[i],
387
- rollbackSaved: r.ok,
388
- ...(r.error !== undefined && { rollbackError: r.error }),
389
- };
390
- }
391
- // Phase 2: advance canonical to :olam-next (steps 4-6).
392
- // FATAL failure: canonical is mixed. Recovery via `olam upgrade --rollback`.
393
- let advanceFailed = false;
394
- let firstFailureIdx = -1;
395
- for (let i = 0; i < plan.length; i++) {
396
- const p = plan[i];
397
- if (advanceFailed) {
398
- // Skip remaining advances after first failure — leaves canonical mixed
399
- // but caller's recovery path is `olam upgrade --rollback`, not
400
- // continue-and-hope.
401
- steps[i] = { ...steps[i], canonicalAdvanced: false };
402
- continue;
403
- }
404
- const r = dockerTag(p.transient, p.canonical);
405
- steps[i] = {
406
- ...steps[i],
407
- canonicalAdvanced: r.ok,
408
- ...(r.error !== undefined && { canonicalError: r.error }),
409
- };
410
- if (!r.ok) {
411
- advanceFailed = true;
412
- firstFailureIdx = i;
413
- }
414
- }
415
- const allAdvanced = steps.every((s) => s.canonicalAdvanced);
416
- const noneAdvanced = steps.every((s) => !s.canonicalAdvanced);
417
- const partialAdvance = !allAdvanced && !noneAdvanced;
418
- const rollbackCoherent = steps.every((s) => s.rollbackSaved);
419
- let summary;
420
- if (allAdvanced) {
421
- const rollbacks = steps.filter((s) => s.rollbackSaved).length;
422
- summary = `Swapped ${plan.length} canonical tags; ${rollbacks} :olam-rollback preserved`;
423
- }
424
- else if (partialAdvance) {
425
- const advanced = steps.filter((s) => s.canonicalAdvanced).length;
426
- const failedStep = steps[firstFailureIdx];
427
- // Audit A6-001: only recommend --rollback when the rollback set is COHERENT.
428
- // Otherwise the operator would either partially restore or hit Phase 2b's
429
- // pre-flight refusal — misleading either way.
430
- const recoveryHint = rollbackCoherent
431
- ? `Run \`olam upgrade --rollback\` to restore coherent prior state.`
432
- : `Rollback set INCOHERENT (${steps.filter((s) => s.rollbackSaved).length} of ${plan.length} :olam-rollback tags written). Manual recovery required: inspect images and re-tag canonical from a known-good source.`;
433
- summary = `PARTIAL: ${advanced} of ${plan.length} canonical tags advanced before failure on ${failedStep?.image}: ${failedStep?.canonicalError}. ${recoveryHint}`;
434
- }
435
- else {
436
- const failedStep = steps[firstFailureIdx];
437
- summary = `Failed on first canonical-advance (${failedStep?.image}): ${failedStep?.canonicalError}. Canonical tags untouched.`;
438
- }
439
- return {
440
- ok: allAdvanced,
441
- steps,
442
- partialAdvance,
443
- rollbackCoherent,
444
- summary,
445
- };
446
- }
447
- /**
448
- * Inverse of performAtomicSwap — restore canonical from :olam-rollback
449
- * (Phase 2b — B1). Three sequential `docker tag` ops:
450
- *
451
- * docker tag olam-auth:olam-rollback olam-auth:local
452
- * docker tag olam-devbox:olam-rollback olam-devbox:latest
453
- * docker tag olam-host-cp:olam-rollback olam-host-cp:latest
454
- *
455
- * No two-phase ceremony — the source `:olam-rollback` set is already a
456
- * coherent prior-good captured by a previous successful `olam upgrade`,
457
- * so we don't need to preserve current canonical (it's known-broken,
458
- * which is why we're rolling back).
459
- *
460
- * Caller MUST pre-flight via `checkRollbackSetExists()` before invoking.
461
- * Behavior on missing source is per-image fatal (returns ok=false +
462
- * error naming the missing image).
463
- */
464
- export function performRollbackSwap(plan) {
465
- const results = [];
466
- for (const p of plan) {
467
- const r = dockerTag(p.rollback, p.canonical);
468
- results.push({
469
- image: p.canonical,
470
- ok: r.ok,
471
- ...(r.error !== undefined && { error: r.error }),
472
- });
473
- }
474
- const allOk = results.every((r) => r.ok);
475
- const summary = allOk
476
- ? `Rolled back ${plan.length} canonical tags from :olam-rollback`
477
- : `PARTIAL rollback: ${results.filter((r) => r.ok).length} of ${plan.length} succeeded; failed: ${results.filter((r) => !r.ok).map((r) => r.image).join(', ')}`;
478
- return { ok: allOk, results, summary };
479
- }
480
- async function confirm(message) {
481
- if (!process.stdin.isTTY)
482
- return true;
483
- const { createInterface } = await import('node:readline');
484
- const rl = createInterface({ input: process.stdin, output: process.stdout });
485
- return new Promise((resolve) => {
486
- rl.question(`${message} [y/N] `, (answer) => {
487
- rl.close();
488
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
489
- });
490
- });
491
- }
492
- async function waitForHealth(timeoutMs = 10_000) {
493
- const deadline = Date.now() + timeoutMs;
494
- while (Date.now() < deadline) {
495
- try {
496
- const res = await fetch('http://127.0.0.1:19000/health', {
497
- signal: AbortSignal.timeout(2000),
498
- });
499
- if (res.ok)
500
- return true;
501
- }
502
- catch {
503
- // not up yet
504
- }
505
- await new Promise((r) => setTimeout(r, 500));
506
- }
507
- return false;
508
- }
509
- /**
510
- * Poll /api/version/status until all three component `.running` SHAs match
511
- * `targetSha`, or until `timeoutMs` elapses. Returns the final snapshot.
512
- *
513
- * Phase 2a — A8: this is the success criterion for the entire upgrade.
514
- * After A6's atomic swap + A7's recreate, the new images should report
515
- * the new SHA via OLAM_BUILD_SHA baked at build time. Round-trip through
516
- * Phase 1's detection path closes the loop.
517
- *
518
- * Returns:
519
- * - { matched: true, snapshot } when all three SHAs equal targetSha within timeout.
520
- * - { matched: false, snapshot } when timeout expires; caller decides
521
- * whether to warn (recreate succeeded but propagation slow) or error.
522
- * - { matched: false, snapshot: null } when /api/version/status is
523
- * unreachable for the entire timeout (host-cp didn't come back up).
524
- */
525
- export async function waitForVersionMatch(targetSha, timeoutMs = 90_000, pollIntervalMs = 1_000) {
526
- const deadline = Date.now() + timeoutMs;
527
- let lastSnapshot = null;
528
- while (Date.now() < deadline) {
529
- try {
530
- const res = await fetch('http://127.0.0.1:19000/api/version/status', {
531
- signal: AbortSignal.timeout(2_000),
532
- });
533
- if (res.ok) {
534
- const snapshot = (await res.json());
535
- lastSnapshot = snapshot;
536
- if (snapshot.hostCp?.running === targetSha &&
537
- snapshot.authService?.running === targetSha &&
538
- snapshot.devbox?.running === targetSha) {
539
- return { matched: true, snapshot };
540
- }
541
- }
542
- }
543
- catch {
544
- // host-cp not yet ready or transient network blip
545
- }
546
- await new Promise((r) => setTimeout(r, pollIntervalMs));
547
- }
548
- return { matched: false, snapshot: lastSnapshot };
549
- }
550
- /**
551
- * Format a version-snapshot mismatch into a readable per-component diff
552
- * (Phase 2a — A8). Used in the timeout-warn path so the operator sees
553
- * which component is lagging.
554
- */
555
- export function formatVersionMismatch(targetSha, snapshot) {
556
- if (!snapshot)
557
- return 'No /api/version/status response received within timeout.';
558
- const lines = [];
559
- for (const [name, comp] of [
560
- ['host-cp', snapshot.hostCp],
561
- ['auth-service', snapshot.authService],
562
- ['devbox', snapshot.devbox],
563
- ]) {
564
- const match = comp?.running === targetSha;
565
- lines.push(` ${match ? '✓' : '✗'} ${name}: running=${abbreviateSha(comp?.running ?? 'unknown')} target=${abbreviateSha(targetSha)}`);
566
- }
567
- return lines.join('\n');
568
- }
569
- /**
570
- * Block until auth-service /health responds or timeout expires (Phase 2a — A7).
571
- *
572
- * Mirrors auth-upgrade.ts's waitForAuthHealth — kept inline to avoid a
573
- * circular dep. When auth-upgrade.ts is refactored later (Phase G+),
574
- * extract a shared helper.
575
- */
576
- /**
577
- * Default 60s. The auth-service container's first /health response after
578
- * a recreate can lag 10–30s on a cold sqlite migration + Express handler
579
- * boot, observed during PR #311's upgrade-trigger dogfood. 15s (the
580
- * previous default) timed out the upgrader pipeline at the recreate-
581
- * verification step on cold-cache machines. 60s gives the cold-boot path
582
- * comfortable headroom; the warn message still tells the operator what
583
- * to inspect if it actually exceeds 60s.
584
- */
585
- const AUTH_HEALTH_TIMEOUT_MS = 60_000;
586
- async function waitForAuthHealthLocal(timeoutMs = AUTH_HEALTH_TIMEOUT_MS) {
587
- const deadline = Date.now() + timeoutMs;
588
- while (Date.now() < deadline) {
589
- try {
590
- const res = await fetch(AUTH_HEALTH_URL, { signal: AbortSignal.timeout(2000) });
591
- if (res.ok)
592
- return true;
593
- }
594
- catch {
595
- // not up yet
596
- }
597
- await new Promise((r) => setTimeout(r, 500));
598
- }
599
- return false;
600
- }
601
- /**
602
- * Recreate the auth-service container against the freshly-tagged
603
- * `olam-auth:local` image (Phase 2a — A7).
604
- *
605
- * Mirrors auth-upgrade.ts:237-275: docker stop → docker rm →
606
- * AuthContainerController.start(). Auth-service is NOT in compose.yaml
607
- * (it runs via the controller's docker run with secret injection), so
608
- * we cannot reuse `docker compose --force-recreate auth-service` —
609
- * compose would fail with "no such service: auth-service" (verified
610
- * during pass-2 review F2 audit).
611
- *
612
- * Errors:
613
- * - docker stop / docker rm errors are swallowed (container may not
614
- * be running or may not exist; both are recoverable states).
615
- * - AuthContainerController.start() throws on real failures (image
616
- * missing, port conflict, secret missing); caller catches and
617
- * reports.
618
- *
619
- * Returns true on successful recreate + /health response within 15s.
620
- */
621
- async function recreateAuthService() {
622
- const start = Date.now();
623
- try {
624
- // Step 1: stop + remove. Errors swallowed — container may be absent/stopped.
625
- spawnSync('docker', ['stop', 'olam-auth'], {
626
- encoding: 'utf-8',
627
- stdio: ['ignore', 'ignore', 'ignore'],
628
- });
629
- spawnSync('docker', ['rm', 'olam-auth'], {
630
- encoding: 'utf-8',
631
- stdio: ['ignore', 'ignore', 'ignore'],
632
- });
633
- // Step 2: start the new container via the controller (handles secret
634
- // injection; reads OLAM_AUTH_SECRET from env or ~/.olam/auth-secret).
635
- const controller = new AuthContainerController();
636
- controller.start();
637
- // Step 3: wait for /health.
638
- const healthy = await waitForAuthHealthLocal();
639
- const durationMs = Date.now() - start;
640
- if (!healthy) {
641
- return {
642
- ok: false,
643
- durationMs,
644
- error: `auth-service /health did not respond within ${AUTH_HEALTH_TIMEOUT_MS / 1000}s after recreate`,
645
- };
646
- }
647
- return { ok: true, durationMs };
648
- }
649
- catch (err) {
650
- return {
651
- ok: false,
652
- durationMs: Date.now() - start,
653
- error: err instanceof Error ? err.message : String(err),
654
- };
655
- }
656
- }
657
- function readBundleHash(cwd) {
658
- const indexPath = path.join(cwd, 'packages/plan-chat-spa/dist/client/index.html');
659
- if (!fs.existsSync(indexPath))
660
- return null;
661
- return extractBundleHash(fs.readFileSync(indexPath, 'utf-8'));
662
- }
663
- export async function runUpgradePullByDigest(deps = {}) {
664
- const docker = deps.docker ?? realDocker;
665
- const digests = deps.digests ?? loadImageDigests();
666
- const registry = deps.registry ?? digests.$registry ?? 'ghcr.io/pleri';
667
- printHeader('olam upgrade (pull-by-digest)');
668
- // Confirmation prompt — skipped when deps.yes is true (--yes flag) or
669
- // when stdin is not a TTY (CI / non-interactive).
670
- if (!deps.yes) {
671
- const proceed = await confirm('Pull latest images and recreate stack?');
672
- if (!proceed) {
673
- printInfo('Aborted', 'no changes made');
674
- return { exitCode: 0, summary: 'aborted by operator' };
675
- }
676
- }
677
- // Step 1 — daemon smoke. Re-uses bootstrap's docker.info().
678
- const infoSpinner = ora('Checking docker daemon').start();
679
- const info = await docker.info();
680
- if (info.exitCode !== 0) {
681
- infoSpinner.fail('docker daemon not reachable');
682
- process.stderr.write(`${pc.red('error')} docker info exited with ${info.exitCode}.\n` +
683
- ' Ensure Docker Desktop / Colima / Rancher is running, then retry.\n');
684
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'docker daemon not reachable' };
685
- }
686
- infoSpinner.succeed('docker daemon reachable');
687
- // Step 2 — parallel pull images by digest. mcp-auth is conditional on
688
- // the digest being present in the manifest (older tarballs don't have
689
- // it; pre-mcp-auth releases shipped 3 images, post-mcp-auth ship 4).
690
- const hasMcpAuthDigest = typeof digests['mcp-auth'] === 'string' && digests['mcp-auth'].length > 0;
691
- const hasKgServiceDigest = typeof digests['kg-service'] === 'string' && digests['kg-service'].length > 0;
692
- // devbox-slim Phase A: pull :base alongside :browser-aliased :latest so
693
- // the new DEFAULT_DEVBOX_IMAGE = olam-devbox:base resolves locally on a
694
- // fresh `olam upgrade -y` host. Older releases without devbox-base in
695
- // image-digests.json still upgrade cleanly — devbox-base is gated on
696
- // its presence the same way mcp-auth is.
697
- const hasDevboxBaseDigest = typeof digests['devbox-base'] === 'string' && digests['devbox-base'].length > 0;
698
- const baseRefs = [
699
- { name: 'host-cp', ref: `${registry}/olam-host-cp@${digests['host-cp']}` },
700
- { name: 'auth', ref: `${registry}/olam-auth@${digests.auth}` },
701
- { name: 'devbox', ref: `${registry}/olam-devbox@${digests.devbox}` },
702
- ];
703
- if (hasDevboxBaseDigest) {
704
- baseRefs.push({
705
- name: 'devbox-base',
706
- ref: `${registry}/olam-devbox@${digests['devbox-base']}`,
707
- });
708
- }
709
- if (hasMcpAuthDigest) {
710
- baseRefs.push({
711
- name: 'mcp-auth',
712
- ref: `${registry}/olam-mcp-auth@${digests['mcp-auth']}`,
713
- });
714
- }
715
- // kg-service joined the publish DAG in v0.1.139 (release.yml fix #565).
716
- // Gated on digest presence so older tarballs (≤ v0.1.138) without the
717
- // entry still upgrade cleanly — same pattern as mcp-auth + devbox-base.
718
- if (hasKgServiceDigest) {
719
- baseRefs.push({
720
- name: 'kg-service',
721
- ref: `${registry}/olam-kg-service@${digests['kg-service']}`,
722
- });
723
- }
724
- const imageRefs = baseRefs;
725
- const pullSpinner = ora(`Pulling images (${imageRefs.length} parallel)`).start();
726
- const pullStart = Date.now();
727
- const pullResults = await Promise.all(imageRefs.map(async ({ name, ref }) => ({
728
- name,
729
- ref,
730
- result: await pullImageWithRetry(ref, docker),
731
- })));
732
- const pullElapsed = ((Date.now() - pullStart) / 1000).toFixed(1);
733
- const failed = pullResults.filter((r) => r.result.exitCode !== 0);
734
- if (failed.length > 0) {
735
- pullSpinner.fail(`Pull failed for ${failed.map((r) => r.name).join(', ')}`);
736
- for (const f of failed) {
737
- process.stderr.write(` ${pc.red(f.name)} (${f.ref}):\n` +
738
- ` exit=${f.result.exitCode}\n` +
739
- ` stderr: ${f.result.stderr.split('\n')[0] ?? '(empty)'}\n`);
740
- }
741
- process.stderr.write('\n Remedy: re-run after resolving network / GHCR availability.\n' +
742
- ' For monorepo dev, `OLAM_DEV=1 olam upgrade --from-source` builds locally.\n');
743
- return {
744
- exitCode: EXIT_BOOTSTRAP_PULL_FAILED,
745
- summary: `pull failed: ${failed.map((r) => r.name).join(', ')}`,
746
- };
747
- }
748
- pullSpinner.succeed(`Pulled ${imageRefs.length} images in ${pullElapsed}s`);
749
- // Step 3 — protocol-version handshake on every image.
750
- const handshakeSpinner = ora('Verifying olam.protocol.versions handshake').start();
751
- for (const { name, ref } of imageRefs) {
752
- const inspect = await docker.inspectLabel(ref, 'olam.protocol.versions');
753
- if (inspect.exitCode !== 0) {
754
- handshakeSpinner.fail(`Could not inspect ${name}`);
755
- process.stderr.write(`${pc.red('error')} docker inspect ${ref} failed: ${inspect.stderr}\n`);
756
- return { exitCode: EXIT_GENERIC_ERROR, summary: `inspect failed: ${name}` };
757
- }
758
- const labelValue = (inspect.stdout || '').trim();
759
- const versions = parseProtocolVersionsLabel(labelValue === '<no value>' ? '' : labelValue);
760
- const decision = checkProtocolOverlap(versions);
761
- if (!decision.compatible) {
762
- handshakeSpinner.fail(`Protocol mismatch on ${name}`);
763
- process.stderr.write(`${pc.red('error')} ${decision.remedy}\n`);
764
- return {
765
- exitCode: EXIT_PROTOCOL_MISMATCH,
766
- summary: `protocol mismatch: ${name}`,
767
- };
768
- }
769
- }
770
- handshakeSpinner.succeed(`Protocol handshake passed (all ${imageRefs.length} images)`);
771
- // Step 4 — re-tag pulled-by-digest images to the canonical local tags
772
- // that compose.yaml + AuthContainerController expect. Pulling by
773
- // digest only writes a content-addressed reference; compose / the
774
- // controller key on `olam-host-cp:latest` and `olam-auth:local`
775
- // respectively, so we tag explicitly.
776
- // Compose.yaml uses `image: ghcr.io/pleri/olam-host-cp:latest` (registry-
777
- // prefixed), NOT bare `olam-host-cp:latest`. Docker treats the two refs as
778
- // distinct local tags. Pre-fix, the upgrade retagged ONLY the bare-name
779
- // tag, so the registry-prefixed tag (used by compose) kept pointing at
780
- // whatever the previous pull put there — leaving compose's recreate to
781
- // pick the OLD image despite the new one being in cache. End state:
782
- // `olam upgrade` reports "Re-tagged 3 images to canonical refs" + "host-cp
783
- // recreated" + "Upgrade complete (running …NEW…)" but the actually-
784
- // running container stayed on the old digest. Repro: compare
785
- // `docker inspect <ghcr-prefixed>:latest` vs `docker inspect <bare>:latest`
786
- // mid-upgrade — they diverge.
787
- // Fix: retag BOTH names. The bare-name tag is kept for legacy callers
788
- // (e.g. `docker run olam-host-cp:latest` in scripts); the registry-
789
- // prefixed tag is what compose + AuthContainerController actually use.
790
- // Look up by NAME rather than positional index — devbox-slim Phase A
791
- // inserted devbox-base into the array, so positional indices for
792
- // mcp-auth shifted. Name-based lookup is order-independent.
793
- const refByName = (n) => {
794
- const found = imageRefs.find((r) => r.name === n);
795
- if (!found)
796
- throw new Error(`upgrade: missing imageRefs entry for "${n}"`);
797
- return found.ref;
798
- };
799
- const tagPlanBase = [
800
- { from: refByName('host-cp'), to: 'olam-host-cp:latest', name: 'host-cp (bare)' },
801
- { from: refByName('host-cp'), to: `${registry}/olam-host-cp:latest`, name: 'host-cp (registry)' },
802
- { from: refByName('auth'), to: 'olam-auth:local', name: 'auth (bare)' },
803
- { from: refByName('auth'), to: `${registry}/olam-auth:latest`, name: 'auth (registry)' },
804
- { from: refByName('devbox'), to: 'olam-devbox:latest', name: 'devbox (bare)' },
805
- { from: refByName('devbox'), to: `${registry}/olam-devbox:latest`, name: 'devbox (registry)' },
806
- ];
807
- // devbox-slim Phase A: tag the :base variant so DEFAULT_DEVBOX_IMAGE
808
- // = olam-devbox:base resolves locally after upgrade. Tag both bare
809
- // and registry-prefixed for parity with :latest above.
810
- if (hasDevboxBaseDigest) {
811
- tagPlanBase.push({ from: refByName('devbox-base'), to: 'olam-devbox:base', name: 'devbox-base (bare)' }, { from: refByName('devbox-base'), to: `${registry}/olam-devbox:base`, name: 'devbox-base (registry)' });
812
- }
813
- if (hasMcpAuthDigest) {
814
- tagPlanBase.push({ from: refByName('mcp-auth'), to: 'olam-mcp-auth:local', name: 'mcp-auth (bare)' }, { from: refByName('mcp-auth'), to: `${registry}/olam-mcp-auth:latest`, name: 'mcp-auth (registry)' });
815
- }
816
- // kg-service: canonical bare tag `:local` matches
817
- // KG_SERVICE_LOCAL_TAG in kg-service-container.ts. Operators running
818
- // `olam services up` after upgrade pick this up.
819
- if (hasKgServiceDigest) {
820
- tagPlanBase.push({ from: refByName('kg-service'), to: 'olam-kg-service:local', name: 'kg-service (bare)' }, { from: refByName('kg-service'), to: `${registry}/olam-kg-service:latest`, name: 'kg-service (registry)' });
821
- }
822
- const tagPlan = tagPlanBase;
823
- const tagSpinner = ora('Tagging digests → canonical local refs').start();
824
- const tagger = deps.tagImpl ?? dockerTag;
825
- for (const t of tagPlan) {
826
- const r = tagger(t.from, t.to);
827
- if (!r.ok) {
828
- tagSpinner.fail(`docker tag failed for ${t.name}`);
829
- process.stderr.write(`${pc.red('error')} ${r.error ?? 'docker tag failed'}\n`);
830
- return { exitCode: EXIT_GENERIC_ERROR, summary: `tag failed: ${t.name}` };
831
- }
832
- }
833
- tagSpinner.succeed(`Re-tagged ${tagPlan.length} image refs to canonical names`);
834
- // Step 5 — recreate the docker-socket-proxy + host-cp via docker
835
- // compose. Auth-service is NOT in compose.yaml (runs via
836
- // AuthContainerController.start), so we recreate it separately in
837
- // step 6.
838
- const composeFile = deps.composeFile ?? findComposeFile();
839
- const authSecret = deps.authSecret ?? readAuthSecret();
840
- const composeRunner = deps.runComposeImpl ?? runCompose;
841
- // Step 5a — recreate docker-socket-proxy FIRST. Prior to this
842
- // change we skipped the sidecar entirely (`--no-deps host-cp` only),
843
- // on the assumption "proxy doesn't change between releases." That
844
- // broke when PR #460 shipped a new compose.yaml that enabled
845
- // IMAGES=1 on the proxy: `olam upgrade -y` pulled new images and
846
- // recreated host-cp, but the running proxy stayed on the old
847
- // (IMAGES=0) config — so the new /api/version/status comparator's
848
- // GET /images/<ref>/json calls stayed denied until the operator
849
- // manually `docker compose up -d --force-recreate
850
- // docker-socket-proxy`. Any future compose.yaml tweak to the
851
- // sidecar would silently no-op the same way.
852
- //
853
- // The original PR #311 concern (recreating the proxy collides with
854
- // host-cp's upgrade-trigger spawning the upgrader inside the same
855
- // compose project) doesn't apply here: we recreate the proxy
856
- // BEFORE host-cp, and `--no-deps` keeps compose from cascading.
857
- // Brief proxy disconnect (~5s) is invisible to the operator CLI
858
- // and tolerated by host-cp's docker client (retries on next poll).
859
- const proxySpinner = ora('docker compose recreate docker-socket-proxy').start();
860
- const proxyResult = composeRunner(['up', '-d', '--force-recreate', '--no-deps', 'docker-socket-proxy'], composeFile, buildComposeEnv(authSecret, captureGhToken()));
861
- if (!proxyResult.ok) {
862
- proxySpinner.fail('docker-socket-proxy recreate failed');
863
- process.stderr.write(`${pc.red('error')} docker compose up --force-recreate docker-socket-proxy failed:\n` +
864
- ` ${proxyResult.stderr.split('\n').slice(0, 3).join('\n ')}\n`);
865
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'socket-proxy recreate failed' };
866
- }
867
- proxySpinner.succeed('docker-socket-proxy recreated');
868
- // Step 5b-pre — clean up pre-rename name-collision orphan. PR #596/#597
869
- // renamed the compose service host-cp → olam-host-cp; the container_name
870
- // was already olam-host-cp, so a pre-existing container from before the
871
- // rename carries labels `com.docker.compose.service=host-cp` and
872
- // `com.docker.compose.project=host-cp` AND occupies the `olam-host-cp`
873
- // container name the new service expects. `--force-recreate` cannot
874
- // reclaim a container whose service label belongs to a different
875
- // (now non-existent) compose service, so the recreate aborts with
876
- // "Conflict. The container name "/olam-host-cp" is already in use".
877
- // Force-remove the orphan first; no-op when no orphan exists or the
878
- // container already belongs to the new service.
879
- const inspectContainer = deps.inspectContainerImpl ?? defaultInspectContainerLabels;
880
- const removeContainer = deps.removeContainerImpl ?? defaultRemoveContainer;
881
- const orphanCheck = inspectContainer('olam-host-cp');
882
- if (!orphanCheck.ok) {
883
- process.stderr.write(`${pc.red('error')} docker inspect olam-host-cp failed:\n` +
884
- ` ${orphanCheck.stderr.split('\n').slice(0, 3).join('\n ')}\n`);
885
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'orphan inspect failed' };
886
- }
887
- if (orphanCheck.exists && orphanCheck.service !== 'olam-host-cp') {
888
- process.stderr.write(`${pc.yellow('info')} Removing pre-rename orphan container olam-host-cp ` +
889
- `(old project=${orphanCheck.project || '<none>'}, old service=${orphanCheck.service || '<none>'})\n`);
890
- const rm = removeContainer('olam-host-cp');
891
- if (!rm.ok) {
892
- process.stderr.write(`${pc.red('error')} docker rm -f olam-host-cp failed:\n` +
893
- ` ${rm.stderr.split('\n').slice(0, 3).join('\n ')}\n`);
894
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'orphan cleanup failed' };
895
- }
896
- }
897
- // Step 5b — recreate host-cp. `--no-deps` keeps compose from
898
- // touching the proxy AGAIN (we just did it in step 5a) and from
899
- // touching auth-service (which isn't a compose service).
900
- const composeSpinner = ora('docker compose recreate olam-host-cp').start();
901
- const composeResult = composeRunner(['up', '-d', '--force-recreate', '--no-deps', 'olam-host-cp'], composeFile, buildComposeEnv(authSecret, captureGhToken()));
902
- if (!composeResult.ok) {
903
- composeSpinner.fail('compose recreate failed');
904
- process.stderr.write(`${pc.red('error')} docker compose up --force-recreate olam-host-cp failed:\n` +
905
- ` ${composeResult.stderr.split('\n').slice(0, 3).join('\n ')}\n`);
906
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'compose recreate failed' };
907
- }
908
- composeSpinner.succeed('olam-host-cp recreated');
909
- // Step 6 — recreate auth-service.
910
- const authSpinner = ora('recreate auth-service').start();
911
- const authRecreate = deps.recreateAuth ?? defaultRecreateAuthForUpgrade;
912
- const authResult = await authRecreate();
913
- if (!authResult.ok) {
914
- authSpinner.fail('auth-service recreate failed');
915
- process.stderr.write(`${pc.red('error')} ${authResult.error ?? 'unknown failure'}\n`);
916
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'auth recreate failed' };
917
- }
918
- authSpinner.succeed('auth-service running on new image');
919
- // Step 7 — recreate mcp-auth-service when its digest is in the manifest.
920
- // Older tarballs (pre-services-bringup) skip this step entirely.
921
- if (hasMcpAuthDigest) {
922
- const mcpSpinner = ora('recreate mcp-auth-service').start();
923
- const mcpRecreate = deps.recreateMcpAuth ?? defaultRecreateMcpAuthForUpgrade;
924
- const mcpResult = await mcpRecreate();
925
- if (!mcpResult.ok) {
926
- mcpSpinner.fail('mcp-auth-service recreate failed');
927
- process.stderr.write(`${pc.red('error')} ${mcpResult.error ?? 'unknown failure'}\n`);
928
- return { exitCode: EXIT_GENERIC_ERROR, summary: 'mcp-auth recreate failed' };
929
- }
930
- mcpSpinner.succeed('mcp-auth-service running on new image');
931
- }
932
- printSuccess('Upgrade complete (pull-by-digest)');
933
- printInfo('host-cp', `running (${digests['host-cp'].slice(0, 19)}…)`);
934
- printInfo('auth', `running (${digests.auth.slice(0, 19)}…)`);
935
- printInfo('devbox', `pulled (${digests.devbox.slice(0, 19)}…)`);
936
- if (hasMcpAuthDigest) {
937
- printInfo('mcp-auth', `running (${digests['mcp-auth'].slice(0, 19)}…)`);
938
- }
939
- return { exitCode: 0, summary: 'stack upgraded' };
940
- }
941
- /**
942
- * Default mcp-auth-service recreate driver for the pull-by-digest upgrade
943
- * path. Stops the existing container, removes it, then starts a fresh one
944
- * via the new image. Mirrors `defaultRecreateAuthForUpgrade` shape.
945
- *
946
- * Implementation lives in @olam/core's container controllers — but for the
947
- * upgrade path we just docker-rm + docker-run because the controller
948
- * encapsulates start-with-image semantics that match what we want here.
949
- */
950
- async function defaultRecreateMcpAuthForUpgrade() {
951
- // Reuse the existing recreateAuthService logic as a template — the
952
- // mcp-auth service follows the same lifecycle (atomic-rename store,
953
- // health-checked start). When the McpAuthContainerController lands as
954
- // a sibling, this function should switch to controller.start().
955
- try {
956
- const { spawnSync: ss } = await import('node:child_process');
957
- ss('docker', ['stop', 'olam-mcp-auth'], { stdio: 'ignore' });
958
- ss('docker', ['rm', '-f', 'olam-mcp-auth'], { stdio: 'ignore' });
959
- const startResult = ss('docker', [
960
- 'run', '-d', '--name', 'olam-mcp-auth',
961
- '-p', '9998:9998',
962
- '-v', 'olam-mcp-auth-data:/mcp-auth-data',
963
- '--restart', 'unless-stopped',
964
- 'olam-mcp-auth:local',
965
- ], { stdio: 'pipe' });
966
- if (startResult.status !== 0) {
967
- return { ok: false, error: `docker run failed: ${startResult.stderr?.toString() ?? 'unknown'}` };
968
- }
969
- return { ok: true };
970
- }
971
- catch (err) {
972
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
973
- }
974
- }
975
- /**
976
- * Default auth-recreate driver for the pull-by-digest upgrade path.
977
- * Mirrors recreateAuthService() defined later in this file but trimmed
978
- * to a UpgradePullDeps.recreateAuth-shaped result.
979
- */
980
- async function defaultRecreateAuthForUpgrade() {
981
- try {
982
- spawnSync('docker', ['stop', 'olam-auth'], {
983
- encoding: 'utf-8',
984
- stdio: ['ignore', 'ignore', 'ignore'],
985
- });
986
- spawnSync('docker', ['rm', 'olam-auth'], {
987
- encoding: 'utf-8',
988
- stdio: ['ignore', 'ignore', 'ignore'],
989
- });
990
- const controller = new AuthContainerController();
991
- controller.start();
992
- const healthy = await waitForAuthHealthLocal();
993
- if (!healthy) {
994
- return {
995
- ok: false,
996
- error: `auth-service /health did not respond within ${AUTH_HEALTH_TIMEOUT_MS / 1000}s`,
997
- };
998
- }
999
- return { ok: true };
1000
- }
1001
- catch (err) {
1002
- return {
1003
- ok: false,
1004
- error: err instanceof Error ? err.message : String(err),
1005
- };
1006
- }
1007
- }
1008
- /**
1009
- * Default `docker inspect` driver for the Step 5b-pre orphan guard.
1010
- * Reads the container's `com.docker.compose.project` and
1011
- * `com.docker.compose.service` labels in a single inspect call. Distinguishes
1012
- * "container does not exist" (treated as ok+exists=false) from real
1013
- * inspect failures (daemon down, permission errors — propagated as ok=false).
1014
- */
1015
- function defaultInspectContainerLabels(containerName) {
1016
- const result = spawnSync('docker', [
1017
- 'inspect',
1018
- '--format',
1019
- '{{index .Config.Labels "com.docker.compose.project"}}|{{index .Config.Labels "com.docker.compose.service"}}',
1020
- containerName,
1021
- ], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
1022
- const stderr = result.stderr ?? '';
1023
- if (result.status === 0) {
1024
- const [project = '', service = ''] = (result.stdout ?? '').trim().split('|');
1025
- return { ok: true, exists: true, project, service, stderr: '' };
1026
- }
1027
- // Docker returns exit 1 with "No such object: <name>" when the container
1028
- // is absent — that's the happy "no collision" case, not an error.
1029
- if (/No such (object|container)/i.test(stderr)) {
1030
- return { ok: true, exists: false, project: '', service: '', stderr: '' };
1031
- }
1032
- // spawn failed before docker could run (e.g. ENOENT — docker not in
1033
- // PATH). Step 1 (daemon smoke) would already have failed in that case,
1034
- // so by the time we get here we know docker is reachable. Treating
1035
- // status=null as "container absent" lets unit tests exercise the
1036
- // recreate flow without a docker daemon and without an inspect mock.
1037
- if (result.status === null) {
1038
- return { ok: true, exists: false, project: '', service: '', stderr: '' };
1039
- }
1040
- return { ok: false, exists: false, project: '', service: '', stderr };
1041
- }
1042
- /**
1043
- * Default `docker rm -f` driver for the Step 5b-pre orphan guard.
1044
- */
1045
- function defaultRemoveContainer(containerName) {
1046
- const result = spawnSync('docker', ['rm', '-f', containerName], {
1047
- encoding: 'utf-8',
1048
- stdio: ['ignore', 'pipe', 'pipe'],
1049
- });
1050
- return { ok: result.status === 0, stderr: result.stderr ?? '' };
1051
- }
1052
- // ── Main handler ──────────────────────────────────────────────────
1053
- async function handleUpgrade(opts) {
1054
- const cwd = process.cwd();
1055
- // 1. Verify repo root.
1056
- const rootCheck = validateRepoRoot(cwd);
1057
- if (!rootCheck.ok) {
1058
- printError(rootCheck.error);
1059
- process.exitCode = 1;
1060
- return;
1061
- }
1062
- // Phase 2c — C2: --history reads the audit log; dispatched BEFORE the
1063
- // upgrade header so the log table prints clean, without the "olam upgrade"
1064
- // / Steps prelude.
1065
- if (opts.history) {
1066
- handleHistory(parseHistoryOpts({ n: opts.historyN, json: opts.historyJson }));
1067
- return;
1068
- }
1069
- // 2. Print plan.
1070
- printHeader('olam upgrade');
1071
- const steps = [
1072
- 'git fetch origin --prune',
1073
- 'git pull --ff-only',
1074
- 'npm install',
1075
- 'npm run build (TS workspaces)',
1076
- 'vite build (SPA)',
1077
- ...(opts.skipImage ? [] : [
1078
- 'bash build-auth.sh (auth-service image)',
1079
- 'bash build-devbox.sh (devbox image)',
1080
- 'bash build-host-cp.sh (host-cp image)',
1081
- 'smoke (docker create + inspect)',
1082
- 'atomic 6-tag swap (canonical -> :olam-rollback; :olam-next -> canonical)',
1083
- 'docker compose --force-recreate olam-host-cp + AuthContainerController.start auth',
1084
- 'poll /api/version/status until SHAs match',
1085
- ]),
1086
- ];
1087
- printInfo('Steps', steps.join(', '));
1088
- if (opts.skipImage)
1089
- printInfo('Mode', '--skip-image: no docker rebuild');
1090
- if (opts.skipInstall)
1091
- printInfo('Mode', '--skip-install: npm install will be skipped');
1092
- if (opts.branch)
1093
- printInfo('Branch', opts.branch);
1094
- process.stdout.write('\n');
1095
- // 3. Confirm unless --yes.
1096
- if (!opts.yes) {
1097
- const proceed = await confirm('Proceed?');
1098
- if (!proceed) {
1099
- printInfo('Aborted', 'no changes made');
1100
- return;
1101
- }
1102
- }
1103
- // Phase 2b — B1: rollback path. Branches at the top so --rollback skips
1104
- // the entire git-pull/build/swap sequence and only retags + recreates.
1105
- if (opts.rollback) {
1106
- return await handleRollback();
1107
- }
1108
- // 3b. Acquire CLI lock (Phase 2a — A1). Refuses if a live upgrade is in flight;
1109
- // auto-recovers stale locks (parse-error / empty / dead-pid / >5 min).
1110
- const lock = acquireLock();
1111
- if (!lock.acquired) {
1112
- printError(formatRefusalMessage(lock, LOCK_FILE_PATH));
1113
- process.exitCode = 1;
1114
- return;
1115
- }
1116
- // SIGINT / SIGTERM handler — release the lock before terminating so the
1117
- // operator's next invocation doesn't have to wait for stale-recovery
1118
- // (audit A1-005). `process.once` so a second Ctrl-C terminates immediately.
1119
- let signalReleased = false;
1120
- const releaseOnSignal = (signal) => {
1121
- if (signalReleased)
1122
- return;
1123
- signalReleased = true;
1124
- try {
1125
- releaseLock();
1126
- }
1127
- catch {
1128
- // best-effort
1129
- }
1130
- // Standard shell exit code for signal-induced termination: 128 + signal-number.
1131
- process.exit(signal === 'SIGINT' ? 130 : 143);
1132
- };
1133
- process.once('SIGINT', releaseOnSignal);
1134
- process.once('SIGTERM', releaseOnSignal);
1135
- // Phase 2c — C1: collect a JSONL log row. Mutated as the upgrade progresses;
1136
- // appended once on the way out (success OR failure) so --history surfaces
1137
- // every attempt, not just successful ones.
1138
- const logRow = {
1139
- started_at: Date.now(),
1140
- durations_ms: {},
1141
- sha_target: '',
1142
- failed_step: null,
1143
- status: 'failed', // default; flipped to 'success' on clean exit
1144
- };
1145
- try {
1146
- await runUpgradeStepsWithLockHeld(opts, cwd, logRow);
1147
- if (process.exitCode !== 1)
1148
- logRow.status = 'success';
1149
- }
1150
- finally {
1151
- const ended_at = Date.now();
1152
- const row = {
1153
- ts: new Date(ended_at).toISOString(),
1154
- started_at: logRow.started_at,
1155
- ended_at,
1156
- sha_target: logRow.sha_target,
1157
- status: logRow.status,
1158
- failed_step: logRow.failed_step,
1159
- durations_ms: logRow.durations_ms,
1160
- };
1161
- appendUpgradeLog(row);
1162
- releaseLock();
1163
- process.removeListener('SIGINT', releaseOnSignal);
1164
- process.removeListener('SIGTERM', releaseOnSignal);
1165
- }
1166
- }
1167
- /**
1168
- * Phase 2b — B1: handle `olam upgrade --rollback`.
1169
- *
1170
- * Pre-flights all three :olam-rollback tags exist; refuses with exit 1 if
1171
- * any missing. Else atomically retags :olam-rollback → canonical for all
1172
- * three images, then recreates host-cp (compose) + auth-service (controller).
1173
- * No git pull, no build, no smoke — the rollback target is a known-good
1174
- * image set captured by a previous successful upgrade.
1175
- *
1176
- * Acquires the same upgrade lock as the regular path so concurrent
1177
- * --rollback + --normal-upgrade refuse at the file-mutex layer.
1178
- */
1179
- async function handleRollback() {
1180
- printHeader('olam upgrade --rollback');
1181
- // 1. Pre-flight — verify rollback set exists.
1182
- const missing = checkRollbackSetExists(PRODUCTION_SWAP_PLAN);
1183
- if (missing !== null) {
1184
- printError(`No rollback-set available — missing :olam-rollback tag(s): ${missing}\n\n` +
1185
- 'A rollback-set is created by the FIRST successful `olam upgrade`. If this\n' +
1186
- 'is your first install, run `olam upgrade` to populate the rollback set.\n' +
1187
- 'If a previous upgrade was incomplete, the rollback set may be partial;\n' +
1188
- 'manually inspect images with `docker images olam-*:olam-rollback`.');
1189
- process.exitCode = 1;
1190
- return;
1191
- }
1192
- // 2. Acquire lock (same primitive as the upgrade path).
1193
- const lock = acquireLock();
1194
- if (!lock.acquired) {
1195
- printError(formatRefusalMessage(lock, LOCK_FILE_PATH));
1196
- process.exitCode = 1;
1197
- return;
1198
- }
1199
- let signalReleased = false;
1200
- const releaseOnSignal = (signal) => {
1201
- if (signalReleased)
1202
- return;
1203
- signalReleased = true;
1204
- try {
1205
- releaseLock();
1206
- }
1207
- catch {
1208
- /* best-effort */
1209
- }
1210
- process.exit(signal === 'SIGINT' ? 130 : 143);
1211
- };
1212
- process.once('SIGINT', releaseOnSignal);
1213
- process.once('SIGTERM', releaseOnSignal);
1214
- try {
1215
- // 3. Inverse swap: 3 docker tag ops.
1216
- process.stdout.write(` ${pc.dim('rollback retag (3 ops)'.padEnd(34))}`);
1217
- const swapStart = Date.now();
1218
- const swapResult = performRollbackSwap(PRODUCTION_SWAP_PLAN);
1219
- const swapDur = `${((Date.now() - swapStart) / 1000).toFixed(1)}s`;
1220
- process.stdout.write(`${swapResult.ok ? pc.green('✓') : pc.red('✗')} ${swapDur}\n`);
1221
- if (!swapResult.ok) {
1222
- printError(`Rollback retag failed: ${swapResult.summary}`);
1223
- process.exitCode = 1;
1224
- return;
1225
- }
1226
- printInfo('Rollback', swapResult.summary);
1227
- // 4. Recreate containers (host-cp via compose, auth via controller).
1228
- const composeFile = findComposeFile();
1229
- const authSecret = readAuthSecret();
1230
- process.stdout.write(` ${pc.dim('docker compose recreate olam-host-cp'.padEnd(34))}`);
1231
- const composeStart = Date.now();
1232
- // `--no-deps` matches the forward-upgrade path (line ~1025) so the
1233
- // rollback recreate doesn't also bounce docker-socket-proxy.
1234
- const composeResult = runCompose(['up', '-d', '--force-recreate', '--no-deps', 'olam-host-cp'], composeFile, buildComposeEnv(authSecret, captureGhToken()));
1235
- const composeDur = `${((Date.now() - composeStart) / 1000).toFixed(1)}s`;
1236
- process.stdout.write(`${composeResult.ok ? pc.green('✓') : pc.red('✗')} ${composeDur}\n`);
1237
- if (!composeResult.ok) {
1238
- printError(`Rollback compose recreate failed:\n${composeResult.stderr}\n` +
1239
- 'Canonical tags are at :olam-rollback (good); container restart pending. ' +
1240
- 'Manually: `docker compose -f packages/host-cp/compose.yaml up -d --force-recreate olam-host-cp`.');
1241
- process.exitCode = 1;
1242
- return;
1243
- }
1244
- process.stdout.write(` ${pc.dim('recreate auth-service'.padEnd(34))}`);
1245
- const authResult = await recreateAuthService();
1246
- const authDur = `${(authResult.durationMs / 1000).toFixed(1)}s`;
1247
- process.stdout.write(`${authResult.ok ? pc.green('✓') : pc.red('✗')} ${authDur}\n`);
1248
- if (!authResult.ok) {
1249
- printError(`Auth-service recreate failed: ${authResult.error ?? 'unknown'}`);
1250
- process.exitCode = 1;
1251
- return;
1252
- }
1253
- process.stdout.write('\n');
1254
- printSuccess('Rollback complete — canonical tags restored from :olam-rollback');
1255
- }
1256
- finally {
1257
- releaseLock();
1258
- process.removeListener('SIGINT', releaseOnSignal);
1259
- process.removeListener('SIGTERM', releaseOnSignal);
1260
- }
1261
- }
1262
- /**
1263
- * Internal — runs all state-changing upgrade steps inside the lock.
1264
- * Extracted so handleUpgrade can wrap in try/finally without indenting the body.
1265
- */
1266
- async function runUpgradeStepsWithLockHeld(opts, cwd, logRow) {
1267
- // 4a. Branch switch (--branch).
1268
- if (opts.branch !== null) {
1269
- if (isGitDirty(cwd)) {
1270
- printError(`Working tree is dirty. Stash changes first:\n git stash\nThen retry with --branch ${opts.branch}`);
1271
- process.exitCode = 1;
1272
- return;
1273
- }
1274
- const switchResult = runStep(`git checkout ${opts.branch}`, 'git', ['checkout', opts.branch], { cwd });
1275
- if (!switchResult.ok) {
1276
- printError(`Failed to switch to branch ${opts.branch}:\n${switchResult.stderr}`);
1277
- process.exitCode = 1;
1278
- return;
1279
- }
1280
- }
1281
- // 4b. Dirty check before pull (skip if we just switched).
1282
- if (opts.branch === null && isGitDirty(cwd)) {
1283
- printError('Working tree is dirty. Stash or commit changes first:\n git stash\nThen re-run `olam upgrade`.');
1284
- process.exitCode = 1;
1285
- return;
1286
- }
1287
- // 4c. Upstream check.
1288
- if (!hasGitUpstream(cwd)) {
1289
- printError('Current branch has no upstream remote. Set one first:\n git branch --set-upstream-to=origin/<branch>\nThen re-run `olam upgrade`.');
1290
- process.exitCode = 1;
1291
- return;
1292
- }
1293
- const timings = [];
1294
- // Step a: git fetch.
1295
- const fetchResult = runStep('git fetch origin --prune', 'git', ['fetch', 'origin', '--prune'], { cwd });
1296
- timings.push({ label: 'git fetch', durationMs: fetchResult.durationMs });
1297
- if (!fetchResult.ok) {
1298
- printError(`git fetch failed:\n${fetchResult.stderr}`);
1299
- process.exitCode = 1;
1300
- return;
1301
- }
1302
- // Step b: git pull --ff-only.
1303
- const pullResult = runStep('git pull --ff-only', 'git', ['pull', '--ff-only'], { cwd });
1304
- timings.push({ label: 'git pull', durationMs: pullResult.durationMs });
1305
- if (!pullResult.ok) {
1306
- printError(`git pull --ff-only failed:\n${pullResult.stderr}\n` +
1307
- 'If there are conflicts, resolve them manually then re-run `olam upgrade`.');
1308
- process.exitCode = 1;
1309
- return;
1310
- }
1311
- // Phase 2a — A2: capture HEAD SHA AFTER pull (sticky for the run).
1312
- // The pull is what we're upgrading to; capturing before would self-refuse
1313
- // at A6's swap-boundary drift check. _targetSha is consumed by A6 (atomic
1314
- // swap) and B4 (drift refusal). Phase 2a A2 just stashes it; A6/B4 land it
1315
- // load-bearing.
1316
- const _targetSha = captureHeadSha(cwd);
1317
- logRow.sha_target = _targetSha ?? '';
1318
- if (_targetSha === null) {
1319
- logRow.failed_step = 'capture HEAD SHA';
1320
- printError('Failed to capture HEAD SHA via `git rev-parse HEAD`. Aborting upgrade.\n' +
1321
- 'Re-run from a clean git checkout; ensure `git rev-parse HEAD` returns a 40-char SHA.');
1322
- process.exitCode = 1;
1323
- return;
1324
- }
1325
- printInfo('Target SHA', abbreviateSha(_targetSha));
1326
- // Step c: npm install (skip when in sync or --skip-install).
1327
- const installDecision = shouldSkipInstall(opts, cwd);
1328
- if (installDecision.skip) {
1329
- printInfo('npm install', `skipped — ${installDecision.reason}`);
1330
- timings.push({ label: 'npm install', durationMs: 0 });
1331
- }
1332
- else {
1333
- const installResult = runStep('npm install', 'npm', ['install', '--silent', '--no-audit', '--no-fund'], { cwd });
1334
- timings.push({ label: 'npm install', durationMs: installResult.durationMs });
1335
- if (!installResult.ok) {
1336
- printError(`npm install failed:\n${installResult.stderr}\n\n` +
1337
- 'Recovery options:\n' +
1338
- ' • If a native module (e.g. better-sqlite3) failed to compile on your Node version:\n' +
1339
- ' olam upgrade --skip-install\n' +
1340
- ' This skips npm install and uses the existing node_modules.\n' +
1341
- ' • To fix the native module, upgrade it in the relevant package.json,\n' +
1342
- ' run `npm install` manually, then retry `olam upgrade --skip-install`.');
1343
- process.exitCode = 1;
1344
- return;
1345
- }
1346
- }
1347
- // Step d: npm run build (TS workspaces).
1348
- const buildResult = runStep('npm run build', 'npm', ['run', 'build'], { cwd });
1349
- timings.push({ label: 'npm run build', durationMs: buildResult.durationMs });
1350
- if (!buildResult.ok) {
1351
- printError(`npm run build failed:\n${buildResult.stderr}`);
1352
- process.exitCode = 1;
1353
- return;
1354
- }
1355
- // Step e: vite build (SPA).
1356
- const authSecret = readAuthSecret();
1357
- const spaDir = path.join(cwd, 'packages/plan-chat-spa');
1358
- const spaResult = runStep('vite build (SPA)', 'npx', ['vite', 'build'], { cwd: spaDir, env: buildComposeEnv(authSecret, captureGhToken()) });
1359
- timings.push({ label: 'vite build (SPA)', durationMs: spaResult.durationMs });
1360
- if (!spaResult.ok) {
1361
- printError(`vite build failed:\n${spaResult.stderr}`);
1362
- process.exitCode = 1;
1363
- return;
1364
- }
1365
- if (opts.skipImage) {
1366
- // Summary — no docker steps.
1367
- process.stdout.write('\n');
1368
- printSuccess('Source rebuilt (--skip-image: docker not touched)');
1369
- const hash = readBundleHash(cwd);
1370
- if (hash)
1371
- printInfo('Bundle hash', hash);
1372
- printTimings(timings);
1373
- return;
1374
- }
1375
- // Note: A4-A8 step durations are captured in `timings`; the per-step
1376
- // durations_ms snapshot on the log row reflects the full timings array
1377
- // at the end of the run (logRow.durations_ms is updated below at each
1378
- // significant boundary so a mid-run failure is recorded with what we know).
1379
- // Phase 2a — A4: sequential build invocation (auth → devbox → host-cp)
1380
- // with OLAM_TAG=olam-next so each script tags its image transiently per
1381
- // A3's retag block. Order is load-bearing: auth first minimises P3's
1382
- // in-flight 401 window when the recreate (A7) restarts auth before
1383
- // host-cp. Devbox uses inherit-stdio (live tee) per audit F13 since
1384
- // its cold-cache build dominates the 12-22 min budget and silent
1385
- // capture is indistinguishable from a hang.
1386
- // Phase 2b — B3: --no-cache passes through to all three build scripts.
1387
- // The build scripts honor DOCKER_BUILD_NO_CACHE via the build-arg env mechanism
1388
- // documented in their shell. (B3 implementation: forward the env; build
1389
- // scripts treat unset as default cache enabled.)
1390
- const olamTagEnv = { OLAM_TAG: 'olam-next' };
1391
- if (opts.noCache) {
1392
- olamTagEnv.DOCKER_BUILD_NO_CACHE = '1';
1393
- }
1394
- const buildScripts = [
1395
- { label: 'bash build-auth.sh', relPath: 'packages/adapters/src/docker/build-auth.sh', tee: false },
1396
- { label: 'bash build-devbox.sh', relPath: 'packages/adapters/src/docker/build-devbox.sh', tee: true },
1397
- { label: 'bash build-host-cp.sh', relPath: 'packages/adapters/src/docker/build-host-cp.sh', tee: false },
1398
- ];
1399
- // Phase B6: resolve each build script via installRoot + dev-mode check.
1400
- // Outside dev mode, surface the named remedy and exit cleanly. The
1401
- // pull-by-digest default path (no --from-source flag) lands in C6.
1402
- const { resolveBuildScript, MissingBuildScriptError } = await import('../install-root.js');
1403
- for (const step of buildScripts) {
1404
- let scriptPath;
1405
- try {
1406
- scriptPath = resolveBuildScript({ scriptRelPath: step.relPath });
1407
- }
1408
- catch (err) {
1409
- if (err instanceof MissingBuildScriptError) {
1410
- printError(err.message);
1411
- process.exitCode = 1;
1412
- return;
1413
- }
1414
- throw err;
1415
- }
1416
- if (step.tee) {
1417
- // Live-tee variant: stdio: 'inherit' so docker build's apt/bundle/npm
1418
- // progress reaches the operator's terminal in real-time. No stdout
1419
- // capture means we can't include stderr in the failure message —
1420
- // operator already saw the failure inline.
1421
- process.stdout.write(` ${pc.dim(step.label.padEnd(34))}\n`);
1422
- const start = Date.now();
1423
- const result = spawnSync('bash', [scriptPath], {
1424
- stdio: 'inherit',
1425
- cwd,
1426
- env: { ...process.env, ...olamTagEnv },
1427
- });
1428
- const durationMs = Date.now() - start;
1429
- const ok = result.status === 0 && result.error === undefined;
1430
- const dur = `${(durationMs / 1000).toFixed(1)}s`;
1431
- process.stdout.write(` ${pc.dim(step.label.padEnd(34))}${ok ? pc.green('✓') : pc.red('✗')} ${dur}\n`);
1432
- timings.push({ label: step.label, durationMs });
1433
- if (!ok) {
1434
- printError(`${step.label} failed (see output above for details).`);
1435
- process.exitCode = 1;
1436
- return;
1437
- }
1438
- }
1439
- else {
1440
- const result = runStep(step.label, 'bash', [scriptPath], {
1441
- cwd,
1442
- env: olamTagEnv,
1443
- });
1444
- timings.push({ label: step.label, durationMs: result.durationMs });
1445
- logRow.durations_ms[step.label] = result.durationMs;
1446
- if (!result.ok) {
1447
- logRow.failed_step = step.label;
1448
- printError(`${step.label} failed:\n${result.stderr.split('\n').slice(-3).join('\n')}`);
1449
- process.exitCode = 1;
1450
- return;
1451
- }
1452
- }
1453
- }
1454
- // Snapshot durations to logRow so a later-step failure preserves what we know.
1455
- for (const t of timings)
1456
- logRow.durations_ms[t.label] = t.durationMs;
1457
- // Phase 2a — A5: smoke each :olam-next image via docker create + inspect.
1458
- // Catches build-corrupt cases (manifest invalid, OLAM_BUILD_SHA label
1459
- // missing, baked SHA != target SHA) before A6's atomic swap touches
1460
- // canonical tags. Sub-second per image; no port bind.
1461
- const smokeStart = Date.now();
1462
- process.stdout.write(` ${pc.dim('smoke (docker create + inspect)'.padEnd(34))}`);
1463
- const smokeImages = [
1464
- 'olam-auth:olam-next',
1465
- 'olam-devbox:olam-next',
1466
- 'olam-host-cp:olam-next',
1467
- ];
1468
- const smokeResults = smokeImages.map((img) => smokeImage(img, _targetSha));
1469
- const smokeFailures = smokeResults.filter((r) => !r.ok);
1470
- const smokeDurationMs = Date.now() - smokeStart;
1471
- const smokeDur = `${(smokeDurationMs / 1000).toFixed(1)}s`;
1472
- process.stdout.write(`${smokeFailures.length === 0 ? pc.green('✓') : pc.red('✗')} ${smokeDur}\n`);
1473
- timings.push({ label: 'smoke', durationMs: smokeDurationMs });
1474
- if (smokeFailures.length > 0) {
1475
- printError(`Smoke failed for ${smokeFailures.length} of ${smokeResults.length} images:\n` +
1476
- smokeFailures.map((r) => ` - ${r.image}: ${r.error}`).join('\n') +
1477
- '\nCanonical tags (`:latest`/`:local`) untouched. Investigate the failed image(s),' +
1478
- ' then re-run `olam upgrade` (--no-cache if cache-poisoning suspected).');
1479
- process.exitCode = 1;
1480
- return;
1481
- }
1482
- // Phase 2b — B4: SHA drift check at swap boundary.
1483
- // Re-read HEAD via `git rev-parse HEAD` and compare to A2's captured
1484
- // _targetSha. If different, refuse the swap unless --force.
1485
- const swapBoundarySha = captureHeadSha(cwd);
1486
- if (swapBoundarySha !== null && swapBoundarySha !== _targetSha && !opts.force) {
1487
- printError(`HEAD drifted during build window:\n` +
1488
- ` captured (after pull): ${abbreviateSha(_targetSha)}\n` +
1489
- ` current at swap: ${abbreviateSha(swapBoundarySha)}\n\n` +
1490
- 'Operator-driven `git checkout` or `git reset` triggered drift.\n' +
1491
- 'Recovery options:\n' +
1492
- ' • Re-run `olam upgrade` (will rebuild against current HEAD).\n' +
1493
- ' • Pass `--force` to swap anyway (canonical advances to the\n' +
1494
- ' captured-at-pull SHA, NOT current HEAD).');
1495
- process.exitCode = 1;
1496
- return;
1497
- }
1498
- // Phase 2a — A6: atomic 6-tag swap.
1499
- // Phase 1 of swap: preserve previous-good as :olam-rollback (3 ops).
1500
- // Phase 2 of swap: advance canonical to :olam-next (3 ops).
1501
- // Sub-second wall-clock; SIGKILL during Phase 2 is recoverable via
1502
- // `olam upgrade --rollback` (Phase 2b) since :olam-rollback is fully
1503
- // populated before any canonical tag is touched.
1504
- process.stdout.write(` ${pc.dim('atomic 6-tag swap'.padEnd(34))}`);
1505
- const swapStart = Date.now();
1506
- const swapResult = performAtomicSwap(PRODUCTION_SWAP_PLAN);
1507
- const swapDurationMs = Date.now() - swapStart;
1508
- const swapDur = `${(swapDurationMs / 1000).toFixed(1)}s`;
1509
- process.stdout.write(`${swapResult.ok ? pc.green('✓') : pc.red('✗')} ${swapDur}\n`);
1510
- timings.push({ label: 'atomic swap', durationMs: swapDurationMs });
1511
- if (!swapResult.ok) {
1512
- printError(`Atomic swap failed: ${swapResult.summary}`);
1513
- process.exitCode = 1;
1514
- return;
1515
- }
1516
- printInfo('Swap', swapResult.summary);
1517
- // Step g: docker compose up -d --force-recreate.
1518
- const composeFile = findComposeFile();
1519
- process.stdout.write(` ${pc.dim('docker compose recreate'.padEnd(34))}`);
1520
- const composeStart = Date.now();
1521
- const composeResult = runCompose(['up', '-d', '--force-recreate'], composeFile, buildComposeEnv(authSecret, captureGhToken()));
1522
- const composeDurationMs = Date.now() - composeStart;
1523
- const composeOk = composeResult.ok;
1524
- const composeDur = `${(composeDurationMs / 1000).toFixed(1)}s`;
1525
- process.stdout.write(`${composeOk ? pc.green('✓') : pc.red('✗')} ${composeDur}\n`);
1526
- timings.push({ label: 'container recreate', durationMs: composeDurationMs });
1527
- if (!composeOk) {
1528
- // Audit A6-002: at this point canonical tags are at NEW SHA but the stack
1529
- // failed to start. Operator needs to know --rollback is one command away.
1530
- printError(`docker compose up --force-recreate failed:\n${composeResult.stderr}\n\n` +
1531
- 'Canonical tags advanced to new SHA but the stack failed to start.\n' +
1532
- 'Recovery options:\n' +
1533
- ' • Run `olam upgrade --rollback` to restore the prior :olam-rollback set, then investigate.\n' +
1534
- ' • Manually `docker logs olam-host-cp` to diagnose; if recoverable, retry recreate without rollback.');
1535
- process.exitCode = 1;
1536
- return;
1537
- }
1538
- // Phase 2a — A7: recreate auth-service via AuthContainerController.
1539
- // Auth is NOT in compose.yaml; reusing the auth-upgrade.ts recreate pattern
1540
- // (docker stop → docker rm → controller.start() → wait /health). The 25s
1541
- // in-flight 401 window for active world API calls during this recreate is
1542
- // documented in the operator's confirmation prompt (P3 mitigation).
1543
- process.stdout.write(` ${pc.dim('recreate auth-service'.padEnd(34))}`);
1544
- const authResult = await recreateAuthService();
1545
- const authDur = `${(authResult.durationMs / 1000).toFixed(1)}s`;
1546
- process.stdout.write(`${authResult.ok ? pc.green('✓') : pc.red('✗')} ${authDur}\n`);
1547
- timings.push({ label: 'auth recreate', durationMs: authResult.durationMs });
1548
- if (!authResult.ok) {
1549
- printError(`Auth-service recreate failed: ${authResult.error ?? 'unknown'}\n\n` +
1550
- 'Canonical tags advanced to new SHA; host-cp recreated but auth-service is broken.\n' +
1551
- 'Recovery options:\n' +
1552
- ' • Run `olam upgrade --rollback` to restore the prior :olam-rollback set + working stack.\n' +
1553
- ' • Manually: `docker logs olam-auth` to diagnose; `olam auth up` to restart.');
1554
- process.exitCode = 1;
1555
- return;
1556
- }
1557
- // Step h: wait for /health (host-cp readiness probe).
1558
- process.stdout.write(` ${pc.dim('waiting for /health'.padEnd(34))}`);
1559
- const healthStart = Date.now();
1560
- const healthy = await waitForHealth(10_000);
1561
- const healthDurationMs = Date.now() - healthStart;
1562
- const healthDur = `${(healthDurationMs / 1000).toFixed(1)}s`;
1563
- process.stdout.write(`${healthy ? pc.green('✓') : pc.yellow('?')} ${healthDur}\n`);
1564
- timings.push({ label: '/health', durationMs: healthDurationMs });
1565
- if (!healthy) {
1566
- printWarning('Host CP started but /health did not respond within 10s.\n' +
1567
- ' • Check: docker logs olam-host-cp\n' +
1568
- ' • If the new SHA is broken: `olam upgrade --rollback` restores the prior set in <30s.');
1569
- }
1570
- // Phase 2a — A8: poll /api/version/status until all three SHAs match
1571
- // captured target. This is the success criterion for the entire upgrade —
1572
- // round-trips through Phase 1's detection path so the SPA banner clears
1573
- // automatically once the polling loop succeeds.
1574
- //
1575
- // Phase 2.5 polish: 60s wasn't enough on first dogfood — host-cp's /health
1576
- // came back instantly but /api/version/status needed >60s to start serving
1577
- // (cold-boot of express handler chain + first registry-stat reads). 90s
1578
- // gives reliable headroom without making the warn-path feel slow when
1579
- // host-cp is genuinely stuck.
1580
- process.stdout.write(` ${pc.dim('verify /version/status round-trip'.padEnd(34))}`);
1581
- const versionStart = Date.now();
1582
- const versionMatch = await waitForVersionMatch(_targetSha, 90_000);
1583
- const versionDurationMs = Date.now() - versionStart;
1584
- const versionDur = `${(versionDurationMs / 1000).toFixed(1)}s`;
1585
- process.stdout.write(`${versionMatch.matched ? pc.green('✓') : pc.yellow('?')} ${versionDur}\n`);
1586
- timings.push({ label: '/version/status round-trip', durationMs: versionDurationMs });
1587
- if (!versionMatch.matched) {
1588
- // Non-fatal — recreate succeeded; SHA propagation may be slow on cold
1589
- // host-cp boot. Operator gets diagnostic output + can decide whether to
1590
- // re-run, wait, or roll back.
1591
- printWarning(`Version round-trip incomplete after ${(versionDurationMs / 1000).toFixed(0)}s:\n` +
1592
- formatVersionMismatch(_targetSha, versionMatch.snapshot) + '\n' +
1593
- ' • Banner may still show UPDATE AVAILABLE until host-cp\'s next ' +
1594
- 'poll cycle (~60s).\n' +
1595
- ' • If the mismatch persists, `olam upgrade --rollback` restores ' +
1596
- 'the prior :olam-rollback set.');
1597
- }
1598
- // 5. Summary.
1599
- process.stdout.write('\n');
1600
- printSuccess('Upgrade complete');
1601
- const hash = readBundleHash(cwd);
1602
- if (hash)
1603
- printInfo('Bundle hash', hash);
1604
- printTimings(timings);
1605
- }
1606
- function printTimings(timings) {
1607
- const total = timings.reduce((s, t) => s + t.durationMs, 0);
1608
- process.stdout.write('\n');
1609
- for (const t of timings) {
1610
- printInfo(t.label, `${(t.durationMs / 1000).toFixed(1)}s`);
1611
- }
1612
- printInfo('total', `${(total / 1000).toFixed(1)}s`);
1613
- }
1614
- // ── Register ──────────────────────────────────────────────────────
1615
- export function registerUpgrade(program) {
1616
- program
1617
- .command('upgrade')
1618
- .description('Upgrade the local Olam stack to the CLI\'s pinned image digests')
1619
- .option('-y, --yes', 'Skip the confirmation prompt')
1620
- .option('--skip-image', 'Skip docker image rebuild + container recreate (requires --from-source; ignored on default digest path)')
1621
- .option('--skip-install', 'Skip npm install entirely (use existing node_modules as-is). ' +
1622
- 'Requires --from-source; ignored on default digest path.')
1623
- .option('--branch <name>', 'Switch to this branch before pulling (requires --from-source; ignored on default digest path)')
1624
- .option('--rollback', 'Restore canonical tags from the :olam-rollback set (created by the prior successful upgrade).\n' +
1625
- ' No git pull, no build, no smoke — just retag + recreate.')
1626
- .option('--force', 'Bypass HEAD-drift refusal at the swap boundary (requires --from-source; ignored on default digest path).\n' +
1627
- ' Swap advances canonical to the captured-at-pull SHA even if current HEAD differs.')
1628
- .option('--no-cache', 'Pass --no-cache to all three build scripts (DOCKER_BUILD_NO_CACHE=1).\n' +
1629
- ' Useful when retrying after a cache-poisoning failure.')
1630
- .option('--history', 'Print the upgrade history (~/.olam/upgrade.log) and exit.\n' +
1631
- ' No upgrade is performed.')
1632
- .option('-n <count>', 'Number of history rows to print (default 10)', '10')
1633
- .option('--json', 'Emit history as JSONL instead of a table')
1634
- .option('--from-source', 'Build host-cp + auth + devbox from monorepo source (legacy path).\n' +
1635
- ' Requires OLAM_DEV=1 + a `packages/` sibling at the install root.\n' +
1636
- ' Default (no flag): pull pre-built images from ghcr.io by digest.')
1637
- .option('--force-refresh-manifests', '[kubernetes substrate only] Re-apply manifests from ~/.olam/k8s/manifests/ before\n' +
1638
- ' the rollout. Security-sensitive field diffs require\n' +
1639
- ' --accept-security-regression to proceed.')
1640
- .option('--accept-security-regression', '[kubernetes substrate only] Accept security-sensitive field changes in manifests\n' +
1641
- ' when using --force-refresh-manifests. Writes an audit\n' +
1642
- ' entry to ~/.olam/state/manifest-refresh-audit.jsonl.')
1643
- .option('--rotate-secrets', '[kubernetes substrate only — B5] Regenerate every rendered Secret value.\n' +
1644
- ' WARNING: rotation breaks worlds that have cached prior\n' +
1645
- ' tokens. Default behaviour reuses values from\n' +
1646
- ' ~/.olam/k8s-secrets-state.json so reapply is non-destructive.')
1647
- .option('--skip-k8s-bootstrap', '[kubernetes substrate only — B4] Skip the namespace + RBAC + secret bootstrap step.\n' +
1648
- ' Use when these are managed out-of-band (e.g. GitOps).')
1649
- .action(async (opts) => {
1650
- const parsed = parseUpgradeOpts(opts);
1651
- // Phase 1b — C2: substrate-aware routing.
1652
- // Read host.substrate from ~/.olam/config.json BEFORE any other branching.
1653
- // Compose path: unchanged (byte-identical to prior behaviour).
1654
- // Kubernetes path: 8-step flow in upgrade-kubernetes.ts.
1655
- const cfg = readConfig();
1656
- if (cfg.host.substrate === 'kubernetes') {
1657
- try {
1658
- const result = await runUpgradeKubernetes({
1659
- forceRefreshManifests: parsed.forceRefreshManifests,
1660
- acceptSecurityRegression: parsed.acceptSecurityRegression,
1661
- rotateSecrets: opts.rotateSecrets === true,
1662
- // B4: bootstrap defaults ON for the CLI entry point so npm-only
1663
- // operators get namespace + RBAC + secrets auto-applied. Opt-out
1664
- // via --skip-k8s-bootstrap (GitOps-managed clusters).
1665
- runK8sBootstrap: opts.skipK8sBootstrap !== true,
1666
- });
1667
- process.exitCode = result.exitCode;
1668
- }
1669
- catch (err) {
1670
- printError(err instanceof Error ? err.message : String(err));
1671
- process.exitCode = EXIT_GENERIC_ERROR;
1672
- }
1673
- return;
1674
- }
1675
- // compose path — unchanged below this line ────────────────────
1676
- // Phase C — C6: --from-source gates the legacy build path. Default
1677
- // pull-by-digest path lands installed-CLI operators on a fresh
1678
- // image set without a monorepo clone.
1679
- if (parsed.fromSource) {
1680
- if (!isDevMode()) {
1681
- printError(new MissingBuildScriptError('packages/adapters/src/docker/build-*.sh').message);
1682
- process.exitCode = 1;
1683
- return;
1684
- }
1685
- await handleUpgrade(parsed);
1686
- return;
1687
- }
1688
- // --history is read-only; honour it on the default path too so
1689
- // operators don't have to remember --from-source just to read the log.
1690
- if (parsed.history) {
1691
- handleHistory(parseHistoryOpts({ n: parsed.historyN, json: parsed.historyJson }));
1692
- return;
1693
- }
1694
- // Warn about flags that only work with --from-source.
1695
- // These flags are silently ignored on the pull-by-digest path; warn
1696
- // the operator so they don't think the flag took effect.
1697
- const digestPathIgnoredFlags = [
1698
- [parsed.skipImage, '--skip-image'],
1699
- [parsed.skipInstall, '--skip-install'],
1700
- [parsed.branch !== null, '--branch'],
1701
- [parsed.force, '--force'],
1702
- ];
1703
- for (const [active, flag] of digestPathIgnoredFlags) {
1704
- if (active) {
1705
- process.stderr.write(`${pc.yellow('warning')} ${flag} only takes effect with --from-source; ignoring.\n`);
1706
- }
1707
- }
1708
- try {
1709
- const result = await runUpgradePullByDigest({ yes: parsed.yes });
1710
- process.exitCode = result.exitCode;
1711
- }
1712
- catch (err) {
1713
- printError(err instanceof Error ? err.message : String(err));
1714
- process.exitCode = EXIT_GENERIC_ERROR;
1715
- }
1716
- });
1717
- }
1718
- //# sourceMappingURL=upgrade.js.map