@quickchatai/quickreview 0.0.0-stage → 1.1.0

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 (1443) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +385 -2
  3. package/THIRD-PARTY-NOTICES.md +134 -0
  4. package/agents/pass-abstraction.md +64 -0
  5. package/agents/pass-ask-human.md +134 -0
  6. package/agents/pass-backcompat.md +67 -0
  7. package/agents/pass-baseline.md +61 -0
  8. package/agents/pass-behaviour-delta.md +64 -0
  9. package/agents/pass-bounds.md +36 -0
  10. package/agents/pass-claims.md +31 -0
  11. package/agents/pass-concurrency.md +168 -0
  12. package/agents/pass-consistency.md +57 -0
  13. package/agents/pass-contracts.md +30 -0
  14. package/agents/pass-data-safety.md +54 -0
  15. package/agents/pass-deferred.md +35 -0
  16. package/agents/pass-delivery.md +37 -0
  17. package/agents/pass-duplication.md +60 -0
  18. package/agents/pass-intent.md +63 -0
  19. package/agents/pass-lint-conformance.md +46 -0
  20. package/agents/pass-maintainability.md +67 -0
  21. package/agents/pass-placement.md +68 -0
  22. package/agents/pass-prod-risk.md +24 -0
  23. package/agents/pass-rollout.md +32 -0
  24. package/agents/pass-roundtrip.md +46 -0
  25. package/agents/pass-security.md +31 -0
  26. package/agents/pass-simplicity.md +68 -0
  27. package/agents/pass-statelessness.md +27 -0
  28. package/agents/pass-style.md +37 -0
  29. package/agents/pass-sweep.md +57 -0
  30. package/agents/pass-tests.md +58 -0
  31. package/agents/pass-ui.md +23 -0
  32. package/agents/pass-validate.md +50 -0
  33. package/agents/pass-widened.md +46 -0
  34. package/agents/scan/pass-scan.md +86 -0
  35. package/bin/qcr.js +11 -0
  36. package/config.example.toml +1067 -0
  37. package/deploy/ansible/README.md +356 -0
  38. package/deploy/ansible/group_vars/all.yml +212 -0
  39. package/deploy/ansible/inventory.example.ini +25 -0
  40. package/deploy/ansible/playbook.yml +360 -0
  41. package/deploy/collect-secrets.sh +289 -0
  42. package/deploy/collect-slack-secrets.mjs +1190 -0
  43. package/deploy/github-app-manifest.json +31 -0
  44. package/deploy/manifest.schema.json +75 -0
  45. package/deploy/qcr-update.sh +803 -0
  46. package/deploy/reviewer/AGENTS.override.md +12 -0
  47. package/dist/extensions/cli/bash-guard-ambient.js +42 -0
  48. package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
  49. package/dist/extensions/cli/bash-guard.js +5 -0
  50. package/dist/extensions/cli/bash-guard.js.map +1 -0
  51. package/dist/extensions/cli/companion-guard.js +5 -0
  52. package/dist/extensions/cli/companion-guard.js.map +1 -0
  53. package/dist/extensions/cli/debug-round.js +8 -0
  54. package/dist/extensions/cli/debug-round.js.map +1 -0
  55. package/dist/extensions/cli/inject-bridge.js +5 -0
  56. package/dist/extensions/cli/inject-bridge.js.map +1 -0
  57. package/dist/extensions/cli/ledger-pin.js +5 -0
  58. package/dist/extensions/cli/ledger-pin.js.map +1 -0
  59. package/dist/extensions/cli/lens.js +13 -0
  60. package/dist/extensions/cli/lens.js.map +1 -0
  61. package/dist/extensions/cli/muted-theme.js +5 -0
  62. package/dist/extensions/cli/muted-theme.js.map +1 -0
  63. package/dist/extensions/cli/onboarding.js +5 -0
  64. package/dist/extensions/cli/onboarding.js.map +1 -0
  65. package/dist/extensions/cli/setup.js +5 -0
  66. package/dist/extensions/cli/setup.js.map +1 -0
  67. package/dist/extensions/cli/tui-view.js +5 -0
  68. package/dist/extensions/cli/tui-view.js.map +1 -0
  69. package/dist/extensions/companion-guard.js +361 -0
  70. package/dist/extensions/companion-guard.js.map +1 -0
  71. package/dist/extensions/debug-round.js +204 -0
  72. package/dist/extensions/debug-round.js.map +1 -0
  73. package/dist/extensions/inject-bridge.js +603 -0
  74. package/dist/extensions/inject-bridge.js.map +1 -0
  75. package/dist/extensions/muted-theme.js +12 -0
  76. package/dist/extensions/muted-theme.js.map +1 -0
  77. package/dist/extensions/onboarding.js +175 -0
  78. package/dist/extensions/onboarding.js.map +1 -0
  79. package/dist/extensions/setup-replay.js +269 -0
  80. package/dist/extensions/setup-replay.js.map +1 -0
  81. package/dist/extensions/setup-scan.js +998 -0
  82. package/dist/extensions/setup-scan.js.map +1 -0
  83. package/dist/extensions/setup-triage.js +175 -0
  84. package/dist/extensions/setup-triage.js.map +1 -0
  85. package/dist/extensions/setup.js +1160 -0
  86. package/dist/extensions/setup.js.map +1 -0
  87. package/dist/extensions/tui-view.js +389 -0
  88. package/dist/extensions/tui-view.js.map +1 -0
  89. package/dist/extensions/vendor/openviking-pi/client.js +376 -0
  90. package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
  91. package/dist/extensions/vendor/openviking-pi/config.js +189 -0
  92. package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
  93. package/dist/extensions/vendor/openviking-pi/index.js +421 -0
  94. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
  95. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
  96. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
  97. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
  98. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
  99. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
  100. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
  101. package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
  102. package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
  103. package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
  104. package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
  105. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
  106. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
  107. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
  108. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
  109. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
  110. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
  111. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
  112. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
  113. package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
  114. package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
  115. package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
  116. package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
  117. package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
  118. package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
  119. package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
  120. package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
  121. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
  122. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
  123. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
  124. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
  125. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
  126. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
  127. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
  128. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
  129. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
  130. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
  131. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
  132. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
  133. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
  134. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
  135. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
  136. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
  137. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
  138. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
  139. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  140. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
  141. package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
  142. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
  143. package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
  144. package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
  145. package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
  146. package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
  147. package/dist/src/anomalies.js +483 -0
  148. package/dist/src/anomalies.js.map +1 -0
  149. package/dist/src/answerworker.js +180 -0
  150. package/dist/src/answerworker.js.map +1 -0
  151. package/dist/src/appstamp.js +68 -0
  152. package/dist/src/appstamp.js.map +1 -0
  153. package/dist/src/archive/admission.js +3 -0
  154. package/dist/src/archive/admission.js.map +1 -0
  155. package/dist/src/archive/archivedb.js +474 -0
  156. package/dist/src/archive/archivedb.js.map +1 -0
  157. package/dist/src/archive/archiver.js +1770 -0
  158. package/dist/src/archive/archiver.js.map +1 -0
  159. package/dist/src/archive/archivewrite.js +198 -0
  160. package/dist/src/archive/archivewrite.js.map +1 -0
  161. package/dist/src/archive/bundle.js +266 -0
  162. package/dist/src/archive/bundle.js.map +1 -0
  163. package/dist/src/archive/dbmove.js +638 -0
  164. package/dist/src/archive/dbmove.js.map +1 -0
  165. package/dist/src/archive/duplicatecounts.js +80 -0
  166. package/dist/src/archive/duplicatecounts.js.map +1 -0
  167. package/dist/src/archive/eligibility.js +113 -0
  168. package/dist/src/archive/eligibility.js.map +1 -0
  169. package/dist/src/archive/extract.js +288 -0
  170. package/dist/src/archive/extract.js.map +1 -0
  171. package/dist/src/archive/fsutil.js +330 -0
  172. package/dist/src/archive/fsutil.js.map +1 -0
  173. package/dist/src/archive/gzfile.js +277 -0
  174. package/dist/src/archive/gzfile.js.map +1 -0
  175. package/dist/src/archive/kinds.js +22 -0
  176. package/dist/src/archive/kinds.js.map +1 -0
  177. package/dist/src/archive/livecheckpoint.js +178 -0
  178. package/dist/src/archive/livecheckpoint.js.map +1 -0
  179. package/dist/src/archive/liveretention.js +145 -0
  180. package/dist/src/archive/liveretention.js.map +1 -0
  181. package/dist/src/archive/manifest.js +284 -0
  182. package/dist/src/archive/manifest.js.map +1 -0
  183. package/dist/src/archive/overseer.js +283 -0
  184. package/dist/src/archive/overseer.js.map +1 -0
  185. package/dist/src/archive/piglobal.js +129 -0
  186. package/dist/src/archive/piglobal.js.map +1 -0
  187. package/dist/src/archive/plan.js +398 -0
  188. package/dist/src/archive/plan.js.map +1 -0
  189. package/dist/src/archive/prarchive.js +831 -0
  190. package/dist/src/archive/prarchive.js.map +1 -0
  191. package/dist/src/archive/prfiles.js +303 -0
  192. package/dist/src/archive/prfiles.js.map +1 -0
  193. package/dist/src/archive/report.js +169 -0
  194. package/dist/src/archive/report.js.map +1 -0
  195. package/dist/src/archive/restore.js +276 -0
  196. package/dist/src/archive/restore.js.map +1 -0
  197. package/dist/src/archive/rows.js +106 -0
  198. package/dist/src/archive/rows.js.map +1 -0
  199. package/dist/src/archive/seal.js +480 -0
  200. package/dist/src/archive/seal.js.map +1 -0
  201. package/dist/src/archive/status.js +528 -0
  202. package/dist/src/archive/status.js.map +1 -0
  203. package/dist/src/archive/storepass.js +409 -0
  204. package/dist/src/archive/storepass.js.map +1 -0
  205. package/dist/src/backfill.js +217 -0
  206. package/dist/src/backfill.js.map +1 -0
  207. package/dist/src/bench/cost.js +87 -0
  208. package/dist/src/bench/exec.js +76 -0
  209. package/dist/src/bench/values.js +14 -0
  210. package/dist/src/botcontrol.js +127 -0
  211. package/dist/src/botcontrol.js.map +1 -0
  212. package/dist/src/botworker.js +805 -0
  213. package/dist/src/botworker.js.map +1 -0
  214. package/dist/src/capture.js +234 -0
  215. package/dist/src/capture.js.map +1 -0
  216. package/dist/src/cases.js +1491 -0
  217. package/dist/src/cases.js.map +1 -0
  218. package/dist/src/channelfs.js +27 -0
  219. package/dist/src/channelfs.js.map +1 -0
  220. package/dist/src/checklist.js +312 -0
  221. package/dist/src/checklist.js.map +1 -0
  222. package/dist/src/ciretry.js +91 -0
  223. package/dist/src/ciretry.js.map +1 -0
  224. package/dist/src/cli.js +1916 -0
  225. package/dist/src/cli.js.map +1 -0
  226. package/dist/src/cliops.js +333 -0
  227. package/dist/src/cliops.js.map +1 -0
  228. package/dist/src/clonedir.js +15 -0
  229. package/dist/src/clonedir.js.map +1 -0
  230. package/dist/src/commandeffects.js +43 -0
  231. package/dist/src/commandeffects.js.map +1 -0
  232. package/dist/src/commands.js +1312 -0
  233. package/dist/src/commands.js.map +1 -0
  234. package/dist/src/companioninbox.js +203 -0
  235. package/dist/src/companioninbox.js.map +1 -0
  236. package/dist/src/compare.js +673 -0
  237. package/dist/src/compare.js.map +1 -0
  238. package/dist/src/config.js +1027 -0
  239. package/dist/src/config.js.map +1 -0
  240. package/dist/src/configcheck.js +111 -0
  241. package/dist/src/configcheck.js.map +1 -0
  242. package/dist/src/coreext.js +43 -0
  243. package/dist/src/coreext.js.map +1 -0
  244. package/dist/src/daemon.js +929 -0
  245. package/dist/src/daemon.js.map +1 -0
  246. package/dist/src/debuground.js +731 -0
  247. package/dist/src/debuground.js.map +1 -0
  248. package/dist/src/debugroundpi.js +219 -0
  249. package/dist/src/debugroundpi.js.map +1 -0
  250. package/dist/src/diagnostics.js +14 -0
  251. package/dist/src/diagnostics.js.map +1 -0
  252. package/dist/src/digest.js +139 -0
  253. package/dist/src/digest.js.map +1 -0
  254. package/dist/src/dispatch.js +1726 -0
  255. package/dist/src/dispatch.js.map +1 -0
  256. package/dist/src/engine/answerguard.js +18 -0
  257. package/dist/src/engine/answerguard.js.map +1 -0
  258. package/dist/src/engine/botsdk.js +262 -0
  259. package/dist/src/engine/botsdk.js.map +1 -0
  260. package/dist/src/engine/caserunner-sdk.js +332 -0
  261. package/dist/src/engine/caserunner-sdk.js.map +1 -0
  262. package/dist/src/engine/debugpi.js +194 -0
  263. package/dist/src/engine/debugpi.js.map +1 -0
  264. package/dist/src/eod.js +473 -0
  265. package/dist/src/eod.js.map +1 -0
  266. package/dist/src/eodpinwatch.js +391 -0
  267. package/dist/src/eodpinwatch.js.map +1 -0
  268. package/dist/src/escalation.js +1009 -0
  269. package/dist/src/escalation.js.map +1 -0
  270. package/dist/src/eventloop.js +779 -0
  271. package/dist/src/eventloop.js.map +1 -0
  272. package/dist/src/execboundary.js +430 -0
  273. package/dist/src/execboundary.js.map +1 -0
  274. package/dist/src/feedback.js +915 -0
  275. package/dist/src/feedback.js.map +1 -0
  276. package/dist/src/findingscheck.js +200 -0
  277. package/dist/src/findingscheck.js.map +1 -0
  278. package/dist/src/fsdenied.js +26 -0
  279. package/dist/src/fsdenied.js.map +1 -0
  280. package/dist/src/ghread.js +181 -0
  281. package/dist/src/ghread.js.map +1 -0
  282. package/dist/src/github.js +1063 -0
  283. package/dist/src/github.js.map +1 -0
  284. package/dist/src/homes.js +103 -0
  285. package/dist/src/homes.js.map +1 -0
  286. package/dist/src/hostroute.js +24 -0
  287. package/dist/src/hostroute.js.map +1 -0
  288. package/dist/src/improvements.js +625 -0
  289. package/dist/src/improvements.js.map +1 -0
  290. package/dist/src/install.js +946 -0
  291. package/dist/src/install.js.map +1 -0
  292. package/dist/src/installkind.js +40 -0
  293. package/dist/src/installkind.js.map +1 -0
  294. package/dist/src/ipc/capability.js +286 -0
  295. package/dist/src/ipc/capability.js.map +1 -0
  296. package/dist/src/ipc/client.js +226 -0
  297. package/dist/src/ipc/client.js.map +1 -0
  298. package/dist/src/ipc/control.js +70 -0
  299. package/dist/src/ipc/control.js.map +1 -0
  300. package/dist/src/ipc/ghreadshim.js +77 -0
  301. package/dist/src/ipc/ghreadshim.js.map +1 -0
  302. package/dist/src/ipc/launcher.js +65 -0
  303. package/dist/src/ipc/launcher.js.map +1 -0
  304. package/dist/src/ipc/permits.js +293 -0
  305. package/dist/src/ipc/permits.js.map +1 -0
  306. package/dist/src/ipc/postshim.js +137 -0
  307. package/dist/src/ipc/postshim.js.map +1 -0
  308. package/dist/src/ipc/protocol.js +239 -0
  309. package/dist/src/ipc/protocol.js.map +1 -0
  310. package/dist/src/ipc/server.js +312 -0
  311. package/dist/src/ipc/server.js.map +1 -0
  312. package/dist/src/ipc/workerspec.js +148 -0
  313. package/dist/src/ipc/workerspec.js.map +1 -0
  314. package/dist/src/learner/learner.js +1272 -0
  315. package/dist/src/learner/learner.js.map +1 -0
  316. package/dist/src/learner/miner.js +948 -0
  317. package/dist/src/learner/miner.js.map +1 -0
  318. package/dist/src/learner/packet.js +216 -0
  319. package/dist/src/learner/packet.js.map +1 -0
  320. package/dist/src/learner/receipt.js +135 -0
  321. package/dist/src/learner/receipt.js.map +1 -0
  322. package/dist/src/learner/status.js +38 -0
  323. package/dist/src/learner/status.js.map +1 -0
  324. package/dist/src/ledgercheck.js +120 -0
  325. package/dist/src/ledgercheck.js.map +1 -0
  326. package/dist/src/lessonledger.js +451 -0
  327. package/dist/src/lessonledger.js.map +1 -0
  328. package/dist/src/lessonselect.js +639 -0
  329. package/dist/src/lessonselect.js.map +1 -0
  330. package/dist/src/lessonshadow.js +540 -0
  331. package/dist/src/lessonshadow.js.map +1 -0
  332. package/dist/src/levers.js +302 -0
  333. package/dist/src/levers.js.map +1 -0
  334. package/dist/src/live/projection.js +450 -0
  335. package/dist/src/live/projection.js.map +1 -0
  336. package/dist/src/live/publish.js +83 -0
  337. package/dist/src/live/publish.js.map +1 -0
  338. package/dist/src/live/read.js +60 -0
  339. package/dist/src/live/read.js.map +1 -0
  340. package/dist/src/livecli.js +288 -0
  341. package/dist/src/livecli.js.map +1 -0
  342. package/dist/src/loginhint.js +42 -0
  343. package/dist/src/loginhint.js.map +1 -0
  344. package/dist/src/loginrefresh/atomic.js +66 -0
  345. package/dist/src/loginrefresh/atomic.js.map +1 -0
  346. package/dist/src/loginrefresh/lock.js +93 -0
  347. package/dist/src/loginrefresh/lock.js.map +1 -0
  348. package/dist/src/loginrefresh/main.js +58 -0
  349. package/dist/src/loginrefresh/main.js.map +1 -0
  350. package/dist/src/loginrefresh/pirefresh.js +49 -0
  351. package/dist/src/loginrefresh/pirefresh.js.map +1 -0
  352. package/dist/src/loginrefresh/refresher.js +286 -0
  353. package/dist/src/loginrefresh/refresher.js.map +1 -0
  354. package/dist/src/loginrefresh/supervisor.js +253 -0
  355. package/dist/src/loginrefresh/supervisor.js.map +1 -0
  356. package/dist/src/loginrefresh/types.js +4 -0
  357. package/dist/src/loginrefresh/types.js.map +1 -0
  358. package/dist/src/memory/context.js +251 -0
  359. package/dist/src/memory/context.js.map +1 -0
  360. package/dist/src/memory/datafence.js +5 -0
  361. package/dist/src/memory/datafence.js.map +1 -0
  362. package/dist/src/memory/delivery.js +131 -0
  363. package/dist/src/memory/delivery.js.map +1 -0
  364. package/dist/src/memory/eventtags.js +81 -0
  365. package/dist/src/memory/eventtags.js.map +1 -0
  366. package/dist/src/memory/health.js +713 -0
  367. package/dist/src/memory/health.js.map +1 -0
  368. package/dist/src/memory/import.js +504 -0
  369. package/dist/src/memory/import.js.map +1 -0
  370. package/dist/src/memory/layout.js +252 -0
  371. package/dist/src/memory/layout.js.map +1 -0
  372. package/dist/src/memory/legacy-file.js +100 -0
  373. package/dist/src/memory/legacy-file.js.map +1 -0
  374. package/dist/src/memory/lessons.js +370 -0
  375. package/dist/src/memory/lessons.js.map +1 -0
  376. package/dist/src/memory/ovsession.js +449 -0
  377. package/dist/src/memory/ovsession.js.map +1 -0
  378. package/dist/src/memory/relayout.js +259 -0
  379. package/dist/src/memory/relayout.js.map +1 -0
  380. package/dist/src/memory/roundactivity.js +180 -0
  381. package/dist/src/memory/roundactivity.js.map +1 -0
  382. package/dist/src/memory/selection.js +690 -0
  383. package/dist/src/memory/selection.js.map +1 -0
  384. package/dist/src/memory/shadowstatus.js +69 -0
  385. package/dist/src/memory/shadowstatus.js.map +1 -0
  386. package/dist/src/memory/store.js +83 -0
  387. package/dist/src/memory/store.js.map +1 -0
  388. package/dist/src/memory/textidentity.js +8 -0
  389. package/dist/src/memory/textidentity.js.map +1 -0
  390. package/dist/src/metrics.js +773 -0
  391. package/dist/src/metrics.js.map +1 -0
  392. package/dist/src/missingconfig.js +8 -0
  393. package/dist/src/missingconfig.js.map +1 -0
  394. package/dist/src/npmverbs.js +226 -0
  395. package/dist/src/npmverbs.js.map +1 -0
  396. package/dist/src/onboard/choices.js +191 -0
  397. package/dist/src/onboard/choices.js.map +1 -0
  398. package/dist/src/onboard/engine.js +344 -0
  399. package/dist/src/onboard/engine.js.map +1 -0
  400. package/dist/src/onboard/launch.js +83 -0
  401. package/dist/src/onboard/launch.js.map +1 -0
  402. package/dist/src/onboard/steps/appslug.js +211 -0
  403. package/dist/src/onboard/steps/appslug.js.map +1 -0
  404. package/dist/src/onboard/steps/bringup.js +408 -0
  405. package/dist/src/onboard/steps/bringup.js.map +1 -0
  406. package/dist/src/onboard/steps/compose.js +343 -0
  407. package/dist/src/onboard/steps/compose.js.map +1 -0
  408. package/dist/src/onboard/steps/configdelivery.js +50 -0
  409. package/dist/src/onboard/steps/configdelivery.js.map +1 -0
  410. package/dist/src/onboard/steps/configfill.js +1148 -0
  411. package/dist/src/onboard/steps/configfill.js.map +1 -0
  412. package/dist/src/onboard/steps/deploysh.js +80 -0
  413. package/dist/src/onboard/steps/deploysh.js.map +1 -0
  414. package/dist/src/onboard/steps/embeddings.js +472 -0
  415. package/dist/src/onboard/steps/embeddings.js.map +1 -0
  416. package/dist/src/onboard/steps/escalation.js +571 -0
  417. package/dist/src/onboard/steps/escalation.js.map +1 -0
  418. package/dist/src/onboard/steps/githubapp.js +1092 -0
  419. package/dist/src/onboard/steps/githubapp.js.map +1 -0
  420. package/dist/src/onboard/steps/golive.js +506 -0
  421. package/dist/src/onboard/steps/golive.js.map +1 -0
  422. package/dist/src/onboard/steps/index.js +67 -0
  423. package/dist/src/onboard/steps/index.js.map +1 -0
  424. package/dist/src/onboard/steps/ingress.js +762 -0
  425. package/dist/src/onboard/steps/ingress.js.map +1 -0
  426. package/dist/src/onboard/steps/inventory.js +420 -0
  427. package/dist/src/onboard/steps/inventory.js.map +1 -0
  428. package/dist/src/onboard/steps/llmauth.js +893 -0
  429. package/dist/src/onboard/steps/llmauth.js.map +1 -0
  430. package/dist/src/onboard/steps/memory.js +1060 -0
  431. package/dist/src/onboard/steps/memory.js.map +1 -0
  432. package/dist/src/onboard/steps/memorykeys.js +43 -0
  433. package/dist/src/onboard/steps/memorykeys.js.map +1 -0
  434. package/dist/src/onboard/steps/preflight.js +172 -0
  435. package/dist/src/onboard/steps/preflight.js.map +1 -0
  436. package/dist/src/onboard/steps/proof.js +502 -0
  437. package/dist/src/onboard/steps/proof.js.map +1 -0
  438. package/dist/src/onboard/steps/ssh.js +300 -0
  439. package/dist/src/onboard/steps/ssh.js.map +1 -0
  440. package/dist/src/onboard/steps/topology.js +291 -0
  441. package/dist/src/onboard/steps/topology.js.map +1 -0
  442. package/dist/src/onboard/steps/transport.js +511 -0
  443. package/dist/src/onboard/steps/transport.js.map +1 -0
  444. package/dist/src/onboard/steps/verifyup.js +362 -0
  445. package/dist/src/onboard/steps/verifyup.js.map +1 -0
  446. package/dist/src/onboard/steps/vmdeploy.js +368 -0
  447. package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
  448. package/dist/src/onboard/toml.js +53 -0
  449. package/dist/src/onboard/toml.js.map +1 -0
  450. package/dist/src/opencli.js +508 -0
  451. package/dist/src/opencli.js.map +1 -0
  452. package/dist/src/openpr.js +457 -0
  453. package/dist/src/openpr.js.map +1 -0
  454. package/dist/src/openviking/client.js +821 -0
  455. package/dist/src/openviking/client.js.map +1 -0
  456. package/dist/src/openviking/context.js +218 -0
  457. package/dist/src/openviking/context.js.map +1 -0
  458. package/dist/src/openviking/embedder.js +457 -0
  459. package/dist/src/openviking/embedder.js.map +1 -0
  460. package/dist/src/openviking/gapsink.js +38 -0
  461. package/dist/src/openviking/gapsink.js.map +1 -0
  462. package/dist/src/openviking/health.js +69 -0
  463. package/dist/src/openviking/health.js.map +1 -0
  464. package/dist/src/openviking/index.js +24 -0
  465. package/dist/src/openviking/index.js.map +1 -0
  466. package/dist/src/openviking/lessonfind.js +54 -0
  467. package/dist/src/openviking/lessonfind.js.map +1 -0
  468. package/dist/src/openviking/probe.js +89 -0
  469. package/dist/src/openviking/probe.js.map +1 -0
  470. package/dist/src/openviking/proc.js +95 -0
  471. package/dist/src/openviking/proc.js.map +1 -0
  472. package/dist/src/openviking/provisionreport.js +144 -0
  473. package/dist/src/openviking/provisionreport.js.map +1 -0
  474. package/dist/src/openviking/reembed.js +317 -0
  475. package/dist/src/openviking/reembed.js.map +1 -0
  476. package/dist/src/openviking/relayout.js +153 -0
  477. package/dist/src/openviking/relayout.js.map +1 -0
  478. package/dist/src/openviking/sidecar-main.js +39 -0
  479. package/dist/src/openviking/sidecar-main.js.map +1 -0
  480. package/dist/src/openviking/sidecar.js +79 -0
  481. package/dist/src/openviking/sidecar.js.map +1 -0
  482. package/dist/src/openviking/store.js +1758 -0
  483. package/dist/src/openviking/store.js.map +1 -0
  484. package/dist/src/openviking/supervisor.js +422 -0
  485. package/dist/src/openviking/supervisor.js.map +1 -0
  486. package/dist/src/openviking/types.js +5 -0
  487. package/dist/src/openviking/types.js.map +1 -0
  488. package/dist/src/operator-recovery.js +172 -0
  489. package/dist/src/operator-recovery.js.map +1 -0
  490. package/dist/src/operatormessage.js +44 -0
  491. package/dist/src/operatormessage.js.map +1 -0
  492. package/dist/src/outcomes.js +1361 -0
  493. package/dist/src/outcomes.js.map +1 -0
  494. package/dist/src/piauthcopy.js +5 -0
  495. package/dist/src/piauthcopy.js.map +1 -0
  496. package/dist/src/picli.js +45 -0
  497. package/dist/src/picli.js.map +1 -0
  498. package/dist/src/picwd.js +25 -0
  499. package/dist/src/picwd.js.map +1 -0
  500. package/dist/src/pilogin.js +35 -0
  501. package/dist/src/pilogin.js.map +1 -0
  502. package/dist/src/pingpong.js +1028 -0
  503. package/dist/src/pingpong.js.map +1 -0
  504. package/dist/src/posting-journal.js +441 -0
  505. package/dist/src/posting-journal.js.map +1 -0
  506. package/dist/src/posting-outbox.js +662 -0
  507. package/dist/src/posting-outbox.js.map +1 -0
  508. package/dist/src/posting-receipt.js +203 -0
  509. package/dist/src/posting-receipt.js.map +1 -0
  510. package/dist/src/posting.js +348 -0
  511. package/dist/src/posting.js.map +1 -0
  512. package/dist/src/postops.js +637 -0
  513. package/dist/src/postops.js.map +1 -0
  514. package/dist/src/pr-activity.js +11 -0
  515. package/dist/src/pr-activity.js.map +1 -0
  516. package/dist/src/pr-author.js +5 -0
  517. package/dist/src/pr-author.js.map +1 -0
  518. package/dist/src/pr-conversation.js +355 -0
  519. package/dist/src/pr-conversation.js.map +1 -0
  520. package/dist/src/pr-session-lifecycle.js +542 -0
  521. package/dist/src/pr-session-lifecycle.js.map +1 -0
  522. package/dist/src/preflight.js +341 -0
  523. package/dist/src/preflight.js.map +1 -0
  524. package/dist/src/priorresolve.js +275 -0
  525. package/dist/src/priorresolve.js.map +1 -0
  526. package/dist/src/qcrbin.js +180 -0
  527. package/dist/src/qcrbin.js.map +1 -0
  528. package/dist/src/recheck.js +1398 -0
  529. package/dist/src/recheck.js.map +1 -0
  530. package/dist/src/reconcile.js +820 -0
  531. package/dist/src/reconcile.js.map +1 -0
  532. package/dist/src/recordedfindings.js +15 -0
  533. package/dist/src/recordedfindings.js.map +1 -0
  534. package/dist/src/regression.js +546 -0
  535. package/dist/src/regression.js.map +1 -0
  536. package/dist/src/replay.js +598 -0
  537. package/dist/src/replay.js.map +1 -0
  538. package/dist/src/requiredpasses.js +113 -0
  539. package/dist/src/requiredpasses.js.map +1 -0
  540. package/dist/src/resumecheck.js +168 -0
  541. package/dist/src/resumecheck.js.map +1 -0
  542. package/dist/src/reviewcli.js +671 -0
  543. package/dist/src/reviewcli.js.map +1 -0
  544. package/dist/src/reviewcontext.js +22 -0
  545. package/dist/src/reviewcontext.js.map +1 -0
  546. package/dist/src/reviewdetach.js +574 -0
  547. package/dist/src/reviewdetach.js.map +1 -0
  548. package/dist/src/reviewhistory.js +284 -0
  549. package/dist/src/reviewhistory.js.map +1 -0
  550. package/dist/src/reviewhome.js +26 -0
  551. package/dist/src/reviewhome.js.map +1 -0
  552. package/dist/src/reviewmemory.js +110 -0
  553. package/dist/src/reviewmemory.js.map +1 -0
  554. package/dist/src/reviewrendercli.js +104 -0
  555. package/dist/src/reviewrendercli.js.map +1 -0
  556. package/dist/src/round-work.js +20 -0
  557. package/dist/src/round-work.js.map +1 -0
  558. package/dist/src/runhost.js +72 -0
  559. package/dist/src/runhost.js.map +1 -0
  560. package/dist/src/scanauth.js +25 -0
  561. package/dist/src/scanauth.js.map +1 -0
  562. package/dist/src/scanbox.js +846 -0
  563. package/dist/src/scanbox.js.map +1 -0
  564. package/dist/src/scancli.js +1074 -0
  565. package/dist/src/scancli.js.map +1 -0
  566. package/dist/src/scandriverlog.js +30 -0
  567. package/dist/src/scandriverlog.js.map +1 -0
  568. package/dist/src/session-bridge.js +75 -0
  569. package/dist/src/session-bridge.js.map +1 -0
  570. package/dist/src/session-command-payload.js +29 -0
  571. package/dist/src/session-command-payload.js.map +1 -0
  572. package/dist/src/session-command-supervisor.js +939 -0
  573. package/dist/src/session-command-supervisor.js.map +1 -0
  574. package/dist/src/sessionjsonl.js +85 -0
  575. package/dist/src/sessionjsonl.js.map +1 -0
  576. package/dist/src/sessions.js +541 -0
  577. package/dist/src/sessions.js.map +1 -0
  578. package/dist/src/setup/adapters.js +330 -0
  579. package/dist/src/setup/adapters.js.map +1 -0
  580. package/dist/src/setup/baselines.js +64 -0
  581. package/dist/src/setup/baselines.js.map +1 -0
  582. package/dist/src/setup/caserunner.js +554 -0
  583. package/dist/src/setup/caserunner.js.map +1 -0
  584. package/dist/src/setup/configcommit.js +358 -0
  585. package/dist/src/setup/configcommit.js.map +1 -0
  586. package/dist/src/setup/engine.js +672 -0
  587. package/dist/src/setup/engine.js.map +1 -0
  588. package/dist/src/setup/facts.js +593 -0
  589. package/dist/src/setup/facts.js.map +1 -0
  590. package/dist/src/setup/findings.js +948 -0
  591. package/dist/src/setup/findings.js.map +1 -0
  592. package/dist/src/setup/history.js +338 -0
  593. package/dist/src/setup/history.js.map +1 -0
  594. package/dist/src/setup/interview.js +185 -0
  595. package/dist/src/setup/interview.js.map +1 -0
  596. package/dist/src/setup/ledger.js +207 -0
  597. package/dist/src/setup/ledger.js.map +1 -0
  598. package/dist/src/setup/pins.js +43 -0
  599. package/dist/src/setup/pins.js.map +1 -0
  600. package/dist/src/setup/portrait.js +432 -0
  601. package/dist/src/setup/portrait.js.map +1 -0
  602. package/dist/src/setup/presentation.js +9 -0
  603. package/dist/src/setup/presentation.js.map +1 -0
  604. package/dist/src/setup/profile.js +417 -0
  605. package/dist/src/setup/profile.js.map +1 -0
  606. package/dist/src/setup/replay.js +240 -0
  607. package/dist/src/setup/replay.js.map +1 -0
  608. package/dist/src/setup/replaybuild.js +311 -0
  609. package/dist/src/setup/replaybuild.js.map +1 -0
  610. package/dist/src/setup/replayrun.js +806 -0
  611. package/dist/src/setup/replayrun.js.map +1 -0
  612. package/dist/src/setup/replaysource.js +169 -0
  613. package/dist/src/setup/replaysource.js.map +1 -0
  614. package/dist/src/setup/retune.js +333 -0
  615. package/dist/src/setup/retune.js.map +1 -0
  616. package/dist/src/setup/retunelog.js +51 -0
  617. package/dist/src/setup/retunelog.js.map +1 -0
  618. package/dist/src/setup/scan.js +725 -0
  619. package/dist/src/setup/scan.js.map +1 -0
  620. package/dist/src/setup/scanbuild.js +133 -0
  621. package/dist/src/setup/scanbuild.js.map +1 -0
  622. package/dist/src/setup/scanfacts.js +96 -0
  623. package/dist/src/setup/scanfacts.js.map +1 -0
  624. package/dist/src/setup/scanlease.js +251 -0
  625. package/dist/src/setup/scanlease.js.map +1 -0
  626. package/dist/src/setup/scanreport.js +289 -0
  627. package/dist/src/setup/scanreport.js.map +1 -0
  628. package/dist/src/setup/scanrun.js +1733 -0
  629. package/dist/src/setup/scanrun.js.map +1 -0
  630. package/dist/src/setup/scantree.js +257 -0
  631. package/dist/src/setup/scantree.js.map +1 -0
  632. package/dist/src/setup/stage2.js +617 -0
  633. package/dist/src/setup/stage2.js.map +1 -0
  634. package/dist/src/setup/stage2request.js +259 -0
  635. package/dist/src/setup/stage2request.js.map +1 -0
  636. package/dist/src/setup/survey.js +307 -0
  637. package/dist/src/setup/survey.js.map +1 -0
  638. package/dist/src/setup/surveyrun.js +549 -0
  639. package/dist/src/setup/surveyrun.js.map +1 -0
  640. package/dist/src/setup/textsniff.js +89 -0
  641. package/dist/src/setup/textsniff.js.map +1 -0
  642. package/dist/src/setup/trackersink.js +395 -0
  643. package/dist/src/setup/trackersink.js.map +1 -0
  644. package/dist/src/setup/triage.js +636 -0
  645. package/dist/src/setup/triage.js.map +1 -0
  646. package/dist/src/setup/tuning.js +585 -0
  647. package/dist/src/setup/tuning.js.map +1 -0
  648. package/dist/src/setup/unitcut.js +437 -0
  649. package/dist/src/setup/unitcut.js.map +1 -0
  650. package/dist/src/singleflight.js +72 -0
  651. package/dist/src/singleflight.js.map +1 -0
  652. package/dist/src/slack/announce.js +725 -0
  653. package/dist/src/slack/announce.js.map +1 -0
  654. package/dist/src/slack/api.js +227 -0
  655. package/dist/src/slack/api.js.map +1 -0
  656. package/dist/src/slack/messages.js +158 -0
  657. package/dist/src/slack/messages.js.map +1 -0
  658. package/dist/src/slack/origin.js +550 -0
  659. package/dist/src/slack/origin.js.map +1 -0
  660. package/dist/src/slack/policy.js +225 -0
  661. package/dist/src/slack/policy.js.map +1 -0
  662. package/dist/src/slack/reply.js +153 -0
  663. package/dist/src/slack/reply.js.map +1 -0
  664. package/dist/src/slack/router.js +189 -0
  665. package/dist/src/slack/router.js.map +1 -0
  666. package/dist/src/slack/socket.js +323 -0
  667. package/dist/src/slack/socket.js.map +1 -0
  668. package/dist/src/slack/types.js +11 -0
  669. package/dist/src/slack/types.js.map +1 -0
  670. package/dist/src/sqlitehint.js +30 -0
  671. package/dist/src/sqlitehint.js.map +1 -0
  672. package/dist/src/ssh-observation.js +25 -0
  673. package/dist/src/ssh-observation.js.map +1 -0
  674. package/dist/src/state.js +5869 -0
  675. package/dist/src/state.js.map +1 -0
  676. package/dist/src/statemachine.js +220 -0
  677. package/dist/src/statemachine.js.map +1 -0
  678. package/dist/src/stubs.js +52 -0
  679. package/dist/src/stubs.js.map +1 -0
  680. package/dist/src/takeover.js +506 -0
  681. package/dist/src/takeover.js.map +1 -0
  682. package/dist/src/takeoverpi.js +249 -0
  683. package/dist/src/takeoverpi.js.map +1 -0
  684. package/dist/src/terminal-failure.js +26 -0
  685. package/dist/src/terminal-failure.js.map +1 -0
  686. package/dist/src/thread-resolution-refusal.js +4 -0
  687. package/dist/src/thread-resolution-refusal.js.map +1 -0
  688. package/dist/src/tmux.js +126 -0
  689. package/dist/src/tmux.js.map +1 -0
  690. package/dist/src/trackerid.js +43 -0
  691. package/dist/src/trackerid.js.map +1 -0
  692. package/dist/src/transcriptindex.js +512 -0
  693. package/dist/src/transcriptindex.js.map +1 -0
  694. package/dist/src/tui/actions.js +404 -0
  695. package/dist/src/tui/actions.js.map +1 -0
  696. package/dist/src/tui/alerts.js +523 -0
  697. package/dist/src/tui/alerts.js.map +1 -0
  698. package/dist/src/tui/app.js +1835 -0
  699. package/dist/src/tui/app.js.map +1 -0
  700. package/dist/src/tui/archiveops.js +164 -0
  701. package/dist/src/tui/archiveops.js.map +1 -0
  702. package/dist/src/tui/autofile.js +191 -0
  703. package/dist/src/tui/autofile.js.map +1 -0
  704. package/dist/src/tui/board.js +1143 -0
  705. package/dist/src/tui/board.js.map +1 -0
  706. package/dist/src/tui/candidates.js +699 -0
  707. package/dist/src/tui/candidates.js.map +1 -0
  708. package/dist/src/tui/cellwidth.js +151 -0
  709. package/dist/src/tui/cellwidth.js.map +1 -0
  710. package/dist/src/tui/changelog-store.js +404 -0
  711. package/dist/src/tui/changelog-store.js.map +1 -0
  712. package/dist/src/tui/chord.js +26 -0
  713. package/dist/src/tui/chord.js.map +1 -0
  714. package/dist/src/tui/clipboard.js +81 -0
  715. package/dist/src/tui/clipboard.js.map +1 -0
  716. package/dist/src/tui/cmdbar.js +253 -0
  717. package/dist/src/tui/cmdbar.js.map +1 -0
  718. package/dist/src/tui/companion.js +1275 -0
  719. package/dist/src/tui/companion.js.map +1 -0
  720. package/dist/src/tui/companionplan.js +200 -0
  721. package/dist/src/tui/companionplan.js.map +1 -0
  722. package/dist/src/tui/companiontab.js +61 -0
  723. package/dist/src/tui/companiontab.js.map +1 -0
  724. package/dist/src/tui/confighome.js +105 -0
  725. package/dist/src/tui/confighome.js.map +1 -0
  726. package/dist/src/tui/connect.js +44 -0
  727. package/dist/src/tui/connect.js.map +1 -0
  728. package/dist/src/tui/deploy-ceremony.js +75 -0
  729. package/dist/src/tui/deploy-ceremony.js.map +1 -0
  730. package/dist/src/tui/deploy-drain.js +93 -0
  731. package/dist/src/tui/deploy-drain.js.map +1 -0
  732. package/dist/src/tui/deploy-process.js +234 -0
  733. package/dist/src/tui/deploy-process.js.map +1 -0
  734. package/dist/src/tui/deploytab.js +1263 -0
  735. package/dist/src/tui/deploytab.js.map +1 -0
  736. package/dist/src/tui/design.js +111 -0
  737. package/dist/src/tui/design.js.map +1 -0
  738. package/dist/src/tui/digesttab.js +148 -0
  739. package/dist/src/tui/digesttab.js.map +1 -0
  740. package/dist/src/tui/emptystate.js +32 -0
  741. package/dist/src/tui/emptystate.js.map +1 -0
  742. package/dist/src/tui/evidence.js +790 -0
  743. package/dist/src/tui/evidence.js.map +1 -0
  744. package/dist/src/tui/feed.js +415 -0
  745. package/dist/src/tui/feed.js.map +1 -0
  746. package/dist/src/tui/feedbacktab.js +907 -0
  747. package/dist/src/tui/feedbacktab.js.map +1 -0
  748. package/dist/src/tui/filters.js +72 -0
  749. package/dist/src/tui/filters.js.map +1 -0
  750. package/dist/src/tui/ghread.js +272 -0
  751. package/dist/src/tui/ghread.js.map +1 -0
  752. package/dist/src/tui/historypull.js +61 -0
  753. package/dist/src/tui/historypull.js.map +1 -0
  754. package/dist/src/tui/inboxpull.js +197 -0
  755. package/dist/src/tui/inboxpull.js.map +1 -0
  756. package/dist/src/tui/index.js +1425 -0
  757. package/dist/src/tui/index.js.map +1 -0
  758. package/dist/src/tui/issuesink.js +206 -0
  759. package/dist/src/tui/issuesink.js.map +1 -0
  760. package/dist/src/tui/jsonfile.js +32 -0
  761. package/dist/src/tui/jsonfile.js.map +1 -0
  762. package/dist/src/tui/keys.js +97 -0
  763. package/dist/src/tui/keys.js.map +1 -0
  764. package/dist/src/tui/layout.js +70 -0
  765. package/dist/src/tui/layout.js.map +1 -0
  766. package/dist/src/tui/learning.js +469 -0
  767. package/dist/src/tui/learning.js.map +1 -0
  768. package/dist/src/tui/lessonswitch.js +103 -0
  769. package/dist/src/tui/lessonswitch.js.map +1 -0
  770. package/dist/src/tui/livecontrol.js +167 -0
  771. package/dist/src/tui/livecontrol.js.map +1 -0
  772. package/dist/src/tui/memforward.js +310 -0
  773. package/dist/src/tui/memforward.js.map +1 -0
  774. package/dist/src/tui/memincident.js +287 -0
  775. package/dist/src/tui/memincident.js.map +1 -0
  776. package/dist/src/tui/memory.js +485 -0
  777. package/dist/src/tui/memory.js.map +1 -0
  778. package/dist/src/tui/memtab.js +513 -0
  779. package/dist/src/tui/memtab.js.map +1 -0
  780. package/dist/src/tui/metricstab.js +979 -0
  781. package/dist/src/tui/metricstab.js.map +1 -0
  782. package/dist/src/tui/model.js +104 -0
  783. package/dist/src/tui/model.js.map +1 -0
  784. package/dist/src/tui/mouse.js +83 -0
  785. package/dist/src/tui/mouse.js.map +1 -0
  786. package/dist/src/tui/msg.js +196 -0
  787. package/dist/src/tui/msg.js.map +1 -0
  788. package/dist/src/tui/nativecompanion.js +321 -0
  789. package/dist/src/tui/nativecompanion.js.map +1 -0
  790. package/dist/src/tui/openurl.js +17 -0
  791. package/dist/src/tui/openurl.js.map +1 -0
  792. package/dist/src/tui/paint.js +501 -0
  793. package/dist/src/tui/paint.js.map +1 -0
  794. package/dist/src/tui/panes.js +74 -0
  795. package/dist/src/tui/panes.js.map +1 -0
  796. package/dist/src/tui/pipane.js +166 -0
  797. package/dist/src/tui/pipane.js.map +1 -0
  798. package/dist/src/tui/poll-hooks.js +11 -0
  799. package/dist/src/tui/poll-hooks.js.map +1 -0
  800. package/dist/src/tui/poll.js +113 -0
  801. package/dist/src/tui/poll.js.map +1 -0
  802. package/dist/src/tui/proposals.js +715 -0
  803. package/dist/src/tui/proposals.js.map +1 -0
  804. package/dist/src/tui/prpause.js +132 -0
  805. package/dist/src/tui/prpause.js.map +1 -0
  806. package/dist/src/tui/reload.js +351 -0
  807. package/dist/src/tui/reload.js.map +1 -0
  808. package/dist/src/tui/remote.js +312 -0
  809. package/dist/src/tui/remote.js.map +1 -0
  810. package/dist/src/tui/rollover.js +767 -0
  811. package/dist/src/tui/rollover.js.map +1 -0
  812. package/dist/src/tui/scantab.js +1053 -0
  813. package/dist/src/tui/scantab.js.map +1 -0
  814. package/dist/src/tui/setupentry.js +81 -0
  815. package/dist/src/tui/setupentry.js.map +1 -0
  816. package/dist/src/tui/sidebar.js +602 -0
  817. package/dist/src/tui/sidebar.js.map +1 -0
  818. package/dist/src/tui/sshtriage.js +393 -0
  819. package/dist/src/tui/sshtriage.js.map +1 -0
  820. package/dist/src/tui/startup.js +172 -0
  821. package/dist/src/tui/startup.js.map +1 -0
  822. package/dist/src/tui/tabs.js +41 -0
  823. package/dist/src/tui/tabs.js.map +1 -0
  824. package/dist/src/tui/termmirror.js +217 -0
  825. package/dist/src/tui/termmirror.js.map +1 -0
  826. package/dist/src/tui/theme.js +95 -0
  827. package/dist/src/tui/theme.js.map +1 -0
  828. package/dist/src/tui/ticketstab.js +224 -0
  829. package/dist/src/tui/ticketstab.js.map +1 -0
  830. package/dist/src/tui/tmuxsocket.js +101 -0
  831. package/dist/src/tui/tmuxsocket.js.map +1 -0
  832. package/dist/src/tui/trackertickets.js +77 -0
  833. package/dist/src/tui/trackertickets.js.map +1 -0
  834. package/dist/src/tui/unwedge.js +245 -0
  835. package/dist/src/tui/unwedge.js.map +1 -0
  836. package/dist/src/tui/viewer.js +479 -0
  837. package/dist/src/tui/viewer.js.map +1 -0
  838. package/dist/src/tui/viewerinput.js +125 -0
  839. package/dist/src/tui/viewerinput.js.map +1 -0
  840. package/dist/src/updatenotice.js +143 -0
  841. package/dist/src/updatenotice.js.map +1 -0
  842. package/dist/src/usage.js +1007 -0
  843. package/dist/src/usage.js.map +1 -0
  844. package/dist/src/verdicts.js +3848 -0
  845. package/dist/src/verdicts.js.map +1 -0
  846. package/dist/src/version.js +58 -0
  847. package/dist/src/version.js.map +1 -0
  848. package/dist/src/wakes.js +411 -0
  849. package/dist/src/wakes.js.map +1 -0
  850. package/dist/src/webhook.js +725 -0
  851. package/dist/src/webhook.js.map +1 -0
  852. package/dist/src/webhookheal.js +238 -0
  853. package/dist/src/webhookheal.js.map +1 -0
  854. package/dist/src/wire.js +1801 -0
  855. package/dist/src/wire.js.map +1 -0
  856. package/dist/src/worker-session-host.js +531 -0
  857. package/dist/src/worker-session-host.js.map +1 -0
  858. package/dist/src/workerbreaker.js +112 -0
  859. package/dist/src/workerbreaker.js.map +1 -0
  860. package/dist/src/workercontrol.js +49 -0
  861. package/dist/src/workercontrol.js.map +1 -0
  862. package/dist/src/workerlease.js +351 -0
  863. package/dist/src/workerlease.js.map +1 -0
  864. package/dist/src/workerliveness.js +84 -0
  865. package/dist/src/workerliveness.js.map +1 -0
  866. package/dist/src/workerlog.js +65 -0
  867. package/dist/src/workerlog.js.map +1 -0
  868. package/dist/src/workerruntime.js +10 -0
  869. package/dist/src/workerruntime.js.map +1 -0
  870. package/dist/src/workersupervisor.js +1206 -0
  871. package/dist/src/workersupervisor.js.map +1 -0
  872. package/dist/src/workerusage.js +23 -0
  873. package/dist/src/workerusage.js.map +1 -0
  874. package/dist/src/workerworld.js +332 -0
  875. package/dist/src/workerworld.js.map +1 -0
  876. package/dist/src/worktrees.js +984 -0
  877. package/dist/src/worktrees.js.map +1 -0
  878. package/docker/Dockerfile +284 -0
  879. package/docker/Dockerfile.dockerignore +35 -0
  880. package/docker/README.md +221 -0
  881. package/docker/companion/Dockerfile +139 -0
  882. package/docker/companion/agents/evidence.md +34 -0
  883. package/docker/companion/agents/explore.md +26 -0
  884. package/docker/companion/agents/pass-survey.md +49 -0
  885. package/docker/companion/agents/study.md +26 -0
  886. package/docker/companion/entrypoint.sh +343 -0
  887. package/docker/companion/memory-health.mjs +269 -0
  888. package/docker/entrypoint.sh +782 -0
  889. package/docker/memory/Dockerfile +191 -0
  890. package/docker/memory/Dockerfile.dockerignore +20 -0
  891. package/docker/memory/README.md +60 -0
  892. package/docker/memory/entrypoint.sh +163 -0
  893. package/docker/setid/qcr-setid.c +109 -0
  894. package/docker/tunnel/Dockerfile.cloudflared +51 -0
  895. package/docker/tunnel/Dockerfile.ngrok +40 -0
  896. package/docker/tunnel/sidecar.mjs +411 -0
  897. package/docker/vm/tmux.conf +26 -0
  898. package/docker-compose.yml +348 -0
  899. package/extensions/cli/bash-guard-ambient.ts +71 -0
  900. package/extensions/cli/bash-guard.ts +12 -0
  901. package/extensions/cli/companion-guard.ts +8 -0
  902. package/extensions/cli/debug-round.ts +9 -0
  903. package/extensions/cli/inject-bridge.ts +8 -0
  904. package/extensions/cli/ledger-pin.ts +13 -0
  905. package/extensions/cli/lens.ts +22 -0
  906. package/extensions/cli/muted-theme.ts +9 -0
  907. package/extensions/cli/onboarding.ts +8 -0
  908. package/extensions/cli/setup.ts +8 -0
  909. package/extensions/cli/tui-view.ts +8 -0
  910. package/extensions/companion-guard.ts +411 -0
  911. package/extensions/debug-round.ts +281 -0
  912. package/extensions/inject-bridge.ts +630 -0
  913. package/extensions/inline-bash.ts +102 -0
  914. package/extensions/muted-theme.json +68 -0
  915. package/extensions/muted-theme.ts +23 -0
  916. package/extensions/onboarding.ts +233 -0
  917. package/extensions/setup-replay.ts +336 -0
  918. package/extensions/setup-scan.ts +1236 -0
  919. package/extensions/setup-triage.ts +190 -0
  920. package/extensions/setup.ts +1428 -0
  921. package/extensions/tui-view.ts +489 -0
  922. package/extensions/vendor/openviking-pi/LICENSE +201 -0
  923. package/extensions/vendor/openviking-pi/NOTICE +68 -0
  924. package/extensions/vendor/openviking-pi/README.md +550 -0
  925. package/extensions/vendor/openviking-pi/client.ts +588 -0
  926. package/extensions/vendor/openviking-pi/config.json +28 -0
  927. package/extensions/vendor/openviking-pi/config.ts +271 -0
  928. package/extensions/vendor/openviking-pi/index.ts +523 -0
  929. package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
  930. package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
  931. package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
  932. package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
  933. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
  934. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
  935. package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
  936. package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
  937. package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
  938. package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
  939. package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
  940. package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
  941. package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
  942. package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
  943. package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
  944. package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
  945. package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
  946. package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
  947. package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
  948. package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
  949. package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
  950. package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
  951. package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
  952. package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
  953. package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
  954. package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
  955. package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
  956. package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
  957. package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
  958. package/extensions/vendor/openviking-pi/recall.ts +149 -0
  959. package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
  960. package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
  961. package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
  962. package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
  963. package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
  964. package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
  965. package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
  966. package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
  967. package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
  968. package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
  969. package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
  970. package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
  971. package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
  972. package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
  973. package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
  974. package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
  975. package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  976. package/extensions/vendor/openviking-pi/sync.ts +163 -0
  977. package/extensions/vendor/openviking-pi/takeover.ts +36 -0
  978. package/extensions/vendor/openviking-pi/tools.ts +479 -0
  979. package/extensions/vendor/openviking-pi/upstream.json +48 -0
  980. package/package.json +138 -4
  981. package/profiles/default/README.md +21 -0
  982. package/profiles/default/addenda/.gitkeep +0 -0
  983. package/profiles/default/profile.toml +17 -0
  984. package/profiles/example-django/README.md +21 -0
  985. package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  986. package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  987. package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  988. package/profiles/example-django/addenda/40-orm-usage.md +19 -0
  989. package/profiles/example-django/addenda/50-declared-shape.md +10 -0
  990. package/profiles/example-django/addenda/60-config-placement.md +14 -0
  991. package/profiles/example-django/heuristics.toml +24 -0
  992. package/profiles/example-django/profile.toml +9 -0
  993. package/profiles/fast/README.md +24 -0
  994. package/profiles/fast/profile.toml +59 -0
  995. package/profiles/minimal/README.md +17 -0
  996. package/profiles/minimal/addenda/.gitkeep +0 -0
  997. package/profiles/minimal/profile.toml +8 -0
  998. package/profiles/snappy/README.md +24 -0
  999. package/profiles/snappy/addenda/10-race-as-question.md +6 -0
  1000. package/profiles/snappy/profile.toml +46 -0
  1001. package/prompts/companion-day-open.md +46 -0
  1002. package/prompts/companion-first-run.md +104 -0
  1003. package/prompts/companion.md +821 -0
  1004. package/prompts/conversation-system.md +117 -0
  1005. package/prompts/eod-sweep.md +120 -0
  1006. package/prompts/onboarding.md +74 -0
  1007. package/prompts/pass-common.md +52 -0
  1008. package/prompts/pass-quality-common.md +22 -0
  1009. package/prompts/round-first.md +874 -0
  1010. package/prompts/round-follow-up.md +991 -0
  1011. package/prompts/round-re-review.md +889 -0
  1012. package/prompts/round-recheck.md +196 -0
  1013. package/prompts/round-staleness.md +856 -0
  1014. package/schemas/review-result.v1.json +391 -0
  1015. package/schemas/scan-result.v1.json +421 -0
  1016. package/schemas/version.v1.json +57 -0
  1017. package/scripts/approve-pr.sh +16 -0
  1018. package/scripts/build-stamp.mjs +67 -0
  1019. package/scripts/commit-verdict.sh +155 -0
  1020. package/scripts/companion-e2e.sh +250 -0
  1021. package/scripts/companion-memory-forward.mjs +1966 -0
  1022. package/scripts/config-fingerprint.sh +28 -0
  1023. package/scripts/deploy-manifest.mjs +141 -0
  1024. package/scripts/deploy.sh +1435 -0
  1025. package/scripts/directory-lock.mjs +193 -0
  1026. package/scripts/heartbeat-fresh.mjs +54 -0
  1027. package/scripts/install-locator.sh +328 -0
  1028. package/scripts/install-wrapper.sh +76 -0
  1029. package/scripts/memory-search-check.mjs +98 -0
  1030. package/scripts/mint-token.sh +212 -0
  1031. package/scripts/openviking-embedder.py +392 -0
  1032. package/scripts/openviking-ov-conf.mjs +161 -0
  1033. package/scripts/openviking-provision.mjs +1122 -0
  1034. package/scripts/openviking-reembed.mjs +1154 -0
  1035. package/scripts/post-review.sh +16 -0
  1036. package/scripts/pr-checks.sh +256 -0
  1037. package/scripts/pr-context.py +174 -0
  1038. package/scripts/pr-context.sh +63 -0
  1039. package/scripts/pr-self.sh +144 -0
  1040. package/scripts/pr-threads.sh +145 -0
  1041. package/scripts/qcr +15 -0
  1042. package/scripts/qcr-host.sh +769 -0
  1043. package/scripts/render-review.sh +24 -0
  1044. package/scripts/reply-thread.sh +16 -0
  1045. package/scripts/required-passes.py +112 -0
  1046. package/scripts/smoke-companion.sh +211 -0
  1047. package/scripts/tui-deploy-runner.mjs +61 -0
  1048. package/scripts/verify-dist.mjs +201 -0
  1049. package/scripts/vm-ghcr-login.sh +188 -0
  1050. package/seeds/agents/pass-baseline.md +61 -0
  1051. package/seeds/agents/pass-contracts.md +30 -0
  1052. package/seeds/agents/pass-data-safety.md +54 -0
  1053. package/seeds/agents/pass-delivery.md +37 -0
  1054. package/seeds/agents/pass-prod-risk.md +24 -0
  1055. package/seeds/agents/pass-security.md +31 -0
  1056. package/seeds/agents/pass-statelessness.md +27 -0
  1057. package/seeds/agents/pass-ui.md +23 -0
  1058. package/seeds/agents/pass-validate.md +50 -0
  1059. package/seeds/prompts/conversation-system.md +117 -0
  1060. package/seeds/prompts/eod-sweep.md +120 -0
  1061. package/seeds/prompts/pass-common.md +52 -0
  1062. package/skills/companion/adjudicate/SKILL.md +247 -0
  1063. package/skills/companion/evidence/SKILL.md +150 -0
  1064. package/skills/companion/land/SKILL.md +200 -0
  1065. package/skills/companion/replay/SKILL.md +138 -0
  1066. package/skills/companion/setup/SKILL.md +167 -0
  1067. package/skills/companion/survey/SKILL.md +160 -0
  1068. package/skills/selfimprove/README.md +34 -0
  1069. package/skills/selfimprove/SKILL.md +127 -0
  1070. package/skills/selfimprove/agents/openai.yaml +2 -0
  1071. package/skills/selfimprove/commands/selfimprove.md +6 -0
  1072. package/skills/selfimprove/references/fix-loop.md +49 -0
  1073. package/skills/selfimprove/references/ledger.md +20 -0
  1074. package/skills/selfimprove/references/setup.md +40 -0
  1075. package/src/anomalies.ts +566 -0
  1076. package/src/answerworker.ts +251 -0
  1077. package/src/appstamp.ts +65 -0
  1078. package/src/archive/admission.ts +3 -0
  1079. package/src/archive/archivedb.ts +548 -0
  1080. package/src/archive/archiver.ts +1963 -0
  1081. package/src/archive/archivewrite.ts +279 -0
  1082. package/src/archive/bundle.ts +333 -0
  1083. package/src/archive/dbmove.ts +911 -0
  1084. package/src/archive/duplicatecounts.ts +98 -0
  1085. package/src/archive/eligibility.ts +181 -0
  1086. package/src/archive/extract.ts +342 -0
  1087. package/src/archive/fsutil.ts +415 -0
  1088. package/src/archive/gzfile.ts +370 -0
  1089. package/src/archive/kinds.ts +21 -0
  1090. package/src/archive/livecheckpoint.ts +217 -0
  1091. package/src/archive/liveretention.ts +180 -0
  1092. package/src/archive/manifest.ts +519 -0
  1093. package/src/archive/overseer.ts +336 -0
  1094. package/src/archive/piglobal.ts +174 -0
  1095. package/src/archive/plan.ts +612 -0
  1096. package/src/archive/prarchive.ts +984 -0
  1097. package/src/archive/prfiles.ts +406 -0
  1098. package/src/archive/report.ts +252 -0
  1099. package/src/archive/restore.ts +330 -0
  1100. package/src/archive/rows.ts +187 -0
  1101. package/src/archive/seal.ts +630 -0
  1102. package/src/archive/status.ts +719 -0
  1103. package/src/archive/storepass.ts +581 -0
  1104. package/src/backfill.ts +362 -0
  1105. package/src/bench/cost.ts +113 -0
  1106. package/src/bench/exec.ts +90 -0
  1107. package/src/bench/values.ts +13 -0
  1108. package/src/botcontrol.ts +176 -0
  1109. package/src/botworker.ts +953 -0
  1110. package/src/capture.ts +321 -0
  1111. package/src/cases.ts +1789 -0
  1112. package/src/channelfs.ts +26 -0
  1113. package/src/checklist.ts +506 -0
  1114. package/src/ciretry.ts +141 -0
  1115. package/src/cli.ts +2271 -0
  1116. package/src/cliops.ts +384 -0
  1117. package/src/clonedir.ts +15 -0
  1118. package/src/commandeffects.ts +57 -0
  1119. package/src/commands.ts +1678 -0
  1120. package/src/companioninbox.ts +239 -0
  1121. package/src/compare.ts +957 -0
  1122. package/src/config.ts +2050 -0
  1123. package/src/configcheck.ts +133 -0
  1124. package/src/coreext.ts +47 -0
  1125. package/src/daemon.ts +1302 -0
  1126. package/src/debuground.ts +927 -0
  1127. package/src/debugroundpi.ts +262 -0
  1128. package/src/diagnostics.ts +14 -0
  1129. package/src/digest.ts +183 -0
  1130. package/src/dispatch.ts +2250 -0
  1131. package/src/engine/answerguard.ts +27 -0
  1132. package/src/engine/botsdk.ts +336 -0
  1133. package/src/engine/caserunner-sdk.ts +423 -0
  1134. package/src/engine/debugpi.ts +235 -0
  1135. package/src/eod.ts +669 -0
  1136. package/src/eodpinwatch.ts +556 -0
  1137. package/src/escalation.ts +1253 -0
  1138. package/src/eventloop.ts +1045 -0
  1139. package/src/execboundary.ts +633 -0
  1140. package/src/feedback.ts +1162 -0
  1141. package/src/findingscheck.ts +298 -0
  1142. package/src/fsdenied.ts +24 -0
  1143. package/src/ghread.ts +242 -0
  1144. package/src/github.ts +1497 -0
  1145. package/src/homes.ts +175 -0
  1146. package/src/hostroute.ts +53 -0
  1147. package/src/improvements.ts +787 -0
  1148. package/src/install.ts +1103 -0
  1149. package/src/installkind.ts +50 -0
  1150. package/src/ipc/capability.ts +333 -0
  1151. package/src/ipc/client.ts +269 -0
  1152. package/src/ipc/control.ts +90 -0
  1153. package/src/ipc/ghreadshim.ts +94 -0
  1154. package/src/ipc/launcher.ts +65 -0
  1155. package/src/ipc/permits.ts +362 -0
  1156. package/src/ipc/postshim.ts +164 -0
  1157. package/src/ipc/protocol.ts +342 -0
  1158. package/src/ipc/server.ts +371 -0
  1159. package/src/ipc/workerspec.ts +218 -0
  1160. package/src/learner/learner.ts +1655 -0
  1161. package/src/learner/miner.ts +1290 -0
  1162. package/src/learner/packet.ts +340 -0
  1163. package/src/learner/receipt.ts +168 -0
  1164. package/src/learner/status.ts +107 -0
  1165. package/src/ledgercheck.ts +157 -0
  1166. package/src/lessonledger.ts +703 -0
  1167. package/src/lessonselect.ts +888 -0
  1168. package/src/lessonshadow.ts +810 -0
  1169. package/src/levers.ts +393 -0
  1170. package/src/live/projection.ts +491 -0
  1171. package/src/live/publish.ts +92 -0
  1172. package/src/live/read.ts +86 -0
  1173. package/src/livecli.ts +336 -0
  1174. package/src/loginhint.ts +56 -0
  1175. package/src/loginrefresh/atomic.ts +76 -0
  1176. package/src/loginrefresh/lock.ts +104 -0
  1177. package/src/loginrefresh/main.ts +59 -0
  1178. package/src/loginrefresh/pirefresh.ts +57 -0
  1179. package/src/loginrefresh/refresher.ts +326 -0
  1180. package/src/loginrefresh/supervisor.ts +273 -0
  1181. package/src/loginrefresh/types.ts +35 -0
  1182. package/src/memory/context.ts +385 -0
  1183. package/src/memory/datafence.ts +9 -0
  1184. package/src/memory/delivery.ts +171 -0
  1185. package/src/memory/eventtags.ts +80 -0
  1186. package/src/memory/health.ts +953 -0
  1187. package/src/memory/import.ts +664 -0
  1188. package/src/memory/layout.ts +261 -0
  1189. package/src/memory/legacy-file.ts +104 -0
  1190. package/src/memory/lessons.ts +447 -0
  1191. package/src/memory/ovsession.ts +601 -0
  1192. package/src/memory/relayout.ts +335 -0
  1193. package/src/memory/roundactivity.ts +209 -0
  1194. package/src/memory/selection.ts +989 -0
  1195. package/src/memory/shadowstatus.ts +125 -0
  1196. package/src/memory/store.ts +275 -0
  1197. package/src/memory/textidentity.ts +8 -0
  1198. package/src/metrics.ts +1056 -0
  1199. package/src/missingconfig.ts +7 -0
  1200. package/src/npmverbs.ts +264 -0
  1201. package/src/onboard/choices.ts +222 -0
  1202. package/src/onboard/engine.ts +518 -0
  1203. package/src/onboard/launch.ts +115 -0
  1204. package/src/onboard/steps/appslug.ts +265 -0
  1205. package/src/onboard/steps/bringup.ts +482 -0
  1206. package/src/onboard/steps/compose.ts +459 -0
  1207. package/src/onboard/steps/configdelivery.ts +58 -0
  1208. package/src/onboard/steps/configfill.ts +1352 -0
  1209. package/src/onboard/steps/deploysh.ts +105 -0
  1210. package/src/onboard/steps/embeddings.ts +550 -0
  1211. package/src/onboard/steps/escalation.ts +640 -0
  1212. package/src/onboard/steps/githubapp.ts +1414 -0
  1213. package/src/onboard/steps/golive.ts +577 -0
  1214. package/src/onboard/steps/index.ts +71 -0
  1215. package/src/onboard/steps/ingress.ts +907 -0
  1216. package/src/onboard/steps/inventory.ts +526 -0
  1217. package/src/onboard/steps/llmauth.ts +1095 -0
  1218. package/src/onboard/steps/memory.ts +1290 -0
  1219. package/src/onboard/steps/memorykeys.ts +85 -0
  1220. package/src/onboard/steps/preflight.ts +191 -0
  1221. package/src/onboard/steps/proof.ts +624 -0
  1222. package/src/onboard/steps/ssh.ts +377 -0
  1223. package/src/onboard/steps/topology.ts +337 -0
  1224. package/src/onboard/steps/transport.ts +665 -0
  1225. package/src/onboard/steps/verifyup.ts +448 -0
  1226. package/src/onboard/steps/vmdeploy.ts +442 -0
  1227. package/src/onboard/toml.ts +50 -0
  1228. package/src/opencli.ts +591 -0
  1229. package/src/openpr.ts +568 -0
  1230. package/src/openviking/client.ts +1131 -0
  1231. package/src/openviking/context.ts +263 -0
  1232. package/src/openviking/embedder.ts +516 -0
  1233. package/src/openviking/gapsink.ts +63 -0
  1234. package/src/openviking/health.ts +83 -0
  1235. package/src/openviking/index.ts +119 -0
  1236. package/src/openviking/lessonfind.ts +92 -0
  1237. package/src/openviking/probe.ts +129 -0
  1238. package/src/openviking/proc.ts +110 -0
  1239. package/src/openviking/provisionreport.ts +160 -0
  1240. package/src/openviking/reembed.ts +412 -0
  1241. package/src/openviking/relayout.ts +177 -0
  1242. package/src/openviking/sidecar-main.ts +38 -0
  1243. package/src/openviking/sidecar.ts +106 -0
  1244. package/src/openviking/store.ts +2025 -0
  1245. package/src/openviking/supervisor.ts +474 -0
  1246. package/src/openviking/types.ts +174 -0
  1247. package/src/operator-recovery.ts +237 -0
  1248. package/src/operatormessage.ts +52 -0
  1249. package/src/outcomes.ts +1670 -0
  1250. package/src/piauthcopy.ts +12 -0
  1251. package/src/picli.ts +49 -0
  1252. package/src/picwd.ts +25 -0
  1253. package/src/pilogin.ts +37 -0
  1254. package/src/pingpong.ts +1339 -0
  1255. package/src/posting-journal.ts +549 -0
  1256. package/src/posting-outbox.ts +802 -0
  1257. package/src/posting-receipt.ts +324 -0
  1258. package/src/posting.ts +603 -0
  1259. package/src/postops.ts +832 -0
  1260. package/src/pr-activity.ts +20 -0
  1261. package/src/pr-author.ts +6 -0
  1262. package/src/pr-conversation.ts +432 -0
  1263. package/src/pr-session-lifecycle.ts +631 -0
  1264. package/src/preflight.ts +391 -0
  1265. package/src/priorresolve.ts +343 -0
  1266. package/src/qcrbin.ts +186 -0
  1267. package/src/recheck.ts +1775 -0
  1268. package/src/reconcile.ts +1030 -0
  1269. package/src/recordedfindings.ts +27 -0
  1270. package/src/regression.ts +833 -0
  1271. package/src/replay.ts +816 -0
  1272. package/src/requiredpasses.ts +148 -0
  1273. package/src/resumecheck.ts +246 -0
  1274. package/src/reviewcli.ts +787 -0
  1275. package/src/reviewcontext.ts +35 -0
  1276. package/src/reviewdetach.ts +734 -0
  1277. package/src/reviewhistory.ts +427 -0
  1278. package/src/reviewhome.ts +36 -0
  1279. package/src/reviewmemory.ts +133 -0
  1280. package/src/reviewrendercli.ts +139 -0
  1281. package/src/round-work.ts +42 -0
  1282. package/src/runhost.ts +108 -0
  1283. package/src/scanauth.ts +36 -0
  1284. package/src/scanbox.ts +1081 -0
  1285. package/src/scancli.ts +1296 -0
  1286. package/src/scandriverlog.ts +32 -0
  1287. package/src/session-bridge.ts +139 -0
  1288. package/src/session-command-payload.ts +35 -0
  1289. package/src/session-command-supervisor.ts +1142 -0
  1290. package/src/sessionjsonl.ts +88 -0
  1291. package/src/sessions.ts +659 -0
  1292. package/src/setup/adapters.ts +335 -0
  1293. package/src/setup/baselines.ts +110 -0
  1294. package/src/setup/caserunner.ts +611 -0
  1295. package/src/setup/configcommit.ts +443 -0
  1296. package/src/setup/engine.ts +1001 -0
  1297. package/src/setup/facts.ts +627 -0
  1298. package/src/setup/findings.ts +1264 -0
  1299. package/src/setup/history.ts +443 -0
  1300. package/src/setup/interview.ts +243 -0
  1301. package/src/setup/ledger.ts +324 -0
  1302. package/src/setup/pins.ts +66 -0
  1303. package/src/setup/portrait.ts +635 -0
  1304. package/src/setup/presentation.ts +18 -0
  1305. package/src/setup/profile.ts +508 -0
  1306. package/src/setup/replay.ts +370 -0
  1307. package/src/setup/replaybuild.ts +392 -0
  1308. package/src/setup/replayrun.ts +1096 -0
  1309. package/src/setup/replaysource.ts +198 -0
  1310. package/src/setup/retune.ts +415 -0
  1311. package/src/setup/retunelog.ts +95 -0
  1312. package/src/setup/scan.ts +1033 -0
  1313. package/src/setup/scanbuild.ts +168 -0
  1314. package/src/setup/scanfacts.ts +107 -0
  1315. package/src/setup/scanlease.ts +297 -0
  1316. package/src/setup/scanreport.ts +373 -0
  1317. package/src/setup/scanrun.ts +2268 -0
  1318. package/src/setup/scantree.ts +259 -0
  1319. package/src/setup/stage2.ts +926 -0
  1320. package/src/setup/stage2request.ts +347 -0
  1321. package/src/setup/survey.ts +458 -0
  1322. package/src/setup/surveyrun.ts +758 -0
  1323. package/src/setup/textsniff.ts +95 -0
  1324. package/src/setup/trackersink.ts +433 -0
  1325. package/src/setup/triage.ts +768 -0
  1326. package/src/setup/tuning.ts +766 -0
  1327. package/src/setup/unitcut.ts +523 -0
  1328. package/src/singleflight.ts +86 -0
  1329. package/src/slack/announce.ts +911 -0
  1330. package/src/slack/api.ts +303 -0
  1331. package/src/slack/messages.ts +288 -0
  1332. package/src/slack/origin.ts +599 -0
  1333. package/src/slack/policy.ts +354 -0
  1334. package/src/slack/reply.ts +201 -0
  1335. package/src/slack/router.ts +285 -0
  1336. package/src/slack/socket.ts +384 -0
  1337. package/src/slack/types.ts +108 -0
  1338. package/src/sqlitehint.ts +32 -0
  1339. package/src/ssh-observation.ts +30 -0
  1340. package/src/state.ts +8243 -0
  1341. package/src/statemachine.ts +369 -0
  1342. package/src/stubs.ts +68 -0
  1343. package/src/takeover.ts +634 -0
  1344. package/src/takeoverpi.ts +293 -0
  1345. package/src/terminal-failure.ts +41 -0
  1346. package/src/thread-resolution-refusal.ts +2 -0
  1347. package/src/tmux.ts +153 -0
  1348. package/src/trackerid.ts +41 -0
  1349. package/src/transcriptindex.ts +665 -0
  1350. package/src/tui/actions.ts +484 -0
  1351. package/src/tui/alerts.ts +666 -0
  1352. package/src/tui/app.ts +1979 -0
  1353. package/src/tui/archiveops.ts +192 -0
  1354. package/src/tui/autofile.ts +233 -0
  1355. package/src/tui/board.ts +1432 -0
  1356. package/src/tui/candidates.ts +889 -0
  1357. package/src/tui/cellwidth.ts +148 -0
  1358. package/src/tui/changelog-store.ts +503 -0
  1359. package/src/tui/chord.ts +25 -0
  1360. package/src/tui/clipboard.ts +92 -0
  1361. package/src/tui/cmdbar.ts +296 -0
  1362. package/src/tui/companion.ts +1427 -0
  1363. package/src/tui/companionplan.ts +247 -0
  1364. package/src/tui/companiontab.ts +83 -0
  1365. package/src/tui/confighome.ts +155 -0
  1366. package/src/tui/connect.ts +68 -0
  1367. package/src/tui/deploy-ceremony.ts +104 -0
  1368. package/src/tui/deploy-drain.ts +129 -0
  1369. package/src/tui/deploy-process.ts +265 -0
  1370. package/src/tui/deploytab.ts +1523 -0
  1371. package/src/tui/design.ts +125 -0
  1372. package/src/tui/digesttab.ts +214 -0
  1373. package/src/tui/emptystate.ts +39 -0
  1374. package/src/tui/evidence.ts +999 -0
  1375. package/src/tui/feed.ts +604 -0
  1376. package/src/tui/feedbacktab.ts +1077 -0
  1377. package/src/tui/filters.ts +90 -0
  1378. package/src/tui/ghread.ts +321 -0
  1379. package/src/tui/historypull.ts +81 -0
  1380. package/src/tui/inboxpull.ts +236 -0
  1381. package/src/tui/index.ts +1565 -0
  1382. package/src/tui/issuesink.ts +238 -0
  1383. package/src/tui/jsonfile.ts +42 -0
  1384. package/src/tui/keys.ts +109 -0
  1385. package/src/tui/layout.ts +108 -0
  1386. package/src/tui/learning.ts +627 -0
  1387. package/src/tui/lessonswitch.ts +118 -0
  1388. package/src/tui/livecontrol.ts +228 -0
  1389. package/src/tui/memforward.ts +443 -0
  1390. package/src/tui/memincident.ts +363 -0
  1391. package/src/tui/memory.ts +620 -0
  1392. package/src/tui/memtab.ts +606 -0
  1393. package/src/tui/metricstab.ts +1177 -0
  1394. package/src/tui/model.ts +131 -0
  1395. package/src/tui/mouse.ts +98 -0
  1396. package/src/tui/msg.ts +268 -0
  1397. package/src/tui/nativecompanion.ts +417 -0
  1398. package/src/tui/openurl.ts +21 -0
  1399. package/src/tui/paint.ts +511 -0
  1400. package/src/tui/panes.ts +98 -0
  1401. package/src/tui/pipane.ts +219 -0
  1402. package/src/tui/poll-hooks.ts +12 -0
  1403. package/src/tui/poll.ts +147 -0
  1404. package/src/tui/proposals.ts +838 -0
  1405. package/src/tui/prpause.ts +150 -0
  1406. package/src/tui/reload.ts +436 -0
  1407. package/src/tui/remote.ts +413 -0
  1408. package/src/tui/rollover.ts +969 -0
  1409. package/src/tui/scantab.ts +1290 -0
  1410. package/src/tui/setupentry.ts +93 -0
  1411. package/src/tui/sidebar.ts +749 -0
  1412. package/src/tui/sshtriage.ts +510 -0
  1413. package/src/tui/startup.ts +231 -0
  1414. package/src/tui/tabs.ts +45 -0
  1415. package/src/tui/termmirror.ts +224 -0
  1416. package/src/tui/theme.ts +95 -0
  1417. package/src/tui/ticketstab.ts +310 -0
  1418. package/src/tui/tmuxsocket.ts +129 -0
  1419. package/src/tui/trackertickets.ts +84 -0
  1420. package/src/tui/unwedge.ts +286 -0
  1421. package/src/tui/viewer.ts +575 -0
  1422. package/src/tui/viewerinput.ts +173 -0
  1423. package/src/updatenotice.ts +178 -0
  1424. package/src/usage.ts +1154 -0
  1425. package/src/verdicts.ts +4689 -0
  1426. package/src/version.ts +80 -0
  1427. package/src/wakes.ts +476 -0
  1428. package/src/webhook.ts +942 -0
  1429. package/src/webhookheal.ts +276 -0
  1430. package/src/wire.ts +2153 -0
  1431. package/src/worker-session-host.ts +602 -0
  1432. package/src/workerbreaker.ts +156 -0
  1433. package/src/workercontrol.ts +104 -0
  1434. package/src/workerlease.ts +441 -0
  1435. package/src/workerliveness.ts +103 -0
  1436. package/src/workerlog.ts +79 -0
  1437. package/src/workerruntime.ts +30 -0
  1438. package/src/workersupervisor.ts +1538 -0
  1439. package/src/workerusage.ts +42 -0
  1440. package/src/workerworld.ts +427 -0
  1441. package/src/worktrees.ts +1138 -0
  1442. package/tests/fixtures/regression-cases.json +795 -0
  1443. package/tests/fixtures/seed-manifest.json +19 -0
