@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
@@ -0,0 +1,565 @@
1
+ // The home's reviews port (src/home/ports.ts `ReviewsPort`) over the app's
2
+ // own reviewer:
3
+ //
4
+ // preview `qcr review <PR> --repo R --preview --json`, the app's own bin
5
+ // run as a child with the person's environment (their pi login):
6
+ // the plan, the price and the key.
7
+ // start the detached registry's `start` (src/reviewdetach.ts), called
8
+ // in this process: `<PR> --repo R --key K --to file --out <a
9
+ // report in the state home>`. Never `--to pr`: a run posts
10
+ // nothing by itself. The run's driver is the app's bin again,
11
+ // detached, so it outlives the home.
12
+ // status the registry's `status`, read in this process.
13
+ // stop the registry's `stop`.
14
+ // runs every run the registry holds for the repo: what the home
15
+ // re-attaches to when it opens.
16
+ // latest the person's own reviews of the repo's PRs: the registry's
17
+ // finished runs and the app's per-target review state (the
18
+ // review home's delta history, packages/pi-plugin/src/reviewstate.ts).
19
+ // autoRounds the PRs the daemon finished a review round of, from its
20
+ // status snapshot (src/home/daemonstatus.ts): a PR whose rounds
21
+ // are all still running or failed has no review yet.
22
+ // post a finished review the run kept for later
23
+ // (packages/pi-plugin/src/savedpost.ts `postSavedReview`): one
24
+ // comment-only review through the gate with the install's dry_run.
25
+ // dryRun the install's dry_run.
26
+ //
27
+ // Everything effectful is a dependency; `realReviewsPort` is the assembly.
28
+ import { execFile } from "node:child_process";
29
+ import { randomBytes } from "node:crypto";
30
+ import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
31
+ import { join } from "node:path";
32
+ import { openFindings, type ReviewState } from "@quickchatai/pi-quickreview/src/reviewstate.js";
33
+ import {
34
+ detachedCommand,
35
+ identifyTarget,
36
+ launchDetached,
37
+ listRunViews,
38
+ runDetachedVerb,
39
+ runView,
40
+ type DetachDeps,
41
+ type RunView,
42
+ watchdogCommand,
43
+ } from "../reviewdetach.js";
44
+ import type { DaemonSnapshot } from "./daemonstatus.js";
45
+ import {
46
+ known,
47
+ unknown,
48
+ type Known,
49
+ type LiveLens,
50
+ type Outcome,
51
+ type PrReview,
52
+ type ReviewFinding,
53
+ type ReviewQuote,
54
+ type ReviewRunState,
55
+ type ReviewRunStatus,
56
+ type ReviewsPort,
57
+ } from "./ports.js";
58
+
59
+ type Env = Readonly<Record<string, string | undefined>>;
60
+
61
+ export interface QcrResult {
62
+ code: number;
63
+ stdout: string;
64
+ stderr: string;
65
+ }
66
+
67
+ export interface ReviewsPortDeps {
68
+ /** The detached-run registry: `<state home>/review/detached`. */
69
+ registry: string;
70
+ /** Where the home's review reports go: `<state home>/home/reviews`. */
71
+ reportsDir: string;
72
+ /** The review home: `<state home>/review`, one store per repo. */
73
+ reviewHome: string;
74
+ /** The daemon's status snapshot (src/home/daemonstatus.ts). */
75
+ snapshot: () => Promise<DaemonSnapshot>;
76
+ /** Run the app's own CLI with these arguments, as the person. */
77
+ qcr: (args: readonly string[], o: { signal?: AbortSignal }) => Promise<QcrResult>;
78
+ /** The registry's process seams: what `start` launches and how a run's
79
+ * liveness is told. */
80
+ detach: Omit<DetachDeps, "home" | "out">;
81
+ /** Post a saved review through the gate; `failed` with its line when it did not. */
82
+ post: (file: string) => Promise<{ failed: boolean; line: string }>;
83
+ /** The install's dry_run; throws when its config cannot be read. */
84
+ dryRun: () => boolean;
85
+ now: () => number;
86
+ /** Read a file (the review state). */
87
+ read?: (path: string) => string;
88
+ list?: (dir: string) => string[];
89
+ }
90
+
91
+ const message = (err: unknown): string => (err instanceof Error ? err.message : String(err));
92
+ const isMissing = (err: unknown): boolean =>
93
+ (err as NodeJS.ErrnoException | null)?.code === "ENOENT";
94
+
95
+ type Json = Record<string, unknown>;
96
+ const obj = (v: unknown): Json | undefined =>
97
+ typeof v === "object" && v !== null && !Array.isArray(v) ? (v as Json) : undefined;
98
+ const str = (v: unknown): string | undefined => (typeof v === "string" ? v : undefined);
99
+ const num = (v: unknown): number | undefined =>
100
+ typeof v === "number" && Number.isFinite(v) ? v : undefined;
101
+
102
+ /** The last JSON object a command printed. */
103
+ export function lastJson(stdout: string): Json | undefined {
104
+ const lines = stdout.trim().split("\n");
105
+ for (let i = lines.length - 1; i >= 0; i--) {
106
+ const l = lines[i]!.trim();
107
+ if (!l.startsWith("{")) continue;
108
+ try {
109
+ return obj(JSON.parse(l));
110
+ } catch {
111
+ // a stray line
112
+ }
113
+ }
114
+ return undefined;
115
+ }
116
+
117
+ const errorsOf = (o: Json | undefined, fallback: string): string => {
118
+ const e = o?.["errors"];
119
+ return Array.isArray(e) && e.length > 0 ? e.map(String).join("; ") : fallback;
120
+ };
121
+
122
+ /** `qcr review --preview --json`'s answer as a quote. */
123
+ export function parsePreview(o: Json | undefined): Outcome<{ quote: ReviewQuote }> {
124
+ const p = obj(o?.["preview"]);
125
+ const est = num(p?.["estimate_usd"]);
126
+ const key = str(p?.["key"]);
127
+ if (o?.["ok"] !== true || p === undefined || est === undefined || key === undefined)
128
+ return { ok: false, why: errorsOf(o, "the reviewer gave no price") };
129
+ const cap = p["cap_usd"];
130
+ const lenses = p["lenses"];
131
+ return {
132
+ ok: true,
133
+ quote: {
134
+ estimateUsd: est,
135
+ capUsd: num(cap) ?? null,
136
+ key,
137
+ lenses: Array.isArray(lenses) ? lenses.map(String) : [],
138
+ files: num(p["files"]) ?? 0,
139
+ headSha: str(p["head_sha"]) ?? null,
140
+ },
141
+ };
142
+ }
143
+
144
+ /** The findings of a result, grouped by severity, as one list worst first. */
145
+ export function parseFindings(groups: unknown): ReviewFinding[] {
146
+ const g = obj(groups);
147
+ if (g === undefined) return [];
148
+ return Object.entries(g).flatMap(([sev, list]) =>
149
+ Array.isArray(list)
150
+ ? list.flatMap((f: unknown) => {
151
+ const x = obj(f);
152
+ if (x === undefined) return [];
153
+ return [
154
+ {
155
+ severity: str(x["severity"]) ?? sev,
156
+ path: str(x["path"]) ?? null,
157
+ line: num(x["line"]) ?? null,
158
+ title: str(x["title"]) ?? "(untitled finding)",
159
+ lens: str(x["lens"]) ?? null,
160
+ },
161
+ ];
162
+ })
163
+ : [],
164
+ );
165
+ }
166
+
167
+ /** The PR a run reviews, from its target label (`PR owner/name#12`). */
168
+ export function prOfTarget(target: string | undefined): { repo: string; pr: number } | null {
169
+ const m = /^PR ([\w.-]+\/[\w.-]+)#(\d+)$/.exec(target ?? "");
170
+ return m ? { repo: m[1]!, pr: Number(m[2]) } : null;
171
+ }
172
+
173
+ const STATES: readonly ReviewRunState[] = ["running", "done", "failed", "stopped"];
174
+
175
+ /** One run as `qcr review status --json` reports it, as the home reads it. */
176
+ export function parseRunStatus(v: Json, argv: readonly string[] = []): ReviewRunStatus | null {
177
+ const runId = str(v["run_id"]);
178
+ const state = str(v["state"]) as ReviewRunState | undefined;
179
+ if (runId === undefined || state === undefined || !STATES.includes(state)) return null;
180
+ const target = prOfTarget(str(v["target"]));
181
+ const out = argv.indexOf("--out");
182
+ const reportArg = out >= 0 ? argv[out + 1] : undefined;
183
+ const result = obj(v["result"]);
184
+ const reportResult = str(result?.["report"]);
185
+ const live = obj(obj(v["progress"])?.["live"]);
186
+ const agents = live?.["agents"];
187
+ const errors = [
188
+ ...(Array.isArray(result?.["errors"]) ? (result["errors"] as unknown[]).map(String) : []),
189
+ ...(Array.isArray(v["errors"]) ? (v["errors"] as unknown[]).map(String) : []),
190
+ ];
191
+ const ended = num(v["ended_at"]);
192
+ const report = reportResult ?? reportArg;
193
+ return {
194
+ runId,
195
+ state,
196
+ repo: target?.repo ?? null,
197
+ pr: target?.pr ?? null,
198
+ startedAt: num(v["started_at"]) ?? 0,
199
+ ...(ended !== undefined ? { endedAt: ended } : {}),
200
+ ...(report !== undefined ? { report } : {}),
201
+ ...(live !== undefined
202
+ ? {
203
+ live: {
204
+ lastActivity: str(live["last_activity"]) ?? "",
205
+ lenses: Array.isArray(agents)
206
+ ? agents.flatMap((a: unknown): LiveLens[] => {
207
+ const x = obj(a);
208
+ const agent = str(x?.["agent"]);
209
+ if (x === undefined || agent === undefined) return [];
210
+ const tool = str(x["current_tool"]);
211
+ const on = str(x["current_target"]);
212
+ return [
213
+ {
214
+ agent,
215
+ status: str(x["status"]) ?? "pending",
216
+ toolCount: num(x["tool_count"]) ?? 0,
217
+ durationMs: num(x["duration_ms"]) ?? 0,
218
+ ...(tool !== undefined ? { currentTool: tool } : {}),
219
+ ...(on !== undefined ? { currentTarget: on } : {}),
220
+ line: str(x["line"]) ?? agent,
221
+ },
222
+ ];
223
+ })
224
+ : [],
225
+ },
226
+ }
227
+ : {}),
228
+ ...(state !== "running" && result !== undefined
229
+ ? {
230
+ result: {
231
+ findings: parseFindings(result["findings"]),
232
+ costUsd: num(result["cost_usd"]) ?? null,
233
+ ...(str(result["post_ready"]) !== undefined
234
+ ? { postFile: str(result["post_ready"])! }
235
+ : {}),
236
+ ...(obj(result["post"])?.["ok"] === true ? { posted: true } : {}),
237
+ },
238
+ }
239
+ : {}),
240
+ errors,
241
+ };
242
+ }
243
+
244
+ const high = (list: readonly ReviewFinding[]): number =>
245
+ list.filter((f) => /^P[01]$/i.test(f.severity)).length;
246
+
247
+ /** A finished run as the person's review of its PR. */
248
+ function runReview(s: ReviewRunStatus): PrReview | null {
249
+ if (s.state !== "done" || s.result === undefined) return null;
250
+ const list = s.result.findings;
251
+ return {
252
+ findings: list.length,
253
+ high: high(list),
254
+ by: "you",
255
+ at: s.endedAt ?? s.startedAt,
256
+ list,
257
+ ...(s.report !== undefined ? { report: s.report } : {}),
258
+ ...(s.endedAt !== undefined ? { tookMs: s.endedAt - s.startedAt } : {}),
259
+ costUsd: s.result.costUsd,
260
+ };
261
+ }
262
+
263
+ const later = (a: PrReview | undefined, b: PrReview): PrReview =>
264
+ a === undefined || b.at > a.at ? b : a;
265
+
266
+ export function reviewsPort(d: ReviewsPortDeps): ReviewsPort {
267
+ const read = d.read ?? ((p: string) => readFileSync(p, "utf8"));
268
+ const list = d.list ?? ((dir: string) => readdirSync(dir));
269
+ const liveness = {
270
+ alive: d.detach.alive,
271
+ now: d.now,
272
+ ...(d.detach.liveContainers ? { liveContainers: d.detach.liveContainers } : {}),
273
+ };
274
+ /** Run one registry verb in this process; its one JSON line. */
275
+ const verb = async (
276
+ v: "start" | "status" | "stop",
277
+ args: readonly string[],
278
+ ): Promise<{ code: number; json: Json | undefined }> => {
279
+ const lines: string[] = [];
280
+ const code = await runDetachedVerb(v, args, true, {
281
+ ...d.detach,
282
+ home: d.registry,
283
+ out: (l) => lines.push(l),
284
+ });
285
+ return { code, json: lastJson(lines.join("\n")) };
286
+ };
287
+ const views = (repo: string): Known<RunView[]> => {
288
+ try {
289
+ return known(
290
+ listRunViews(d.registry, liveness).filter(
291
+ (r) => prOfTarget(str(r.view["target"]))?.repo === repo,
292
+ ),
293
+ );
294
+ } catch (err) {
295
+ return unknown(`can't read the review runs in ${d.registry}: ${message(err)}`);
296
+ }
297
+ };
298
+
299
+ return {
300
+ preview: async (repo, pr, o = {}) => {
301
+ const r = await d.qcr(["review", String(pr), "--repo", repo, "--preview", "--json"], o);
302
+ const parsed = parsePreview(lastJson(r.stdout));
303
+ if (parsed.ok || lastJson(r.stdout) !== undefined) return parsed;
304
+ return {
305
+ ok: false,
306
+ why: (r.stderr.trim().split("\n").pop() ?? "").trim() || `qcr exited ${r.code}`,
307
+ };
308
+ },
309
+
310
+ start: async (repo, pr, key) => {
311
+ const stamp = new Date(d.now())
312
+ .toISOString()
313
+ .replace(/[^0-9]/g, "")
314
+ .slice(0, 14);
315
+ const dir = join(d.reportsDir, repo.replace("/", "-"));
316
+ try {
317
+ mkdirSync(dir, { recursive: true });
318
+ } catch (err) {
319
+ return { ok: false, why: `can't make ${dir}: ${message(err)}` };
320
+ }
321
+ const report = join(dir, `pr-${pr}-${stamp}.md`);
322
+ // a file in the state home, never the PR: the run posts nothing by itself
323
+ const args = [String(pr), "--repo", repo, "--key", key, "--to", "file", "--out", report];
324
+ const r = await verb("start", args);
325
+ const runId = str(r.json?.["run_id"]);
326
+ if (r.code !== 0 || runId === undefined)
327
+ return { ok: false, why: errorsOf(r.json, "the review did not start") };
328
+ return { ok: true, runId, report };
329
+ },
330
+
331
+ status: async (runId) => {
332
+ try {
333
+ const v = runView(d.registry, runId, liveness);
334
+ if (v === null) return unknown(`no review run ${runId}`);
335
+ const s = parseRunStatus(v.view, v.argv);
336
+ return s === null ? unknown(`run ${runId}'s status is not one the home reads`) : known(s);
337
+ } catch (err) {
338
+ return unknown(message(err));
339
+ }
340
+ },
341
+
342
+ stop: async (runId) => {
343
+ const r = await verb("stop", [runId]);
344
+ return r.code === 0 ? { ok: true } : { ok: false, why: errorsOf(r.json, "it did not stop") };
345
+ },
346
+
347
+ runs: async (repo) => {
348
+ const v = views(repo);
349
+ if (v.kind === "unknown") return v;
350
+ return known(
351
+ v.value.flatMap((r) => {
352
+ const s = parseRunStatus(r.view, r.argv);
353
+ return s === null ? [] : [s];
354
+ }),
355
+ );
356
+ },
357
+
358
+ latest: async (repo) => {
359
+ const v = views(repo);
360
+ if (v.kind === "unknown") return v;
361
+ const by = new Map<number, PrReview>();
362
+ for (const r of v.value) {
363
+ const s = parseRunStatus(r.view, r.argv);
364
+ const rv = s && s.pr !== null ? runReview(s) : null;
365
+ if (s && s.pr !== null && rv) by.set(s.pr, later(by.get(s.pr), rv));
366
+ }
367
+ // the per-target review state: a PR reviewed from a checkout
368
+ let stores: string[];
369
+ try {
370
+ stores = existsSync(d.reviewHome) ? list(d.reviewHome) : [];
371
+ } catch (err) {
372
+ return unknown(`can't read the review state in ${d.reviewHome}: ${message(err)}`);
373
+ }
374
+ const prefix = `pr:${repo}#`;
375
+ for (const store of stores) {
376
+ const dir = join(d.reviewHome, store, "state");
377
+ let files: string[];
378
+ try {
379
+ files = list(dir);
380
+ } catch (err) {
381
+ if (isMissing(err) || (err as NodeJS.ErrnoException).code === "ENOTDIR") continue;
382
+ return unknown(`can't read ${dir}: ${message(err)}`);
383
+ }
384
+ for (const f of files.filter((x) => x.endsWith(".json"))) {
385
+ let s: ReviewState;
386
+ try {
387
+ s = JSON.parse(read(join(dir, f))) as ReviewState;
388
+ } catch {
389
+ continue; // unreadable state is no state (reviewstate.ts)
390
+ }
391
+ if (typeof s.target !== "string" || !s.target.startsWith(prefix)) continue;
392
+ const pr = Number(s.target.slice(prefix.length));
393
+ const last = Array.isArray(s.rounds) ? s.rounds[s.rounds.length - 1] : undefined;
394
+ const at = last ? Date.parse(last.reviewed_at) : NaN;
395
+ if (!Number.isInteger(pr) || !Number.isFinite(at)) continue;
396
+ let open: ReviewFinding[];
397
+ try {
398
+ open = openFindings(s).findings.map((x) => ({
399
+ severity: x.severity ?? "P3",
400
+ path: x.path,
401
+ line: x.line,
402
+ title: x.title ?? "(untitled finding)",
403
+ lens: x.lens || null,
404
+ }));
405
+ } catch {
406
+ continue;
407
+ }
408
+ by.set(
409
+ pr,
410
+ later(by.get(pr), {
411
+ findings: open.length,
412
+ high: high(open),
413
+ by: "you",
414
+ at,
415
+ list: open,
416
+ costUsd: last?.cost_usd ?? null,
417
+ }),
418
+ );
419
+ }
420
+ }
421
+ return known(by);
422
+ },
423
+
424
+ autoRounds: async () => {
425
+ let snapshot: DaemonSnapshot;
426
+ try {
427
+ snapshot = await d.snapshot();
428
+ } catch (err) {
429
+ return unknown(`can't read auto-review's status: ${message(err)}`);
430
+ }
431
+ // auto-review recorded nothing here: it reviewed nothing
432
+ if (snapshot.kind === "none") return known(new Map());
433
+ if (snapshot.kind === "off")
434
+ return unknown("auto-review is off, and its reviews are read from it while it runs");
435
+ if (snapshot.kind === "unknown") return unknown(snapshot.why);
436
+ let snap: Json | undefined;
437
+ try {
438
+ snap = obj(JSON.parse(snapshot.text));
439
+ } catch (err) {
440
+ return unknown(`auto-review's status is not JSON: ${message(err)}`);
441
+ }
442
+ const prs = snap?.["prs"];
443
+ if (!Array.isArray(prs)) return unknown("auto-review's status lists no pull requests");
444
+ const written = Date.parse(str(snap?.["written_at"]) ?? "");
445
+ const by = new Map<number, PrReview>();
446
+ for (const p of prs) {
447
+ const x = obj(p);
448
+ const n = num(x?.["number"]);
449
+ // `rounds` counts every round, running and failed ones too; a verdict
450
+ // is only recorded when a round finishes (src/state.ts): that is a review
451
+ const verdict = str(x?.["last_verdict"]);
452
+ if (x === undefined || n === undefined || verdict === undefined || verdict === "") continue;
453
+ const at = Date.parse(str(x["updated_at"]) ?? "");
454
+ // the snapshot counts rounds, not findings: those are on the PR
455
+ by.set(n, {
456
+ findings: null,
457
+ high: null,
458
+ by: "auto",
459
+ at: Number.isFinite(at) ? at : Number.isFinite(written) ? written : 0,
460
+ });
461
+ }
462
+ return known(by);
463
+ },
464
+
465
+ post: async (file) => {
466
+ try {
467
+ const r = await d.post(file);
468
+ return r.failed
469
+ ? { ok: false, why: r.line.replace(/^Review not posted: /, "") }
470
+ : { ok: true, line: r.line };
471
+ } catch (err) {
472
+ return { ok: false, why: message(err) };
473
+ }
474
+ },
475
+
476
+ dryRun: async () => {
477
+ try {
478
+ return known(d.dryRun());
479
+ } catch (err) {
480
+ return unknown(message(err));
481
+ }
482
+ },
483
+ };
484
+ }
485
+
486
+ // ---- the real assembly -------------------------------------------------------------
487
+
488
+ /** The app's bin, run as a child of this node. */
489
+ export function qcrChild(o: {
490
+ execPath: string;
491
+ bin: string;
492
+ cwd: string;
493
+ env: Env;
494
+ timeoutMs?: number;
495
+ }): ReviewsPortDeps["qcr"] {
496
+ return (args, { signal } = {}) =>
497
+ new Promise((resolve) => {
498
+ execFile(
499
+ o.execPath,
500
+ [o.bin, ...args],
501
+ {
502
+ cwd: o.cwd,
503
+ env: { ...o.env },
504
+ timeout: o.timeoutMs ?? 180_000,
505
+ maxBuffer: 32 << 20,
506
+ ...(signal ? { signal } : {}),
507
+ },
508
+ (err, stdout, stderr) => {
509
+ const e = err as (NodeJS.ErrnoException & { code?: unknown }) | null;
510
+ resolve({
511
+ code: e === null ? 0 : typeof e.code === "number" ? e.code : 1,
512
+ stdout: String(stdout),
513
+ stderr: e && !stderr ? e.message : String(stderr),
514
+ });
515
+ },
516
+ );
517
+ });
518
+ }
519
+
520
+ /** The registry's process seams, as `qcr review start` has them, with the
521
+ * app's bin as the driver: a run is a process group of its own that outlives
522
+ * the home. */
523
+ export function realDetachSeams(o: {
524
+ execPath: string;
525
+ bin: string;
526
+ cwd: string;
527
+ env: Env;
528
+ }): Omit<DetachDeps, "home" | "out"> {
529
+ const entry = { entry: o.bin, execPath: o.execPath, execArgv: [] as string[] };
530
+ return {
531
+ now: Date.now,
532
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
533
+ newId: () =>
534
+ `${new Date()
535
+ .toISOString()
536
+ .replace(/[^0-9]/g, "")
537
+ .slice(0, 14)}-${randomBytes(3).toString("hex")}`,
538
+ alive: (pid) => {
539
+ try {
540
+ process.kill(pid, 0);
541
+ return true;
542
+ } catch {
543
+ return false;
544
+ }
545
+ },
546
+ kill: (pid, sig) => process.kill(-pid, sig),
547
+ identify: (args) => identifyTarget(args, o.cwd),
548
+ launch: (l) =>
549
+ launchDetached({
550
+ ...detachedCommand(l, entry),
551
+ dir: l.dir,
552
+ env: { ...o.env },
553
+ cwd: o.cwd,
554
+ }),
555
+ watch: (w) => {
556
+ launchDetached({
557
+ ...watchdogCommand(w, entry),
558
+ dir: w.dir,
559
+ env: { ...o.env },
560
+ cwd: o.cwd,
561
+ log: "watchdog.log",
562
+ });
563
+ },
564
+ };
565
+ }