@quickchatai/quickreview 1.1.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -57
- package/bin/qcr.js +1 -1
- package/config.example.toml +60 -14
- package/deploy/ansible/README.md +20 -12
- package/deploy/ansible/inventory.example.ini +1 -1
- package/deploy/collect-secrets.sh +1 -1
- package/deploy/qcr-update.sh +1 -1
- package/dist/extensions/cli/home.js +14 -0
- package/dist/extensions/cli/home.js.map +1 -0
- package/dist/extensions/cli/login-route.js +5 -0
- package/dist/extensions/cli/login-route.js.map +1 -0
- package/dist/extensions/cli/selfimprove.js +18 -0
- package/dist/extensions/cli/selfimprove.js.map +1 -0
- package/dist/extensions/home.js +350 -0
- package/dist/extensions/home.js.map +1 -0
- package/dist/extensions/login-route.js +180 -0
- package/dist/extensions/login-route.js.map +1 -0
- package/dist/extensions/onboarding.js +35 -11
- package/dist/extensions/onboarding.js.map +1 -1
- package/dist/extensions/selfimprove.js +49 -0
- package/dist/extensions/selfimprove.js.map +1 -0
- package/dist/extensions/setup-scan.js +23 -592
- package/dist/extensions/setup-scan.js.map +1 -1
- package/dist/extensions/setup.js +2 -47
- package/dist/extensions/setup.js.map +1 -1
- package/dist/src/archive/restore.js +18 -15
- package/dist/src/archive/restore.js.map +1 -1
- package/dist/src/botworker.js +13 -2
- package/dist/src/botworker.js.map +1 -1
- package/dist/src/channelfs.js +3 -26
- package/dist/src/channelfs.js.map +1 -1
- package/dist/src/ciretry.js +2 -1
- package/dist/src/ciretry.js.map +1 -1
- package/dist/src/cli.js +54 -61
- package/dist/src/cli.js.map +1 -1
- package/dist/src/cliops.js +3 -2
- package/dist/src/cliops.js.map +1 -1
- package/dist/src/commands.js +25 -2
- package/dist/src/commands.js.map +1 -1
- package/dist/src/config.js +32 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/daemon.js +50 -8
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/debuground.js +1 -0
- package/dist/src/debuground.js.map +1 -1
- package/dist/src/debugroundpi.js +6 -1
- package/dist/src/debugroundpi.js.map +1 -1
- package/dist/src/dispatch.js +5 -4
- package/dist/src/dispatch.js.map +1 -1
- package/dist/src/engine/caserunner-sdk.js +15 -251
- package/dist/src/engine/caserunner-sdk.js.map +1 -1
- package/dist/src/engine/debugpi.js +17 -5
- package/dist/src/engine/debugpi.js.map +1 -1
- package/dist/src/execboundary.js +10 -3
- package/dist/src/execboundary.js.map +1 -1
- package/dist/src/github.js +51 -16
- package/dist/src/github.js.map +1 -1
- package/dist/src/githubtoken.js +346 -0
- package/dist/src/githubtoken.js.map +1 -0
- package/dist/src/home/adapters.js +349 -0
- package/dist/src/home/adapters.js.map +1 -0
- package/dist/src/home/advanced.js +318 -0
- package/dist/src/home/advanced.js.map +1 -0
- package/dist/src/home/advancedtools.js +57 -0
- package/dist/src/home/advancedtools.js.map +1 -0
- package/dist/src/home/autoreview.js +325 -0
- package/dist/src/home/autoreview.js.map +1 -0
- package/dist/src/home/daemonstatus.js +32 -0
- package/dist/src/home/daemonstatus.js.map +1 -0
- package/dist/src/home/devicelogin.js +61 -0
- package/dist/src/home/devicelogin.js.map +1 -0
- package/dist/src/home/launch.js +202 -0
- package/dist/src/home/launch.js.map +1 -0
- package/dist/src/home/loginowner.js +205 -0
- package/dist/src/home/loginowner.js.map +1 -0
- package/dist/src/home/memory.js +122 -0
- package/dist/src/home/memory.js.map +1 -0
- package/dist/src/home/model.js +811 -0
- package/dist/src/home/model.js.map +1 -0
- package/dist/src/home/panel.js +520 -0
- package/dist/src/home/panel.js.map +1 -0
- package/dist/src/home/ports.js +19 -0
- package/dist/src/home/ports.js.map +1 -0
- package/dist/src/home/review.js +163 -0
- package/dist/src/home/review.js.map +1 -0
- package/dist/src/home/reviews.js +478 -0
- package/dist/src/home/reviews.js.map +1 -0
- package/dist/src/home/setup.js +507 -0
- package/dist/src/home/setup.js.map +1 -0
- package/dist/src/home/setupadapters.js +214 -0
- package/dist/src/home/setupadapters.js.map +1 -0
- package/dist/src/home/setupcard.js +199 -0
- package/dist/src/home/setupcard.js.map +1 -0
- package/dist/src/home/setuptools.js +45 -0
- package/dist/src/home/setuptools.js.map +1 -0
- package/dist/src/home/spend.js +160 -0
- package/dist/src/home/spend.js.map +1 -0
- package/dist/src/home/switches.js +209 -0
- package/dist/src/home/switches.js.map +1 -0
- package/dist/src/home/switchon.js +439 -0
- package/dist/src/home/switchon.js.map +1 -0
- package/dist/src/home/toolnames.js +41 -0
- package/dist/src/home/toolnames.js.map +1 -0
- package/dist/src/home/tune.js +341 -0
- package/dist/src/home/tune.js.map +1 -0
- package/dist/src/home/words.js +7 -0
- package/dist/src/home/words.js.map +1 -0
- package/dist/src/homes.js +14 -0
- package/dist/src/homes.js.map +1 -1
- package/dist/src/install.js +1 -1
- package/dist/src/install.js.map +1 -1
- package/dist/src/ipc/workerspec.js +8 -0
- package/dist/src/ipc/workerspec.js.map +1 -1
- package/dist/src/live/publish.js +1 -1
- package/dist/src/live/publish.js.map +1 -1
- package/dist/src/livecli.js +1 -1
- package/dist/src/livecli.js.map +1 -1
- package/dist/src/loginhint.js +8 -3
- package/dist/src/loginhint.js.map +1 -1
- package/dist/src/loginrefresh/handover.js +121 -0
- package/dist/src/loginrefresh/handover.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +54 -3
- package/dist/src/loginrefresh/lock.js.map +1 -1
- package/dist/src/loginrefresh/pirefresh.js +3 -0
- package/dist/src/loginrefresh/pirefresh.js.map +1 -1
- package/dist/src/loginrefresh/refresher.js +33 -5
- package/dist/src/loginrefresh/refresher.js.map +1 -1
- package/dist/src/loginrefresh/supervisor.js +18 -1
- package/dist/src/loginrefresh/supervisor.js.map +1 -1
- package/dist/src/npmverbs.js +46 -14
- package/dist/src/npmverbs.js.map +1 -1
- package/dist/src/onboard/choices.js +35 -4
- package/dist/src/onboard/choices.js.map +1 -1
- package/dist/src/onboard/engine.js +32 -306
- package/dist/src/onboard/engine.js.map +1 -1
- package/dist/src/onboard/pipin.js +22 -0
- package/dist/src/onboard/pipin.js.map +1 -0
- package/dist/src/onboard/stepforward.js +41 -0
- package/dist/src/onboard/stepforward.js.map +1 -0
- package/dist/src/onboard/steps/appslug.js +4 -1
- package/dist/src/onboard/steps/appslug.js.map +1 -1
- package/dist/src/onboard/steps/bringup.js +3 -2
- package/dist/src/onboard/steps/bringup.js.map +1 -1
- package/dist/src/onboard/steps/compose.js +29 -0
- package/dist/src/onboard/steps/compose.js.map +1 -1
- package/dist/src/onboard/steps/configfill.js +120 -39
- package/dist/src/onboard/steps/configfill.js.map +1 -1
- package/dist/src/onboard/steps/githubaccount.js +595 -0
- package/dist/src/onboard/steps/githubaccount.js.map +1 -0
- package/dist/src/onboard/steps/githubapp.js +65 -7
- package/dist/src/onboard/steps/githubapp.js.map +1 -1
- package/dist/src/onboard/steps/golive.js +2 -0
- package/dist/src/onboard/steps/golive.js.map +1 -1
- package/dist/src/onboard/steps/index.js +11 -0
- package/dist/src/onboard/steps/index.js.map +1 -1
- package/dist/src/onboard/steps/ingress.js +80 -25
- package/dist/src/onboard/steps/ingress.js.map +1 -1
- package/dist/src/onboard/steps/llmauth.js +103 -30
- package/dist/src/onboard/steps/llmauth.js.map +1 -1
- package/dist/src/onboard/steps/loginshare.js +213 -0
- package/dist/src/onboard/steps/loginshare.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +67 -3
- package/dist/src/onboard/steps/memory.js.map +1 -1
- package/dist/src/onboard/steps/proof.js +366 -56
- package/dist/src/onboard/steps/proof.js.map +1 -1
- package/dist/src/onboard/steps/repoconfig.js +72 -0
- package/dist/src/onboard/steps/repoconfig.js.map +1 -0
- package/dist/src/onboard/steps/topology.js +6 -6
- package/dist/src/onboard/steps/topology.js.map +1 -1
- package/dist/src/onboard/steps/transport.js +9 -1
- package/dist/src/onboard/steps/transport.js.map +1 -1
- package/dist/src/onboard/steps/tuiroute.js +102 -0
- package/dist/src/onboard/steps/tuiroute.js.map +1 -0
- package/dist/src/onboard/steps/verifyup.js +17 -1
- package/dist/src/onboard/steps/verifyup.js.map +1 -1
- package/dist/src/onboard/toolnames.js +30 -0
- package/dist/src/onboard/toolnames.js.map +1 -0
- package/dist/src/operatorwrite.js +23 -0
- package/dist/src/operatorwrite.js.map +1 -0
- package/dist/src/outcomes.js +4 -1
- package/dist/src/outcomes.js.map +1 -1
- package/dist/src/piagentdir.js +13 -0
- package/dist/src/piagentdir.js.map +1 -0
- package/dist/src/pilogin.js +31 -16
- package/dist/src/pilogin.js.map +1 -1
- package/dist/src/posting-journal.js +2 -1
- package/dist/src/posting-journal.js.map +1 -1
- package/dist/src/posting-outbox.js +6 -2
- package/dist/src/posting-outbox.js.map +1 -1
- package/dist/src/posting-receipt.js +14 -1
- package/dist/src/posting-receipt.js.map +1 -1
- package/dist/src/qcrbin.js +16 -11
- package/dist/src/qcrbin.js.map +1 -1
- package/dist/src/recheck.js +2 -1
- package/dist/src/recheck.js.map +1 -1
- package/dist/src/reconcile.js +22 -2
- package/dist/src/reconcile.js.map +1 -1
- package/dist/src/reviewargs.js +122 -0
- package/dist/src/reviewargs.js.map +1 -0
- package/dist/src/reviewcli.js +7 -161
- package/dist/src/reviewcli.js.map +1 -1
- package/dist/src/reviewdetach.js +76 -11
- package/dist/src/reviewdetach.js.map +1 -1
- package/dist/src/reviewhistory.js +32 -6
- package/dist/src/reviewhistory.js.map +1 -1
- package/dist/src/reviewnow.js +45 -0
- package/dist/src/reviewnow.js.map +1 -0
- package/dist/src/scancli.js +39 -691
- package/dist/src/scancli.js.map +1 -1
- package/dist/src/selfimprove.js +66 -0
- package/dist/src/selfimprove.js.map +1 -0
- package/dist/src/setup/adapters.js +5 -223
- package/dist/src/setup/adapters.js.map +1 -1
- package/dist/src/setup/caserunner.js +15 -249
- package/dist/src/setup/caserunner.js.map +1 -1
- package/dist/src/setup/engine.js +32 -341
- package/dist/src/setup/engine.js.map +1 -1
- package/dist/src/setup/facts.js +3 -592
- package/dist/src/setup/facts.js.map +1 -1
- package/dist/src/setup/findings.js +3 -947
- package/dist/src/setup/findings.js.map +1 -1
- package/dist/src/setup/presentation.js +3 -8
- package/dist/src/setup/presentation.js.map +1 -1
- package/dist/src/setup/scan.js +3 -724
- package/dist/src/setup/scan.js.map +1 -1
- package/dist/src/setup/scanbuild.js +9 -92
- package/dist/src/setup/scanbuild.js.map +1 -1
- package/dist/src/setup/scanlease.js +3 -250
- package/dist/src/setup/scanlease.js.map +1 -1
- package/dist/src/setup/scanreport.js +3 -288
- package/dist/src/setup/scanreport.js.map +1 -1
- package/dist/src/setup/scanrun.js +3 -1732
- package/dist/src/setup/scanrun.js.map +1 -1
- package/dist/src/setup/scantree.js +3 -256
- package/dist/src/setup/scantree.js.map +1 -1
- package/dist/src/setup/stage2request.js +3 -258
- package/dist/src/setup/stage2request.js.map +1 -1
- package/dist/src/setup/unitcut.js +3 -436
- package/dist/src/setup/unitcut.js.map +1 -1
- package/dist/src/state.js +2 -0
- package/dist/src/state.js.map +1 -1
- package/dist/src/statemachine.js +4 -1
- package/dist/src/statemachine.js.map +1 -1
- package/dist/src/takeover.js +1 -0
- package/dist/src/takeover.js.map +1 -1
- package/dist/src/takeoverpi.js +15 -1
- package/dist/src/takeoverpi.js.map +1 -1
- package/dist/src/tui/app.js +49 -0
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/board.js +88 -12
- package/dist/src/tui/board.js.map +1 -1
- package/dist/src/tui/cmdbar.js +4 -1
- package/dist/src/tui/cmdbar.js.map +1 -1
- package/dist/src/tui/companion.js +76 -11
- package/dist/src/tui/companion.js.map +1 -1
- package/dist/src/tui/connect.js +7 -1
- package/dist/src/tui/connect.js.map +1 -1
- package/dist/src/tui/deploytab.js +2 -2
- package/dist/src/tui/deploytab.js.map +1 -1
- package/dist/src/tui/filters.js +5 -2
- package/dist/src/tui/filters.js.map +1 -1
- package/dist/src/tui/ghread.js +61 -5
- package/dist/src/tui/ghread.js.map +1 -1
- package/dist/src/tui/index.js +109 -20
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/memforward.js +39 -2
- package/dist/src/tui/memforward.js.map +1 -1
- package/dist/src/tui/memory.js +3 -16
- package/dist/src/tui/memory.js.map +1 -1
- package/dist/src/tui/memoryurl.js +22 -0
- package/dist/src/tui/memoryurl.js.map +1 -0
- package/dist/src/tui/model.js +25 -0
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/prpause.js +7 -0
- package/dist/src/tui/prpause.js.map +1 -1
- package/dist/src/tui/remote.js +7 -1
- package/dist/src/tui/remote.js.map +1 -1
- package/dist/src/tui/route.js +143 -0
- package/dist/src/tui/route.js.map +1 -0
- package/dist/src/tui/setupstate.js +32 -0
- package/dist/src/tui/setupstate.js.map +1 -0
- package/dist/src/tui/sshtriage.js +102 -5
- package/dist/src/tui/sshtriage.js.map +1 -1
- package/dist/src/tui/theme.js +2 -1
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/unwedge.js +2 -12
- package/dist/src/tui/unwedge.js.map +1 -1
- package/dist/src/verdicts.js +17 -5
- package/dist/src/verdicts.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/src/webhook.js +12 -1
- package/dist/src/webhook.js.map +1 -1
- package/dist/src/wire.js +34 -8
- package/dist/src/wire.js.map +1 -1
- package/dist/src/workerworld.js +11 -1
- package/dist/src/workerworld.js.map +1 -1
- package/docker/README.md +1 -1
- package/docker/companion/entrypoint.sh +20 -1
- package/docker/entrypoint.sh +57 -6
- package/docker-compose.yml +16 -1
- package/extensions/cli/home.ts +14 -0
- package/extensions/cli/login-route.ts +10 -0
- package/extensions/cli/selfimprove.ts +18 -0
- package/extensions/home.ts +484 -0
- package/extensions/login-route.ts +220 -0
- package/extensions/onboarding.ts +41 -11
- package/extensions/selfimprove.ts +65 -0
- package/extensions/setup-scan.ts +79 -766
- package/extensions/setup.ts +14 -70
- package/package.json +6 -4
- package/prompts/onboarding.md +58 -18
- package/schemas/review-result.v1.json +60 -0
- package/scripts/deploy.sh +8 -6
- package/scripts/mint-token.sh +25 -4
- package/scripts/qcr-host.sh +28 -5
- package/scripts/vm-ghcr-login.sh +7 -4
- package/src/archive/restore.ts +18 -21
- package/src/botworker.ts +13 -2
- package/src/channelfs.ts +3 -26
- package/src/ciretry.ts +2 -1
- package/src/cli.ts +59 -67
- package/src/cliops.ts +3 -2
- package/src/commands.ts +28 -2
- package/src/config.ts +62 -4
- package/src/daemon.ts +58 -7
- package/src/debuground.ts +3 -0
- package/src/debugroundpi.ts +12 -1
- package/src/dispatch.ts +5 -4
- package/src/engine/caserunner-sdk.ts +29 -314
- package/src/engine/debugpi.ts +17 -5
- package/src/execboundary.ts +16 -4
- package/src/github.ts +57 -15
- package/src/githubtoken.ts +435 -0
- package/src/home/adapters.ts +418 -0
- package/src/home/advanced.ts +421 -0
- package/src/home/advancedtools.ts +96 -0
- package/src/home/autoreview.ts +385 -0
- package/src/home/daemonstatus.ts +55 -0
- package/src/home/devicelogin.ts +107 -0
- package/src/home/launch.ts +257 -0
- package/src/home/loginowner.ts +248 -0
- package/src/home/memory.ts +176 -0
- package/src/home/model.ts +965 -0
- package/src/home/panel.ts +666 -0
- package/src/home/ports.ts +338 -0
- package/src/home/review.ts +206 -0
- package/src/home/reviews.ts +565 -0
- package/src/home/setup.ts +689 -0
- package/src/home/setupadapters.ts +217 -0
- package/src/home/setupcard.ts +288 -0
- package/src/home/setuptools.ts +57 -0
- package/src/home/spend.ts +194 -0
- package/src/home/switches.ts +293 -0
- package/src/home/switchon.ts +554 -0
- package/src/home/toolnames.ts +46 -0
- package/src/home/tune.ts +405 -0
- package/src/home/words.ts +8 -0
- package/src/homes.ts +17 -0
- package/src/install.ts +1 -1
- package/src/ipc/workerspec.ts +15 -0
- package/src/live/publish.ts +4 -1
- package/src/livecli.ts +6 -1
- package/src/loginhint.ts +8 -3
- package/src/loginrefresh/handover.ts +138 -0
- package/src/loginrefresh/lock.ts +72 -3
- package/src/loginrefresh/pirefresh.ts +3 -0
- package/src/loginrefresh/refresher.ts +34 -5
- package/src/loginrefresh/supervisor.ts +25 -2
- package/src/npmverbs.ts +55 -13
- package/src/onboard/choices.ts +40 -4
- package/src/onboard/engine.ts +70 -421
- package/src/onboard/pipin.ts +31 -0
- package/src/onboard/stepforward.ts +61 -0
- package/src/onboard/steps/appslug.ts +4 -1
- package/src/onboard/steps/bringup.ts +3 -2
- package/src/onboard/steps/compose.ts +31 -0
- package/src/onboard/steps/configfill.ts +144 -40
- package/src/onboard/steps/githubaccount.ts +716 -0
- package/src/onboard/steps/githubapp.ts +92 -8
- package/src/onboard/steps/golive.ts +2 -0
- package/src/onboard/steps/index.ts +11 -0
- package/src/onboard/steps/ingress.ts +98 -31
- package/src/onboard/steps/llmauth.ts +114 -29
- package/src/onboard/steps/loginshare.ts +258 -0
- package/src/onboard/steps/memory.ts +76 -3
- package/src/onboard/steps/proof.ts +473 -55
- package/src/onboard/steps/repoconfig.ts +85 -0
- package/src/onboard/steps/topology.ts +7 -6
- package/src/onboard/steps/transport.ts +15 -0
- package/src/onboard/steps/tuiroute.ts +119 -0
- package/src/onboard/steps/verifyup.ts +20 -1
- package/src/onboard/toolnames.ts +34 -0
- package/src/operatorwrite.ts +25 -0
- package/src/outcomes.ts +8 -1
- package/src/piagentdir.ts +12 -0
- package/src/pilogin.ts +32 -16
- package/src/posting-journal.ts +6 -1
- package/src/posting-outbox.ts +10 -2
- package/src/posting-receipt.ts +17 -0
- package/src/qcrbin.ts +16 -11
- package/src/recheck.ts +2 -1
- package/src/reconcile.ts +29 -3
- package/src/reviewargs.ts +119 -0
- package/src/reviewcli.ts +6 -167
- package/src/reviewdetach.ts +107 -13
- package/src/reviewhistory.ts +20 -6
- package/src/reviewnow.ts +76 -0
- package/src/scancli.ts +75 -826
- package/src/selfimprove.ts +90 -0
- package/src/setup/adapters.ts +13 -221
- package/src/setup/caserunner.ts +34 -273
- package/src/setup/engine.ts +62 -496
- package/src/setup/facts.ts +3 -627
- package/src/setup/findings.ts +3 -1264
- package/src/setup/presentation.ts +3 -18
- package/src/setup/scan.ts +3 -1033
- package/src/setup/scanbuild.ts +20 -114
- package/src/setup/scanlease.ts +3 -297
- package/src/setup/scanreport.ts +3 -373
- package/src/setup/scanrun.ts +3 -2268
- package/src/setup/scantree.ts +3 -259
- package/src/setup/stage2request.ts +3 -347
- package/src/setup/unitcut.ts +3 -523
- package/src/state.ts +43 -0
- package/src/statemachine.ts +4 -1
- package/src/takeover.ts +5 -0
- package/src/takeoverpi.ts +16 -2
- package/src/tui/app.ts +47 -0
- package/src/tui/board.ts +111 -14
- package/src/tui/cmdbar.ts +4 -1
- package/src/tui/companion.ts +80 -11
- package/src/tui/connect.ts +7 -1
- package/src/tui/deploytab.ts +2 -2
- package/src/tui/filters.ts +7 -3
- package/src/tui/ghread.ts +74 -7
- package/src/tui/index.ts +117 -23
- package/src/tui/memforward.ts +52 -3
- package/src/tui/memory.ts +3 -15
- package/src/tui/memoryurl.ts +22 -0
- package/src/tui/model.ts +36 -2
- package/src/tui/prpause.ts +11 -2
- package/src/tui/remote.ts +9 -1
- package/src/tui/route.ts +173 -0
- package/src/tui/setupstate.ts +56 -0
- package/src/tui/sshtriage.ts +127 -6
- package/src/tui/theme.ts +2 -1
- package/src/tui/unwedge.ts +2 -14
- package/src/verdicts.ts +23 -5
- package/src/version.ts +1 -1
- package/src/webhook.ts +10 -1
- package/src/wire.ts +42 -9
- package/src/workerworld.ts +16 -2
- package/dist/src/live/projection.js +0 -450
- package/dist/src/live/projection.js.map +0 -1
- package/dist/src/live/read.js +0 -60
- package/dist/src/live/read.js.map +0 -1
- package/dist/src/onboard/launch.js +0 -83
- package/dist/src/onboard/launch.js.map +0 -1
- package/dist/src/setup/ledger.js +0 -207
- package/dist/src/setup/ledger.js.map +0 -1
- package/dist/src/setup/stage2.js +0 -617
- package/dist/src/setup/stage2.js.map +0 -1
- package/dist/src/setup/textsniff.js +0 -89
- package/dist/src/setup/textsniff.js.map +0 -1
- package/src/live/projection.ts +0 -491
- package/src/live/read.ts +0 -86
- package/src/onboard/launch.ts +0 -115
- package/src/setup/ledger.ts +0 -324
- package/src/setup/stage2.ts +0 -926
- package/src/setup/textsniff.ts +0 -95
package/src/cli.ts
CHANGED
|
@@ -68,6 +68,7 @@ import { sanitizeText } from "./tui/paint.js";
|
|
|
68
68
|
import { operatorCommandWriteScript } from "./tui/unwedge.js";
|
|
69
69
|
import { shellQuote, transportOf } from "./tui/remote.js";
|
|
70
70
|
import { MutationGate } from "@quickchatai/pi-quickreview/src/gate.js";
|
|
71
|
+
import { botAccountLogin, isPersonMode } from "@quickchatai/pi-quickreview/src/identity.js";
|
|
71
72
|
import type { GitHubAdapter } from "./github.js";
|
|
72
73
|
import type { OutcomeCapture } from "./outcomes.js";
|
|
73
74
|
import type { Learner, MineReport } from "./learner/learner.js";
|
|
@@ -198,17 +199,18 @@ export async function cmdOutcomes(rest: string[], json: boolean): Promise<number
|
|
|
198
199
|
const outcomes = new OutcomeCapture({
|
|
199
200
|
store,
|
|
200
201
|
botLogin: cfg.github.bot_login,
|
|
202
|
+
personMode: isPersonMode(cfg.github),
|
|
201
203
|
...(existsSync(worktrees.basePath) ? { git: worktrees } : {}),
|
|
202
204
|
});
|
|
203
205
|
const since = new Date(Date.now() - days * 86_400_000).toISOString();
|
|
204
206
|
if (capture) {
|
|
205
207
|
let read: GitHubAdapter | undefined;
|
|
206
208
|
if (readGithub) {
|
|
207
|
-
const {
|
|
209
|
+
const { githubCredential } = await import("@quickchatai/pi-quickreview/src/githubAuth.js");
|
|
208
210
|
const { GitHubAdapter } = await import("./github.js");
|
|
209
211
|
read = new GitHubAdapter(
|
|
210
212
|
cfg,
|
|
211
|
-
|
|
213
|
+
githubCredential(cfg.github),
|
|
212
214
|
recordOnlyGate(join(stateDir(), "dryrun")),
|
|
213
215
|
);
|
|
214
216
|
}
|
|
@@ -365,7 +367,8 @@ export async function cmdLessons(rest: string[], json: boolean): Promise<number>
|
|
|
365
367
|
root,
|
|
366
368
|
eodTime: cfg.eod.time,
|
|
367
369
|
timeZone: cfg.eod.timezone,
|
|
368
|
-
|
|
370
|
+
// The bot ACCOUNT ("" in person mode, where the person's words are a human's).
|
|
371
|
+
botLogin: botAccountLogin(cfg.github),
|
|
369
372
|
baseBranch: cfg.github.base_branch,
|
|
370
373
|
config: learnerKnobsOf(cfg.memory),
|
|
371
374
|
...(existsSync(worktrees.basePath) ? { git: worktrees } : {}),
|
|
@@ -703,13 +706,13 @@ export interface DoubtedFinding {
|
|
|
703
706
|
* when the developer machine carries the app credential. */
|
|
704
707
|
export async function enrichWhy(payload: WhyPayload, secretsDir?: string): Promise<WhyPayload> {
|
|
705
708
|
try {
|
|
706
|
-
const {
|
|
709
|
+
const { githubCredential } = await import("@quickchatai/pi-quickreview/src/githubAuth.js");
|
|
707
710
|
const { GitHubAdapter } = await import("./github.js");
|
|
708
711
|
const cfg = loadConfig();
|
|
709
712
|
const gate = recordOnlyGate(join(stateDir(), "dryrun"), "qcr why");
|
|
710
713
|
const adapter = new GitHubAdapter(
|
|
711
714
|
cfg,
|
|
712
|
-
|
|
715
|
+
githubCredential({ ...cfg.github, secrets_dir: secretsDir ?? cfg.github.secrets_dir }),
|
|
713
716
|
gate,
|
|
714
717
|
);
|
|
715
718
|
const convo = await adapter.prConversation(payload.pr, 3);
|
|
@@ -928,14 +931,22 @@ export async function cmdBackfill(
|
|
|
928
931
|
limit: number | undefined,
|
|
929
932
|
json: boolean,
|
|
930
933
|
): Promise<void> {
|
|
931
|
-
const {
|
|
934
|
+
const { githubCredential } = await import("@quickchatai/pi-quickreview/src/githubAuth.js");
|
|
932
935
|
const { GitHubAdapter } = await import("./github.js");
|
|
933
936
|
const { runBackfill } = await import("./backfill.js");
|
|
934
937
|
const cfg = loadConfig();
|
|
938
|
+
// The import reads "reviews by the bot's login" as the bot's history. Under
|
|
939
|
+
// the person's own credential that login is the person's, and their own
|
|
940
|
+
// reviews are not the bot's: there is nothing this importer can tell apart.
|
|
941
|
+
if (isPersonMode(cfg.github))
|
|
942
|
+
throw new Error(
|
|
943
|
+
'qcr backfill imports a GitHub App\'s review history; under [github] auth = "user" the login is ' +
|
|
944
|
+
"your own, so your reviews and the bot's cannot be told apart by it — nothing to import",
|
|
945
|
+
);
|
|
935
946
|
// A gate that can only ever record: the importer performs no mutations, and
|
|
936
947
|
// this keeps that structural rather than prompted.
|
|
937
948
|
const gate = recordOnlyGate(join(stateDir(), "dryrun"), "qcr backfill");
|
|
938
|
-
const adapter = new GitHubAdapter(cfg,
|
|
949
|
+
const adapter = new GitHubAdapter(cfg, githubCredential(cfg.github), gate);
|
|
939
950
|
const store = openStore();
|
|
940
951
|
try {
|
|
941
952
|
const stats = await runBackfill(store, adapter, cfg, {
|
|
@@ -976,12 +987,12 @@ export async function cmdHistory(rest: string[]): Promise<number> {
|
|
|
976
987
|
const months = intFlag(rest, "--months", HISTORY_USAGE);
|
|
977
988
|
if (sub === "digest") {
|
|
978
989
|
const limit = intFlag(rest, "--limit", HISTORY_USAGE);
|
|
979
|
-
const {
|
|
990
|
+
const { githubCredential } = await import("@quickchatai/pi-quickreview/src/githubAuth.js");
|
|
980
991
|
const { GitHubAdapter } = await import("./github.js");
|
|
981
992
|
const { collectReviewHistory } = await import("./reviewhistory.js");
|
|
982
993
|
const cfg = loadConfig();
|
|
983
994
|
const gate = recordOnlyGate(join(stateDir(), "dryrun"), "qcr history");
|
|
984
|
-
const adapter = new GitHubAdapter(cfg,
|
|
995
|
+
const adapter = new GitHubAdapter(cfg, githubCredential(cfg.github), gate);
|
|
985
996
|
const store = openStore();
|
|
986
997
|
try {
|
|
987
998
|
const digest = await collectReviewHistory(adapter, store, cfg, {
|
|
@@ -1230,12 +1241,13 @@ async function cmdDrain(rest: string[]): Promise<number> {
|
|
|
1230
1241
|
// until `qcr resume-pr`, and no session lifts it. The cutover wizard's stage 9
|
|
1231
1242
|
// pauses every tracked PR but one with it before the drain is lifted.
|
|
1232
1243
|
|
|
1233
|
-
export const PR_LEVER_USAGE =
|
|
1244
|
+
export const PR_LEVER_USAGE =
|
|
1245
|
+
"usage: qcr pause-pr <pr> [--reason <text>] | qcr resume-pr <pr> | qcr review-now <pr>";
|
|
1234
1246
|
|
|
1235
1247
|
/** `qcr pause-pr|resume-pr`'s arguments: one PR number, and for a pause an
|
|
1236
1248
|
* optional `--reason <text>` (the reason the board and `qcr status` show). */
|
|
1237
1249
|
export function parsePrLeverArgs(
|
|
1238
|
-
verb: "pause-pr" | "resume-pr",
|
|
1250
|
+
verb: "pause-pr" | "resume-pr" | "review-now",
|
|
1239
1251
|
rest: readonly string[],
|
|
1240
1252
|
): import("./tui/prpause.js").PrLeverCommand {
|
|
1241
1253
|
let pr: number | undefined;
|
|
@@ -1251,10 +1263,14 @@ export function parsePrLeverArgs(
|
|
|
1251
1263
|
}
|
|
1252
1264
|
if (pr === undefined) throw new Error(PR_LEVER_USAGE);
|
|
1253
1265
|
if (verb === "resume-pr") return { command: "resume-pr", pr };
|
|
1266
|
+
if (verb === "review-now") return { command: "review-now", pr };
|
|
1254
1267
|
return { command: "pause-pr", pr, reason: reason ?? "paused by the operator (qcr pause-pr)" };
|
|
1255
1268
|
}
|
|
1256
1269
|
|
|
1257
|
-
async function cmdPrLever(
|
|
1270
|
+
async function cmdPrLever(
|
|
1271
|
+
verb: "pause-pr" | "resume-pr" | "review-now",
|
|
1272
|
+
rest: string[],
|
|
1273
|
+
): Promise<number> {
|
|
1258
1274
|
const cmd = parsePrLeverArgs(verb, rest);
|
|
1259
1275
|
const cfg = loadConfig();
|
|
1260
1276
|
const { runnerFor } = await import("./tui/remote.js");
|
|
@@ -1540,6 +1556,7 @@ export async function cmdCompanion(sub: string | undefined): Promise<void> {
|
|
|
1540
1556
|
dir: companionGithubDir(),
|
|
1541
1557
|
repo: full.github.repo,
|
|
1542
1558
|
secretsDir: full.github.secrets_dir,
|
|
1559
|
+
auth: full.github.auth ?? "app",
|
|
1543
1560
|
}).refresh();
|
|
1544
1561
|
console.log(`companion ${cfg.container_name}: ${tokenOutcomeLine(gh)}`);
|
|
1545
1562
|
const mirror = lc.syncConfigMirror();
|
|
@@ -1840,55 +1857,6 @@ export async function cmdSetup(rest: string[]): Promise<number> {
|
|
|
1840
1857
|
return 0;
|
|
1841
1858
|
}
|
|
1842
1859
|
|
|
1843
|
-
/** `qcr onboard` — the guided setup wizard: pi at the pinned
|
|
1844
|
-
* version via npx (works from a bare clone; pi stays a non-dependency), the
|
|
1845
|
-
* onboarding prompt + extension + a restricted tool set, then the TUI handoff
|
|
1846
|
-
* when the checklist verifies complete. Resumable: run it again after a crash
|
|
1847
|
-
* and completed steps are re-verified, not repeated. */
|
|
1848
|
-
export async function cmdOnboard(): Promise<number> {
|
|
1849
|
-
const { piPinFromPackageJson, onboardingSessionDir, runOnboard } =
|
|
1850
|
-
await import("./onboard/launch.js");
|
|
1851
|
-
const { checklistComplete } = await import("./onboard/engine.js");
|
|
1852
|
-
const { spawn } = await import("node:child_process");
|
|
1853
|
-
const root = sandboxRoot();
|
|
1854
|
-
// Pin source: config.toml [pi] when it loads; package.json's "qcrPi" block on a
|
|
1855
|
-
// bare clone (config.toml is written later, BY onboarding). Same pinned
|
|
1856
|
-
// version by the rule-7 mirror discipline.
|
|
1857
|
-
let pin: { package: string; version: string } | null = null;
|
|
1858
|
-
try {
|
|
1859
|
-
const { package: pkg, version } = loadConfig().pi;
|
|
1860
|
-
pin = { package: pkg, version };
|
|
1861
|
-
} catch {
|
|
1862
|
-
pin = piPinFromPackageJson(root);
|
|
1863
|
-
}
|
|
1864
|
-
if (!pin)
|
|
1865
|
-
throw new Error(
|
|
1866
|
-
'onboard: pi pin not found (need config.toml [pi] or the package.json "qcrPi" block)',
|
|
1867
|
-
);
|
|
1868
|
-
mkdirSync(onboardingSessionDir(root), { recursive: true });
|
|
1869
|
-
const run = (cmd: string, args: string[]): Promise<number> =>
|
|
1870
|
-
new Promise((resolvePromise) => {
|
|
1871
|
-
const child = spawn(cmd, args, {
|
|
1872
|
-
stdio: "inherit",
|
|
1873
|
-
cwd: root,
|
|
1874
|
-
env: { ...process.env, QCR_ROOT: root },
|
|
1875
|
-
});
|
|
1876
|
-
child.on("error", (err) => {
|
|
1877
|
-
console.error(`onboard: ${cmd} failed to start: ${err.message}`);
|
|
1878
|
-
resolvePromise(1);
|
|
1879
|
-
});
|
|
1880
|
-
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
1881
|
-
});
|
|
1882
|
-
return runOnboard({
|
|
1883
|
-
pin,
|
|
1884
|
-
root,
|
|
1885
|
-
spawn: run,
|
|
1886
|
-
isComplete: () => checklistComplete(join(root, "state")),
|
|
1887
|
-
log: (line) => console.log(line),
|
|
1888
|
-
cliPath: join(root, "dist", "src", "cli.js"),
|
|
1889
|
-
});
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
1860
|
export const ARCHIVE_USAGE =
|
|
1893
1861
|
"usage: qcr archive status [--plan] [--json] | qcr archive run | qcr archive restore <pr> | " +
|
|
1894
1862
|
"qcr archive extract <pr> --to <dir>";
|
|
@@ -2137,6 +2105,16 @@ export async function main(rawArgv: string[]): Promise<void> {
|
|
|
2137
2105
|
const { runTui } = await import("./tui/index.js");
|
|
2138
2106
|
return runTui(rest);
|
|
2139
2107
|
}
|
|
2108
|
+
case "home": {
|
|
2109
|
+
// the home (src/home/launch.ts): the bin routes a bare `qcr` here too
|
|
2110
|
+
// (src/qcrbin.ts); the image's home mode reaches it through this verb
|
|
2111
|
+
if (rest.length > 0) throw new Error("usage: qcr home");
|
|
2112
|
+
const { homeRunDeps, runHome } = await import("./home/launch.js");
|
|
2113
|
+
// the token taken at startup, when the wrapper handed one over
|
|
2114
|
+
const githubToken = takeHandoffToken();
|
|
2115
|
+
process.exitCode = runHome({ ...homeRunDeps(sandboxRoot()), githubToken: () => githubToken });
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2140
2118
|
case "companion":
|
|
2141
2119
|
if (rest.length !== 1) throw new Error("usage: qcr companion <up|down|status|rebuild|plan>");
|
|
2142
2120
|
return cmdCompanion(rest[0]);
|
|
@@ -2144,10 +2122,6 @@ export async function main(rawArgv: string[]): Promise<void> {
|
|
|
2144
2122
|
process.exitCode = await cmdMemory(rest);
|
|
2145
2123
|
return;
|
|
2146
2124
|
}
|
|
2147
|
-
case "onboard": {
|
|
2148
|
-
process.exitCode = await cmdOnboard();
|
|
2149
|
-
return;
|
|
2150
|
-
}
|
|
2151
2125
|
case "setup": {
|
|
2152
2126
|
process.exitCode = await cmdSetup(rest);
|
|
2153
2127
|
return;
|
|
@@ -2157,10 +2131,28 @@ export async function main(rawArgv: string[]): Promise<void> {
|
|
|
2157
2131
|
return;
|
|
2158
2132
|
}
|
|
2159
2133
|
case "pause-pr":
|
|
2160
|
-
case "resume-pr":
|
|
2134
|
+
case "resume-pr":
|
|
2135
|
+
case "review-now": {
|
|
2136
|
+
// review-now: the operator's "review this PR now" — queued as a
|
|
2137
|
+
// human-asked round through the sealed operator channel, no label.
|
|
2161
2138
|
process.exitCode = await cmdPrLever(cmd, rest);
|
|
2162
2139
|
return;
|
|
2163
2140
|
}
|
|
2141
|
+
case "github-token": {
|
|
2142
|
+
// The own-account route's token refresher (src/githubtoken.ts): gh's
|
|
2143
|
+
// token → <secrets>/github_token, 0600. The crontab entry runs this.
|
|
2144
|
+
const { cmdGithubToken } = await import("./githubtoken.js");
|
|
2145
|
+
process.exitCode = await cmdGithubToken(rest, {
|
|
2146
|
+
secretsDir: () => {
|
|
2147
|
+
try {
|
|
2148
|
+
return loadConfig().github.secrets_dir;
|
|
2149
|
+
} catch {
|
|
2150
|
+
return locateSecrets({}).path;
|
|
2151
|
+
}
|
|
2152
|
+
},
|
|
2153
|
+
});
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2164
2156
|
case "message": {
|
|
2165
2157
|
process.exitCode = await cmdMessage(rest);
|
|
2166
2158
|
return;
|
|
@@ -2238,7 +2230,7 @@ export async function main(rawArgv: string[]): Promise<void> {
|
|
|
2238
2230
|
}
|
|
2239
2231
|
default:
|
|
2240
2232
|
console.log(
|
|
2241
|
-
"usage: qcr <version|config check [--config-home <dir>]|
|
|
2233
|
+
"usage: qcr <version|config check [--config-home <dir>]|home|setup [validate [<profile>]|commit]|status|spend|today|why <n>|metrics [--pr <n>]|events|takeover <n>|debug-round <n>|release <n>|tui [pr|overseer]|drain <on [--hold]|off>|pause-pr <pr> [--reason <text>]|resume-pr <pr>|review-now <pr>|github-token sync [--gh <path>] [--secrets-dir <dir>]|message <pr> [--may-post] <text...>|archive <status [--plan]|run|restore <pr>|extract <pr> --to <dir>>|install <where|migrate>|" +
|
|
2242
2234
|
"companion <up|down|status|rebuild>|review [start|status|stop|branch|--uncommitted|--diff FILE|-|<PR number|URL>] [--repo OWNER/NAME] [--base REF] [--preview] [--key K] [--limit USD] [--json]|live [RUN_ID|DIR] [--lens ID] [--round N] [--follow] [--json]|open [RUN_ID|DIR|FILE.jsonl|scan[:RUN]|pr N [--round R]] [--lens ID] [--write] [--print]|scan <preview|start|status|stop|findings|matches|match|suggestions|stage2 propose|stage2 start|dismiss|units show|units propose|units adjust|units save|login|doctor|rebuild>|memory reembed [--user <name>]|memory relayout [--plan|--apply]|backfill [--dry-run] [--limit <n>]|history <digest|pull>|outcomes [--days <n>] [--capture [--read-github]]|lessons [mine|shadow-report [--days <n>]|off|on|suppress <id>|unsuppress <id>]|checklist|compare <pr...>|" +
|
|
2243
2235
|
"regression [--set <path>] [--clone <path>] [--change <name>] [--limit <n>] [--out <path>]>",
|
|
2244
2236
|
);
|
package/src/cliops.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// qcr checklist / qcr compare — production wiring of the cutover harnesses. Local
|
|
2
2
|
// runs exercise the offline halves; the live halves (tunnel, real searches,
|
|
3
3
|
// live rounds) light up on the VM where the daemon and pi are running.
|
|
4
|
+
import { botAccountLogin } from "@quickchatai/pi-quickreview/src/identity.js";
|
|
4
5
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
5
6
|
import { join } from "node:path";
|
|
6
7
|
import { runChecklist, type ChecklistDeps } from "./checklist.js";
|
|
@@ -253,7 +254,7 @@ export async function cmdChecklist(parts?: ProductionParts): Promise<number> {
|
|
|
253
254
|
await p.adapter.searchLabelTriggered(),
|
|
254
255
|
"label",
|
|
255
256
|
cfg.dry_run,
|
|
256
|
-
cfg.github
|
|
257
|
+
botAccountLogin(cfg.github),
|
|
257
258
|
store,
|
|
258
259
|
),
|
|
259
260
|
teamTriggered: async () =>
|
|
@@ -261,7 +262,7 @@ export async function cmdChecklist(parts?: ProductionParts): Promise<number> {
|
|
|
261
262
|
await p.adapter.searchTeamTriggered(),
|
|
262
263
|
"team_request",
|
|
263
264
|
cfg.dry_run,
|
|
264
|
-
cfg.github
|
|
265
|
+
botAccountLogin(cfg.github),
|
|
265
266
|
store,
|
|
266
267
|
),
|
|
267
268
|
derivedQueue: () => derivedQueueOf(store.allPrs(), cfg.dry_run),
|
package/src/commands.ts
CHANGED
|
@@ -99,7 +99,10 @@ export const CONTROL_TEXT_MAX = 8000;
|
|
|
99
99
|
* session's command file cannot spell them: in the session directories, and
|
|
100
100
|
* in an UNSEALED operator directory (served as a session's), they are refused
|
|
101
101
|
* at validation, and `executeCommand` refuses
|
|
102
|
-
* them again for any issuer but the operator.
|
|
102
|
+
* them again for any issuer but the operator. `review-now` is the operator's
|
|
103
|
+
* "review this PR now": one PR queued as a human-asked round, exactly as a
|
|
104
|
+
* fresh trigger label would queue it, with no GitHub write to ask for it (the
|
|
105
|
+
* onboarding wizard's proof round sends it). The kill switch's state lives
|
|
103
106
|
* in state.db (src/lessonledger.ts), never as a memory tag anyone holding a
|
|
104
107
|
* key could write.
|
|
105
108
|
*/
|
|
@@ -110,6 +113,7 @@ export const OPERATOR_COMMANDS = [
|
|
|
110
113
|
"unsuppress-lesson",
|
|
111
114
|
"archive-run",
|
|
112
115
|
"archive-restore",
|
|
116
|
+
"review-now",
|
|
113
117
|
...CONTROL_COMMANDS,
|
|
114
118
|
] as const;
|
|
115
119
|
|
|
@@ -180,6 +184,9 @@ export type Command =
|
|
|
180
184
|
* valid in a session's file. */
|
|
181
185
|
| { command: "archive-run" }
|
|
182
186
|
| { command: "archive-restore"; pr: number }
|
|
187
|
+
/** The operator's "review this PR now" (`OPERATOR_COMMANDS`): never valid
|
|
188
|
+
* in a session's file. */
|
|
189
|
+
| { command: "review-now"; pr: number }
|
|
183
190
|
/** The operator's round controls (`CONTROL_COMMANDS`): never valid in a
|
|
184
191
|
* session's file. */
|
|
185
192
|
| { command: "steer"; pr: number; text: string }
|
|
@@ -263,6 +270,9 @@ export interface CommandDeps {
|
|
|
263
270
|
* only for an operator-issued command; optional like `drain`. */
|
|
264
271
|
archiveRun?(file: string): Promise<FileOutcome | void> | FileOutcome | void;
|
|
265
272
|
archiveRestore?(pr: number, file: string): Promise<FileOutcome | void> | FileOutcome | void;
|
|
273
|
+
/** The operator's "review this PR now": queue one PR for a human-asked
|
|
274
|
+
* round. Reached only for an operator-issued command; optional like `drain`. */
|
|
275
|
+
reviewNow?(pr: number, file: string): Promise<FileOutcome | void> | FileOutcome | void;
|
|
266
276
|
/** The operator's round controls (`CONTROL_COMMANDS`), forwarded to the PR's
|
|
267
277
|
* host. Reached only for an operator-issued command; optional like `drain`,
|
|
268
278
|
* so a daemon with no worker host refuses them with a reason. */
|
|
@@ -338,7 +348,7 @@ export function validateCommand(raw: unknown, opts: ValidateOptions = {}): Valid
|
|
|
338
348
|
ok: false,
|
|
339
349
|
reason:
|
|
340
350
|
`command '${name}' is the OPERATOR's alone: it is accepted only from the root-sealed commands/operator/ ` +
|
|
341
|
-
"channel (`qcr lessons off|on|suppress|unsuppress`, `qcr archive run|restore`), never from a session's command file",
|
|
351
|
+
"channel (`qcr lessons off|on|suppress|unsuppress`, `qcr archive run|restore`, `qcr review-now`), never from a session's command file",
|
|
342
352
|
};
|
|
343
353
|
return validateOperatorCommand(name, obj);
|
|
344
354
|
}
|
|
@@ -609,6 +619,12 @@ function validateOperatorCommand(
|
|
|
609
619
|
if (!isPrNumber(obj["pr"])) return { ok: false, reason: "'pr' must be a positive integer" };
|
|
610
620
|
return { ok: true, command: { command: "archive-restore", pr: obj["pr"] } };
|
|
611
621
|
}
|
|
622
|
+
case "review-now": {
|
|
623
|
+
const err = fieldError(obj, ["pr"]);
|
|
624
|
+
if (err) return { ok: false, reason: err };
|
|
625
|
+
if (!isPrNumber(obj["pr"])) return { ok: false, reason: "'pr' must be a positive integer" };
|
|
626
|
+
return { ok: true, command: { command: "review-now", pr: obj["pr"] } };
|
|
627
|
+
}
|
|
612
628
|
case "steer":
|
|
613
629
|
case "follow-up":
|
|
614
630
|
case "message": {
|
|
@@ -971,6 +987,16 @@ export async function executeCommand(
|
|
|
971
987
|
return { ok: false, reason: "archive-restore is not wired on this daemon" };
|
|
972
988
|
return normalize(await deps.archiveRestore(command.pr, ctx.delivery_key ?? ctx.file));
|
|
973
989
|
}
|
|
990
|
+
case "review-now": {
|
|
991
|
+
// The belt behind validation's brace, as for the kill switch.
|
|
992
|
+
if (ctx.issuer !== "operator")
|
|
993
|
+
return {
|
|
994
|
+
ok: false,
|
|
995
|
+
reason: `'${command.command}' is the operator's alone — refused for issuer '${ctx.issuer}'`,
|
|
996
|
+
};
|
|
997
|
+
if (!deps.reviewNow) return { ok: false, reason: "review-now is not wired on this daemon" };
|
|
998
|
+
return normalize(await deps.reviewNow(command.pr, ctx.delivery_key ?? ctx.file));
|
|
999
|
+
}
|
|
974
1000
|
case "steer":
|
|
975
1001
|
case "follow-up":
|
|
976
1002
|
case "abort-round":
|
package/src/config.ts
CHANGED
|
@@ -974,8 +974,11 @@ function checkSectionTimeZone<T extends { timezone: string }>(section: T, where:
|
|
|
974
974
|
* what an absent section means) starts nothing at all.
|
|
975
975
|
*/
|
|
976
976
|
export interface IngressConfig {
|
|
977
|
-
/** "none" ⇒ no watcher is ever constructed; "tunnel-file" ⇒ self-heal on
|
|
978
|
-
|
|
977
|
+
/** "none" ⇒ no watcher is ever constructed; "tunnel-file" ⇒ self-heal on;
|
|
978
|
+
* "polling" ⇒ no webhooks at all: the daemon's `/webhook` route refuses
|
|
979
|
+
* every delivery, no webhook secret is read, and the reconcile poll is the
|
|
980
|
+
* only trigger. `[github] auth = "user"` requires "polling". */
|
|
981
|
+
shape: "none" | "tunnel-file" | "polling";
|
|
979
982
|
/** File the tunnel sidecar writes the bare public hostname into. Empty ⇒
|
|
980
983
|
* `<root>/state/ingress/hostname` (see ingressHostnameFile). */
|
|
981
984
|
hostname_file: string;
|
|
@@ -994,6 +997,13 @@ export const INGRESS_DEFAULTS: IngressConfig = {
|
|
|
994
997
|
sweep_interval_s: 300,
|
|
995
998
|
};
|
|
996
999
|
|
|
1000
|
+
/** Does this install take webhooks at all? Not under the polling shape
|
|
1001
|
+
* (and so never under `[github] auth = "user"`, which requires it): there the
|
|
1002
|
+
* `/webhook` route refuses every delivery and no webhook secret is read. */
|
|
1003
|
+
export function webhooksEnabled(cfg: Pick<Config, "ingress">): boolean {
|
|
1004
|
+
return cfg.ingress.shape !== "polling";
|
|
1005
|
+
}
|
|
1006
|
+
|
|
997
1007
|
/** The hostname file's resolved path — the one place the default is spelled. */
|
|
998
1008
|
export function ingressHostnameFile(cfg: Config, root: string): string {
|
|
999
1009
|
const configured = cfg.ingress.hostname_file.trim();
|
|
@@ -1148,6 +1158,43 @@ export interface Config extends CoreConfig {
|
|
|
1148
1158
|
/** The bot login's single refresher (`src/loginrefresh/`). Optional
|
|
1149
1159
|
* in the file; off unless the cutover's config turns it on. */
|
|
1150
1160
|
login_refresh: LoginRefreshConfig;
|
|
1161
|
+
/** The home, the app's own pi a bare `qcr` opens (src/home/launch.ts).
|
|
1162
|
+
* Optional in the file and here, so hand-rolled configs keep compiling;
|
|
1163
|
+
* the parser fills every key. */
|
|
1164
|
+
home?: HomeConfig;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/** Which tools the home's model may call: `reviewer` is the plugin's tools,
|
|
1168
|
+
* the home's own `home_*` tools and pi's read tools; `full` is all of pi's. */
|
|
1169
|
+
export type HomeToolSet = "reviewer" | "full";
|
|
1170
|
+
|
|
1171
|
+
/** `[home]`: the home a bare `qcr` opens (src/home/launch.ts reads it). */
|
|
1172
|
+
export interface HomeConfig {
|
|
1173
|
+
/** The chat's model, `provider/id`. The panel's login line counts only
|
|
1174
|
+
* this model's provider as signed in. */
|
|
1175
|
+
model: string;
|
|
1176
|
+
tools: HomeToolSet;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
export const HOME_DEFAULTS: Readonly<HomeConfig> = {
|
|
1180
|
+
model: "openai-codex/gpt-6.1-sol",
|
|
1181
|
+
tools: "reviewer",
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
/** `[home]`, every key defaulted; a value of the wrong kind is refused. */
|
|
1185
|
+
export function parseHomeSection(raw: unknown): HomeConfig {
|
|
1186
|
+
const t = (raw ?? {}) as Record<string, unknown>;
|
|
1187
|
+
const model = t["model"] ?? HOME_DEFAULTS.model;
|
|
1188
|
+
if (typeof model !== "string" || !/^[^/\s]+\/\S+$/.test(model))
|
|
1189
|
+
throw new Error(
|
|
1190
|
+
`config.toml [home]: model must be "provider/id" (got ${JSON.stringify(model)})`,
|
|
1191
|
+
);
|
|
1192
|
+
const tools = t["tools"] ?? HOME_DEFAULTS.tools;
|
|
1193
|
+
if (tools !== "reviewer" && tools !== "full")
|
|
1194
|
+
throw new Error(
|
|
1195
|
+
`config.toml [home]: tools must be "reviewer" or "full" (got ${JSON.stringify(tools)})`,
|
|
1196
|
+
);
|
|
1197
|
+
return { model, tools };
|
|
1151
1198
|
}
|
|
1152
1199
|
|
|
1153
1200
|
/**
|
|
@@ -1729,9 +1776,19 @@ export function parseConfig(toml: string): Config {
|
|
|
1729
1776
|
...INGRESS_DEFAULTS,
|
|
1730
1777
|
...((raw.ingress ?? {}) as Partial<IngressConfig>),
|
|
1731
1778
|
};
|
|
1732
|
-
if (!["none", "tunnel-file"].includes(ingress.shape)) {
|
|
1779
|
+
if (!["none", "tunnel-file", "polling"].includes(ingress.shape)) {
|
|
1780
|
+
throw new Error(
|
|
1781
|
+
`config.toml [ingress]: shape must be "none", "tunnel-file" or "polling" (got ${JSON.stringify(ingress.shape)})`,
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
// The person's own credential has no webhook: GitHub delivers webhooks to
|
|
1785
|
+
// an App, and the person has none. So that mode is polling-only, and any
|
|
1786
|
+
// other shape is refused here rather than left to run half-wired.
|
|
1787
|
+
if (github.auth === "user" && ingress.shape !== "polling") {
|
|
1733
1788
|
throw new Error(
|
|
1734
|
-
`config.toml [
|
|
1789
|
+
`config.toml: [github] auth = "user" runs polling-only — set [ingress] shape = "polling" ` +
|
|
1790
|
+
`(got ${JSON.stringify(ingress.shape)}${raw.ingress === undefined ? ", the default for an absent [ingress]" : ""}). ` +
|
|
1791
|
+
`Webhooks and the webhook self-heal need a GitHub App: use auth = "app" for them.`,
|
|
1735
1792
|
);
|
|
1736
1793
|
}
|
|
1737
1794
|
const cfg: Config = {
|
|
@@ -1802,6 +1859,7 @@ export function parseConfig(toml: string): Config {
|
|
|
1802
1859
|
(raw.login_refresh ?? {}) as Record<string, unknown>,
|
|
1803
1860
|
Number((req(raw, "daemon") as { round_deadline_min?: number }).round_deadline_min ?? 75),
|
|
1804
1861
|
),
|
|
1862
|
+
home: parseHomeSection(raw.home),
|
|
1805
1863
|
};
|
|
1806
1864
|
validateReviewSection(cfg);
|
|
1807
1865
|
foldLegacyOwnerSlackId(raw, cfg.owner);
|
package/src/daemon.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
DEFAULT_POSTING_RETRY_WINDOW_MIN,
|
|
19
19
|
loadConfig,
|
|
20
20
|
sandboxRoot,
|
|
21
|
+
webhooksEnabled,
|
|
21
22
|
type Config,
|
|
22
23
|
} from "./config.js";
|
|
23
24
|
import { dispatchOnce, type DispatchAdapter, type PreparedTree } from "./dispatch.js";
|
|
@@ -30,7 +31,13 @@ import {
|
|
|
30
31
|
} from "./eventloop.js";
|
|
31
32
|
import { MutationGate, type MutationIntent } from "@quickchatai/pi-quickreview/src/gate.js";
|
|
32
33
|
import { GitHubAdapter } from "./github.js";
|
|
33
|
-
import {
|
|
34
|
+
import { githubCredential } from "@quickchatai/pi-quickreview/src/githubAuth.js";
|
|
35
|
+
import {
|
|
36
|
+
botAccountLogin,
|
|
37
|
+
isPersonMode,
|
|
38
|
+
ownActorAlias,
|
|
39
|
+
ownLogins,
|
|
40
|
+
} from "@quickchatai/pi-quickreview/src/identity.js";
|
|
34
41
|
import { REVIEW_SET_ASIDE_STALE_WINDOW_MS, type FeedbackEvent } from "./feedback.js";
|
|
35
42
|
import { enforceLedgerFreshness } from "./ledgercheck.js";
|
|
36
43
|
import { readLedgerCarry } from "@quickchatai/pi-quickreview/src/ledgercarry.js";
|
|
@@ -57,7 +64,13 @@ import {
|
|
|
57
64
|
} from "./recheck.js";
|
|
58
65
|
import type { DispatchAnnouncer, VerdictAnnouncer } from "./slack/announce.js";
|
|
59
66
|
import { applyEvent } from "./statemachine.js";
|
|
60
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
buildPipelineStatus,
|
|
69
|
+
buildStatusSnapshot,
|
|
70
|
+
StateStore,
|
|
71
|
+
writeStatusJson,
|
|
72
|
+
type OpenPrsSnapshot,
|
|
73
|
+
} from "./state.js";
|
|
61
74
|
import { PiAuthWatch } from "@quickchatai/pi-quickreview/src/authwatch.js";
|
|
62
75
|
import { SessionCommandSupervisor } from "./session-command-supervisor.js";
|
|
63
76
|
import { readRoundProgress } from "./requiredpasses.js";
|
|
@@ -313,13 +326,18 @@ export interface Daemon {
|
|
|
313
326
|
firstHealthyReconcileAt(): Date | null;
|
|
314
327
|
}
|
|
315
328
|
|
|
329
|
+
/** The webhook HMAC secret — or "" under the polling shape, which takes no
|
|
330
|
+
* webhooks and needs no secret (the `/webhook` route then refuses every
|
|
331
|
+
* delivery, webhook.ts). `[github] auth = "user"` is always polling. */
|
|
316
332
|
function readWebhookSecret(cfg: Config): string {
|
|
333
|
+
if (!webhooksEnabled(cfg)) return "";
|
|
317
334
|
return readFileSync(join(cfg.github.secrets_dir, "webhook_secret"), "utf8").trim();
|
|
318
335
|
}
|
|
319
336
|
|
|
320
|
-
/** The real adapter:
|
|
337
|
+
/** The real adapter: credential (`[github] auth`) → mutation gate (dry-run
|
|
338
|
+
* aware) → adapter. */
|
|
321
339
|
function buildGitHubAdapter(cfg: Config, stateDir: string): GitHubAdapter {
|
|
322
|
-
const minter =
|
|
340
|
+
const minter = githubCredential(cfg.github);
|
|
323
341
|
const gate: MutationGate = new MutationGate(
|
|
324
342
|
cfg.dry_run,
|
|
325
343
|
join(stateDir, "dryrun"),
|
|
@@ -496,7 +514,8 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
496
514
|
dir: join(stateDir, "posting-receipts"),
|
|
497
515
|
repo: cfg.github.repo,
|
|
498
516
|
dryRun: cfg.dry_run,
|
|
499
|
-
botLogins:
|
|
517
|
+
botLogins: ownLogins(cfg.github),
|
|
518
|
+
personMode: isPersonMode(cfg.github),
|
|
500
519
|
adapter: deps.postingDelivery,
|
|
501
520
|
current: (job) => {
|
|
502
521
|
const pr = store.getPr(job.pr);
|
|
@@ -587,7 +606,7 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
587
606
|
log,
|
|
588
607
|
isDraining: () => store.drainState().draining,
|
|
589
608
|
botLogin: cfg.github.bot_login,
|
|
590
|
-
ownerActorAlias:
|
|
609
|
+
ownerActorAlias: ownActorAlias(cfg.github),
|
|
591
610
|
// The author asked for a person on a review thread.
|
|
592
611
|
//
|
|
593
612
|
// There is no park to put them in: the state event is a deliberate no-op
|
|
@@ -606,7 +625,7 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
606
625
|
{ type: "human_requested" },
|
|
607
626
|
{},
|
|
608
627
|
{
|
|
609
|
-
botLogin: cfg.github
|
|
628
|
+
botLogin: botAccountLogin(cfg.github),
|
|
610
629
|
reason: "pingpong:human-requested",
|
|
611
630
|
},
|
|
612
631
|
);
|
|
@@ -806,10 +825,15 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
806
825
|
const outcomes = new OutcomeCapture({
|
|
807
826
|
store,
|
|
808
827
|
botLogin: cfg.github.bot_login,
|
|
828
|
+
personMode: isPersonMode(cfg.github),
|
|
809
829
|
...(deps.outcomeGit ? { git: deps.outcomeGit } : {}),
|
|
810
830
|
...(deps.now ? { now: deps.now } : {}),
|
|
811
831
|
});
|
|
812
832
|
|
|
833
|
+
/** The open PRs the last listing reconcile pass saw (status.json `open_prs`). */
|
|
834
|
+
let openPrs: OpenPrsSnapshot | undefined;
|
|
835
|
+
/** The last reconcile pass (status.json `poll`): null until one has ended. */
|
|
836
|
+
let lastPoll: { at: string; ok: boolean; error?: string } | null = null;
|
|
813
837
|
let firstReconcileSeen = false;
|
|
814
838
|
let firstHealthyReconcileAt: Date | null = null;
|
|
815
839
|
// Deploy-window drain auto-lift: only the boot's first reconcile pass may
|
|
@@ -866,6 +890,7 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
866
890
|
const bootFirstPass = !firstReconcileSeen;
|
|
867
891
|
firstReconcileSeen = true;
|
|
868
892
|
let completed = false;
|
|
893
|
+
let thrown: unknown = undefined;
|
|
869
894
|
await guard("reconcile", async () => {
|
|
870
895
|
const report = await reconcileOnce({
|
|
871
896
|
cfg,
|
|
@@ -892,7 +917,17 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
892
917
|
// sweep reads session transcripts and is not needed for that, so it
|
|
893
918
|
// starts on the next pass to keep the boot pass short.
|
|
894
919
|
receiptSweep: !bootFirstPass,
|
|
920
|
+
}).catch((err: unknown) => {
|
|
921
|
+
thrown = err;
|
|
922
|
+
throw err;
|
|
895
923
|
});
|
|
924
|
+
lastPoll = {
|
|
925
|
+
at: report.ran_at,
|
|
926
|
+
ok: report.errors.length === 0,
|
|
927
|
+
...(report.errors[0] !== undefined ? { error: report.errors[0] } : {}),
|
|
928
|
+
};
|
|
929
|
+
// The board's "review a PR" list: kept from the last pass that listed.
|
|
930
|
+
if (report.open_list) openPrs = { listed_at: report.ran_at, prs: report.open_list };
|
|
896
931
|
log(
|
|
897
932
|
`reconcile: open=${report.open_prs} triggered=${report.triggered.length} ` +
|
|
898
933
|
`closed=${report.closed.length} captured=${report.captured_comments} ` +
|
|
@@ -933,6 +968,15 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
933
968
|
}
|
|
934
969
|
completed = true;
|
|
935
970
|
});
|
|
971
|
+
if (thrown !== undefined)
|
|
972
|
+
lastPoll = {
|
|
973
|
+
at: (deps.now ? deps.now() : new Date()).toISOString(),
|
|
974
|
+
ok: false,
|
|
975
|
+
error: `the reconcile pass failed: ${thrown instanceof Error ? thrown.message : JSON.stringify(thrown)}`,
|
|
976
|
+
};
|
|
977
|
+
// The poll's outcome reaches status.json now, not at the next heartbeat:
|
|
978
|
+
// "working" is judged on it.
|
|
979
|
+
writeStatus();
|
|
936
980
|
return completed;
|
|
937
981
|
};
|
|
938
982
|
|
|
@@ -1097,6 +1141,13 @@ export function createDaemon(deps: DaemonDeps = {}): Daemon {
|
|
|
1097
1141
|
...(job.deferred_at ? { deferred_since: job.deferred_at } : {}),
|
|
1098
1142
|
};
|
|
1099
1143
|
}
|
|
1144
|
+
if (openPrs) snapshot.open_prs = openPrs;
|
|
1145
|
+
snapshot.poll = {
|
|
1146
|
+
last_at: lastPoll?.at ?? null,
|
|
1147
|
+
ok: lastPoll?.ok ?? null,
|
|
1148
|
+
...(lastPoll?.error !== undefined ? { error: lastPoll.error } : {}),
|
|
1149
|
+
interval_s: cfg.daemon.poll_interval_s,
|
|
1150
|
+
};
|
|
1100
1151
|
if (deps.runHost) snapshot.run_host = deps.runHost;
|
|
1101
1152
|
writeStatusJson(stateDir, snapshot);
|
|
1102
1153
|
} catch (err) {
|
package/src/debuground.ts
CHANGED
|
@@ -123,6 +123,8 @@ export interface DebugRoundSpec {
|
|
|
123
123
|
cwd: string;
|
|
124
124
|
/** The login's owner dir: read-only, borrowed from. */
|
|
125
125
|
agent_dir: string;
|
|
126
|
+
/** The login's dir when it is not `agent_dir` (the worker spec's). */
|
|
127
|
+
login_dir?: string;
|
|
126
128
|
/** Where the pi's own agent dir (the blanked login copy) is made. */
|
|
127
129
|
borrow_dir: string;
|
|
128
130
|
/** The window the borrowed login must outlast. */
|
|
@@ -603,6 +605,7 @@ export class DebugRoundManager {
|
|
|
603
605
|
root: spec.root,
|
|
604
606
|
cwd: spec.cwd,
|
|
605
607
|
agent_dir: spec.agent_dir,
|
|
608
|
+
...(spec.login_dir !== undefined ? { login_dir: spec.login_dir } : {}),
|
|
606
609
|
borrow_dir: borrow,
|
|
607
610
|
idle_ms: this.idleMs,
|
|
608
611
|
...(file !== undefined ? { session_file: file } : {}),
|