@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,21 +1,17 @@
1
- // `qcr scan` — the companion's scan tools (extensions/setup-scan.ts) as a
2
- // scriptable CLI verb. Every subcommand calls what its tool calls (the
3
- // ScanRunner, the findings store, the triage, and the shared verbs beside the
4
- // tools) and renders it twice: the human default, worded for the CLI, and a
1
+ // `qcr scan` — the scan's verbs (src/setup/scanverbs.ts, the same ones the
2
+ // companion's scan tools in extensions/setup-scan.ts call) as a scriptable
3
+ // CLI verb, assembled over this machine's state home and config. Every
4
+ // subcommand renders twice: the human default, worded for the CLI, and a
5
5
  // `--json` object on one line for scripts.
6
6
  //
7
- // The promises are the runner's own and hold here unchanged: `start` must
8
- // quote the key `preview` handed out (a plan, sha, price or setting that
9
- // moved since is refused), and a run stops hard at the estimate it showed.
10
- //
11
- // Where a run is driven: `start` (and `stage2 start`) launches a detached
12
- // `qcr scan … --foreground` driver, waits until it has recorded the run (or
13
- // refused), and returns. With `[scan] sandbox = "docker"` (the default) the
14
- // driver runs in a throwaway container from the scan sandbox image
15
- // (src/scanbox.ts); with `"host"` it is a detached process on this machine.
16
- // Either way its records are the state home's, so every other subcommand
17
- // reads them here, on the host. The driver runs the
18
- // cases, answers the stop channel (the TUI's `[8] scan` stop and
7
+ // What is the app's here, around the verbs: where a run is driven. `start`
8
+ // (and `stage2 start`) launches a detached `qcr scan … --foreground` driver,
9
+ // waits until it has recorded the run (or refused), and returns. With
10
+ // `[scan] sandbox = "docker"` (the default) the driver runs in a throwaway
11
+ // container from the scan sandbox image (src/scanbox.ts); with `"host"` it is
12
+ // a detached process on this machine. Either way its records are the state
13
+ // home's, so every other subcommand reads them here, on the host. The driver
14
+ // runs the cases, answers the stop channel (the TUI's `[8] scan` stop and
19
15
  // `qcr scan stop` alike), and holds the driver lease (src/setup/scanlease.ts)
20
16
  // so that the companion and other drivers read its run as running and start
21
17
  // nothing beside it. `--foreground` drives in the calling process instead,
@@ -23,40 +19,45 @@
23
19
  // driven by anyone.
24
20
  //
25
21
  // Host-side beside them: `units` — the setup belt's unit-cut tools
26
- // (extensions/setup.ts) — and the sandbox's `login` and `doctor`.
27
- //
28
- // Exit codes: 0 done; 1 refused (or a waited-for run that did not end
29
- // complete); 2 usage; 3 `status --wait` gave up at its `--timeout`.
30
- import { dismissedLine } from "./setup/presentation.js";
22
+ // (extensions/setup.ts) — and the sandbox's `login`, `doctor` and `rebuild`.
31
23
  import { spawn, spawnSync } from "node:child_process";
32
- import { closeSync, existsSync, openSync, readFileSync } from "node:fs";
24
+ import { closeSync, existsSync, openSync } from "node:fs";
33
25
  import { join } from "node:path";
34
- import { CLI_VERBS, FINDINGS_LIMIT, startedDetails, answerMatch, findingsFilter, listFindings, previewDetails, proposalDetails, renderFindings, renderPreview, renderProposal, renderQuestion, renderStatus, renderSuggestions, stage2StartedLine, startedLine, stopScan, } from "../extensions/setup-scan.js";
35
- import { CUT_CLI_VERBS, renderCut, renderDraft, renderStatus as renderCutStatus, } from "../extensions/setup.js";
36
26
  import { loadConfig, sandboxRoot } from "./config.js";
37
27
  import { stateHome } from "./install.js";
38
28
  import { IN_SANDBOX_ENV, STARTED_FILE_ENV, renderDoctor, writeStartedFile, } from "./scanbox.js";
39
29
  import { DETACH_TIMEOUT_MS, DRIVER_LOG, DRIVER_LOG_MAX, prepareDriverLog, } from "./scandriverlog.js";
