@timurproko/a1 0.1.8-dev.235df0e → 0.1.8-dev.259
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 +8 -3
- 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 +2 -2
- package/bin/sync-pi-tui-proxy.js +97 -0
- package/bin/ui.js +47 -20
- package/bin/warmup.js +27 -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 +15 -1
- package/dist/cli/dispatch.js +261 -0
- package/dist/cli/package-messages.d.ts +26 -0
- package/dist/cli/package-messages.js +71 -0
- package/dist/{src/cli → cli}/packages.d.ts +11 -2
- package/dist/{src/cli → cli}/packages.js +24 -27
- 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 +31 -0
- package/dist/composition/owned-ui.js +66 -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/agent-engine-contracts → contracts/agent-engine}/package-ports.d.ts +6 -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/features/launch/intent.d.ts +10 -0
- package/dist/features/launch/intent.js +8 -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 +2 -0
- package/dist/{src/features → features}/owned-ui/index.js +2 -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/features/owned-ui/session-fork-prompt.d.ts +8 -0
- package/dist/features/owned-ui/session-fork-prompt.js +34 -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.d.ts +2 -0
- package/dist/{src/foundation → foundation}/launch-guardian/main.js +18 -5
- package/dist/{src/foundation → foundation}/lifecycle/index.d.ts +2 -0
- package/dist/{src/foundation → foundation}/lifecycle/index.js +2 -0
- 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 +30 -0
- package/dist/{src/foundation → foundation}/lifecycle/paths.js +29 -1
- package/dist/foundation/lifecycle/session-selection.d.ts +9 -0
- package/dist/foundation/lifecycle/session-selection.js +39 -0
- package/dist/foundation/lifecycle/supervisor-startup.d.ts +30 -0
- package/dist/foundation/lifecycle/supervisor-startup.js +106 -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/foundation/process-containment/darwin-process-inspector.d.ts +5 -0
- package/dist/foundation/process-containment/darwin-process-inspector.js +7 -0
- package/dist/{src/foundation → foundation}/process-containment/index.d.ts +1 -0
- package/dist/{src/foundation → foundation}/process-containment/index.js +1 -0
- 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 +3 -1
- package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.js +5 -2
- 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 +23 -6
- package/dist/{src/foundation → foundation}/release/bootstrap.js +120 -34
- package/dist/foundation/release/cohort-state.d.ts +156 -0
- package/dist/foundation/release/cohort-state.js +471 -0
- package/dist/foundation/release/dependency-layer.d.ts +78 -0
- package/dist/foundation/release/dependency-layer.js +488 -0
- package/dist/foundation/release/endpoints.d.ts +20 -0
- package/dist/foundation/release/endpoints.js +50 -0
- package/dist/foundation/release/immutable-platform.d.ts +6 -0
- package/dist/foundation/release/immutable-platform.js +13 -0
- package/dist/{src/foundation → foundation}/release/index.d.ts +4 -0
- package/dist/{src/foundation → foundation}/release/index.js +4 -0
- package/dist/foundation/release/release-gc.d.ts +71 -0
- package/dist/foundation/release/release-gc.js +734 -0
- package/dist/{src/foundation → foundation}/release/release-store.d.ts +4 -2
- package/dist/{src/foundation → foundation}/release/release-store.js +64 -12
- package/dist/{src/foundation → foundation}/release/release.d.ts +4 -1
- package/dist/{src/foundation → foundation}/release/release.js +10 -3
- package/dist/foundation/release/restart-certification.d.ts +67 -0
- package/dist/foundation/release/restart-certification.js +211 -0
- 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 +116 -56
- 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/foundation/supervision/main.d.ts +1 -0
- package/dist/foundation/supervision/main.js +77 -0
- 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 +65 -3
- 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/{src/foundation/pi-component-adapter → integrations/pi/components}/index.d.ts +1 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.js +1 -1
- 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 +49 -18
- 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 +5 -2
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.js +2 -1
- package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.d.ts +57 -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 +28 -4
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.js +714 -127
- 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 +4 -0
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.js +4 -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 +32 -26
- 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 +19 -1
- package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.js +47 -5
- 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/session-selection.d.ts +28 -0
- package/dist/integrations/pi/engine/session-selection.js +90 -0
- 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/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.d.ts +18 -1
- 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 +141 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +1158 -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 +479 -719
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +45 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +463 -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 +74 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +380 -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 +4 -4
- package/dist/native/darwin-arm64/process-guardian +0 -0
- 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 +5 -0
- package/dist/{src/foundation/ui-components → ui/components}/index.js +5 -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 +208 -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 +152 -0
- package/dist/ui/components/transcript-viewport.js +631 -0
- package/dist/ui/components/visible-hyperlinks.d.ts +19 -0
- package/dist/ui/components/visible-hyperlinks.js +64 -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/process-guardian-provenance.md +3 -1
- 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 +36 -5
- package/docs/ci-release-runbook.md +186 -79
- package/docs/features/launch-profiles.md +24 -17
- package/docs/manual-ghost-link-baseline.md +89 -0
- 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.d.ts +0 -7
- 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 -106
- 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/main.d.ts +0 -1
- package/dist/src/foundation/supervision/main.js +0 -41
- 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.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}/lifecycle/commands.d.ts +0 -0
- /package/dist/{src/foundation → foundation}/lifecycle/commands.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}/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/pi-component-adapter → integrations/pi/components}/components.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}/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.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 { type ReleaseIdentity } from "./release.js";
|
|
2
|
+
import { type RuntimePayloadInventory } from "./dependency-layer.js";
|
|
2
3
|
export declare const RELEASE_MANIFEST_FILENAME: string;
|
|
3
4
|
export interface MaterializedRelease extends ReleaseIdentity {
|
|
4
5
|
readonly releaseRoot: string;
|
|
5
6
|
}
|
|
6
|
-
export type ReleaseContentOperation = "source-read" | "candidate-write" | "verification-read";
|
|
7
|
+
export type ReleaseContentOperation = "source-read" | "candidate-write" | "layer-write" | "layer-reuse" | "verification-read";
|
|
7
8
|
export interface ReleaseContentOperationEvent {
|
|
8
9
|
readonly operation: ReleaseContentOperation;
|
|
9
10
|
readonly path: string;
|
|
@@ -15,6 +16,7 @@ export interface MaterializeReleaseOptions {
|
|
|
15
16
|
readonly fileCount: number;
|
|
16
17
|
}) => void;
|
|
17
18
|
readonly onOperation?: (event: ReleaseContentOperationEvent) => void;
|
|
19
|
+
readonly onRuntimeInventory?: (inventory: RuntimePayloadInventory) => void;
|
|
18
20
|
/** Test seam for deterministic write-failure coverage. */
|
|
19
21
|
readonly writeCandidateFile?: (path: string, bytes: Uint8Array, mode: number) => Promise<void>;
|
|
20
22
|
}
|
|
@@ -30,7 +32,7 @@ export interface CertifiedReleaseRecord {
|
|
|
30
32
|
export declare function materializeRelease(packageRoot: string, dataDir: string, options?: MaterializeReleaseOptions): Promise<MaterializedRelease>;
|
|
31
33
|
/** Consume proof that this exact object was freshly materialized or fully verified in this process. */
|
|
32
34
|
export declare function consumeMaterializationProof(release: MaterializedRelease): boolean;
|
|
33
|
-
export declare function readMaterializedRelease(releaseRoot: string): Promise<MaterializedRelease>;
|
|
35
|
+
export declare function readMaterializedRelease(releaseRoot: string, selectedStoreRoot?: string): Promise<MaterializedRelease>;
|
|
34
36
|
/**
|
|
35
37
|
* Load metadata for a release whose bytes were already certified by the
|
|
36
38
|
* current parent process or an authenticated live supervisor. Callers must
|
|
@@ -1,9 +1,11 @@
|
|
|
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 { assertImmutableFileMode } from "./immutable-platform.js";
|
|
8
|
+
import { dependencyReference, materializeDependencyLayer, readCertifiedDependencyLayer, selectPublishedDependencyRuntimePayload, verifyDependencyLayer, } from "./dependency-layer.js";
|
|
7
9
|
const RELEASE_FILE_IO_CONCURRENCY = 32;
|
|
8
10
|
const certificationReady = new WeakSet();
|
|
9
11
|
export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
|
|
@@ -13,15 +15,32 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
13
15
|
});
|
|
14
16
|
const storeRoot = resolve(dataDir, "releases");
|
|
15
17
|
await mkdir(storeRoot, { recursive: true, mode: 0o700 });
|
|
16
|
-
|
|
18
|
+
const dependencyPaths = payload.paths.filter(path => path.startsWith("node_modules/"));
|
|
19
|
+
const productPaths = payload.paths.filter(path => !path.startsWith("node_modules/"));
|
|
20
|
+
const selectedDependencies = await selectPublishedDependencyRuntimePayload(payload.packageRoot, dependencyPaths);
|
|
21
|
+
options.onRuntimeInventory?.(selectedDependencies.inventory);
|
|
22
|
+
const layerOperations = [];
|
|
23
|
+
const layer = await materializeDependencyLayer(payload.packageRoot, dataDir, selectedDependencies.paths, {
|
|
24
|
+
inventory: selectedDependencies.inventory,
|
|
25
|
+
cachedFiles: payload.cachedFiles,
|
|
26
|
+
onOperation: event => layerOperations.push(event),
|
|
27
|
+
...(options.writeCandidateFile === undefined ? {} : { writeCandidateFile: options.writeCandidateFile }),
|
|
28
|
+
});
|
|
29
|
+
const layerReferences = layer === null ? [] : [dependencyReference(layer)];
|
|
30
|
+
options.onProgress?.({
|
|
31
|
+
phase: "copying",
|
|
32
|
+
fileCount: productPaths.length + (layer?.reused === false ? layer.files.length : 0),
|
|
33
|
+
});
|
|
34
|
+
for (const event of layerOperations)
|
|
35
|
+
options.onOperation?.(event);
|
|
17
36
|
const candidate = resolveWithin(storeRoot, `.candidate-${randomUUID()}`);
|
|
18
37
|
await mkdir(candidate, { recursive: false, mode: 0o700 });
|
|
19
38
|
try {
|
|
20
|
-
const directories = [...new Set(
|
|
39
|
+
const directories = [...new Set(productPaths.map(path => dirname(resolveWithin(candidate, path))))];
|
|
21
40
|
await mapWithConcurrency(directories, RELEASE_FILE_IO_CONCURRENCY, async (directory) => {
|
|
22
41
|
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
23
42
|
});
|
|
24
|
-
const files = await mapWithConcurrency(
|
|
43
|
+
const files = await mapWithConcurrency(productPaths, RELEASE_FILE_IO_CONCURRENCY, async (path) => {
|
|
25
44
|
const source = resolveWithin(payload.packageRoot, path);
|
|
26
45
|
const destination = resolveWithin(candidate, path);
|
|
27
46
|
const metadata = await lstat(source);
|
|
@@ -41,7 +60,12 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
41
60
|
options.onOperation?.({ operation: "candidate-write", path, bytes: bytes.length });
|
|
42
61
|
return releaseFileIdentity(path, bytes, (metadata.mode & 0o111) !== 0);
|
|
43
62
|
});
|
|
44
|
-
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files);
|
|
63
|
+
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files, layerReferences);
|
|
64
|
+
if (layer !== null) {
|
|
65
|
+
const binding = resolveWithin(candidate, "node_modules");
|
|
66
|
+
const target = resolveWithin(layer.layerRoot, "node_modules");
|
|
67
|
+
await symlink(target, binding, process.platform === "win32" ? "junction" : "dir");
|
|
68
|
+
}
|
|
45
69
|
const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
|
|
46
70
|
if (await lstat(releaseRoot).catch(() => null)) {
|
|
47
71
|
await rm(candidate, { recursive: true, force: true });
|
|
@@ -71,10 +95,10 @@ export function consumeMaterializationProof(release) {
|
|
|
71
95
|
certificationReady.delete(release);
|
|
72
96
|
return true;
|
|
73
97
|
}
|
|
74
|
-
export async function readMaterializedRelease(releaseRoot) {
|
|
98
|
+
export async function readMaterializedRelease(releaseRoot, selectedStoreRoot) {
|
|
75
99
|
const canonical = await realpath(releaseRoot);
|
|
76
100
|
const manifest = JSON.parse(await readFile(resolve(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
77
|
-
return await verifyMaterializedRelease(canonical, manifest);
|
|
101
|
+
return certificationReadyRelease(await verifyMaterializedRelease(canonical, manifest, selectedStoreRoot));
|
|
78
102
|
}
|
|
79
103
|
/**
|
|
80
104
|
* Load metadata for a release whose bytes were already certified by the
|
|
@@ -84,7 +108,8 @@ export async function readMaterializedRelease(releaseRoot) {
|
|
|
84
108
|
*/
|
|
85
109
|
export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
86
110
|
const canonical = await realpath(record.releaseRoot);
|
|
87
|
-
|
|
111
|
+
const canonicalStoreRoot = await realpath(selectedStoreRoot);
|
|
112
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
88
113
|
const manifest = JSON.parse(await readFile(resolveWithin(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
89
114
|
validateManifest(manifest);
|
|
90
115
|
if (manifest.releaseId !== record.releaseId || manifest.contentDigest !== record.contentDigest
|
|
@@ -93,12 +118,13 @@ export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
|
93
118
|
}
|
|
94
119
|
if (canonical.split(sep).at(-1) !== manifest.releaseId)
|
|
95
120
|
throw new Error(`release directory does not match identity ${manifest.releaseId}`);
|
|
121
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], false);
|
|
96
122
|
return { ...manifest, releaseRoot: canonical };
|
|
97
123
|
}
|
|
98
124
|
export async function verifyMaterializedRelease(releaseRoot, expected, selectedStoreRoot, options = {}) {
|
|
99
125
|
const canonical = await realpath(releaseRoot);
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
const canonicalStoreRoot = selectedStoreRoot ? await realpath(selectedStoreRoot) : await realpath(dirname(canonical));
|
|
127
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
102
128
|
const manifestPath = resolveWithin(canonical, RELEASE_MANIFEST_FILENAME);
|
|
103
129
|
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
104
130
|
validateManifest(manifest);
|
|
@@ -111,9 +137,10 @@ export async function verifyMaterializedRelease(releaseRoot, expected, selectedS
|
|
|
111
137
|
await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
|
|
112
138
|
await verifyFile(canonical, file, options);
|
|
113
139
|
});
|
|
114
|
-
const recomputed =
|
|
140
|
+
const recomputed = createReleaseIdentity(manifest.packageRoot, manifest.packageVersion, manifest.files, manifest.dependencyLayers ?? []).contentDigest;
|
|
115
141
|
if (recomputed !== manifest.contentDigest)
|
|
116
142
|
throw new Error(`release content digest mismatch for ${manifest.releaseId}`);
|
|
143
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], true, options);
|
|
117
144
|
return { ...manifest, releaseRoot: canonical };
|
|
118
145
|
}
|
|
119
146
|
export async function assertImmutableExecutionRoot(release, dataDir) {
|
|
@@ -135,6 +162,25 @@ export async function resolveReleaseEntryPoint(release, entryPoint) {
|
|
|
135
162
|
assertContained(release.releaseRoot, canonical, "entry point resolves outside the selected release root");
|
|
136
163
|
return canonical;
|
|
137
164
|
}
|
|
165
|
+
async function verifyReleaseDependencies(releaseRoot, storeRoot, references, fullVerification, options = {}) {
|
|
166
|
+
if (references.length === 0)
|
|
167
|
+
return;
|
|
168
|
+
if (references.length !== 1)
|
|
169
|
+
throw new Error("release currently supports exactly one dependency layer");
|
|
170
|
+
const dataDir = dirname(storeRoot);
|
|
171
|
+
const reference = references[0];
|
|
172
|
+
const layer = fullVerification
|
|
173
|
+
? await verifyDependencyLayer(dataDir, reference, event => options.onOperation?.({ operation: event.operation, path: event.path, bytes: event.bytes }))
|
|
174
|
+
: await readCertifiedDependencyLayer(dataDir, reference.layerId, reference);
|
|
175
|
+
const binding = resolveWithin(releaseRoot, reference.binding);
|
|
176
|
+
const metadata = await lstat(binding);
|
|
177
|
+
if (!metadata.isSymbolicLink())
|
|
178
|
+
throw new Error(`release dependency binding is not managed: ${binding}`);
|
|
179
|
+
const target = await realpath(binding);
|
|
180
|
+
const expected = await realpath(resolveWithin(layer.layerRoot, "node_modules"));
|
|
181
|
+
if (target !== expected)
|
|
182
|
+
throw new Error(`release dependency binding targets unexpected content: ${target}`);
|
|
183
|
+
}
|
|
138
184
|
async function verifyFile(root, file, options) {
|
|
139
185
|
const path = resolveWithin(root, file.path);
|
|
140
186
|
const metadata = await lstat(path).catch(() => null);
|
|
@@ -142,6 +188,7 @@ async function verifyFile(root, file, options) {
|
|
|
142
188
|
throw new Error(`release candidate is incomplete: ${file.path}`);
|
|
143
189
|
if (metadata.size !== file.bytes)
|
|
144
190
|
throw new Error(`release file size mismatch: ${file.path}`);
|
|
191
|
+
assertImmutableFileMode(metadata, path);
|
|
145
192
|
const bytes = await readFile(path);
|
|
146
193
|
options.onOperation?.({ operation: "verification-read", path: file.path, bytes: bytes.length });
|
|
147
194
|
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
@@ -159,6 +206,11 @@ function validateManifest(value) {
|
|
|
159
206
|
throw new Error(PRODUCT_TEXT.diagnostic("release identity is invalid"));
|
|
160
207
|
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
161
208
|
throw new Error("release manifest contains no files");
|
|
209
|
+
if (value.dependencyLayers !== undefined && (!Array.isArray(value.dependencyLayers) || value.dependencyLayers.length === 0
|
|
210
|
+
|| value.dependencyLayers.some(layer => !/^dependencies-[a-f0-9]{32}$/.test(layer.layerId)
|
|
211
|
+
|| !/^[a-f0-9]{64}$/.test(layer.contentDigest) || layer.binding !== "node_modules"))) {
|
|
212
|
+
throw new Error("release dependency-layer references are invalid");
|
|
213
|
+
}
|
|
162
214
|
for (const file of value.files) {
|
|
163
215
|
if (typeof file.path !== "string" || file.path.length === 0 || file.path.includes("\\") || file.path.startsWith("/") || file.path.split("/").includes("..")) {
|
|
164
216
|
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
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
2
|
+
import { type DependencyLayerReference } from "./dependency-layer.js";
|
|
3
|
+
import { type ImmutablePlatformPolicy } from "./immutable-platform.js";
|
|
4
|
+
import { type ReleaseFileIdentity } from "./release.js";
|
|
5
|
+
import { type CertifiedReleaseRecord, type MaterializedRelease } from "./release-store.js";
|
|
6
|
+
declare const RESTART_SEAL_VERSION = 1;
|
|
7
|
+
type EvidenceKind = "directory" | "file" | "binding";
|
|
8
|
+
interface PathEvidence {
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly kind: EvidenceKind;
|
|
11
|
+
readonly device: string;
|
|
12
|
+
readonly inode: string;
|
|
13
|
+
readonly mode: number;
|
|
14
|
+
readonly size: string;
|
|
15
|
+
readonly modifiedNs: string;
|
|
16
|
+
readonly changedNs: string;
|
|
17
|
+
readonly bornNs: string;
|
|
18
|
+
}
|
|
19
|
+
interface LayerRestartEvidence extends DependencyLayerReference {
|
|
20
|
+
readonly layerRoot: PathEvidence;
|
|
21
|
+
readonly manifest: PathEvidence;
|
|
22
|
+
readonly certification: PathEvidence;
|
|
23
|
+
readonly bindingEvidence: PathEvidence;
|
|
24
|
+
readonly bindingTarget: string;
|
|
25
|
+
}
|
|
26
|
+
interface RestartSealCore {
|
|
27
|
+
readonly version: typeof RESTART_SEAL_VERSION;
|
|
28
|
+
readonly platform: NodeJS.Platform;
|
|
29
|
+
readonly platformPolicy: ImmutablePlatformPolicy;
|
|
30
|
+
readonly releaseId: string;
|
|
31
|
+
readonly packageVersion: string;
|
|
32
|
+
readonly contentDigest: string;
|
|
33
|
+
readonly packageRoot: string;
|
|
34
|
+
readonly releaseRoot: PathEvidence;
|
|
35
|
+
readonly manifest: PathEvidence;
|
|
36
|
+
readonly launchEntries: readonly ReleaseFileIdentity[];
|
|
37
|
+
readonly launchEntryEvidence: readonly PathEvidence[];
|
|
38
|
+
readonly dependencyLayers: readonly LayerRestartEvidence[];
|
|
39
|
+
readonly certifiedAt: string;
|
|
40
|
+
}
|
|
41
|
+
export interface RestartSeal extends RestartSealCore {
|
|
42
|
+
readonly sealDigest: string;
|
|
43
|
+
}
|
|
44
|
+
interface ReleaseCertificationDocument {
|
|
45
|
+
readonly schema: typeof PRODUCT_IDENTITY.evidence.releaseCertificationSchema;
|
|
46
|
+
readonly releaseId: string;
|
|
47
|
+
readonly contentDigest: string;
|
|
48
|
+
readonly verifiedAt: string;
|
|
49
|
+
readonly checks: readonly {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly passed: boolean;
|
|
52
|
+
}[];
|
|
53
|
+
readonly restartSeal: RestartSeal | null;
|
|
54
|
+
}
|
|
55
|
+
export interface RestartValidationEvent {
|
|
56
|
+
readonly operation: "restart-evidence-read";
|
|
57
|
+
readonly path: string;
|
|
58
|
+
}
|
|
59
|
+
/** Build compact restart authority after complete content verification established read-only payload files. */
|
|
60
|
+
export declare function createRestartSeal(release: MaterializedRelease, dataDir: string): Promise<RestartSeal | null>;
|
|
61
|
+
/** Recover an approved release from durable bounded evidence without traversing its payload. */
|
|
62
|
+
export declare function readRestartCertifiedRelease(record: CertifiedReleaseRecord & {
|
|
63
|
+
readonly diagnosticsPath?: string | null;
|
|
64
|
+
}, dataDir: string, onEvent?: (event: RestartValidationEvent) => void): Promise<MaterializedRelease>;
|
|
65
|
+
export declare function releaseCertificationDocument(release: MaterializedRelease, restartSeal: RestartSeal | null, verifiedAt?: string): ReleaseCertificationDocument;
|
|
66
|
+
export declare function restartSealDigest(core: RestartSealCore): string;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, readFile, realpath, stat } from "node:fs/promises";
|
|
3
|
+
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
5
|
+
import { DEPENDENCY_LAYER_MANIFEST, dependencyLayerCertificationPath } from "./dependency-layer.js";
|
|
6
|
+
import { immutablePlatformPolicy } from "./immutable-platform.js";
|
|
7
|
+
import { PRODUCT_PACKAGE_NAME } from "./release.js";
|
|
8
|
+
import { RELEASE_MANIFEST_FILENAME } from "./release-store.js";
|
|
9
|
+
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
10
|
+
const RESTART_SEAL_VERSION = 1;
|
|
11
|
+
const REQUIRED_LAUNCH_ENTRIES = ["bin/supervisor.js", "bin/guardian.js"];
|
|
12
|
+
/** Build compact restart authority after complete content verification established read-only payload files. */
|
|
13
|
+
export async function createRestartSeal(release, dataDir) {
|
|
14
|
+
const policy = immutablePlatformPolicy();
|
|
15
|
+
if (policy === null)
|
|
16
|
+
return null;
|
|
17
|
+
const canonicalData = await realpath(dataDir);
|
|
18
|
+
const releasesRoot = await realpath(resolve(canonicalData, "releases"));
|
|
19
|
+
const releaseRoot = await realpath(release.releaseRoot);
|
|
20
|
+
assertDirectChild(releasesRoot, releaseRoot, release.releaseId, "release");
|
|
21
|
+
const manifestPath = resolve(releaseRoot, RELEASE_MANIFEST_FILENAME);
|
|
22
|
+
const launchEntries = REQUIRED_LAUNCH_ENTRIES.map(path => release.files.find(candidate => candidate.path === path));
|
|
23
|
+
if (launchEntries.some(file => file === undefined))
|
|
24
|
+
return null;
|
|
25
|
+
const certifiedLaunchEntries = launchEntries;
|
|
26
|
+
const layersRoot = release.dependencyLayers?.length ? await realpath(resolve(canonicalData, "dependency-layers")) : null;
|
|
27
|
+
const dependencyLayers = await Promise.all((release.dependencyLayers ?? []).map(async (reference) => {
|
|
28
|
+
if (layersRoot === null)
|
|
29
|
+
throw new Error("restart certification has no dependency-layer root");
|
|
30
|
+
const layerRoot = await realpath(resolve(layersRoot, reference.layerId));
|
|
31
|
+
assertDirectChild(layersRoot, layerRoot, reference.layerId, "dependency layer");
|
|
32
|
+
const bindingPath = resolve(releaseRoot, reference.binding);
|
|
33
|
+
const bindingTarget = await realpath(bindingPath);
|
|
34
|
+
const expectedTarget = await realpath(resolve(layerRoot, "node_modules"));
|
|
35
|
+
if (bindingTarget !== expectedTarget)
|
|
36
|
+
throw new Error(`restart certification dependency binding targets unexpected content: ${bindingPath}`);
|
|
37
|
+
return {
|
|
38
|
+
...reference,
|
|
39
|
+
layerRoot: await pathEvidence(layerRoot, "directory"),
|
|
40
|
+
manifest: await pathEvidence(resolve(layerRoot, DEPENDENCY_LAYER_MANIFEST), "file"),
|
|
41
|
+
certification: await pathEvidence(dependencyLayerCertificationPath(canonicalData, reference.layerId), "file"),
|
|
42
|
+
bindingEvidence: await pathEvidence(bindingPath, "binding", true),
|
|
43
|
+
bindingTarget,
|
|
44
|
+
};
|
|
45
|
+
}));
|
|
46
|
+
const core = {
|
|
47
|
+
version: RESTART_SEAL_VERSION,
|
|
48
|
+
platform: process.platform,
|
|
49
|
+
platformPolicy: policy,
|
|
50
|
+
releaseId: release.releaseId,
|
|
51
|
+
packageVersion: release.packageVersion,
|
|
52
|
+
contentDigest: release.contentDigest,
|
|
53
|
+
packageRoot: release.packageRoot,
|
|
54
|
+
releaseRoot: await pathEvidence(releaseRoot, "directory"),
|
|
55
|
+
manifest: await pathEvidence(manifestPath, "file"),
|
|
56
|
+
launchEntries: certifiedLaunchEntries,
|
|
57
|
+
launchEntryEvidence: await Promise.all(certifiedLaunchEntries.map(file => pathEvidence(resolve(releaseRoot, file.path), "file"))),
|
|
58
|
+
dependencyLayers,
|
|
59
|
+
certifiedAt: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
return { ...core, sealDigest: restartSealDigest(core) };
|
|
62
|
+
}
|
|
63
|
+
/** Recover an approved release from durable bounded evidence without traversing its payload. */
|
|
64
|
+
export async function readRestartCertifiedRelease(record, dataDir, onEvent) {
|
|
65
|
+
const transaction = await new UpdateTransactionStore(dataDir).read();
|
|
66
|
+
if (transaction?.status === "active")
|
|
67
|
+
throw new Error("restart certification is unavailable during an active update transaction");
|
|
68
|
+
const certificationPath = resolve(dataDir, `certification-${record.releaseId}.json`);
|
|
69
|
+
if (record.diagnosticsPath !== undefined && record.diagnosticsPath !== certificationPath) {
|
|
70
|
+
throw new Error("restart certification path differs from the approved release record");
|
|
71
|
+
}
|
|
72
|
+
const certificationMetadata = await lstat(certificationPath);
|
|
73
|
+
onEvent?.({ operation: "restart-evidence-read", path: certificationPath });
|
|
74
|
+
if (!certificationMetadata.isFile() || certificationMetadata.isSymbolicLink() || (certificationMetadata.mode & 0o222) !== 0) {
|
|
75
|
+
throw new Error("restart certification is not an immutable regular file");
|
|
76
|
+
}
|
|
77
|
+
const certification = await readJson(certificationPath, onEvent);
|
|
78
|
+
if (certification.schema !== PRODUCT_IDENTITY.evidence.releaseCertificationSchema
|
|
79
|
+
|| certification.releaseId !== record.releaseId || certification.contentDigest !== record.contentDigest
|
|
80
|
+
|| !certification.checks.some(check => check.id === "immutable-content" && check.passed)) {
|
|
81
|
+
throw new Error("restart certification differs from the approved release record");
|
|
82
|
+
}
|
|
83
|
+
const seal = normalizeRestartSeal(certification.restartSeal);
|
|
84
|
+
if (seal.platform !== process.platform || seal.platformPolicy !== immutablePlatformPolicy()) {
|
|
85
|
+
throw new Error("restart certification platform immutability evidence is unsupported");
|
|
86
|
+
}
|
|
87
|
+
if (seal.releaseId !== record.releaseId || seal.contentDigest !== record.contentDigest
|
|
88
|
+
|| resolve(seal.releaseRoot.path) !== resolve(record.releaseRoot)
|
|
89
|
+
|| (record.packageVersion !== undefined && seal.packageVersion !== record.packageVersion)) {
|
|
90
|
+
throw new Error("restart seal differs from the approved release identity");
|
|
91
|
+
}
|
|
92
|
+
const { sealDigest, ...core } = seal;
|
|
93
|
+
if (restartSealDigest(core) !== sealDigest)
|
|
94
|
+
throw new Error("restart seal digest is invalid");
|
|
95
|
+
const canonicalData = await observedRealpath(dataDir, onEvent);
|
|
96
|
+
const releasesRoot = await observedRealpath(resolve(canonicalData, "releases"), onEvent);
|
|
97
|
+
const releaseRoot = await observedRealpath(seal.releaseRoot.path, onEvent);
|
|
98
|
+
assertDirectChild(releasesRoot, releaseRoot, seal.releaseId, "release");
|
|
99
|
+
await assertPathEvidence(seal.releaseRoot, onEvent);
|
|
100
|
+
await assertPathEvidence(seal.manifest, onEvent);
|
|
101
|
+
for (let index = 0; index < seal.launchEntries.length; index += 1) {
|
|
102
|
+
const file = seal.launchEntries[index];
|
|
103
|
+
if (!REQUIRED_LAUNCH_ENTRIES.includes(file.path)) {
|
|
104
|
+
throw new Error(`restart seal contains an unexpected launch entry: ${file.path}`);
|
|
105
|
+
}
|
|
106
|
+
const evidence = seal.launchEntryEvidence[index];
|
|
107
|
+
if (!evidence || evidence.path !== resolve(releaseRoot, file.path) || file.bytes !== Number(evidence.size)) {
|
|
108
|
+
throw new Error(`restart launch entry evidence differs: ${file.path}`);
|
|
109
|
+
}
|
|
110
|
+
await assertPathEvidence(evidence, onEvent);
|
|
111
|
+
}
|
|
112
|
+
if (seal.launchEntries.length !== REQUIRED_LAUNCH_ENTRIES.length)
|
|
113
|
+
throw new Error("restart seal launch entries are incomplete");
|
|
114
|
+
const layersRoot = seal.dependencyLayers.length ? await observedRealpath(resolve(canonicalData, "dependency-layers"), onEvent) : null;
|
|
115
|
+
for (const layer of seal.dependencyLayers) {
|
|
116
|
+
if (layersRoot === null)
|
|
117
|
+
throw new Error("restart seal has no dependency-layer root");
|
|
118
|
+
const layerRoot = await observedRealpath(layer.layerRoot.path, onEvent);
|
|
119
|
+
assertDirectChild(layersRoot, layerRoot, layer.layerId, "dependency layer");
|
|
120
|
+
await assertPathEvidence(layer.layerRoot, onEvent);
|
|
121
|
+
await assertPathEvidence(layer.manifest, onEvent);
|
|
122
|
+
await assertPathEvidence(layer.certification, onEvent);
|
|
123
|
+
await assertPathEvidence(layer.bindingEvidence, onEvent, true);
|
|
124
|
+
const bindingTarget = await observedRealpath(layer.bindingEvidence.path, onEvent);
|
|
125
|
+
const expectedTarget = await observedRealpath(resolve(layerRoot, "node_modules"), onEvent);
|
|
126
|
+
if (bindingTarget !== layer.bindingTarget || bindingTarget !== expectedTarget) {
|
|
127
|
+
throw new Error(`restart dependency binding targets unexpected content: ${layer.bindingEvidence.path}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
packageName: PRODUCT_PACKAGE_NAME,
|
|
132
|
+
packageVersion: seal.packageVersion,
|
|
133
|
+
contentDigest: seal.contentDigest,
|
|
134
|
+
releaseId: seal.releaseId,
|
|
135
|
+
packageRoot: seal.packageRoot,
|
|
136
|
+
files: seal.launchEntries,
|
|
137
|
+
...(seal.dependencyLayers.length === 0 ? {} : {
|
|
138
|
+
dependencyLayers: seal.dependencyLayers.map(({ layerId, contentDigest, binding }) => ({ layerId, contentDigest, binding })),
|
|
139
|
+
}),
|
|
140
|
+
releaseRoot,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function releaseCertificationDocument(release, restartSeal, verifiedAt = new Date().toISOString()) {
|
|
144
|
+
return {
|
|
145
|
+
schema: PRODUCT_IDENTITY.evidence.releaseCertificationSchema,
|
|
146
|
+
releaseId: release.releaseId,
|
|
147
|
+
contentDigest: release.contentDigest,
|
|
148
|
+
verifiedAt,
|
|
149
|
+
checks: [{ id: "immutable-content", passed: true }],
|
|
150
|
+
restartSeal,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export function restartSealDigest(core) {
|
|
154
|
+
return createHash("sha256").update(JSON.stringify(core)).digest("hex");
|
|
155
|
+
}
|
|
156
|
+
async function assertPathEvidence(evidence, onEvent, symbolic = false) {
|
|
157
|
+
const actual = await pathEvidence(evidence.path, evidence.kind, symbolic, onEvent);
|
|
158
|
+
if (JSON.stringify(actual) !== JSON.stringify(evidence))
|
|
159
|
+
throw new Error(`restart path evidence changed: ${evidence.path}`);
|
|
160
|
+
}
|
|
161
|
+
async function pathEvidence(path, kind, symbolic = false, onEvent) {
|
|
162
|
+
onEvent?.({ operation: "restart-evidence-read", path });
|
|
163
|
+
const metadata = symbolic ? await lstat(path, { bigint: true }) : await stat(path, { bigint: true });
|
|
164
|
+
if ((kind === "directory") !== metadata.isDirectory())
|
|
165
|
+
throw new Error(`restart evidence kind changed: ${path}`);
|
|
166
|
+
if (kind === "file" && !metadata.isFile())
|
|
167
|
+
throw new Error(`restart evidence file changed: ${path}`);
|
|
168
|
+
if (kind === "binding" && !metadata.isSymbolicLink())
|
|
169
|
+
throw new Error(`restart evidence binding changed: ${path}`);
|
|
170
|
+
if (kind === "file" && (metadata.mode & 146n) !== 0n)
|
|
171
|
+
throw new Error(`restart evidence file is writable: ${path}`);
|
|
172
|
+
return {
|
|
173
|
+
path: resolve(path),
|
|
174
|
+
kind,
|
|
175
|
+
device: metadata.dev.toString(),
|
|
176
|
+
inode: metadata.ino.toString(),
|
|
177
|
+
mode: Number(metadata.mode),
|
|
178
|
+
size: metadata.size.toString(),
|
|
179
|
+
modifiedNs: metadata.mtimeNs.toString(),
|
|
180
|
+
changedNs: metadata.ctimeNs.toString(),
|
|
181
|
+
bornNs: metadata.birthtimeNs.toString(),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function normalizeRestartSeal(value) {
|
|
185
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
186
|
+
throw new Error("release certification has no durable restart seal");
|
|
187
|
+
const seal = value;
|
|
188
|
+
if (seal.version !== RESTART_SEAL_VERSION || typeof seal.platform !== "string" || typeof seal.platformPolicy !== "string"
|
|
189
|
+
|| typeof seal.releaseId !== "string" || typeof seal.packageVersion !== "string" || typeof seal.contentDigest !== "string"
|
|
190
|
+
|| typeof seal.packageRoot !== "string" || typeof seal.certifiedAt !== "string" || typeof seal.sealDigest !== "string"
|
|
191
|
+
|| !seal.releaseRoot || !seal.manifest || !Array.isArray(seal.launchEntries) || !Array.isArray(seal.launchEntryEvidence)
|
|
192
|
+
|| !Array.isArray(seal.dependencyLayers)) {
|
|
193
|
+
throw new Error("durable restart seal is invalid");
|
|
194
|
+
}
|
|
195
|
+
return seal;
|
|
196
|
+
}
|
|
197
|
+
async function readJson(path, onEvent) {
|
|
198
|
+
onEvent?.({ operation: "restart-evidence-read", path });
|
|
199
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
200
|
+
}
|
|
201
|
+
async function observedRealpath(path, onEvent) {
|
|
202
|
+
onEvent?.({ operation: "restart-evidence-read", path });
|
|
203
|
+
return await realpath(path);
|
|
204
|
+
}
|
|
205
|
+
function assertDirectChild(parent, child, expectedName, kind) {
|
|
206
|
+
const fromParent = relative(parent, child);
|
|
207
|
+
if (isAbsolute(fromParent) || fromParent === ".." || fromParent.startsWith(`..${sep}`) || fromParent.includes(sep)
|
|
208
|
+
|| basename(child) !== expectedName) {
|
|
209
|
+
throw new Error(`restart ${kind} is outside its managed store: ${child}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -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;
|