@timurproko/a1 0.1.8-dev.21b95d6 → 0.1.8-dev.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -67
- package/bin/cli.js +28 -10
- package/bin/guardian.js +4 -1
- package/bin/module-identity.js +69 -35
- package/bin/pi-tui.d.ts +6 -0
- package/bin/pi-tui.js +28 -0
- package/bin/release-cleanup.js +8 -0
- package/bin/supervisor.js +1 -1
- package/bin/sync-pi-tui-proxy.js +97 -0
- package/bin/ui.js +25 -6
- package/bin/warmup.js +18 -0
- package/dist/cli/capabilities.d.ts +6 -0
- package/dist/{src/cli → cli}/capabilities.js +1 -1
- package/dist/{src/cli → cli}/dispatch.d.ts +6 -0
- package/dist/cli/dispatch.js +202 -0
- package/dist/{src/cli → cli}/packages.d.ts +10 -2
- package/dist/{src/cli → cli}/packages.js +24 -20
- package/dist/{src/cli → cli}/version-stats.d.ts +6 -0
- package/dist/cli/version-stats.js +112 -0
- package/dist/composition/agent-engine-bridge.d.ts +3 -0
- package/dist/composition/agent-engine-bridge.js +108 -0
- package/dist/composition/index.d.ts +3 -0
- package/dist/composition/index.js +3 -0
- package/dist/composition/owned-ui.d.ts +28 -0
- package/dist/composition/owned-ui.js +64 -0
- package/dist/composition/process.d.ts +16 -0
- package/dist/composition/process.js +45 -0
- package/dist/composition/settings-route-host.d.ts +8 -0
- package/dist/composition/settings-route-host.js +75 -0
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.d.ts +2 -2
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.js +22 -4
- package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.d.ts +17 -0
- package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.d.ts +36 -0
- package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.js +25 -0
- package/dist/{src/features → features}/launch/development-launch.js +11 -3
- package/dist/{src/features → features}/launch/index.d.ts +0 -1
- package/dist/{src/features → features}/launch/index.js +0 -1
- package/dist/{src/features → features}/launch/intent.d.ts +3 -2
- package/dist/features/launch/intent.js +3 -0
- package/dist/{src/features → features}/launch/prepare-launch.d.ts +0 -2
- package/dist/{src/features → features}/launch/prepare-launch.js +2 -9
- package/dist/{src/features → features}/launch/profile-paths.d.ts +1 -2
- package/dist/{src/features → features}/launch/profile-paths.js +2 -8
- package/dist/{src/features → features}/launch/runtime-selection.d.ts +2 -5
- package/dist/{src/features → features}/owned-ui/customization.d.ts +3 -1
- package/dist/{src/features → features}/owned-ui/customization.js +3 -1
- package/dist/{src/features → features}/owned-ui/diagnostics.d.ts +2 -1
- package/dist/{src/features → features}/owned-ui/diagnostics.js +1 -0
- package/dist/{src/features → features}/owned-ui/index.d.ts +1 -0
- package/dist/{src/features → features}/owned-ui/index.js +1 -0
- package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
- package/dist/features/owned-ui/project-trust-prompt.js +149 -0
- package/dist/{src/features → features}/owned-ui/run.d.ts +2 -2
- package/dist/{src/features → features}/owned-ui/run.js +2 -0
- package/dist/{src/features → features}/owned-ui/settings-app.d.ts +3 -3
- package/dist/{src/features → features}/owned-ui/settings-app.js +103 -54
- package/dist/{src/features → features}/workspace/capabilities.d.ts +1 -1
- package/dist/{src/features → features}/workspace/reducer.d.ts +2 -1
- package/dist/{src/features → features}/workspace/reducer.js +2 -1
- package/dist/{src/features → features}/workspace/router.d.ts +2 -1
- package/dist/{src/features → features}/workspace/router.js +1 -0
- package/dist/{src/features → features}/workspace/store.d.ts +2 -1
- package/dist/{src/features → features}/workspace/store.js +1 -0
- package/dist/{src/foundation → foundation}/launch-guardian/main.js +10 -2
- package/dist/{src/foundation → foundation}/lifecycle/model.d.ts +1 -1
- package/dist/{src/foundation → foundation}/lifecycle/model.js +1 -1
- package/dist/foundation/lifecycle/paths.d.ts +28 -0
- package/dist/{src/foundation → foundation}/lifecycle/paths.js +21 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/codec.d.ts +2 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/codec.js +2 -0
- package/dist/{src/foundation → foundation}/native-host-protocol/messages.d.ts +1 -1
- package/dist/{src/foundation → foundation}/native-host-protocol/messages.js +1 -1
- package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.js +1 -0
- package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.js +1 -0
- package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.js +1 -0
- package/dist/{src/foundation → foundation}/protocol/client.d.ts +1 -0
- package/dist/{src/foundation → foundation}/protocol/client.js +1 -0
- package/dist/{src/foundation → foundation}/protocol/messages.d.ts +1 -0
- package/dist/{src/foundation → foundation}/protocol/messages.js +1 -0
- package/dist/{src/foundation → foundation}/release/bootstrap.d.ts +14 -4
- package/dist/{src/foundation → foundation}/release/bootstrap.js +66 -13
- package/dist/foundation/release/cohort-state.d.ts +131 -0
- package/dist/foundation/release/cohort-state.js +384 -0
- package/dist/foundation/release/dependency-layer.d.ts +78 -0
- package/dist/foundation/release/dependency-layer.js +487 -0
- package/dist/foundation/release/endpoints.d.ts +20 -0
- package/dist/foundation/release/endpoints.js +50 -0
- package/dist/{src/foundation → foundation}/release/index.d.ts +3 -0
- package/dist/{src/foundation → foundation}/release/index.js +3 -0
- package/dist/foundation/release/release-gc.d.ts +60 -0
- package/dist/foundation/release/release-gc.js +538 -0
- package/dist/{src/foundation → foundation}/release/release-store.d.ts +3 -1
- package/dist/{src/foundation → foundation}/release/release-store.js +60 -10
- package/dist/{src/foundation → foundation}/release/release.d.ts +4 -1
- package/dist/{src/foundation → foundation}/release/release.js +10 -3
- package/dist/{src/foundation → foundation}/release/update-transaction.d.ts +1 -0
- package/dist/{src/foundation → foundation}/release/update-transaction.js +1 -0
- package/dist/{src/foundation → foundation}/release/update.d.ts +19 -3
- package/dist/{src/foundation → foundation}/release/update.js +114 -54
- package/dist/foundation/release/warmup.d.ts +3 -0
- package/dist/foundation/release/warmup.js +35 -0
- package/dist/foundation/startup/index.d.ts +1 -0
- package/dist/foundation/startup/index.js +1 -0
- package/dist/foundation/startup/startup-runtime.d.ts +36 -0
- package/dist/foundation/startup/startup-runtime.js +140 -0
- package/dist/{src/foundation → foundation}/storage/control-store.d.ts +2 -1
- package/dist/{src/foundation → foundation}/storage/control-store.js +39 -4
- package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.d.ts +1 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.js +1 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.js +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/state.d.ts +2 -1
- package/dist/{src/foundation → foundation}/structured-agent-runtime/state.js +2 -1
- package/dist/foundation/supervision/index.d.ts +3 -0
- package/dist/foundation/supervision/index.js +3 -0
- package/dist/{src/foundation → foundation}/supervision/main.js +11 -5
- package/dist/foundation/supervision/paths.d.ts +1 -0
- package/dist/foundation/supervision/paths.js +1 -0
- package/dist/{src/foundation → foundation}/supervision/server.d.ts +19 -1
- package/dist/{src/foundation → foundation}/supervision/server.js +51 -2
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.d.ts +1 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.js +3 -2
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +55 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +778 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.d.ts +1 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.js +1 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.js +54 -6
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.d.ts +3 -3
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.js +32 -17
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +25 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-presenters-transcript.js +92 -131
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.d.ts +52 -8
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.js +4 -2
- package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +13 -0
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +27 -0
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.d.ts +10 -2
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.js +36 -3
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.d.ts +1 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.d.ts +22 -4
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.js +289 -68
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.d.ts +2 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.js +4 -2
- package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
- package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.d.ts +3 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.js +3 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.d.ts +2 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.js +1 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.d.ts +1 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.js +2 -2
- package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
- package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.d.ts +2 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.js +1 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.d.ts +16 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.js +39 -4
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.d.ts +4 -2
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.js +19 -4
- package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
- package/dist/integrations/pi/engine/settings-effects.js +249 -0
- package/dist/integrations/pi/engine/settings-integration.d.ts +43 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/settings-integration.js +102 -108
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.d.ts +3 -2
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.js +1 -0
- package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
- package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
- package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.d.ts +1 -0
- package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.js +1 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +23 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +246 -0
- package/dist/integrations/pi/session-ui/route-host.d.ts +5 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +137 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +1106 -0
- package/dist/integrations/pi/session-ui/session-shell.d.ts +55 -0
- package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/session-shell.js +415 -775
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +44 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +434 -0
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +20 -0
- package/dist/integrations/pi/session-ui/system-clipboard.js +130 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.d.ts +8 -1
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.js +216 -9
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.d.ts +29 -6
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.d.ts +2 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.js +2 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
- package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.d.ts +1 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +3 -3
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/manifest.json +3 -3
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/{src/product-identity.d.ts → product-identity.d.ts} +3 -3
- package/dist/{src/product-identity.js → product-identity.js} +7 -5
- package/dist/{src/product-identity.json → product-identity.json} +11 -6
- package/dist/runtime-payload-inventory.json +122328 -0
- package/dist/{src/foundation/ui-apps → ui/apps}/contracts.d.ts +23 -1
- package/dist/{src/foundation/ui-apps → ui/apps}/host.d.ts +1 -1
- package/dist/{src/foundation/ui-apps → ui/apps}/host.js +14 -10
- package/dist/{src/foundation/ui-apps → ui/apps}/registry.d.ts +1 -0
- package/dist/{src/foundation/ui-apps → ui/apps}/registry.js +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.js +5 -3
- package/dist/{src/foundation/ui-components → ui/components}/frame.d.ts +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/frame.js +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/index.d.ts +4 -0
- package/dist/{src/foundation/ui-components → ui/components}/index.js +4 -0
- package/dist/{src/foundation/ui-components → ui/components}/line-input.d.ts +1 -6
- package/dist/{src/foundation/ui-components → ui/components}/line-input.js +4 -9
- package/dist/{src/foundation/ui-components → ui/components}/list-view.d.ts +0 -6
- package/dist/{src/foundation/ui-components → ui/components}/list-view.js +14 -7
- package/dist/{src/foundation/ui-components → ui/components}/mouse.d.ts +10 -0
- package/dist/{src/foundation/ui-components → ui/components}/mouse.js +23 -0
- package/dist/ui/components/progress-status.d.ts +2 -0
- package/dist/ui/components/progress-status.js +4 -0
- package/dist/ui/components/prompt-row.d.ts +14 -0
- package/dist/ui/components/prompt-row.js +20 -0
- package/dist/{src/foundation/ui-components → ui/components}/scrollbar.d.ts +26 -0
- package/dist/{src/foundation/ui-components → ui/components}/scrollbar.js +35 -0
- package/dist/{src/foundation/ui-components → ui/components}/shortcuts.d.ts +1 -0
- package/dist/{src/foundation/ui-components → ui/components}/shortcuts.js +1 -0
- package/dist/ui/components/spans.d.ts +25 -0
- package/dist/ui/components/spans.js +257 -0
- package/dist/ui/components/submitted-prompt.d.ts +23 -0
- package/dist/ui/components/submitted-prompt.js +50 -0
- package/dist/{src/foundation/ui-components → ui/components}/surface.d.ts +0 -5
- package/dist/{src/foundation/ui-components → ui/components}/surface.js +1 -5
- package/dist/ui/components/text-selection.d.ts +62 -0
- package/dist/ui/components/text-selection.js +235 -0
- package/dist/{src/foundation/ui-components → ui/components}/text.d.ts +14 -0
- package/dist/{src/foundation/ui-components → ui/components}/text.js +66 -4
- package/dist/ui/components/transcript-viewport.d.ts +146 -0
- package/dist/ui/components/transcript-viewport.js +629 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.d.ts +8 -1
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.js +33 -5
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.d.ts +1 -1
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.js +21 -2
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.d.ts +6 -19
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.js +32 -23
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.d.ts +9 -17
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.js +65 -55
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.d.ts +1 -0
- package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.js +2 -1
- package/docs/architecture/boundaries.md +9 -9
- package/docs/architecture/code-documentation.md +77 -0
- package/docs/architecture/project-structure.md +67 -30
- package/docs/architecture/resource-and-data-policy.md +1 -1
- package/docs/architecture/toolchain.md +10 -10
- package/docs/architecture/ui-reference-provenance.md +34 -5
- package/docs/ci-release-runbook.md +186 -79
- package/docs/features/launch-profiles.md +10 -19
- package/docs/manual-launch-instance-acceptance.md +12 -35
- package/docs/manual-owned-ui-checkpoint.md +78 -0
- package/docs/manual-pi-boundary-candidate.md +4 -5
- package/docs/manual-terminal-colour-check.md +4 -3
- package/docs/repository-governance-live-acceptance.md +77 -0
- package/package.json +37 -27
- package/dist/src/cli/capabilities.d.ts +0 -15
- package/dist/src/cli/dispatch.js +0 -177
- package/dist/src/cli/version-stats.js +0 -72
- package/dist/src/composition/index.d.ts +0 -48
- package/dist/src/composition/index.js +0 -258
- package/dist/src/features/launch/intent.js +0 -4
- package/dist/src/features/launch/profiles.d.ts +0 -12
- package/dist/src/features/launch/profiles.js +0 -26
- package/dist/src/foundation/lifecycle/paths.d.ts +0 -10
- package/dist/src/foundation/pi-component-adapter/shell-presenters-transcript.d.ts +0 -57
- package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +0 -55
- package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +0 -29
- package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +0 -112
- package/dist/src/foundation/release/cohort-state.d.ts +0 -70
- package/dist/src/foundation/release/cohort-state.js +0 -202
- package/dist/src/foundation/release/release-gc.d.ts +0 -5
- package/dist/src/foundation/release/release-gc.js +0 -16
- package/dist/src/foundation/supervision/index.d.ts +0 -3
- package/dist/src/foundation/supervision/index.js +0 -3
- package/dist/src/foundation/supervision/paths.d.ts +0 -1
- package/dist/src/foundation/supervision/paths.js +0 -1
- package/dist/src/foundation/ui-components/spans.d.ts +0 -7
- package/dist/src/foundation/ui-components/spans.js +0 -59
- package/docs/manual-transparent-checkpoint.md +0 -53
- /package/dist/{src/cli → cli}/index.d.ts +0 -0
- /package/dist/{src/cli → cli}/index.js +0 -0
- /package/dist/{src/cli → cli}/version.d.ts +0 -0
- /package/dist/{src/cli → cli}/version.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.js +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.d.ts +0 -0
- /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.js +0 -0
- /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.d.ts +0 -0
- /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.d.ts +0 -0
- /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.js +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.d.ts +0 -0
- /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.js +0 -0
- /package/dist/{src/features → features}/launch/development-launch.d.ts +0 -0
- /package/dist/{src/features → features}/launch/initialize-profile.d.ts +0 -0
- /package/dist/{src/features → features}/launch/initialize-profile.js +0 -0
- /package/dist/{src/features → features}/launch/runtime-selection.js +0 -0
- /package/dist/{src/features → features}/workspace/capabilities.js +0 -0
- /package/dist/{src/features → features}/workspace/index.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/index.js +0 -0
- /package/dist/{src/features → features}/workspace/presentation.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/presentation.js +0 -0
- /package/dist/{src/features → features}/workspace/reconciliation.d.ts +0 -0
- /package/dist/{src/features → features}/workspace/reconciliation.js +0 -0
- /package/dist/{src/foundation → foundation}/launch-guardian/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/launch-guardian/index.js +0 -0
- /package/dist/{src/foundation → foundation}/launch-guardian/main.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/commands.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/commands.js +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/index.js +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/index.js +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.js +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/artifact.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/artifact.js +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/contracts.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/contracts.js +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/process-containment/index.js +0 -0
- /package/dist/{src/foundation → foundation}/protocol/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/protocol/index.js +0 -0
- /package/dist/{src/foundation → foundation}/release/cohort-selection.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/cohort-selection.js +0 -0
- /package/dist/{src/foundation → foundation}/release/concurrency.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/concurrency.js +0 -0
- /package/dist/{src/foundation → foundation}/release/process-cleanup.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/process-cleanup.js +0 -0
- /package/dist/{src/foundation → foundation}/release/stable-release.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/release/stable-release.js +0 -0
- /package/dist/{src/foundation → foundation}/storage/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/storage/index.js +0 -0
- /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.js +0 -0
- /package/dist/{src/foundation → foundation}/supervision/main.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.js +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.d.ts +0 -0
- /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.js +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/pi-settings-metadata.json +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.d.ts +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.js +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.d.ts +0 -0
- /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.js +0 -0
- /package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/route-host.js +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.d.ts +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.js +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.js +0 -0
- /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.js +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/contracts.js +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/index.d.ts +0 -0
- /package/dist/{src/foundation/ui-apps → ui/apps}/index.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/label.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/label.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/list-block.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/list-block.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/pane.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/pane.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/revision.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/revision.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/status-line.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/status-line.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/stepper.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/stepper.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/theme.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/theme.js +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/value-menu.d.ts +0 -0
- /package/dist/{src/foundation/ui-components → ui/components}/value-menu.js +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.js +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.d.ts +0 -0
- /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.js +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { PRODUCT_PACKAGE_NAME, createReleaseIdentity,
|
|
4
|
+
import { PRODUCT_PACKAGE_NAME, createReleaseIdentity, discoverReleasePayload, releaseFileIdentity, resolveWithin, } from "./release.js";
|
|
5
5
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
|
|
6
6
|
import { mapWithConcurrency } from "./concurrency.js";
|
|
7
|
+
import { dependencyReference, materializeDependencyLayer, readCertifiedDependencyLayer, selectPublishedDependencyRuntimePayload, verifyDependencyLayer, } from "./dependency-layer.js";
|
|
7
8
|
const RELEASE_FILE_IO_CONCURRENCY = 32;
|
|
8
9
|
const certificationReady = new WeakSet();
|
|
9
10
|
export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
|
|
@@ -13,15 +14,32 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
13
14
|
});
|
|
14
15
|
const storeRoot = resolve(dataDir, "releases");
|
|
15
16
|
await mkdir(storeRoot, { recursive: true, mode: 0o700 });
|
|
16
|
-
|
|
17
|
+
const dependencyPaths = payload.paths.filter(path => path.startsWith("node_modules/"));
|
|
18
|
+
const productPaths = payload.paths.filter(path => !path.startsWith("node_modules/"));
|
|
19
|
+
const selectedDependencies = await selectPublishedDependencyRuntimePayload(payload.packageRoot, dependencyPaths);
|
|
20
|
+
options.onRuntimeInventory?.(selectedDependencies.inventory);
|
|
21
|
+
const layerOperations = [];
|
|
22
|
+
const layer = await materializeDependencyLayer(payload.packageRoot, dataDir, selectedDependencies.paths, {
|
|
23
|
+
inventory: selectedDependencies.inventory,
|
|
24
|
+
cachedFiles: payload.cachedFiles,
|
|
25
|
+
onOperation: event => layerOperations.push(event),
|
|
26
|
+
...(options.writeCandidateFile === undefined ? {} : { writeCandidateFile: options.writeCandidateFile }),
|
|
27
|
+
});
|
|
28
|
+
const layerReferences = layer === null ? [] : [dependencyReference(layer)];
|
|
29
|
+
options.onProgress?.({
|
|
30
|
+
phase: "copying",
|
|
31
|
+
fileCount: productPaths.length + (layer?.reused === false ? layer.files.length : 0),
|
|
32
|
+
});
|
|
33
|
+
for (const event of layerOperations)
|
|
34
|
+
options.onOperation?.(event);
|
|
17
35
|
const candidate = resolveWithin(storeRoot, `.candidate-${randomUUID()}`);
|
|
18
36
|
await mkdir(candidate, { recursive: false, mode: 0o700 });
|
|
19
37
|
try {
|
|
20
|
-
const directories = [...new Set(
|
|
38
|
+
const directories = [...new Set(productPaths.map(path => dirname(resolveWithin(candidate, path))))];
|
|
21
39
|
await mapWithConcurrency(directories, RELEASE_FILE_IO_CONCURRENCY, async (directory) => {
|
|
22
40
|
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
23
41
|
});
|
|
24
|
-
const files = await mapWithConcurrency(
|
|
42
|
+
const files = await mapWithConcurrency(productPaths, RELEASE_FILE_IO_CONCURRENCY, async (path) => {
|
|
25
43
|
const source = resolveWithin(payload.packageRoot, path);
|
|
26
44
|
const destination = resolveWithin(candidate, path);
|
|
27
45
|
const metadata = await lstat(source);
|
|
@@ -41,7 +59,12 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
41
59
|
options.onOperation?.({ operation: "candidate-write", path, bytes: bytes.length });
|
|
42
60
|
return releaseFileIdentity(path, bytes, (metadata.mode & 0o111) !== 0);
|
|
43
61
|
});
|
|
44
|
-
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files);
|
|
62
|
+
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files, layerReferences);
|
|
63
|
+
if (layer !== null) {
|
|
64
|
+
const binding = resolveWithin(candidate, "node_modules");
|
|
65
|
+
const target = resolveWithin(layer.layerRoot, "node_modules");
|
|
66
|
+
await symlink(target, binding, process.platform === "win32" ? "junction" : "dir");
|
|
67
|
+
}
|
|
45
68
|
const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
|
|
46
69
|
if (await lstat(releaseRoot).catch(() => null)) {
|
|
47
70
|
await rm(candidate, { recursive: true, force: true });
|
|
@@ -84,7 +107,8 @@ export async function readMaterializedRelease(releaseRoot) {
|
|
|
84
107
|
*/
|
|
85
108
|
export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
86
109
|
const canonical = await realpath(record.releaseRoot);
|
|
87
|
-
|
|
110
|
+
const canonicalStoreRoot = await realpath(selectedStoreRoot);
|
|
111
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
88
112
|
const manifest = JSON.parse(await readFile(resolveWithin(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
89
113
|
validateManifest(manifest);
|
|
90
114
|
if (manifest.releaseId !== record.releaseId || manifest.contentDigest !== record.contentDigest
|
|
@@ -93,12 +117,13 @@ export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
|
93
117
|
}
|
|
94
118
|
if (canonical.split(sep).at(-1) !== manifest.releaseId)
|
|
95
119
|
throw new Error(`release directory does not match identity ${manifest.releaseId}`);
|
|
120
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], false);
|
|
96
121
|
return { ...manifest, releaseRoot: canonical };
|
|
97
122
|
}
|
|
98
123
|
export async function verifyMaterializedRelease(releaseRoot, expected, selectedStoreRoot, options = {}) {
|
|
99
124
|
const canonical = await realpath(releaseRoot);
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
const canonicalStoreRoot = selectedStoreRoot ? await realpath(selectedStoreRoot) : await realpath(dirname(canonical));
|
|
126
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
102
127
|
const manifestPath = resolveWithin(canonical, RELEASE_MANIFEST_FILENAME);
|
|
103
128
|
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
104
129
|
validateManifest(manifest);
|
|
@@ -111,9 +136,10 @@ export async function verifyMaterializedRelease(releaseRoot, expected, selectedS
|
|
|
111
136
|
await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
|
|
112
137
|
await verifyFile(canonical, file, options);
|
|
113
138
|
});
|
|
114
|
-
const recomputed =
|
|
139
|
+
const recomputed = createReleaseIdentity(manifest.packageRoot, manifest.packageVersion, manifest.files, manifest.dependencyLayers ?? []).contentDigest;
|
|
115
140
|
if (recomputed !== manifest.contentDigest)
|
|
116
141
|
throw new Error(`release content digest mismatch for ${manifest.releaseId}`);
|
|
142
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], true, options);
|
|
117
143
|
return { ...manifest, releaseRoot: canonical };
|
|
118
144
|
}
|
|
119
145
|
export async function assertImmutableExecutionRoot(release, dataDir) {
|
|
@@ -135,6 +161,25 @@ export async function resolveReleaseEntryPoint(release, entryPoint) {
|
|
|
135
161
|
assertContained(release.releaseRoot, canonical, "entry point resolves outside the selected release root");
|
|
136
162
|
return canonical;
|
|
137
163
|
}
|
|
164
|
+
async function verifyReleaseDependencies(releaseRoot, storeRoot, references, fullVerification, options = {}) {
|
|
165
|
+
if (references.length === 0)
|
|
166
|
+
return;
|
|
167
|
+
if (references.length !== 1)
|
|
168
|
+
throw new Error("release currently supports exactly one dependency layer");
|
|
169
|
+
const dataDir = dirname(storeRoot);
|
|
170
|
+
const reference = references[0];
|
|
171
|
+
const layer = fullVerification
|
|
172
|
+
? await verifyDependencyLayer(dataDir, reference, event => options.onOperation?.({ operation: event.operation, path: event.path, bytes: event.bytes }))
|
|
173
|
+
: await readCertifiedDependencyLayer(dataDir, reference.layerId, reference);
|
|
174
|
+
const binding = resolveWithin(releaseRoot, reference.binding);
|
|
175
|
+
const metadata = await lstat(binding);
|
|
176
|
+
if (!metadata.isSymbolicLink())
|
|
177
|
+
throw new Error(`release dependency binding is not managed: ${binding}`);
|
|
178
|
+
const target = await realpath(binding);
|
|
179
|
+
const expected = await realpath(resolveWithin(layer.layerRoot, "node_modules"));
|
|
180
|
+
if (target !== expected)
|
|
181
|
+
throw new Error(`release dependency binding targets unexpected content: ${target}`);
|
|
182
|
+
}
|
|
138
183
|
async function verifyFile(root, file, options) {
|
|
139
184
|
const path = resolveWithin(root, file.path);
|
|
140
185
|
const metadata = await lstat(path).catch(() => null);
|
|
@@ -159,6 +204,11 @@ function validateManifest(value) {
|
|
|
159
204
|
throw new Error(PRODUCT_TEXT.diagnostic("release identity is invalid"));
|
|
160
205
|
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
161
206
|
throw new Error("release manifest contains no files");
|
|
207
|
+
if (value.dependencyLayers !== undefined && (!Array.isArray(value.dependencyLayers) || value.dependencyLayers.length === 0
|
|
208
|
+
|| value.dependencyLayers.some(layer => !/^dependencies-[a-f0-9]{32}$/.test(layer.layerId)
|
|
209
|
+
|| !/^[a-f0-9]{64}$/.test(layer.contentDigest) || layer.binding !== "node_modules"))) {
|
|
210
|
+
throw new Error("release dependency-layer references are invalid");
|
|
211
|
+
}
|
|
162
212
|
for (const file of value.files) {
|
|
163
213
|
if (typeof file.path !== "string" || file.path.length === 0 || file.path.includes("\\") || file.path.startsWith("/") || file.path.split("/").includes("..")) {
|
|
164
214
|
throw new Error(`invalid release manifest path: ${String(file.path)}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DependencyLayerReference } from "./dependency-layer.js";
|
|
1
2
|
export declare const PRODUCT_PACKAGE_NAME: string;
|
|
2
3
|
export interface ReleaseFileIdentity {
|
|
3
4
|
readonly path: string;
|
|
@@ -12,6 +13,8 @@ export interface ReleaseIdentity {
|
|
|
12
13
|
readonly releaseId: string;
|
|
13
14
|
readonly packageRoot: string;
|
|
14
15
|
readonly files: readonly ReleaseFileIdentity[];
|
|
16
|
+
/** Absent for legacy full-copy releases. */
|
|
17
|
+
readonly dependencyLayers?: readonly DependencyLayerReference[];
|
|
15
18
|
}
|
|
16
19
|
export interface DiscoveredReleasePayload {
|
|
17
20
|
readonly packageRoot: string;
|
|
@@ -31,7 +34,7 @@ export declare function discoverReleasePayload(packageRoot: string, options?: Di
|
|
|
31
34
|
* content.
|
|
32
35
|
*/
|
|
33
36
|
export declare function deriveReleaseIdentity(packageRoot: string): Promise<ReleaseIdentity>;
|
|
34
|
-
export declare function createReleaseIdentity(packageRoot: string, packageVersion: string, files: readonly ReleaseFileIdentity[]): ReleaseIdentity;
|
|
37
|
+
export declare function createReleaseIdentity(packageRoot: string, packageVersion: string, files: readonly ReleaseFileIdentity[], dependencyLayers?: readonly DependencyLayerReference[]): ReleaseIdentity;
|
|
35
38
|
export declare function releaseFileIdentity(path: string, bytes: Uint8Array, executable: boolean): ReleaseFileIdentity;
|
|
36
39
|
export declare function digestManifestFiles(files: readonly ReleaseFileIdentity[]): string;
|
|
37
40
|
export declare function resolveWithin(root: string, candidate: string): string;
|
|
@@ -51,8 +51,14 @@ export async function deriveReleaseIdentity(packageRoot) {
|
|
|
51
51
|
});
|
|
52
52
|
return createReleaseIdentity(payload.packageRoot, payload.packageVersion, files);
|
|
53
53
|
}
|
|
54
|
-
export function createReleaseIdentity(packageRoot, packageVersion, files) {
|
|
55
|
-
const
|
|
54
|
+
export function createReleaseIdentity(packageRoot, packageVersion, files, dependencyLayers = []) {
|
|
55
|
+
const productDigest = digestManifestFiles(files);
|
|
56
|
+
const contentDigest = dependencyLayers.length === 0
|
|
57
|
+
? productDigest
|
|
58
|
+
: createHash("sha256")
|
|
59
|
+
.update(`product\0${productDigest}\n`)
|
|
60
|
+
.update(dependencyLayers.map(layer => `${layer.layerId}\0${layer.contentDigest}\0${layer.binding}\n`).join(""))
|
|
61
|
+
.digest("hex");
|
|
56
62
|
return {
|
|
57
63
|
packageName: PRODUCT_PACKAGE_NAME,
|
|
58
64
|
packageVersion,
|
|
@@ -60,6 +66,7 @@ export function createReleaseIdentity(packageRoot, packageVersion, files) {
|
|
|
60
66
|
releaseId: `${packageVersion}-${contentDigest.slice(0, 20)}`,
|
|
61
67
|
packageRoot,
|
|
62
68
|
files: [...files].sort(compareReleaseFiles),
|
|
69
|
+
...(dependencyLayers.length === 0 ? {} : { dependencyLayers: [...dependencyLayers] }),
|
|
63
70
|
};
|
|
64
71
|
}
|
|
65
72
|
export function releaseFileIdentity(path, bytes, executable) {
|
|
@@ -143,7 +150,7 @@ async function findInstalledDependency(root, requesterRoot, name) {
|
|
|
143
150
|
const metadata = await lstat(candidate).catch(() => null);
|
|
144
151
|
if (metadata?.isDirectory() && !metadata.isSymbolicLink())
|
|
145
152
|
return candidate;
|
|
146
|
-
//
|
|
153
|
+
// Invariant: a linked dependency directory is how this installation unifies a module
|
|
147
154
|
// that would otherwise be present twice, and the link is made by A1 itself
|
|
148
155
|
// at install and at every launch. Follow it to the directory it names and
|
|
149
156
|
// collect from there, so the payload still reads only real files — but
|
|
@@ -14,6 +14,7 @@ export interface UpdateTransaction {
|
|
|
14
14
|
readonly startedAt: string;
|
|
15
15
|
readonly updatedAt: string;
|
|
16
16
|
}
|
|
17
|
+
/** Persists the durable update journal and permits only validated forward transaction phases. */
|
|
17
18
|
export declare class UpdateTransactionStore {
|
|
18
19
|
#private;
|
|
19
20
|
readonly path: string;
|
|
@@ -3,6 +3,7 @@ import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
|
|
5
5
|
export const UPDATE_JOURNAL_SCHEMA = PRODUCT_IDENTITY.protocol.updateJournalSchema;
|
|
6
|
+
/** Persists the durable update journal and permits only validated forward transaction phases. */
|
|
6
7
|
export class UpdateTransactionStore {
|
|
7
8
|
path;
|
|
8
9
|
constructor(dataDir) { this.path = resolve(dataDir, "update-transaction.json"); }
|
|
@@ -18,7 +18,7 @@ export interface UpdateOutput {
|
|
|
18
18
|
stdout(message: string): void;
|
|
19
19
|
stderr(message: string): void;
|
|
20
20
|
}
|
|
21
|
-
export type UpdateMeasuredPhase = "package-version" | "target-resolution" | "global-root" | "ownership-release" | "npm-install" | "materialized" | "certified" | "active-reference-committed" | "supervisor-verified" | "transaction-complete";
|
|
21
|
+
export type UpdateMeasuredPhase = "package-version" | "target-resolution" | "global-root" | "ownership-release" | "npm-install" | "materialized" | "certified" | "active-reference-committed" | "warmup" | "supervisor-verified" | "transaction-complete";
|
|
22
22
|
export interface UpdatePhaseTimingEvent {
|
|
23
23
|
readonly phase: UpdateMeasuredPhase;
|
|
24
24
|
readonly durationMs: number;
|
|
@@ -28,12 +28,18 @@ export interface UpdatePerformanceEvidence {
|
|
|
28
28
|
readonly sourceReads: number;
|
|
29
29
|
readonly candidateWrites: number;
|
|
30
30
|
readonly verificationReads: number;
|
|
31
|
+
readonly layerWrites?: number;
|
|
32
|
+
readonly layerReusedFiles?: number;
|
|
33
|
+
readonly layerReusedBytes?: number;
|
|
34
|
+
readonly payloadExcludedFiles?: number;
|
|
35
|
+
readonly payloadExcludedBytes?: number;
|
|
36
|
+
readonly warmupDurationMs?: number;
|
|
31
37
|
readonly postNpmDurationMs: number;
|
|
32
38
|
}
|
|
33
39
|
export interface SelfUpdateOptions {
|
|
34
40
|
packageRoot: string;
|
|
35
41
|
channel?: UpdateChannel;
|
|
36
|
-
/** A specific preview to install, named by its
|
|
42
|
+
/** A specific preview to install, named by its development number or full version. */
|
|
37
43
|
target?: string;
|
|
38
44
|
environment?: NodeJS.ProcessEnv;
|
|
39
45
|
fileSystem?: UpdateFileSystem;
|
|
@@ -41,6 +47,8 @@ export interface SelfUpdateOptions {
|
|
|
41
47
|
runner?: UpdateProcessRunner;
|
|
42
48
|
lifecycle?: UpdateLifecycleCoordinator;
|
|
43
49
|
transactionStore?: UpdateTransactionJournal;
|
|
50
|
+
/** Test or embedding seam for post-activation release maintenance. */
|
|
51
|
+
maintenance?: () => Promise<void>;
|
|
44
52
|
progress?: boolean;
|
|
45
53
|
onPhaseTiming?: (event: UpdatePhaseTimingEvent) => void;
|
|
46
54
|
now?: () => number;
|
|
@@ -61,7 +69,7 @@ export interface UpdateLifecycleCoordinator {
|
|
|
61
69
|
priorActiveVersion: string | null;
|
|
62
70
|
}>;
|
|
63
71
|
verifyPackageUnlocked(packageRoot: string): Promise<void>;
|
|
64
|
-
activateInstalled(packageRoot: string, targetVersion: string, phase: (phase: UpdateActivationPhase) => Promise<void>, onMaterializing?: (progress: UpdateMaterializationProgress) => void): Promise<void>;
|
|
72
|
+
activateInstalled(packageRoot: string, targetVersion: string, phase: (phase: UpdateActivationPhase) => Promise<void>, onMaterializing?: (progress: UpdateMaterializationProgress) => void, onWarmup?: (state: "started" | "completed") => void): Promise<void>;
|
|
65
73
|
}
|
|
66
74
|
export interface UpdateTransactionJournal {
|
|
67
75
|
readonly path: string;
|
|
@@ -77,6 +85,14 @@ export interface UpdateTransactionJournal {
|
|
|
77
85
|
clearCompleted(): Promise<void>;
|
|
78
86
|
}
|
|
79
87
|
export declare function createNpmProcessRunner(platform?: NodeJS.Platform): UpdateProcessRunner;
|
|
88
|
+
export type UpdateOwnershipAction = "clean-dead-record" | "leave-running" | "end-session";
|
|
89
|
+
/**
|
|
90
|
+
* What an update does about the owner it found. A cohort running from retained immutable
|
|
91
|
+
* content keeps its work: the installation replaces files it does not read, and it listens on
|
|
92
|
+
* its own endpoint. A cohort running from the mutable installation is the one exception,
|
|
93
|
+
* because that installation is exactly what is about to be replaced under it.
|
|
94
|
+
*/
|
|
95
|
+
export declare function planUpdateOwnership(ownership: "live-verified" | "dead", runsFromRetainedRelease: boolean): UpdateOwnershipAction;
|
|
80
96
|
export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput): UpdateLifecycleCoordinator;
|
|
81
97
|
export declare function runSelfUpdate(options: SelfUpdateOptions): Promise<number>;
|
|
82
98
|
export declare function assertUpdatePerformanceBudget(evidence: UpdatePerformanceEvidence, maximumPostNpmDurationMs?: number): void;
|
|
@@ -6,21 +6,16 @@ import crossSpawn from "cross-spawn";
|
|
|
6
6
|
import { valid as validSemver } from "semver";
|
|
7
7
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
|
|
8
8
|
import { certifyMaterializedRelease, probeOwnership, readEndpointMetadata, removeEndpointArtifacts, startSupervisor, waitForProcessExit, waitForVerifiedEndpoint, } from "./bootstrap.js";
|
|
9
|
-
import { resolveProductPaths } from "../lifecycle/index.js";
|
|
9
|
+
import { resolveCohortEndpoint, resolveProductPaths } from "../lifecycle/index.js";
|
|
10
10
|
import { encodeFrame, LineFrameDecoder } from "../protocol/index.js";
|
|
11
11
|
import { CohortStateStore } from "./cohort-state.js";
|
|
12
12
|
import { cleanupVerifiedOwner, processIsAlive } from "./process-cleanup.js";
|
|
13
13
|
import { materializeRelease, readMaterializedRelease } from "./release-store.js";
|
|
14
|
+
import { scheduleReleaseCleanup } from "./release-gc.js";
|
|
15
|
+
import { warmMaterializedRelease } from "./warmup.js";
|
|
14
16
|
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
15
17
|
export const PRODUCT_PACKAGE = PRODUCT_TEXT.packageName;
|
|
16
18
|
const UPDATE_DIST_TAGS = { stable: "latest", next: "next" };
|
|
17
|
-
/**
|
|
18
|
-
* What each channel is called when A1 says it out loud. The internal name stays
|
|
19
|
-
* `stable` because that is what the channel is, but what a reader is moving to is
|
|
20
|
-
* a release, and that is the word the repository, its tags, and its GitHub
|
|
21
|
-
* releases all use.
|
|
22
|
-
*/
|
|
23
|
-
const UPDATE_CHANNEL_LABELS = { stable: "release", next: "next" };
|
|
24
19
|
const defaultFileSystem = {
|
|
25
20
|
async readFile(path) { return await readFile(path, "utf8"); },
|
|
26
21
|
realpath,
|
|
@@ -47,6 +42,33 @@ export function createNpmProcessRunner(platform = process.platform) {
|
|
|
47
42
|
} });
|
|
48
43
|
});
|
|
49
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* What an update does about the owner it found. A cohort running from retained immutable
|
|
47
|
+
* content keeps its work: the installation replaces files it does not read, and it listens on
|
|
48
|
+
* its own endpoint. A cohort running from the mutable installation is the one exception,
|
|
49
|
+
* because that installation is exactly what is about to be replaced under it.
|
|
50
|
+
*/
|
|
51
|
+
export function planUpdateOwnership(ownership, runsFromRetainedRelease) {
|
|
52
|
+
if (ownership === "dead")
|
|
53
|
+
return "clean-dead-record";
|
|
54
|
+
return runsFromRetainedRelease ? "leave-running" : "end-session";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The endpoint of the active cohort, together with where it is recorded, so a caller that
|
|
58
|
+
* finds it can also clean it up. A release that predates cohort-scoped endpoints published
|
|
59
|
+
* one endpoint for the runtime directory, and that one is recognized too.
|
|
60
|
+
*/
|
|
61
|
+
async function readActiveEndpoint(paths, activeReleaseId, environment) {
|
|
62
|
+
if (activeReleaseId !== null) {
|
|
63
|
+
const cohort = resolveCohortEndpoint(paths, activeReleaseId, environment);
|
|
64
|
+
const metadata = await readEndpointMetadata(cohort.endpointMetadataPath);
|
|
65
|
+
if (metadata)
|
|
66
|
+
return { metadata, paths: cohort };
|
|
67
|
+
}
|
|
68
|
+
const legacyPaths = { endpoint: paths.endpoint, endpointMetadataPath: paths.endpointMetadataPath };
|
|
69
|
+
const legacy = await readEndpointMetadata(legacyPaths.endpointMetadataPath);
|
|
70
|
+
return legacy ? { metadata: legacy, paths: legacyPaths } : null;
|
|
71
|
+
}
|
|
50
72
|
export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, output = defaultOutput) {
|
|
51
73
|
const paths = resolveProductPaths(environment);
|
|
52
74
|
const stateStore = new CohortStateStore(paths.dataDir);
|
|
@@ -56,45 +78,51 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
56
78
|
const activeId = state.references.active;
|
|
57
79
|
if (!activeId || state.releases[activeId]?.packageVersion !== targetVersion)
|
|
58
80
|
return false;
|
|
59
|
-
const endpoint = await
|
|
60
|
-
return endpoint?.releaseId === activeId && await probeOwnership(endpoint) === "live-verified";
|
|
81
|
+
const endpoint = await readActiveEndpoint(paths, activeId, environment);
|
|
82
|
+
return endpoint?.metadata.releaseId === activeId && await probeOwnership(endpoint.metadata) === "live-verified";
|
|
61
83
|
},
|
|
62
84
|
async shutdownVerifiedOwners(targetVersion) {
|
|
63
85
|
const state = await stateStore.read();
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
86
|
+
const activeId = state.references.active;
|
|
87
|
+
const priorActiveVersion = activeId ? state.releases[activeId]?.packageVersion ?? null : null;
|
|
88
|
+
const owner = await readActiveEndpoint(paths, activeId, environment);
|
|
89
|
+
if (!owner)
|
|
67
90
|
return { priorActiveVersion };
|
|
91
|
+
const endpoint = owner.metadata;
|
|
68
92
|
const ownership = await probeOwnership(endpoint);
|
|
69
93
|
if (ownership !== "live-verified" && ownership !== "dead") {
|
|
70
94
|
throw new Error(PRODUCT_TEXT.diagnostic(`refused update shutdown because supervisor ownership is ${ownership}`));
|
|
71
95
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
const plan = planUpdateOwnership(ownership, await canonicalImmutableRoot(paths.dataDir, endpoint.releaseRoot));
|
|
97
|
+
if (plan === "clean-dead-record") {
|
|
98
|
+
await removeEndpointArtifacts(owner.paths.endpointMetadataPath, owner.paths.endpoint);
|
|
99
|
+
return { priorActiveVersion };
|
|
100
|
+
}
|
|
101
|
+
// Rationale: leaving sessions alone is the expected outcome, and saying so mid-update tears
|
|
102
|
+
// the progress bar; only ending a session (below) is worth interrupting it for.
|
|
103
|
+
if (plan === "leave-running")
|
|
104
|
+
return { priorActiveVersion };
|
|
105
|
+
// Rationale: say whose work is ending before it ends.
|
|
106
|
+
const live = endpoint.ownership.liveInstanceIds.length;
|
|
107
|
+
if (live > 0) {
|
|
108
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`ending ${live === 1 ? "one session" : `${live} sessions`} that run from the installation being replaced; a session started by an installed release would have been left alone.`)}\n`);
|
|
109
|
+
}
|
|
110
|
+
const identity = await requestUpdateShutdown(endpoint, targetVersion, 2_000);
|
|
77
111
|
if (!identity.accepted && processIsAlive(endpoint.pid)) {
|
|
78
|
-
//
|
|
112
|
+
// Security: explicit update consent permits bounded cleanup of an authenticated
|
|
79
113
|
// older owner that predates the update-shutdown message.
|
|
80
|
-
const cleanup = await cleanupVerifiedOwner(endpoint, {
|
|
81
|
-
allowLiveInstances: true,
|
|
82
|
-
reason: legacyMutableInstall ? "legacy-mutable-install" : "explicit-update",
|
|
83
|
-
});
|
|
114
|
+
const cleanup = await cleanupVerifiedOwner(endpoint, { allowLiveInstances: true, reason: "legacy-mutable-install" });
|
|
84
115
|
if (!cleanup.terminated)
|
|
85
116
|
throw new Error(`verified ${PRODUCT_TEXT.displayName} owner ${endpoint.pid} rejected shutdown and could not be terminated: ${identity.reason}`);
|
|
86
117
|
}
|
|
87
118
|
else if (identity.accepted) {
|
|
88
119
|
await waitForProcessExit(endpoint.pid, 3_000).catch(async () => {
|
|
89
|
-
const cleanup = await cleanupVerifiedOwner(endpoint, {
|
|
90
|
-
allowLiveInstances: true,
|
|
91
|
-
reason: legacyMutableInstall ? "legacy-mutable-install" : "explicit-update",
|
|
92
|
-
});
|
|
120
|
+
const cleanup = await cleanupVerifiedOwner(endpoint, { allowLiveInstances: true, reason: "legacy-mutable-install" });
|
|
93
121
|
if (!cleanup.terminated)
|
|
94
122
|
throw new Error(`verified ${PRODUCT_TEXT.displayName} owner ${endpoint.pid} did not terminate`);
|
|
95
123
|
});
|
|
96
124
|
}
|
|
97
|
-
await removeEndpointArtifacts(paths.endpointMetadataPath, paths.endpoint);
|
|
125
|
+
await removeEndpointArtifacts(owner.paths.endpointMetadataPath, owner.paths.endpoint);
|
|
98
126
|
return { priorActiveVersion };
|
|
99
127
|
},
|
|
100
128
|
async verifyPackageUnlocked(packageRoot) {
|
|
@@ -107,12 +135,12 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
107
135
|
await rename(probe, packageRoot);
|
|
108
136
|
}
|
|
109
137
|
catch (error) {
|
|
110
|
-
//
|
|
138
|
+
// Invariant: best-effort rollback runs if the first rename succeeded and the second did not.
|
|
111
139
|
await rename(probe, packageRoot).catch(() => { });
|
|
112
140
|
throw new Error(PRODUCT_TEXT.diagnostic(`package remains locked after verified shutdown: ${errorMessage(error)}`));
|
|
113
141
|
}
|
|
114
142
|
},
|
|
115
|
-
async activateInstalled(packageRoot, targetVersion, phase, onMaterializing) {
|
|
143
|
+
async activateInstalled(packageRoot, targetVersion, phase, onMaterializing, onWarmup) {
|
|
116
144
|
let total = 0;
|
|
117
145
|
let completed = 0;
|
|
118
146
|
const candidate = await materializeRelease(packageRoot, paths.dataDir, {
|
|
@@ -121,7 +149,7 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
121
149
|
onMaterializing?.({ completed, total });
|
|
122
150
|
},
|
|
123
151
|
onOperation: event => {
|
|
124
|
-
if (event.operation !== "candidate-write")
|
|
152
|
+
if (event.operation !== "candidate-write" && event.operation !== "layer-write")
|
|
125
153
|
return;
|
|
126
154
|
completed += 1;
|
|
127
155
|
onMaterializing?.({ completed, total });
|
|
@@ -136,8 +164,11 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
136
164
|
await phase("certified");
|
|
137
165
|
await stateStore.activate(candidate.releaseId);
|
|
138
166
|
await phase("active-reference-committed");
|
|
167
|
+
onWarmup?.("started");
|
|
168
|
+
await warmMaterializedRelease(candidate, environment);
|
|
169
|
+
onWarmup?.("completed");
|
|
139
170
|
await startSupervisor(candidate, environment);
|
|
140
|
-
await waitForVerifiedEndpoint(paths.endpointMetadataPath, candidate, 8_000);
|
|
171
|
+
await waitForVerifiedEndpoint(resolveCohortEndpoint(paths, candidate.releaseId, environment).endpointMetadataPath, candidate, 8_000);
|
|
141
172
|
},
|
|
142
173
|
};
|
|
143
174
|
}
|
|
@@ -188,7 +219,7 @@ function createUpdateProgress(output, enabled) {
|
|
|
188
219
|
draw();
|
|
189
220
|
if (creepTo <= current)
|
|
190
221
|
return;
|
|
191
|
-
//
|
|
222
|
+
// Rationale: creep asymptotically toward (but never reach) the next milestone so
|
|
192
223
|
// long opaque phases such as npm install still show visible motion. The
|
|
193
224
|
// ceiling stays a whole point below the milestone: settling on `creepTo`
|
|
194
225
|
// itself would render as that milestone and make arriving at it invisible,
|
|
@@ -199,7 +230,7 @@ function createUpdateProgress(output, enabled) {
|
|
|
199
230
|
}, PROGRESS_TICK_MS);
|
|
200
231
|
timer.unref?.();
|
|
201
232
|
},
|
|
202
|
-
//
|
|
233
|
+
// Rationale: the bar exists to say the update is still moving. Once it has finished
|
|
203
234
|
// there is a better line to occupy that row — the one naming what is now
|
|
204
235
|
// installed — so the bar gives the row back rather than leaving a full
|
|
205
236
|
// meter above a message that already implies it.
|
|
@@ -223,12 +254,13 @@ function createUpdateProgress(output, enabled) {
|
|
|
223
254
|
}
|
|
224
255
|
/** The newest version the channel points at, which is what an unqualified update takes. */
|
|
225
256
|
async function resolveChannelHead(runner, distTag, output) {
|
|
226
|
-
const
|
|
257
|
+
const publicChannel = distTag === "next" ? "development" : "release";
|
|
258
|
+
const lookup = await runNpm(runner, ["view", `${PRODUCT_PACKAGE}@${distTag}`, "version"], true, output, `query the npm ${publicChannel} channel`);
|
|
227
259
|
if (lookup.result === null)
|
|
228
260
|
return { version: null, exitCode: lookup.exitCode };
|
|
229
261
|
const version = validSemver(lookup.result.stdout.trim());
|
|
230
262
|
if (version === null) {
|
|
231
|
-
output.stderr(`${PRODUCT_TEXT.diagnostic(`received a malformed ${
|
|
263
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`received a malformed ${publicChannel} channel version from npm: ${JSON.stringify(lookup.result.stdout.trim())}.`)}\n`);
|
|
232
264
|
return { version: null, exitCode: 1 };
|
|
233
265
|
}
|
|
234
266
|
return { version, exitCode: 0 };
|
|
@@ -236,14 +268,10 @@ async function resolveChannelHead(runner, distTag, output) {
|
|
|
236
268
|
/**
|
|
237
269
|
* Resolve a preview the caller named.
|
|
238
270
|
*
|
|
239
|
-
* A preview is published as `<version>-dev.<
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* The published list is the authority: naming a commit that was never published,
|
|
245
|
-
* or one published more than once under different versions, is an error rather
|
|
246
|
-
* than a guess.
|
|
271
|
+
* A preview is published as `<version>-dev.<pull-request-number>`, so its decimal
|
|
272
|
+
* development number is enough to identify it. A full version is accepted too.
|
|
273
|
+
* The published list remains authoritative: nothing is constructed from the
|
|
274
|
+
* installed package's base version.
|
|
247
275
|
*/
|
|
248
276
|
async function resolveRequestedPreview(runner, requested, output) {
|
|
249
277
|
const lookup = await runNpm(runner, ["view", PRODUCT_PACKAGE, "versions", "--json"], true, output, "list the published versions");
|
|
@@ -260,9 +288,8 @@ async function resolveRequestedPreview(runner, requested, output) {
|
|
|
260
288
|
const versions = (Array.isArray(published) ? published : [published]).filter((value) => typeof value === "string");
|
|
261
289
|
const exact = versions.find(version => version === requested);
|
|
262
290
|
if (exact !== undefined) {
|
|
263
|
-
//
|
|
264
|
-
// different command with a different meaning.
|
|
265
|
-
// one, so only the fuller spelling of a preview reaches this.
|
|
291
|
+
// Rationale: naming a release here would install it through the preview path, which is a
|
|
292
|
+
// different command with a different meaning.
|
|
266
293
|
if (!exact.includes("-dev.")) {
|
|
267
294
|
output.stderr(`${PRODUCT_TEXT.diagnostic(`${exact} is a release, not a preview; run ${PRODUCT_TEXT.commandName} update to move to the current release.`)}\n`);
|
|
268
295
|
return { version: null, exitCode: 1 };
|
|
@@ -314,9 +341,9 @@ export async function runSelfUpdate(options) {
|
|
|
314
341
|
if (resolved.version === null)
|
|
315
342
|
return resolved.exitCode;
|
|
316
343
|
const targetVersion = resolved.version;
|
|
317
|
-
//
|
|
344
|
+
// Rationale: no full stop after a version: it already ends in a dot-separated identifier,
|
|
318
345
|
// and a trailing one reads as part of the version rather than as punctuation.
|
|
319
|
-
output.stdout(`${PRODUCT_TEXT.commandName} update
|
|
346
|
+
output.stdout(`${PRODUCT_TEXT.commandName} update: ${runningVersion} → ${targetVersion}\n`);
|
|
320
347
|
const progress = createUpdateProgress(output, options.progress ?? (options.output === undefined && process.stdout.isTTY === true));
|
|
321
348
|
const rootLookup = await measure("global-root", async () => await runNpm(runner, ["root", "--global"], true, output, "resolve npm's global package root"));
|
|
322
349
|
if (rootLookup.result === null)
|
|
@@ -342,6 +369,9 @@ export async function runSelfUpdate(options) {
|
|
|
342
369
|
const paths = resolveProductPaths(environment);
|
|
343
370
|
const lifecycle = options.lifecycle ?? createUpdateLifecycleCoordinator(environment, fileSystem, output);
|
|
344
371
|
const transactionStore = options.transactionStore ?? new UpdateTransactionStore(paths.dataDir);
|
|
372
|
+
const maintenance = options.maintenance ?? (options.lifecycle
|
|
373
|
+
? async () => { }
|
|
374
|
+
: async () => await scheduleReleaseCleanup(paths.dataDir, paths));
|
|
345
375
|
let transaction = await transactionStore.read();
|
|
346
376
|
try {
|
|
347
377
|
if (await lifecycle.targetIsActive(targetVersion)) {
|
|
@@ -349,11 +379,12 @@ export async function runSelfUpdate(options) {
|
|
|
349
379
|
await transactionStore.advance("supervisor-verified");
|
|
350
380
|
await transactionStore.finish("completed");
|
|
351
381
|
}
|
|
382
|
+
await maintenance();
|
|
352
383
|
await transactionStore.clearCompleted();
|
|
353
384
|
output.stdout(`${PRODUCT_TEXT.commandName} is up to date — no update needed.\n`);
|
|
354
385
|
return 0;
|
|
355
386
|
}
|
|
356
|
-
//
|
|
387
|
+
// Rationale: the bar first appears here so a no-change run never flashes it.
|
|
357
388
|
progress.set(3, 15);
|
|
358
389
|
const cohortState = await new CohortStateStore(paths.dataDir).read();
|
|
359
390
|
transaction = await transactionStore.begin({
|
|
@@ -381,8 +412,21 @@ export async function runSelfUpdate(options) {
|
|
|
381
412
|
}
|
|
382
413
|
transaction = await transactionStore.advance("package-installed");
|
|
383
414
|
}
|
|
415
|
+
// Compatibility: npm 12 blocks install scripts unless allowScripts covers the package, so
|
|
416
|
+
// the postinstall that points the #pi-tui proxy at the tree npm just built
|
|
417
|
+
// may never have run. Run the shipped script directly: the proxy must be
|
|
418
|
+
// correct before the release store copies this tree. A failure is reported
|
|
419
|
+
// and left to the launch-time self-heal rather than failing the update.
|
|
420
|
+
try {
|
|
421
|
+
const proxySync = await runner(process.execPath, [resolve(packageRoot, "bin", "sync-pi-tui-proxy.js")], { captureStdout: true });
|
|
422
|
+
if (proxySync.code !== 0)
|
|
423
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`could not point the #pi-tui proxy at the installed tree (exited ${formatExitCode(proxySync.code)}).`)}\n`);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
output.stderr(`${PRODUCT_TEXT.diagnostic(`could not point the #pi-tui proxy at the installed tree: ${errorMessage(error)}`)}\n`);
|
|
427
|
+
}
|
|
384
428
|
progress.set(70, 75);
|
|
385
|
-
//
|
|
429
|
+
// Concurrency: ownership can be reacquired after an interrupted installation (for
|
|
386
430
|
// example, if bare A1 is launched before the update is resumed). Recheck
|
|
387
431
|
// immediately before activation so recovery cannot start a second cohort.
|
|
388
432
|
await measure("ownership-release", async () => { await lifecycle.shutdownVerifiedOwners(targetVersion); });
|
|
@@ -397,11 +441,24 @@ export async function runSelfUpdate(options) {
|
|
|
397
441
|
const span = MATERIALIZE_PROGRESS.to - MATERIALIZE_PROGRESS.from;
|
|
398
442
|
const done = total > 0 ? Math.min(1, completed / total) : 0;
|
|
399
443
|
progress.set(MATERIALIZE_PROGRESS.from + span * done);
|
|
444
|
+
}, state => {
|
|
445
|
+
if (state === "started") {
|
|
446
|
+
activationPhaseStartedAt = now();
|
|
447
|
+
progress.set(98, 99);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
options.onPhaseTiming?.({ phase: "warmup", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
|
|
451
|
+
activationPhaseStartedAt = now();
|
|
452
|
+
progress.set(99);
|
|
453
|
+
}
|
|
400
454
|
});
|
|
401
455
|
options.onPhaseTiming?.({ phase: "supervisor-verified", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
|
|
402
456
|
const transactionStartedAt = now();
|
|
403
457
|
await transactionStore.advance("supervisor-verified");
|
|
404
458
|
await transactionStore.finish("completed");
|
|
459
|
+
// Invariant: successful output follows the durable cleanup disposition. Slow recursive
|
|
460
|
+
// removal belongs to the detached worker started by this maintenance coordinator.
|
|
461
|
+
await maintenance();
|
|
405
462
|
await transactionStore.clearCompleted();
|
|
406
463
|
options.onPhaseTiming?.({ phase: "transaction-complete", durationMs: Math.max(0, now() - transactionStartedAt) });
|
|
407
464
|
progress.finish();
|
|
@@ -426,8 +483,9 @@ export function assertUpdatePerformanceBudget(evidence, maximumPostNpmDurationMs
|
|
|
426
483
|
failures.push("fixture contains no payload files");
|
|
427
484
|
if (evidence.sourceReads !== evidence.fileCount)
|
|
428
485
|
failures.push(`source payload read count is ${evidence.sourceReads} for ${evidence.fileCount} files`);
|
|
429
|
-
|
|
430
|
-
|
|
486
|
+
const totalWrites = evidence.candidateWrites + (evidence.layerWrites ?? 0);
|
|
487
|
+
if (totalWrites !== evidence.fileCount)
|
|
488
|
+
failures.push(`runtime payload write count is ${totalWrites} for ${evidence.fileCount} files`);
|
|
431
489
|
if (evidence.verificationReads > 0)
|
|
432
490
|
failures.push(`fresh certification reread ${evidence.verificationReads} candidate files`);
|
|
433
491
|
if (evidence.postNpmDurationMs > maximumPostNpmDurationMs)
|
|
@@ -495,8 +553,10 @@ async function rollbackPriorCohort(dataDir, environment, priorReleaseId) {
|
|
|
495
553
|
}
|
|
496
554
|
const release = await readMaterializedRelease(prior.releaseRoot);
|
|
497
555
|
const paths = resolveProductPaths(environment);
|
|
556
|
+
// Invariant: rollback re-points the active reference and starts the prior cohort on its own endpoint;
|
|
557
|
+
// a cohort that survived the update keeps serving the work it already had.
|
|
498
558
|
await startSupervisor(release, environment);
|
|
499
|
-
await waitForVerifiedEndpoint(paths.endpointMetadataPath, release, 8_000);
|
|
559
|
+
await waitForVerifiedEndpoint(resolveCohortEndpoint(paths, release.releaseId, environment).endpointMetadataPath, release, 8_000);
|
|
500
560
|
return "rolled back";
|
|
501
561
|
}
|
|
502
562
|
function phaseBefore(current, target) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MaterializedRelease } from "./release-store.js";
|
|
2
|
+
/** Import the exact immutable startup graph in a terminal-free bounded child process. */
|
|
3
|
+
export declare function warmMaterializedRelease(release: MaterializedRelease, environment: NodeJS.ProcessEnv, timeoutMs?: number): Promise<void>;
|