40
- import { dismissedLookalike, validityOf } from "./setup/scanreport.js";
41
- import { lensTiers, stage2Lenses, MAX_STAGE2_ROWS } from "./setup/stage2.js";
30
+ import { runScanVerb, usage, } from "@quickchatai/pi-quickreview/src/scan/scanverbs.js";
42
31
  import { SCAN_SCHEMA_VERSION, withSchemaVersion, } from "@quickchatai/pi-quickreview/src/contract.js";
43
- export const SCAN_USAGE = "usage: qcr scan preview [--sha REF] [--delta] | start --key K [--sha REF] [--delta] [--foreground] | " +
44
- "status [--wait [--timeout S]] | stop | " +
45
- "findings [--state S] [--lens L] [--unit U] [--severity P] [--validity V] [--doubted|--not-doubted] [--limit N] | " +
46
- "matches | match --key K [OLD=NEW…] | suggestions | " +
47
- "stage2 propose [--rows FILE|-] [--budget USD] [--deep|--standard] [--sha REF] | " +
48
- "stage2 start --key K [--rows ID,ID…] [--foreground] | dismiss FP… --reason TEXT | " +
49
- "units show | units propose [--file F|-] | units adjust --file F|- | units save | login [--provider P] | doctor | rebuild (every subcommand takes --json, and --sandbox host|docker over [scan] sandbox)";
32
+ // The verbs themselves, and their JSON shapes, are src/setup/scanverbs.ts's.
33
+ export { POLL_MS, SCAN_USAGE, findingJson, parseScanArgs, runJson, } from "@quickchatai/pi-quickreview/src/scan/scanverbs.js";
34
+ /** Run one `qcr scan` subcommand; resolves its exit code. */
35
+ export function runScanCli(rest, json, deps) {
36
+ const { doctor, ...rest2 } = deps;
37
+ return runScanVerb(rest, json, {
38
+ ...rest2,
39
+ driverLog: join(deps.home, DRIVER_LOG),
40
+ ...(doctor
41
+ ? {
42
+ doctor: async () => {
43
+ const r = await doctor();
44
+ return {
45
+ ok: r.ok,
46
+ human: renderDoctor(r),
47
+ data: r,
48
+ };
49
+ },
50
+ }
51
+ : {}),
52
+ });
53
+ }
50
54
  export { DETACH_TIMEOUT_MS, DRIVER_LOG, DRIVER_LOG_MAX };
51
- /** How often `status --wait` and `stop` look again. */
52
- export const POLL_MS = 2000;
53
55
  /** The SDK leaf host's private agent dir, under the state home: its own models
54
56
  * list lives here, never in the operator's pi home. */
55
57
  const SCAN_SDK_AGENT_DIR = "scan/sdk-agent";
56
- const usage = (why) => new Error(why ? `${SCAN_USAGE}\n(${why})` : SCAN_USAGE);
57
58
  /** `--sandbox host|docker`, taken off any subcommand's arguments: where this
58
- * call runs a scan, over `[scan] sandbox`. The pi plugin's /scan passes
59
- * `host` unless the person asked for docker. */
59
+ * call runs a scan, over `[scan] sandbox`. The pi plugin's /qcr-scan never
60
+ * comes here: it runs the plugin's own engine in its pi. */
60
61
  export function takeSandboxFlag(rest) {
61
62
  const out = [];
62
63
  let sandbox;
@@ -74,659 +75,6 @@ export function takeSandboxFlag(rest) {
74
75
  }
75
76
  return sandbox ? { rest: out, sandbox } : { rest: out };
76
77
  }
