@quickchatai/quickreview 1.1.0 → 1.3.1

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 (471) hide show
  1. package/README.md +137 -57
  2. package/bin/qcr.js +1 -1
  3. package/config.example.toml +60 -14
  4. package/deploy/ansible/README.md +20 -12
  5. package/deploy/ansible/inventory.example.ini +1 -1
  6. package/deploy/collect-secrets.sh +1 -1
  7. package/deploy/qcr-update.sh +1 -1
  8. package/dist/extensions/cli/home.js +14 -0
  9. package/dist/extensions/cli/home.js.map +1 -0
  10. package/dist/extensions/cli/login-route.js +5 -0
  11. package/dist/extensions/cli/login-route.js.map +1 -0
  12. package/dist/extensions/cli/selfimprove.js +18 -0
  13. package/dist/extensions/cli/selfimprove.js.map +1 -0
  14. package/dist/extensions/home.js +350 -0
  15. package/dist/extensions/home.js.map +1 -0
  16. package/dist/extensions/login-route.js +180 -0
  17. package/dist/extensions/login-route.js.map +1 -0
  18. package/dist/extensions/onboarding.js +35 -11
  19. package/dist/extensions/onboarding.js.map +1 -1
  20. package/dist/extensions/selfimprove.js +49 -0
  21. package/dist/extensions/selfimprove.js.map +1 -0
  22. package/dist/extensions/setup-scan.js +23 -592
  23. package/dist/extensions/setup-scan.js.map +1 -1
  24. package/dist/extensions/setup.js +2 -47
  25. package/dist/extensions/setup.js.map +1 -1
  26. package/dist/src/archive/restore.js +18 -15
  27. package/dist/src/archive/restore.js.map +1 -1
  28. package/dist/src/botworker.js +13 -2
  29. package/dist/src/botworker.js.map +1 -1
  30. package/dist/src/channelfs.js +3 -26
  31. package/dist/src/channelfs.js.map +1 -1
  32. package/dist/src/ciretry.js +2 -1
  33. package/dist/src/ciretry.js.map +1 -1
  34. package/dist/src/cli.js +54 -61
  35. package/dist/src/cli.js.map +1 -1
  36. package/dist/src/cliops.js +3 -2
  37. package/dist/src/cliops.js.map +1 -1
  38. package/dist/src/commands.js +25 -2
  39. package/dist/src/commands.js.map +1 -1
  40. package/dist/src/config.js +32 -2
  41. package/dist/src/config.js.map +1 -1
  42. package/dist/src/daemon.js +50 -8
  43. package/dist/src/daemon.js.map +1 -1
  44. package/dist/src/debuground.js +1 -0
  45. package/dist/src/debuground.js.map +1 -1
  46. package/dist/src/debugroundpi.js +6 -1
  47. package/dist/src/debugroundpi.js.map +1 -1
  48. package/dist/src/dispatch.js +5 -4
  49. package/dist/src/dispatch.js.map +1 -1
  50. package/dist/src/engine/caserunner-sdk.js +15 -251
  51. package/dist/src/engine/caserunner-sdk.js.map +1 -1
  52. package/dist/src/engine/debugpi.js +17 -5
  53. package/dist/src/engine/debugpi.js.map +1 -1
  54. package/dist/src/execboundary.js +10 -3
  55. package/dist/src/execboundary.js.map +1 -1
  56. package/dist/src/github.js +51 -16
  57. package/dist/src/github.js.map +1 -1
  58. package/dist/src/githubtoken.js +346 -0
  59. package/dist/src/githubtoken.js.map +1 -0
  60. package/dist/src/home/adapters.js +349 -0
  61. package/dist/src/home/adapters.js.map +1 -0
  62. package/dist/src/home/advanced.js +318 -0
  63. package/dist/src/home/advanced.js.map +1 -0
  64. package/dist/src/home/advancedtools.js +57 -0
  65. package/dist/src/home/advancedtools.js.map +1 -0
  66. package/dist/src/home/autoreview.js +325 -0
  67. package/dist/src/home/autoreview.js.map +1 -0
  68. package/dist/src/home/daemonstatus.js +32 -0
  69. package/dist/src/home/daemonstatus.js.map +1 -0
  70. package/dist/src/home/devicelogin.js +61 -0
  71. package/dist/src/home/devicelogin.js.map +1 -0
  72. package/dist/src/home/launch.js +202 -0
  73. package/dist/src/home/launch.js.map +1 -0
  74. package/dist/src/home/loginowner.js +205 -0
  75. package/dist/src/home/loginowner.js.map +1 -0
  76. package/dist/src/home/memory.js +122 -0
  77. package/dist/src/home/memory.js.map +1 -0
  78. package/dist/src/home/model.js +811 -0
  79. package/dist/src/home/model.js.map +1 -0
  80. package/dist/src/home/panel.js +520 -0
  81. package/dist/src/home/panel.js.map +1 -0
  82. package/dist/src/home/ports.js +19 -0
  83. package/dist/src/home/ports.js.map +1 -0
  84. package/dist/src/home/review.js +163 -0
  85. package/dist/src/home/review.js.map +1 -0
  86. package/dist/src/home/reviews.js +478 -0
  87. package/dist/src/home/reviews.js.map +1 -0
  88. package/dist/src/home/setup.js +507 -0
  89. package/dist/src/home/setup.js.map +1 -0
  90. package/dist/src/home/setupadapters.js +214 -0
  91. package/dist/src/home/setupadapters.js.map +1 -0
  92. package/dist/src/home/setupcard.js +199 -0
  93. package/dist/src/home/setupcard.js.map +1 -0
  94. package/dist/src/home/setuptools.js +45 -0
  95. package/dist/src/home/setuptools.js.map +1 -0
  96. package/dist/src/home/spend.js +160 -0
  97. package/dist/src/home/spend.js.map +1 -0
  98. package/dist/src/home/switches.js +209 -0
  99. package/dist/src/home/switches.js.map +1 -0
  100. package/dist/src/home/switchon.js +439 -0
  101. package/dist/src/home/switchon.js.map +1 -0
  102. package/dist/src/home/toolnames.js +41 -0
  103. package/dist/src/home/toolnames.js.map +1 -0
  104. package/dist/src/home/tune.js +341 -0
  105. package/dist/src/home/tune.js.map +1 -0
  106. package/dist/src/home/words.js +7 -0
  107. package/dist/src/home/words.js.map +1 -0
  108. package/dist/src/homes.js +14 -0
  109. package/dist/src/homes.js.map +1 -1
  110. package/dist/src/install.js +1 -1
  111. package/dist/src/install.js.map +1 -1
  112. package/dist/src/ipc/workerspec.js +8 -0
  113. package/dist/src/ipc/workerspec.js.map +1 -1
  114. package/dist/src/live/publish.js +1 -1
  115. package/dist/src/live/publish.js.map +1 -1
  116. package/dist/src/livecli.js +1 -1
  117. package/dist/src/livecli.js.map +1 -1
  118. package/dist/src/loginhint.js +8 -3
  119. package/dist/src/loginhint.js.map +1 -1
  120. package/dist/src/loginrefresh/handover.js +121 -0
  121. package/dist/src/loginrefresh/handover.js.map +1 -0
  122. package/dist/src/loginrefresh/lock.js +54 -3
  123. package/dist/src/loginrefresh/lock.js.map +1 -1
  124. package/dist/src/loginrefresh/pirefresh.js +3 -0
  125. package/dist/src/loginrefresh/pirefresh.js.map +1 -1
  126. package/dist/src/loginrefresh/refresher.js +33 -5
  127. package/dist/src/loginrefresh/refresher.js.map +1 -1
  128. package/dist/src/loginrefresh/supervisor.js +18 -1
  129. package/dist/src/loginrefresh/supervisor.js.map +1 -1
  130. package/dist/src/npmverbs.js +46 -14
  131. package/dist/src/npmverbs.js.map +1 -1
  132. package/dist/src/onboard/choices.js +35 -4
  133. package/dist/src/onboard/choices.js.map +1 -1
  134. package/dist/src/onboard/engine.js +32 -306
  135. package/dist/src/onboard/engine.js.map +1 -1
  136. package/dist/src/onboard/pipin.js +22 -0
  137. package/dist/src/onboard/pipin.js.map +1 -0
  138. package/dist/src/onboard/stepforward.js +41 -0
  139. package/dist/src/onboard/stepforward.js.map +1 -0
  140. package/dist/src/onboard/steps/appslug.js +4 -1
  141. package/dist/src/onboard/steps/appslug.js.map +1 -1
  142. package/dist/src/onboard/steps/bringup.js +3 -2
  143. package/dist/src/onboard/steps/bringup.js.map +1 -1
  144. package/dist/src/onboard/steps/compose.js +29 -0
  145. package/dist/src/onboard/steps/compose.js.map +1 -1
  146. package/dist/src/onboard/steps/configfill.js +120 -39
  147. package/dist/src/onboard/steps/configfill.js.map +1 -1
  148. package/dist/src/onboard/steps/githubaccount.js +595 -0
  149. package/dist/src/onboard/steps/githubaccount.js.map +1 -0
  150. package/dist/src/onboard/steps/githubapp.js +65 -7
  151. package/dist/src/onboard/steps/githubapp.js.map +1 -1
  152. package/dist/src/onboard/steps/golive.js +2 -0
  153. package/dist/src/onboard/steps/golive.js.map +1 -1
  154. package/dist/src/onboard/steps/index.js +11 -0
  155. package/dist/src/onboard/steps/index.js.map +1 -1
  156. package/dist/src/onboard/steps/ingress.js +80 -25
  157. package/dist/src/onboard/steps/ingress.js.map +1 -1
  158. package/dist/src/onboard/steps/llmauth.js +103 -30
  159. package/dist/src/onboard/steps/llmauth.js.map +1 -1
  160. package/dist/src/onboard/steps/loginshare.js +213 -0
  161. package/dist/src/onboard/steps/loginshare.js.map +1 -0
  162. package/dist/src/onboard/steps/memory.js +67 -3
  163. package/dist/src/onboard/steps/memory.js.map +1 -1
  164. package/dist/src/onboard/steps/proof.js +366 -56
  165. package/dist/src/onboard/steps/proof.js.map +1 -1
  166. package/dist/src/onboard/steps/repoconfig.js +72 -0
  167. package/dist/src/onboard/steps/repoconfig.js.map +1 -0
  168. package/dist/src/onboard/steps/topology.js +6 -6
  169. package/dist/src/onboard/steps/topology.js.map +1 -1
  170. package/dist/src/onboard/steps/transport.js +9 -1
  171. package/dist/src/onboard/steps/transport.js.map +1 -1
  172. package/dist/src/onboard/steps/tuiroute.js +102 -0
  173. package/dist/src/onboard/steps/tuiroute.js.map +1 -0
  174. package/dist/src/onboard/steps/verifyup.js +17 -1
  175. package/dist/src/onboard/steps/verifyup.js.map +1 -1
  176. package/dist/src/onboard/toolnames.js +30 -0
  177. package/dist/src/onboard/toolnames.js.map +1 -0
  178. package/dist/src/operatorwrite.js +23 -0
  179. package/dist/src/operatorwrite.js.map +1 -0
  180. package/dist/src/outcomes.js +4 -1
  181. package/dist/src/outcomes.js.map +1 -1
  182. package/dist/src/piagentdir.js +13 -0
  183. package/dist/src/piagentdir.js.map +1 -0
  184. package/dist/src/pilogin.js +31 -16
  185. package/dist/src/pilogin.js.map +1 -1
  186. package/dist/src/posting-journal.js +2 -1
  187. package/dist/src/posting-journal.js.map +1 -1
  188. package/dist/src/posting-outbox.js +6 -2
  189. package/dist/src/posting-outbox.js.map +1 -1
  190. package/dist/src/posting-receipt.js +14 -1
  191. package/dist/src/posting-receipt.js.map +1 -1
  192. package/dist/src/qcrbin.js +16 -11
  193. package/dist/src/qcrbin.js.map +1 -1
  194. package/dist/src/recheck.js +2 -1
  195. package/dist/src/recheck.js.map +1 -1
  196. package/dist/src/reconcile.js +22 -2
  197. package/dist/src/reconcile.js.map +1 -1
  198. package/dist/src/reviewargs.js +122 -0
  199. package/dist/src/reviewargs.js.map +1 -0
  200. package/dist/src/reviewcli.js +7 -161
  201. package/dist/src/reviewcli.js.map +1 -1
  202. package/dist/src/reviewdetach.js +76 -11
  203. package/dist/src/reviewdetach.js.map +1 -1
  204. package/dist/src/reviewhistory.js +32 -6
  205. package/dist/src/reviewhistory.js.map +1 -1
  206. package/dist/src/reviewnow.js +45 -0
  207. package/dist/src/reviewnow.js.map +1 -0
  208. package/dist/src/scancli.js +39 -691
  209. package/dist/src/scancli.js.map +1 -1
  210. package/dist/src/selfimprove.js +66 -0
  211. package/dist/src/selfimprove.js.map +1 -0
  212. package/dist/src/setup/adapters.js +5 -223
  213. package/dist/src/setup/adapters.js.map +1 -1
  214. package/dist/src/setup/caserunner.js +15 -249
  215. package/dist/src/setup/caserunner.js.map +1 -1
  216. package/dist/src/setup/engine.js +32 -341
  217. package/dist/src/setup/engine.js.map +1 -1
  218. package/dist/src/setup/facts.js +3 -592
  219. package/dist/src/setup/facts.js.map +1 -1
  220. package/dist/src/setup/findings.js +3 -947
  221. package/dist/src/setup/findings.js.map +1 -1
  222. package/dist/src/setup/presentation.js +3 -8
  223. package/dist/src/setup/presentation.js.map +1 -1
  224. package/dist/src/setup/scan.js +3 -724
  225. package/dist/src/setup/scan.js.map +1 -1
  226. package/dist/src/setup/scanbuild.js +9 -92
  227. package/dist/src/setup/scanbuild.js.map +1 -1
  228. package/dist/src/setup/scanlease.js +3 -250
  229. package/dist/src/setup/scanlease.js.map +1 -1
  230. package/dist/src/setup/scanreport.js +3 -288
  231. package/dist/src/setup/scanreport.js.map +1 -1
  232. package/dist/src/setup/scanrun.js +3 -1732
  233. package/dist/src/setup/scanrun.js.map +1 -1
  234. package/dist/src/setup/scantree.js +3 -256
  235. package/dist/src/setup/scantree.js.map +1 -1
  236. package/dist/src/setup/stage2request.js +3 -258
  237. package/dist/src/setup/stage2request.js.map +1 -1
  238. package/dist/src/setup/unitcut.js +3 -436
  239. package/dist/src/setup/unitcut.js.map +1 -1
  240. package/dist/src/state.js +2 -0
  241. package/dist/src/state.js.map +1 -1
  242. package/dist/src/statemachine.js +4 -1
  243. package/dist/src/statemachine.js.map +1 -1
  244. package/dist/src/takeover.js +1 -0
  245. package/dist/src/takeover.js.map +1 -1
  246. package/dist/src/takeoverpi.js +15 -1
  247. package/dist/src/takeoverpi.js.map +1 -1
  248. package/dist/src/tui/app.js +49 -0
  249. package/dist/src/tui/app.js.map +1 -1
  250. package/dist/src/tui/board.js +88 -12
  251. package/dist/src/tui/board.js.map +1 -1
  252. package/dist/src/tui/cmdbar.js +4 -1
  253. package/dist/src/tui/cmdbar.js.map +1 -1
  254. package/dist/src/tui/companion.js +76 -11
  255. package/dist/src/tui/companion.js.map +1 -1
  256. package/dist/src/tui/connect.js +7 -1
  257. package/dist/src/tui/connect.js.map +1 -1
  258. package/dist/src/tui/deploytab.js +2 -2
  259. package/dist/src/tui/deploytab.js.map +1 -1
  260. package/dist/src/tui/filters.js +5 -2
  261. package/dist/src/tui/filters.js.map +1 -1
  262. package/dist/src/tui/ghread.js +61 -5
  263. package/dist/src/tui/ghread.js.map +1 -1
  264. package/dist/src/tui/index.js +109 -20
  265. package/dist/src/tui/index.js.map +1 -1
  266. package/dist/src/tui/memforward.js +39 -2
  267. package/dist/src/tui/memforward.js.map +1 -1
  268. package/dist/src/tui/memory.js +3 -16
  269. package/dist/src/tui/memory.js.map +1 -1
  270. package/dist/src/tui/memoryurl.js +22 -0
  271. package/dist/src/tui/memoryurl.js.map +1 -0
  272. package/dist/src/tui/model.js +25 -0
  273. package/dist/src/tui/model.js.map +1 -1
  274. package/dist/src/tui/prpause.js +7 -0
  275. package/dist/src/tui/prpause.js.map +1 -1
  276. package/dist/src/tui/remote.js +7 -1
  277. package/dist/src/tui/remote.js.map +1 -1
  278. package/dist/src/tui/route.js +143 -0
  279. package/dist/src/tui/route.js.map +1 -0
  280. package/dist/src/tui/setupstate.js +32 -0
  281. package/dist/src/tui/setupstate.js.map +1 -0
  282. package/dist/src/tui/sshtriage.js +102 -5
  283. package/dist/src/tui/sshtriage.js.map +1 -1
  284. package/dist/src/tui/theme.js +2 -1
  285. package/dist/src/tui/theme.js.map +1 -1
  286. package/dist/src/tui/unwedge.js +2 -12
  287. package/dist/src/tui/unwedge.js.map +1 -1
  288. package/dist/src/verdicts.js +17 -5
  289. package/dist/src/verdicts.js.map +1 -1
  290. package/dist/src/version.js +1 -1
  291. package/dist/src/version.js.map +1 -1
  292. package/dist/src/webhook.js +12 -1
  293. package/dist/src/webhook.js.map +1 -1
  294. package/dist/src/wire.js +34 -8
  295. package/dist/src/wire.js.map +1 -1
  296. package/dist/src/workerworld.js +11 -1
  297. package/dist/src/workerworld.js.map +1 -1
  298. package/docker/README.md +1 -1
  299. package/docker/companion/entrypoint.sh +20 -1
  300. package/docker/entrypoint.sh +57 -6
  301. package/docker-compose.yml +16 -1
  302. package/extensions/cli/home.ts +14 -0
  303. package/extensions/cli/login-route.ts +10 -0
  304. package/extensions/cli/selfimprove.ts +18 -0
  305. package/extensions/home.ts +484 -0
  306. package/extensions/login-route.ts +220 -0
  307. package/extensions/onboarding.ts +41 -11
  308. package/extensions/selfimprove.ts +65 -0
  309. package/extensions/setup-scan.ts +79 -766
  310. package/extensions/setup.ts +14 -70
  311. package/package.json +6 -4
  312. package/prompts/onboarding.md +58 -18
  313. package/schemas/review-result.v1.json +60 -0
  314. package/scripts/deploy.sh +8 -6
  315. package/scripts/mint-token.sh +25 -4
  316. package/scripts/qcr-host.sh +28 -5
  317. package/scripts/vm-ghcr-login.sh +7 -4
  318. package/src/archive/restore.ts +18 -21
  319. package/src/botworker.ts +13 -2
  320. package/src/channelfs.ts +3 -26
  321. package/src/ciretry.ts +2 -1
  322. package/src/cli.ts +59 -67
  323. package/src/cliops.ts +3 -2
  324. package/src/commands.ts +28 -2
  325. package/src/config.ts +62 -4
  326. package/src/daemon.ts +58 -7
  327. package/src/debuground.ts +3 -0
  328. package/src/debugroundpi.ts +12 -1
  329. package/src/dispatch.ts +5 -4
  330. package/src/engine/caserunner-sdk.ts +29 -314
  331. package/src/engine/debugpi.ts +17 -5
  332. package/src/execboundary.ts +16 -4
  333. package/src/github.ts +57 -15
  334. package/src/githubtoken.ts +435 -0
  335. package/src/home/adapters.ts +418 -0
  336. package/src/home/advanced.ts +421 -0
  337. package/src/home/advancedtools.ts +96 -0
  338. package/src/home/autoreview.ts +385 -0
  339. package/src/home/daemonstatus.ts +55 -0
  340. package/src/home/devicelogin.ts +107 -0
  341. package/src/home/launch.ts +257 -0
  342. package/src/home/loginowner.ts +248 -0
  343. package/src/home/memory.ts +176 -0
  344. package/src/home/model.ts +965 -0
  345. package/src/home/panel.ts +666 -0
  346. package/src/home/ports.ts +338 -0
  347. package/src/home/review.ts +206 -0
  348. package/src/home/reviews.ts +565 -0
  349. package/src/home/setup.ts +689 -0
  350. package/src/home/setupadapters.ts +217 -0
  351. package/src/home/setupcard.ts +288 -0
  352. package/src/home/setuptools.ts +57 -0
  353. package/src/home/spend.ts +194 -0
  354. package/src/home/switches.ts +293 -0
  355. package/src/home/switchon.ts +554 -0
  356. package/src/home/toolnames.ts +46 -0
  357. package/src/home/tune.ts +405 -0
  358. package/src/home/words.ts +8 -0
  359. package/src/homes.ts +17 -0
  360. package/src/install.ts +1 -1
  361. package/src/ipc/workerspec.ts +15 -0
  362. package/src/live/publish.ts +4 -1
  363. package/src/livecli.ts +6 -1
  364. package/src/loginhint.ts +8 -3
  365. package/src/loginrefresh/handover.ts +138 -0
  366. package/src/loginrefresh/lock.ts +72 -3
  367. package/src/loginrefresh/pirefresh.ts +3 -0
  368. package/src/loginrefresh/refresher.ts +34 -5
  369. package/src/loginrefresh/supervisor.ts +25 -2
  370. package/src/npmverbs.ts +55 -13
  371. package/src/onboard/choices.ts +40 -4
  372. package/src/onboard/engine.ts +70 -421
  373. package/src/onboard/pipin.ts +31 -0
  374. package/src/onboard/stepforward.ts +61 -0
  375. package/src/onboard/steps/appslug.ts +4 -1
  376. package/src/onboard/steps/bringup.ts +3 -2
  377. package/src/onboard/steps/compose.ts +31 -0
  378. package/src/onboard/steps/configfill.ts +144 -40
  379. package/src/onboard/steps/githubaccount.ts +716 -0
  380. package/src/onboard/steps/githubapp.ts +92 -8
  381. package/src/onboard/steps/golive.ts +2 -0
  382. package/src/onboard/steps/index.ts +11 -0
  383. package/src/onboard/steps/ingress.ts +98 -31
  384. package/src/onboard/steps/llmauth.ts +114 -29
  385. package/src/onboard/steps/loginshare.ts +258 -0
  386. package/src/onboard/steps/memory.ts +76 -3
  387. package/src/onboard/steps/proof.ts +473 -55
  388. package/src/onboard/steps/repoconfig.ts +85 -0
  389. package/src/onboard/steps/topology.ts +7 -6
  390. package/src/onboard/steps/transport.ts +15 -0
  391. package/src/onboard/steps/tuiroute.ts +119 -0
  392. package/src/onboard/steps/verifyup.ts +20 -1
  393. package/src/onboard/toolnames.ts +34 -0
  394. package/src/operatorwrite.ts +25 -0
  395. package/src/outcomes.ts +8 -1
  396. package/src/piagentdir.ts +12 -0
  397. package/src/pilogin.ts +32 -16
  398. package/src/posting-journal.ts +6 -1
  399. package/src/posting-outbox.ts +10 -2
  400. package/src/posting-receipt.ts +17 -0
  401. package/src/qcrbin.ts +16 -11
  402. package/src/recheck.ts +2 -1
  403. package/src/reconcile.ts +29 -3
  404. package/src/reviewargs.ts +119 -0
  405. package/src/reviewcli.ts +6 -167
  406. package/src/reviewdetach.ts +107 -13
  407. package/src/reviewhistory.ts +20 -6
  408. package/src/reviewnow.ts +76 -0
  409. package/src/scancli.ts +75 -826
  410. package/src/selfimprove.ts +90 -0
  411. package/src/setup/adapters.ts +13 -221
  412. package/src/setup/caserunner.ts +34 -273
  413. package/src/setup/engine.ts +62 -496
  414. package/src/setup/facts.ts +3 -627
  415. package/src/setup/findings.ts +3 -1264
  416. package/src/setup/presentation.ts +3 -18
  417. package/src/setup/scan.ts +3 -1033
  418. package/src/setup/scanbuild.ts +20 -114
  419. package/src/setup/scanlease.ts +3 -297
  420. package/src/setup/scanreport.ts +3 -373
  421. package/src/setup/scanrun.ts +3 -2268
  422. package/src/setup/scantree.ts +3 -259
  423. package/src/setup/stage2request.ts +3 -347
  424. package/src/setup/unitcut.ts +3 -523
  425. package/src/state.ts +43 -0
  426. package/src/statemachine.ts +4 -1
  427. package/src/takeover.ts +5 -0
  428. package/src/takeoverpi.ts +16 -2
  429. package/src/tui/app.ts +47 -0
  430. package/src/tui/board.ts +111 -14
  431. package/src/tui/cmdbar.ts +4 -1
  432. package/src/tui/companion.ts +80 -11
  433. package/src/tui/connect.ts +7 -1
  434. package/src/tui/deploytab.ts +2 -2
  435. package/src/tui/filters.ts +7 -3
  436. package/src/tui/ghread.ts +74 -7
  437. package/src/tui/index.ts +117 -23
  438. package/src/tui/memforward.ts +52 -3
  439. package/src/tui/memory.ts +3 -15
  440. package/src/tui/memoryurl.ts +22 -0
  441. package/src/tui/model.ts +36 -2
  442. package/src/tui/prpause.ts +11 -2
  443. package/src/tui/remote.ts +9 -1
  444. package/src/tui/route.ts +173 -0
  445. package/src/tui/setupstate.ts +56 -0
  446. package/src/tui/sshtriage.ts +127 -6
  447. package/src/tui/theme.ts +2 -1
  448. package/src/tui/unwedge.ts +2 -14
  449. package/src/verdicts.ts +23 -5
  450. package/src/version.ts +1 -1
  451. package/src/webhook.ts +10 -1
  452. package/src/wire.ts +42 -9
  453. package/src/workerworld.ts +16 -2
  454. package/dist/src/live/projection.js +0 -450
  455. package/dist/src/live/projection.js.map +0 -1
  456. package/dist/src/live/read.js +0 -60
  457. package/dist/src/live/read.js.map +0 -1
  458. package/dist/src/onboard/launch.js +0 -83
  459. package/dist/src/onboard/launch.js.map +0 -1
  460. package/dist/src/setup/ledger.js +0 -207
  461. package/dist/src/setup/ledger.js.map +0 -1
  462. package/dist/src/setup/stage2.js +0 -617
  463. package/dist/src/setup/stage2.js.map +0 -1
  464. package/dist/src/setup/textsniff.js +0 -89
  465. package/dist/src/setup/textsniff.js.map +0 -1
  466. package/src/live/projection.ts +0 -491
  467. package/src/live/read.ts +0 -86
  468. package/src/onboard/launch.ts +0 -115
  469. package/src/setup/ledger.ts +0 -324
  470. package/src/setup/stage2.ts +0 -926
  471. package/src/setup/textsniff.ts +0 -95
