@quickchatai/quickreview 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +385 -2
- package/THIRD-PARTY-NOTICES.md +134 -0
- package/agents/pass-abstraction.md +64 -0
- package/agents/pass-ask-human.md +134 -0
- package/agents/pass-backcompat.md +67 -0
- package/agents/pass-baseline.md +61 -0
- package/agents/pass-behaviour-delta.md +64 -0
- package/agents/pass-bounds.md +36 -0
- package/agents/pass-claims.md +31 -0
- package/agents/pass-concurrency.md +168 -0
- package/agents/pass-consistency.md +57 -0
- package/agents/pass-contracts.md +30 -0
- package/agents/pass-data-safety.md +54 -0
- package/agents/pass-deferred.md +35 -0
- package/agents/pass-delivery.md +37 -0
- package/agents/pass-duplication.md +60 -0
- package/agents/pass-intent.md +63 -0
- package/agents/pass-lint-conformance.md +46 -0
- package/agents/pass-maintainability.md +67 -0
- package/agents/pass-placement.md +68 -0
- package/agents/pass-prod-risk.md +24 -0
- package/agents/pass-rollout.md +32 -0
- package/agents/pass-roundtrip.md +46 -0
- package/agents/pass-security.md +31 -0
- package/agents/pass-simplicity.md +68 -0
- package/agents/pass-statelessness.md +27 -0
- package/agents/pass-style.md +37 -0
- package/agents/pass-sweep.md +57 -0
- package/agents/pass-tests.md +58 -0
- package/agents/pass-ui.md +23 -0
- package/agents/pass-validate.md +50 -0
- package/agents/pass-widened.md +46 -0
- package/agents/scan/pass-scan.md +86 -0
- package/bin/qcr.js +11 -0
- package/config.example.toml +1067 -0
- package/deploy/ansible/README.md +356 -0
- package/deploy/ansible/group_vars/all.yml +212 -0
- package/deploy/ansible/inventory.example.ini +25 -0
- package/deploy/ansible/playbook.yml +360 -0
- package/deploy/collect-secrets.sh +289 -0
- package/deploy/collect-slack-secrets.mjs +1190 -0
- package/deploy/github-app-manifest.json +31 -0
- package/deploy/manifest.schema.json +75 -0
- package/deploy/qcr-update.sh +803 -0
- package/deploy/reviewer/AGENTS.override.md +12 -0
- package/dist/extensions/cli/bash-guard-ambient.js +42 -0
- package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
- package/dist/extensions/cli/bash-guard.js +5 -0
- package/dist/extensions/cli/bash-guard.js.map +1 -0
- package/dist/extensions/cli/companion-guard.js +5 -0
- package/dist/extensions/cli/companion-guard.js.map +1 -0
- package/dist/extensions/cli/debug-round.js +8 -0
- package/dist/extensions/cli/debug-round.js.map +1 -0
- package/dist/extensions/cli/inject-bridge.js +5 -0
- package/dist/extensions/cli/inject-bridge.js.map +1 -0
- package/dist/extensions/cli/ledger-pin.js +5 -0
- package/dist/extensions/cli/ledger-pin.js.map +1 -0
- package/dist/extensions/cli/lens.js +13 -0
- package/dist/extensions/cli/lens.js.map +1 -0
- package/dist/extensions/cli/muted-theme.js +5 -0
- package/dist/extensions/cli/muted-theme.js.map +1 -0
- package/dist/extensions/cli/onboarding.js +5 -0
- package/dist/extensions/cli/onboarding.js.map +1 -0
- package/dist/extensions/cli/setup.js +5 -0
- package/dist/extensions/cli/setup.js.map +1 -0
- package/dist/extensions/cli/tui-view.js +5 -0
- package/dist/extensions/cli/tui-view.js.map +1 -0
- package/dist/extensions/companion-guard.js +361 -0
- package/dist/extensions/companion-guard.js.map +1 -0
- package/dist/extensions/debug-round.js +204 -0
- package/dist/extensions/debug-round.js.map +1 -0
- package/dist/extensions/inject-bridge.js +603 -0
- package/dist/extensions/inject-bridge.js.map +1 -0
- package/dist/extensions/muted-theme.js +12 -0
- package/dist/extensions/muted-theme.js.map +1 -0
- package/dist/extensions/onboarding.js +175 -0
- package/dist/extensions/onboarding.js.map +1 -0
- package/dist/extensions/setup-replay.js +269 -0
- package/dist/extensions/setup-replay.js.map +1 -0
- package/dist/extensions/setup-scan.js +998 -0
- package/dist/extensions/setup-scan.js.map +1 -0
- package/dist/extensions/setup-triage.js +175 -0
- package/dist/extensions/setup-triage.js.map +1 -0
- package/dist/extensions/setup.js +1160 -0
- package/dist/extensions/setup.js.map +1 -0
- package/dist/extensions/tui-view.js +389 -0
- package/dist/extensions/tui-view.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/client.js +376 -0
- package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/config.js +189 -0
- package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/index.js +421 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
- package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
- package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
- package/dist/src/anomalies.js +483 -0
- package/dist/src/anomalies.js.map +1 -0
- package/dist/src/answerworker.js +180 -0
- package/dist/src/answerworker.js.map +1 -0
- package/dist/src/appstamp.js +68 -0
- package/dist/src/appstamp.js.map +1 -0
- package/dist/src/archive/admission.js +3 -0
- package/dist/src/archive/admission.js.map +1 -0
- package/dist/src/archive/archivedb.js +474 -0
- package/dist/src/archive/archivedb.js.map +1 -0
- package/dist/src/archive/archiver.js +1770 -0
- package/dist/src/archive/archiver.js.map +1 -0
- package/dist/src/archive/archivewrite.js +198 -0
- package/dist/src/archive/archivewrite.js.map +1 -0
- package/dist/src/archive/bundle.js +266 -0
- package/dist/src/archive/bundle.js.map +1 -0
- package/dist/src/archive/dbmove.js +638 -0
- package/dist/src/archive/dbmove.js.map +1 -0
- package/dist/src/archive/duplicatecounts.js +80 -0
- package/dist/src/archive/duplicatecounts.js.map +1 -0
- package/dist/src/archive/eligibility.js +113 -0
- package/dist/src/archive/eligibility.js.map +1 -0
- package/dist/src/archive/extract.js +288 -0
- package/dist/src/archive/extract.js.map +1 -0
- package/dist/src/archive/fsutil.js +330 -0
- package/dist/src/archive/fsutil.js.map +1 -0
- package/dist/src/archive/gzfile.js +277 -0
- package/dist/src/archive/gzfile.js.map +1 -0
- package/dist/src/archive/kinds.js +22 -0
- package/dist/src/archive/kinds.js.map +1 -0
- package/dist/src/archive/livecheckpoint.js +178 -0
- package/dist/src/archive/livecheckpoint.js.map +1 -0
- package/dist/src/archive/liveretention.js +145 -0
- package/dist/src/archive/liveretention.js.map +1 -0
- package/dist/src/archive/manifest.js +284 -0
- package/dist/src/archive/manifest.js.map +1 -0
- package/dist/src/archive/overseer.js +283 -0
- package/dist/src/archive/overseer.js.map +1 -0
- package/dist/src/archive/piglobal.js +129 -0
- package/dist/src/archive/piglobal.js.map +1 -0
- package/dist/src/archive/plan.js +398 -0
- package/dist/src/archive/plan.js.map +1 -0
- package/dist/src/archive/prarchive.js +831 -0
- package/dist/src/archive/prarchive.js.map +1 -0
- package/dist/src/archive/prfiles.js +303 -0
- package/dist/src/archive/prfiles.js.map +1 -0
- package/dist/src/archive/report.js +169 -0
- package/dist/src/archive/report.js.map +1 -0
- package/dist/src/archive/restore.js +276 -0
- package/dist/src/archive/restore.js.map +1 -0
- package/dist/src/archive/rows.js +106 -0
- package/dist/src/archive/rows.js.map +1 -0
- package/dist/src/archive/seal.js +480 -0
- package/dist/src/archive/seal.js.map +1 -0
- package/dist/src/archive/status.js +528 -0
- package/dist/src/archive/status.js.map +1 -0
- package/dist/src/archive/storepass.js +409 -0
- package/dist/src/archive/storepass.js.map +1 -0
- package/dist/src/backfill.js +217 -0
- package/dist/src/backfill.js.map +1 -0
- package/dist/src/bench/cost.js +87 -0
- package/dist/src/bench/exec.js +76 -0
- package/dist/src/bench/values.js +14 -0
- package/dist/src/botcontrol.js +127 -0
- package/dist/src/botcontrol.js.map +1 -0
- package/dist/src/botworker.js +805 -0
- package/dist/src/botworker.js.map +1 -0
- package/dist/src/capture.js +234 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cases.js +1491 -0
- package/dist/src/cases.js.map +1 -0
- package/dist/src/channelfs.js +27 -0
- package/dist/src/channelfs.js.map +1 -0
- package/dist/src/checklist.js +312 -0
- package/dist/src/checklist.js.map +1 -0
- package/dist/src/ciretry.js +91 -0
- package/dist/src/ciretry.js.map +1 -0
- package/dist/src/cli.js +1916 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/cliops.js +333 -0
- package/dist/src/cliops.js.map +1 -0
- package/dist/src/clonedir.js +15 -0
- package/dist/src/clonedir.js.map +1 -0
- package/dist/src/commandeffects.js +43 -0
- package/dist/src/commandeffects.js.map +1 -0
- package/dist/src/commands.js +1312 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/companioninbox.js +203 -0
- package/dist/src/companioninbox.js.map +1 -0
- package/dist/src/compare.js +673 -0
- package/dist/src/compare.js.map +1 -0
- package/dist/src/config.js +1027 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/configcheck.js +111 -0
- package/dist/src/configcheck.js.map +1 -0
- package/dist/src/coreext.js +43 -0
- package/dist/src/coreext.js.map +1 -0
- package/dist/src/daemon.js +929 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/debuground.js +731 -0
- package/dist/src/debuground.js.map +1 -0
- package/dist/src/debugroundpi.js +219 -0
- package/dist/src/debugroundpi.js.map +1 -0
- package/dist/src/diagnostics.js +14 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/digest.js +139 -0
- package/dist/src/digest.js.map +1 -0
- package/dist/src/dispatch.js +1726 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/engine/answerguard.js +18 -0
- package/dist/src/engine/answerguard.js.map +1 -0
- package/dist/src/engine/botsdk.js +262 -0
- package/dist/src/engine/botsdk.js.map +1 -0
- package/dist/src/engine/caserunner-sdk.js +332 -0
- package/dist/src/engine/caserunner-sdk.js.map +1 -0
- package/dist/src/engine/debugpi.js +194 -0
- package/dist/src/engine/debugpi.js.map +1 -0
- package/dist/src/eod.js +473 -0
- package/dist/src/eod.js.map +1 -0
- package/dist/src/eodpinwatch.js +391 -0
- package/dist/src/eodpinwatch.js.map +1 -0
- package/dist/src/escalation.js +1009 -0
- package/dist/src/escalation.js.map +1 -0
- package/dist/src/eventloop.js +779 -0
- package/dist/src/eventloop.js.map +1 -0
- package/dist/src/execboundary.js +430 -0
- package/dist/src/execboundary.js.map +1 -0
- package/dist/src/feedback.js +915 -0
- package/dist/src/feedback.js.map +1 -0
- package/dist/src/findingscheck.js +200 -0
- package/dist/src/findingscheck.js.map +1 -0
- package/dist/src/fsdenied.js +26 -0
- package/dist/src/fsdenied.js.map +1 -0
- package/dist/src/ghread.js +181 -0
- package/dist/src/ghread.js.map +1 -0
- package/dist/src/github.js +1063 -0
- package/dist/src/github.js.map +1 -0
- package/dist/src/homes.js +103 -0
- package/dist/src/homes.js.map +1 -0
- package/dist/src/hostroute.js +24 -0
- package/dist/src/hostroute.js.map +1 -0
- package/dist/src/improvements.js +625 -0
- package/dist/src/improvements.js.map +1 -0
- package/dist/src/install.js +946 -0
- package/dist/src/install.js.map +1 -0
- package/dist/src/installkind.js +40 -0
- package/dist/src/installkind.js.map +1 -0
- package/dist/src/ipc/capability.js +286 -0
- package/dist/src/ipc/capability.js.map +1 -0
- package/dist/src/ipc/client.js +226 -0
- package/dist/src/ipc/client.js.map +1 -0
- package/dist/src/ipc/control.js +70 -0
- package/dist/src/ipc/control.js.map +1 -0
- package/dist/src/ipc/ghreadshim.js +77 -0
- package/dist/src/ipc/ghreadshim.js.map +1 -0
- package/dist/src/ipc/launcher.js +65 -0
- package/dist/src/ipc/launcher.js.map +1 -0
- package/dist/src/ipc/permits.js +293 -0
- package/dist/src/ipc/permits.js.map +1 -0
- package/dist/src/ipc/postshim.js +137 -0
- package/dist/src/ipc/postshim.js.map +1 -0
- package/dist/src/ipc/protocol.js +239 -0
- package/dist/src/ipc/protocol.js.map +1 -0
- package/dist/src/ipc/server.js +312 -0
- package/dist/src/ipc/server.js.map +1 -0
- package/dist/src/ipc/workerspec.js +148 -0
- package/dist/src/ipc/workerspec.js.map +1 -0
- package/dist/src/learner/learner.js +1272 -0
- package/dist/src/learner/learner.js.map +1 -0
- package/dist/src/learner/miner.js +948 -0
- package/dist/src/learner/miner.js.map +1 -0
- package/dist/src/learner/packet.js +216 -0
- package/dist/src/learner/packet.js.map +1 -0
- package/dist/src/learner/receipt.js +135 -0
- package/dist/src/learner/receipt.js.map +1 -0
- package/dist/src/learner/status.js +38 -0
- package/dist/src/learner/status.js.map +1 -0
- package/dist/src/ledgercheck.js +120 -0
- package/dist/src/ledgercheck.js.map +1 -0
- package/dist/src/lessonledger.js +451 -0
- package/dist/src/lessonledger.js.map +1 -0
- package/dist/src/lessonselect.js +639 -0
- package/dist/src/lessonselect.js.map +1 -0
- package/dist/src/lessonshadow.js +540 -0
- package/dist/src/lessonshadow.js.map +1 -0
- package/dist/src/levers.js +302 -0
- package/dist/src/levers.js.map +1 -0
- package/dist/src/live/projection.js +450 -0
- package/dist/src/live/projection.js.map +1 -0
- package/dist/src/live/publish.js +83 -0
- package/dist/src/live/publish.js.map +1 -0
- package/dist/src/live/read.js +60 -0
- package/dist/src/live/read.js.map +1 -0
- package/dist/src/livecli.js +288 -0
- package/dist/src/livecli.js.map +1 -0
- package/dist/src/loginhint.js +42 -0
- package/dist/src/loginhint.js.map +1 -0
- package/dist/src/loginrefresh/atomic.js +66 -0
- package/dist/src/loginrefresh/atomic.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +93 -0
- package/dist/src/loginrefresh/lock.js.map +1 -0
- package/dist/src/loginrefresh/main.js +58 -0
- package/dist/src/loginrefresh/main.js.map +1 -0
- package/dist/src/loginrefresh/pirefresh.js +49 -0
- package/dist/src/loginrefresh/pirefresh.js.map +1 -0
- package/dist/src/loginrefresh/refresher.js +286 -0
- package/dist/src/loginrefresh/refresher.js.map +1 -0
- package/dist/src/loginrefresh/supervisor.js +253 -0
- package/dist/src/loginrefresh/supervisor.js.map +1 -0
- package/dist/src/loginrefresh/types.js +4 -0
- package/dist/src/loginrefresh/types.js.map +1 -0
- package/dist/src/memory/context.js +251 -0
- package/dist/src/memory/context.js.map +1 -0
- package/dist/src/memory/datafence.js +5 -0
- package/dist/src/memory/datafence.js.map +1 -0
- package/dist/src/memory/delivery.js +131 -0
- package/dist/src/memory/delivery.js.map +1 -0
- package/dist/src/memory/eventtags.js +81 -0
- package/dist/src/memory/eventtags.js.map +1 -0
- package/dist/src/memory/health.js +713 -0
- package/dist/src/memory/health.js.map +1 -0
- package/dist/src/memory/import.js +504 -0
- package/dist/src/memory/import.js.map +1 -0
- package/dist/src/memory/layout.js +252 -0
- package/dist/src/memory/layout.js.map +1 -0
- package/dist/src/memory/legacy-file.js +100 -0
- package/dist/src/memory/legacy-file.js.map +1 -0
- package/dist/src/memory/lessons.js +370 -0
- package/dist/src/memory/lessons.js.map +1 -0
- package/dist/src/memory/ovsession.js +449 -0
- package/dist/src/memory/ovsession.js.map +1 -0
- package/dist/src/memory/relayout.js +259 -0
- package/dist/src/memory/relayout.js.map +1 -0
- package/dist/src/memory/roundactivity.js +180 -0
- package/dist/src/memory/roundactivity.js.map +1 -0
- package/dist/src/memory/selection.js +690 -0
- package/dist/src/memory/selection.js.map +1 -0
- package/dist/src/memory/shadowstatus.js +69 -0
- package/dist/src/memory/shadowstatus.js.map +1 -0
- package/dist/src/memory/store.js +83 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/memory/textidentity.js +8 -0
- package/dist/src/memory/textidentity.js.map +1 -0
- package/dist/src/metrics.js +773 -0
- package/dist/src/metrics.js.map +1 -0
- package/dist/src/missingconfig.js +8 -0
- package/dist/src/missingconfig.js.map +1 -0
- package/dist/src/npmverbs.js +226 -0
- package/dist/src/npmverbs.js.map +1 -0
- package/dist/src/onboard/choices.js +191 -0
- package/dist/src/onboard/choices.js.map +1 -0
- package/dist/src/onboard/engine.js +344 -0
- package/dist/src/onboard/engine.js.map +1 -0
- package/dist/src/onboard/launch.js +83 -0
- package/dist/src/onboard/launch.js.map +1 -0
- package/dist/src/onboard/steps/appslug.js +211 -0
- package/dist/src/onboard/steps/appslug.js.map +1 -0
- package/dist/src/onboard/steps/bringup.js +408 -0
- package/dist/src/onboard/steps/bringup.js.map +1 -0
- package/dist/src/onboard/steps/compose.js +343 -0
- package/dist/src/onboard/steps/compose.js.map +1 -0
- package/dist/src/onboard/steps/configdelivery.js +50 -0
- package/dist/src/onboard/steps/configdelivery.js.map +1 -0
- package/dist/src/onboard/steps/configfill.js +1148 -0
- package/dist/src/onboard/steps/configfill.js.map +1 -0
- package/dist/src/onboard/steps/deploysh.js +80 -0
- package/dist/src/onboard/steps/deploysh.js.map +1 -0
- package/dist/src/onboard/steps/embeddings.js +472 -0
- package/dist/src/onboard/steps/embeddings.js.map +1 -0
- package/dist/src/onboard/steps/escalation.js +571 -0
- package/dist/src/onboard/steps/escalation.js.map +1 -0
- package/dist/src/onboard/steps/githubapp.js +1092 -0
- package/dist/src/onboard/steps/githubapp.js.map +1 -0
- package/dist/src/onboard/steps/golive.js +506 -0
- package/dist/src/onboard/steps/golive.js.map +1 -0
- package/dist/src/onboard/steps/index.js +67 -0
- package/dist/src/onboard/steps/index.js.map +1 -0
- package/dist/src/onboard/steps/ingress.js +762 -0
- package/dist/src/onboard/steps/ingress.js.map +1 -0
- package/dist/src/onboard/steps/inventory.js +420 -0
- package/dist/src/onboard/steps/inventory.js.map +1 -0
- package/dist/src/onboard/steps/llmauth.js +893 -0
- package/dist/src/onboard/steps/llmauth.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +1060 -0
- package/dist/src/onboard/steps/memory.js.map +1 -0
- package/dist/src/onboard/steps/memorykeys.js +43 -0
- package/dist/src/onboard/steps/memorykeys.js.map +1 -0
- package/dist/src/onboard/steps/preflight.js +172 -0
- package/dist/src/onboard/steps/preflight.js.map +1 -0
- package/dist/src/onboard/steps/proof.js +502 -0
- package/dist/src/onboard/steps/proof.js.map +1 -0
- package/dist/src/onboard/steps/ssh.js +300 -0
- package/dist/src/onboard/steps/ssh.js.map +1 -0
- package/dist/src/onboard/steps/topology.js +291 -0
- package/dist/src/onboard/steps/topology.js.map +1 -0
- package/dist/src/onboard/steps/transport.js +511 -0
- package/dist/src/onboard/steps/transport.js.map +1 -0
- package/dist/src/onboard/steps/verifyup.js +362 -0
- package/dist/src/onboard/steps/verifyup.js.map +1 -0
- package/dist/src/onboard/steps/vmdeploy.js +368 -0
- package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
- package/dist/src/onboard/toml.js +53 -0
- package/dist/src/onboard/toml.js.map +1 -0
- package/dist/src/opencli.js +508 -0
- package/dist/src/opencli.js.map +1 -0
- package/dist/src/openpr.js +457 -0
- package/dist/src/openpr.js.map +1 -0
- package/dist/src/openviking/client.js +821 -0
- package/dist/src/openviking/client.js.map +1 -0
- package/dist/src/openviking/context.js +218 -0
- package/dist/src/openviking/context.js.map +1 -0
- package/dist/src/openviking/embedder.js +457 -0
- package/dist/src/openviking/embedder.js.map +1 -0
- package/dist/src/openviking/gapsink.js +38 -0
- package/dist/src/openviking/gapsink.js.map +1 -0
- package/dist/src/openviking/health.js +69 -0
- package/dist/src/openviking/health.js.map +1 -0
- package/dist/src/openviking/index.js +24 -0
- package/dist/src/openviking/index.js.map +1 -0
- package/dist/src/openviking/lessonfind.js +54 -0
- package/dist/src/openviking/lessonfind.js.map +1 -0
- package/dist/src/openviking/probe.js +89 -0
- package/dist/src/openviking/probe.js.map +1 -0
- package/dist/src/openviking/proc.js +95 -0
- package/dist/src/openviking/proc.js.map +1 -0
- package/dist/src/openviking/provisionreport.js +144 -0
- package/dist/src/openviking/provisionreport.js.map +1 -0
- package/dist/src/openviking/reembed.js +317 -0
- package/dist/src/openviking/reembed.js.map +1 -0
- package/dist/src/openviking/relayout.js +153 -0
- package/dist/src/openviking/relayout.js.map +1 -0
- package/dist/src/openviking/sidecar-main.js +39 -0
- package/dist/src/openviking/sidecar-main.js.map +1 -0
- package/dist/src/openviking/sidecar.js +79 -0
- package/dist/src/openviking/sidecar.js.map +1 -0
- package/dist/src/openviking/store.js +1758 -0
- package/dist/src/openviking/store.js.map +1 -0
- package/dist/src/openviking/supervisor.js +422 -0
- package/dist/src/openviking/supervisor.js.map +1 -0
- package/dist/src/openviking/types.js +5 -0
- package/dist/src/openviking/types.js.map +1 -0
- package/dist/src/operator-recovery.js +172 -0
- package/dist/src/operator-recovery.js.map +1 -0
- package/dist/src/operatormessage.js +44 -0
- package/dist/src/operatormessage.js.map +1 -0
- package/dist/src/outcomes.js +1361 -0
- package/dist/src/outcomes.js.map +1 -0
- package/dist/src/piauthcopy.js +5 -0
- package/dist/src/piauthcopy.js.map +1 -0
- package/dist/src/picli.js +45 -0
- package/dist/src/picli.js.map +1 -0
- package/dist/src/picwd.js +25 -0
- package/dist/src/picwd.js.map +1 -0
- package/dist/src/pilogin.js +35 -0
- package/dist/src/pilogin.js.map +1 -0
- package/dist/src/pingpong.js +1028 -0
- package/dist/src/pingpong.js.map +1 -0
- package/dist/src/posting-journal.js +441 -0
- package/dist/src/posting-journal.js.map +1 -0
- package/dist/src/posting-outbox.js +662 -0
- package/dist/src/posting-outbox.js.map +1 -0
- package/dist/src/posting-receipt.js +203 -0
- package/dist/src/posting-receipt.js.map +1 -0
- package/dist/src/posting.js +348 -0
- package/dist/src/posting.js.map +1 -0
- package/dist/src/postops.js +637 -0
- package/dist/src/postops.js.map +1 -0
- package/dist/src/pr-activity.js +11 -0
- package/dist/src/pr-activity.js.map +1 -0
- package/dist/src/pr-author.js +5 -0
- package/dist/src/pr-author.js.map +1 -0
- package/dist/src/pr-conversation.js +355 -0
- package/dist/src/pr-conversation.js.map +1 -0
- package/dist/src/pr-session-lifecycle.js +542 -0
- package/dist/src/pr-session-lifecycle.js.map +1 -0
- package/dist/src/preflight.js +341 -0
- package/dist/src/preflight.js.map +1 -0
- package/dist/src/priorresolve.js +275 -0
- package/dist/src/priorresolve.js.map +1 -0
- package/dist/src/qcrbin.js +180 -0
- package/dist/src/qcrbin.js.map +1 -0
- package/dist/src/recheck.js +1398 -0
- package/dist/src/recheck.js.map +1 -0
- package/dist/src/reconcile.js +820 -0
- package/dist/src/reconcile.js.map +1 -0
- package/dist/src/recordedfindings.js +15 -0
- package/dist/src/recordedfindings.js.map +1 -0
- package/dist/src/regression.js +546 -0
- package/dist/src/regression.js.map +1 -0
- package/dist/src/replay.js +598 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/requiredpasses.js +113 -0
- package/dist/src/requiredpasses.js.map +1 -0
- package/dist/src/resumecheck.js +168 -0
- package/dist/src/resumecheck.js.map +1 -0
- package/dist/src/reviewcli.js +671 -0
- package/dist/src/reviewcli.js.map +1 -0
- package/dist/src/reviewcontext.js +22 -0
- package/dist/src/reviewcontext.js.map +1 -0
- package/dist/src/reviewdetach.js +574 -0
- package/dist/src/reviewdetach.js.map +1 -0
- package/dist/src/reviewhistory.js +284 -0
- package/dist/src/reviewhistory.js.map +1 -0
- package/dist/src/reviewhome.js +26 -0
- package/dist/src/reviewhome.js.map +1 -0
- package/dist/src/reviewmemory.js +110 -0
- package/dist/src/reviewmemory.js.map +1 -0
- package/dist/src/reviewrendercli.js +104 -0
- package/dist/src/reviewrendercli.js.map +1 -0
- package/dist/src/round-work.js +20 -0
- package/dist/src/round-work.js.map +1 -0
- package/dist/src/runhost.js +72 -0
- package/dist/src/runhost.js.map +1 -0
- package/dist/src/scanauth.js +25 -0
- package/dist/src/scanauth.js.map +1 -0
- package/dist/src/scanbox.js +846 -0
- package/dist/src/scanbox.js.map +1 -0
- package/dist/src/scancli.js +1074 -0
- package/dist/src/scancli.js.map +1 -0
- package/dist/src/scandriverlog.js +30 -0
- package/dist/src/scandriverlog.js.map +1 -0
- package/dist/src/session-bridge.js +75 -0
- package/dist/src/session-bridge.js.map +1 -0
- package/dist/src/session-command-payload.js +29 -0
- package/dist/src/session-command-payload.js.map +1 -0
- package/dist/src/session-command-supervisor.js +939 -0
- package/dist/src/session-command-supervisor.js.map +1 -0
- package/dist/src/sessionjsonl.js +85 -0
- package/dist/src/sessionjsonl.js.map +1 -0
- package/dist/src/sessions.js +541 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/setup/adapters.js +330 -0
- package/dist/src/setup/adapters.js.map +1 -0
- package/dist/src/setup/baselines.js +64 -0
- package/dist/src/setup/baselines.js.map +1 -0
- package/dist/src/setup/caserunner.js +554 -0
- package/dist/src/setup/caserunner.js.map +1 -0
- package/dist/src/setup/configcommit.js +358 -0
- package/dist/src/setup/configcommit.js.map +1 -0
- package/dist/src/setup/engine.js +672 -0
- package/dist/src/setup/engine.js.map +1 -0
- package/dist/src/setup/facts.js +593 -0
- package/dist/src/setup/facts.js.map +1 -0
- package/dist/src/setup/findings.js +948 -0
- package/dist/src/setup/findings.js.map +1 -0
- package/dist/src/setup/history.js +338 -0
- package/dist/src/setup/history.js.map +1 -0
- package/dist/src/setup/interview.js +185 -0
- package/dist/src/setup/interview.js.map +1 -0
- package/dist/src/setup/ledger.js +207 -0
- package/dist/src/setup/ledger.js.map +1 -0
- package/dist/src/setup/pins.js +43 -0
- package/dist/src/setup/pins.js.map +1 -0
- package/dist/src/setup/portrait.js +432 -0
- package/dist/src/setup/portrait.js.map +1 -0
- package/dist/src/setup/presentation.js +9 -0
- package/dist/src/setup/presentation.js.map +1 -0
- package/dist/src/setup/profile.js +417 -0
- package/dist/src/setup/profile.js.map +1 -0
- package/dist/src/setup/replay.js +240 -0
- package/dist/src/setup/replay.js.map +1 -0
- package/dist/src/setup/replaybuild.js +311 -0
- package/dist/src/setup/replaybuild.js.map +1 -0
- package/dist/src/setup/replayrun.js +806 -0
- package/dist/src/setup/replayrun.js.map +1 -0
- package/dist/src/setup/replaysource.js +169 -0
- package/dist/src/setup/replaysource.js.map +1 -0
- package/dist/src/setup/retune.js +333 -0
- package/dist/src/setup/retune.js.map +1 -0
- package/dist/src/setup/retunelog.js +51 -0
- package/dist/src/setup/retunelog.js.map +1 -0
- package/dist/src/setup/scan.js +725 -0
- package/dist/src/setup/scan.js.map +1 -0
- package/dist/src/setup/scanbuild.js +133 -0
- package/dist/src/setup/scanbuild.js.map +1 -0
- package/dist/src/setup/scanfacts.js +96 -0
- package/dist/src/setup/scanfacts.js.map +1 -0
- package/dist/src/setup/scanlease.js +251 -0
- package/dist/src/setup/scanlease.js.map +1 -0
- package/dist/src/setup/scanreport.js +289 -0
- package/dist/src/setup/scanreport.js.map +1 -0
- package/dist/src/setup/scanrun.js +1733 -0
- package/dist/src/setup/scanrun.js.map +1 -0
- package/dist/src/setup/scantree.js +257 -0
- package/dist/src/setup/scantree.js.map +1 -0
- package/dist/src/setup/stage2.js +617 -0
- package/dist/src/setup/stage2.js.map +1 -0
- package/dist/src/setup/stage2request.js +259 -0
- package/dist/src/setup/stage2request.js.map +1 -0
- package/dist/src/setup/survey.js +307 -0
- package/dist/src/setup/survey.js.map +1 -0
- package/dist/src/setup/surveyrun.js +549 -0
- package/dist/src/setup/surveyrun.js.map +1 -0
- package/dist/src/setup/textsniff.js +89 -0
- package/dist/src/setup/textsniff.js.map +1 -0
- package/dist/src/setup/trackersink.js +395 -0
- package/dist/src/setup/trackersink.js.map +1 -0
- package/dist/src/setup/triage.js +636 -0
- package/dist/src/setup/triage.js.map +1 -0
- package/dist/src/setup/tuning.js +585 -0
- package/dist/src/setup/tuning.js.map +1 -0
- package/dist/src/setup/unitcut.js +437 -0
- package/dist/src/setup/unitcut.js.map +1 -0
- package/dist/src/singleflight.js +72 -0
- package/dist/src/singleflight.js.map +1 -0
- package/dist/src/slack/announce.js +725 -0
- package/dist/src/slack/announce.js.map +1 -0
- package/dist/src/slack/api.js +227 -0
- package/dist/src/slack/api.js.map +1 -0
- package/dist/src/slack/messages.js +158 -0
- package/dist/src/slack/messages.js.map +1 -0
- package/dist/src/slack/origin.js +550 -0
- package/dist/src/slack/origin.js.map +1 -0
- package/dist/src/slack/policy.js +225 -0
- package/dist/src/slack/policy.js.map +1 -0
- package/dist/src/slack/reply.js +153 -0
- package/dist/src/slack/reply.js.map +1 -0
- package/dist/src/slack/router.js +189 -0
- package/dist/src/slack/router.js.map +1 -0
- package/dist/src/slack/socket.js +323 -0
- package/dist/src/slack/socket.js.map +1 -0
- package/dist/src/slack/types.js +11 -0
- package/dist/src/slack/types.js.map +1 -0
- package/dist/src/sqlitehint.js +30 -0
- package/dist/src/sqlitehint.js.map +1 -0
- package/dist/src/ssh-observation.js +25 -0
- package/dist/src/ssh-observation.js.map +1 -0
- package/dist/src/state.js +5869 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/statemachine.js +220 -0
- package/dist/src/statemachine.js.map +1 -0
- package/dist/src/stubs.js +52 -0
- package/dist/src/stubs.js.map +1 -0
- package/dist/src/takeover.js +506 -0
- package/dist/src/takeover.js.map +1 -0
- package/dist/src/takeoverpi.js +249 -0
- package/dist/src/takeoverpi.js.map +1 -0
- package/dist/src/terminal-failure.js +26 -0
- package/dist/src/terminal-failure.js.map +1 -0
- package/dist/src/thread-resolution-refusal.js +4 -0
- package/dist/src/thread-resolution-refusal.js.map +1 -0
- package/dist/src/tmux.js +126 -0
- package/dist/src/tmux.js.map +1 -0
- package/dist/src/trackerid.js +43 -0
- package/dist/src/trackerid.js.map +1 -0
- package/dist/src/transcriptindex.js +512 -0
- package/dist/src/transcriptindex.js.map +1 -0
- package/dist/src/tui/actions.js +404 -0
- package/dist/src/tui/actions.js.map +1 -0
- package/dist/src/tui/alerts.js +523 -0
- package/dist/src/tui/alerts.js.map +1 -0
- package/dist/src/tui/app.js +1835 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/archiveops.js +164 -0
- package/dist/src/tui/archiveops.js.map +1 -0
- package/dist/src/tui/autofile.js +191 -0
- package/dist/src/tui/autofile.js.map +1 -0
- package/dist/src/tui/board.js +1143 -0
- package/dist/src/tui/board.js.map +1 -0
- package/dist/src/tui/candidates.js +699 -0
- package/dist/src/tui/candidates.js.map +1 -0
- package/dist/src/tui/cellwidth.js +151 -0
- package/dist/src/tui/cellwidth.js.map +1 -0
- package/dist/src/tui/changelog-store.js +404 -0
- package/dist/src/tui/changelog-store.js.map +1 -0
- package/dist/src/tui/chord.js +26 -0
- package/dist/src/tui/chord.js.map +1 -0
- package/dist/src/tui/clipboard.js +81 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/cmdbar.js +253 -0
- package/dist/src/tui/cmdbar.js.map +1 -0
- package/dist/src/tui/companion.js +1275 -0
- package/dist/src/tui/companion.js.map +1 -0
- package/dist/src/tui/companionplan.js +200 -0
- package/dist/src/tui/companionplan.js.map +1 -0
- package/dist/src/tui/companiontab.js +61 -0
- package/dist/src/tui/companiontab.js.map +1 -0
- package/dist/src/tui/confighome.js +105 -0
- package/dist/src/tui/confighome.js.map +1 -0
- package/dist/src/tui/connect.js +44 -0
- package/dist/src/tui/connect.js.map +1 -0
- package/dist/src/tui/deploy-ceremony.js +75 -0
- package/dist/src/tui/deploy-ceremony.js.map +1 -0
- package/dist/src/tui/deploy-drain.js +93 -0
- package/dist/src/tui/deploy-drain.js.map +1 -0
- package/dist/src/tui/deploy-process.js +234 -0
- package/dist/src/tui/deploy-process.js.map +1 -0
- package/dist/src/tui/deploytab.js +1263 -0
- package/dist/src/tui/deploytab.js.map +1 -0
- package/dist/src/tui/design.js +111 -0
- package/dist/src/tui/design.js.map +1 -0
- package/dist/src/tui/digesttab.js +148 -0
- package/dist/src/tui/digesttab.js.map +1 -0
- package/dist/src/tui/emptystate.js +32 -0
- package/dist/src/tui/emptystate.js.map +1 -0
- package/dist/src/tui/evidence.js +790 -0
- package/dist/src/tui/evidence.js.map +1 -0
- package/dist/src/tui/feed.js +415 -0
- package/dist/src/tui/feed.js.map +1 -0
- package/dist/src/tui/feedbacktab.js +907 -0
- package/dist/src/tui/feedbacktab.js.map +1 -0
- package/dist/src/tui/filters.js +72 -0
- package/dist/src/tui/filters.js.map +1 -0
- package/dist/src/tui/ghread.js +272 -0
- package/dist/src/tui/ghread.js.map +1 -0
- package/dist/src/tui/historypull.js +61 -0
- package/dist/src/tui/historypull.js.map +1 -0
- package/dist/src/tui/inboxpull.js +197 -0
- package/dist/src/tui/inboxpull.js.map +1 -0
- package/dist/src/tui/index.js +1425 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/issuesink.js +206 -0
- package/dist/src/tui/issuesink.js.map +1 -0
- package/dist/src/tui/jsonfile.js +32 -0
- package/dist/src/tui/jsonfile.js.map +1 -0
- package/dist/src/tui/keys.js +97 -0
- package/dist/src/tui/keys.js.map +1 -0
- package/dist/src/tui/layout.js +70 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/learning.js +469 -0
- package/dist/src/tui/learning.js.map +1 -0
- package/dist/src/tui/lessonswitch.js +103 -0
- package/dist/src/tui/lessonswitch.js.map +1 -0
- package/dist/src/tui/livecontrol.js +167 -0
- package/dist/src/tui/livecontrol.js.map +1 -0
- package/dist/src/tui/memforward.js +310 -0
- package/dist/src/tui/memforward.js.map +1 -0
- package/dist/src/tui/memincident.js +287 -0
- package/dist/src/tui/memincident.js.map +1 -0
- package/dist/src/tui/memory.js +485 -0
- package/dist/src/tui/memory.js.map +1 -0
- package/dist/src/tui/memtab.js +513 -0
- package/dist/src/tui/memtab.js.map +1 -0
- package/dist/src/tui/metricstab.js +979 -0
- package/dist/src/tui/metricstab.js.map +1 -0
- package/dist/src/tui/model.js +104 -0
- package/dist/src/tui/model.js.map +1 -0
- package/dist/src/tui/mouse.js +83 -0
- package/dist/src/tui/mouse.js.map +1 -0
- package/dist/src/tui/msg.js +196 -0
- package/dist/src/tui/msg.js.map +1 -0
- package/dist/src/tui/nativecompanion.js +321 -0
- package/dist/src/tui/nativecompanion.js.map +1 -0
- package/dist/src/tui/openurl.js +17 -0
- package/dist/src/tui/openurl.js.map +1 -0
- package/dist/src/tui/paint.js +501 -0
- package/dist/src/tui/paint.js.map +1 -0
- package/dist/src/tui/panes.js +74 -0
- package/dist/src/tui/panes.js.map +1 -0
- package/dist/src/tui/pipane.js +166 -0
- package/dist/src/tui/pipane.js.map +1 -0
- package/dist/src/tui/poll-hooks.js +11 -0
- package/dist/src/tui/poll-hooks.js.map +1 -0
- package/dist/src/tui/poll.js +113 -0
- package/dist/src/tui/poll.js.map +1 -0
- package/dist/src/tui/proposals.js +715 -0
- package/dist/src/tui/proposals.js.map +1 -0
- package/dist/src/tui/prpause.js +132 -0
- package/dist/src/tui/prpause.js.map +1 -0
- package/dist/src/tui/reload.js +351 -0
- package/dist/src/tui/reload.js.map +1 -0
- package/dist/src/tui/remote.js +312 -0
- package/dist/src/tui/remote.js.map +1 -0
- package/dist/src/tui/rollover.js +767 -0
- package/dist/src/tui/rollover.js.map +1 -0
- package/dist/src/tui/scantab.js +1053 -0
- package/dist/src/tui/scantab.js.map +1 -0
- package/dist/src/tui/setupentry.js +81 -0
- package/dist/src/tui/setupentry.js.map +1 -0
- package/dist/src/tui/sidebar.js +602 -0
- package/dist/src/tui/sidebar.js.map +1 -0
- package/dist/src/tui/sshtriage.js +393 -0
- package/dist/src/tui/sshtriage.js.map +1 -0
- package/dist/src/tui/startup.js +172 -0
- package/dist/src/tui/startup.js.map +1 -0
- package/dist/src/tui/tabs.js +41 -0
- package/dist/src/tui/tabs.js.map +1 -0
- package/dist/src/tui/termmirror.js +217 -0
- package/dist/src/tui/termmirror.js.map +1 -0
- package/dist/src/tui/theme.js +95 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/ticketstab.js +224 -0
- package/dist/src/tui/ticketstab.js.map +1 -0
- package/dist/src/tui/tmuxsocket.js +101 -0
- package/dist/src/tui/tmuxsocket.js.map +1 -0
- package/dist/src/tui/trackertickets.js +77 -0
- package/dist/src/tui/trackertickets.js.map +1 -0
- package/dist/src/tui/unwedge.js +245 -0
- package/dist/src/tui/unwedge.js.map +1 -0
- package/dist/src/tui/viewer.js +479 -0
- package/dist/src/tui/viewer.js.map +1 -0
- package/dist/src/tui/viewerinput.js +125 -0
- package/dist/src/tui/viewerinput.js.map +1 -0
- package/dist/src/updatenotice.js +143 -0
- package/dist/src/updatenotice.js.map +1 -0
- package/dist/src/usage.js +1007 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/src/verdicts.js +3848 -0
- package/dist/src/verdicts.js.map +1 -0
- package/dist/src/version.js +58 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/wakes.js +411 -0
- package/dist/src/wakes.js.map +1 -0
- package/dist/src/webhook.js +725 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/webhookheal.js +238 -0
- package/dist/src/webhookheal.js.map +1 -0
- package/dist/src/wire.js +1801 -0
- package/dist/src/wire.js.map +1 -0
- package/dist/src/worker-session-host.js +531 -0
- package/dist/src/worker-session-host.js.map +1 -0
- package/dist/src/workerbreaker.js +112 -0
- package/dist/src/workerbreaker.js.map +1 -0
- package/dist/src/workercontrol.js +49 -0
- package/dist/src/workercontrol.js.map +1 -0
- package/dist/src/workerlease.js +351 -0
- package/dist/src/workerlease.js.map +1 -0
- package/dist/src/workerliveness.js +84 -0
- package/dist/src/workerliveness.js.map +1 -0
- package/dist/src/workerlog.js +65 -0
- package/dist/src/workerlog.js.map +1 -0
- package/dist/src/workerruntime.js +10 -0
- package/dist/src/workerruntime.js.map +1 -0
- package/dist/src/workersupervisor.js +1206 -0
- package/dist/src/workersupervisor.js.map +1 -0
- package/dist/src/workerusage.js +23 -0
- package/dist/src/workerusage.js.map +1 -0
- package/dist/src/workerworld.js +332 -0
- package/dist/src/workerworld.js.map +1 -0
- package/dist/src/worktrees.js +984 -0
- package/dist/src/worktrees.js.map +1 -0
- package/docker/Dockerfile +284 -0
- package/docker/Dockerfile.dockerignore +35 -0
- package/docker/README.md +221 -0
- package/docker/companion/Dockerfile +139 -0
- package/docker/companion/agents/evidence.md +34 -0
- package/docker/companion/agents/explore.md +26 -0
- package/docker/companion/agents/pass-survey.md +49 -0
- package/docker/companion/agents/study.md +26 -0
- package/docker/companion/entrypoint.sh +343 -0
- package/docker/companion/memory-health.mjs +269 -0
- package/docker/entrypoint.sh +782 -0
- package/docker/memory/Dockerfile +191 -0
- package/docker/memory/Dockerfile.dockerignore +20 -0
- package/docker/memory/README.md +60 -0
- package/docker/memory/entrypoint.sh +163 -0
- package/docker/setid/qcr-setid.c +109 -0
- package/docker/tunnel/Dockerfile.cloudflared +51 -0
- package/docker/tunnel/Dockerfile.ngrok +40 -0
- package/docker/tunnel/sidecar.mjs +411 -0
- package/docker/vm/tmux.conf +26 -0
- package/docker-compose.yml +348 -0
- package/extensions/cli/bash-guard-ambient.ts +71 -0
- package/extensions/cli/bash-guard.ts +12 -0
- package/extensions/cli/companion-guard.ts +8 -0
- package/extensions/cli/debug-round.ts +9 -0
- package/extensions/cli/inject-bridge.ts +8 -0
- package/extensions/cli/ledger-pin.ts +13 -0
- package/extensions/cli/lens.ts +22 -0
- package/extensions/cli/muted-theme.ts +9 -0
- package/extensions/cli/onboarding.ts +8 -0
- package/extensions/cli/setup.ts +8 -0
- package/extensions/cli/tui-view.ts +8 -0
- package/extensions/companion-guard.ts +411 -0
- package/extensions/debug-round.ts +281 -0
- package/extensions/inject-bridge.ts +630 -0
- package/extensions/inline-bash.ts +102 -0
- package/extensions/muted-theme.json +68 -0
- package/extensions/muted-theme.ts +23 -0
- package/extensions/onboarding.ts +233 -0
- package/extensions/setup-replay.ts +336 -0
- package/extensions/setup-scan.ts +1236 -0
- package/extensions/setup-triage.ts +190 -0
- package/extensions/setup.ts +1428 -0
- package/extensions/tui-view.ts +489 -0
- package/extensions/vendor/openviking-pi/LICENSE +201 -0
- package/extensions/vendor/openviking-pi/NOTICE +68 -0
- package/extensions/vendor/openviking-pi/README.md +550 -0
- package/extensions/vendor/openviking-pi/client.ts +588 -0
- package/extensions/vendor/openviking-pi/config.json +28 -0
- package/extensions/vendor/openviking-pi/config.ts +271 -0
- package/extensions/vendor/openviking-pi/index.ts +523 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
- package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
- package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
- package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
- package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
- package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
- package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
- package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
- package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
- package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
- package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
- package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
- package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
- package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
- package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
- package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
- package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
- package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
- package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
- package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
- package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
- package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
- package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
- package/extensions/vendor/openviking-pi/recall.ts +149 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
- package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
- package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
- package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
- package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
- package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
- package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
- package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/extensions/vendor/openviking-pi/sync.ts +163 -0
- package/extensions/vendor/openviking-pi/takeover.ts +36 -0
- package/extensions/vendor/openviking-pi/tools.ts +479 -0
- package/extensions/vendor/openviking-pi/upstream.json +48 -0
- package/package.json +138 -4
- package/profiles/default/README.md +21 -0
- package/profiles/default/addenda/.gitkeep +0 -0
- package/profiles/default/profile.toml +17 -0
- package/profiles/example-django/README.md +21 -0
- package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
- package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
- package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
- package/profiles/example-django/addenda/40-orm-usage.md +19 -0
- package/profiles/example-django/addenda/50-declared-shape.md +10 -0
- package/profiles/example-django/addenda/60-config-placement.md +14 -0
- package/profiles/example-django/heuristics.toml +24 -0
- package/profiles/example-django/profile.toml +9 -0
- package/profiles/fast/README.md +24 -0
- package/profiles/fast/profile.toml +59 -0
- package/profiles/minimal/README.md +17 -0
- package/profiles/minimal/addenda/.gitkeep +0 -0
- package/profiles/minimal/profile.toml +8 -0
- package/profiles/snappy/README.md +24 -0
- package/profiles/snappy/addenda/10-race-as-question.md +6 -0
- package/profiles/snappy/profile.toml +46 -0
- package/prompts/companion-day-open.md +46 -0
- package/prompts/companion-first-run.md +104 -0
- package/prompts/companion.md +821 -0
- package/prompts/conversation-system.md +117 -0
- package/prompts/eod-sweep.md +120 -0
- package/prompts/onboarding.md +74 -0
- package/prompts/pass-common.md +52 -0
- package/prompts/pass-quality-common.md +22 -0
- package/prompts/round-first.md +874 -0
- package/prompts/round-follow-up.md +991 -0
- package/prompts/round-re-review.md +889 -0
- package/prompts/round-recheck.md +196 -0
- package/prompts/round-staleness.md +856 -0
- package/schemas/review-result.v1.json +391 -0
- package/schemas/scan-result.v1.json +421 -0
- package/schemas/version.v1.json +57 -0
- package/scripts/approve-pr.sh +16 -0
- package/scripts/build-stamp.mjs +67 -0
- package/scripts/commit-verdict.sh +155 -0
- package/scripts/companion-e2e.sh +250 -0
- package/scripts/companion-memory-forward.mjs +1966 -0
- package/scripts/config-fingerprint.sh +28 -0
- package/scripts/deploy-manifest.mjs +141 -0
- package/scripts/deploy.sh +1435 -0
- package/scripts/directory-lock.mjs +193 -0
- package/scripts/heartbeat-fresh.mjs +54 -0
- package/scripts/install-locator.sh +328 -0
- package/scripts/install-wrapper.sh +76 -0
- package/scripts/memory-search-check.mjs +98 -0
- package/scripts/mint-token.sh +212 -0
- package/scripts/openviking-embedder.py +392 -0
- package/scripts/openviking-ov-conf.mjs +161 -0
- package/scripts/openviking-provision.mjs +1122 -0
- package/scripts/openviking-reembed.mjs +1154 -0
- package/scripts/post-review.sh +16 -0
- package/scripts/pr-checks.sh +256 -0
- package/scripts/pr-context.py +174 -0
- package/scripts/pr-context.sh +63 -0
- package/scripts/pr-self.sh +144 -0
- package/scripts/pr-threads.sh +145 -0
- package/scripts/qcr +15 -0
- package/scripts/qcr-host.sh +769 -0
- package/scripts/render-review.sh +24 -0
- package/scripts/reply-thread.sh +16 -0
- package/scripts/required-passes.py +112 -0
- package/scripts/smoke-companion.sh +211 -0
- package/scripts/tui-deploy-runner.mjs +61 -0
- package/scripts/verify-dist.mjs +201 -0
- package/scripts/vm-ghcr-login.sh +188 -0
- package/seeds/agents/pass-baseline.md +61 -0
- package/seeds/agents/pass-contracts.md +30 -0
- package/seeds/agents/pass-data-safety.md +54 -0
- package/seeds/agents/pass-delivery.md +37 -0
- package/seeds/agents/pass-prod-risk.md +24 -0
- package/seeds/agents/pass-security.md +31 -0
- package/seeds/agents/pass-statelessness.md +27 -0
- package/seeds/agents/pass-ui.md +23 -0
- package/seeds/agents/pass-validate.md +50 -0
- package/seeds/prompts/conversation-system.md +117 -0
- package/seeds/prompts/eod-sweep.md +120 -0
- package/seeds/prompts/pass-common.md +52 -0
- package/skills/companion/adjudicate/SKILL.md +247 -0
- package/skills/companion/evidence/SKILL.md +150 -0
- package/skills/companion/land/SKILL.md +200 -0
- package/skills/companion/replay/SKILL.md +138 -0
- package/skills/companion/setup/SKILL.md +167 -0
- package/skills/companion/survey/SKILL.md +160 -0
- package/skills/selfimprove/README.md +34 -0
- package/skills/selfimprove/SKILL.md +127 -0
- package/skills/selfimprove/agents/openai.yaml +2 -0
- package/skills/selfimprove/commands/selfimprove.md +6 -0
- package/skills/selfimprove/references/fix-loop.md +49 -0
- package/skills/selfimprove/references/ledger.md +20 -0
- package/skills/selfimprove/references/setup.md +40 -0
- package/src/anomalies.ts +566 -0
- package/src/answerworker.ts +251 -0
- package/src/appstamp.ts +65 -0
- package/src/archive/admission.ts +3 -0
- package/src/archive/archivedb.ts +548 -0
- package/src/archive/archiver.ts +1963 -0
- package/src/archive/archivewrite.ts +279 -0
- package/src/archive/bundle.ts +333 -0
- package/src/archive/dbmove.ts +911 -0
- package/src/archive/duplicatecounts.ts +98 -0
- package/src/archive/eligibility.ts +181 -0
- package/src/archive/extract.ts +342 -0
- package/src/archive/fsutil.ts +415 -0
- package/src/archive/gzfile.ts +370 -0
- package/src/archive/kinds.ts +21 -0
- package/src/archive/livecheckpoint.ts +217 -0
- package/src/archive/liveretention.ts +180 -0
- package/src/archive/manifest.ts +519 -0
- package/src/archive/overseer.ts +336 -0
- package/src/archive/piglobal.ts +174 -0
- package/src/archive/plan.ts +612 -0
- package/src/archive/prarchive.ts +984 -0
- package/src/archive/prfiles.ts +406 -0
- package/src/archive/report.ts +252 -0
- package/src/archive/restore.ts +330 -0
- package/src/archive/rows.ts +187 -0
- package/src/archive/seal.ts +630 -0
- package/src/archive/status.ts +719 -0
- package/src/archive/storepass.ts +581 -0
- package/src/backfill.ts +362 -0
- package/src/bench/cost.ts +113 -0
- package/src/bench/exec.ts +90 -0
- package/src/bench/values.ts +13 -0
- package/src/botcontrol.ts +176 -0
- package/src/botworker.ts +953 -0
- package/src/capture.ts +321 -0
- package/src/cases.ts +1789 -0
- package/src/channelfs.ts +26 -0
- package/src/checklist.ts +506 -0
- package/src/ciretry.ts +141 -0
- package/src/cli.ts +2271 -0
- package/src/cliops.ts +384 -0
- package/src/clonedir.ts +15 -0
- package/src/commandeffects.ts +57 -0
- package/src/commands.ts +1678 -0
- package/src/companioninbox.ts +239 -0
- package/src/compare.ts +957 -0
- package/src/config.ts +2050 -0
- package/src/configcheck.ts +133 -0
- package/src/coreext.ts +47 -0
- package/src/daemon.ts +1302 -0
- package/src/debuground.ts +927 -0
- package/src/debugroundpi.ts +262 -0
- package/src/diagnostics.ts +14 -0
- package/src/digest.ts +183 -0
- package/src/dispatch.ts +2250 -0
- package/src/engine/answerguard.ts +27 -0
- package/src/engine/botsdk.ts +336 -0
- package/src/engine/caserunner-sdk.ts +423 -0
- package/src/engine/debugpi.ts +235 -0
- package/src/eod.ts +669 -0
- package/src/eodpinwatch.ts +556 -0
- package/src/escalation.ts +1253 -0
- package/src/eventloop.ts +1045 -0
- package/src/execboundary.ts +633 -0
- package/src/feedback.ts +1162 -0
- package/src/findingscheck.ts +298 -0
- package/src/fsdenied.ts +24 -0
- package/src/ghread.ts +242 -0
- package/src/github.ts +1497 -0
- package/src/homes.ts +175 -0
- package/src/hostroute.ts +53 -0
- package/src/improvements.ts +787 -0
- package/src/install.ts +1103 -0
- package/src/installkind.ts +50 -0
- package/src/ipc/capability.ts +333 -0
- package/src/ipc/client.ts +269 -0
- package/src/ipc/control.ts +90 -0
- package/src/ipc/ghreadshim.ts +94 -0
- package/src/ipc/launcher.ts +65 -0
- package/src/ipc/permits.ts +362 -0
- package/src/ipc/postshim.ts +164 -0
- package/src/ipc/protocol.ts +342 -0
- package/src/ipc/server.ts +371 -0
- package/src/ipc/workerspec.ts +218 -0
- package/src/learner/learner.ts +1655 -0
- package/src/learner/miner.ts +1290 -0
- package/src/learner/packet.ts +340 -0
- package/src/learner/receipt.ts +168 -0
- package/src/learner/status.ts +107 -0
- package/src/ledgercheck.ts +157 -0
- package/src/lessonledger.ts +703 -0
- package/src/lessonselect.ts +888 -0
- package/src/lessonshadow.ts +810 -0
- package/src/levers.ts +393 -0
- package/src/live/projection.ts +491 -0
- package/src/live/publish.ts +92 -0
- package/src/live/read.ts +86 -0
- package/src/livecli.ts +336 -0
- package/src/loginhint.ts +56 -0
- package/src/loginrefresh/atomic.ts +76 -0
- package/src/loginrefresh/lock.ts +104 -0
- package/src/loginrefresh/main.ts +59 -0
- package/src/loginrefresh/pirefresh.ts +57 -0
- package/src/loginrefresh/refresher.ts +326 -0
- package/src/loginrefresh/supervisor.ts +273 -0
- package/src/loginrefresh/types.ts +35 -0
- package/src/memory/context.ts +385 -0
- package/src/memory/datafence.ts +9 -0
- package/src/memory/delivery.ts +171 -0
- package/src/memory/eventtags.ts +80 -0
- package/src/memory/health.ts +953 -0
- package/src/memory/import.ts +664 -0
- package/src/memory/layout.ts +261 -0
- package/src/memory/legacy-file.ts +104 -0
- package/src/memory/lessons.ts +447 -0
- package/src/memory/ovsession.ts +601 -0
- package/src/memory/relayout.ts +335 -0
- package/src/memory/roundactivity.ts +209 -0
- package/src/memory/selection.ts +989 -0
- package/src/memory/shadowstatus.ts +125 -0
- package/src/memory/store.ts +275 -0
- package/src/memory/textidentity.ts +8 -0
- package/src/metrics.ts +1056 -0
- package/src/missingconfig.ts +7 -0
- package/src/npmverbs.ts +264 -0
- package/src/onboard/choices.ts +222 -0
- package/src/onboard/engine.ts +518 -0
- package/src/onboard/launch.ts +115 -0
- package/src/onboard/steps/appslug.ts +265 -0
- package/src/onboard/steps/bringup.ts +482 -0
- package/src/onboard/steps/compose.ts +459 -0
- package/src/onboard/steps/configdelivery.ts +58 -0
- package/src/onboard/steps/configfill.ts +1352 -0
- package/src/onboard/steps/deploysh.ts +105 -0
- package/src/onboard/steps/embeddings.ts +550 -0
- package/src/onboard/steps/escalation.ts +640 -0
- package/src/onboard/steps/githubapp.ts +1414 -0
- package/src/onboard/steps/golive.ts +577 -0
- package/src/onboard/steps/index.ts +71 -0
- package/src/onboard/steps/ingress.ts +907 -0
- package/src/onboard/steps/inventory.ts +526 -0
- package/src/onboard/steps/llmauth.ts +1095 -0
- package/src/onboard/steps/memory.ts +1290 -0
- package/src/onboard/steps/memorykeys.ts +85 -0
- package/src/onboard/steps/preflight.ts +191 -0
- package/src/onboard/steps/proof.ts +624 -0
- package/src/onboard/steps/ssh.ts +377 -0
- package/src/onboard/steps/topology.ts +337 -0
- package/src/onboard/steps/transport.ts +665 -0
- package/src/onboard/steps/verifyup.ts +448 -0
- package/src/onboard/steps/vmdeploy.ts +442 -0
- package/src/onboard/toml.ts +50 -0
- package/src/opencli.ts +591 -0
- package/src/openpr.ts +568 -0
- package/src/openviking/client.ts +1131 -0
- package/src/openviking/context.ts +263 -0
- package/src/openviking/embedder.ts +516 -0
- package/src/openviking/gapsink.ts +63 -0
- package/src/openviking/health.ts +83 -0
- package/src/openviking/index.ts +119 -0
- package/src/openviking/lessonfind.ts +92 -0
- package/src/openviking/probe.ts +129 -0
- package/src/openviking/proc.ts +110 -0
- package/src/openviking/provisionreport.ts +160 -0
- package/src/openviking/reembed.ts +412 -0
- package/src/openviking/relayout.ts +177 -0
- package/src/openviking/sidecar-main.ts +38 -0
- package/src/openviking/sidecar.ts +106 -0
- package/src/openviking/store.ts +2025 -0
- package/src/openviking/supervisor.ts +474 -0
- package/src/openviking/types.ts +174 -0
- package/src/operator-recovery.ts +237 -0
- package/src/operatormessage.ts +52 -0
- package/src/outcomes.ts +1670 -0
- package/src/piauthcopy.ts +12 -0
- package/src/picli.ts +49 -0
- package/src/picwd.ts +25 -0
- package/src/pilogin.ts +37 -0
- package/src/pingpong.ts +1339 -0
- package/src/posting-journal.ts +549 -0
- package/src/posting-outbox.ts +802 -0
- package/src/posting-receipt.ts +324 -0
- package/src/posting.ts +603 -0
- package/src/postops.ts +832 -0
- package/src/pr-activity.ts +20 -0
- package/src/pr-author.ts +6 -0
- package/src/pr-conversation.ts +432 -0
- package/src/pr-session-lifecycle.ts +631 -0
- package/src/preflight.ts +391 -0
- package/src/priorresolve.ts +343 -0
- package/src/qcrbin.ts +186 -0
- package/src/recheck.ts +1775 -0
- package/src/reconcile.ts +1030 -0
- package/src/recordedfindings.ts +27 -0
- package/src/regression.ts +833 -0
- package/src/replay.ts +816 -0
- package/src/requiredpasses.ts +148 -0
- package/src/resumecheck.ts +246 -0
- package/src/reviewcli.ts +787 -0
- package/src/reviewcontext.ts +35 -0
- package/src/reviewdetach.ts +734 -0
- package/src/reviewhistory.ts +427 -0
- package/src/reviewhome.ts +36 -0
- package/src/reviewmemory.ts +133 -0
- package/src/reviewrendercli.ts +139 -0
- package/src/round-work.ts +42 -0
- package/src/runhost.ts +108 -0
- package/src/scanauth.ts +36 -0
- package/src/scanbox.ts +1081 -0
- package/src/scancli.ts +1296 -0
- package/src/scandriverlog.ts +32 -0
- package/src/session-bridge.ts +139 -0
- package/src/session-command-payload.ts +35 -0
- package/src/session-command-supervisor.ts +1142 -0
- package/src/sessionjsonl.ts +88 -0
- package/src/sessions.ts +659 -0
- package/src/setup/adapters.ts +335 -0
- package/src/setup/baselines.ts +110 -0
- package/src/setup/caserunner.ts +611 -0
- package/src/setup/configcommit.ts +443 -0
- package/src/setup/engine.ts +1001 -0
- package/src/setup/facts.ts +627 -0
- package/src/setup/findings.ts +1264 -0
- package/src/setup/history.ts +443 -0
- package/src/setup/interview.ts +243 -0
- package/src/setup/ledger.ts +324 -0
- package/src/setup/pins.ts +66 -0
- package/src/setup/portrait.ts +635 -0
- package/src/setup/presentation.ts +18 -0
- package/src/setup/profile.ts +508 -0
- package/src/setup/replay.ts +370 -0
- package/src/setup/replaybuild.ts +392 -0
- package/src/setup/replayrun.ts +1096 -0
- package/src/setup/replaysource.ts +198 -0
- package/src/setup/retune.ts +415 -0
- package/src/setup/retunelog.ts +95 -0
- package/src/setup/scan.ts +1033 -0
- package/src/setup/scanbuild.ts +168 -0
- package/src/setup/scanfacts.ts +107 -0
- package/src/setup/scanlease.ts +297 -0
- package/src/setup/scanreport.ts +373 -0
- package/src/setup/scanrun.ts +2268 -0
- package/src/setup/scantree.ts +259 -0
- package/src/setup/stage2.ts +926 -0
- package/src/setup/stage2request.ts +347 -0
- package/src/setup/survey.ts +458 -0
- package/src/setup/surveyrun.ts +758 -0
- package/src/setup/textsniff.ts +95 -0
- package/src/setup/trackersink.ts +433 -0
- package/src/setup/triage.ts +768 -0
- package/src/setup/tuning.ts +766 -0
- package/src/setup/unitcut.ts +523 -0
- package/src/singleflight.ts +86 -0
- package/src/slack/announce.ts +911 -0
- package/src/slack/api.ts +303 -0
- package/src/slack/messages.ts +288 -0
- package/src/slack/origin.ts +599 -0
- package/src/slack/policy.ts +354 -0
- package/src/slack/reply.ts +201 -0
- package/src/slack/router.ts +285 -0
- package/src/slack/socket.ts +384 -0
- package/src/slack/types.ts +108 -0
- package/src/sqlitehint.ts +32 -0
- package/src/ssh-observation.ts +30 -0
- package/src/state.ts +8243 -0
- package/src/statemachine.ts +369 -0
- package/src/stubs.ts +68 -0
- package/src/takeover.ts +634 -0
- package/src/takeoverpi.ts +293 -0
- package/src/terminal-failure.ts +41 -0
- package/src/thread-resolution-refusal.ts +2 -0
- package/src/tmux.ts +153 -0
- package/src/trackerid.ts +41 -0
- package/src/transcriptindex.ts +665 -0
- package/src/tui/actions.ts +484 -0
- package/src/tui/alerts.ts +666 -0
- package/src/tui/app.ts +1979 -0
- package/src/tui/archiveops.ts +192 -0
- package/src/tui/autofile.ts +233 -0
- package/src/tui/board.ts +1432 -0
- package/src/tui/candidates.ts +889 -0
- package/src/tui/cellwidth.ts +148 -0
- package/src/tui/changelog-store.ts +503 -0
- package/src/tui/chord.ts +25 -0
- package/src/tui/clipboard.ts +92 -0
- package/src/tui/cmdbar.ts +296 -0
- package/src/tui/companion.ts +1427 -0
- package/src/tui/companionplan.ts +247 -0
- package/src/tui/companiontab.ts +83 -0
- package/src/tui/confighome.ts +155 -0
- package/src/tui/connect.ts +68 -0
- package/src/tui/deploy-ceremony.ts +104 -0
- package/src/tui/deploy-drain.ts +129 -0
- package/src/tui/deploy-process.ts +265 -0
- package/src/tui/deploytab.ts +1523 -0
- package/src/tui/design.ts +125 -0
- package/src/tui/digesttab.ts +214 -0
- package/src/tui/emptystate.ts +39 -0
- package/src/tui/evidence.ts +999 -0
- package/src/tui/feed.ts +604 -0
- package/src/tui/feedbacktab.ts +1077 -0
- package/src/tui/filters.ts +90 -0
- package/src/tui/ghread.ts +321 -0
- package/src/tui/historypull.ts +81 -0
- package/src/tui/inboxpull.ts +236 -0
- package/src/tui/index.ts +1565 -0
- package/src/tui/issuesink.ts +238 -0
- package/src/tui/jsonfile.ts +42 -0
- package/src/tui/keys.ts +109 -0
- package/src/tui/layout.ts +108 -0
- package/src/tui/learning.ts +627 -0
- package/src/tui/lessonswitch.ts +118 -0
- package/src/tui/livecontrol.ts +228 -0
- package/src/tui/memforward.ts +443 -0
- package/src/tui/memincident.ts +363 -0
- package/src/tui/memory.ts +620 -0
- package/src/tui/memtab.ts +606 -0
- package/src/tui/metricstab.ts +1177 -0
- package/src/tui/model.ts +131 -0
- package/src/tui/mouse.ts +98 -0
- package/src/tui/msg.ts +268 -0
- package/src/tui/nativecompanion.ts +417 -0
- package/src/tui/openurl.ts +21 -0
- package/src/tui/paint.ts +511 -0
- package/src/tui/panes.ts +98 -0
- package/src/tui/pipane.ts +219 -0
- package/src/tui/poll-hooks.ts +12 -0
- package/src/tui/poll.ts +147 -0
- package/src/tui/proposals.ts +838 -0
- package/src/tui/prpause.ts +150 -0
- package/src/tui/reload.ts +436 -0
- package/src/tui/remote.ts +413 -0
- package/src/tui/rollover.ts +969 -0
- package/src/tui/scantab.ts +1290 -0
- package/src/tui/setupentry.ts +93 -0
- package/src/tui/sidebar.ts +749 -0
- package/src/tui/sshtriage.ts +510 -0
- package/src/tui/startup.ts +231 -0
- package/src/tui/tabs.ts +45 -0
- package/src/tui/termmirror.ts +224 -0
- package/src/tui/theme.ts +95 -0
- package/src/tui/ticketstab.ts +310 -0
- package/src/tui/tmuxsocket.ts +129 -0
- package/src/tui/trackertickets.ts +84 -0
- package/src/tui/unwedge.ts +286 -0
- package/src/tui/viewer.ts +575 -0
- package/src/tui/viewerinput.ts +173 -0
- package/src/updatenotice.ts +178 -0
- package/src/usage.ts +1154 -0
- package/src/verdicts.ts +4689 -0
- package/src/version.ts +80 -0
- package/src/wakes.ts +476 -0
- package/src/webhook.ts +942 -0
- package/src/webhookheal.ts +276 -0
- package/src/wire.ts +2153 -0
- package/src/worker-session-host.ts +602 -0
- package/src/workerbreaker.ts +156 -0
- package/src/workercontrol.ts +104 -0
- package/src/workerlease.ts +441 -0
- package/src/workerliveness.ts +103 -0
- package/src/workerlog.ts +79 -0
- package/src/workerruntime.ts +30 -0
- package/src/workersupervisor.ts +1538 -0
- package/src/workerusage.ts +42 -0
- package/src/workerworld.ts +427 -0
- package/src/worktrees.ts +1138 -0
- package/tests/fixtures/regression-cases.json +795 -0
- package/tests/fixtures/seed-manifest.json +19 -0
|
@@ -0,0 +1,1425 @@
|
|
|
1
|
+
// `qcr tui [pr]` entry point. Wires the real ssh runner; the optional PR
|
|
2
|
+
// number opens the board with that PR selected.
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { liveConfigPath, loadConfig, sandboxRoot } from "../config.js";
|
|
5
|
+
import { App } from "./app.js";
|
|
6
|
+
import { powerActionFor } from "./deploytab.js";
|
|
7
|
+
import { ensureSshMaster } from "./connect.js";
|
|
8
|
+
import { MOUSE_OFF, MOUSE_ON } from "./mouse.js";
|
|
9
|
+
import { isFreshHealth } from "./poll.js";
|
|
10
|
+
import { ALT_SCREEN_OFF, ALT_SCREEN_ON, CLEAR } from "./paint.js";
|
|
11
|
+
import { storeHealth } from "./memory.js";
|
|
12
|
+
import { entryKey, findMemoryEntry, memoryOrder, memoryRefUnread, setTimelineFilter, } from "./memtab.js";
|
|
13
|
+
import { refOf } from "../memory/store.js";
|
|
14
|
+
import { isTab } from "./tabs.js";
|
|
15
|
+
import { laptopCompanionConfig, laptopInstallEnv, laptopLifecycle } from "./companionplan.js";
|
|
16
|
+
import { TUI_COMPANION_ENV, TUI_COMPANION_TMUX_SOCKET, tmuxSocketRunner } from "./tmuxsocket.js";
|
|
17
|
+
import { APP_BUILD_INFO, BUILD_STAMP, CORE_DIST, HotReloader, parseResumeArg, readResumeState, realReexec, RELOAD_EXIT_CODE, resumeStatePath, } from "./reload.js";
|
|
18
|
+
import { killInFlightSsh, runnerFor, transportOf } from "./remote.js";
|
|
19
|
+
import { chainOnPoll } from "./poll-hooks.js";
|
|
20
|
+
import { deliverMessage } from "./msg.js";
|
|
21
|
+
export { chainOnPoll } from "./poll-hooks.js";
|
|
22
|
+
/**
|
|
23
|
+
* `qcr tui <pr>` opens the board with that PR selected. `overseer` and `main`
|
|
24
|
+
* are refused with the place to go instead: the TUI never attaches to a VM
|
|
25
|
+
* session.
|
|
26
|
+
*/
|
|
27
|
+
export function parseTarget(arg) {
|
|
28
|
+
if (arg === undefined || arg === "")
|
|
29
|
+
return { kind: "none" };
|
|
30
|
+
if (/^\d+$/.test(arg))
|
|
31
|
+
return { kind: "pr", pr: Number(arg) };
|
|
32
|
+
if (arg === "overseer" || arg === "main") {
|
|
33
|
+
return {
|
|
34
|
+
kind: "bad",
|
|
35
|
+
reason: `'${arg}' is no longer a target — the TUI never attaches to the ${arg} session; open the board and ask the companion`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { kind: "bad", reason: `unknown target '${arg}' — expected a PR number` };
|
|
39
|
+
}
|
|
40
|
+
export const TUI_HELP = `qcr tui — live board and operator console for the review daemon (over ssh)
|
|
41
|
+
|
|
42
|
+
usage: qcr tui [<pr>] [--no-companion] [--no-reload]
|
|
43
|
+
|
|
44
|
+
qcr tui open the board
|
|
45
|
+
qcr tui 5012 open the board with PR 5012 selected (⏎ focuses its pi pane)
|
|
46
|
+
|
|
47
|
+
--no-companion do not run the local companion (container, ambient feed,
|
|
48
|
+
alert pipeline). The layout does not change: the column
|
|
49
|
+
is still on the right and says "companion off"
|
|
50
|
+
--no-reload do not re-exec when a new dist/ build lands
|
|
51
|
+
|
|
52
|
+
layout: ONE shape, always — the tab on the left, the companion column on the
|
|
53
|
+
right. Nothing goes full-screen and nothing collapses the column;
|
|
54
|
+
anything you would have attached to a VM session for, ask the
|
|
55
|
+
companion.
|
|
56
|
+
|
|
57
|
+
keys: 1…8 the eight surfaces — board · memory · digest · tickets · deploy ·
|
|
58
|
+
metrics · feedback · scan (docs/tui.md says what each is for; the bar at the
|
|
59
|
+
foot of the screen shows what the current frame answers to) ·
|
|
60
|
+
2 memory: j/k select · [ ] older/newer day · e p x n edit/pin/retire/new ·
|
|
61
|
+
3 digest: [ ] older/newer · 4 tickets: c actionable ⇄ all ·
|
|
62
|
+
5 deploy: click ▶/■ buttons (D D deploy, S S stop/start) ·
|
|
63
|
+
6 metrics: w/m/a window, # <pr> ⏎ for a PR timeline ·
|
|
64
|
+
7 feedback: t focus · a archived · A/x accept/reject a proposal ·
|
|
65
|
+
R then y reverts a feedback-derived commit (local, never pushed) ·
|
|
66
|
+
8 scan: [ ] unit · p promote · d dismiss · f/F filter · t tier · P plan (space
|
|
67
|
+
toggles a row, ⏎ approves, Esc back) · x stop · D delta re-scan ·
|
|
68
|
+
companion column on every tab: type any unclaimed key (or /) to talk to
|
|
69
|
+
it — every byte reaches pi · wheel over the column, or PgUp/PgDn while
|
|
70
|
+
focused, walks its scrollback ·
|
|
71
|
+
drag its left edge to resize · click it to take the keyboard ·
|
|
72
|
+
board: j/k select · ⏎ focus the PR's pi pane · m quick-message ·
|
|
73
|
+
g github · u unwedge · e events overlay · I set up the repo ·
|
|
74
|
+
Ctrl-q release focus · Ctrl-c ×3 hard-kill the TUI ·
|
|
75
|
+
s reconnect ssh (GCP passphrase, once) · r refresh ·
|
|
76
|
+
? full key map · q quit
|
|
77
|
+
|
|
78
|
+
troubleshooting:
|
|
79
|
+
✖ unreachable ssh to the VM failed. The board names WHICH of the
|
|
80
|
+
three usual causes it is and prints the exact command to
|
|
81
|
+
run — a key missing from the agent (ssh-add …), a wedged
|
|
82
|
+
shared master (ssh -O exit …), or a host that is simply
|
|
83
|
+
not answering. Nothing is run for you; s re-checks and
|
|
84
|
+
reports, and offers the one GCP passphrase prompt when a
|
|
85
|
+
key is what is missing
|
|
86
|
+
▲ DATA STALE ssh works but status.json stopped updating — the
|
|
87
|
+
container is down or the daemon hung (qcr-side:
|
|
88
|
+
ssh <host> sudo docker ps / docker logs)
|
|
89
|
+
keys lost in pi the outer terminal must speak csi-u (kitty/ghostty/
|
|
90
|
+
wezterm/iterm2); running inside an outer tmux needs
|
|
91
|
+
extended-keys passthrough there too
|
|
92
|
+
`;
|
|
93
|
+
export function parseTuiArgs(args) {
|
|
94
|
+
const { stateFile, rest } = parseResumeArg(args);
|
|
95
|
+
const flags = new Set(rest.filter((a) => a.startsWith("-")));
|
|
96
|
+
return {
|
|
97
|
+
target: rest.find((a) => !a.startsWith("-")),
|
|
98
|
+
companion: !flags.has("--no-companion"),
|
|
99
|
+
reload: !flags.has("--no-reload"),
|
|
100
|
+
resumeState: stateFile,
|
|
101
|
+
help: flags.has("--help") || flags.has("-h"),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The companion action channel's executor, mapped onto the App's public
|
|
106
|
+
* surface plus the per-tab states the install hooks return. Exported so tests
|
|
107
|
+
* can drive a real App through `processTuiActions`; runTui wires it inside
|
|
108
|
+
* the companionUp block. Actions run instantly — no confirm gate (see
|
|
109
|
+
* src/tui/actions.ts header).
|
|
110
|
+
*/
|
|
111
|
+
export function buildTuiDriver(app, learning, metricsView, deployState) {
|
|
112
|
+
// One keyboard owner at a time: an action must never type into a focused
|
|
113
|
+
// pi pane or companion column (same rule the mouse/tab handlers follow).
|
|
114
|
+
// A busy App (operator input box open, terminal lent to the `s` passphrase
|
|
115
|
+
// prompt) is refused: a synthesized keystroke would land in the operator's
|
|
116
|
+
// draft or nowhere.
|
|
117
|
+
const prep = () => {
|
|
118
|
+
if (app.piFocused())
|
|
119
|
+
app.blurPiPane();
|
|
120
|
+
app.sidebar?.blur();
|
|
121
|
+
if (app.isBusy())
|
|
122
|
+
throw new Error("operator input open / terminal in use — action refused");
|
|
123
|
+
};
|
|
124
|
+
/** Press a deploy-tab run key twice (arm + confirm) and answer whether a
|
|
125
|
+
* run started: the tab refuses a press with a result on screen, a drain
|
|
126
|
+
* still owned, or a target not yet loaded, and a refused printable would
|
|
127
|
+
* otherwise fall through to the companion column while the action read as
|
|
128
|
+
* done. */
|
|
129
|
+
const startDeployRun = (key, kind) => {
|
|
130
|
+
if (deployState.run)
|
|
131
|
+
throw new Error(`deploy_tab ${kind} refused — ${deployState.run.running ? "a run is in flight" : "the previous result is still on screen (x closes it)"}`);
|
|
132
|
+
// Begin this action's own arm/confirm pair. An operator's earlier arm
|
|
133
|
+
// would make the first press confirm and the second reach the companion.
|
|
134
|
+
deployState.armedAt = undefined;
|
|
135
|
+
deployState.armedKind = undefined;
|
|
136
|
+
app.pressKey(key);
|
|
137
|
+
app.pressKey(key);
|
|
138
|
+
const started = () => deployState.run?.kind; // read after the presses
|
|
139
|
+
if (started() !== kind)
|
|
140
|
+
throw new Error(`deploy_tab ${kind} refused by the tab${app.view.noticeLine ? ` — ${app.view.noticeLine}` : ""}`);
|
|
141
|
+
};
|
|
142
|
+
const driver = {
|
|
143
|
+
switchTab: (tab) => {
|
|
144
|
+
prep();
|
|
145
|
+
app.tab = tab; // one vocabulary: the verb's tab name IS the App's (src/tui/tabs.ts)
|
|
146
|
+
app.repaint();
|
|
147
|
+
},
|
|
148
|
+
selectPr: (pr) => {
|
|
149
|
+
prep();
|
|
150
|
+
// public App API — selects + refreshes detail; a refusal leaves the tab
|
|
151
|
+
if (!app.selectPr(pr))
|
|
152
|
+
throw new Error(`#${pr} is not on the board`);
|
|
153
|
+
app.tab = "board";
|
|
154
|
+
app.repaint();
|
|
155
|
+
},
|
|
156
|
+
metricsWindow: (w) => {
|
|
157
|
+
prep();
|
|
158
|
+
app.tab = "metrics";
|
|
159
|
+
metricsView.drill = undefined;
|
|
160
|
+
metricsView.window = w === "7d" ? "d7" : w === "30d" ? "d30" : "all";
|
|
161
|
+
metricsView.scroll = 0;
|
|
162
|
+
app.repaint();
|
|
163
|
+
},
|
|
164
|
+
metricsPr: (pr) => {
|
|
165
|
+
prep();
|
|
166
|
+
app.tab = "metrics";
|
|
167
|
+
app.repaint();
|
|
168
|
+
// Drive the tab's own PR-entry state machine so the timeline FETCH runs
|
|
169
|
+
// (drillTo is closure-private): # opens entry, digits accumulate, ⏎ drills.
|
|
170
|
+
// The sequence starts from the dashboard: a half-typed operator entry
|
|
171
|
+
// would swallow the "#" and prefix our digits, and an open timeline
|
|
172
|
+
// answers to Esc only, so its "#" would reach the companion column.
|
|
173
|
+
metricsView.prEntry = undefined;
|
|
174
|
+
metricsView.drill = undefined;
|
|
175
|
+
metricsView.scroll = 0;
|
|
176
|
+
app.pressKey("#");
|
|
177
|
+
if (metricsView.prEntry !== "")
|
|
178
|
+
throw new Error(`metrics pr ${pr} refused — the metrics tab did not open its PR entry`);
|
|
179
|
+
for (const d of String(pr))
|
|
180
|
+
app.pressKey(d);
|
|
181
|
+
app.pressKey("\r");
|
|
182
|
+
},
|
|
183
|
+
digestDay: (date) => {
|
|
184
|
+
prep();
|
|
185
|
+
app.tab = "digest";
|
|
186
|
+
app.repaint();
|
|
187
|
+
// Page to the day through the tab's own [ / ] keys so content loads.
|
|
188
|
+
// files are date-named ASCENDING (parseDigestList sorts); back counts
|
|
189
|
+
// BACK from the newest file. An unlisted digest is a failed result —
|
|
190
|
+
// including when the listing itself has not happened or failed.
|
|
191
|
+
const files = learning.store.digest.files ?? [];
|
|
192
|
+
const i = files.findIndex((f) => f.startsWith(date));
|
|
193
|
+
if (i < 0)
|
|
194
|
+
throw new Error(`no digest for ${date}`);
|
|
195
|
+
const target = files.length - 1 - i;
|
|
196
|
+
const delta = target - learning.store.digest.back;
|
|
197
|
+
for (let k = 0; k < Math.abs(delta); k++)
|
|
198
|
+
app.pressKey(delta > 0 ? "[" : "]");
|
|
199
|
+
},
|
|
200
|
+
memoryView: (opts) => {
|
|
201
|
+
prep();
|
|
202
|
+
app.tab = "memory";
|
|
203
|
+
const mem = learning.memory;
|
|
204
|
+
if (opts.tag !== undefined)
|
|
205
|
+
setTimelineFilter(mem, opts.tag);
|
|
206
|
+
if (opts.day !== undefined) {
|
|
207
|
+
// The day is a JUMP in the day-grouped timeline: land on its first
|
|
208
|
+
// entry. A day with no entry under the current filter is a failed
|
|
209
|
+
// result, not a silent no-op.
|
|
210
|
+
const ordered = memoryOrder(learning.store.entries, mem.filter);
|
|
211
|
+
const i = ordered.findIndex((e) => e.day === opts.day);
|
|
212
|
+
if (i < 0)
|
|
213
|
+
throw new Error(`no memory entries on ${opts.day}${mem.filter ? ` under #${mem.filter}` : ""}`);
|
|
214
|
+
mem.sel = i;
|
|
215
|
+
mem.selKey = entryKey(ordered[i]);
|
|
216
|
+
}
|
|
217
|
+
app.repaint();
|
|
218
|
+
},
|
|
219
|
+
memoryPin: async (ref, pin) => {
|
|
220
|
+
// The companion may pin or unpin an existing entry, and nothing else
|
|
221
|
+
// about memory rides this channel — the only op built here is `pin` or
|
|
222
|
+
// `unpin` (the companion boundary). A memory write, not a keystroke: no tab
|
|
223
|
+
// switch and no focus change (prep is for synthesized keys). It goes
|
|
224
|
+
// through the one store — the same verb the tab's `p` uses — against
|
|
225
|
+
// the entry as a new read finds it (the tab may never have been
|
|
226
|
+
// opened). That read lists every tree again (the TUI's store keeps no
|
|
227
|
+
// sweep cache; a file the listing shows unchanged may come from its
|
|
228
|
+
// per-file cache), and the write re-reads the file and refuses a stale
|
|
229
|
+
// hash — never applied blind.
|
|
230
|
+
const memory = learning.store.memory;
|
|
231
|
+
const entry = findMemoryEntry(await memory.read(), ref);
|
|
232
|
+
if (!entry) {
|
|
233
|
+
// Not found in a read that did not read everything is not "no such
|
|
234
|
+
// entry": answering that would publish a fact to the companion from
|
|
235
|
+
// no data.
|
|
236
|
+
const unread = memoryRefUnread(storeHealth(memory), ref);
|
|
237
|
+
if (unread)
|
|
238
|
+
throw new Error(`memory not fully readable — '${ref}' may be there; nothing was written: ${unread}`);
|
|
239
|
+
throw new Error(`no memory entry '${ref}' — its id or URI, as memory lists it`);
|
|
240
|
+
}
|
|
241
|
+
if (entry.pinned === pin)
|
|
242
|
+
return `${entry.id} is already ${pin ? "pinned" : "unpinned"}`;
|
|
243
|
+
const res = await memory.apply({
|
|
244
|
+
op: pin ? "pin" : "unpin",
|
|
245
|
+
...refOf(entry),
|
|
246
|
+
});
|
|
247
|
+
learning.store.reloadMemory();
|
|
248
|
+
if (!res.ok)
|
|
249
|
+
throw new Error(res.message);
|
|
250
|
+
return res.message;
|
|
251
|
+
},
|
|
252
|
+
setBoardFilter: (f) => {
|
|
253
|
+
prep();
|
|
254
|
+
app.tab = "board";
|
|
255
|
+
app.setBoardFilter(f);
|
|
256
|
+
app.repaint();
|
|
257
|
+
},
|
|
258
|
+
setMemoryFilter: (tag) => {
|
|
259
|
+
prep();
|
|
260
|
+
app.tab = "memory";
|
|
261
|
+
setTimelineFilter(learning.memory, tag);
|
|
262
|
+
app.repaint();
|
|
263
|
+
},
|
|
264
|
+
msgSession: (session, text) => deliverMessage(app, session, text),
|
|
265
|
+
// The companion's tier (rule 8): the same operator route the viewer's input
|
|
266
|
+
// line uses. A refusal or a failed write rejects, so the result file says
|
|
267
|
+
// so; an unconfirmed send resolves, and its label says to check first.
|
|
268
|
+
operatorCommand: async (command) => {
|
|
269
|
+
const send = app.hooks.operatorCommand;
|
|
270
|
+
if (!send)
|
|
271
|
+
throw new Error("the operator command route is not wired in this TUI");
|
|
272
|
+
const out = await send(command, app);
|
|
273
|
+
if (out.state === "refused" || out.state === "failed")
|
|
274
|
+
throw new Error(out.label);
|
|
275
|
+
return out.label;
|
|
276
|
+
},
|
|
277
|
+
deployTab: (action) => {
|
|
278
|
+
prep();
|
|
279
|
+
app.tab = "deploy";
|
|
280
|
+
app.repaint();
|
|
281
|
+
if (action === "abort") {
|
|
282
|
+
if (!deployState.run)
|
|
283
|
+
throw new Error("deploy_tab abort refused — no run in flight");
|
|
284
|
+
app.pressKey("x"); // kills a running run (closes a finished log otherwise)
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (action === "deploy") {
|
|
288
|
+
startDeployRun("D", "deploy"); // arm+confirm inside the window = instant
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
// stop/start: the tab's S button computes its own kind from the observed
|
|
292
|
+
// container state; refuse a mismatched request rather than silently doing
|
|
293
|
+
// the opposite — and refuse outright when that state has not been read,
|
|
294
|
+
// so a stop request can never stop a live reviewer on data nobody
|
|
295
|
+
// fetched.
|
|
296
|
+
if (deployState.containerStatus === undefined) {
|
|
297
|
+
throw new Error(`deploy_tab ${action} refused — the container's state has not been read yet`);
|
|
298
|
+
}
|
|
299
|
+
const kind = powerActionFor(deployState.containerStatus);
|
|
300
|
+
if (kind !== action)
|
|
301
|
+
throw new Error(`deploy_tab ${action} refused — container is ${deployState.containerStatus}`);
|
|
302
|
+
startDeployRun("S", action);
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
return driver;
|
|
306
|
+
}
|
|
307
|
+
/** First slice of the quit-time wait for an in-flight bring-up. */
|
|
308
|
+
/** Startup steps that exist only for the companion (the memory forward also serves the Memory tab). */
|
|
309
|
+
const COMPANION_ONLY_STEPS = [
|
|
310
|
+
"memory-namespaces",
|
|
311
|
+
"container",
|
|
312
|
+
"rollover",
|
|
313
|
+
"companion-pi",
|
|
314
|
+
];
|
|
315
|
+
const BRING_UP_QUIT_GRACE_MS = 250;
|
|
316
|
+
/** True if the promise settled inside `ms`. The timer is unref'd, so a slow
|
|
317
|
+
* bring-up can never be the thing that holds the process open. */
|
|
318
|
+
async function settledWithin(p, ms) {
|
|
319
|
+
let timer;
|
|
320
|
+
const timeout = new Promise((resolve) => {
|
|
321
|
+
timer = setTimeout(() => resolve(false), ms);
|
|
322
|
+
timer.unref?.();
|
|
323
|
+
});
|
|
324
|
+
const settled = await Promise.race([p.then(() => true), timeout]);
|
|
325
|
+
if (timer)
|
|
326
|
+
clearTimeout(timer);
|
|
327
|
+
return settled;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Clean quit stops the container (conversation + auth survive on volumes).
|
|
331
|
+
* The question is whether THIS start put a container up — true from the
|
|
332
|
+
* moment `ensureUp` returns, not only once the rollover and the pi spawn
|
|
333
|
+
* behind it finished: a quit whose bring-up grace ran out mid-rollover would
|
|
334
|
+
* otherwise leave a freshly started container running. A failed stop is
|
|
335
|
+
* swallowed — leaving it running is harmless, the next start adopts it.
|
|
336
|
+
*/
|
|
337
|
+
export async function stopCompanionOnQuit(opts) {
|
|
338
|
+
if (!opts.containerStarted)
|
|
339
|
+
return;
|
|
340
|
+
opts.write("qcr tui: stopping the companion container…\n");
|
|
341
|
+
try {
|
|
342
|
+
await opts.stop();
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
/* leaving it running is harmless: the next start adopts */
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* `qcr tui` — the whole startup, in two phases.
|
|
350
|
+
*
|
|
351
|
+
* Phase 0 is local only: parse, config, wire, and paint. Nothing external —
|
|
352
|
+
* no ssh, no docker, no network, no directory scan — may run before the first
|
|
353
|
+
* frame, and the frame it paints is the real layout with every unfetched value
|
|
354
|
+
* marked unknown. The companion column's width comes from config, the only
|
|
355
|
+
* layout-affecting fact, so nothing jumps when the container later arrives.
|
|
356
|
+
*
|
|
357
|
+
* Phase 1 is the bring-up, streaming in behind that frame and reporting into
|
|
358
|
+
* the companion column: connection triage, memory forward, namespaces,
|
|
359
|
+
* container, day rollover, pi. It never blocks the board, and a step that
|
|
360
|
+
* fails is a degraded mode, never a reason the TUI does not open.
|
|
361
|
+
*/
|
|
362
|
+
export async function runTui(argv = [], deps = {}) {
|
|
363
|
+
const args = parseTuiArgs(typeof argv === "string" ? [argv] : argv);
|
|
364
|
+
if (args.help) {
|
|
365
|
+
console.log(TUI_HELP);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const target = parseTarget(args.target);
|
|
369
|
+
if (target.kind === "bad") {
|
|
370
|
+
console.error(`qcr tui: ${target.reason}`);
|
|
371
|
+
process.exitCode = 2;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const out = deps.out ?? process.stdout;
|
|
375
|
+
const input = deps.input ?? process.stdin;
|
|
376
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
377
|
+
if (!out.isTTY || !input.isTTY) {
|
|
378
|
+
console.error("qcr tui needs an interactive terminal");
|
|
379
|
+
process.exitCode = 2;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// ---- phase 0: local only, ending in the first frame -----------------------
|
|
383
|
+
const loaded = deps.config ?? loadConfig();
|
|
384
|
+
// In the containerised TUI the companion is a container the host wrapper
|
|
385
|
+
// runs, reached through a shared tmux socket. Its channel dirs are the
|
|
386
|
+
// host's, named by the wrapper in env; nothing here can run docker.
|
|
387
|
+
const socketMode = process.env[TUI_COMPANION_ENV] === TUI_COMPANION_TMUX_SOCKET;
|
|
388
|
+
const full = socketMode
|
|
389
|
+
? { ...loaded, companion: laptopCompanionConfig(process.env, loaded.companion) }
|
|
390
|
+
: loaded;
|
|
391
|
+
const cfg = full.tui;
|
|
392
|
+
// One transport decision for every remote op — ssh to the VM, or the same
|
|
393
|
+
// commands against local docker on the laptop topology.
|
|
394
|
+
const runner = runnerFor(cfg);
|
|
395
|
+
const app = new App(cfg, runner, out, input);
|
|
396
|
+
// The terminal must come back whatever kills us — raw mode + alt screen
|
|
397
|
+
// surviving a crash is exactly the "only closing the terminal works" trap.
|
|
398
|
+
const restoreTerminal = () => {
|
|
399
|
+
try {
|
|
400
|
+
if (input.isTTY)
|
|
401
|
+
input.setRawMode(false);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
/* stdin already gone */
|
|
405
|
+
}
|
|
406
|
+
out.write(MOUSE_OFF + ALT_SCREEN_OFF);
|
|
407
|
+
};
|
|
408
|
+
// A reload under the supervisor exits with RELOAD_EXIT_CODE so the next
|
|
409
|
+
// build takes over this terminal state — the alt screen, mouse reporting
|
|
410
|
+
// and raw mode stay exactly as they are. Restoring here would flash the
|
|
411
|
+
// shell between builds. Every other exit restores.
|
|
412
|
+
process.on("exit", (code) => {
|
|
413
|
+
if (code === RELOAD_EXIT_CODE)
|
|
414
|
+
return;
|
|
415
|
+
restoreTerminal();
|
|
416
|
+
});
|
|
417
|
+
process.on("SIGINT", () => {
|
|
418
|
+
restoreTerminal();
|
|
419
|
+
exit(130);
|
|
420
|
+
});
|
|
421
|
+
process.on("SIGTERM", () => {
|
|
422
|
+
restoreTerminal();
|
|
423
|
+
exit(143);
|
|
424
|
+
});
|
|
425
|
+
process.on("uncaughtException", (err) => {
|
|
426
|
+
restoreTerminal();
|
|
427
|
+
console.error("qcr tui crashed:", err);
|
|
428
|
+
exit(1);
|
|
429
|
+
});
|
|
430
|
+
/** Suspend the TUI, run something on the real terminal, come back. */
|
|
431
|
+
const onRealTerminal = (fn) => {
|
|
432
|
+
// Quit can land mid-flight (the operator presses `s`, then Ctrl-C twice
|
|
433
|
+
// while the probes run). The terminal is already restored by then, so
|
|
434
|
+
// going back into the alt screen would leave a blank alternate screen with
|
|
435
|
+
// no TUI behind it. Run the work plainly and leave the terminal alone.
|
|
436
|
+
if (app.isStopped()) {
|
|
437
|
+
fn();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
app.suspend();
|
|
441
|
+
try {
|
|
442
|
+
if (input.isTTY)
|
|
443
|
+
input.setRawMode(false);
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
/* keep going — fn may still work */
|
|
447
|
+
}
|
|
448
|
+
out.write(MOUSE_OFF + ALT_SCREEN_OFF);
|
|
449
|
+
try {
|
|
450
|
+
fn();
|
|
451
|
+
}
|
|
452
|
+
finally {
|
|
453
|
+
// Deliberate, and NOT safe to "fix": this return also swallows a throw
|
|
454
|
+
// from fn(), which is what we want on the quit path — the alt screen is
|
|
455
|
+
// already gone, and restoring it to report an error would repaint over
|
|
456
|
+
// the shell the operator is back in. Restructuring so the return sits
|
|
457
|
+
// outside the finally would change that, and error propagation on quit
|
|
458
|
+
// is behaviour, not style.
|
|
459
|
+
// eslint-disable-next-line no-unsafe-finally
|
|
460
|
+
if (app.isStopped())
|
|
461
|
+
return; // quit happened while fn ran
|
|
462
|
+
if (input.isTTY)
|
|
463
|
+
input.setRawMode(true);
|
|
464
|
+
out.write(ALT_SCREEN_ON + CLEAR + MOUSE_ON);
|
|
465
|
+
app.resume();
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
const { DurableDeployProcess } = await import("./deploy-process.js");
|
|
469
|
+
const durableDeploy = new DurableDeployProcess(sandboxRoot());
|
|
470
|
+
app.hooks.deployRun = (_theApp, runId, version, onData, onExit) => durableDeploy.startOrAttach(runId, version, onData, onExit);
|
|
471
|
+
app.view.repo = full.github.repo;
|
|
472
|
+
const { spawn } = await import("node:child_process");
|
|
473
|
+
const { openUrlDetached } = await import("./openurl.js");
|
|
474
|
+
const { existsSync, mkdirSync, readFileSync, writeFileSync } = await import("node:fs");
|
|
475
|
+
app.hooks.openUrl = (url) => openUrlDetached(url, (line) => app.notice(line));
|
|
476
|
+
const marker = join(sandboxRoot(), "state", "tui-help-shown");
|
|
477
|
+
app.hooks.helpSeen = {
|
|
478
|
+
get: () => existsSync(marker),
|
|
479
|
+
set: () => {
|
|
480
|
+
mkdirSync(join(sandboxRoot(), "state"), { recursive: true });
|
|
481
|
+
writeFileSync(marker, new Date().toISOString());
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
// The companion column is layout: its width depends on nothing but the
|
|
485
|
+
// terminal, so wiring it here — before anything asks docker whether the
|
|
486
|
+
// container is running — is what makes the first frame the real one. The
|
|
487
|
+
// sidebar is only `available()` once the container answers, and the
|
|
488
|
+
// container fills a column that was already the right size.
|
|
489
|
+
const { CompanionSidebar, restorePreferredSidebarWidth } = await import("./sidebar.js");
|
|
490
|
+
const { CompanionLifecycle, realCompanionFs, realDockerRunner, tmuxSocketPath } = await import("./companion.js");
|
|
491
|
+
const { NativeCompanion, SelectingCompanion, nativeAgentDir, probeDocker, UNSANDBOXED_WARNING } = await import("./nativecompanion.js");
|
|
492
|
+
const { configHome: companionConfigHome } = await import("../install.js");
|
|
493
|
+
const { MEMORY_OFF_NO_DOCKER } = await import("../memory/health.js");
|
|
494
|
+
// Is Docker answering here? Asked once, in the bring-up — never before the
|
|
495
|
+
// first frame — and shared by the companion's choice and the memory forward.
|
|
496
|
+
let dockerProbe;
|
|
497
|
+
const dockerUp = () => (dockerProbe ??= probeDocker(realDockerRunner).catch(() => false));
|
|
498
|
+
// Docker answering → the container companion; else the native one on this
|
|
499
|
+
// host, with the same guards and an "unsandboxed" warning
|
|
500
|
+
// (nativecompanion.ts). The containerised TUI keeps its wrapper-run
|
|
501
|
+
// container: it has no Docker of its own to ask.
|
|
502
|
+
const containerLifecycle = new CompanionLifecycle(full.companion, realDockerRunner);
|
|
503
|
+
const selecting = socketMode
|
|
504
|
+
? undefined
|
|
505
|
+
: new SelectingCompanion(containerLifecycle, realDockerRunner, {
|
|
506
|
+
dockerUp,
|
|
507
|
+
makeNative: () => new NativeCompanion(containerLifecycle, {
|
|
508
|
+
appRoot: sandboxRoot(),
|
|
509
|
+
agentDir: nativeAgentDir(),
|
|
510
|
+
configHome: companionConfigHome(),
|
|
511
|
+
}),
|
|
512
|
+
onNative: () => {
|
|
513
|
+
app.sidebar?.setHeader({ unsandboxed: true });
|
|
514
|
+
app.notice(`companion: ${UNSANDBOXED_WARNING}`);
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
// The one seam the lifecycle, the column and the rollover reach the companion
|
|
518
|
+
// through: docker exec, the native host's tmux, or tmux -S over the shared
|
|
519
|
+
// socket.
|
|
520
|
+
const companionDocker = selecting
|
|
521
|
+
? selecting.runner
|
|
522
|
+
: tmuxSocketRunner(tmuxSocketPath(full.companion), full.companion.container_name);
|
|
523
|
+
const companion = selecting ?? laptopLifecycle(process.env, companionDocker, loaded.companion, realCompanionFs);
|
|
524
|
+
// The companion's read-only GitHub token, minted here from the App
|
|
525
|
+
// credential this host holds and handed over as an env file in a dir the
|
|
526
|
+
// container mounts read-only. Refreshed while the TUI runs (it lives an
|
|
527
|
+
// hour); constructed now, first minted in the bring-up — it is network.
|
|
528
|
+
const { CompanionGithubToken, tokenOutcomeLine } = await import("./ghread.js");
|
|
529
|
+
const { companionGithubDir } = await import("../install.js");
|
|
530
|
+
const ghToken = new CompanionGithubToken({
|
|
531
|
+
dir: companionGithubDir(socketMode ? laptopInstallEnv(process.env) : undefined),
|
|
532
|
+
repo: full.github.repo,
|
|
533
|
+
secretsDir: full.github.secrets_dir,
|
|
534
|
+
});
|
|
535
|
+
let configMirrorTimer;
|
|
536
|
+
const CONFIG_MIRROR_EVERY_MS = 30_000;
|
|
537
|
+
const companionOn = args.companion && full.companion.enabled;
|
|
538
|
+
let companionUp = false;
|
|
539
|
+
/** `ensureUp` returned: a container is running on this start's account,
|
|
540
|
+
* whether or not the rollover and pi spawn behind it ever finish. */
|
|
541
|
+
let companionContainerStarted = false;
|
|
542
|
+
let companionError;
|
|
543
|
+
// The column exists in every configuration: `--no-companion` and
|
|
544
|
+
// `[companion] enabled = false` decide what runs and what the column says,
|
|
545
|
+
// never whether the right half of the screen is there.
|
|
546
|
+
app.sidebar = new CompanionSidebar(full.companion, companionDocker, {
|
|
547
|
+
available: () => companionUp,
|
|
548
|
+
onUpdate: () => app.repaint(),
|
|
549
|
+
// pi's `/copy` inside the container lands in a tmux paste buffer; the
|
|
550
|
+
// column forwards it here, to the host clipboard.
|
|
551
|
+
onCopy: (text) => app.copyText(text, "the companion"),
|
|
552
|
+
});
|
|
553
|
+
if (!companionOn)
|
|
554
|
+
app.sidebar.setOff(args.companion ? "[companion] enabled = false" : "--no-companion");
|
|
555
|
+
// Bring-up progress goes in that column: it is empty at this moment and
|
|
556
|
+
// already the right shape, so it costs no new surface — and the lines are
|
|
557
|
+
// never written to the normal screen, where the alt screen's clear would
|
|
558
|
+
// erase them (a visible flicker). With the companion off the same column
|
|
559
|
+
// carries the skipped steps, and stays as the place that says why it is off.
|
|
560
|
+
const { StartupProgress } = await import("./startup.js");
|
|
561
|
+
const progress = new StartupProgress(undefined, {
|
|
562
|
+
onUpdate: (p) => {
|
|
563
|
+
// Kept after the bring-up settles on purpose: a `✗ companion container ·
|
|
564
|
+
// docker is not running` is the most useful thing the column can show,
|
|
565
|
+
// and clearing it would delete the diagnosis a second after it appeared.
|
|
566
|
+
// The hand-off is the sidebar's `mirrored` latch — the checklist yields
|
|
567
|
+
// to pi's first real capture and never comes back.
|
|
568
|
+
app.sidebar?.setStartup(p.lines());
|
|
569
|
+
app.repaint();
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
app.sidebar.setStartup(progress.lines());
|
|
573
|
+
// No attach hooks: the TUI never attaches to a VM session — see AppHooks.
|
|
574
|
+
const { installMessageHooks } = await import("./msg.js");
|
|
575
|
+
const { LiveControlSender, installLiveControlHooks } = await import("./livecontrol.js");
|
|
576
|
+
// One control sender for the viewer's input line and for `m` on a worker PR.
|
|
577
|
+
const control = new LiveControlSender(app.cfg, app.ssh);
|
|
578
|
+
installLiveControlHooks(app, control);
|
|
579
|
+
installMessageHooks(app, undefined, control);
|
|
580
|
+
const { installUnwedgeHooks } = await import("./unwedge.js");
|
|
581
|
+
installUnwedgeHooks(app);
|
|
582
|
+
// `I` asks the companion to run its setup skill — one inject into
|
|
583
|
+
// its own column (drained now, or at its next start) — and hands it the
|
|
584
|
+
// keyboard. The same ask `qcr setup` makes from a shell.
|
|
585
|
+
const { requestSetup, commitRequestedProfiles, commitLine } = await import("./setupentry.js");
|
|
586
|
+
const { companionInjectDir } = await import("./companion.js");
|
|
587
|
+
app.hooks.setup = (theApp) => {
|
|
588
|
+
try {
|
|
589
|
+
requestSetup({ injectDir: companionInjectDir(full.companion) });
|
|
590
|
+
}
|
|
591
|
+
catch (err) {
|
|
592
|
+
theApp.notice(`setup: the request could not be written — ${String(err)}`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
theApp.notice(companionUp
|
|
596
|
+
? "setup: asked the companion — it takes it from here"
|
|
597
|
+
: "setup: asked — the companion picks it up when it starts");
|
|
598
|
+
theApp.openCompanion();
|
|
599
|
+
};
|
|
600
|
+
// The profile commits setup asked for, carried out here, on the config
|
|
601
|
+
// mirror's timer — the container cannot see the config home's .git (the
|
|
602
|
+
// home holds secrets/, so it is never mounted whole).
|
|
603
|
+
const commitProfiles = () => {
|
|
604
|
+
for (const r of commitRequestedProfiles())
|
|
605
|
+
app.notice(commitLine(r));
|
|
606
|
+
};
|
|
607
|
+
// The four learning surfaces — [2] memory, [3] digest, [4] tickets,
|
|
608
|
+
// [7] feedback — over one store. Accept-verdict git actions run against
|
|
609
|
+
// this working copy (the canonical repo); the [sink] config decides where
|
|
610
|
+
// the defect auto-filer writes (wayfinder tracker, gated github command
|
|
611
|
+
// channel, or none) and which effort the ticket projection reads.
|
|
612
|
+
const { installLearningTabs } = await import("./learning.js");
|
|
613
|
+
const { execFile } = await import("node:child_process");
|
|
614
|
+
const gitRun = (cmd, args2, stdin) => new Promise((res, rej) => {
|
|
615
|
+
const child = execFile(cmd, args2, { cwd: sandboxRoot() }, (err, stdout, stderr) => {
|
|
616
|
+
if (err)
|
|
617
|
+
rej(new Error(stderr.trim() || err.message));
|
|
618
|
+
else
|
|
619
|
+
res(stdout);
|
|
620
|
+
});
|
|
621
|
+
if (stdin !== undefined)
|
|
622
|
+
child.stdin?.end(stdin);
|
|
623
|
+
else
|
|
624
|
+
child.stdin?.end();
|
|
625
|
+
});
|
|
626
|
+
// One memory store for the whole TUI: OpenViking on the VM, reached over
|
|
627
|
+
// the memory forward's loopback listener, holding both user keys. Every
|
|
628
|
+
// memory surface in the TUI shares this instance — a second one would mean
|
|
629
|
+
// a second sweep and a second health opinion.
|
|
630
|
+
const { tuiMemoryStore, tuiMemoryBaseUrl } = await import("./memory.js");
|
|
631
|
+
// The companion namespace's retired URIs, published into the state home
|
|
632
|
+
// the companion mounts — its recall excludes them.
|
|
633
|
+
const { MEMORY: COMPANION_MEMORY } = await import("./companion.js");
|
|
634
|
+
const { stateHome: memStateHome } = await import("../install.js");
|
|
635
|
+
const memStore = tuiMemoryStore({
|
|
636
|
+
retiredFiles: { companion: join(memStateHome(), COMPANION_MEMORY.retiredFile) },
|
|
637
|
+
});
|
|
638
|
+
// The TUI brings the memory forward up itself, so the Memory tab works
|
|
639
|
+
// without the operator installing the systemd unit first. It adopts a
|
|
640
|
+
// forward that is already listening (the unit, or another qcr tui) and stops
|
|
641
|
+
// only one it started — see memforward.ts for why that split matters.
|
|
642
|
+
// Constructed here, started in the bring-up: `ensure()` probes over HTTP.
|
|
643
|
+
const { MemoryForward, forwardUsable, tuiMemoryContainer } = await import("./memforward.js");
|
|
644
|
+
const memForward = new MemoryForward({
|
|
645
|
+
baseUrl: tuiMemoryBaseUrl(),
|
|
646
|
+
repoRoot: sandboxRoot(),
|
|
647
|
+
// The forward follows the same [tui] target knob — local mode reaches the
|
|
648
|
+
// daemon container's loopback engine via plain docker exec.
|
|
649
|
+
local: transportOf(cfg) === "local",
|
|
650
|
+
sudoDocker: cfg.sudo_docker,
|
|
651
|
+
sshHost: cfg.ssh_host,
|
|
652
|
+
// OpenViking's own container: the memory sidecar once the VM flips
|
|
653
|
+
// `[openviking] endpoint = "remote"`, else the daemon container.
|
|
654
|
+
container: tuiMemoryContainer(cfg),
|
|
655
|
+
// A local engine runs in Docker; with none here, memory is off — said
|
|
656
|
+
// once as the forward's state, never retried as a failure.
|
|
657
|
+
...(!socketMode && transportOf(cfg) === "local"
|
|
658
|
+
? {
|
|
659
|
+
offReason: async () => ((await dockerUp()) ? undefined : MEMORY_OFF_NO_DOCKER),
|
|
660
|
+
}
|
|
661
|
+
: {}),
|
|
662
|
+
log: (line) => app.notice(line),
|
|
663
|
+
});
|
|
664
|
+
const { buildIssueSink, ticketProjection } = await import("./issuesink.js");
|
|
665
|
+
const { readEffortTickets } = await import("./trackertickets.js");
|
|
666
|
+
const projection = ticketProjection(full);
|
|
667
|
+
const { RealDeployFetcher, installDeployHooks, powerSpawnArgv, realLocalRunner } = await import("./deploytab.js");
|
|
668
|
+
const { FileDeployCeremonyStore } = await import("./deploy-ceremony.js");
|
|
669
|
+
const localRunner = realLocalRunner(sandboxRoot());
|
|
670
|
+
// The change register — every feedback-derived commit (identified by its
|
|
671
|
+
// `Ratified-in-session:` trailer), newest first, with a one-key revert on
|
|
672
|
+
// the feedback tab. It reads and writes the local checkout only, through
|
|
673
|
+
// the same read-only local-git seam the deploy tab uses; nothing here
|
|
674
|
+
// touches the VM, GitHub or the gate. Persisted under state/ so the list is
|
|
675
|
+
// on screen before git has been asked anything.
|
|
676
|
+
const { ChangeLog, ChangeRegisterStore } = await import("./changelog-store.js");
|
|
677
|
+
const { stateHome } = await import("../install.js");
|
|
678
|
+
const { PIN_PROPOSALS_FILE, parsePinProposals } = await import("../setup/pins.js");
|
|
679
|
+
const learning = installLearningTabs(app, {
|
|
680
|
+
memory: memStore,
|
|
681
|
+
// The entries setup proposed to pin, marked ☆ on the memory tab.
|
|
682
|
+
readPinProposals: () => {
|
|
683
|
+
try {
|
|
684
|
+
return parsePinProposals(readFileSync(join(stateHome(), PIN_PROPOSALS_FILE), "utf8"));
|
|
685
|
+
}
|
|
686
|
+
catch (err) {
|
|
687
|
+
return err.code === "ENOENT"
|
|
688
|
+
? { state: "absent" }
|
|
689
|
+
: { state: "unreadable", error: String(err) };
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
forward: memForward,
|
|
693
|
+
gitRun,
|
|
694
|
+
repoRoot: sandboxRoot(),
|
|
695
|
+
configPath: liveConfigPath(),
|
|
696
|
+
sink: buildIssueSink(full, app.ssh, memStore),
|
|
697
|
+
issueLabel: full.github.issue_label,
|
|
698
|
+
eod: { time: full.eod.time, timezone: full.eod.timezone },
|
|
699
|
+
changes: new ChangeLog(new ChangeRegisterStore(join(sandboxRoot(), "state", "changes")), localRunner),
|
|
700
|
+
...(projection
|
|
701
|
+
? {
|
|
702
|
+
readTickets: () => readEffortTickets(projection.root, projection.effort),
|
|
703
|
+
ticketsEffort: projection.effort,
|
|
704
|
+
}
|
|
705
|
+
: {}),
|
|
706
|
+
});
|
|
707
|
+
app.hooks.powerRun = (_theApp, action, onData, onExit) => {
|
|
708
|
+
// docker stop/start on the VM (or local docker in local mode), streamed
|
|
709
|
+
// into the tab like a deploy.
|
|
710
|
+
const cmd = powerSpawnArgv(cfg, action);
|
|
711
|
+
const child = spawn(cmd[0], cmd.slice(1), { stdio: ["ignore", "pipe", "pipe"] });
|
|
712
|
+
onData(`$ ${cmd.join(" ")}\n`);
|
|
713
|
+
child.stdout.on("data", (b) => onData(b.toString("utf8")));
|
|
714
|
+
child.stderr.on("data", (b) => onData(b.toString("utf8")));
|
|
715
|
+
child.on("close", (code) => onExit(code));
|
|
716
|
+
child.on("error", (err) => {
|
|
717
|
+
onData(`\nspawn failed: ${String(err)}\n`);
|
|
718
|
+
onExit(1);
|
|
719
|
+
});
|
|
720
|
+
return {
|
|
721
|
+
kill: () => {
|
|
722
|
+
try {
|
|
723
|
+
child.kill("SIGTERM");
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
/* already gone */
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
};
|
|
730
|
+
};
|
|
731
|
+
const deployState = installDeployHooks(app, new RealDeployFetcher(cfg, runner, localRunner), undefined, undefined, new FileDeployCeremonyStore(join(sandboxRoot(), "state", "tui-deploy-ceremony.json")));
|
|
732
|
+
const { installMetricsHooks, localHeadProbe } = await import("./metricstab.js");
|
|
733
|
+
// The metrics OPERATIONS card answers "deploy needed?" through the same
|
|
734
|
+
// read-only local-git seam the deploy tab uses.
|
|
735
|
+
const metricsView = installMetricsHooks(app, undefined, localHeadProbe(localRunner));
|
|
736
|
+
// `[8] scan` — the scan report, read from the state home the
|
|
737
|
+
// companion writes. Its two request files (the stage-2 start, the stop)
|
|
738
|
+
// are answered by the companion's scan belt; promote, dismiss and delta go
|
|
739
|
+
// to the companion as the operator's asks, through the setup entry's inject
|
|
740
|
+
// channel.
|
|
741
|
+
const { installScanTab, ScanReportStore } = await import("./scantab.js");
|
|
742
|
+
const { writeCompanionAsk } = await import("./setupentry.js");
|
|
743
|
+
installScanTab(app, {
|
|
744
|
+
store: new ScanReportStore(stateHome()),
|
|
745
|
+
ask: (text, verb) => {
|
|
746
|
+
try {
|
|
747
|
+
writeCompanionAsk({
|
|
748
|
+
injectDir: companionInjectDir(full.companion),
|
|
749
|
+
suffix: `scan-${verb}`,
|
|
750
|
+
text,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
return { ok: false, line: `✖ scan ${verb}: the ask could not be written — ${String(err)}` };
|
|
755
|
+
}
|
|
756
|
+
return {
|
|
757
|
+
ok: true,
|
|
758
|
+
line: companionUp
|
|
759
|
+
? `scan ${verb}: asked the companion — the report updates when it has acted`
|
|
760
|
+
: `scan ${verb}: asked — the companion picks it up when it starts`,
|
|
761
|
+
};
|
|
762
|
+
},
|
|
763
|
+
companionUp: () => !companionOn || companionError !== undefined ? false : companionUp ? true : undefined,
|
|
764
|
+
});
|
|
765
|
+
// Finished-round evidence, copied down from the VM into a local store the
|
|
766
|
+
// companion reads read-only. Its dir is created in the bring-up,
|
|
767
|
+
// before the container starts — the mount source must exist first, or docker
|
|
768
|
+
// invents it root-owned and the copier can never write into it.
|
|
769
|
+
const { EvidenceCopier, EvidenceStore, SWEEP_PROGRESS_FILE, SweepProgressFile, evidenceWhyIntervalMs, } = await import("./evidence.js");
|
|
770
|
+
const evidence = new EvidenceStore(full.companion.evidence_dir);
|
|
771
|
+
// Ambient feed + alert pipeline + startup healing. All of it hangs
|
|
772
|
+
// off companionOn, so --no-companion really means "none of this runs" —
|
|
773
|
+
// the column itself is not part of "this": it is layout, and it is always
|
|
774
|
+
// there.
|
|
775
|
+
const { companionStarter, installCompanionTabHooks } = await import("./companiontab.js");
|
|
776
|
+
// The companion sidebar is the only companion surface. Mirror + send go
|
|
777
|
+
// through the same local docker seam the lifecycle uses; refresh piggybacks
|
|
778
|
+
// the app poll (the sidebar's own 500ms timer only runs focused/after-send).
|
|
779
|
+
chainOnPoll(app, (a) => void a.sidebar?.refresh());
|
|
780
|
+
// The header carries what the sidebar cannot know for itself: whose day it
|
|
781
|
+
// is (the companion rolls over on the ops-timezone boundary, so a stale day
|
|
782
|
+
// in the header is the first visible sign a rollover was missed) and whether
|
|
783
|
+
// memory is actually reachable — a companion talking with no memory behind
|
|
784
|
+
// it looks identical to a healthy one until you ask it something.
|
|
785
|
+
const { todayDate } = await import("./rollover.js");
|
|
786
|
+
chainOnPoll(app, (a) => {
|
|
787
|
+
const fwd = memForward.view();
|
|
788
|
+
// A native companion has no memory wiring: no Docker, no sidecar.
|
|
789
|
+
const noDocker = selecting?.kind === "native" || (fwd.state === "off" && fwd.detail === MEMORY_OFF_NO_DOCKER);
|
|
790
|
+
a.sidebar?.setHeader({
|
|
791
|
+
day: todayDate(new Date(), process.env, full.ops.timezone),
|
|
792
|
+
memory: noDocker
|
|
793
|
+
? "off"
|
|
794
|
+
: fwd.state === "failed"
|
|
795
|
+
? "degraded"
|
|
796
|
+
: fwd.state === "off"
|
|
797
|
+
? "off"
|
|
798
|
+
: "ok",
|
|
799
|
+
memoryNote: noDocker ? MEMORY_OFF_NO_DOCKER : undefined,
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
// The evidence copier rides the existing poll — no second scheduler,
|
|
803
|
+
// and deliberately not awaited. `sweep()` is re-entrant-safe (a sweep in
|
|
804
|
+
// flight makes the next call a no-op) and every remote read is killed at
|
|
805
|
+
// `evidence_timeout_s`, so a wedged ssh control master stalls the copier and
|
|
806
|
+
// nothing else: the poll keeps polling and the board keeps painting. Runs
|
|
807
|
+
// whether or not the companion is up — the record is worth keeping either
|
|
808
|
+
// way, and it is what the companion will read when it next starts.
|
|
809
|
+
//
|
|
810
|
+
// Its per-PR progress lives beside the TUI's other state files,
|
|
811
|
+
// so a re-exec (every local build) resumes instead of re-running `qcr why`
|
|
812
|
+
// for every PR, and its `why` execs are paced by `evidence_why_interval_s`
|
|
813
|
+
// and held while the board's last status shows a drain or a stale heartbeat.
|
|
814
|
+
const evidenceCopier = new EvidenceCopier(cfg, evidence, runner, {
|
|
815
|
+
sessionDir: full.pi.session_dir,
|
|
816
|
+
timeoutMs: Math.max(5, cfg.evidence_timeout_s) * 1000,
|
|
817
|
+
whyIntervalMs: evidenceWhyIntervalMs(cfg),
|
|
818
|
+
progress: new SweepProgressFile(join(sandboxRoot(), "state", SWEEP_PROGRESS_FILE)),
|
|
819
|
+
onNotice: (text) => app.notice(text),
|
|
820
|
+
});
|
|
821
|
+
chainOnPoll(app, (a) => {
|
|
822
|
+
void evidenceCopier.sweep(a.view.snapshot).catch(() => {
|
|
823
|
+
/* best-effort — a failed copy is retried on the next poll, never fatal */
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
// The durable candidate list behind the `survey` skill. Derived from what
|
|
827
|
+
// is already on this disk — the poll snapshot plus the evidence store's
|
|
828
|
+
// round manifests — so it costs no remote reads and rides the same poll. It lands
|
|
829
|
+
// in the evidence dir, which is already mounted `/evidence:ro`, so the
|
|
830
|
+
// companion reads `/evidence/candidates.json` over an existing channel and
|
|
831
|
+
// cannot write to it.
|
|
832
|
+
//
|
|
833
|
+
// Judged-ness comes back the other way, out of the `#adjudication` memory
|
|
834
|
+
// entries `adjudicate` already mandates — one record, one direction, and no
|
|
835
|
+
// second write path from the companion. It is read at a slow cadence because
|
|
836
|
+
// memory is an HTTP hop and the list only needs it to stop re-offering
|
|
837
|
+
// something settled.
|
|
838
|
+
const { CandidateStore, CandidateSweeper, adjudicationsFrom } = await import("./candidates.js");
|
|
839
|
+
const candidates = new CandidateStore(full.companion.evidence_dir);
|
|
840
|
+
let adjudicationsAt = 0;
|
|
841
|
+
let adjudicationsCache = new Map();
|
|
842
|
+
const candidateSweeper = new CandidateSweeper(candidates, {
|
|
843
|
+
evidenceDir: evidence.dir,
|
|
844
|
+
onNotice: (text) => app.notice(text),
|
|
845
|
+
adjudications: async () => {
|
|
846
|
+
// 5 minutes: an adjudication is a human-scale event, and a stale map only
|
|
847
|
+
// re-offers something already judged for one more cycle.
|
|
848
|
+
if (Date.now() - adjudicationsAt < 300_000)
|
|
849
|
+
return adjudicationsCache;
|
|
850
|
+
adjudicationsAt = Date.now();
|
|
851
|
+
adjudicationsCache = adjudicationsFrom(await memStore.read());
|
|
852
|
+
return adjudicationsCache;
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
chainOnPoll(app, (a) => {
|
|
856
|
+
void candidateSweeper.sweep(a.view.snapshot).catch(() => {
|
|
857
|
+
/* best-effort — the survey is rebuilt from scratch on the next poll */
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
// A reviewer-memory incident older than
|
|
861
|
+
// `[memory] incident_ticket_after_min` becomes one issue-tracker ticket
|
|
862
|
+
// (actor qcr-tui, deduped by the incident's start), read from status.json —
|
|
863
|
+
// never from memory, which is the thing that is down (the `#bug` filer reads
|
|
864
|
+
// memory, so it could never report this). Independent of the companion:
|
|
865
|
+
// the board banner and this ticket reach a person with the column off.
|
|
866
|
+
const { maybeFileCurationIncident, maybeFileMemoryIncident, newIncidentFilerMemo } = await import("./memincident.js");
|
|
867
|
+
const incidentMemo = newIncidentFilerMemo();
|
|
868
|
+
const incidentTracker = full.sink.kind === "wayfinder" && full.sink.root && full.sink.effort && full.sink.parent
|
|
869
|
+
? { root: full.sink.root, effort: full.sink.effort, parent: full.sink.parent }
|
|
870
|
+
: undefined;
|
|
871
|
+
/** One incident filer's notices: a filing, a missing tracker, a failure —
|
|
872
|
+
* each distinct line once, so a tracker that keeps failing does not repaint
|
|
873
|
+
* the notice every poll. */
|
|
874
|
+
const incidentNotifier = (what) => {
|
|
875
|
+
let last = "";
|
|
876
|
+
return (a, out) => {
|
|
877
|
+
const line = out.kind === "filed"
|
|
878
|
+
? `${what} filed as ${out.ticketId} (${out.file})`
|
|
879
|
+
: out.kind === "no-tracker" || out.kind === "failed"
|
|
880
|
+
? out.line
|
|
881
|
+
: "";
|
|
882
|
+
if (line !== "" && line !== last) {
|
|
883
|
+
last = line;
|
|
884
|
+
a.notice(line);
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
};
|
|
888
|
+
const incidentNotice = incidentNotifier("memory incident");
|
|
889
|
+
chainOnPoll(app, (a) => {
|
|
890
|
+
const out = maybeFileMemoryIncident(incidentTracker, a.view.snapshot?.memory, {
|
|
891
|
+
fresh: isFreshHealth(a.view.health),
|
|
892
|
+
now: new Date(),
|
|
893
|
+
afterMs: full.memory.incident_ticket_after_min * 60_000,
|
|
894
|
+
memo: incidentMemo,
|
|
895
|
+
});
|
|
896
|
+
incidentNotice(a, out);
|
|
897
|
+
});
|
|
898
|
+
// The learner's EOD curation that did not reach memory (no receipt, or
|
|
899
|
+
// lessons memory does not hold) — the same person path: one
|
|
900
|
+
// ticket per curation incident (its oldest unresolved failure) older than
|
|
901
|
+
// the same threshold, from status.json `memory.curation`.
|
|
902
|
+
const curationMemo = newIncidentFilerMemo();
|
|
903
|
+
const curationNotice = incidentNotifier("EOD curation incident");
|
|
904
|
+
chainOnPoll(app, (a) => {
|
|
905
|
+
const out = maybeFileCurationIncident(incidentTracker, a.view.snapshot?.memory, {
|
|
906
|
+
fresh: isFreshHealth(a.view.health),
|
|
907
|
+
now: new Date(),
|
|
908
|
+
afterMs: full.memory.incident_ticket_after_min * 60_000,
|
|
909
|
+
memo: curationMemo,
|
|
910
|
+
});
|
|
911
|
+
curationNotice(a, out);
|
|
912
|
+
});
|
|
913
|
+
// ---- the connection triage ------------------------------------------------
|
|
914
|
+
//
|
|
915
|
+
// The TUI does the three-way ssh triage behind the first frame:
|
|
916
|
+
// `ensureSshMaster` before the alt screen would be both a network call
|
|
917
|
+
// gating the first paint and, on a bad day, an interactive prompt. Nothing
|
|
918
|
+
// here is self-healing by design; the TUI reports the cause and the exact
|
|
919
|
+
// command, and `s` re-runs the check.
|
|
920
|
+
const { diagnosisLine, triageConnection } = await import("./sshtriage.js");
|
|
921
|
+
const applyDiagnosis = (d) => {
|
|
922
|
+
app.view.connection = d;
|
|
923
|
+
if (d.kind === "ok" || d.kind === "local")
|
|
924
|
+
progress.done("ssh", d.cause);
|
|
925
|
+
else
|
|
926
|
+
progress.fail("ssh", diagnosisLine(d));
|
|
927
|
+
app.notice(d.kind === "ok" || d.kind === "local" ? `✓ ${d.cause}` : `✖ ${diagnosisLine(d)}`);
|
|
928
|
+
app.repaint();
|
|
929
|
+
};
|
|
930
|
+
// `s` starts its own async chain, independent of the bring-up. Tracked for
|
|
931
|
+
// the same reason: quit must not tear the terminal down underneath it.
|
|
932
|
+
let pendingReconnect = Promise.resolve();
|
|
933
|
+
app.hooks.reconnect = () => {
|
|
934
|
+
progress.begin("ssh", "re-checking");
|
|
935
|
+
app.notice("checking the connection to the VM…");
|
|
936
|
+
pendingReconnect = (async () => {
|
|
937
|
+
let d = await triageConnection(cfg);
|
|
938
|
+
// A missing key is the one class the operator can clear from here: `s`
|
|
939
|
+
// is them asking for the single native passphrase prompt. A wedged
|
|
940
|
+
// master or a dead host is reported with its command instead — prompting
|
|
941
|
+
// against either would just hang.
|
|
942
|
+
if (d.kind === "no-agent-key" || d.kind === "unknown") {
|
|
943
|
+
onRealTerminal(() => {
|
|
944
|
+
ensureSshMaster(cfg);
|
|
945
|
+
});
|
|
946
|
+
d = await triageConnection(cfg);
|
|
947
|
+
}
|
|
948
|
+
applyDiagnosis(d);
|
|
949
|
+
// Awaited, not fired and forgotten: it is part of "re-run the check and
|
|
950
|
+
// report the new outcome", and quit waits for this chain.
|
|
951
|
+
await app.poller.tick();
|
|
952
|
+
})().catch((err) => app.notice(`✖ ssh check failed: ${String(err instanceof Error ? err.message : err)}`));
|
|
953
|
+
};
|
|
954
|
+
// ---- the bring-up: everything external, behind the first frame ------------
|
|
955
|
+
const startCompanion = async () => {
|
|
956
|
+
try {
|
|
957
|
+
// Before the container, never after: the companion's pi extension picks
|
|
958
|
+
// the user namespace it writes into by listing `viking://user/`, and
|
|
959
|
+
// caches that choice for the life of the process. A namespace whose
|
|
960
|
+
// directory does not exist yet is invisible to it, so it falls back to
|
|
961
|
+
// `user/default` and every write is refused, reported only as "access
|
|
962
|
+
// denied". Needs the forward up, so it is awaited here.
|
|
963
|
+
progress.begin("memory-forward");
|
|
964
|
+
const fwd = await memForward.ensure();
|
|
965
|
+
if (fwd.state === "failed")
|
|
966
|
+
progress.fail("memory-forward", fwd.detail);
|
|
967
|
+
else
|
|
968
|
+
progress.done("memory-forward", fwd.state === "off" ? fwd.detail : fwd.state);
|
|
969
|
+
if (forwardUsable(fwd)) {
|
|
970
|
+
progress.begin("memory-namespaces");
|
|
971
|
+
const notes = await memStore.ensureNamespaces();
|
|
972
|
+
for (const note of notes)
|
|
973
|
+
app.notice(`memory: ${note}`);
|
|
974
|
+
progress.done("memory-namespaces", notes.length > 0 ? `${notes.length} note${notes.length === 1 ? "" : "s"}` : "ready");
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
progress.skip("memory-namespaces", fwd.state === "off" ? fwd.detail : "forward down");
|
|
978
|
+
}
|
|
979
|
+
progress.begin("container");
|
|
980
|
+
// Before the container, so a cold start finds the token already there.
|
|
981
|
+
// Total: no credential, or GitHub down, is a note — never a failed start.
|
|
982
|
+
const gh = await ghToken.refresh();
|
|
983
|
+
progress.note("container", tokenOutcomeLine(gh));
|
|
984
|
+
app.notice(`companion: ${tokenOutcomeLine(gh)}`);
|
|
985
|
+
ghToken.start((o) => {
|
|
986
|
+
if (o.kind === "failed")
|
|
987
|
+
app.notice(`companion: ${tokenOutcomeLine(o)}`);
|
|
988
|
+
});
|
|
989
|
+
// The companion's read-only mirror of the live config — a copy, not a
|
|
990
|
+
// file bind, so a config.toml replaced by rename is still seen (see
|
|
991
|
+
// CONTAINER.configLive). Kept in step on the same timer as the token.
|
|
992
|
+
const syncConfig = () => {
|
|
993
|
+
const m = companion.syncConfigMirror();
|
|
994
|
+
if (typeof m === "object")
|
|
995
|
+
app.notice(`companion: config mirror NOT refreshed — ${m.failed}`);
|
|
996
|
+
commitProfiles();
|
|
997
|
+
};
|
|
998
|
+
syncConfig();
|
|
999
|
+
configMirrorTimer ??= setInterval(syncConfig, CONFIG_MIRROR_EVERY_MS);
|
|
1000
|
+
configMirrorTimer.unref();
|
|
1001
|
+
await companion.ensureUp((line) => {
|
|
1002
|
+
progress.note("container", line);
|
|
1003
|
+
app.notice(`companion: ${line}`);
|
|
1004
|
+
});
|
|
1005
|
+
companionContainerStarted = true;
|
|
1006
|
+
progress.done("container");
|
|
1007
|
+
// The container's memory network exists now: a forward we started
|
|
1008
|
+
// before it (a cold host) binds the bridge address it could not.
|
|
1009
|
+
await memForward.rebindMissing();
|
|
1010
|
+
// Daily rollover — strictly between "container is up" and
|
|
1011
|
+
// "pi is spawned": on the first start of a new ops day it archives
|
|
1012
|
+
// yesterday, kills the session and queues the opening context, so the
|
|
1013
|
+
// spawnPi below births the fresh conversation that reads it.
|
|
1014
|
+
progress.begin("rollover");
|
|
1015
|
+
const { CompanionRollover } = await import("./rollover.js");
|
|
1016
|
+
const { latestDigestHeadline } = await import("../digest.js");
|
|
1017
|
+
const rollover = new CompanionRollover(full.companion, companionDocker, {
|
|
1018
|
+
statePath: join(companionStateDir, "rollover.json"),
|
|
1019
|
+
policyStatePath,
|
|
1020
|
+
templatePath: join(sandboxRoot(), "prompts", "companion-day-open.md"),
|
|
1021
|
+
injectDir: companion.hostInjectDir(),
|
|
1022
|
+
localDigestHeadline: () => latestDigestHeadline(),
|
|
1023
|
+
ownerName: full.owner.name,
|
|
1024
|
+
timeZone: full.ops.timezone,
|
|
1025
|
+
// The first-run interview — the marker path is the setup wizard's
|
|
1026
|
+
// contract; the template makes it work without the wizard too.
|
|
1027
|
+
firstRunTemplatePath: join(sandboxRoot(), "prompts", "companion-first-run.md"),
|
|
1028
|
+
firstRunMarkerPath: join(companionStateDir, "first-run.json"),
|
|
1029
|
+
});
|
|
1030
|
+
const report = await rollover.run();
|
|
1031
|
+
if (report.deferred) {
|
|
1032
|
+
// The container could not be read, so the day is deliberately not
|
|
1033
|
+
// recorded and yesterday's conversation continues. Loud, because a
|
|
1034
|
+
// rollover that silently did not happen reads as one that did.
|
|
1035
|
+
app.notice(`companion: day rollover DEFERRED — ${report.deferred}`);
|
|
1036
|
+
}
|
|
1037
|
+
else if (report.stoodDown) {
|
|
1038
|
+
app.notice(`companion: ${report.stoodDown}`);
|
|
1039
|
+
}
|
|
1040
|
+
else if (report.verdict.kind === "rollover") {
|
|
1041
|
+
app.notice(`companion: new day ${report.verdict.today} — fresh conversation (${report.steps.length} steps)`);
|
|
1042
|
+
}
|
|
1043
|
+
for (const warning of report.warnings ?? [])
|
|
1044
|
+
app.notice(`companion: ${warning}`);
|
|
1045
|
+
if (report.interviewFile) {
|
|
1046
|
+
app.notice("companion: first start on this installation — greeting + calibration interview queued");
|
|
1047
|
+
}
|
|
1048
|
+
progress.done("rollover", report.deferred
|
|
1049
|
+
? `deferred — ${report.deferred}`
|
|
1050
|
+
: report.stoodDown
|
|
1051
|
+
? "rolled over by another start"
|
|
1052
|
+
: report.verdict.kind === "rollover"
|
|
1053
|
+
? `new day ${report.verdict.today}`
|
|
1054
|
+
: "same day");
|
|
1055
|
+
progress.begin("companion-pi");
|
|
1056
|
+
await companion.spawnPi();
|
|
1057
|
+
progress.done("companion-pi");
|
|
1058
|
+
companionUp = true;
|
|
1059
|
+
companionError = undefined;
|
|
1060
|
+
app.repaint();
|
|
1061
|
+
}
|
|
1062
|
+
catch (err) {
|
|
1063
|
+
companionError = String(err instanceof Error ? err.message : err);
|
|
1064
|
+
for (const key of ["memory-forward", ...COMPANION_ONLY_STEPS]) {
|
|
1065
|
+
const step = progress.steps().find((s) => s.key === key);
|
|
1066
|
+
if (step?.state === "running")
|
|
1067
|
+
progress.fail(key, companionError);
|
|
1068
|
+
else if (step?.state === "pending")
|
|
1069
|
+
progress.skip(key, "companion unavailable");
|
|
1070
|
+
}
|
|
1071
|
+
app.notice(`companion unavailable: ${companionError}`);
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
const companionStateDir = join(sandboxRoot(), "state", "companion");
|
|
1075
|
+
const policyStatePath = join(companionStateDir, "alert-policy.json");
|
|
1076
|
+
// Boot and retry share ONE start: the channels below are wired after the
|
|
1077
|
+
// first start that leaves the companion up, whichever path that was.
|
|
1078
|
+
const startAndWire = companionStarter({
|
|
1079
|
+
start: startCompanion,
|
|
1080
|
+
up: () => companionUp,
|
|
1081
|
+
wire: () => wireCompanionChannels(),
|
|
1082
|
+
});
|
|
1083
|
+
installCompanionTabHooks(app, {
|
|
1084
|
+
available: () => companionUp,
|
|
1085
|
+
failure: () => companionError,
|
|
1086
|
+
retry: companionOn ? startAndWire : undefined,
|
|
1087
|
+
});
|
|
1088
|
+
/** Everything the companion feed / alert pipeline needs, once it is up. */
|
|
1089
|
+
const wireCompanionChannels = async () => {
|
|
1090
|
+
const { FeedWriter, buildTuiView } = await import("./feed.js");
|
|
1091
|
+
const feed = new FeedWriter(full.companion.feed_dir);
|
|
1092
|
+
// Publish the operator's view: the last painted frame plus the derived
|
|
1093
|
+
// state behind it. Two triggers, one throttle: the poll below (so it
|
|
1094
|
+
// keeps up with the daemon) and every repaint (so it keeps up with the
|
|
1095
|
+
// operator's keystrokes — "what is on their screen" is only true if it
|
|
1096
|
+
// tracks the screen). Files only: this never injects a companion turn,
|
|
1097
|
+
// and the companion reads it on demand through the `tui_*` tools.
|
|
1098
|
+
const publishView = () => {
|
|
1099
|
+
try {
|
|
1100
|
+
// One read of the frame for both: the view's screen metadata (rows,
|
|
1101
|
+
// chars, truncation) describes the text written beside it.
|
|
1102
|
+
const screen = app.screenText();
|
|
1103
|
+
feed.writeView(buildTuiView({
|
|
1104
|
+
now: new Date(),
|
|
1105
|
+
screen,
|
|
1106
|
+
tab: app.tab,
|
|
1107
|
+
snapshot: app.view.snapshot,
|
|
1108
|
+
health: app.view.health,
|
|
1109
|
+
selectedPr: app.currentPr(),
|
|
1110
|
+
panePr: app.view.piPr,
|
|
1111
|
+
paneFocused: app.view.piFocus === true,
|
|
1112
|
+
notice: app.view.noticeLine,
|
|
1113
|
+
deploy: deployState,
|
|
1114
|
+
tuiMemoryRequests: memStore.requestCounts(),
|
|
1115
|
+
}), screen);
|
|
1116
|
+
}
|
|
1117
|
+
catch {
|
|
1118
|
+
// Publishing the view is a side effect of painting it — a failure here
|
|
1119
|
+
// must never surface on the notice line, let alone break the poll.
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
app.afterRepaint = publishView;
|
|
1123
|
+
chainOnPoll(app, () => publishView());
|
|
1124
|
+
chainOnPoll(app, (a) => {
|
|
1125
|
+
const snap = a.view.snapshot;
|
|
1126
|
+
if (!snap)
|
|
1127
|
+
return;
|
|
1128
|
+
try {
|
|
1129
|
+
feed.write(snap, a.rawEvents, a.view.health.kind);
|
|
1130
|
+
}
|
|
1131
|
+
catch (err) {
|
|
1132
|
+
a.notice(`✖ feed write failed: ${String(err instanceof Error ? err.message : err)}`);
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
const { AlertConsumer, AlertPolicy, filePolicyStore, notifySendRunner } = await import("./alerts.js");
|
|
1136
|
+
// Persisted per-cause memory: "already told you" survives a TUI restart
|
|
1137
|
+
// inside the same ops day, and resets with the day.
|
|
1138
|
+
const policy = new AlertPolicy(full.companion.cooldown_min, () => new Date(), filePolicyStore(policyStatePath), full.ops.timezone);
|
|
1139
|
+
// No overseer inject: the overseer is retired (rule 8). An alert is the
|
|
1140
|
+
// status line, the badge and the desktop notification.
|
|
1141
|
+
const consumer = new AlertConsumer(full.companion.alerts_dir, policy, {
|
|
1142
|
+
notice: (text) => app.notice(text),
|
|
1143
|
+
badge: (badge) => app.setAlertBadge(badge),
|
|
1144
|
+
notify: notifySendRunner,
|
|
1145
|
+
});
|
|
1146
|
+
consumer.start(Math.max(1, cfg.poll_interval_s) * 1000);
|
|
1147
|
+
alerts = consumer;
|
|
1148
|
+
// The bot's companion inbox (anomalies, heartbeat, login alerts, the EOD
|
|
1149
|
+
// sweep): pulled over the TUI's one runner into the companion's inject dir
|
|
1150
|
+
// and the alerts feed above, then acked back. Rule 8, src/tui/inboxpull.ts.
|
|
1151
|
+
const { InboxPuller } = await import("./inboxpull.js");
|
|
1152
|
+
const { companionInjectDir: injectDirOf } = await import("./companion.js");
|
|
1153
|
+
const inboxPuller = new InboxPuller({
|
|
1154
|
+
cfg,
|
|
1155
|
+
ssh: runner,
|
|
1156
|
+
injectDir: injectDirOf(full.companion),
|
|
1157
|
+
alertsDir: full.companion.alerts_dir,
|
|
1158
|
+
}, (line) => app.notice(line));
|
|
1159
|
+
chainOnPoll(app, () => {
|
|
1160
|
+
void inboxPuller.tick().catch(() => {
|
|
1161
|
+
/* best-effort — never break the poll loop */
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
// Daemon-detected pi auth failures (status.json `pi_auth_expired`) ride
|
|
1165
|
+
// the same pipeline: on each poll, bridge a fresh flag into one alert
|
|
1166
|
+
// file; the consumer above does badge + notify + cooldown.
|
|
1167
|
+
const { maybeWritePiAuthAlert, maybeWriteMemoryAlert, maybeWriteCurationAlert, maybeWriteWorkerCrashAlerts, } = await import("./alerts.js");
|
|
1168
|
+
const piAuthMem = { lastWritten: "" };
|
|
1169
|
+
chainOnPoll(app, (a) => {
|
|
1170
|
+
try {
|
|
1171
|
+
maybeWritePiAuthAlert(full.companion.alerts_dir, a.view.snapshot?.pi_auth_expired, piAuthMem);
|
|
1172
|
+
}
|
|
1173
|
+
catch {
|
|
1174
|
+
// alerting is best-effort; never break the poll loop
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
// Reviewer memory refusing a key or down — one desktop notification per
|
|
1178
|
+
// transition, from status.json `memory` (never from OpenViking), and only
|
|
1179
|
+
// from a fresh snapshot.
|
|
1180
|
+
const memoryAlertMem = { lastWritten: "" };
|
|
1181
|
+
chainOnPoll(app, (a) => {
|
|
1182
|
+
try {
|
|
1183
|
+
maybeWriteMemoryAlert(full.companion.alerts_dir, a.view.snapshot?.memory, isFreshHealth(a.view.health), memoryAlertMem);
|
|
1184
|
+
}
|
|
1185
|
+
catch {
|
|
1186
|
+
// alerting is best-effort; never break the poll loop
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
// The day's EOD curation did not reach memory (no receipt,
|
|
1190
|
+
// or a lesson refused / not found) — one notification per (state, day),
|
|
1191
|
+
// from status.json `memory.curation`, fresh snapshots only.
|
|
1192
|
+
const curationAlertMem = { lastWritten: "" };
|
|
1193
|
+
chainOnPoll(app, (a) => {
|
|
1194
|
+
try {
|
|
1195
|
+
maybeWriteCurationAlert(full.companion.alerts_dir, a.view.snapshot?.memory, isFreshHealth(a.view.health), curationAlertMem);
|
|
1196
|
+
}
|
|
1197
|
+
catch {
|
|
1198
|
+
// alerting is best-effort; never break the poll loop
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
// A PR parked by the worker crash-loop breaker, or the bot drained by it
|
|
1202
|
+
// — one notification per park / per drain, from a fresh snapshot.
|
|
1203
|
+
const workerCrashMem = { written: new Set() };
|
|
1204
|
+
chainOnPoll(app, (a) => {
|
|
1205
|
+
try {
|
|
1206
|
+
maybeWriteWorkerCrashAlerts(full.companion.alerts_dir, a.view.snapshot, isFreshHealth(a.view.health), workerCrashMem);
|
|
1207
|
+
}
|
|
1208
|
+
catch {
|
|
1209
|
+
// alerting is best-effort; never break the poll loop
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
// No memory request-file channel here, and no memory feed: the companion
|
|
1213
|
+
// reaches OpenViking directly, over a host-owned forward, holding both
|
|
1214
|
+
// user keys. A second write path is exactly what must not exist — do not
|
|
1215
|
+
// add one as an "offline fallback" (the companion boundary).
|
|
1216
|
+
// The companion → TUI action channel. Sweep discipline is the usual one
|
|
1217
|
+
// (poll cadence, busy flag so a slow sweep never stacks);
|
|
1218
|
+
// processTuiActions itself archives-before-execute and writes the result
|
|
1219
|
+
// files. Each executed action's trace lands on the notice line — actions
|
|
1220
|
+
// execute instantly, with no confirm gate.
|
|
1221
|
+
const { processTuiActions, parseCompanionCommand } = await import("./actions.js");
|
|
1222
|
+
const tuiActionsDir = join(full.companion.alerts_dir, "tui-actions");
|
|
1223
|
+
// The companion's commands/ (rule 8): under the state home it mounts rw,
|
|
1224
|
+
// where the seeded EOD sweep writes its receipt. Each file is one command
|
|
1225
|
+
// of the companion's tier, forwarded once down the operator route.
|
|
1226
|
+
const { COMPANION_COMMANDS_DIR } = await import("../install.js");
|
|
1227
|
+
const companionCommandsDir = join(companion.hostHomes().state, COMPANION_COMMANDS_DIR);
|
|
1228
|
+
const driver = buildTuiDriver(app, learning, metricsView, deployState);
|
|
1229
|
+
let tuiActBusy = false;
|
|
1230
|
+
chainOnPoll(app, (a) => {
|
|
1231
|
+
if (tuiActBusy)
|
|
1232
|
+
return;
|
|
1233
|
+
tuiActBusy = true;
|
|
1234
|
+
void processTuiActions(tuiActionsDir, driver)
|
|
1235
|
+
.then(async (done) => {
|
|
1236
|
+
for (const r of done)
|
|
1237
|
+
a.notice(r.trace ?? `companion tui-action ${r.file}: ✖ ${r.message}`);
|
|
1238
|
+
for (const r of await processTuiActions(companionCommandsDir, driver, parseCompanionCommand))
|
|
1239
|
+
a.notice(r.trace ?? `companion command ${r.file}: ✖ ${r.message}`);
|
|
1240
|
+
})
|
|
1241
|
+
.catch(() => {
|
|
1242
|
+
/* best-effort — never break the poll loop */
|
|
1243
|
+
})
|
|
1244
|
+
.finally(() => {
|
|
1245
|
+
tuiActBusy = false;
|
|
1246
|
+
});
|
|
1247
|
+
});
|
|
1248
|
+
};
|
|
1249
|
+
let alerts;
|
|
1250
|
+
/**
|
|
1251
|
+
* PHASE 1. Every external thing the TUI does at startup, in one place,
|
|
1252
|
+
* behind the first frame and reported into the companion column.
|
|
1253
|
+
*/
|
|
1254
|
+
const bringUp = async () => {
|
|
1255
|
+
// The tracker scan is a filesystem walk, so it waits for the frame too.
|
|
1256
|
+
learning.store.reloadTickets(); // total: an unreadable tracker is an UNKNOWN pane, never a failed start
|
|
1257
|
+
// The triage runs CONCURRENTLY with the companion: a wedged ssh master can
|
|
1258
|
+
// cost its probe seconds, and the companion has nothing to do with ssh.
|
|
1259
|
+
//
|
|
1260
|
+
// `begin` first, and it is not decoration: the probe chain is the slowest
|
|
1261
|
+
// thing here (a blackholed host costs its ConnectTimeout), so this is the
|
|
1262
|
+
// step most likely to need the overdue warning — and a step left `pending`
|
|
1263
|
+
// says "nobody has reached this yet", which would be a lie the moment the
|
|
1264
|
+
// probes start.
|
|
1265
|
+
progress.begin("ssh");
|
|
1266
|
+
const triaged = triageConnection(cfg)
|
|
1267
|
+
.then(applyDiagnosis)
|
|
1268
|
+
.catch((err) => progress.fail("ssh", `probe failed: ${String(err instanceof Error ? err.message : err)}`));
|
|
1269
|
+
// The evidence store's dir must exist before the container mounts
|
|
1270
|
+
// it (docker would invent it root-owned), and the copier writes into it
|
|
1271
|
+
// whether or not the companion is up.
|
|
1272
|
+
try {
|
|
1273
|
+
evidence.ensure();
|
|
1274
|
+
}
|
|
1275
|
+
catch (err) {
|
|
1276
|
+
app.notice(`✖ evidence store ${evidence.dir}: ${String(err instanceof Error ? err.message : err)}`);
|
|
1277
|
+
}
|
|
1278
|
+
if (companionOn) {
|
|
1279
|
+
await startAndWire();
|
|
1280
|
+
}
|
|
1281
|
+
else {
|
|
1282
|
+
for (const key of COMPANION_ONLY_STEPS) {
|
|
1283
|
+
progress.skip(key, "--no-companion");
|
|
1284
|
+
}
|
|
1285
|
+
// The Memory tab still needs the forward even with the companion off.
|
|
1286
|
+
progress.begin("memory-forward");
|
|
1287
|
+
const fwd = await memForward.ensure();
|
|
1288
|
+
if (fwd.state === "failed")
|
|
1289
|
+
progress.fail("memory-forward", fwd.detail);
|
|
1290
|
+
else
|
|
1291
|
+
progress.done("memory-forward", fwd.state === "off" ? fwd.detail : fwd.state);
|
|
1292
|
+
}
|
|
1293
|
+
await triaged;
|
|
1294
|
+
};
|
|
1295
|
+
// The first poll is a step like any other: it begins when the poller does
|
|
1296
|
+
// (inside `run()` below) and completes on the first snapshot. Until then the
|
|
1297
|
+
// board says PRS (?) — an honest unknown, never a count of zero.
|
|
1298
|
+
progress.begin("poll");
|
|
1299
|
+
chainOnPoll(app, (a) => {
|
|
1300
|
+
const step = progress.steps().find((s) => s.key === "poll");
|
|
1301
|
+
if (a.view.snapshot) {
|
|
1302
|
+
if (step?.state !== "done")
|
|
1303
|
+
progress.done("poll", `${a.view.snapshot.prs.length} PRs`);
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
if (step?.state === "running" && a.poller.state.consecutiveFailures >= 2) {
|
|
1307
|
+
progress.fail("poll", app.view.connection ? diagnosisLine(app.view.connection) : "status.json unreadable");
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
// Restore a hot-reload snapshot before run(), so the first paint is already
|
|
1311
|
+
// on the right tab with the right selection.
|
|
1312
|
+
const resumed = args.resumeState ? readResumeState(args.resumeState) : undefined;
|
|
1313
|
+
if (resumed) {
|
|
1314
|
+
// A tab name this build does not know (e.g. from an older build) lands on
|
|
1315
|
+
// the board.
|
|
1316
|
+
if (isTab(resumed.tab))
|
|
1317
|
+
app.tab = resumed.tab;
|
|
1318
|
+
app.view.eventsScroll = resumed.eventsScroll;
|
|
1319
|
+
app.view.eventsFocus = resumed.eventsFocus;
|
|
1320
|
+
if (app.sidebar && resumed.sidebarWidth !== undefined) {
|
|
1321
|
+
app.sidebar.preferredWidth = restorePreferredSidebarWidth(resumed.sidebarWidth);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
// Hot-reload: watch our own dist/, the build stamp and (without acting) the
|
|
1325
|
+
// core's dist/, and re-exec when a whole build lands (reload.ts says why a
|
|
1326
|
+
// core write alone never reloads).
|
|
1327
|
+
const reloadState = resumeStatePath(sandboxRoot());
|
|
1328
|
+
const reloader = args.reload
|
|
1329
|
+
? new HotReloader({
|
|
1330
|
+
distDir: join(sandboxRoot(), "dist"),
|
|
1331
|
+
buildStamp: join(sandboxRoot(), BUILD_STAMP),
|
|
1332
|
+
// A core-only change built without the stamp (a raw `tsc -b`, an
|
|
1333
|
+
// IDE): the core's dist/ only marks it, the app's clean build info
|
|
1334
|
+
// reloads (reload.ts).
|
|
1335
|
+
coreDistDir: join(sandboxRoot(), CORE_DIST),
|
|
1336
|
+
appBuildInfo: join(sandboxRoot(), APP_BUILD_INFO),
|
|
1337
|
+
stateFile: reloadState,
|
|
1338
|
+
snapshot: () => ({
|
|
1339
|
+
tab: app.tab,
|
|
1340
|
+
pr: app.currentPr(),
|
|
1341
|
+
eventsScroll: app.view.eventsScroll,
|
|
1342
|
+
eventsFocus: app.view.eventsFocus,
|
|
1343
|
+
sidebarWidth: app.sidebar?.preferredWidth,
|
|
1344
|
+
saved_at: "",
|
|
1345
|
+
}),
|
|
1346
|
+
// The forward must be released here, not in the cleanup after
|
|
1347
|
+
// `await running`. `realReexec` calls this teardown and then
|
|
1348
|
+
// spawnSync's the new build synchronously — `app.quit()` merely
|
|
1349
|
+
// resolves a promise, so that cleanup is a microtask which cannot run
|
|
1350
|
+
// until spawnSync returns, i.e. not until the child has exited. Left
|
|
1351
|
+
// to it, the old process would sit inside spawnSync holding :1933
|
|
1352
|
+
// with a FROZEN event loop: the new TUI finds the port taken, cannot
|
|
1353
|
+
// bind, and memory is dead for the rest of the session.
|
|
1354
|
+
// Same reasoning covers in-flight ssh: the per-call SIGKILL in
|
|
1355
|
+
// `realSshRunner` is a timer on the loop that spawnSync is about to
|
|
1356
|
+
// freeze, so a capture/poll/why still running at reload time is
|
|
1357
|
+
// orphaned forever. Against a wedged ControlMaster (an ssh config
|
|
1358
|
+
// block with no ServerAliveInterval lets a network blip hang the
|
|
1359
|
+
// master and every slave riding it) those children never exit on
|
|
1360
|
+
// their own either, and they stack up one generation per reload.
|
|
1361
|
+
// Kill them here, while the loop still turns.
|
|
1362
|
+
reexec: (file) => realReexec(file, () => {
|
|
1363
|
+
killInFlightSsh();
|
|
1364
|
+
memForward.stop();
|
|
1365
|
+
app.quit();
|
|
1366
|
+
}),
|
|
1367
|
+
})
|
|
1368
|
+
: undefined;
|
|
1369
|
+
// ---- the first frame ------------------------------------------------------
|
|
1370
|
+
//
|
|
1371
|
+
// Deep link: `qcr tui <pr>` is the board with that PR selected — nothing
|
|
1372
|
+
// opens over it.
|
|
1373
|
+
const initial = target.kind === "pr" ? target.pr : resumed?.pr;
|
|
1374
|
+
const running = app.run(initial);
|
|
1375
|
+
// Only now: a recursive dist watcher walks the tree, and the bring-up talks
|
|
1376
|
+
// to ssh, docker and OpenViking. Neither may precede the frame above.
|
|
1377
|
+
reloader?.start();
|
|
1378
|
+
// Kept, not dropped on the floor: quitting during the bring-up must not
|
|
1379
|
+
// leave it running against the teardown below, or `spawnPi()` could land
|
|
1380
|
+
// after `gracefulStop()` (a clean quit that leaves a freshly spawned
|
|
1381
|
+
// companion behind) and `memForward.stop()` could close a forward the
|
|
1382
|
+
// bring-up was still about to use.
|
|
1383
|
+
const bringUpDone = bringUp().catch(() => {
|
|
1384
|
+
/* every step already reports itself; a bring-up cannot fail the quit */
|
|
1385
|
+
});
|
|
1386
|
+
await running;
|
|
1387
|
+
// Let the bring-up finish before tearing down what it is still touching.
|
|
1388
|
+
// Bounded, and announced: this runs AFTER the terminal is restored, so a
|
|
1389
|
+
// silent wait here is a terminal that looks dead — the same reasoning the
|
|
1390
|
+
// companion stop below is written under.
|
|
1391
|
+
const inFlight = Promise.allSettled([bringUpDone, pendingReconnect]);
|
|
1392
|
+
if (!(await settledWithin(inFlight, BRING_UP_QUIT_GRACE_MS))) {
|
|
1393
|
+
out.write("qcr tui: waiting for startup to finish before shutting down…\n");
|
|
1394
|
+
await settledWithin(inFlight, BRING_UP_QUIT_GRACE_MS * 3);
|
|
1395
|
+
}
|
|
1396
|
+
reloader?.stop();
|
|
1397
|
+
progress.stop();
|
|
1398
|
+
alerts?.stop();
|
|
1399
|
+
ghToken.stop();
|
|
1400
|
+
if (configMirrorTimer)
|
|
1401
|
+
clearInterval(configMirrorTimer);
|
|
1402
|
+
// Closes only a forward THIS process bound; an adopted one (systemd unit,
|
|
1403
|
+
// another qcr tui) is left running on purpose — see memforward.ts.
|
|
1404
|
+
memForward.stop();
|
|
1405
|
+
// Clean quit stops the container (conversation + auth survive on volumes).
|
|
1406
|
+
// A crash deliberately leaves it running — the next start adopts it.
|
|
1407
|
+
//
|
|
1408
|
+
// This runs after the terminal is restored, so every millisecond it takes is
|
|
1409
|
+
// a terminal that looks dead: board gone, prompt not back, nothing
|
|
1410
|
+
// responding. Say what is happening: a slow docker, or a container whose
|
|
1411
|
+
// PID 1 ignores SIGTERM (so `docker stop` runs to its full timeout), must
|
|
1412
|
+
// not look like a hang.
|
|
1413
|
+
await stopCompanionOnQuit({
|
|
1414
|
+
// The wrapper that ran the container stops it: nothing here can.
|
|
1415
|
+
containerStarted: !socketMode && (companionContainerStarted || companionUp),
|
|
1416
|
+
stop: () => companion.gracefulStop(),
|
|
1417
|
+
write: (line) => out.write(line),
|
|
1418
|
+
});
|
|
1419
|
+
// Exit explicitly. Anything still holding the event loop — a docker exec
|
|
1420
|
+
// that never returned, an ssh master, a socket — would otherwise keep the
|
|
1421
|
+
// process alive with the terminal already restored, which is exactly the
|
|
1422
|
+
// "TUI is gone but the shell never comes back" state.
|
|
1423
|
+
exit(0);
|
|
1424
|
+
}
|
|
1425
|
+
//# sourceMappingURL=index.js.map
|