@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/onboard/engine.ts
CHANGED
|
@@ -1,26 +1,49 @@
|
|
|
1
|
-
// OnboardingEngine
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// tool per step); the
|
|
5
|
-
// itself is fully drivable from tests and from a non-LLM fallback
|
|
6
|
-
// nothing in here touches the network, Docker, or a real pi except
|
|
7
|
-
// the adapters.
|
|
1
|
+
// OnboardingEngine: the onboarding wizard's checklist, the plugin's setup
|
|
2
|
+
// checklist (@quickchatai/pi-quickreview/src/setupchecklist.ts, the step registry
|
|
3
|
+
// and its engine) bound to this app's adapters and state. pi is a thin
|
|
4
|
+
// driver over it (extensions/onboarding.ts registers one tool per step); the
|
|
5
|
+
// engine itself is fully drivable from tests and from a non-LLM fallback
|
|
6
|
+
// entry, and nothing in here touches the network, Docker, or a real pi except
|
|
7
|
+
// through the adapters.
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
// -
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
// What this module adds to the core's checklist:
|
|
10
|
+
// - the wizard's adapter set (OnboardAdapters) and its step type;
|
|
11
|
+
// - where the checklist lives: state/onboarding/checklist.json, tmp+rename
|
|
12
|
+
// atomic, re-verified on resume (the core's resumability contract);
|
|
13
|
+
// - the finish path's first-run marker, state/companion/first-run.json
|
|
14
|
+
// ({"completed_at": "<iso>"}), written exactly once: the contract the
|
|
15
|
+
// companion's first-run greeting consumes. Keep the path and shape
|
|
16
|
+
// stable.
|
|
17
|
+
import {
|
|
18
|
+
SetupChecklist,
|
|
19
|
+
checklistCompleteNow,
|
|
20
|
+
readChecklistFile,
|
|
21
|
+
writeAtomic,
|
|
22
|
+
type ChecklistAdapters,
|
|
23
|
+
type ChecklistFile,
|
|
24
|
+
type SetupStep,
|
|
25
|
+
type StepContext as ChecklistStepContext,
|
|
26
|
+
type StepRegistry,
|
|
27
|
+
type VerifyAllResult,
|
|
28
|
+
} from "@quickchatai/pi-quickreview/src/setupchecklist.js";
|
|
29
|
+
import type { StepForward, StepForwardRequest } from "./stepforward.js";
|
|
30
|
+
import { existsSync } from "node:fs";
|
|
31
|
+
import { join, resolve } from "node:path";
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
CHECKLIST_VERSION,
|
|
35
|
+
failures,
|
|
36
|
+
readTextOrNull,
|
|
37
|
+
writeAtomic,
|
|
38
|
+
type CheckResult,
|
|
39
|
+
type ChecklistFile,
|
|
40
|
+
type ReconcileReport,
|
|
41
|
+
type StepRecord,
|
|
42
|
+
type StepReport,
|
|
43
|
+
type StepRunResult,
|
|
44
|
+
type StepStatusLine,
|
|
45
|
+
type VerifyAllResult,
|
|
46
|
+
} from "@quickchatai/pi-quickreview/src/setupchecklist.js";
|
|
24
47
|
|
|
25
48
|
// ---- adapters ---------------------------------------------------------------
|
|
26
49
|
|
|
@@ -40,7 +63,7 @@ export interface ProcessRunner {
|
|
|
40
63
|
* fake every field; extensions/onboarding.ts builds the real set. Steps use
|
|
41
64
|
* fetch (manifest exchange, health probes) and prompter (non-LLM fallback
|
|
42
65
|
* entry) as they need them; preflight needs neither. */
|
|
43
|
-
export interface OnboardAdapters {
|
|
66
|
+
export interface OnboardAdapters extends ChecklistAdapters {
|
|
44
67
|
/** HTTP seam (GitHub App manifest exchange, health endpoints, …). */
|
|
45
68
|
fetch: typeof fetch;
|
|
46
69
|
runner: ProcessRunner;
|
|
@@ -60,101 +83,26 @@ export interface OnboardAdapters {
|
|
|
60
83
|
* so no step builds a config path itself. */
|
|
61
84
|
paths: { stateDir: string; configPath: string };
|
|
62
85
|
log?: (line: string) => void;
|
|
86
|
+
/** Opens the local memory forward for one step call (src/onboard/stepforward.ts):
|
|
87
|
+
* the compose route's memory engine is reachable from this machine only
|
|
88
|
+
* through it. The real assembly sets it; absent (every test), no forward is
|
|
89
|
+
* opened and the step sees whatever already listens. */
|
|
90
|
+
memoryForward?: (req: StepForwardRequest) => Promise<StepForward | null>;
|
|
63
91
|
}
|
|
64
92
|
|
|
65
93
|
// ---- step vocabulary --------------------------------------------------------
|
|
66
94
|
|
|
67
|
-
export
|
|
68
|
-
name: string;
|
|
69
|
-
ok: boolean;
|
|
70
|
-
detail: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** What run() and verify() both return. `data` (run only) is persisted onto
|
|
74
|
-
* the step's checklist record and handed back as ctx.data on later calls. */
|
|
75
|
-
export interface StepReport {
|
|
76
|
-
ok: boolean;
|
|
77
|
-
checks: CheckResult[];
|
|
78
|
-
data?: Record<string, unknown>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface StepContext {
|
|
82
|
-
adapters: OnboardAdapters;
|
|
83
|
-
/** The tool call's inputs — the operator's answer to a step that asks one
|
|
84
|
-
* (`{}` for a step that declares no `parameters`, and always on verify()).
|
|
85
|
-
* A step that needs an answer and does not get one must return a refusal
|
|
86
|
-
* naming the options, never a guess: the wizard offers, the operator picks. */
|
|
87
|
-
input: Readonly<Record<string, unknown>>;
|
|
88
|
-
/** Recorded operator choices (topology, auth route, captured uid, …). */
|
|
89
|
-
choices: Readonly<Record<string, unknown>>;
|
|
90
|
-
/** This step's persisted data from a previous run ({} on first run). */
|
|
91
|
-
data: Readonly<Record<string, unknown>>;
|
|
92
|
-
/** Record an operator choice / derived fact for later steps (persisted with
|
|
93
|
-
* the step result). */
|
|
94
|
-
recordChoice(key: string, value: unknown): void;
|
|
95
|
-
}
|
|
95
|
+
export type StepContext = ChecklistStepContext<OnboardAdapters>;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* One onboarding step (the registered set is defaultSteps in
|
|
99
|
-
* src/onboard/steps/index.ts)
|
|
100
|
-
*
|
|
101
|
-
* registers each step as tool `onboard_<id>`;
|
|
102
|
-
* - `explain` is the teach-text the guide relays before running the step;
|
|
103
|
-
* - `enabled` gates on recorded choices (e.g. the VM route's steps are
|
|
104
|
-
* disabled on the laptop topology); absent ⇒ always enabled;
|
|
105
|
-
* - `run` performs the step; `verify` re-checks its outcome against reality
|
|
106
|
-
* and must be safe to call any number of times.
|
|
99
|
+
* src/onboard/steps/index.ts): the core's SetupStep over the wizard's
|
|
100
|
+
* adapters. The pi extension registers each step as tool `onboard_<id>`.
|
|
107
101
|
*/
|
|
108
|
-
export
|
|
109
|
-
id: string;
|
|
110
|
-
title: string;
|
|
111
|
-
explain: string;
|
|
112
|
-
/** Ascending display/registration order. */
|
|
113
|
-
order: number;
|
|
114
|
-
/** JSON-schema object the step's pi tool declares, for steps that need the
|
|
115
|
-
* operator's answer (a choice, a hostname). Absent ⇒ the tool takes no
|
|
116
|
-
* input, which is the common case. */
|
|
117
|
-
parameters?: unknown;
|
|
118
|
-
enabled?(choices: Readonly<Record<string, unknown>>): boolean;
|
|
119
|
-
run(ctx: StepContext): Promise<StepReport>;
|
|
120
|
-
verify(ctx: StepContext): Promise<StepReport>;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function failures(checks: readonly CheckResult[]): string[] {
|
|
124
|
-
return checks.filter((c) => !c.ok).map((c) => `${c.name}: ${c.detail}`);
|
|
125
|
-
}
|
|
102
|
+
export type OnboardingStep = SetupStep<OnboardAdapters>;
|
|
126
103
|
|
|
127
104
|
// ---- checklist file ---------------------------------------------------------
|
|
128
105
|
|
|
129
|
-
export const CHECKLIST_VERSION = 1;
|
|
130
|
-
|
|
131
|
-
export interface StepRecord {
|
|
132
|
-
status: "pending" | "done";
|
|
133
|
-
completed_at: string | null;
|
|
134
|
-
/** Last successful re-verification (resume / finish). */
|
|
135
|
-
verified_at: string | null;
|
|
136
|
-
/** Why a claimed-done step was demoted, or other engine notes. */
|
|
137
|
-
note: string | null;
|
|
138
|
-
data: Record<string, unknown>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface ChecklistFile {
|
|
142
|
-
version: number;
|
|
143
|
-
created_at: string;
|
|
144
|
-
updated_at: string;
|
|
145
|
-
/** Set exactly once, by finish() when every enabled step verifies green.
|
|
146
|
-
* History — when onboarding first finished; it never moves and never clears. */
|
|
147
|
-
completed_at: string | null;
|
|
148
|
-
/** When a completed checklist stopped verifying (a demotion, a failed run,
|
|
149
|
-
* a refused finish, a newly-enabled unfinished step, a step run again —
|
|
150
|
-
* a run changes what the full verification checked); null/absent while the
|
|
151
|
-
* completion still holds. A green finish() clears it. Whether onboarding is
|
|
152
|
-
* complete now is `completed_at` set and this unset. */
|
|
153
|
-
reopened_at?: string | null;
|
|
154
|
-
choices: Record<string, unknown>;
|
|
155
|
-
steps: Record<string, StepRecord>;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
106
|
export function checklistPath(stateDir: string): string {
|
|
159
107
|
return join(stateDir, "onboarding", "checklist.json");
|
|
160
108
|
}
|
|
@@ -165,32 +113,9 @@ export function firstRunMarkerPath(stateDir: string): string {
|
|
|
165
113
|
return join(stateDir, "companion", "first-run.json");
|
|
166
114
|
}
|
|
167
115
|
|
|
168
|
-
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
169
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
116
|
/** Read the checklist; null when absent or unreadable (both mean "fresh"). */
|
|
173
117
|
export function readChecklist(stateDir: string): ChecklistFile | null {
|
|
174
|
-
|
|
175
|
-
if (!existsSync(path)) return null;
|
|
176
|
-
try {
|
|
177
|
-
const raw = JSON.parse(readFileSync(path, "utf8")) as unknown;
|
|
178
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
179
|
-
const file = raw as ChecklistFile;
|
|
180
|
-
// Every field the engine dereferences is checked here: a file that parses
|
|
181
|
-
// but lacks one (hand-edited, foreign) would otherwise throw out of a
|
|
182
|
-
// step predicate or record() on every call — a wedge, not a fresh start.
|
|
183
|
-
if (typeof file.version !== "number") return null;
|
|
184
|
-
if (!isPlainObject(file.choices)) return null;
|
|
185
|
-
if (file.completed_at !== null && typeof file.completed_at !== "string") return null;
|
|
186
|
-
if (!isPlainObject(file.steps)) return null;
|
|
187
|
-
for (const rec of Object.values(file.steps)) {
|
|
188
|
-
if (!isPlainObject(rec) || !isPlainObject(rec.data)) return null;
|
|
189
|
-
}
|
|
190
|
-
return file;
|
|
191
|
-
} catch {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
118
|
+
return readChecklistFile(checklistPath(stateDir));
|
|
194
119
|
}
|
|
195
120
|
|
|
196
121
|
/** Does the checklist file say onboarding finished? The CLI's post-exit
|
|
@@ -198,13 +123,7 @@ export function readChecklist(stateDir: string): ChecklistFile | null {
|
|
|
198
123
|
* re-verification, so "the file says so" is a verified claim). */
|
|
199
124
|
export function checklistComplete(stateDir: string): boolean {
|
|
200
125
|
const file = readChecklist(stateDir);
|
|
201
|
-
return file !== null &&
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** Is onboarding complete NOW: finished once, and not reopened since. The one
|
|
205
|
-
* reading of the two fields, for the file on disk and the engine's copy. */
|
|
206
|
-
function completeNow(file: Pick<ChecklistFile, "completed_at" | "reopened_at">): boolean {
|
|
207
|
-
return typeof file.completed_at === "string" && !file.reopened_at;
|
|
126
|
+
return file !== null && checklistCompleteNow(file);
|
|
208
127
|
}
|
|
209
128
|
|
|
210
129
|
/** `<sandbox root>` — the parent of `paths.stateDir`, by the adapters'
|
|
@@ -213,306 +132,36 @@ export function sandboxRoot(paths: { readonly stateDir: string }): string {
|
|
|
213
132
|
return resolve(paths.stateDir, "..");
|
|
214
133
|
}
|
|
215
134
|
|
|
216
|
-
/** tmp+rename atomic write — the one writer every onboarding file write goes
|
|
217
|
-
* through (the checklist, config.toml, .env, the inventory): a reader, the
|
|
218
|
-
* daemon's own loader one bind mount away included, never sees a half-written
|
|
219
|
-
* file, and an interrupted write leaves the previous file whole. */
|
|
220
|
-
export function writeAtomic(path: string, content: string): void {
|
|
221
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
222
|
-
const tmp = join(dirname(path), `.${basename(path)}.onboard.tmp`);
|
|
223
|
-
writeFileSync(tmp, content);
|
|
224
|
-
renameSync(tmp, path);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** A file's text, or null when it cannot be read for any reason. For a read
|
|
228
|
-
* where only ABSENCE may be null, read it yourself. */
|
|
229
|
-
export function readTextOrNull(path: string): string | null {
|
|
230
|
-
try {
|
|
231
|
-
return readFileSync(path, "utf8");
|
|
232
|
-
} catch {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
135
|
// ---- engine -----------------------------------------------------------------
|
|
238
136
|
|
|
239
|
-
export interface StepStatusLine {
|
|
240
|
-
id: string;
|
|
241
|
-
title: string;
|
|
242
|
-
explain: string;
|
|
243
|
-
status: "pending" | "done" | "disabled";
|
|
244
|
-
note: string | null;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export interface ReconcileReport {
|
|
248
|
-
/** Claimed-done steps whose verify() held. */
|
|
249
|
-
verified: string[];
|
|
250
|
-
/** Claimed-done steps whose verify() failed — demoted to pending. */
|
|
251
|
-
demoted: Array<{ id: string; failures: string[] }>;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export interface StepRunResult {
|
|
255
|
-
id: string;
|
|
256
|
-
ok: boolean;
|
|
257
|
-
checks: CheckResult[];
|
|
258
|
-
failures: string[];
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export interface VerifyAllResult {
|
|
262
|
-
ok: boolean;
|
|
263
|
-
results: Array<{ id: string; ok: boolean; failures: string[] }>;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
137
|
export type FinishResult =
|
|
267
138
|
| { ok: true; completed_at: string; marker_written: boolean }
|
|
268
139
|
| { ok: false; results: VerifyAllResult["results"] };
|
|
269
140
|
|
|
270
|
-
export class OnboardingEngine {
|
|
271
|
-
private readonly registry: OnboardingStep[];
|
|
272
|
-
private checklist: ChecklistFile;
|
|
273
|
-
|
|
141
|
+
export class OnboardingEngine extends SetupChecklist<OnboardAdapters> {
|
|
274
142
|
constructor(
|
|
275
|
-
steps: OnboardingStep[],
|
|
276
|
-
|
|
143
|
+
steps: StepRegistry<OnboardAdapters> | readonly OnboardingStep[],
|
|
144
|
+
adapters: OnboardAdapters,
|
|
277
145
|
) {
|
|
278
|
-
|
|
279
|
-
const loaded = readChecklist(adapters.paths.stateDir);
|
|
280
|
-
if (loaded === null && existsSync(checklistPath(adapters.paths.stateDir))) {
|
|
281
|
-
adapters.log?.(
|
|
282
|
-
"onboarding: checklist file unreadable — starting fresh (recorded choices and step progress were reset)",
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
this.checklist = loaded ?? this.fresh();
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
private fresh(): ChecklistFile {
|
|
289
|
-
const now = this.adapters.clock().toISOString();
|
|
290
|
-
return {
|
|
291
|
-
version: CHECKLIST_VERSION,
|
|
292
|
-
created_at: now,
|
|
293
|
-
updated_at: now,
|
|
294
|
-
completed_at: null,
|
|
295
|
-
choices: {},
|
|
296
|
-
steps: {},
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private save(): void {
|
|
301
|
-
this.checklist.updated_at = this.adapters.clock().toISOString();
|
|
302
|
-
writeAtomic(
|
|
303
|
-
checklistPath(this.adapters.paths.stateDir),
|
|
304
|
-
`${JSON.stringify(this.checklist, null, 2)}\n`,
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
private record(id: string): StepRecord {
|
|
309
|
-
const existing = this.checklist.steps[id];
|
|
310
|
-
if (existing) return existing;
|
|
311
|
-
const created: StepRecord = {
|
|
312
|
-
status: "pending",
|
|
313
|
-
completed_at: null,
|
|
314
|
-
verified_at: null,
|
|
315
|
-
note: null,
|
|
316
|
-
data: {},
|
|
317
|
-
};
|
|
318
|
-
this.checklist.steps[id] = created;
|
|
319
|
-
return created;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
private ctxFor(id: string, input: Record<string, unknown> = {}): StepContext {
|
|
323
|
-
return {
|
|
324
|
-
adapters: this.adapters,
|
|
325
|
-
input,
|
|
326
|
-
choices: this.checklist.choices,
|
|
327
|
-
data: this.record(id).data,
|
|
328
|
-
recordChoice: (key, value) => {
|
|
329
|
-
this.checklist.choices[key] = value;
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** The registered steps, in order (the pi tools register off this). */
|
|
335
|
-
stepDefs(): readonly OnboardingStep[] {
|
|
336
|
-
return this.registry;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private enabled(step: OnboardingStep): boolean {
|
|
340
|
-
return step.enabled ? step.enabled(this.checklist.choices) : true;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
choices(): Readonly<Record<string, unknown>> {
|
|
344
|
-
return this.checklist.choices;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
isComplete(): boolean {
|
|
348
|
-
return completeNow(this.checklist);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/** A completed checklist no longer verifies: withdraw the current-completion
|
|
352
|
-
* claim (the post-exit handoff reads it) without touching the history. */
|
|
353
|
-
private reopen(): void {
|
|
354
|
-
if (this.checklist.completed_at === null || this.checklist.reopened_at) return;
|
|
355
|
-
this.checklist.reopened_at = this.adapters.clock().toISOString();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
list(): StepStatusLine[] {
|
|
359
|
-
return this.registry.map((step) => {
|
|
360
|
-
const rec = this.checklist.steps[step.id];
|
|
361
|
-
return {
|
|
362
|
-
id: step.id,
|
|
363
|
-
title: step.title,
|
|
364
|
-
explain: step.explain,
|
|
365
|
-
status: this.enabled(step) ? (rec?.status ?? "pending") : "disabled",
|
|
366
|
-
note: rec?.note ?? null,
|
|
367
|
-
};
|
|
368
|
-
});
|
|
146
|
+
super(steps, adapters, { file: checklistPath(adapters.paths.stateDir) });
|
|
369
147
|
}
|
|
370
148
|
|
|
371
149
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*/
|
|
377
|
-
async reconcile(): Promise<ReconcileReport> {
|
|
378
|
-
const report: ReconcileReport = { verified: [], demoted: [] };
|
|
379
|
-
for (const step of this.registry) {
|
|
380
|
-
if (!this.enabled(step)) continue;
|
|
381
|
-
const rec = this.checklist.steps[step.id];
|
|
382
|
-
if (!rec || rec.status !== "done") {
|
|
383
|
-
this.reopen();
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const outcome = await this.safeVerify(step);
|
|
387
|
-
if (outcome.ok) {
|
|
388
|
-
rec.verified_at = this.adapters.clock().toISOString();
|
|
389
|
-
report.verified.push(step.id);
|
|
390
|
-
} else {
|
|
391
|
-
const fails = failures(outcome.checks);
|
|
392
|
-
rec.status = "pending";
|
|
393
|
-
rec.note = `re-verification failed: ${fails.join("; ") || "unknown"}`;
|
|
394
|
-
this.reopen();
|
|
395
|
-
report.demoted.push({ id: step.id, failures: fails });
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
this.save();
|
|
399
|
-
return report;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/** Run one step, optionally with the operator's answer (`input`, the pi
|
|
403
|
-
* tool's params). Unknown/disabled ids are refusals-as-answers, never
|
|
404
|
-
* throws (the caller is an LLM tool belt). Persists after the step. */
|
|
405
|
-
async runStep(id: string, input: Record<string, unknown> = {}): Promise<StepRunResult> {
|
|
406
|
-
const step = this.registry.find((s) => s.id === id);
|
|
407
|
-
if (!step) {
|
|
408
|
-
return {
|
|
409
|
-
id,
|
|
410
|
-
ok: false,
|
|
411
|
-
checks: [],
|
|
412
|
-
failures: [`unknown step "${id}" — known: ${this.registry.map((s) => s.id).join(", ")}`],
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
if (!this.enabled(step)) {
|
|
416
|
-
return {
|
|
417
|
-
id,
|
|
418
|
-
ok: false,
|
|
419
|
-
checks: [],
|
|
420
|
-
failures: [`step "${id}" is disabled by the recorded choices`],
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
const rec = this.record(id);
|
|
424
|
-
rec.status = "pending";
|
|
425
|
-
this.reopen();
|
|
426
|
-
this.save();
|
|
427
|
-
let report: StepReport;
|
|
428
|
-
try {
|
|
429
|
-
report = await step.run(this.ctxFor(id, input));
|
|
430
|
-
} catch (err) {
|
|
431
|
-
report = {
|
|
432
|
-
ok: false,
|
|
433
|
-
checks: [{ name: id, ok: false, detail: `step threw: ${String(err)}` }],
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
if (report.data) rec.data = { ...rec.data, ...report.data };
|
|
437
|
-
if (report.ok) {
|
|
438
|
-
rec.status = "done";
|
|
439
|
-
rec.completed_at = this.adapters.clock().toISOString();
|
|
440
|
-
rec.verified_at = null;
|
|
441
|
-
rec.note = null;
|
|
442
|
-
// A run acts on the installation (choices, config, deploys) after the
|
|
443
|
-
// full verification that backed the completion claim: only the next
|
|
444
|
-
// green finish() may make that claim again.
|
|
445
|
-
this.reopen();
|
|
446
|
-
} else {
|
|
447
|
-
rec.status = "pending";
|
|
448
|
-
rec.note = `last run failed: ${failures(report.checks).join("; ") || "unknown"}`;
|
|
449
|
-
this.reopen();
|
|
450
|
-
}
|
|
451
|
-
this.save();
|
|
452
|
-
return { id, ok: report.ok, checks: report.checks, failures: failures(report.checks) };
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
private async safeVerify(step: OnboardingStep): Promise<StepReport> {
|
|
456
|
-
try {
|
|
457
|
-
return await step.verify(this.ctxFor(step.id));
|
|
458
|
-
} catch (err) {
|
|
459
|
-
return {
|
|
460
|
-
ok: false,
|
|
461
|
-
checks: [{ name: step.id, ok: false, detail: `verify threw: ${String(err)}` }],
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Verify every enabled step — done or not. A pending step whose verify()
|
|
468
|
-
* holds is promoted to done (its outcome exists in reality, which is the
|
|
469
|
-
* only truth the checklist mirrors); a done step whose verify() fails
|
|
470
|
-
* reports red here (finish refuses) but demotion is reconcile()'s job.
|
|
471
|
-
*/
|
|
472
|
-
async verifyAll(): Promise<VerifyAllResult> {
|
|
473
|
-
const results: VerifyAllResult["results"] = [];
|
|
474
|
-
for (const step of this.registry) {
|
|
475
|
-
if (!this.enabled(step)) continue;
|
|
476
|
-
const outcome = await this.safeVerify(step);
|
|
477
|
-
const rec = this.record(step.id);
|
|
478
|
-
if (outcome.ok) {
|
|
479
|
-
rec.verified_at = this.adapters.clock().toISOString();
|
|
480
|
-
if (rec.status !== "done") {
|
|
481
|
-
rec.status = "done";
|
|
482
|
-
rec.completed_at = rec.completed_at ?? rec.verified_at;
|
|
483
|
-
rec.note = "verified green without a recorded run";
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
results.push({ id: step.id, ok: outcome.ok, failures: failures(outcome.checks) });
|
|
487
|
-
}
|
|
488
|
-
this.save();
|
|
489
|
-
return { ok: results.every((r) => r.ok), results };
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* The finish tool's engine half: re-run the full verification checklist.
|
|
494
|
-
* Anything red ⇒ refuse with the failures (the caller returns them as the
|
|
495
|
-
* tool result and does not exit). All green ⇒ mark the checklist complete
|
|
496
|
-
* and write the first-run marker — once: a marker already on disk is left
|
|
150
|
+
* The finish tool's engine half: the checklist's full verification. Anything
|
|
151
|
+
* red ⇒ refuse with the failures (the caller returns them as the tool
|
|
152
|
+
* result and does not exit). All green ⇒ the checklist is complete, and the
|
|
153
|
+
* first-run marker is written — once: a marker already on disk is left
|
|
497
154
|
* untouched, so the completed_at the companion greets on never moves.
|
|
498
155
|
*/
|
|
499
|
-
async finish(): Promise<FinishResult> {
|
|
500
|
-
const
|
|
501
|
-
if (!
|
|
502
|
-
this.reopen();
|
|
503
|
-
this.save();
|
|
504
|
-
return { ok: false, results: all.results };
|
|
505
|
-
}
|
|
506
|
-
const completedAt = this.checklist.completed_at ?? this.adapters.clock().toISOString();
|
|
507
|
-
this.checklist.completed_at = completedAt;
|
|
508
|
-
this.checklist.reopened_at = null;
|
|
509
|
-
this.save();
|
|
156
|
+
override async finish(): Promise<FinishResult> {
|
|
157
|
+
const done = await super.finish();
|
|
158
|
+
if (!done.ok) return done;
|
|
510
159
|
const marker = firstRunMarkerPath(this.adapters.paths.stateDir);
|
|
511
160
|
let markerWritten = false;
|
|
512
161
|
if (!existsSync(marker)) {
|
|
513
|
-
writeAtomic(marker, `${JSON.stringify({ completed_at:
|
|
162
|
+
writeAtomic(marker, `${JSON.stringify({ completed_at: done.completed_at })}\n`);
|
|
514
163
|
markerWritten = true;
|
|
515
164
|
}
|
|
516
|
-
return { ok: true, completed_at:
|
|
165
|
+
return { ok: true, completed_at: done.completed_at, marker_written: markerWritten };
|
|
517
166
|
}
|
|
518
167
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The pinned pi version, read from package.json. A leaf: the daemon reaches it
|
|
2
|
+
// through src/version.ts, so it imports nothing from the onboarding wizard.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export interface PiPin {
|
|
7
|
+
package: string;
|
|
8
|
+
version: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** The pin from package.json's "qcrPi" block — the mirror that exists on a bare
|
|
12
|
+
* public clone, where config.toml does not (the wizard writes it later).
|
|
13
|
+
* config.toml [pi] stays the preferred source when it loads (cmdOnboard tries
|
|
14
|
+
* it first); both mirror the same pinned version by rule 7's discipline. */
|
|
15
|
+
export function piPinFromPackageJson(
|
|
16
|
+
root: string,
|
|
17
|
+
readFile: (p: string) => string = (p) => readFileSync(p, "utf8"),
|
|
18
|
+
): PiPin | null {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(readFile(join(root, "package.json"))) as {
|
|
21
|
+
qcrPi?: { package?: unknown; version?: unknown };
|
|
22
|
+
};
|
|
23
|
+
const pi = pkg.qcrPi;
|
|
24
|
+
if (pi && typeof pi.package === "string" && typeof pi.version === "string") {
|
|
25
|
+
return { package: pi.package, version: pi.version };
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// fall through — the caller reports the missing pin
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The memory forward, held open for ONE onboarding step call.
|
|
2
|
+
//
|
|
3
|
+
// On the compose route the memory engine listens only on its own container's
|
|
4
|
+
// loopback; this machine reaches it through the memory forward, the same relay
|
|
5
|
+
// the TUI runs in-process (src/tui/memforward.ts over
|
|
6
|
+
// scripts/companion-memory-forward.mjs). The wizard runs before any TUI, so
|
|
7
|
+
// without this the memory steps' phase B (minting the user keys, the
|
|
8
|
+
// confinement matrix) found nothing listening and sent the operator to a
|
|
9
|
+
// second terminal — and a wizard driven in order never got memory working.
|
|
10
|
+
//
|
|
11
|
+
// A forward that is already listening (`qcr tui`, the systemd unit) is
|
|
12
|
+
// adopted and left alone; one this opens is closed when the step ends, so the
|
|
13
|
+
// wizard leaves nothing running behind it. The real adapter assembly
|
|
14
|
+
// (extensions/onboarding.ts) hands this to the steps; a test's adapters never
|
|
15
|
+
// do, so no test binds a port.
|
|
16
|
+
import { MemoryForward } from "../tui/memforward.js";
|
|
17
|
+
|
|
18
|
+
export interface StepForwardRequest {
|
|
19
|
+
/** Where the step reaches memory (the forward's loopback listener). */
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
/** The checkout holding scripts/. */
|
|
22
|
+
repoRoot: string;
|
|
23
|
+
/** The memory sidecar's container. */
|
|
24
|
+
container: string;
|
|
25
|
+
sudoDocker: boolean;
|
|
26
|
+
log?: (line: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StepForward {
|
|
30
|
+
/** true when this call started the forward (and stop() closes it). */
|
|
31
|
+
started: boolean;
|
|
32
|
+
detail: string;
|
|
33
|
+
stop(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Open (or adopt) the local memory forward; null when none could be made to
|
|
37
|
+
* answer, with nothing left bound. */
|
|
38
|
+
export async function openStepMemoryForward(
|
|
39
|
+
req: StepForwardRequest,
|
|
40
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
41
|
+
): Promise<StepForward | null> {
|
|
42
|
+
const forward = new MemoryForward({
|
|
43
|
+
baseUrl: req.baseUrl,
|
|
44
|
+
repoRoot: req.repoRoot,
|
|
45
|
+
env,
|
|
46
|
+
local: true,
|
|
47
|
+
sudoDocker: req.sudoDocker,
|
|
48
|
+
container: req.container,
|
|
49
|
+
...(req.log ? { log: req.log } : {}),
|
|
50
|
+
});
|
|
51
|
+
const status = await forward.ensure();
|
|
52
|
+
if (status.state === "owned")
|
|
53
|
+
return {
|
|
54
|
+
started: true,
|
|
55
|
+
detail: `opened a memory forward to ${req.container} for this step; it closes when the step ends, and \`qcr tui\` runs its own`,
|
|
56
|
+
stop: () => forward.stop(),
|
|
57
|
+
};
|
|
58
|
+
if (status.state === "adopted") return { started: false, detail: status.detail, stop: () => {} };
|
|
59
|
+
forward.stop();
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
tomlString,
|
|
51
51
|
type TomlEdit,
|
|
52
52
|
} from "./configfill.js";
|
|
53
|
-
import { CHOICE_KEYS, botLoginFor } from "../choices.js";
|
|
53
|
+
import { CHOICE_KEYS, botLoginFor, userAuthRecorded } from "../choices.js";
|
|
54
54
|
import { api, readCredentials } from "./githubapp.js";
|
|
55
55
|
|
|
56
56
|
export const APP_SLUG_STEP_ID = "app_slug_reconcile";
|
|
@@ -254,6 +254,9 @@ export function appSlugStep(opts: AppSlugStepOptions = {}): OnboardingStep {
|
|
|
254
254
|
"slug, so this step asks the App itself who it is and corrects [github] app_slug and bot_login to match. It matters more " +
|
|
255
255
|
"than it looks: bot_login is how the reviewer recognises its own reviews and comments, and a wrong one fails silently.",
|
|
256
256
|
order: 65,
|
|
257
|
+
// There is no App slug on the own-account route: `[github] app_slug` is ""
|
|
258
|
+
// and bot_login is the person's login (the github_account step writes both).
|
|
259
|
+
enabled: (choices) => !userAuthRecorded(choices),
|
|
257
260
|
async run(ctx): Promise<StepReport> {
|
|
258
261
|
return reconcile(ctx, true);
|
|
259
262
|
},
|