@@ -1,26 +1,49 @@
1
- // OnboardingEngine — the seam of the onboarding wizard: a checklist state
2
- // machine plus deterministic step implementations behind injected adapters.
3
- // pi is a thin driver over this engine (extensions/onboarding.ts registers one
4
- // tool per step); the engine
5
- // itself is fully drivable from tests and from a non-LLM fallback entry, and
6
- // nothing in here touches the network, Docker, or a real pi except through
7
- // the adapters.
1
+ // OnboardingEngine: the onboarding wizard's checklist, the plugin's setup
2
+ // checklist (@quickchatai/pi-quickreview/src/setupchecklist.ts, the step registry
3
+ // and its engine) bound to this app's adapters and state. pi is a thin
4
+ // driver over it (extensions/onboarding.ts registers one tool per step); the
5
+ // engine itself is fully drivable from tests and from a non-LLM fallback
6
+ // entry, and nothing in here touches the network, Docker, or a real pi except
7
+ // through the adapters.
8
8
  //
9
- // Resumability contract:
10
- // - checklist state persists to state/onboarding/checklist.json after every
11
- // step, tmp+rename atomic (the verdicts.ts convention);
12
- // - startup reconciles claimed state against reality: a step claimed done is
13
- // re-verified with its verify(), never trusted from the file alone;
14
- // - a fully-done checklist re-runs as verify-only (the wizard doubles as a
15
- // diagnostic);
16
- // - a corrupt/unreadable checklist degrades to a fresh one (the resume
17
- // re-verifies everything anyway) — never a crash.
18
- //
19
- // The finish path writes the first-run marker state/companion/first-run.json
20
- // ({"completed_at": "<iso>"}) exactly once — the contract the companion's
21
- // first-run greeting consumes. Keep the path and shape stable.
22
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
23
- import { basename, dirname, join, resolve } from "node:path";
9
+ // What this module adds to the core's checklist:
10
+ // - the wizard's adapter set (OnboardAdapters) and its step type;
11
+ // - where the checklist lives: state/onboarding/checklist.json, tmp+rename
12
+ // atomic, re-verified on resume (the core's resumability contract);
13
+ // - the finish path's first-run marker, state/companion/first-run.json
14
+ // ({"completed_at": "<iso>"}), written exactly once: the contract the
15
+ // companion's first-run greeting consumes. Keep the path and shape
16
+ // stable.
17
+ import {
18
+ SetupChecklist,
19
+ checklistCompleteNow,
20
+ readChecklistFile,
21
+ writeAtomic,
22
+ type ChecklistAdapters,
23
+ type ChecklistFile,
24
+ type SetupStep,
25
+ type StepContext as ChecklistStepContext,
26
+ type StepRegistry,
27
+ type VerifyAllResult,
28
+ } from "@quickchatai/pi-quickreview/src/setupchecklist.js";
29
+ import type { StepForward, StepForwardRequest } from "./stepforward.js";
30
+ import { existsSync } from "node:fs";
31
+ import { join, resolve } from "node:path";
32
+
33
+ export {
34
+ CHECKLIST_VERSION,
35
+ failures,
36
+ readTextOrNull,
37
+ writeAtomic,
38
+ type CheckResult,
39
+ type ChecklistFile,
40
+ type ReconcileReport,
41
+ type StepRecord,
42
+ type StepReport,
43
+ type StepRunResult,
44
+ type StepStatusLine,
45
+ type VerifyAllResult,
46
+ } from "@quickchatai/pi-quickreview/src/setupchecklist.js";
24
47
 