77
- /** `qcr scan`'s arguments (`--json` taken off before). An unknown token is
78
- * refused, so a mistyped flag never runs something else. */
79
- export function parseScanArgs(rest) {
80
- const [first, ...more] = rest;
81
- let sub = first;
82
- let args = more;
83
- if (first === "stage2" || first === "units") {
84
- sub = `${first}-${more[0] ?? ""}`;
85
- args = more.slice(1);
86
- }
87
- const flags = new Map();
88
- const positional = [];
89
- const VALUED = new Set([
90
- "--sha",
91
- "--key",
92
- "--timeout",
93
- "--state",
94
- "--lens",
95
- "--unit",
96
- "--severity",
97
- "--validity",
98
- "--limit",
99
- "--rows",
100
- "--budget",
101
- "--reason",
102
- "--file",
103
- "--provider",
104
- ]);
105
- const BARE = new Set([
106
- "--delta",
107
- "--foreground",
108
- "--wait",
109
- "--doubted",
110
- "--not-doubted",
111
- "--deep",
112
- "--standard",
113
- ]);
114
- for (let i = 0; i < args.length; i++) {
115
- const a = args[i];
116
- if (VALUED.has(a)) {
117
- const v = args[++i];
118
- if (v === undefined || v.startsWith("--"))
119
- throw usage();
120
- flags.set(a, v);
121
- }
122
- else if (BARE.has(a))
123
- flags.set(a, true);
124
- else if (a.startsWith("--"))
125
- throw usage();
126
- else
127
- positional.push(a);
128
- }
129
- const allowed = (...names) => {
130
- for (const k of flags.keys())
131
- if (!names.includes(k))
132
- throw usage();
133
- };
134
- const noPositional = () => {
135
- if (positional.length > 0)
136
- throw usage();
137
- };
138
- const str = (k) => {
139
- const v = flags.get(k);
140
- return typeof v === "string" ? v : undefined;
141
- };
142
- const num = (k, ok) => {
143
- const v = str(k);
144
- if (v === undefined)
145
- return undefined;
146
- const n = Number(v);
147
- if (!Number.isFinite(n) || !ok(n))
148
- throw usage();
149
- return n;
150
- };
151
- const sha = () => (str("--sha") !== undefined ? { sha: str("--sha") } : {});
152
- switch (sub) {
153
- case "preview":
154
- allowed("--sha", "--delta");
155
- noPositional();
156
- return { verb: "preview", ...sha(), delta: flags.has("--delta") };
157
- case "start": {
158
- allowed("--key", "--sha", "--delta", "--foreground");
159
- noPositional();
160
- const key = str("--key");
161
- if (key === undefined)
162
- throw usage("start needs --key — the key a preview showed");
163
- return {
164
- verb: "start",
165
- key,
166
- ...sha(),
167
- delta: flags.has("--delta"),
168
- foreground: flags.has("--foreground"),
169
- };
170
- }
171
- case "status": {
172
- allowed("--wait", "--timeout");
173
- noPositional();
174
- const timeout = num("--timeout", (n) => n > 0);
175
- if (timeout !== undefined && !flags.has("--wait"))
176
- throw usage("--timeout needs --wait");
177
- return {
178
- verb: "status",
179
- wait: flags.has("--wait"),
180
- ...(timeout !== undefined ? { timeout } : {}),
181
- };
182
- }
183
- case "stop":
184
- allowed();
185
- noPositional();
186
- return { verb: "stop" };
187
- case "findings": {
188
- allowed("--state", "--lens", "--unit", "--severity", "--validity", "--doubted", "--not-doubted", "--limit");
189
- noPositional();
190
- if (flags.has("--doubted") && flags.has("--not-doubted"))
191
- throw usage();
192
- const query = {};
193
- for (const k of ["state", "lens", "unit", "severity", "validity"]) {
194
- const v = str(`--${k}`);
195
- if (v !== undefined)
196
- query[k] = v;
197
- }
198
- if (flags.has("--doubted"))
199
- query.doubted = true;
200
- if (flags.has("--not-doubted"))
201
- query.doubted = false;
202
- const limit = num("--limit", (n) => Number.isInteger(n) && n > 0);
203
- if (limit !== undefined)
204
- query.limit = limit;
205
- return { verb: "findings", query };
206
- }
207
- case "matches":
208
- allowed();
209
- noPositional();
210
- return { verb: "matches" };
211
- case "match": {
212
- allowed("--key");
213
- const key = str("--key");
214
- if (key === undefined)
215
- throw usage("match needs --key — the key the match question showed");
216
- const pairs = positional.map((p) => {
217
- const m = /^([0-9a-f]+)=([0-9a-f]+)$/i.exec(p);
218
- if (!m)
219
- throw usage(`a pair is OLD=NEW (two fingerprints), not ${JSON.stringify(p)}`);
220
- return { old: m[1], new: m[2] };
221
- });
222
- return { verb: "match", key, pairs };
223
- }
224
- case "suggestions":
225
- allowed();
226
- noPositional();
227
- return { verb: "suggestions" };
228
- case "stage2-propose": {
229
- allowed("--rows", "--budget", "--deep", "--standard", "--sha");
230
- noPositional();
231
- if (flags.has("--deep") && flags.has("--standard"))
232
- throw usage();
233
- const budget = num("--budget", (n) => n > 0);
234
- return {
235
- verb: "stage2-propose",
236
- ...(str("--rows") !== undefined ? { rows: str("--rows") } : {}),
237
- ...(budget !== undefined ? { budget } : {}),
238
- ...(flags.has("--deep") ? { deep: true } : flags.has("--standard") ? { deep: false } : {}),
239
- ...sha(),
240
- };
241
- }
242
- case "stage2-start": {
243
- allowed("--key", "--rows", "--foreground");
244
- noPositional();
245
- const key = str("--key");
246
- if (key === undefined)
247
- throw usage("stage2 start needs --key — the key the stage 2 proposal showed");
248
- const rows = str("--rows");
249
- return {
250
- verb: "stage2-start",
251
- key,
252
- ...(rows !== undefined ? { rows: rows.split(",").map((r) => r.trim()) } : {}),
253
- foreground: flags.has("--foreground"),
254
- };
255
- }
256
- case "dismiss": {
257
- allowed("--reason");
258
- const reason = str("--reason");
259
- if (reason === undefined)
260
- throw usage("dismiss needs --reason — it is what PR rounds read");
261
- if (positional.length === 0)
262
- throw usage("dismiss needs at least one fingerprint");
263
- return { verb: "dismiss", fingerprints: positional, reason };
264
- }
265
- case "login": {
266
- allowed("--provider");
267
- noPositional();
268
- const provider = str("--provider");
269
- return { verb: "login", ...(provider !== undefined ? { provider } : {}) };
270
- }
271
- case "units-show":
272
- case "units-save":
273
- case "doctor":
274
- case "rebuild":
275
- allowed();
276
- noPositional();
277
- return { verb: sub };
278
- case "units-propose": {
279
- allowed("--file");
280
- noPositional();
281
- const file = str("--file");
282
- return { verb: "units-propose", ...(file !== undefined ? { file } : {}) };
283
- }
284
- case "units-adjust": {
285
- allowed("--file");
286
- noPositional();
287
- const file = str("--file");
288
- if (file === undefined)
289
- throw usage("units adjust needs --file — a JSON array of adjust ops (or - for stdin)");
290
- return { verb: "units-adjust", file };
291
- }
292
- default:
293
- throw usage();
294
- }
295
- }
296
- export function findingJson(f) {
297
- const look = dismissedLookalike(f);
298
- return {
299
- fingerprint: f.fingerprint,
300
- file: f.path,
301
- range: { start: f.start, end: f.end },
302
- lens: f.lens,
303
- lenses: f.lenses ?? [f.lens],
304
- unit: f.unit,
305
- kind: f.kind,
306
- severity: f.severity,
307
- validity: validityOf(f),
308
- state: f.state,
309
- claim: f.claim,
310
- doubted: f.doubt !== null,
311
- doubt: f.doubt
312
- ? { word: f.doubt.word, reason: f.doubt.reason, class: f.doubt.class ?? null }
313
- : null,
314
- looks_like_dismissed: look ? { fingerprint: look.fingerprint, reason: look.reason } : null,
315
- dismissal_reason: f.dismissal?.reason ?? null,
316
- ticket: f.ticket?.ref ?? null,
317
- first_seen: { run: f.first_seen.run, sha: f.first_seen.sha },
318
- last_seen: { run: f.last_seen.run, sha: f.last_seen.sha },
319
- };
320
- }
321
- /** A run as `status --json` and a foreground start carry it. */
322
- export function runJson(run) {
323
- const n = (k) => run.cases.reduce((t, c) => t + c[k], 0);
324
- return {
325
- id: run.id,
326
- mode: run.mode,
327
- sha: run.sha,
328
- state: run.state,
329
- display: run.display ?? run.state,
330
- depth: run.settings.depth ?? "standard",
331
- started_at: run.started_at,
332
- finished_at: run.finished_at,
333
- cap_usd: run.cap_usd,
334
- estimate_usd: run.estimate_usd,
335
- spent_usd: run.spent_usd,
336
- unpriced_cases: run.unpriced_cases,
337
- cases_total: run.cases.length,
338
- cases_done: run.cases.filter((c) => c.status === "done").length,
339
- cases_failed: run.cases.filter((c) => c.status === "failed").length,
340
- findings: n("findings"),
341
- doubted: n("doubted"),
342
- matching: run.matching ?? null,
343
- tickets_closed: run.tickets_closed ?? 0,
344
- problems: run.problems,
345
- cases: run.cases.map((c) => ({
346
- id: c.id,
347
- unit: c.unit,
348
- status: c.status,
349
- findings: c.findings,
350
- doubted: c.doubted,
351
- cost_usd: c.cost_usd,
352
- error: c.error,
353
- ...(c.lens !== undefined ? { lens: c.lens, tier: c.tier ?? null } : {}),
354
- })),
355
- };
356
- }
357
- function proposalJson(p) {
358
- return {
359
- ...proposalDetails(p),
360
- budget_usd: p.budget_usd,
361
- over_budget_usd: p.over_budget_usd,
362
- validator: p.validator,
363
- row_list: p.rows.map((r) => ({
364
- id: r.id,
365
- lens: r.lens,
366
- tier: r.tier,
367
- units: r.units,
368
- files: r.files.map((f) => f.path),
369
- tests: r.tests.map((f) => f.path),
370
- lines: r.lines,
371
- estimate_usd: r.estimate_usd,
372
- status: r.status,
373
- floor: r.floor === true,
374
- reason: r.reason,
375
- })),
376
- };
377
- }
378
- /** Run one `qcr scan` subcommand; resolves its exit code. */
379
- export async function runScanCli(rest, json, deps) {
380
- const emit = (code, human, data) => {
381
- deps.out(json ? JSON.stringify(withSchemaVersion(SCAN_SCHEMA_VERSION, data)) : human);
382
- return code;
383
- };
384
- const refusal = (what, errors, code = 1) => emit(code, `${what} refused:\n${errors.map((e) => `✗ ${e}`).join("\n")}`, {
385
- ok: false,
386
- errors,
387
- });
388
- let cmd;
389
- try {
390
- cmd = parseScanArgs(rest);
391
- }
392
- catch (err) {
393
- const msg = err instanceof Error ? err.message : String(err);
394
- return emit(2, msg, { ok: false, errors: [msg] });
395
- }
396
- const { scan } = deps;
397
- switch (cmd.verb) {
398
- case "preview": {
399
- const r = await scan.preview({ ...(cmd.sha ? { sha: cmd.sha } : {}), delta: cmd.delta });
400
- if (!r.ok)
401
- return refusal("Scan preview", r.errors);
402
- const p = r.preview;
403
- const start = p.blockers.length > 0
404
- ? null
405
- : ["--key", p.key, "--sha", p.sha, ...(p.mode === "delta" ? ["--delta"] : [])];
406
- return emit(0, renderPreview(p, CLI_VERBS), {
407
- ok: true,
408
- preview: previewDetails(p),
409
- start,
410
- });
411
- }
412
- case "start":
413
- case "stage2-start": {
414
- const stage2 = cmd.verb === "stage2-start";
415
- const argv = cmd.verb === "start"
416
- ? [
417
- "start",
418
- "--key",
419
- cmd.key,
420
- ...(cmd.sha ? ["--sha", cmd.sha] : []),
421
- ...(cmd.delta ? ["--delta"] : []),
422
- ]
423
- : [
424
- "stage2",
425
- "start",
426
- "--key",
427
- cmd.key,
428
- ...(cmd.rows ? ["--rows", cmd.rows.join(",")] : []),
429
- ];
430
- const what = stage2 ? "Stage 2 start" : "Scan start";
431
- const line = (run) => stage2
432
- ? stage2StartedLine(run, scan.stage2Proposal().proposal?.runnable ?? null, cmd.verb === "stage2-start" && cmd.rows !== undefined, CLI_VERBS)
433
- : startedLine(run, CLI_VERBS);
434
- if (!cmd.foreground) {
435
- const d = await deps.detach(argv);
436
- if (!d.ok)
437
- return refusal(what, d.errors);
438
- return emit(0, `${line(d.run)}\nDriver output: ${d.log}${d.container ? `\nContainer: ${d.container}` : ""}`, {
439
- ok: true,
440
- run: startedDetails(d.run),
441
- log: d.log,
442
- ...(d.container ? { container: d.container } : {}),
443
- });
444
- }
445
- const r = cmd.verb === "start"
446
- ? await scan.start({
447
- key: cmd.key,
448
- ...(cmd.sha ? { sha: cmd.sha } : {}),
449
- delta: cmd.delta,
450
- })
451
- : await scan.startStage2({ key: cmd.key, ...(cmd.rows ? { rows: cmd.rows } : {}) });
452
- if (!r.ok) {
453
- deps.onStart?.({ ok: false, errors: r.errors });
454
- return refusal(what, r.errors);
455
- }
456
- deps.onStart?.({ ok: true, run: r.run, log: join(deps.home, DRIVER_LOG) });
457
- if (!json)
458
- deps.out(line(r.run));
459
- // A pending promise alone keeps no process alive: hold it open until
460
- // the run ends.
461
- const keep = setInterval(() => { }, 60_000);
462
- const unwatch = deps.watchStops?.(scan) ?? null;
463
- try {
464
- await scan.idle();
465
- }
466
- finally {
467
- unwatch?.();
468
- clearInterval(keep);
469
- }
470
- const s = scan.status();
471
- const complete = s.run?.display === "complete";
472
- return emit(complete ? 0 : 1, renderStatus(s, CLI_VERBS), {
473
- ok: complete,
474
- run: s.run ? runJson(s.run) : null,
475
- });
476
- }
477
- case "status": {
478
- let s = scan.status();
479
- const t0 = deps.now();
480
- while (cmd.wait && s.run?.display === "running") {
481
- if (cmd.timeout !== undefined && deps.now() - t0 >= cmd.timeout * 1000) {
482
- return emit(3, `${renderStatus(s, CLI_VERBS)}\nstill running after ${cmd.timeout}s`, {
483
- ...statusJson(s),
484
- timed_out: true,
485
- });
486
- }
487
- await deps.sleep(POLL_MS);
488
- s = scan.status();
489
- }
490
- const code = cmd.wait
491
- ? s.run === null || s.run.display === "complete"
492
- ? 0
493
- : 1
494
- : s.ok
495
- ? 0
496
- : 1;
497
- return emit(code, renderStatus(s, CLI_VERBS), statusJson(s));
498
- }
499
- case "stop":
500
- return stop(deps, emit, refusal);
501
- case "findings": {
502
- const f = findingsFilter(cmd.query);
503
- if (!f.ok)
504
- return emit(2, f.error, { ok: false, errors: [f.error] });
505
- const listed = listFindings(deps.findings, f.filter);
506
- if (!listed.ok)
507
- return refusal("Findings", [listed.error]);
508
- const all = listed.findings;
509
- const shown = all.slice(0, cmd.query.limit ?? (json ? all.length : FINDINGS_LIMIT));
510
- return emit(0, renderFindings(shown, all.length), {
511
- ok: true,
512
- total: all.length,
513
- validity: listed.validity,
514
- findings: shown.map(findingJson),
515
- });
516
- }
517
- case "matches": {
518
- const { question, error } = deps.findings.question();
519
- if (error !== null)
520
- return refusal("Match question", [error]);
521
- return emit(0, renderQuestion(question, CLI_VERBS), {
522
- ok: true,
523
- question: question
524
- ? {
525
- key: question.key,
526
- run: question.run,
527
- sha: question.sha,
528
- unreported: question.unreported.map(findingJson),
529
- fresh: question.fresh.map(findingJson),
530
- }
531
- : null,
532
- });
533
- }
534
- case "match": {
535
- const r = await answerMatch(deps.findings, { key: cmd.key, pairs: cmd.pairs }, deps.afterMatch);
536
- if (!r.ok)
537
- return refusal("Match", r.errors);
538
- const { tickets, lines, ...result } = r;
539
- return emit(0, lines.join("\n"), {
540
- ...result,
541
- tickets_closed: tickets?.closed.length ?? 0,
542
- });
543
- }
544
- case "suggestions": {
545
- const { suggestions, error } = scan.suggestions();
546
- if (error !== null)
547
- return refusal("Suggestions", [error]);
548
- const s = deps.settingsOf();
549
- return emit(0, renderSuggestions(suggestions, s), {
550
- ok: true,
551
- suggestions,
552
- stage2: s
553
- ? {
554
- lenses: stage2Lenses(s.lenses).map((lens) => ({
555
- lens,
556
- tiers: lensTiers(s.lenses, lens, s.riskPasses ?? []),
557
- })),
558
- depth: s.scan.depth,
559
- deep_lenses: s.scan.deep_lenses,
560
- case_max_files: s.scan.case_max_files,
561
- case_max_lines: s.scan.case_max_lines,
562
- max_rows: MAX_STAGE2_ROWS,
563
- }
564
- : null,
565
- });
566
- }
567
- case "stage2-propose": {
568
- let rows = [];
569
- if (cmd.rows !== undefined) {
570
- try {
571
- rows = JSON.parse(readFileSync(cmd.rows === "-" ? 0 : cmd.rows, "utf8"));
572
- }
573
- catch (err) {
574
- return refusal("Stage 2 proposal", [
575
- `the rows (${cmd.rows}) cannot be read as JSON: ${err instanceof Error ? err.message : String(err)}`,
576
- ]);
577
- }
578
- }
579
- const r = await scan.proposeStage2({
580
- rows,
581
- ...(cmd.budget !== undefined ? { budget_usd: cmd.budget } : {}),
582
- ...(cmd.sha ? { sha: cmd.sha } : {}),
583
- ...(cmd.deep !== undefined ? { deep: cmd.deep } : {}),
584
- });
585
- if (!r.ok)
586
- return refusal("Stage 2 proposal", r.errors);
587
- return emit(0, renderProposal(r.proposal, CLI_VERBS), {
588
- ok: true,
589
- proposal: proposalJson(r.proposal),
590
- });
591
- }
592
- case "dismiss": {
593
- if (deps.triage === null)
594
- return refusal("Dismiss", ["no triage in this assembly"]);
595
- const dismissed = [];
596
- const errors = [];
597
- const lines = [];
598
- for (const fp of cmd.fingerprints) {
599
- const r = await deps.triage.dismiss({ fingerprint: fp, reason: cmd.reason });
600
- if (!r.ok) {
601
- errors.push(`${fp}: ${r.errors.join("; ")}`);
602
- lines.push(`✗ ${fp}: ${r.errors.join("; ")}`);
603
- continue;
604
- }
605
- dismissed.push({
606
- fingerprint: r.finding.fingerprint,
607
- again: r.again,
608
- memory: r.memory === "written"
609
- ? { written: true }
610
- : { written: false, problem: r.memory.problem },
611
- });
612
- lines.push(dismissedLine(r));
613
- }
614
- return emit(errors.length === 0 ? 0 : 1, lines.join("\n"), {
615
- ok: errors.length === 0,
616
- dismissed,
617
- errors,
618
- });
619
- }
620
- case "units-show":
621
- case "units-propose":
622
- case "units-adjust":
623
- case "units-save":
624
- return units(cmd, deps.units ?? null, emit, refusal);
625
- case "doctor": {
626
- if (!deps.doctor)
627
- return refusal("Doctor", ["no doctor in this assembly"]);
628
- const r = await deps.doctor();
629
- return emit(r.ok ? 0 : 1, renderDoctor(r), r);
630
- }
631
- case "login":
632
- if (json)
633
- return refusal("Login", ["login is interactive; run qcr scan login without --json"]);
634
- if (!deps.login)
635
- return refusal("Login", ["no login in this assembly"]);
636
- return deps.login(cmd.provider);
637
- case "rebuild": {
638
- if (!deps.rebuild)
639
- return refusal("Scan image rebuild", ["no rebuild in this assembly"]);
640
- const r = await deps.rebuild();
641
- if (!r.ok)
642
- return refusal("Scan image rebuild", r.errors);
643
- return emit(0, `Scan image ${r.image} built (stamp ${r.stamp}); no container was stopped or replaced. ` +
644
- "A companion on the same image keeps its container until `qcr companion rebuild`.", { ok: true, image: r.image, stamp: r.stamp });
645
- }
646
- }
647
- }
648
- /** `qcr scan units …`: the setup belt's unit-cut tools, over the same
649
- * engine and rendering, worded for the CLI. */
650
- function units(cmd, engine, emit, refusal) {
651
- if (engine === null)
652
- return refusal("Unit cut", ["no setup engine in this assembly"]);
653
- const readJson = (file) => {
654
- try {
655
- return { ok: true, value: JSON.parse(readFileSync(file === "-" ? 0 : file, "utf8")) };
656
- }
657
- catch (err) {
658
- return {
659
- ok: false,
660
- error: `${file === "-" ? "stdin" : file} cannot be read as JSON: ${err instanceof Error ? err.message : String(err)}`,
661
- };
662
- }
663
- };
664
- switch (cmd.verb) {
665
- case "units-show": {
666
- const s = engine.status();
667
- return emit(0, renderCutStatus(s), { ok: true, status: s });
668
- }
669
- case "units-propose": {
670
- let input;
671
- if (cmd.file !== undefined) {
672
- const j = readJson(cmd.file);
673
- if (!j.ok)
674
- return refusal("Proposal", [j.error]);
675
- input = j.value;
676
- }
677
- const r = engine.propose(input);
678
- if (!r.ok)
679
- return refusal("Proposal", r.errors);
680
- return emit(0, renderDraft("Draft stored", r.draft, CUT_CLI_VERBS), {
681
- ok: true,
682
- draft: r.draft,
683
- });
684
- }
685
- case "units-adjust": {
686
- const j = readJson(cmd.file);
687
- if (!j.ok)
688
- return refusal("Adjustment", [j.error]);
689
- // The tool's own parameter shape, `{ops: […]}`, or the ops alone.
690
- const v = j.value;
691
- const ops = typeof v === "object" && v !== null && !Array.isArray(v) && "ops" in v ? v.ops : v;
692
- const r = engine.adjust(ops);
693
- if (!r.ok)
694
- return refusal("Adjustment", r.errors);
695
- return emit(0, renderDraft("Draft adjusted", r.draft, CUT_CLI_VERBS), {
696
- ok: true,
697
- draft: r.draft,
698
- });
699
- }
700
- case "units-save": {
701
- const r = engine.save();
702
- if (!r.ok)
703
- return refusal("Save", r.errors);
704
- return emit(0, renderCut("Unit cut saved", r.saved), { ok: true, saved: r.saved });
705
- }
706
- }
707
- }
708
- function statusJson(s) {
709
- return {
710
- ok: s.ok,
711
- problems: s.problems,
712
- run: s.run ? runJson(s.run) : null,
713
- active: s.active,
714
- history: s.history,
715
- ledger: s.ledger,
716
- };
717
- }
718
- /** `qcr scan stop`: the shared stop (`stopScan`), rendered. */
719
- async function stop(deps, emit, refusal) {
720
- const r = await stopScan(deps.scan, {
721
- home: deps.home,
722
- by: "cli",
723
- sleep: deps.sleep,
724
- now: deps.now,
725
- });
726
- if (!r.ok)
727
- return refusal("Scan stop", r.errors);
728
- return emit(0, r.answer, { ok: true, run: r.run, answer: r.answer });
729
- }
730
78
  // ---- assembly ------------------------------------------------------------------
731
79
  /** The env a host-side `qcr scan` assembles under: the reviewed checkout and
732
80
  * the tracker from `[companion]` when the env does not name them, so the CLI