@@ -0,0 +1,1414 @@
1
+ // GitHub App in one click — the wizard step that turns the
2
+ // test-pinned manifest artifact into a fully credentialed, installed App.
3
+ //
4
+ // The shape, end to end:
5
+ // 1. serve a localhost page whose form POSTs deploy/github-app-manifest.json
6
+ // to GitHub's app-creation page (personal or organization target — the
7
+ // org URL differs only in path), carrying a `state` token for CSRF and a
8
+ // `redirect_url` pointing back at this process's loopback listener;
9
+ // 2. receive the one-time `code` on that listener and exchange it at the
10
+ // UNAUTHENTICATED conversion endpoint (POST /app-manifests/{code}/
11
+ // conversions). The 201 carries id / slug / pem / webhook_secret — the
12
+ // ONLY moment GitHub ever reveals that secret — and those three land
13
+ // straight in the secrets dir under the same file names, permissions and
14
+ // validation deploy/collect-secrets.sh uses. Nothing is ever pasted by
15
+ // the operator, so no credential passes through a clipboard;
16
+ // 3. guided install click, then VERIFY it via the App's own installation
17
+ // listing (App-JWT authed) — bounded polling with progress lines, never
18
+ // an infinite wait;
19
+ // 4. create the trigger / in-progress / reviewed labels through the
20
+ // installation token, idempotently (a label that already exists is a
21
+ // success, not an error).
22
+ //
23
+ // Three hard boundaries this step inherits:
24
+ // * root rule 3 — the App credential is the ONLY credential. Nothing here
25
+ // reads, mints or accepts a personal token, and no credential VALUE is
26
+ // ever logged (ids and lengths only).
27
+ // * root rule 1 stays where it is — this is the WIZARD, not the daemon, and
28
+ // it runs before any daemon exists. The only repo write it makes is
29
+ // CREATING LABEL DEFINITIONS (POST /repos/{o}/{r}/labels), which is not a
30
+ // `MutationKind` at all: it never posts a review, never applies or removes
31
+ // a label on a PR, never pushes a branch, and so never goes near the
32
+ // MutationGate's kinds. Do not grow this step into a repo-mutating path —
33
+ // anything the daemon does belongs behind the gate.
34
+ // * the manifest is CONSUMED, never copied: loadManifest reads
35
+ // deploy/github-app-manifest.json, the artifact tests/github-app-manifest
36
+ // .test.ts pins to the webhook handler and the mutation gate. Only three
37
+ // fields are overridden for this operator (name, redirect_url, and the
38
+ // webhook URL when the tunnel step has already recorded one); permissions
39
+ // and events are whatever the pinned artifact says.
40
+ //
41
+ // Everything effectful is injected: fetch/clock/log/runner from the engine's
42
+ // adapters, and the loopback listener + sleep + token source through the
43
+ // step's own options — so the tests fake the whole flow and never bind a port
44
+ // or touch the network.
45
+ import { createPrivateKey, randomBytes } from "node:crypto";
46
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
47
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
48
+ import { isAbsolute, join } from "node:path";
49
+ import type { Socket } from "node:net";
50
+ import { mintAppJwt } from "@quickchatai/pi-quickreview/src/githubAuth.js";
51
+ import { CHOICE_KEYS, REPO_SLUG_RE, botLoginFor } from "../choices.js";
52
+ import { CONFIG_FILL_STEP_ID } from "./configfill.js";
53
+ import { readTomlKey, secretsDirFor } from "./ingress.js";
54
+ import {
55
+ sandboxRoot,
56
+ readTextOrNull,
57
+ type CheckResult,
58
+ type OnboardingStep,
59
+ type StepContext,
60
+ type StepReport,
61
+ } from "../engine.js";
62
+
63
+ export const GITHUB_APP_STEP_ID = "github_app";
64
+
65
+ // ---- the choice vocabulary --------------------------------------------------
66
+ // Read: the reviewed repo and (optionally) the operator's target/name/webhook.
67
+ // Written: everything the config-fill step needs to write config.toml, so the
68
+ // App identity is derived once and never re-typed.
69
+
70
+ // Every one of these is an alias of the shared declaration in
71
+ // src/onboard/choices.ts, so this step reads exactly the keys its siblings
72
+ // write.
73
+
74
+ /** "owner/name" of the reviewed repo — recorded by the interview (config_fill). */
75
+ export const REPO_CHOICE_KEY = CHOICE_KEYS.repo;
76
+ /** "personal" | "org:<login>". Absent ⇒ asked (prompter) or derived. */
77
+ export const APP_TARGET_CHOICE_KEY = CHOICE_KEYS.appTarget;
78
+ /** The App name: this step's own `app_name` input, recorded so a re-run keeps
79
+ * it. Absent ⇒ suggested with a uniqueness suffix. */
80
+ export const APP_NAME_CHOICE_KEY = CHOICE_KEYS.appName;
81
+ /** Public webhook URL, when the ingress step has already settled a stable one. */
82
+ export const WEBHOOK_URL_CHOICE_KEY = CHOICE_KEYS.webhookUrl;
83
+ /** Secrets dir override (config.toml [github] secrets_dir, via config_fill). */
84
+ export const SECRETS_DIR_CHOICE_KEY = CHOICE_KEYS.secretsDir;
85
+ /** Trigger/status label names as the operator's config carries them. */
86
+ export const LABELS_CHOICE_KEY = CHOICE_KEYS.labels;
87
+
88
+ export const APP_ID_CHOICE_KEY = CHOICE_KEYS.appId;
89
+ export const APP_SLUG_CHOICE_KEY = CHOICE_KEYS.appSlug;
90
+ export const BOT_LOGIN_CHOICE_KEY = CHOICE_KEYS.botLogin;
91
+ export const INSTALLATION_ID_CHOICE_KEY = CHOICE_KEYS.installationId;
92
+ export const APP_HTML_URL_CHOICE_KEY = CHOICE_KEYS.appHtmlUrl;
93
+
94
+ /** Secrets-dir file names — the SAME three deploy/collect-secrets.sh writes. */
95
+ export const SECRET_FILES = {
96
+ appId: "app_id",
97
+ pem: "app.pem",
98
+ webhookSecret: "webhook_secret",
99
+ } as const;
100
+
101
+ /** config.example.toml's label defaults; a recorded choice overrides them. */
102
+ export const DEFAULT_LABELS = {
103
+ trigger: "AI review requested",
104
+ in_progress: "AI review in progress",
105
+ reviewed: "Automatic Review",
106
+ } as const;
107
+
108
+ const LABEL_META: Array<{ key: keyof typeof DEFAULT_LABELS; color: string; description: string }> =
109
+ [
110
+ {
111
+ key: "trigger",
112
+ color: "1d76db",
113
+ description: "Ask the automated reviewer for a review of this PR",
114
+ },
115
+ {
116
+ key: "in_progress",
117
+ color: "fbca04",
118
+ description: "The automated reviewer is running a round on this PR",
119
+ },
120
+ {
121
+ key: "reviewed",
122
+ color: "0e8a16",
123
+ description: "This PR has been reviewed by the automated reviewer",
124
+ },
125
+ ];
126
+
127
+ export const DEFAULT_CALLBACK_PORT = 8788;
128
+ export const CALLBACK_PATH = "/manifest-callback";
129
+ /** GitHub App names are globally unique and length-capped; the suggestion is
130
+ * trimmed to fit and stays editable on GitHub's own creation page.
131
+ * // VERIFY-ON-PIN: the exact 34-character cap is GitHub's, asserted from the
132
+ * manifest-flow docs and not checkable offline — the named `github-app`
133
+ * onboarding smoke step (register a throwaway App against a scratch account)
134
+ * is where it is confirmed. Over-trimming costs nothing; the operator can
135
+ * rename on the page. */
136
+ export const MAX_APP_NAME_LENGTH = 34;
137
+
138
+ // ---- the manifest artifact --------------------------------------------------
139
+
140
+ export interface AppManifest {
141
+ name: string;
142
+ url: string;
143
+ description: string;
144
+ public: boolean;
145
+ hook_attributes: { url: string; active: boolean };
146
+ redirect_url: string;
147
+ default_permissions: Record<string, string>;
148
+ default_events: string[];
149
+ [key: string]: unknown;
150
+ }
151
+
152
+ /** The pinned artifact's path — deploy/github-app-manifest.json, never a copy. */
153
+ export function manifestPath(root: string): string {
154
+ return join(root, "deploy", "github-app-manifest.json");
155
+ }
156
+
157
+ /** Read the pinned manifest. Throws with the path on a missing/broken file —
158
+ * the step turns that into one red check. */
159
+ export function loadManifest(
160
+ root: string,
161
+ readFile: (p: string) => string = (p) => readFileSync(p, "utf8"),
162
+ ): AppManifest {
163
+ const path = manifestPath(root);
164
+ const parsed = JSON.parse(readFile(path)) as AppManifest;
165
+ if (
166
+ !parsed ||
167
+ typeof parsed !== "object" ||
168
+ typeof parsed.name !== "string" ||
169
+ typeof parsed.default_permissions !== "object"
170
+ ) {
171
+ throw new Error(`${path} is not a GitHub App manifest`);
172
+ }
173
+ return parsed;
174
+ }
175
+
176
+ /** A uniqueness suffix on the artifact's name; GitHub App names are global. */
177
+ export function suggestAppName(base: string, suffix: string): string {
178
+ const cleanBase = base.trim().replace(/\s+/g, "-");
179
+ const tail = `-${suffix}`;
180
+ return `${cleanBase.slice(0, Math.max(1, MAX_APP_NAME_LENGTH - tail.length))}${tail}`;
181
+ }
182
+
183
+ export interface ManifestOverrides {
184
+ name: string;
185
+ redirectUrl: string;
186
+ /** Public webhook endpoint; absent keeps the artifact's placeholder (the
187
+ * tunnel step re-points the App's webhook URL later). */
188
+ webhookUrl?: string;
189
+ }
190
+
191
+ /**
192
+ * The manifest as POSTed: the pinned artifact with exactly three operator
193
+ * fields overridden. `$comment` (documentation for humans reading the file) is
194
+ * dropped; every permission and event stays exactly as pinned.
195
+ */
196
+ export function renderManifest(manifest: AppManifest, o: ManifestOverrides): AppManifest {
197
+ const rendered: AppManifest = { ...manifest };
198
+ for (const key of Object.keys(rendered)) if (key.startsWith("$")) delete rendered[key];
199
+ rendered.name = o.name;
200
+ rendered.redirect_url = o.redirectUrl;
201
+ rendered.hook_attributes = {
202
+ ...manifest.hook_attributes,
203
+ url: o.webhookUrl ?? manifest.hook_attributes.url,
204
+ };
205
+ return rendered;
206
+ }
207
+
208
+ // ---- target (personal vs organization) --------------------------------------
209
+
210
+ export type AppTarget = { kind: "personal" } | { kind: "org"; login: string };
211
+
212
+ /** The creation page. The org URL differs from the personal one only in path. */
213
+ export function appCreationUrl(target: AppTarget, githubBase = "https://github.com"): string {
214
+ return target.kind === "org"
215
+ ? `${githubBase}/organizations/${encodeURIComponent(target.login)}/settings/apps/new`
216
+ : `${githubBase}/settings/apps/new`;
217
+ }
218
+
219
+ /** "personal" | "org:<login>" ⇒ a target; anything else ⇒ null. */
220
+ export function parseTargetChoice(value: unknown): AppTarget | null {
221
+ if (typeof value !== "string") return null;
222
+ const v = value.trim();
223
+ if (v === "personal") return { kind: "personal" };
224
+ const m = /^org:(.+)$/.exec(v);
225
+ return m && m[1]!.trim() ? { kind: "org", login: m[1]!.trim() } : null;
226
+ }
227
+
228
+ export function formatTarget(target: AppTarget): string {
229
+ return target.kind === "org" ? `org:${target.login}` : "personal";
230
+ }
231
+
232
+ // ---- repo slug --------------------------------------------------------------
233
+
234
+ export interface RepoSlug {
235
+ owner: string;
236
+ name: string;
237
+ }
238
+
239
+ export function parseRepoSlug(value: unknown): RepoSlug | null {
240
+ if (typeof value !== "string") return null;
241
+ const m = REPO_SLUG_RE.exec(value.trim());
242
+ return m ? { owner: m[1]!, name: m[2]! } : null;
243
+ }
244
+
245
+ // ---- credentials on disk ----------------------------------------------------
246
+
247
+ export interface CredentialState {
248
+ appId: string | null;
249
+ pem: string | null;
250
+ webhookSecret: string | null;
251
+ /** One line per missing/malformed file — rendered straight into checks. */
252
+ problems: string[];
253
+ complete: boolean;
254
+ }
255
+
256
+ /** The same rules deploy/collect-secrets.sh validates with: a numeric app_id,
257
+ * a PEM that actually parses as a private key (openssl pkey there, node's
258
+ * createPrivateKey here — no subprocess), a non-trivial webhook secret. */
259
+ export function validateCredentialValues(v: {
260
+ appId: string;
261
+ pem: string;
262
+ webhookSecret: string;
263
+ }): string[] {
264
+ const problems: string[] = [];
265
+ if (!/^[0-9]{1,12}$/.test(v.appId.trim())) problems.push("app_id is not a number");
266
+ try {
267
+ createPrivateKey(v.pem);
268
+ } catch {
269
+ problems.push("app.pem does not parse as a private key");
270
+ }
271
+ if (v.webhookSecret.trim().length < 8)
272
+ problems.push("webhook_secret is suspiciously short (<8 chars)");
273
+ return problems;
274
+ }
275
+
276
+ export function readCredentials(secretsDir: string): CredentialState {
277
+ const read = (name: string): string | null => {
278
+ const path = join(secretsDir, name);
279
+ try {
280
+ const body = readFileSync(path, "utf8");
281
+ return body.trim() ? body : null;
282
+ } catch {
283
+ return null;
284
+ }
285
+ };
286
+ const appId = read(SECRET_FILES.appId);
287
+ const pem = read(SECRET_FILES.pem);
288
+ const webhookSecret = read(SECRET_FILES.webhookSecret);
289
+ const problems: string[] = [];
290
+ for (const [name, value] of [
291
+ [SECRET_FILES.appId, appId],
292
+ [SECRET_FILES.pem, pem],
293
+ [SECRET_FILES.webhookSecret, webhookSecret],
294
+ ] as const) {
295
+ if (value === null) problems.push(`${name} missing from ${secretsDir}`);
296
+ }
297
+ if (appId !== null && pem !== null && webhookSecret !== null) {
298
+ problems.push(...validateCredentialValues({ appId, pem, webhookSecret }));
299
+ }
300
+ return {
301
+ appId: appId?.trim() ?? null,
302
+ pem,
303
+ webhookSecret: webhookSecret?.trim() ?? null,
304
+ problems,
305
+ complete: problems.length === 0,
306
+ };
307
+ }
308
+
309
+ /** Land the conversion response: 0600 files under a 0700 dir, validated
310
+ * BEFORE anything is written (a half-written credential set is worse than
311
+ * none). Returns the problems that stopped it; empty means written. */
312
+ export function writeCredentials(
313
+ secretsDir: string,
314
+ v: { appId: string; pem: string; webhookSecret: string },
315
+ ): string[] {
316
+ const problems = validateCredentialValues(v);
317
+ if (problems.length > 0) return problems;
318
+ mkdirSync(secretsDir, { recursive: true, mode: 0o700 });
319
+ chmodSync(secretsDir, 0o700);
320
+ const put = (name: string, body: string): void => {
321
+ const path = join(secretsDir, name);
322
+ writeFileSync(path, body, { mode: 0o600 });
323
+ chmodSync(path, 0o600); // an existing file keeps its old mode otherwise
324
+ };
325
+ put(SECRET_FILES.appId, `${v.appId.trim()}\n`);
326
+ put(SECRET_FILES.pem, v.pem.endsWith("\n") ? v.pem : `${v.pem}\n`);
327
+ put(SECRET_FILES.webhookSecret, v.webhookSecret.trim());
328
+ return [];
329
+ }
330
+
331
+ // ---- the localhost page -----------------------------------------------------
332
+
333
+ function escapeHtml(s: string): string {
334
+ return s
335
+ .replace(/&/g, "&amp;")
336
+ .replace(/</g, "&lt;")
337
+ .replace(/>/g, "&gt;")
338
+ .replace(/"/g, "&quot;");
339
+ }
340
+
341
+ export interface PageInputs {
342
+ postUrl: string;
343
+ state: string;
344
+ manifest: AppManifest;
345
+ target: AppTarget;
346
+ }
347
+
348
+ /**
349
+ * The one-click page. A real form POST (GitHub's manifest flow requires the
350
+ * browser to submit the manifest as a form field) — deliberately NOT
351
+ * auto-submitting: the operator sees what is about to be created, and the
352
+ * click is theirs.
353
+ */
354
+ export function manifestPageHtml(i: PageInputs): string {
355
+ const json = JSON.stringify(i.manifest);
356
+ const perms = Object.entries(i.manifest.default_permissions)
357
+ .map(([k, v]) => `<li><code>${escapeHtml(k)}</code>: ${escapeHtml(v)}</li>`)
358
+ .join("");
359
+ const where =
360
+ i.target.kind === "org"
361
+ ? `the <strong>${escapeHtml(i.target.login)}</strong> organization`
362
+ : "your personal account";
363
+ return `<!doctype html>
364
+ <html lang="en"><head><meta charset="utf-8"><title>Create your QuickReview GitHub App</title>
365
+ <style>body{font:16px/1.5 system-ui,sans-serif;max-width:44rem;margin:3rem auto;padding:0 1rem}
366
+ button{font:inherit;padding:.6rem 1.2rem;cursor:pointer}code{background:#f3f3f3;padding:0 .2rem}
367
+ ul{columns:2}</style></head>
368
+ <body>
369
+ <h1>Create your GitHub App</h1>
370
+ <p>This creates the App <code>${escapeHtml(i.manifest.name)}</code> under ${where}. The name is
371
+ globally unique across GitHub — you can edit it on GitHub's own page before confirming.</p>
372
+ <p>Permissions and events come from this repository's pinned manifest
373
+ (<code>deploy/github-app-manifest.json</code>), so there is nothing to transcribe:</p>
374
+ <ul>${perms}</ul>
375
+ <p>Webhook: <code>${escapeHtml(i.manifest.hook_attributes.url)}</code></p>
376
+ <p>After you confirm on GitHub, you are sent back here and the wizard picks up the App id,
377
+ private key and webhook secret automatically. Nothing is copied by hand.</p>
378
+ <form action="${escapeHtml(i.postUrl)}?state=${encodeURIComponent(i.state)}" method="post">
379
+ <input type="hidden" name="manifest" value="${escapeHtml(json)}">
380
+ <button type="submit">Create the App on GitHub</button>
381
+ </form>
382
+ </body></html>
383
+ `;
384
+ }
385
+
386
+ function resultPageHtml(title: string, body: string): string {
387
+ return `<!doctype html>
388
+ <html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>
389
+ <style>body{font:16px/1.5 system-ui,sans-serif;max-width:40rem;margin:3rem auto;padding:0 1rem}</style>
390
+ </head><body><h1>${escapeHtml(title)}</h1><p>${escapeHtml(body)}</p></body></html>
391
+ `;
392
+ }
393
+
394
+ // ---- the loopback listener seam ---------------------------------------------
395
+
396
+ export interface CodeArrival {
397
+ code: string | null;
398
+ /** Plain-language account of what happened (timeout, state mismatch, …). */
399
+ detail: string;
400
+ }
401
+
402
+ export interface CallbackListener {
403
+ /** The page the operator opens. */
404
+ url: string;
405
+ /** What goes into the manifest as `redirect_url`. */
406
+ redirectUrl: string;
407
+ /** Resolves once GitHub redirects back, or on the listener's own timeout. */
408
+ waitForCode(): Promise<CodeArrival>;
409
+ close(): Promise<void>;
410
+ }
411
+
412
+ export interface CallbackServerOptions {
413
+ /** The page body, built once the port is known — the manifest's
414
+ * `redirect_url` must name the port actually bound, so the page cannot be
415
+ * rendered before listening. */
416
+ page: (bound: { url: string; redirectUrl: string }) => string;
417
+ state: string;
418
+ /** Preferred loopback port; a busy port falls back to an ephemeral one and
419
+ * the manifest's redirect_url follows the port actually bound. */
420
+ port: number;
421
+ timeoutMs: number;
422
+ }
423
+
424
+ export type CallbackServerFactory = (o: CallbackServerOptions) => Promise<CallbackListener>;
425
+
426
+ /** The real listener: loopback only, deterministic close (open sockets are
427
+ * destroyed, so close() never hangs on a browser keep-alive). */
428
+ export const nodeCallbackServer: CallbackServerFactory = async (o) => {
429
+ const sockets = new Set<Socket>();
430
+ let settle: ((a: CodeArrival) => void) | null = null;
431
+ const arrival = new Promise<CodeArrival>((resolve) => {
432
+ settle = resolve;
433
+ });
434
+ const deliver = (a: CodeArrival): void => {
435
+ if (settle) {
436
+ const s = settle;
437
+ settle = null;
438
+ s(a);
439
+ }
440
+ };
441
+
442
+ let html = "";
443
+ // The authorities this listener answers to, set once it is bound. Anything
444
+ // else is a rebound hostname (DNS rebinding) that must never see the page,
445
+ // which carries the state token, nor reach the callback.
446
+ let allowedHosts = new Set<string>();
447
+ const handler = (req: IncomingMessage, res: ServerResponse): void => {
448
+ if (!allowedHosts.has((req.headers.host ?? "").toLowerCase())) {
449
+ res.writeHead(421, { "content-type": "text/plain; charset=utf-8" });
450
+ res.end("misdirected request\n");
451
+ return;
452
+ }
453
+ let url: URL;
454
+ try {
455
+ url = new URL(req.url ?? "/", "http://127.0.0.1");
456
+ } catch {
457
+ res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
458
+ res.end("bad request\n");
459
+ return;
460
+ }
461
+ if (url.pathname === CALLBACK_PATH) {
462
+ const code = url.searchParams.get("code");
463
+ const state = url.searchParams.get("state");
464
+ if (state !== o.state) {
465
+ res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
466
+ res.end(
467
+ resultPageHtml(
468
+ "Ignored",
469
+ "That callback did not carry this wizard's state token, so it was ignored.",
470
+ ),
471
+ );
472
+ return;
473
+ }
474
+ res.writeHead(code ? 200 : 400, { "content-type": "text/html; charset=utf-8" });
475
+ res.end(
476
+ code
477
+ ? resultPageHtml("App created", "You can close this tab and go back to the wizard.")
478
+ : resultPageHtml(
479
+ "No code",
480
+ "GitHub redirected back without a code. Run the step again to restart.",
481
+ ),
482
+ );
483
+ deliver(
484
+ code
485
+ ? { code, detail: "code received on the loopback callback" }
486
+ : { code: null, detail: "callback carried no code" },
487
+ );
488
+ return;
489
+ }
490
+ if (url.pathname === "/") {
491
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
492
+ res.end(html);
493
+ return;
494
+ }
495
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
496
+ res.end("not found\n");
497
+ };
498
+
499
+ const server: Server = createServer(handler);
500
+ server.on("connection", (s) => {
501
+ sockets.add(s);
502
+ s.on("close", () => sockets.delete(s));
503
+ });
504
+
505
+ const listen = (port: number): Promise<number> =>
506
+ new Promise((resolve, reject) => {
507
+ const onError = (err: Error): void => reject(err);
508
+ server.once("error", onError);
509
+ server.listen(port, "127.0.0.1", () => {
510
+ server.removeListener("error", onError);
511
+ const addr = server.address();
512
+ resolve(typeof addr === "object" && addr !== null ? addr.port : port);
513
+ });
514
+ });
515
+
516
+ let port: number;
517
+ try {
518
+ port = await listen(o.port);
519
+ } catch {
520
+ port = await listen(0); // preferred port busy — any loopback port will do
521
+ }
522
+
523
+ allowedHosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
524
+ const url = `http://127.0.0.1:${port}/`;
525
+ const redirectUrl = `http://127.0.0.1:${port}${CALLBACK_PATH}`;
526
+ html = o.page({ url, redirectUrl });
527
+
528
+ const timer = setTimeout(
529
+ () => deliver({ code: null, detail: `no callback within ${Math.round(o.timeoutMs / 1000)}s` }),
530
+ o.timeoutMs,
531
+ );
532
+ timer.unref?.();
533
+
534
+ return {
535
+ url,
536
+ redirectUrl,
537
+ waitForCode: () => arrival,
538
+ close: () =>
539
+ new Promise((resolve) => {
540
+ clearTimeout(timer);
541
+ deliver({ code: null, detail: "listener closed" });
542
+ for (const s of sockets) s.destroy();
543
+ server.close(() => resolve());
544
+ }),
545
+ };
546
+ };
547
+
548
+ // ---- GitHub calls -----------------------------------------------------------
549
+
550
+ export interface ApiResult {
551
+ /** The HTTP status; 0 when the request itself failed (`text` says why). */
552
+ status: number;
553
+ body: unknown;
554
+ text: string;
555
+ link?: string | null;
556
+ }
557
+
558
+ /** One GitHub REST call with the wizard's headers, the body decoded
559
+ * tolerantly. Total: a network failure is status 0, never a throw. */
560
+ export async function api(
561
+ fetchFn: typeof fetch,
562
+ url: string,
563
+ init?: RequestInit,
564
+ ): Promise<ApiResult> {
565
+ const controller = new AbortController();
566
+ const signal = init?.signal
567
+ ? AbortSignal.any([init.signal, controller.signal])
568
+ : controller.signal;
569
+ let timer: ReturnType<typeof setTimeout> | undefined;
570
+ const deadline = new Promise<never>((_resolve, reject) => {
571
+ timer = setTimeout(() => {
572
+ controller.abort();
573
+ reject(new Error("GitHub request deadline exceeded"));
574
+ }, 10_000);
575
+ });
576
+ const request = async (): Promise<ApiResult> => {
577
+ try {
578
+ const res = await fetchFn(url, {
579
+ ...init,
580
+ signal,
581
+ headers: {
582
+ accept: "application/vnd.github+json",
583
+ "user-agent": "quickreview-onboarding",
584
+ ...(init?.headers ?? {}),
585
+ },
586
+ });
587
+ const text = await res.text();
588
+ let body: unknown = null;
589
+ try {
590
+ body = text ? JSON.parse(text) : null;
591
+ } catch {
592
+ body = null;
593
+ }
594
+ return { status: res.status, body, text, link: res.headers?.get?.("link") ?? null };
595
+ } catch (err) {
596
+ return { status: 0, body: null, text: `request failed: ${String(err)}` };
597
+ }
598
+ };
599
+ try {
600
+ return await Promise.race([request(), deadline]);
601
+ } catch (err) {
602
+ return { status: 0, body: null, text: `request failed: ${String(err)}` };
603
+ } finally {
604
+ clearTimeout(timer);
605
+ }
606
+ }
607
+
608
+ function apiError(r: ApiResult): string {
609
+ const message = (r.body as { message?: string } | null)?.message;
610
+ return `HTTP ${r.status}${message ? `: ${message}` : r.text ? `: ${r.text.slice(0, 200)}` : ""}`;
611
+ }
612
+
613
+ export interface ConvertedApp {
614
+ id: number;
615
+ slug: string;
616
+ pem: string;
617
+ webhook_secret: string;
618
+ html_url?: string;
619
+ owner?: { login?: string };
620
+ }
621
+
622
+ export type ConversionOutcome =
623
+ { ok: true; app: ConvertedApp } | { ok: false; detail: string; restart: boolean };
624
+
625
+ /**
626
+ * The UNAUTHENTICATED conversion: POST /app-manifests/{code}/conversions.
627
+ * The code is single-use and expires one hour after GitHub issues it, so a
628
+ * 404/422 is never a wedge — it means "start the flow again", and the step
629
+ * says exactly that.
630
+ * // VERIFY-ON-PIN: the 201-with-{id,slug,pem,webhook_secret} shape and the
631
+ * 404-on-expired/used status are GitHub's documented manifest-flow contract,
632
+ * unverifiable offline — the named `github-app` onboarding smoke step
633
+ * (throwaway App on a scratch account) is where they are confirmed live.
634
+ */
635
+ export async function convertManifestCode(
636
+ fetchFn: typeof fetch,
637
+ apiBase: string,
638
+ code: string,
639
+ ): Promise<ConversionOutcome> {
640
+ const r = await api(fetchFn, `${apiBase}/app-manifests/${encodeURIComponent(code)}/conversions`, {
641
+ method: "POST",
642
+ });
643
+ if (r.status === 201) {
644
+ const app = r.body as Partial<ConvertedApp> | null;
645
+ if (
646
+ !app ||
647
+ typeof app.id !== "number" ||
648
+ typeof app.pem !== "string" ||
649
+ typeof app.webhook_secret !== "string"
650
+ ) {
651
+ return {
652
+ ok: false,
653
+ detail:
654
+ "GitHub accepted the code but the conversion response was missing id/pem/webhook_secret",
655
+ restart: true,
656
+ };
657
+ }
658
+ return {
659
+ ok: true,
660
+ app: { ...app, slug: typeof app.slug === "string" ? app.slug : "" } as ConvertedApp,
661
+ };
662
+ }
663
+ if (r.status === 404 || r.status === 422) {
664
+ return {
665
+ ok: false,
666
+ restart: true,
667
+ detail:
668
+ `the one-time code was rejected (${apiError(r)}) — manifest codes are single-use and expire an hour after GitHub issues them. ` +
669
+ "Run this step again: it serves a fresh page and a fresh code. Nothing was written.",
670
+ };
671
+ }
672
+ return {
673
+ ok: false,
674
+ restart: false,
675
+ detail:
676
+ `conversion failed (${apiError(r)}) — this looks transient; run the step again. ` +
677
+ "Running it again creates a fresh App, so GitHub may still hold the one this run created: check your " +
678
+ "GitHub App settings and delete an App this run left behind before retrying.",
679
+ };
680
+ }
681
+
682
+ export type InstallationLookup =
683
+ { status: "found"; id: number } | { status: "none" } | { status: "error"; detail: string };
684
+
685
+ /** The App's own installation listing (App JWT, never a user token). */
686
+ export async function findInstallation(
687
+ fetchFn: typeof fetch,
688
+ apiBase: string,
689
+ jwt: string,
690
+ owner: string,
691
+ ): Promise<InstallationLookup> {
692
+ const seen = new Set<string>();
693
+ for (let page = 1; page <= 100; page++) {
694
+ const r = await api(
695
+ fetchFn,
696
+ `${apiBase}/app/installations?per_page=100${page === 1 ? "" : `&page=${page}`}`,
697
+ {
698
+ headers: { authorization: `Bearer ${jwt}` },
699
+ },
700
+ );
701
+ if (r.status !== 200)
702
+ return { status: "error", detail: `GET /app/installations failed (${apiError(r)})` };
703
+ if (!Array.isArray(r.body))
704
+ return { status: "error", detail: "GET /app/installations returned an unreadable page" };
705
+ const list = r.body as Array<{ id?: number; account?: { login?: string } }>;
706
+ const match = list.find((i) => (i.account?.login ?? "").toLowerCase() === owner.toLowerCase());
707
+ if (match && typeof match.id === "number") return { status: "found", id: match.id };
708
+ if (list.length < 100) return { status: "none" };
709
+ const signature = JSON.stringify(list.map((i) => i.id).sort((a, b) => (a ?? 0) - (b ?? 0)));
710
+ if (seen.has(signature))
711
+ return {
712
+ status: "error",
713
+ detail: "GET /app/installations repeated a full page; lookup remains incomplete",
714
+ };
715
+ seen.add(signature);
716
+ }
717
+ return {
718
+ status: "error",
719
+ detail: "GET /app/installations exceeded the page budget; lookup remains incomplete",
720
+ };
721
+ }
722
+
723
+ async function installationToken(
724
+ fetchFn: typeof fetch,
725
+ apiBase: string,
726
+ jwt: string,
727
+ id: number,
728
+ ): Promise<{ token: string } | { error: string }> {
729
+ const r = await api(fetchFn, `${apiBase}/app/installations/${id}/access_tokens`, {
730
+ method: "POST",
731
+ headers: { authorization: `Bearer ${jwt}` },
732
+ });
733
+ const token = (r.body as { token?: string } | null)?.token;
734
+ if (r.status !== 201 || typeof token !== "string")
735
+ return { error: `installation token mint failed (${apiError(r)})` };
736
+ return { token };
737
+ }
738
+
739
+ /** Does the installation actually cover the reviewed repo? A 404 through the
740
+ * installation token means the App is installed on the account but this repo
741
+ * was not selected — a distinct, fixable thing, said plainly. */
742
+ async function repoCovered(
743
+ fetchFn: typeof fetch,
744
+ apiBase: string,
745
+ token: string,
746
+ repo: RepoSlug,
747
+ ): Promise<CheckResult> {
748
+ const r = await api(fetchFn, `${apiBase}/repos/${repo.owner}/${repo.name}`, {
749
+ headers: { authorization: `Bearer ${token}` },
750
+ });
751
+ if (r.status === 200)
752
+ return {
753
+ name: "repo access",
754
+ ok: true,
755
+ detail: `the installation can read ${repo.owner}/${repo.name}`,
756
+ };
757
+ if (r.status === 404) {
758
+ return {
759
+ name: "repo access",
760
+ ok: false,
761
+ detail:
762
+ `the App is installed on ${repo.owner} but ${repo.owner}/${repo.name} is not among the selected repositories — ` +
763
+ "open the App's Install/Configure page, add the repo, then run this step again",
764
+ };
765
+ }
766
+ return {
767
+ name: "repo access",
768
+ ok: false,
769
+ detail: `could not read ${repo.owner}/${repo.name} through the installation (${apiError(r)})`,
770
+ };
771
+ }
772
+
773
+ /** Idempotent by construction: an existing label is a SUCCESS. */
774
+ export async function ensureLabel(
775
+ fetchFn: typeof fetch,
776
+ apiBase: string,
777
+ token: string,
778
+ repo: RepoSlug,
779
+ label: { name: string; color: string; description: string },
780
+ create: boolean,
781
+ ): Promise<CheckResult> {
782
+ const base = `${apiBase}/repos/${repo.owner}/${repo.name}/labels`;
783
+ const auth = { authorization: `Bearer ${token}` };
784
+ const existing = await api(fetchFn, `${base}/${encodeURIComponent(label.name)}`, {
785
+ headers: auth,
786
+ });
787
+ if (existing.status === 200)
788
+ return {
789
+ name: `label "${label.name}"`,
790
+ ok: true,
791
+ detail: "already present — left exactly as it is",
792
+ };
793
+ if (existing.status !== 404) {
794
+ return {
795
+ name: `label "${label.name}"`,
796
+ ok: false,
797
+ detail: `could not read the label (${apiError(existing)})`,
798
+ };
799
+ }
800
+ if (!create)
801
+ return {
802
+ name: `label "${label.name}"`,
803
+ ok: false,
804
+ detail: "missing from the repo — run the GitHub App step to create it",
805
+ };
806
+ const created = await api(fetchFn, base, {
807
+ method: "POST",
808
+ headers: { ...auth, "content-type": "application/json" },
809
+ body: JSON.stringify({ name: label.name, color: label.color, description: label.description }),
810
+ });
811
+ if (created.status === 201) return { name: `label "${label.name}"`, ok: true, detail: "created" };
812
+ // 422 is either "already_exists" (someone, or a parallel run, got there
813
+ // first) or a validation failure (e.g. an over-long configured name) that
814
+ // leaves the repo without it. Only a re-read tells them apart.
815
+ if (created.status === 422) {
816
+ const reread = await api(fetchFn, `${base}/${encodeURIComponent(label.name)}`, {
817
+ headers: auth,
818
+ });
819
+ if (reread.status === 200)
820
+ return {
821
+ name: `label "${label.name}"`,
822
+ ok: true,
823
+ detail: "already existed at creation time — treated as success",
824
+ };
825
+ }
826
+ return {
827
+ name: `label "${label.name}"`,
828
+ ok: false,
829
+ detail: `could not create the label (${apiError(created)})`,
830
+ };
831
+ }
832
+
833
+ // ---- step options -----------------------------------------------------------
834
+
835
+ export interface GithubAppStepOptions {
836
+ /** Repo root holding deploy/github-app-manifest.json; default: the parent of
837
+ * the engine's stateDir (`<root>/state`). */
838
+ repoRoot?: string;
839
+ /** Secrets dir; default: the recorded choice, then QC_SECRETS_DIR, then the
840
+ * install locator's answer (`<config home>/secrets`). */
841
+ secretsDir?: string;
842
+ apiBase?: string;
843
+ githubBase?: string;
844
+ callbackPort?: number;
845
+ callbackTimeoutMs?: number;
846
+ installPollAttempts?: number;
847
+ installPollIntervalMs?: number;
848
+ sleep?: (ms: number) => Promise<void>;
849
+ serveCallback?: CallbackServerFactory;
850
+ /** Random hex source (state token + name suffix). */
851
+ randomToken?: (bytes: number) => string;
852
+ /** Try to open the operator's browser at the page (best effort, never fatal). */
853
+ openBrowser?: boolean;
854
+ }
855
+
856
+ interface Resolved {
857
+ root: string;
858
+ secretsDir: string;
859
+ apiBase: string;
860
+ githubBase: string;
861
+ repo: RepoSlug;
862
+ labels: Array<{ name: string; color: string; description: string }>;
863
+ }
864
+
865
+ function resolveLabels(ctx: StepContext): Resolved["labels"] {
866
+ const config = readTextOrNull(ctx.adapters.paths.configPath);
867
+ const recorded = (ctx.choices[CHOICE_KEYS.labels] ?? {}) as Partial<
868
+ Record<keyof typeof DEFAULT_LABELS, unknown>
869
+ >;
870
+ return LABEL_META.map((meta) => {
871
+ const configKey = {
872
+ trigger: "trigger_label",
873
+ in_progress: "in_progress_label",
874
+ reviewed: "reviewed_label",
875
+ }[meta.key];
876
+ const override =
877
+ (config !== null ? readTomlKey(config, "github", configKey) : null) ?? recorded[meta.key];
878
+ return {
879
+ name:
880
+ typeof override === "string" && override.trim()
881
+ ? override.trim()
882
+ : DEFAULT_LABELS[meta.key],
883
+ color: meta.color,
884
+ description: meta.description,
885
+ };
886
+ });
887
+ }
888
+
889
+ // ---- the step ---------------------------------------------------------------
890
+
891
+ export function githubAppStep(opts: GithubAppStepOptions = {}): OnboardingStep {
892
+ const apiBase = opts.apiBase ?? "https://api.github.com";
893
+ const githubBase = opts.githubBase ?? "https://github.com";
894
+ const serve = opts.serveCallback ?? nodeCallbackServer;
895
+ const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
896
+ const token = opts.randomToken ?? ((bytes: number) => randomBytes(bytes).toString("hex"));
897
+ const callbackTimeoutMs = opts.callbackTimeoutMs ?? 10 * 60_000;
898
+ const pollAttempts = opts.installPollAttempts ?? 40;
899
+ const pollIntervalMs = opts.installPollIntervalMs ?? 15_000;
900
+
901
+ function resolveInputs(ctx: StepContext): Resolved | string {
902
+ // Config first, like the labels: a repository edited straight into
903
+ // config.toml is the one the daemon watches, so it is the one verified.
904
+ const config = readTextOrNull(ctx.adapters.paths.configPath);
905
+ const repo =
906
+ parseRepoSlug(config !== null ? readTomlKey(config, "github", "repo") : null) ??
907
+ parseRepoSlug(ctx.choices[CHOICE_KEYS.repo]);
908
+ if (!repo) {
909
+ // Reachable only when the interview genuinely has not run (or its answer
910
+ // is unusable): CONFIG_FILL_STEP_ID is the step that records this key,
911
+ // and it is order 35 against this step's 40. Say which step, not just
912
+ // which key — a refusal that names a choice nobody can set by hand is
913
+ // a dead end.
914
+ return (
915
+ `no reviewed repo recorded — the "${CHOICE_KEYS.repo}" choice must hold "owner/name" before the App can be ` +
916
+ `created or verified. Fix: run the ${CONFIG_FILL_STEP_ID} step (the interview) first; it asks for the repository ` +
917
+ "and records it for this step."
918
+ );
919
+ }
920
+ // The one resolution every step uses: config.toml, then the recorded
921
+ // choice, then $QC_SECRETS_DIR, then the install locator.
922
+ const secretsDir = opts.secretsDir ?? secretsDirFor(ctx.adapters, ctx.choices);
923
+ const root = opts.repoRoot ?? sandboxRoot(ctx.adapters.paths);
924
+ return {
925
+ root,
926
+ secretsDir: isAbsolute(secretsDir) ? secretsDir : join(root, secretsDir),
927
+ apiBase,
928
+ githubBase,
929
+ repo,
930
+ labels: resolveLabels(ctx),
931
+ };
932
+ }
933
+
934
+ /** The App name the operator supplied — this call's input first, then what an
935
+ * earlier call recorded. "" means nothing was supplied. */
936
+ function chosenAppName(ctx: StepContext): string {
937
+ for (const raw of [ctx.input.app_name, ctx.choices[CHOICE_KEYS.appName]]) {
938
+ if (typeof raw === "string" && raw.trim()) return raw.trim();
939
+ }
940
+ return "";
941
+ }
942
+
943
+ function log(ctx: StepContext, line: string): void {
944
+ ctx.adapters.log?.(`github_app: ${line}`);
945
+ }
946
+
947
+ /** The org caveat, stated wherever the target is an org — informational,
948
+ * never a failure: the label trigger works fully without members:read
949
+ * (only the review-TEAM trigger needs it), so onboarding never blocks on an
950
+ * organization owner's approval. */
951
+ function orgCaveat(target: AppTarget | null): CheckResult {
952
+ if (target === null)
953
+ return {
954
+ name: "org approval",
955
+ ok: true,
956
+ detail:
957
+ "App target is unknown (not recorded) — check its settings for organization approval; the label trigger works without the optional members:read permission",
958
+ };
959
+ if (target?.kind !== "org") {
960
+ return {
961
+ name: "org approval",
962
+ ok: true,
963
+ detail: "personal-account App — no organization approval is involved",
964
+ };
965
+ }
966
+ return {
967
+ name: "org approval",
968
+ ok: true,
969
+ detail:
970
+ `the manifest requests the organization permission members:read, which an owner of "${target.login}" must approve. ` +
971
+ "That approval is DEFERRABLE and nothing here waits for it: it only powers the optional review-team trigger. " +
972
+ "The label trigger — the default way to ask for a review — works fully without it.",
973
+ };
974
+ }
975
+
976
+ /** Everything that can be checked against reality, with no creation. Shared
977
+ * by run() (called first, so a configured install verifies instead of
978
+ * re-creating) and verify(). */
979
+ async function verifyCore(
980
+ ctx: StepContext,
981
+ r: Resolved,
982
+ createLabels: boolean,
983
+ ): Promise<CheckResult[]> {
984
+ const checks: CheckResult[] = [];
985
+ const creds = readCredentials(r.secretsDir);
986
+ if (!creds.complete) {
987
+ checks.push({ name: "credentials", ok: false, detail: creds.problems.join("; ") });
988
+ return checks;
989
+ }
990
+ checks.push({
991
+ name: "credentials",
992
+ ok: true,
993
+ detail: `app_id ${creds.appId}, app.pem (${creds.pem!.length} bytes) and webhook_secret present and parseable in ${r.secretsDir}`,
994
+ });
995
+
996
+ const jwt = mintAppJwt(creds.appId!, creds.pem!, () => ctx.adapters.clock().getTime());
997
+ const app = await api(ctx.adapters.fetch, `${r.apiBase}/app`, {
998
+ headers: { authorization: `Bearer ${jwt}` },
999
+ });
1000
+ if (app.status !== 200) {
1001
+ checks.push({
1002
+ name: "app reachable",
1003
+ ok: false,
1004
+ detail: `GET /app with the App JWT failed (${apiError(app)}) — the App id and private key must belong to the same App`,
1005
+ });
1006
+ return checks;
1007
+ }
1008
+ const meta = app.body as {
1009
+ id?: number;
1010
+ slug?: string;
1011
+ owner?: { login?: string; type?: string };
1012
+ };
1013
+ if (String(meta.id) !== creds.appId) {
1014
+ checks.push({
1015
+ name: "app reachable",
1016
+ ok: false,
1017
+ detail: `the private key belongs to App ${meta.id}, but app_id says ${creds.appId}`,
1018
+ });
1019
+ return checks;
1020
+ }
1021
+ checks.push({
1022
+ name: "app reachable",
1023
+ ok: true,
1024
+ detail: `App "${meta.slug ?? creds.appId}" (id ${meta.id}) answers to its own JWT`,
1025
+ });
1026
+ if (typeof meta.slug === "string" && meta.slug) {
1027
+ ctx.recordChoice(CHOICE_KEYS.appSlug, meta.slug);
1028
+ ctx.recordChoice(CHOICE_KEYS.botLogin, botLoginFor(meta.slug));
1029
+ }
1030
+ ctx.recordChoice(CHOICE_KEYS.appId, creds.appId);
1031
+
1032
+ const found = await findInstallation(ctx.adapters.fetch, r.apiBase, jwt, r.repo.owner);
1033
+ if (found.status === "error") {
1034
+ // Its own name, never "installation": a lookup that failed is not an
1035
+ // App that is not installed, and waiting for an Install click cannot
1036
+ // fix it — the install poll returns on it at once, cause intact.
1037
+ checks.push({ name: "installation lookup", ok: false, detail: found.detail });
1038
+ return checks;
1039
+ }
1040
+ if (found.status === "none") {
1041
+ checks.push({
1042
+ name: "installation",
1043
+ ok: false,
1044
+ detail:
1045
+ `the App is not installed on "${r.repo.owner}" yet — open the App's page, choose Install App, pick ${r.repo.owner}, ` +
1046
+ `select "Only select repositories" and add ${r.repo.name}, then run this step again`,
1047
+ });
1048
+ return checks;
1049
+ }
1050
+ checks.push({
1051
+ name: "installation",
1052
+ ok: true,
1053
+ detail: `installation ${found.id} on ${r.repo.owner}`,
1054
+ });
1055
+ ctx.recordChoice(CHOICE_KEYS.installationId, found.id);
1056
+
1057
+ const minted = await installationToken(ctx.adapters.fetch, r.apiBase, jwt, found.id);
1058
+ if ("error" in minted) {
1059
+ checks.push({ name: "installation token", ok: false, detail: minted.error });
1060
+ return checks;
1061
+ }
1062
+ checks.push({
1063
+ name: "installation token",
1064
+ ok: true,
1065
+ detail: "the App JWT exchanges for an installation token",
1066
+ });
1067
+
1068
+ const covered = await repoCovered(ctx.adapters.fetch, r.apiBase, minted.token, r.repo);
1069
+ checks.push(covered);
1070
+ if (!covered.ok) return checks;
1071
+
1072
+ for (const label of r.labels) {
1073
+ checks.push(
1074
+ await ensureLabel(ctx.adapters.fetch, r.apiBase, minted.token, r.repo, label, createLabels),
1075
+ );
1076
+ }
1077
+ return checks;
1078
+ }
1079
+
1080
+ /** Choice > prompter > owner-type probe. The result is recorded, so the
1081
+ * question is asked at most once. */
1082
+ async function resolveTarget(ctx: StepContext, r: Resolved): Promise<AppTarget> {
1083
+ const recorded = parseTargetChoice(ctx.choices[CHOICE_KEYS.appTarget]);
1084
+ if (recorded) return recorded;
1085
+
1086
+ const prompter = ctx.adapters.prompter;
1087
+ if (prompter) {
1088
+ const answer = await prompter(
1089
+ `Register the GitHub App under your personal account, or under an organization? ` +
1090
+ `Answer "personal", or the organization login (e.g. "${r.repo.owner}").`,
1091
+ );
1092
+ const trimmed = answer.trim();
1093
+ const parsed =
1094
+ parseTargetChoice(trimmed) ??
1095
+ (trimmed && trimmed !== "personal" ? { kind: "org", login: trimmed } : null);
1096
+ if (parsed) {
1097
+ ctx.recordChoice(CHOICE_KEYS.appTarget, formatTarget(parsed));
1098
+ return parsed;
1099
+ }
1100
+ }
1101
+
1102
+ const probe = await api(ctx.adapters.fetch, `${r.apiBase}/users/${r.repo.owner}`);
1103
+ const type = (probe.body as { type?: string } | null)?.type;
1104
+ const derived: AppTarget =
1105
+ type === "Organization" ? { kind: "org", login: r.repo.owner } : { kind: "personal" };
1106
+ if (probe.status !== 200) {
1107
+ log(
1108
+ ctx,
1109
+ `could not read the owner type of "${r.repo.owner}" (${apiError(probe)}) — assuming a personal account. ` +
1110
+ `Record the "${CHOICE_KEYS.appTarget}" choice as "org:<login>" to override.`,
1111
+ );
1112
+ }
1113
+ ctx.recordChoice(CHOICE_KEYS.appTarget, formatTarget(derived));
1114
+ return derived;
1115
+ }
1116
+
1117
+ /** Serve the page, wait for the code, exchange it, land the credentials. */
1118
+ async function createApp(
1119
+ ctx: StepContext,
1120
+ r: Resolved,
1121
+ target: AppTarget,
1122
+ ): Promise<CheckResult[]> {
1123
+ let manifest: AppManifest;
1124
+ try {
1125
+ manifest = loadManifest(r.root);
1126
+ } catch (err) {
1127
+ return [
1128
+ {
1129
+ name: "manifest",
1130
+ ok: false,
1131
+ detail: `could not read the pinned manifest: ${String(err)}`,
1132
+ },
1133
+ ];
1134
+ }
1135
+
1136
+ // The operator's own App name: this step's `app_name` INPUT (its tool
1137
+ // parameter), or what an earlier call recorded. Absent means the suggested
1138
+ // uniqueness-suffixed name, which stays editable on GitHub's own page.
1139
+ const chosenName = chosenAppName(ctx);
1140
+ if (chosenName) ctx.recordChoice(CHOICE_KEYS.appName, chosenName);
1141
+ const name = chosenName || suggestAppName(manifest.name, token(3));
1142
+ const webhookUrl =
1143
+ typeof ctx.choices[CHOICE_KEYS.webhookUrl] === "string"
1144
+ ? (ctx.choices[CHOICE_KEYS.webhookUrl] as string).trim()
1145
+ : "";
1146
+ const state = token(16);
1147
+
1148
+ // The page can only be rendered once the port is known (the manifest's
1149
+ // redirect_url must name the port actually bound), so the listener builds
1150
+ // it from its own bound address.
1151
+ let listener: CallbackListener;
1152
+ try {
1153
+ listener = await serve({
1154
+ state,
1155
+ port: opts.callbackPort ?? DEFAULT_CALLBACK_PORT,
1156
+ timeoutMs: callbackTimeoutMs,
1157
+ page: (bound) =>
1158
+ manifestPageHtml({
1159
+ postUrl: appCreationUrl(target, r.githubBase),
1160
+ state,
1161
+ target,
1162
+ manifest: renderManifest(manifest, {
1163
+ name,
1164
+ redirectUrl: bound.redirectUrl,
1165
+ webhookUrl: webhookUrl || undefined,
1166
+ }),
1167
+ }),
1168
+ });
1169
+ } catch (err) {
1170
+ return [
1171
+ {
1172
+ name: "callback listener",
1173
+ ok: false,
1174
+ detail: `could not listen on loopback for the GitHub callback: ${String(err)}`,
1175
+ },
1176
+ ];
1177
+ }
1178
+
1179
+ try {
1180
+ log(ctx, `open ${listener.url} in your browser and click "Create the App on GitHub".`);
1181
+ log(
1182
+ ctx,
1183
+ `it creates "${name}" under ${formatTarget(target)}; you can rename it on GitHub's own page.`,
1184
+ );
1185
+ if (!webhookUrl)
1186
+ log(
1187
+ ctx,
1188
+ `the webhook URL is the manifest placeholder (${manifest.hook_attributes.url}) — no fixed hostname was recorded by the ingress step. ` +
1189
+ "A rotating tunnel's daemon re-points it itself; under polling nothing is delivered; for any other endpoint, set it on the App's settings page.",
1190
+ );
1191
+ if (opts.openBrowser) {
1192
+ const opener = process.platform === "darwin" ? "open" : "xdg-open";
1193
+ await ctx.adapters.runner.run(opener, [listener.url]).catch(() => undefined);
1194
+ }
1195
+
1196
+ const arrival = await listener.waitForCode();
1197
+ if (!arrival.code) {
1198
+ return [
1199
+ {
1200
+ name: "manifest exchange",
1201
+ ok: false,
1202
+ detail:
1203
+ `${arrival.detail} — no credentials were saved. Whether GitHub created the App is not known from here: if ` +
1204
+ "you clicked Create on GitHub's page, check your GitHub App settings and delete an App this run left behind " +
1205
+ "before retrying. Run this step again: it serves a fresh page with a fresh one-time code (GitHub's codes are " +
1206
+ "single-use and expire after an hour).",
1207
+ },
1208
+ ];
1209
+ }
1210
+
1211
+ const converted = await convertManifestCode(ctx.adapters.fetch, r.apiBase, arrival.code);
1212
+ if (!converted.ok) {
1213
+ return [{ name: "manifest exchange", ok: false, detail: converted.detail }];
1214
+ }
1215
+
1216
+ const problems = writeCredentials(r.secretsDir, {
1217
+ appId: String(converted.app.id),
1218
+ pem: converted.app.pem,
1219
+ webhookSecret: converted.app.webhook_secret,
1220
+ });
1221
+ if (problems.length > 0) {
1222
+ return [
1223
+ {
1224
+ name: "credentials",
1225
+ ok: false,
1226
+ detail: `GitHub's conversion response did not validate: ${problems.join("; ")}`,
1227
+ },
1228
+ ];
1229
+ }
1230
+ ctx.recordChoice(CHOICE_KEYS.appId, String(converted.app.id));
1231
+ if (converted.app.slug) {
1232
+ ctx.recordChoice(CHOICE_KEYS.appSlug, converted.app.slug);
1233
+ ctx.recordChoice(CHOICE_KEYS.botLogin, botLoginFor(converted.app.slug));
1234
+ }
1235
+ if (converted.app.html_url) ctx.recordChoice(CHOICE_KEYS.appHtmlUrl, converted.app.html_url);
1236
+ log(
1237
+ ctx,
1238
+ `App ${converted.app.id} created; id, private key and webhook secret written to ${r.secretsDir} (0600). Nothing was pasted by hand.`,
1239
+ );
1240
+ return [
1241
+ {
1242
+ name: "manifest exchange",
1243
+ ok: true,
1244
+ detail: `App ${converted.app.id}${converted.app.slug ? ` ("${converted.app.slug}")` : ""} created; app_id, app.pem and webhook_secret written to ${r.secretsDir}`,
1245
+ },
1246
+ ];
1247
+ } finally {
1248
+ await listener.close();
1249
+ }
1250
+ }
1251
+
1252
+ /**
1253
+ * The guided install click, then verification through the App's own
1254
+ * installation listing — bounded polling with a progress line per attempt.
1255
+ * Only a MISSING INSTALLATION is worth waiting for; any other red check
1256
+ * (network, wrong key, unselected repo) returns immediately with its own
1257
+ * plain guidance rather than burning the budget. Labels are created on the
1258
+ * way through, idempotently, by verifyCore's create pass.
1259
+ */
1260
+ async function pollInstallAndLabels(
1261
+ ctx: StepContext,
1262
+ r: Resolved,
1263
+ initial?: CheckResult[],
1264
+ ): Promise<CheckResult[]> {
1265
+ const appHtmlUrl =
1266
+ typeof ctx.choices[CHOICE_KEYS.appHtmlUrl] === "string"
1267
+ ? (ctx.choices[CHOICE_KEYS.appHtmlUrl] as string)
1268
+ : null;
1269
+ let announced = false;
1270
+ let checks: CheckResult[] = [];
1271
+ for (let attempt = 1; attempt <= pollAttempts; attempt += 1) {
1272
+ // `initial` is the caller's own just-taken reading — never re-taken, so
1273
+ // the poll budget counts attempts, not round-trips.
1274
+ checks = attempt === 1 && initial ? initial : await verifyCore(ctx, r, true);
1275
+ if (checks.every((c) => c.ok)) return checks;
1276
+ const blocker = checks.find((c) => !c.ok)!;
1277
+ if (blocker.name !== "installation") return checks;
1278
+ if (!announced) {
1279
+ announced = true;
1280
+ log(
1281
+ ctx,
1282
+ `now install the App on ${r.repo.owner}/${r.repo.name}: ${appHtmlUrl ? `${appHtmlUrl} → ` : ""}Install App → ${r.repo.owner} → ` +
1283
+ `"Only select repositories" → ${r.repo.name}.`,
1284
+ );
1285
+ log(
1286
+ ctx,
1287
+ `checking for it up to ${pollAttempts} times, ${Math.round(pollIntervalMs / 1000)}s apart — this never waits forever.`,
1288
+ );
1289
+ }
1290
+ log(
1291
+ ctx,
1292
+ `installation not visible yet (attempt ${attempt}/${pollAttempts}) — still waiting for the Install click.`,
1293
+ );
1294
+ if (attempt < pollAttempts) await sleep(pollIntervalMs);
1295
+ }
1296
+ return [
1297
+ ...checks.filter((c) => c.name !== "installation"),
1298
+ {
1299
+ name: "installation",
1300
+ ok: false,
1301
+ detail:
1302
+ `no installation on "${r.repo.owner}" appeared while waiting — the App exists and its credentials are saved, so nothing is lost. ` +
1303
+ "Install it on the repo (Install App → Only select repositories) and run this step again; it will verify, not re-create.",
1304
+ },
1305
+ ];
1306
+ }
1307
+
1308
+ return {
1309
+ id: GITHUB_APP_STEP_ID,
1310
+ title: "GitHub App: create, install, label",
1311
+ explain:
1312
+ "Creates your GitHub App in one click. The wizard serves a page on your own machine that submits this repository's " +
1313
+ "pinned App manifest to GitHub — the permissions and webhook events come from the same artifact the tests pin, so there is " +
1314
+ "nothing to transcribe. GitHub sends the App id, private key and webhook secret straight back to the wizard, which stores " +
1315
+ "them 0600 in your secrets dir: no credential ever passes through your clipboard. Then you click Install on your repo, the " +
1316
+ "wizard verifies the installation through the App's own listing, and creates the trigger labels. If the App already exists " +
1317
+ "and is installed, running this again just verifies it.",
1318
+ // 40: after preflight (10), the topology/auth questions (20/30) and the
1319
+ // ingress step (39), whose recorded webhook URL goes into the manifest;
1320
+ // before the bring-up steps.
1321
+ order: 40,
1322
+ // The reviewed repo is NOT a parameter: it is the interview's answer, read
1323
+ // from the checklist (CHOICE_KEYS.repo). The only thing the operator may
1324
+ // supply here is what nothing else can derive — the App's display name.
1325
+ parameters: {
1326
+ type: "object",
1327
+ properties: {
1328
+ app_name: {
1329
+ type: "string",
1330
+ description:
1331
+ "The GitHub App's display name. Omit to have the wizard suggest a globally-unique one (still editable on " +
1332
+ "GitHub's page). Ignored when the App already exists.",
1333
+ },
1334
+ },
1335
+ },
1336
+
1337
+ async run(ctx): Promise<StepReport> {
1338
+ const resolved = resolveInputs(ctx);
1339
+ if (typeof resolved === "string")
1340
+ return { ok: false, checks: [{ name: "inputs", ok: false, detail: resolved }] };
1341
+ const r = resolved;
1342
+
1343
+ // Resume/verify-only: everything already in place ⇒ never re-create.
1344
+ const existing = readCredentials(r.secretsDir);
1345
+ if (existing.complete) {
1346
+ log(
1347
+ ctx,
1348
+ `credentials already present in ${r.secretsDir} — verifying the existing App instead of creating another one.`,
1349
+ );
1350
+ const checks = await verifyCore(ctx, r, true);
1351
+ if (checks.every((c) => c.ok)) {
1352
+ checks.push(orgCaveat(parseTargetChoice(ctx.choices[CHOICE_KEYS.appTarget])));
1353
+ return { ok: true, checks, data: { created: false, verified: true } };
1354
+ }
1355
+ const target = parseTargetChoice(ctx.choices[CHOICE_KEYS.appTarget]);
1356
+ // Only a missing INSTALLATION is worth waiting on; anything else
1357
+ // (network, wrong key, unselected repo) is reported as itself.
1358
+ if (checks.find((c) => !c.ok)!.name !== "installation") {
1359
+ checks.push(orgCaveat(target));
1360
+ return { ok: false, checks, data: { created: false } };
1361
+ }
1362
+ // Credentials exist but the install is missing: guide the install
1363
+ // click and poll — still no re-creation.
1364
+ const polled = await pollInstallAndLabels(ctx, r, checks);
1365
+ polled.push(orgCaveat(target));
1366
+ return { ok: polled.every((c) => c.ok), checks: polled, data: { created: false } };
1367
+ }
1368
+
1369
+ const target = await resolveTarget(ctx, r);
1370
+ const created = await createApp(ctx, r, target);
1371
+ // A creation lands all three files, so surviving ones of an incomplete
1372
+ // set are replaced by the new App's: say so.
1373
+ const surviving = (
1374
+ [
1375
+ [SECRET_FILES.appId, existing.appId],
1376
+ [SECRET_FILES.pem, existing.pem],
1377
+ [SECRET_FILES.webhookSecret, existing.webhookSecret],
1378
+ ] as const
1379
+ )
1380
+ .filter(([, value]) => value !== null)
1381
+ .map(([name]) => name);
1382
+ if (surviving.length > 0)
1383
+ created.unshift({
1384
+ name: "credentials replaced",
1385
+ ok: true,
1386
+ detail:
1387
+ `${r.secretsDir} held an incomplete credential set (${existing.problems.join("; ")}); ` +
1388
+ `${surviving.join(", ")} ${surviving.length === 1 ? "was" : "were"} replaced by the new App's credentials if its creation succeeded`,
1389
+ });
1390
+ if (!created.every((c) => c.ok)) {
1391
+ created.push(orgCaveat(target));
1392
+ return { ok: false, checks: created };
1393
+ }
1394
+
1395
+ const polled = await pollInstallAndLabels(ctx, r);
1396
+ const checks = [...created, ...polled, orgCaveat(target)];
1397
+ return {
1398
+ ok: checks.every((c) => c.ok),
1399
+ checks,
1400
+ data: { created: true, app_id: ctx.choices[CHOICE_KEYS.appId] ?? null },
1401
+ };
1402
+ },
1403
+
1404
+ async verify(ctx): Promise<StepReport> {
1405
+ const resolved = resolveInputs(ctx);
1406
+ if (typeof resolved === "string")
1407
+ return { ok: false, checks: [{ name: "inputs", ok: false, detail: resolved }] };
1408
+ // verify NEVER creates: a missing label is red here and fixed by a run.
1409
+ const checks = await verifyCore(ctx, resolved, false);
1410
+ checks.push(orgCaveat(parseTargetChoice(ctx.choices[CHOICE_KEYS.appTarget])));
1411
+ return { ok: checks.every((c) => c.ok), checks };
1412
+ },
1413
+ };
1414
+ }