25
48
  // ---- adapters ---------------------------------------------------------------
26
49
 
@@ -40,7 +63,7 @@ export interface ProcessRunner {
40
63
  * fake every field; extensions/onboarding.ts builds the real set. Steps use
41
64
  * fetch (manifest exchange, health probes) and prompter (non-LLM fallback
42
65
  * entry) as they need them; preflight needs neither. */
43
- export interface OnboardAdapters {
66
+ export interface OnboardAdapters extends ChecklistAdapters {
44
67
  /** HTTP seam (GitHub App manifest exchange, health endpoints, …). */
45
68
  fetch: typeof fetch;
46
69
  runner: ProcessRunner;
@@ -60,101 +83,26 @@ export interface OnboardAdapters {
60
83
  * so no step builds a config path itself. */
61
84
  paths: { stateDir: string; configPath: string };
62
85
  log?: (line: string) => void;
86
+ /** Opens the local memory forward for one step call (src/onboard/stepforward.ts):
87
+ * the compose route's memory engine is reachable from this machine only
88
+ * through it. The real assembly sets it; absent (every test), no forward is
89
+ * opened and the step sees whatever already listens. */
90
+ memoryForward?: (req: StepForwardRequest) => Promise<StepForward | null>;
63
91
  }
64
92
 
65
93
  // ---- step vocabulary --------------------------------------------------------
66
94
 
67
- export interface CheckResult {
68
- name: string;
69
- ok: boolean;
70
- detail: string;
71
- }
72
-
73
- /** What run() and verify() both return. `data` (run only) is persisted onto
74
- * the step's checklist record and handed back as ctx.data on later calls. */
75
- export interface StepReport {
76
- ok: boolean;
77
- checks: CheckResult[];
78
- data?: Record<string, unknown>;
79
- }
80
-
81
- export interface StepContext {
82
- adapters: OnboardAdapters;
83
- /** The tool call's inputs — the operator's answer to a step that asks one
84
- * (`{}` for a step that declares no `parameters`, and always on verify()).
85
- * A step that needs an answer and does not get one must return a refusal
86
- * naming the options, never a guess: the wizard offers, the operator picks. */
87
- input: Readonly<Record<string, unknown>>;
88
- /** Recorded operator choices (topology, auth route, captured uid, …). */
89
- choices: Readonly<Record<string, unknown>>;
90
- /** This step's persisted data from a previous run ({} on first run). */
91
- data: Readonly<Record<string, unknown>>;
92
- /** Record an operator choice / derived fact for later steps (persisted with
93
- * the step result). */
94
- recordChoice(key: string, value: unknown): void;
95
- }
95
+ export type StepContext = ChecklistStepContext<OnboardAdapters>;
96
96
 
97
97
  /**
98
98
  * One onboarding step (the registered set is defaultSteps in
99
- * src/onboard/steps/index.ts); the interface is the extension point:
100
- * - `id` must be tool-name-safe ([a-z][a-z0-9_]*) — the pi extension
101
- * registers each step as tool `onboard_<id>`;
102
- * - `explain` is the teach-text the guide relays before running the step;
103
- * - `enabled` gates on recorded choices (e.g. the VM route's steps are
104
- * disabled on the laptop topology); absent ⇒ always enabled;
105
- * - `run` performs the step; `verify` re-checks its outcome against reality
106
- * and must be safe to call any number of times.
99
+ * src/onboard/steps/index.ts): the core's SetupStep over the wizard's
100
+ * adapters. The pi extension registers each step as tool `onboard_<id>`.
107
101
  */
108
- export interface OnboardingStep {
109
- id: string;
110
- title: string;
111
- explain: string;
112
- /** Ascending display/registration order. */
113
- order: number;
114
- /** JSON-schema object the step's pi tool declares, for steps that need the
115
- * operator's answer (a choice, a hostname). Absent ⇒ the tool takes no
116
- * input, which is the common case. */
117
- parameters?: unknown;
118
- enabled?(choices: Readonly<Record<string, unknown>>): boolean;
119
- run(ctx: StepContext): Promise<StepReport>;
120
- verify(ctx: StepContext): Promise<StepReport>;
121
- }
122
-
123
- export function failures(checks: readonly CheckResult[]): string[] {
124
- return checks.filter((c) => !c.ok).map((c) => `${c.name}: ${c.detail}`);
125
- }
102
+ export type OnboardingStep = SetupStep<OnboardAdapters>;
126
103
 
127
104
  // ---- checklist file ---------------------------------------------------------
128
105
 
129
- export const CHECKLIST_VERSION = 1;
130
-
131
- export interface StepRecord {
132
- status: "pending" | "done";
133
- completed_at: string | null;
134
- /** Last successful re-verification (resume / finish). */
135
- verified_at: string | null;
136
- /** Why a claimed-done step was demoted, or other engine notes. */
137
- note: string | null;
138
- data: Record<string, unknown>;
139
- }
140
-
141
- export interface ChecklistFile {
142
- version: number;
143
- created_at: string;
144
- updated_at: string;
145
- /** Set exactly once, by finish() when every enabled step verifies green.
146
- * History — when onboarding first finished; it never moves and never clears. */
147
- completed_at: string | null;
148
- /** When a completed checklist stopped verifying (a demotion, a failed run,
149
- * a refused finish, a newly-enabled unfinished step, a step run again —
150
- * a run changes what the full verification checked); null/absent while the
151
- * completion still holds. A green finish() clears it. Whether onboarding is
152
- * complete now is `completed_at` set and this unset. */
153
- reopened_at?: string | null;
154
- choices: Record<string, unknown>;
155
- steps: Record<string, StepRecord>;
156
- }
157
-
158
106
  export function checklistPath(stateDir: string): string {
159
107
  return join(stateDir, "onboarding", "checklist.json");
160
108
  }
@@ -165,32 +113,9 @@ export function firstRunMarkerPath(stateDir: string): string {
165
113
  return join(stateDir, "companion", "first-run.json");
166
114
  }
167
115
 
168
- function isPlainObject(v: unknown): v is Record<string, unknown> {
169
- return typeof v === "object" && v !== null && !Array.isArray(v);
170
- }
171
-
172
116
  /** Read the checklist; null when absent or unreadable (both mean "fresh"). */
173
117
  export function readChecklist(stateDir: string): ChecklistFile | null {
174
- const path = checklistPath(stateDir);
175
- if (!existsSync(path)) return null;
176
- try {
177
- const raw = JSON.parse(readFileSync(path, "utf8")) as unknown;
178
- if (typeof raw !== "object" || raw === null) return null;
179
- const file = raw as ChecklistFile;
180
- // Every field the engine dereferences is checked here: a file that parses
181
- // but lacks one (hand-edited, foreign) would otherwise throw out of a
182
- // step predicate or record() on every call — a wedge, not a fresh start.
183
- if (typeof file.version !== "number") return null;
184
- if (!isPlainObject(file.choices)) return null;
185
- if (file.completed_at !== null && typeof file.completed_at !== "string") return null;
186
- if (!isPlainObject(file.steps)) return null;
187
- for (const rec of Object.values(file.steps)) {
188
- if (!isPlainObject(rec) || !isPlainObject(rec.data)) return null;
189
- }
190
- return file;
191
- } catch {
192
- return null;
193
- }
118
+ return readChecklistFile(checklistPath(stateDir));
194
119
  }
195
120
 
196
121
  /** Does the checklist file say onboarding finished? The CLI's post-exit
@@ -198,13 +123,7 @@ export function readChecklist(stateDir: string): ChecklistFile | null {
198
123
  * re-verification, so "the file says so" is a verified claim). */
199
124
  export function checklistComplete(stateDir: string): boolean {
200
125
  const file = readChecklist(stateDir);
201
- return file !== null && completeNow(file);
202
- }
203
-
204
- /** Is onboarding complete NOW: finished once, and not reopened since. The one
205
- * reading of the two fields, for the file on disk and the engine's copy. */
206
- function completeNow(file: Pick<ChecklistFile, "completed_at" | "reopened_at">): boolean {
207
- return typeof file.completed_at === "string" && !file.reopened_at;
126
+ return file !== null && checklistCompleteNow(file);
208
127
  }
209
128
 
210
129
  /** `<sandbox root>` — the parent of `paths.stateDir`, by the adapters'
@@ -213,306 +132,36 @@ export function sandboxRoot(paths: { readonly stateDir: string }): string {
213
132
  return resolve(paths.stateDir, "..");
214
133
  }
215
134
 
216
- /** tmp+rename atomic write — the one writer every onboarding file write goes
217
- * through (the checklist, config.toml, .env, the inventory): a reader, the
218
- * daemon's own loader one bind mount away included, never sees a half-written
219
- * file, and an interrupted write leaves the previous file whole. */
220
- export function writeAtomic(path: string, content: string): void {
221
- mkdirSync(dirname(path), { recursive: true });
222
- const tmp = join(dirname(path), `.${basename(path)}.onboard.tmp`);
223
- writeFileSync(tmp, content);
224
- renameSync(tmp, path);
225
- }
226
-
227
- /** A file's text, or null when it cannot be read for any reason. For a read
228
- * where only ABSENCE may be null, read it yourself. */
229
- export function readTextOrNull(path: string): string | null {
230
- try {
231
- return readFileSync(path, "utf8");
232
- } catch {
233
- return null;
234
- }
235
- }
236
-
237
135
  // ---- engine -----------------------------------------------------------------
238
136
 
239
- export interface StepStatusLine {
240
- id: string;
241
- title: string;
242
- explain: string;
243
- status: "pending" | "done" | "disabled";
244
- note: string | null;
245
- }
246
-
247
- export interface ReconcileReport {
248
- /** Claimed-done steps whose verify() held. */
249
- verified: string[];
250
- /** Claimed-done steps whose verify() failed — demoted to pending. */
251
- demoted: Array<{ id: string; failures: string[] }>;
252
- }
253
-
254
- export interface StepRunResult {
255
- id: string;
256
- ok: boolean;
257
- checks: CheckResult[];
258
- failures: string[];
259
- }
260
-
261
- export interface VerifyAllResult {
262
- ok: boolean;
263
- results: Array<{ id: string; ok: boolean; failures: string[] }>;
264
- }
265
-
266
137
  export type FinishResult =
267
138
  | { ok: true; completed_at: string; marker_written: boolean }
268
139
  | { ok: false; results: VerifyAllResult["results"] };
269
140
 
270
- export class OnboardingEngine {
271
- private readonly registry: OnboardingStep[];
272
- private checklist: ChecklistFile;
273
-
141
+ export class OnboardingEngine extends SetupChecklist<OnboardAdapters> {
274
142
  constructor(
275
- steps: OnboardingStep[],
276
- private readonly adapters: OnboardAdapters,
143
+ steps: StepRegistry<OnboardAdapters> | readonly OnboardingStep[],
144
+ adapters: OnboardAdapters,
277
145
  ) {
278
- this.registry = [...steps].sort((a, b) => a.order - b.order);
279
- const loaded = readChecklist(adapters.paths.stateDir);
280
- if (loaded === null && existsSync(checklistPath(adapters.paths.stateDir))) {
281
- adapters.log?.(
282
- "onboarding: checklist file unreadable — starting fresh (recorded choices and step progress were reset)",
283
- );
284
- }
285
- this.checklist = loaded ?? this.fresh();
286
- }
287
-
288
- private fresh(): ChecklistFile {
289
- const now = this.adapters.clock().toISOString();
290
- return {
291
- version: CHECKLIST_VERSION,
292
- created_at: now,
293
- updated_at: now,
294
- completed_at: null,
295
- choices: {},
296
- steps: {},
297
- };
298
- }
299
-
300
- private save(): void {
301
- this.checklist.updated_at = this.adapters.clock().toISOString();
302
- writeAtomic(
303
- checklistPath(this.adapters.paths.stateDir),
304
- `${JSON.stringify(this.checklist, null, 2)}\n`,
305
- );
306
- }
307
-
308
- private record(id: string): StepRecord {
309
- const existing = this.checklist.steps[id];
310
- if (existing) return existing;
311
- const created: StepRecord = {
312
- status: "pending",
313
- completed_at: null,
314
- verified_at: null,
315
- note: null,
316
- data: {},
317
- };
318
- this.checklist.steps[id] = created;
319
- return created;
320
- }
321
-
322
- private ctxFor(id: string, input: Record<string, unknown> = {}): StepContext {
323
- return {
324
- adapters: this.adapters,
325
- input,
326
- choices: this.checklist.choices,
327
- data: this.record(id).data,
328
- recordChoice: (key, value) => {
329
- this.checklist.choices[key] = value;
330
- },
331
- };
332
- }
333
-
334
- /** The registered steps, in order (the pi tools register off this). */
335
- stepDefs(): readonly OnboardingStep[] {
336
- return this.registry;
337
- }
338
-
339
- private enabled(step: OnboardingStep): boolean {
340
- return step.enabled ? step.enabled(this.checklist.choices) : true;
341
- }
342
-
343
- choices(): Readonly<Record<string, unknown>> {
344
- return this.checklist.choices;
345
- }
346
-
347
- isComplete(): boolean {
348
- return completeNow(this.checklist);
349
- }
350
-
351
- /** A completed checklist no longer verifies: withdraw the current-completion
352
- * claim (the post-exit handoff reads it) without touching the history. */
353
- private reopen(): void {
354
- if (this.checklist.completed_at === null || this.checklist.reopened_at) return;
355
- this.checklist.reopened_at = this.adapters.clock().toISOString();
356
- }
357
-
358
- list(): StepStatusLine[] {
359
- return this.registry.map((step) => {
360
- const rec = this.checklist.steps[step.id];
361
- return {
362
- id: step.id,
363
- title: step.title,
364
- explain: step.explain,
365
- status: this.enabled(step) ? (rec?.status ?? "pending") : "disabled",
366
- note: rec?.note ?? null,
367
- };
368
- });
146
+ super(steps, adapters, { file: checklistPath(adapters.paths.stateDir) });
369
147
  }
370
148
 
371
149
  /**
372
- * Resume: re-verify every enabled claimed-done step against reality. A step
373
- * whose verify() fails is demoted to pending with the failures in its note —
374
- * the file's claim is never trusted alone. A fully-done checklist therefore
375
- * re-runs as verify-only. Persists once at the end.
376
- */
377
- async reconcile(): Promise<ReconcileReport> {
378
- const report: ReconcileReport = { verified: [], demoted: [] };
379
- for (const step of this.registry) {
380
- if (!this.enabled(step)) continue;
381
- const rec = this.checklist.steps[step.id];
382
- if (!rec || rec.status !== "done") {
383
- this.reopen();
384
- continue;
385
- }
386
- const outcome = await this.safeVerify(step);
387
- if (outcome.ok) {
388
- rec.verified_at = this.adapters.clock().toISOString();
389
- report.verified.push(step.id);
390
- } else {
391
- const fails = failures(outcome.checks);
392
- rec.status = "pending";
393
- rec.note = `re-verification failed: ${fails.join("; ") || "unknown"}`;
394
- this.reopen();
395
- report.demoted.push({ id: step.id, failures: fails });
396
- }
397
- }
398
- this.save();
399
- return report;
400
- }
401
-
402
- /** Run one step, optionally with the operator's answer (`input`, the pi
403
- * tool's params). Unknown/disabled ids are refusals-as-answers, never
404
- * throws (the caller is an LLM tool belt). Persists after the step. */
405
- async runStep(id: string, input: Record<string, unknown> = {}): Promise<StepRunResult> {
406
- const step = this.registry.find((s) => s.id === id);
407
- if (!step) {
408
- return {
409
- id,
410
- ok: false,
411
- checks: [],
412
- failures: [`unknown step "${id}" — known: ${this.registry.map((s) => s.id).join(", ")}`],
413
- };
414
- }
415
- if (!this.enabled(step)) {
416
- return {
417
- id,
418
- ok: false,
419
- checks: [],
420
- failures: [`step "${id}" is disabled by the recorded choices`],
421
- };
422
- }
423
- const rec = this.record(id);
424
- rec.status = "pending";
425
- this.reopen();
426
- this.save();
427
- let report: StepReport;
428
- try {
429
- report = await step.run(this.ctxFor(id, input));
430
- } catch (err) {
431
- report = {
432
- ok: false,
433
- checks: [{ name: id, ok: false, detail: `step threw: ${String(err)}` }],
434
- };
435
- }
436
- if (report.data) rec.data = { ...rec.data, ...report.data };
437
- if (report.ok) {
438
- rec.status = "done";
439
- rec.completed_at = this.adapters.clock().toISOString();
440
- rec.verified_at = null;
441
- rec.note = null;
442
- // A run acts on the installation (choices, config, deploys) after the
443
- // full verification that backed the completion claim: only the next
444
- // green finish() may make that claim again.
445
- this.reopen();
446
- } else {
447
- rec.status = "pending";
448
- rec.note = `last run failed: ${failures(report.checks).join("; ") || "unknown"}`;
449
- this.reopen();
450
- }
451
- this.save();
452
- return { id, ok: report.ok, checks: report.checks, failures: failures(report.checks) };
453
- }
454
-
455
- private async safeVerify(step: OnboardingStep): Promise<StepReport> {
456
- try {
457
- return await step.verify(this.ctxFor(step.id));
458
- } catch (err) {
459
- return {
460
- ok: false,
461
- checks: [{ name: step.id, ok: false, detail: `verify threw: ${String(err)}` }],
462
- };
463
- }
464
- }
465
-
466
- /**
467
- * Verify every enabled step — done or not. A pending step whose verify()
468
- * holds is promoted to done (its outcome exists in reality, which is the
469
- * only truth the checklist mirrors); a done step whose verify() fails
470
- * reports red here (finish refuses) but demotion is reconcile()'s job.
471
- */
472
- async verifyAll(): Promise<VerifyAllResult> {
473
- const results: VerifyAllResult["results"] = [];
474
- for (const step of this.registry) {
475
- if (!this.enabled(step)) continue;
476
- const outcome = await this.safeVerify(step);
477
- const rec = this.record(step.id);
478
- if (outcome.ok) {
479
- rec.verified_at = this.adapters.clock().toISOString();
480
- if (rec.status !== "done") {
481
- rec.status = "done";
482
- rec.completed_at = rec.completed_at ?? rec.verified_at;
483
- rec.note = "verified green without a recorded run";
484
- }
485
- }
486
- results.push({ id: step.id, ok: outcome.ok, failures: failures(outcome.checks) });
487
- }
488
- this.save();
489
- return { ok: results.every((r) => r.ok), results };
490
- }
491
-
492
- /**
493
- * The finish tool's engine half: re-run the full verification checklist.
494
- * Anything red ⇒ refuse with the failures (the caller returns them as the
495
- * tool result and does not exit). All green ⇒ mark the checklist complete
496
- * and write the first-run marker — once: a marker already on disk is left
150
+ * The finish tool's engine half: the checklist's full verification. Anything
151
+ * red ⇒ refuse with the failures (the caller returns them as the tool
152
+ * result and does not exit). All green ⇒ the checklist is complete, and the
153
+ * first-run marker is written — once: a marker already on disk is left
497
154
  * untouched, so the completed_at the companion greets on never moves.
498
155
  */
499
- async finish(): Promise<FinishResult> {
500
- const all = await this.verifyAll();
501
- if (!all.ok) {
502
- this.reopen();
503
- this.save();
504
- return { ok: false, results: all.results };
505
- }
506
- const completedAt = this.checklist.completed_at ?? this.adapters.clock().toISOString();
507
- this.checklist.completed_at = completedAt;
508
- this.checklist.reopened_at = null;
509
- this.save();
156
+ override async finish(): Promise<FinishResult> {
157
+ const done = await super.finish();
158
+ if (!done.ok) return done;
510
159
  const marker = firstRunMarkerPath(this.adapters.paths.stateDir);
511
160
  let markerWritten = false;
512
161
  if (!existsSync(marker)) {
513
- writeAtomic(marker, `${JSON.stringify({ completed_at: completedAt })}\n`);
162
+ writeAtomic(marker, `${JSON.stringify({ completed_at: done.completed_at })}\n`);
514
163
  markerWritten = true;
515
164
  }
516
- return { ok: true, completed_at: completedAt, marker_written: markerWritten };
165
+ return { ok: true, completed_at: done.completed_at, marker_written: markerWritten };
517
166
  }
518
167
  }
@@ -0,0 +1,31 @@
1
+ // The pinned pi version, read from package.json. A leaf: the daemon reaches it
2
+ // through src/version.ts, so it imports nothing from the onboarding wizard.
3
+ import { readFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+
6
+ export interface PiPin {
7
+ package: string;
8
+ version: string;
9
+ }
10
+
11
+ /** The pin from package.json's "qcrPi" block — the mirror that exists on a bare
12
+ * public clone, where config.toml does not (the wizard writes it later).
13
+ * config.toml [pi] stays the preferred source when it loads (cmdOnboard tries
14
+ * it first); both mirror the same pinned version by rule 7's discipline. */
15
+ export function piPinFromPackageJson(
16
+ root: string,
17
+ readFile: (p: string) => string = (p) => readFileSync(p, "utf8"),
18
+ ): PiPin | null {
19
+ try {
20
+ const pkg = JSON.parse(readFile(join(root, "package.json"))) as {
21
+ qcrPi?: { package?: unknown; version?: unknown };
22
+ };
23
+ const pi = pkg.qcrPi;
24
+ if (pi && typeof pi.package === "string" && typeof pi.version === "string") {
25
+ return { package: pi.package, version: pi.version };
26
+ }
27
+ } catch {
28
+ // fall through — the caller reports the missing pin
29
+ }
30
+ return null;
31
+ }
@@ -0,0 +1,61 @@
1
+ // The memory forward, held open for ONE onboarding step call.
2
+ //
3
+ // On the compose route the memory engine listens only on its own container's
4
+ // loopback; this machine reaches it through the memory forward, the same relay
5
+ // the TUI runs in-process (src/tui/memforward.ts over
6
+ // scripts/companion-memory-forward.mjs). The wizard runs before any TUI, so
7
+ // without this the memory steps' phase B (minting the user keys, the
8
+ // confinement matrix) found nothing listening and sent the operator to a
9
+ // second terminal — and a wizard driven in order never got memory working.
10
+ //
11
+ // A forward that is already listening (`qcr tui`, the systemd unit) is
12
+ // adopted and left alone; one this opens is closed when the step ends, so the
13
+ // wizard leaves nothing running behind it. The real adapter assembly
14
+ // (extensions/onboarding.ts) hands this to the steps; a test's adapters never
15
+ // do, so no test binds a port.
16
+ import { MemoryForward } from "../tui/memforward.js";
17
+
18
+ export interface StepForwardRequest {
19
+ /** Where the step reaches memory (the forward's loopback listener). */
20
+ baseUrl: string;
21
+ /** The checkout holding scripts/. */
22
+ repoRoot: string;
23
+ /** The memory sidecar's container. */
24
+ container: string;
25
+ sudoDocker: boolean;
26
+ log?: (line: string) => void;
27
+ }
28
+
29
+ export interface StepForward {
30
+ /** true when this call started the forward (and stop() closes it). */
31
+ started: boolean;
32
+ detail: string;
33
+ stop(): void;
34
+ }
35
+
36
+ /** Open (or adopt) the local memory forward; null when none could be made to
37
+ * answer, with nothing left bound. */
38
+ export async function openStepMemoryForward(
39
+ req: StepForwardRequest,
40
+ env: NodeJS.ProcessEnv = process.env,
41
+ ): Promise<StepForward | null> {
42
+ const forward = new MemoryForward({
43
+ baseUrl: req.baseUrl,
44
+ repoRoot: req.repoRoot,
45
+ env,
46
+ local: true,
47
+ sudoDocker: req.sudoDocker,
48
+ container: req.container,
49
+ ...(req.log ? { log: req.log } : {}),
50
+ });
51
+ const status = await forward.ensure();
52
+ if (status.state === "owned")
53
+ return {
54
+ started: true,
55
+ detail: `opened a memory forward to ${req.container} for this step; it closes when the step ends, and \`qcr tui\` runs its own`,
56
+ stop: () => forward.stop(),
57
+ };
58
+ if (status.state === "adopted") return { started: false, detail: status.detail, stop: () => {} };
59
+ forward.stop();
60
+ return null;
61
+ }
@@ -50,7 +50,7 @@ import {
50
50
  tomlString,
51
51
  type TomlEdit,
52
52
  } from "./configfill.js";
53
- import { CHOICE_KEYS, botLoginFor } from "../choices.js";
53
+ import { CHOICE_KEYS, botLoginFor, userAuthRecorded } from "../choices.js";
54
54
  import { api, readCredentials } from "./githubapp.js";
55
55
 
56
56
  export const APP_SLUG_STEP_ID = "app_slug_reconcile";
@@ -254,6 +254,9 @@ export function appSlugStep(opts: AppSlugStepOptions = {}): OnboardingStep {
254
254
  "slug, so this step asks the App itself who it is and corrects [github] app_slug and bot_login to match. It matters more " +
255
255
  "than it looks: bot_login is how the reviewer recognises its own reviews and comments, and a wrong one fails silently.",
256
256
  order: 65,
257
+ // There is no App slug on the own-account route: `[github] app_slug` is ""
258
+ // and bot_login is the person's login (the github_account step writes both).
259
+ enabled: (choices) => !userAuthRecorded(choices),
257
260
  async run(ctx): Promise<StepReport> {
258
261
  return reconcile(ctx, true);
259
262
  },