@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/loginrefresh/lock.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// The single-instance lock: an exclusive flock on a SEPARATE lock file
|
|
2
|
-
// (`pihome/agent/
|
|
2
|
+
// (`pihome/agent/loginrefresh.lock`), held for the refresher's whole life.
|
|
3
|
+
//
|
|
4
|
+
// Why not `auth.json.lock`: that path is pi's. pi guards auth.json with
|
|
5
|
+
// proper-lockfile, whose lock is a DIRECTORY `<file>.lock` (mkdir; a stale one
|
|
6
|
+
// is rmdir'd). A plain file at that path makes every pi CLI fail at startup
|
|
7
|
+
// (`ENOTDIR: not a directory, rmdir '…/auth.json.lock'`), so this lock is named
|
|
8
|
+
// for this process and never `<anything pi locks>.lock`. Earlier builds did use
|
|
9
|
+
// `auth.json.lock`; `removeLegacyLockFile` clears what they left behind.
|
|
3
10
|
//
|
|
4
11
|
// Why not a lock on auth.json: every refresh replaces auth.json by rename
|
|
5
12
|
// (atomic.ts), so a lock on it stays on the old inode, a second refresher could
|
|
@@ -12,8 +19,17 @@
|
|
|
12
19
|
// lock lasts exactly as long as the pipe's write end does: release() closes it,
|
|
13
20
|
// and a refresher that is killed in any way closes it too (the kernel closes its
|
|
14
21
|
// descriptors), so a crash can never leave the lock held by an orphan.
|
|
15
|
-
import { spawn } from "node:child_process";
|
|
16
|
-
import {
|
|
22
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
23
|
+
import {
|
|
24
|
+
chmodSync,
|
|
25
|
+
chownSync,
|
|
26
|
+
closeSync,
|
|
27
|
+
existsSync,
|
|
28
|
+
lstatSync,
|
|
29
|
+
mkdirSync,
|
|
30
|
+
openSync,
|
|
31
|
+
unlinkSync,
|
|
32
|
+
} from "node:fs";
|
|
17
33
|
import { dirname } from "node:path";
|
|
18
34
|
|
|
19
35
|
/** flock's exit status when the lock is held by someone else (-E). */
|
|
@@ -102,3 +118,56 @@ export function acquireLoginLock(
|
|
|
102
118
|
});
|
|
103
119
|
});
|
|
104
120
|
}
|
|
121
|
+
|
|
122
|
+
/** What `removeLegacyLockFile` found at the old lock path. */
|
|
123
|
+
export type LegacyLockOutcome = "absent" | "not-a-file" | "removed" | "unremovable";
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Remove the plain lock FILE an earlier refresher left at `path` (the old
|
|
127
|
+
* `auth.json.lock`, which is pi's proper-lockfile path and breaks every pi CLI
|
|
128
|
+
* while it is a file). Only a regular file is removed: a directory there is
|
|
129
|
+
* pi's own live lock and a symlink is not ours, so both are left alone. Throws
|
|
130
|
+
* `LoginLockHeld` when a refresher of an earlier build still holds a flock on
|
|
131
|
+
* it (the two would otherwise run side by side on different lock files).
|
|
132
|
+
*/
|
|
133
|
+
export function removeLegacyLockFile(
|
|
134
|
+
path: string,
|
|
135
|
+
o: { flockBin?: string } = {},
|
|
136
|
+
): LegacyLockOutcome {
|
|
137
|
+
let isFile: boolean;
|
|
138
|
+
try {
|
|
139
|
+
isFile = lstatSync(path).isFile();
|
|
140
|
+
} catch {
|
|
141
|
+
return "absent";
|
|
142
|
+
}
|
|
143
|
+
if (!isFile) return "not-a-file";
|
|
144
|
+
const probe = spawnSync(
|
|
145
|
+
o.flockBin ?? "flock",
|
|
146
|
+
["-n", "-E", String(LOCK_HELD_EXIT), path, "true"],
|
|
147
|
+
{ stdio: "ignore" },
|
|
148
|
+
);
|
|
149
|
+
if (probe.status === LOCK_HELD_EXIT) throw new LoginLockHeld(path);
|
|
150
|
+
try {
|
|
151
|
+
unlinkSync(path);
|
|
152
|
+
return "removed";
|
|
153
|
+
} catch (err) {
|
|
154
|
+
return (err as NodeJS.ErrnoException).code === "ENOENT" ? "absent" : "unremovable";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Does a refresher hold the lock at `path` now? true or false; null when it
|
|
160
|
+
* cannot be told (no flock on this machine, or flock failed). A probe, never a
|
|
161
|
+
* holder: no lock file is created where there is none (none: nobody holds it).
|
|
162
|
+
*/
|
|
163
|
+
export function loginLockHeld(path: string, o: { flockBin?: string } = {}): boolean | null {
|
|
164
|
+
if (!existsSync(path)) return false;
|
|
165
|
+
const probe = spawnSync(
|
|
166
|
+
o.flockBin ?? "flock",
|
|
167
|
+
["-n", "-E", String(LOCK_HELD_EXIT), path, "true"],
|
|
168
|
+
{ stdio: "ignore" },
|
|
169
|
+
);
|
|
170
|
+
if (probe.error) return null;
|
|
171
|
+
if (probe.status === LOCK_HELD_EXIT) return true;
|
|
172
|
+
return probe.status === 0 ? false : null;
|
|
173
|
+
}
|
|
@@ -20,6 +20,9 @@ const PI_AI = "@earendil-works/pi-ai";
|
|
|
20
20
|
export const OAUTH_LOADERS: Readonly<Record<string, string>> = {
|
|
21
21
|
anthropic: "loadAnthropicOAuth",
|
|
22
22
|
"openai-codex": "loadOpenAICodexOAuth",
|
|
23
|
+
// pi 1.4's "Sign in with ChatGPT" (the provider `openai`, type oauth): a
|
|
24
|
+
// newer OpenAI login is refreshed by the same owner.
|
|
25
|
+
openai: "loadOpenAIChatGPTOAuth",
|
|
23
26
|
"github-copilot": "loadGitHubCopilotOAuth",
|
|
24
27
|
openrouter: "loadOpenRouterOAuth",
|
|
25
28
|
"kimi-coding": "loadKimiCodingOAuth",
|
|
@@ -6,8 +6,18 @@
|
|
|
6
6
|
// (main.ts) the daemon spawns and supervises by path, so the daemon imports
|
|
7
7
|
// neither pi nor src/engine/.
|
|
8
8
|
//
|
|
9
|
-
// Lock an exclusive flock on `
|
|
10
|
-
// a second refresher is refused, before and after any refresh.
|
|
9
|
+
// Lock an exclusive flock on `loginrefresh.lock` for its whole life (lock.ts);
|
|
10
|
+
// a second refresher is refused, before and after any refresh. Never
|
|
11
|
+
// `auth.json.lock`: that is pi's own proper-lockfile path (a
|
|
12
|
+
// directory), and a file there breaks every pi CLI. A plain file an
|
|
13
|
+
// earlier build left there is removed at start.
|
|
14
|
+
// pi lock not taken: the refresher does not hold pi's proper-lockfile lock
|
|
15
|
+
// around its read-refresh-write (proper-lockfile is pi's dependency,
|
|
16
|
+
// not ours). It needs none while it is healthy: it refreshes far
|
|
17
|
+
// ahead of pi's own window, so a pi on this file has nothing to
|
|
18
|
+
// refresh, and a login goes through the entrypoint's pause below. A
|
|
19
|
+
// raw `pi` + `/login` that bypasses the pause can lose its new
|
|
20
|
+
// login to a refresh that lands in the same moment.
|
|
11
21
|
// Refresh every 10 minutes, any OAuth entry that is inside pi's refresh
|
|
12
22
|
// window plus a margin longer than the longest worker cap is
|
|
13
23
|
// refreshed through `deps.refresh` (pi-ai's own, pirefresh.ts) and
|
|
@@ -26,11 +36,14 @@
|
|
|
26
36
|
import { existsSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
27
37
|
import { join } from "node:path";
|
|
28
38
|
import { writeFileAtomic } from "./atomic.js";
|
|
29
|
-
import { acquireLoginLock, type LoginLock } from "./lock.js";
|
|
39
|
+
import { acquireLoginLock, removeLegacyLockFile, type LoginLock } from "./lock.js";
|
|
30
40
|
import type { LoginRefreshFileStatus } from "./types.js";
|
|
31
41
|
|
|
32
42
|
export const AUTH_FILE = "auth.json";
|
|
33
|
-
|
|
43
|
+
/** The single-instance lock. Not `${AUTH_FILE}.lock`: that is pi's (lock.ts). */
|
|
44
|
+
export const LOCK_FILE = "loginrefresh.lock";
|
|
45
|
+
/** Where earlier builds kept the lock: pi's proper-lockfile path, cleared at start. */
|
|
46
|
+
export const LEGACY_LOCK_FILE = "auth.json.lock";
|
|
34
47
|
export const PAUSE_FILE = "auth.json.pause";
|
|
35
48
|
export const STATUS_FILE = "loginrefresh.status.json";
|
|
36
49
|
|
|
@@ -134,10 +147,26 @@ export class LoginRefresher {
|
|
|
134
147
|
* keep looking.
|
|
135
148
|
*/
|
|
136
149
|
static async start(deps: RefresherDeps, o: { autoRun?: boolean } = {}): Promise<LoginRefresher> {
|
|
150
|
+
const flock = deps.flockBin ? { flockBin: deps.flockBin } : {};
|
|
137
151
|
const lock = await acquireLoginLock(join(deps.agentDir, LOCK_FILE), {
|
|
138
|
-
...
|
|
152
|
+
...flock,
|
|
139
153
|
gid: deps.gid,
|
|
140
154
|
});
|
|
155
|
+
const legacy = join(deps.agentDir, LEGACY_LOCK_FILE);
|
|
156
|
+
let outcome: ReturnType<typeof removeLegacyLockFile>;
|
|
157
|
+
try {
|
|
158
|
+
outcome = removeLegacyLockFile(legacy, flock);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
// a refresher of an earlier build still runs on the old lock
|
|
161
|
+
await lock.release();
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
if (outcome === "removed")
|
|
165
|
+
deps.log(`login refresh: removed the old lock file ${legacy} (it is pi's lock path)`);
|
|
166
|
+
if (outcome === "unremovable")
|
|
167
|
+
deps.log(
|
|
168
|
+
`login refresh: could not remove the old lock file ${legacy}; pi fails until it goes`,
|
|
169
|
+
);
|
|
141
170
|
const r = new LoginRefresher(deps, lock);
|
|
142
171
|
r.publish();
|
|
143
172
|
await r.tick();
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// second refresher would rotate the refresh token under them.
|
|
22
22
|
import { existsSync, readFileSync } from "node:fs";
|
|
23
23
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
24
|
-
import { join } from "node:path";
|
|
24
|
+
import { isAbsolute, join } from "node:path";
|
|
25
25
|
import {
|
|
26
26
|
foldChildLines,
|
|
27
27
|
restartDelayMs,
|
|
@@ -33,6 +33,28 @@ import {
|
|
|
33
33
|
import { LOCK_HELD_EXIT } from "./lock.js";
|
|
34
34
|
import type { LoginRefreshFileStatus, LoginRefreshStatus } from "./types.js";
|
|
35
35
|
|
|
36
|
+
/** Names the login's dir when it is not pi's agent dir. The single box's
|
|
37
|
+
* shared login (one login per box): the home's switch-on mounts the
|
|
38
|
+
* host pi home's agent dir at CONTAINER_LOGIN_DIR (docker-compose.yml,
|
|
39
|
+
* QCR_PI_LOGIN_DIR in the project .env) and the compose file names it here.
|
|
40
|
+
* Only the login moves: the refresher keeps auth.json there and every worker
|
|
41
|
+
* borrows from there, while settings, models.json and the lens agents stay in
|
|
42
|
+
* the container's own agent dir, whose settings the entrypoint provisions and
|
|
43
|
+
* which must never be the person's own. */
|
|
44
|
+
export const LOGIN_DIR_ENV = "QCR_LOGIN_DIR";
|
|
45
|
+
/** Where docker-compose.yml mounts the host's login. */
|
|
46
|
+
export const CONTAINER_LOGIN_DIR = "/opt/qcr/login";
|
|
47
|
+
|
|
48
|
+
/** The login's dir: `QCR_LOGIN_DIR` when it is an absolute path, else pi's
|
|
49
|
+
* agent dir (the login volume, as before). */
|
|
50
|
+
export function loginDirOf(
|
|
51
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
52
|
+
agentDir: string,
|
|
53
|
+
): string {
|
|
54
|
+
const set = (env[LOGIN_DIR_ENV] ?? "").trim();
|
|
55
|
+
return set !== "" && isAbsolute(set) ? set : agentDir;
|
|
56
|
+
}
|
|
57
|
+
|
|
36
58
|
/** The restart ladder, ms: a refresher that cannot start (lock held, config) is
|
|
37
59
|
* retried slowly; one that crashed after a good run is retried at once. */
|
|
38
60
|
export const LOGIN_RESTART_LADDER_MS = [2_000, 10_000, 60_000];
|
|
@@ -44,7 +66,8 @@ export const LOGIN_STATUS_POLL_MS = 5_000;
|
|
|
44
66
|
export interface LoginRefreshSupervisorDeps {
|
|
45
67
|
/** The checkout root: where `dist/src/loginrefresh/main.js` lives. */
|
|
46
68
|
repoRoot: string;
|
|
47
|
-
/** pi's agent dir (the login volume's `agent/`)
|
|
69
|
+
/** The login's dir: pi's agent dir (the login volume's `agent/`), or the
|
|
70
|
+
* single box's shared login (`loginDirOf`). */
|
|
48
71
|
agentDir: string;
|
|
49
72
|
marginMin: number;
|
|
50
73
|
/** The login group, when the daemon knows it. */
|
package/src/npmverbs.ts
CHANGED
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
// qcr login [provider] pi's own /login, in the pinned pi (src/picli.ts),
|
|
6
6
|
// into the user's pi agent dir. pi's UI is the flow:
|
|
7
7
|
// qcr types /login for the person and steps aside.
|
|
8
|
+
// When auto-review's refresher holds that login, it
|
|
9
|
+
// is paused first and released after, as the home's
|
|
10
|
+
// re-login does (src/loginrefresh/handover.ts).
|
|
8
11
|
// qcr pi [args] the pinned pi with the qcr plugin preloaded
|
|
9
|
-
// (`-e <the plugin package's dir>`)
|
|
12
|
+
// (`-e <the plugin package's dir>`) and the app's
|
|
13
|
+
// own /selfimprove beside it (`-e` its shim,
|
|
14
|
+
// `APP_PI_EXTENSIONS`), args passed on.
|
|
10
15
|
// qcr update the package manager that installed qcr (npm, pnpm or
|
|
11
16
|
// bun) updates it globally. The release image keeps its
|
|
12
17
|
// own route (a release), and a checkout its git.
|
|
@@ -15,11 +20,12 @@
|
|
|
15
20
|
// `npmVerbDeps()` is the real assembly.
|
|
16
21
|
import { spawnSync, type SpawnSyncOptions } from "node:child_process";
|
|
17
22
|
import { existsSync, realpathSync } from "node:fs";
|
|
18
|
-
import { homedir } from "node:os";
|
|
19
23
|
import { delimiter, join } from "node:path";
|
|
20
24
|
import { pluginPackageDir } from "./coreext.js";
|
|
21
25
|
import { detectInstall, type Installer } from "./installkind.js";
|
|
26
|
+
import type { RefresherHold } from "./loginrefresh/handover.js";
|
|
22
27
|
import { readLogins } from "./loginhint.js";
|
|
28
|
+
import { piAgentDir } from "./piagentdir.js";
|
|
23
29
|
import { piCliPath } from "./picli.js";
|
|
24
30
|
|
|
25
31
|
/** The npm package this CLI is published as. */
|
|
@@ -44,12 +50,9 @@ export function updateCommand(installer: Installer, pkg: string = CLI_PACKAGE):
|
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (set !== "") return set;
|
|
51
|
-
return join((env["HOME"] ?? "").trim() || homedir(), ".pi", "agent");
|
|
52
|
-
}
|
|
53
|
+
// pi's agent dir lives in a leaf of its own, so the home reads the same rule
|
|
54
|
+
// without these verbs.
|
|
55
|
+
export { piAgentDir };
|
|
53
56
|
|
|
54
57
|
/** The providers pi holds a credential for in `agentDir`, from its auth.json;
|
|
55
58
|
* empty when there is none or it cannot be read. Never a token. */
|
|
@@ -86,6 +89,9 @@ export interface NpmVerbDeps {
|
|
|
86
89
|
err: (line: string) => void;
|
|
87
90
|
/** `node`: the pinned pi's entry is a script, run by this node. */
|
|
88
91
|
execPath: string;
|
|
92
|
+
/** Pause auto-review's login refresher of `agentDir` for a login, when one
|
|
93
|
+
* runs (src/loginrefresh/handover.ts `holdRefresher`). */
|
|
94
|
+
holdLogin: (agentDir: string) => Promise<RefresherHold>;
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
export function npmVerbDeps(pkgRoot: string): NpmVerbDeps {
|
|
@@ -103,10 +109,20 @@ export function npmVerbDeps(pkgRoot: string): NpmVerbDeps {
|
|
|
103
109
|
out: (l) => console.log(l),
|
|
104
110
|
err: (l) => console.error(l),
|
|
105
111
|
execPath: process.execPath,
|
|
112
|
+
holdLogin: async (agentDir) => {
|
|
113
|
+
const { holdRefresher } = await import("./loginrefresh/handover.js");
|
|
114
|
+
return holdRefresher(agentDir);
|
|
115
|
+
},
|
|
106
116
|
};
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
/**
|
|
119
|
+
/** The app's own extensions `qcr pi` loads beside the plugin, as paths under
|
|
120
|
+
* the CLI package's root: the commands that are the app's, not the plugin's
|
|
121
|
+
* (ADR 0001). */
|
|
122
|
+
export const APP_PI_EXTENSIONS: readonly string[] = ["extensions/cli/selfimprove.ts"];
|
|
123
|
+
|
|
124
|
+
/** `qcr pi [args]`: the pinned pi with the plugin and the app's own
|
|
125
|
+
* extensions preloaded; pi's exit code. */
|
|
110
126
|
export function cmdPi(args: readonly string[], deps: NpmVerbDeps): number {
|
|
111
127
|
let pi: string;
|
|
112
128
|
let plugin: string;
|
|
@@ -117,7 +133,8 @@ export function cmdPi(args: readonly string[], deps: NpmVerbDeps): number {
|
|
|
117
133
|
deps.err(`qcr pi: ${err instanceof Error ? err.message : String(err)}`);
|
|
118
134
|
return 1;
|
|
119
135
|
}
|
|
120
|
-
const
|
|
136
|
+
const app = APP_PI_EXTENSIONS.flatMap((e) => ["-e", join(deps.pkgRoot, e)]);
|
|
137
|
+
const r = deps.run(deps.execPath, [pi, "-e", plugin, ...app, ...args], {});
|
|
121
138
|
if (r.error) {
|
|
122
139
|
deps.err(`qcr pi: pi could not start: ${r.error.message}`);
|
|
123
140
|
return 1;
|
|
@@ -144,8 +161,10 @@ export async function cmdLogin(args: readonly string[], deps: NpmVerbDeps): Prom
|
|
|
144
161
|
"",
|
|
145
162
|
"Opens pi's own /login in the pi qcr runs on, so the review sessions can use it.",
|
|
146
163
|
"The login is kept in pi's agent dir ($PI_CODING_AGENT_DIR, default ~/.pi/agent).",
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
'Any OpenAI login serves the default review models: "OpenAI Codex (legacy)" or "OpenAI"',
|
|
165
|
+
'(Sign in with ChatGPT) under "Sign in with an account", or an OpenAI API key (at /login, or',
|
|
166
|
+
"OPENAI_API_KEY in the environment). A key for another provider does not stand in for it:",
|
|
167
|
+
"log in, or set the review's models in your qcr config.",
|
|
149
168
|
].join("\n"),
|
|
150
169
|
);
|
|
151
170
|
return 0;
|
|
@@ -167,6 +186,27 @@ export async function cmdLogin(args: readonly string[], deps: NpmVerbDeps): Prom
|
|
|
167
186
|
return 1;
|
|
168
187
|
}
|
|
169
188
|
const agentDir = piAgentDir(deps.env);
|
|
189
|
+
// The refresher first, when one runs: a refresh that read the old grant and
|
|
190
|
+
// lands after the login would write it back over the new one.
|
|
191
|
+
const hold = await deps.holdLogin(agentDir);
|
|
192
|
+
if (!hold.ok) {
|
|
193
|
+
deps.err(`qcr login: ${hold.why}; nothing was changed.`);
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
if (hold.held) deps.out("qcr login: auto-review's login refresher is paused while you log in.");
|
|
197
|
+
try {
|
|
198
|
+
return await runLogin(provider, pi, agentDir, deps);
|
|
199
|
+
} finally {
|
|
200
|
+
hold.release();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function runLogin(
|
|
205
|
+
provider: string | undefined,
|
|
206
|
+
pi: string,
|
|
207
|
+
agentDir: string,
|
|
208
|
+
deps: NpmVerbDeps,
|
|
209
|
+
): Promise<number> {
|
|
170
210
|
const before = loggedInProviders(agentDir);
|
|
171
211
|
let status: number | null;
|
|
172
212
|
if (onPath("tmux", deps.env["PATH"])) {
|
|
@@ -194,7 +234,9 @@ export async function cmdLogin(args: readonly string[], deps: NpmVerbDeps): Prom
|
|
|
194
234
|
deps.out(
|
|
195
235
|
`qcr login: opening pi — type /login${provider ? ` ${provider}` : ""} and pick your provider, then quit pi (the login lands in ${agentDir}).`,
|
|
196
236
|
);
|
|
197
|
-
|
|
237
|
+
// --no-approve as on the tmux route (src/pilogin.ts): a login needs
|
|
238
|
+
// nothing from this folder, so pi neither asks to trust it nor trusts it.
|
|
239
|
+
const r = deps.run(deps.execPath, [pi, "--no-approve"], {});
|
|
198
240
|
if (r.error) {
|
|
199
241
|
deps.err(`qcr login: pi could not start: ${r.error.message}`);
|
|
200
242
|
return 1;
|
package/src/onboard/choices.ts
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
// record each key, and the test proves each named step really does — so a
|
|
17
17
|
// key that only ever gets read cannot survive review.
|
|
18
18
|
//
|
|
19
|
-
// A key private to one step — the go-live decision, the
|
|
20
|
-
// vm-deploy record, the model prefix, the memory key-file path — stays a local
|
|
19
|
+
// A key private to one step — the go-live decision, the vm-deploy record, the model prefix, the memory key-file path — stays a local
|
|
21
20
|
// constant in that step. This file is about the handoffs.
|
|
22
21
|
//
|
|
23
22
|
// Nothing here imports a step (the steps import this), so there is no cycle and
|
|
@@ -41,6 +40,12 @@ export type IngressChoice = (typeof INGRESS_CHOICES)[number];
|
|
|
41
40
|
export const AUTH_ROUTES = ["oauth", "api"] as const;
|
|
42
41
|
export type AuthRoute = (typeof AUTH_ROUTES)[number];
|
|
43
42
|
|
|
43
|
+
/** How the daemon authenticates to GitHub (`config.toml [github] auth`):
|
|
44
|
+
* "user" is the person's own account (polling only, no App), "app" a GitHub
|
|
45
|
+
* App. Answered as the github_account step's input; absent ⇒ unanswered. */
|
|
46
|
+
export const GITHUB_AUTH_CHOICES = ["user", "app"] as const;
|
|
47
|
+
export type GithubAuth = (typeof GITHUB_AUTH_CHOICES)[number];
|
|
48
|
+
|
|
44
49
|
/** "owner/name" — the reviewed repo as the interview records it and the App
|
|
45
50
|
* step parses it: no URL, no trailing `.git`. */
|
|
46
51
|
export const REPO_SLUG_RE = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/;
|
|
@@ -59,6 +64,10 @@ export type MemoryChoice = (typeof MEMORY_CHOICE_IDS)[number];
|
|
|
59
64
|
export const CHOICE_KEYS = {
|
|
60
65
|
/** The operator's uid, captured by preflight: the containers run as it. */
|
|
61
66
|
uid: "qcr_uid",
|
|
67
|
+
/** When the bring-up step last brought the stack up (an ISO timestamp).
|
|
68
|
+
* Written by bringup; read by the login-share step, which recreates a
|
|
69
|
+
* stack brought up before so the daemon gets its login mount. */
|
|
70
|
+
broughtUpAt: "stack_brought_up_at",
|
|
62
71
|
|
|
63
72
|
/** `Topology`. Written by the topology step; read by everything that has to
|
|
64
73
|
* know where the stack runs (transport selection, the VM-only steps). */
|
|
@@ -71,6 +80,20 @@ export const CHOICE_KEYS = {
|
|
|
71
80
|
/** `AuthRoute`. Written by the LLM step; read by the interview's shape line. */
|
|
72
81
|
authRoute: "llm_auth_route",
|
|
73
82
|
|
|
83
|
+
/** `GithubAuth`. Written by the github_account step (the operator's answer,
|
|
84
|
+
* defaulted to "user" where `gh auth token` works); read by everything that
|
|
85
|
+
* differs between the own-account route and the App route — which of
|
|
86
|
+
* github_account / github_app / app_slug_reconcile is enabled, the
|
|
87
|
+
* interview's App questions, the ingress shapes on offer, verification. */
|
|
88
|
+
githubAuth: "github_auth",
|
|
89
|
+
/** The person's GitHub login, from `GET /user` with their own token (own-
|
|
90
|
+
* account route). The interview's owner-login answer and `[github]
|
|
91
|
+
* bot_login` both come from it. */
|
|
92
|
+
accountLogin: "github_account_login",
|
|
93
|
+
/** "owner/name" the person picked from the repos their account reaches
|
|
94
|
+
* (own-account route). The interview reads it as its repo answer. */
|
|
95
|
+
accountRepo: "github_account_repo",
|
|
96
|
+
|
|
74
97
|
/** "owner/name" of the reviewed repo. Written by the interview; read by the
|
|
75
98
|
* GitHub App step and the proof round. */
|
|
76
99
|
repo: "reviewed_repo",
|
|
@@ -144,6 +167,10 @@ export type ChoiceKey = (typeof CHOICE_KEYS)[ChoiceName];
|
|
|
144
167
|
*/
|
|
145
168
|
export const CHOICE_WRITERS: Record<ChoiceName, readonly string[]> = {
|
|
146
169
|
uid: ["preflight"],
|
|
170
|
+
broughtUpAt: ["bringup"],
|
|
171
|
+
githubAuth: ["github_account"],
|
|
172
|
+
accountLogin: ["github_account"],
|
|
173
|
+
accountRepo: ["github_account"],
|
|
147
174
|
topology: ["topology"],
|
|
148
175
|
sshAlias: ["topology"],
|
|
149
176
|
sshUser: ["ssh_preflight"],
|
|
@@ -158,7 +185,7 @@ export const CHOICE_WRITERS: Record<ChoiceName, readonly string[]> = {
|
|
|
158
185
|
dryRun: ["config_fill"],
|
|
159
186
|
labels: ["config_fill"],
|
|
160
187
|
appSlug: ["config_fill", "github_app", "app_slug_reconcile"],
|
|
161
|
-
botLogin: ["github_app", "app_slug_reconcile"],
|
|
188
|
+
botLogin: ["github_account", "github_app", "app_slug_reconcile"],
|
|
162
189
|
appId: ["github_app"],
|
|
163
190
|
appTarget: ["github_app"],
|
|
164
191
|
appName: ["github_app"],
|
|
@@ -187,7 +214,6 @@ export const CHOICE_WRITERS: Record<ChoiceName, readonly string[]> = {
|
|
|
187
214
|
export const PRIVATE_CHOICE_KEYS: Readonly<Record<string, string>> = {
|
|
188
215
|
llm_model_prefix: "llm_auth",
|
|
189
216
|
openviking_keys_file: "memory",
|
|
190
|
-
stack_brought_up_at: "bringup",
|
|
191
217
|
go_live_decision: "go_live",
|
|
192
218
|
};
|
|
193
219
|
|
|
@@ -216,6 +242,16 @@ export function authRouteOf(choices: Readonly<Record<string, unknown>>): AuthRou
|
|
|
216
242
|
return memberOf(AUTH_ROUTES, raw(choices, CHOICE_KEYS.authRoute));
|
|
217
243
|
}
|
|
218
244
|
|
|
245
|
+
/** The recorded GitHub auth route, or null while unanswered. */
|
|
246
|
+
export function githubAuthOf(choices: Readonly<Record<string, unknown>>): GithubAuth | null {
|
|
247
|
+
return memberOf(GITHUB_AUTH_CHOICES, raw(choices, CHOICE_KEYS.githubAuth));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Is the own-account route recorded? (Unanswered is NOT user mode.) */
|
|
251
|
+
export function userAuthRecorded(choices: Readonly<Record<string, unknown>>): boolean {
|
|
252
|
+
return githubAuthOf(choices) === "user";
|
|
253
|
+
}
|
|
254
|
+
|
|
219
255
|
/** The recorded ingress shape, or null when the ingress step has not run. */
|
|
220
256
|
export function ingressChoiceOf(choices: Readonly<Record<string, unknown>>): IngressChoice | null {
|
|
221
257
|
return memberOf(INGRESS_CHOICES, raw(choices, CHOICE_KEYS.ingress));
|