@timurproko/a1 0.2.6-dev.736 → 0.2.7-dev.1221
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 +41 -3
- package/bin/automatic-update.js +7 -0
- package/bin/cli.js +4 -0
- package/bin/guardian.js +5 -0
- package/bin/tab-launch-context.d.ts +27 -0
- package/bin/tab-launch-context.js +94 -0
- package/bin/ui.js +107 -59
- package/dist/app/session-shell/clipboard-diagnostics.js +1 -1
- package/dist/app/session-shell/dialog-coordinator.d.ts +42 -0
- package/dist/app/session-shell/dialog-coordinator.js +133 -0
- package/dist/app/session-shell/dock-chrome.d.ts +34 -0
- package/dist/app/session-shell/dock-chrome.js +109 -0
- package/dist/app/session-shell/exit-notice.d.ts +1 -1
- package/dist/app/session-shell/exit-notice.js +1 -1
- package/dist/app/session-shell/extension-surface.d.ts +30 -0
- package/dist/app/session-shell/extension-surface.js +105 -0
- package/dist/app/session-shell/helper-pool.d.ts +3 -5
- package/dist/app/session-shell/helper-pool.js +3 -5
- package/dist/app/session-shell/input-surface-switch.d.ts +36 -0
- package/dist/app/session-shell/input-surface-switch.js +85 -0
- package/dist/app/session-shell/login-interaction.d.ts +27 -0
- package/dist/app/session-shell/login-interaction.js +91 -0
- package/dist/app/session-shell/prompt-submission.d.ts +48 -0
- package/dist/app/session-shell/prompt-submission.js +276 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +38 -0
- package/dist/app/session-shell/resident-tab-coordinator.js +77 -0
- package/dist/app/session-shell/response-copy-helper.js +21 -6
- package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
- package/dist/app/session-shell/response-copy-transport.d.ts +16 -6
- package/dist/app/session-shell/response-copy-transport.js +69 -17
- package/dist/app/session-shell/session-concern.d.ts +22 -0
- package/dist/app/session-shell/session-concern.js +29 -0
- package/dist/app/session-shell/session-lifecycle.d.ts +29 -0
- package/dist/app/session-shell/session-lifecycle.js +49 -0
- package/dist/app/session-shell/session-shell-root.d.ts +42 -6
- package/dist/app/session-shell/session-shell-root.js +196 -908
- package/dist/app/session-shell/session-shell.d.ts +3 -8
- package/dist/app/session-shell/session-shell.js +324 -835
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
- package/dist/app/session-shell/session-viewport-controller.js +29 -15
- package/dist/app/session-shell/settings-bindings.d.ts +17 -0
- package/dist/app/session-shell/settings-bindings.js +91 -0
- package/dist/app/session-shell/shell-editor-factory.d.ts +27 -0
- package/dist/app/session-shell/shell-editor-factory.js +102 -0
- package/dist/app/session-shell/shell-host.d.ts +6 -0
- package/dist/app/session-shell/shell-host.js +34 -7
- package/dist/app/session-shell/shell-layout.d.ts +64 -0
- package/dist/app/session-shell/shell-layout.js +18 -0
- package/dist/app/session-shell/suggestion-surface.d.ts +25 -0
- package/dist/app/session-shell/suggestion-surface.js +63 -0
- package/dist/app/session-shell/transcript-document.d.ts +78 -0
- package/dist/app/session-shell/transcript-document.js +365 -0
- package/dist/app/session-shell/viewport-layout.d.ts +31 -0
- package/dist/app/session-shell/viewport-layout.js +160 -0
- package/dist/app/session-shell/workflow-output.d.ts +33 -0
- package/dist/app/session-shell/workflow-output.js +174 -0
- package/dist/app/session-shell/workflow-router.d.ts +25 -0
- package/dist/app/session-shell/workflow-router.js +39 -0
- package/dist/cli/dispatch.d.ts +11 -1
- package/dist/cli/dispatch.js +79 -5
- package/dist/cli/tabs-request.d.ts +7 -0
- package/dist/cli/tabs-request.js +1 -0
- package/dist/cli/tabs.d.ts +14 -0
- package/dist/cli/tabs.js +151 -0
- package/dist/cli/version-stats.js +38 -3
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +27 -28
- package/dist/composition/owned-ui.js +205 -153
- package/dist/composition/resident-tab-bridge.d.ts +25 -0
- package/dist/composition/resident-tab-bridge.js +180 -0
- package/dist/composition/session-backend.d.ts +19 -0
- package/dist/composition/session-backend.js +1 -0
- package/dist/composition/session-runtime-registry.d.ts +20 -0
- package/dist/composition/session-runtime-registry.js +85 -0
- package/dist/composition/settings-route-host.d.ts +16 -1
- package/dist/composition/settings-route-host.js +29 -10
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/composition/startup-route.d.ts +32 -0
- package/dist/composition/startup-route.js +72 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +242 -0
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/model.d.ts +3 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +3 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +11 -4
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/intent.d.ts +3 -1
- package/dist/features/launch/intent.js +7 -4
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +6 -8
- package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
- package/dist/features/owned-ui/project-trust-prompt.js +7 -3
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/resources/release-notes.json +4 -0
- package/dist/features/owned-ui/settings-app.d.ts +12 -2
- package/dist/features/owned-ui/settings-app.js +119 -84
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/auto-name.d.ts +11 -0
- package/dist/features/resident-tab/auto-name.js +44 -0
- package/dist/features/resident-tab/index.d.ts +5 -0
- package/dist/features/resident-tab/index.js +7 -0
- package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
- package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
- package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
- package/dist/features/resident-tab/shortcut-declarations.js +26 -0
- package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
- package/dist/features/resident-tab/shortcut-projection.js +64 -0
- package/dist/features/resident-tab/status-deriver.d.ts +10 -0
- package/dist/features/resident-tab/status-deriver.js +12 -0
- package/dist/foundation/launch-context/index.d.ts +12 -0
- package/dist/foundation/launch-context/index.js +26 -0
- package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
- package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
- package/dist/foundation/launch-guardian/main.d.ts +7 -1
- package/dist/foundation/launch-guardian/main.js +60 -7
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
- package/dist/foundation/process-containment/addon-containment.js +139 -0
- package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
- package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
- package/dist/foundation/process-containment/addon.d.ts +42 -0
- package/dist/foundation/process-containment/addon.js +57 -0
- package/dist/foundation/process-containment/artifact.d.ts +1 -1
- package/dist/foundation/process-containment/artifact.js +6 -5
- package/dist/foundation/process-containment/contracts.d.ts +2 -0
- package/dist/foundation/process-containment/index.d.ts +4 -4
- package/dist/foundation/process-containment/index.js +3 -3
- package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
- package/dist/foundation/process-containment/native-process-inspector.js +10 -10
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +7 -9
- package/dist/foundation/release/bootstrap.js +107 -24
- package/dist/foundation/release/cohort-state.d.ts +1 -1
- package/dist/foundation/release/cohort-state.js +6 -3
- package/dist/foundation/release/dependency-certification.js +54 -19
- package/dist/foundation/release/early-accent.d.ts +12 -0
- package/dist/foundation/release/early-accent.js +59 -0
- package/dist/foundation/release/index.d.ts +2 -2
- package/dist/foundation/release/index.js +2 -2
- package/dist/foundation/release/latest-release.d.ts +24 -2
- package/dist/foundation/release/latest-release.js +51 -1
- package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
- package/dist/foundation/release/progress-palette.generated.js +137 -3
- package/dist/foundation/release/release-gc.d.ts +3 -0
- package/dist/foundation/release/release-gc.js +59 -8
- package/dist/foundation/release/release-store.d.ts +12 -0
- package/dist/foundation/release/release-store.js +124 -2
- package/dist/foundation/release/update-execution-lease.d.ts +48 -0
- package/dist/foundation/release/update-execution-lease.js +161 -0
- package/dist/foundation/release/update.d.ts +12 -7
- package/dist/foundation/release/update.js +281 -196
- package/dist/foundation/resident-tabs/bounded-host-command.d.ts +18 -0
- package/dist/foundation/resident-tabs/bounded-host-command.js +39 -0
- package/dist/foundation/resident-tabs/bridge-client.d.ts +70 -0
- package/dist/foundation/resident-tabs/bridge-client.js +321 -0
- package/dist/foundation/resident-tabs/doctor.d.ts +28 -0
- package/dist/foundation/resident-tabs/doctor.js +166 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +36 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +151 -0
- package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
- package/dist/foundation/resident-tabs/fault-injection.js +26 -0
- package/dist/foundation/resident-tabs/fixture-control.d.ts +7 -0
- package/dist/foundation/resident-tabs/fixture-control.js +35 -0
- package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
- package/dist/foundation/resident-tabs/host-entry.js +17 -0
- package/dist/foundation/resident-tabs/index.d.ts +12 -0
- package/dist/foundation/resident-tabs/index.js +6 -0
- package/dist/foundation/resident-tabs/maintenance.d.ts +39 -0
- package/dist/foundation/resident-tabs/maintenance.js +75 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +4 -0
- package/dist/foundation/resident-tabs/progress-observation.js +150 -0
- package/dist/foundation/resident-tabs/prompt-journal.d.ts +63 -0
- package/dist/foundation/resident-tabs/prompt-journal.js +213 -0
- package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
- package/dist/foundation/resident-tabs/protocol.js +195 -0
- package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
- package/dist/foundation/resident-tabs/recovery.js +205 -0
- package/dist/foundation/resident-tabs/release-holds.d.ts +28 -0
- package/dist/foundation/resident-tabs/release-holds.js +75 -0
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/routing.d.ts +11 -0
- package/dist/foundation/resident-tabs/routing.js +10 -0
- package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
- package/dist/foundation/resident-tabs/session-sync.js +37 -0
- package/dist/foundation/resident-tabs/settings.d.ts +12 -0
- package/dist/foundation/resident-tabs/settings.js +58 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +14 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.js +78 -0
- package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
- package/dist/foundation/resident-tabs/zip-archive.js +56 -0
- package/dist/foundation/session-writer-lock/index.d.ts +30 -0
- package/dist/foundation/session-writer-lock/index.js +203 -0
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
- package/dist/foundation/terminal-cleanup/index.js +1 -1
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +6 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +6 -0
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +6 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +90 -6
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
- package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
- package/dist/integrations/pi/components/share-operation-dialog.d.ts +2 -0
- package/dist/integrations/pi/components/share-operation-dialog.js +2 -1
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +58 -36
- package/dist/integrations/pi/components/shell-extension-ui.js +79 -60
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +21 -5
- package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +5 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +18 -4
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +12 -1
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -6
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +16 -9
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +4 -4
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +31 -25
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +8 -7
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +8 -7
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +10 -9
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +2 -6
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +103 -101
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +9 -9
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +13 -21
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/history/editor-core.js +7 -5
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +29 -2
- package/dist/integrations/pi/engine/adapter.d.ts +5 -77
- package/dist/integrations/pi/engine/adapter.js +95 -394
- package/dist/integrations/pi/engine/command-dispatch.d.ts +13 -0
- package/dist/integrations/pi/engine/command-dispatch.js +57 -0
- package/dist/integrations/pi/engine/host.js +2 -2
- package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
- package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
- package/dist/integrations/pi/engine/pi-settings-metadata.json +14 -2
- package/dist/integrations/pi/engine/ports.d.ts +56 -0
- package/dist/integrations/pi/engine/ports.js +93 -0
- package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
- package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/runtime-integration.js +102 -29
- package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
- package/dist/integrations/pi/engine/session-runtime.js +13 -5
- package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
- package/dist/integrations/pi/engine/session-selection.js +45 -9
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +15 -0
- package/dist/integrations/pi/engine/settings-port.js +11 -15
- package/dist/integrations/pi/engine/view-state.d.ts +42 -0
- package/dist/integrations/pi/engine/view-state.js +126 -0
- package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
- package/dist/integrations/pi/engine/workflow-runner.js +73 -4
- package/dist/integrations/pi/engine/workflow-support.d.ts +12 -0
- package/dist/integrations/pi/engine/workflow-support.js +36 -0
- package/dist/integrations/pi/engine/workflows.d.ts +3 -1
- package/dist/integrations/pi/engine/workflows.js +1 -0
- package/dist/integrations/pi/startup-public.js +257 -310
- package/dist/integrations/pi/startup-public.manifest.json +564 -440
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +32 -1
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +5 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +12 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +31 -2
- package/dist/native/darwin-arm64/manifest.json +10 -7
- package/dist/native/darwin-arm64/process-guardian.node +0 -0
- package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
- package/dist/native/darwin-arm64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +23 -0
- package/dist/native/darwin-x64/process-guardian.node +0 -0
- package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
- package/dist/native/darwin-x64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +10 -7
- package/dist/native/linux-x64/process-guardian.node +0 -0
- package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
- package/dist/native/linux-x64/terminal-host.manifest.json +46 -0
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +10 -7
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +46 -0
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +3 -3
- package/dist/product-identity.js +4 -4
- package/dist/product-identity.json +16 -2
- package/dist/runtime-payload-inventory.json +6330 -6677
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.js +35 -30
- package/dist/ui/components/index.d.ts +2 -2
- package/dist/ui/components/index.js +1 -1
- package/dist/ui/components/line-input.d.ts +4 -1
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +11 -8
- package/dist/ui/components/prompt-input.js +15 -13
- package/dist/ui/components/selection-copy.d.ts +2 -0
- package/dist/ui/components/selection-copy.js +13 -0
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/submitted-prompt.d.ts +9 -6
- package/dist/ui/components/submitted-prompt.js +20 -12
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/components/transcript-viewport.d.ts +9 -2
- package/dist/ui/components/transcript-viewport.js +176 -55
- package/dist/ui/components/value-menu.d.ts +6 -3
- package/dist/ui/components/value-menu.js +6 -5
- package/dist/ui/settings/declarations.d.ts +90 -16
- package/dist/ui/settings/declarations.js +67 -14
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/index.d.ts +4 -2
- package/dist/ui/settings/index.js +2 -1
- package/dist/ui/settings/manager.d.ts +16 -3
- package/dist/ui/settings/manager.js +122 -33
- package/dist/ui/settings/migrations.d.ts +6 -0
- package/dist/ui/settings/migrations.js +73 -0
- package/dist/ui/settings/profile-document.d.ts +26 -0
- package/dist/ui/settings/profile-document.js +164 -0
- package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
- package/dist/ui/settings/progress-palette.generated.js +139 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/boundaries.md +25 -10
- package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
- package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
- package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
- package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
- package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +70 -0
- package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +32 -0
- package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +115 -0
- package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +48 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +125 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +56 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +174 -0
- package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +18 -0
- package/docs/architecture/internal-naming.md +3 -1
- package/docs/architecture/process-guardian-provenance.md +18 -10
- package/docs/architecture/project-structure.md +8 -0
- package/docs/architecture/resident-tabs-requirements.md +18 -0
- package/docs/architecture/resident-tabs.md +648 -0
- package/docs/architecture/response-copy-delivery.md +1 -1
- package/docs/architecture/terminal-host-proof-gate.md +3 -1
- package/docs/architecture/terminal-host-provenance.md +16 -4
- package/docs/architecture/terminal-host-spike-evidence.md +1 -1
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/architecture/toolchain.md +11 -9
- package/docs/ci-release-runbook.md +14 -7
- package/docs/features/launch-profiles.md +11 -8
- package/docs/features/prompt-history.md +41 -20
- package/docs/manual-resident-tabs-acceptance.md +51 -0
- package/docs/openspec-archive-automation.md +49 -22
- package/docs/releases/0.2.6.md +44 -0
- package/docs/validation.md +49 -9
- package/package.json +19 -6
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
- package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
- package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
- package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/process-guardian.exe +0 -0
|
@@ -14,6 +14,7 @@ import { UpdateTransactionStore } from "./update-transaction.js";
|
|
|
14
14
|
import { cleanupUpdateRecoveryCapsules } from "./update-recovery.js";
|
|
15
15
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
16
16
|
import { collectCompileCaches, startupCompileCachePath } from "../startup/index.js";
|
|
17
|
+
import { readResidentReleaseHolds } from "../resident-tabs/index.js";
|
|
17
18
|
const DEFAULT_CANDIDATE_AGE_MS = 60 * 60 * 1_000;
|
|
18
19
|
const DEFAULT_LIMITS = { maxItems: 8, maxDurationMs: 2_000, concurrency: 1 };
|
|
19
20
|
const DEFAULT_WORKER_DURATION_MS = 10 * 60 * 1_000;
|
|
@@ -30,7 +31,7 @@ export async function prepareReleaseCleanup(dataDir, paths, options = {}) {
|
|
|
30
31
|
const store = new CohortStateStore(dataDir);
|
|
31
32
|
const discovered = await discoverCleanupInputs(dataDir, store, options);
|
|
32
33
|
const transactionStore = options.transactionStore ?? new UpdateTransactionStore(dataDir);
|
|
33
|
-
await store.reconcileRetention(async () => await protectionInputs(paths, transactionStore, options.externalHolds ?? []), discovered.orphans);
|
|
34
|
+
await store.reconcileRetention(async () => await protectionInputs(dataDir, paths, transactionStore, options.externalHolds ?? [], options.residentHolds), discovered.orphans);
|
|
34
35
|
for (const diagnostic of discovered.diagnostics.slice(0, 16)) {
|
|
35
36
|
await store.recordCleanupFailure(diagnostic.releaseId, diagnostic.stage, diagnostic.error);
|
|
36
37
|
}
|
|
@@ -77,7 +78,7 @@ export async function runBoundedReleaseCleanup(dataDir, paths, options = {}) {
|
|
|
77
78
|
maximum -= 1;
|
|
78
79
|
const certificationId = certificationReleaseId(artifact);
|
|
79
80
|
if (certificationId !== null) {
|
|
80
|
-
const inputs = await protectionInputs(paths, transactionStore, options.externalHolds ?? []);
|
|
81
|
+
const inputs = await protectionInputs(dataDir, paths, transactionStore, options.externalHolds ?? [], options.residentHolds);
|
|
81
82
|
if (isProtectedDetachedRelease(await store.read(), inputs, certificationId))
|
|
82
83
|
continue;
|
|
83
84
|
}
|
|
@@ -232,7 +233,7 @@ export async function collectRelease(store, dataDir, releaseId, externalReferenc
|
|
|
232
233
|
const state = await store.read();
|
|
233
234
|
if (!state.releases[releaseId])
|
|
234
235
|
throw new Error(`unknown release ${releaseId}`);
|
|
235
|
-
await store.reconcileRetention(async () => ({ ...(await protectionInputs(paths, new UpdateTransactionStore(dataDir), holds)) }));
|
|
236
|
+
await store.reconcileRetention(async () => ({ ...(await protectionInputs(dataDir, paths, new UpdateTransactionStore(dataDir), holds)) }));
|
|
236
237
|
const disposition = (await store.read()).cleanup.pending[releaseId];
|
|
237
238
|
if (!disposition)
|
|
238
239
|
throw new Error(`release ${releaseId} is still referenced and cannot be collected`);
|
|
@@ -243,7 +244,7 @@ export async function collectRelease(store, dataDir, releaseId, externalReferenc
|
|
|
243
244
|
async function collectDisposition(store, dataDir, paths, disposition, options) {
|
|
244
245
|
const releaseId = disposition.release.releaseId;
|
|
245
246
|
try {
|
|
246
|
-
const inputs = await protectionInputs(paths, options.transactionStore ?? new UpdateTransactionStore(dataDir), options.externalHolds ?? []);
|
|
247
|
+
const inputs = await protectionInputs(dataDir, paths, options.transactionStore ?? new UpdateTransactionStore(dataDir), options.externalHolds ?? [], options.residentHolds);
|
|
247
248
|
const current = await store.read();
|
|
248
249
|
if (isProtectedDetachedRelease(current, inputs, releaseId))
|
|
249
250
|
return false;
|
|
@@ -289,12 +290,62 @@ function isProtectedDetachedRelease(state, inputs, releaseId) {
|
|
|
289
290
|
|| (inputs.externalHolds ?? []).some(hold => hold.releaseId === releaseId)
|
|
290
291
|
|| (inputs.transaction?.status === "active" && inputs.transaction.priorActiveReleaseId === releaseId);
|
|
291
292
|
}
|
|
292
|
-
async function protectionInputs(paths, transactionStore, externalHolds) {
|
|
293
|
-
const [liveIds, transaction] = await Promise.all([
|
|
293
|
+
async function protectionInputs(dataDir, paths, transactionStore, externalHolds, residentHolds) {
|
|
294
|
+
const [liveIds, transaction, resident] = await Promise.all([
|
|
294
295
|
paths ? liveReleaseIds(paths) : Promise.resolve([]),
|
|
295
296
|
transactionStore.read(),
|
|
297
|
+
residentReleaseHolds(dataDir, residentHolds),
|
|
296
298
|
]);
|
|
297
|
-
return { liveReleaseIds: liveIds, externalHolds, transaction: transactionReference(transaction) };
|
|
299
|
+
return { liveReleaseIds: liveIds, externalHolds: [...externalHolds, ...resident], transaction: transactionReference(transaction) };
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Holds every release a live resident server, holder, or tab child runs from. Retention fails
|
|
303
|
+
* closed: an incomplete report, or a recorded root inside the release store that names no known
|
|
304
|
+
* release, holds every known release.
|
|
305
|
+
*/
|
|
306
|
+
async function residentReleaseHolds(dataDir, reader) {
|
|
307
|
+
const state = await new CohortStateStore(dataDir).read();
|
|
308
|
+
const records = [
|
|
309
|
+
...Object.values(state.releases),
|
|
310
|
+
...Object.values(state.cleanup.pending).map(disposition => disposition.release),
|
|
311
|
+
];
|
|
312
|
+
const preferred = [state.references.active, state.references.rollback]
|
|
313
|
+
.flatMap(id => id && state.releases[id] ? [state.releases[id].releaseRoot] : []);
|
|
314
|
+
const roots = [...new Set([...preferred, ...Object.values(state.releases).map(record => record.releaseRoot)])];
|
|
315
|
+
const report = await (reader ?? ((directory, releaseRoots) => readResidentReleaseHolds({ dataDir: directory, releaseRoots })))(dataDir, roots);
|
|
316
|
+
const everything = () => [...new Set(records.map(record => record.releaseId))].map(releaseId => ({ authority: "resident", releaseId }));
|
|
317
|
+
if (!report.complete)
|
|
318
|
+
return everything();
|
|
319
|
+
if (report.releaseRoots.length === 0)
|
|
320
|
+
return [];
|
|
321
|
+
// Rationale: native Windows extended paths and Unix aliases must match the catalog's spelling.
|
|
322
|
+
const byRoot = new Map();
|
|
323
|
+
for (const record of records) {
|
|
324
|
+
const canonical = await realpath(record.releaseRoot).catch(() => record.releaseRoot);
|
|
325
|
+
byRoot.set(comparablePath(canonical), record.releaseId);
|
|
326
|
+
}
|
|
327
|
+
const directory = await realpath(resolve(dataDir, "releases")).catch(() => null);
|
|
328
|
+
if (directory === null)
|
|
329
|
+
return everything();
|
|
330
|
+
const store = comparablePath(directory);
|
|
331
|
+
const holds = new Set();
|
|
332
|
+
for (const root of report.releaseRoots) {
|
|
333
|
+
// Security: an unresolved live root may alias managed content; uncertainty must retain it.
|
|
334
|
+
const canonical = await realpath(root).catch(() => null);
|
|
335
|
+
if (canonical === null)
|
|
336
|
+
return everything();
|
|
337
|
+
const key = comparablePath(canonical);
|
|
338
|
+
const releaseId = byRoot.get(key);
|
|
339
|
+
if (releaseId)
|
|
340
|
+
holds.add(releaseId);
|
|
341
|
+
else if (key.startsWith(`${store}${sep}`))
|
|
342
|
+
return everything();
|
|
343
|
+
}
|
|
344
|
+
return [...holds].sort().map(releaseId => ({ authority: "resident", releaseId }));
|
|
345
|
+
}
|
|
346
|
+
function comparablePath(path) {
|
|
347
|
+
const absolute = resolve(path);
|
|
348
|
+
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
298
349
|
}
|
|
299
350
|
function transactionReference(transaction) {
|
|
300
351
|
return transaction === null ? null : { status: transaction.status, priorActiveReleaseId: transaction.priorActiveReleaseId };
|
|
@@ -745,7 +796,7 @@ function parseWorkerHolds(value) {
|
|
|
745
796
|
if (!Array.isArray(parsed))
|
|
746
797
|
return [];
|
|
747
798
|
return parsed.filter((hold) => Boolean(hold) && typeof hold === "object"
|
|
748
|
-
&&
|
|
799
|
+
&& ["agent", "migration", "resident"].includes(hold.authority)
|
|
749
800
|
&& typeof hold.releaseId === "string");
|
|
750
801
|
}
|
|
751
802
|
catch {
|
|
@@ -31,12 +31,24 @@ export interface CertifiedReleaseRecord {
|
|
|
31
31
|
/** Absent on records written before releases declared a handoff contract. */
|
|
32
32
|
readonly launchContract?: string;
|
|
33
33
|
}
|
|
34
|
+
/** Reports that installed package bytes cannot safely reproduce an approved release. */
|
|
35
|
+
export declare class ReleaseRepairUnavailableError extends Error {
|
|
36
|
+
readonly code: "RELEASE_REPAIR_UNAVAILABLE";
|
|
37
|
+
constructor(message: string, options?: ErrorOptions);
|
|
38
|
+
}
|
|
34
39
|
/** Process-authority compatibility controls for metadata-only release loading. */
|
|
35
40
|
export type ReadCertifiedReleaseManifestOptions = ReadCertifiedDependencyLayerOptions;
|
|
36
41
|
export declare function materializeRelease(packageRoot: string, dataDir: string, options?: MaterializeReleaseOptions): Promise<MaterializedRelease>;
|
|
37
42
|
/** Consume proof that this exact object was freshly materialized or fully verified in this process. */
|
|
38
43
|
export declare function consumeMaterializationProof(release: MaterializedRelease): boolean;
|
|
39
44
|
export declare function readMaterializedRelease(releaseRoot: string, selectedStoreRoot?: string): Promise<MaterializedRelease>;
|
|
45
|
+
/** Fast presence check before trusted certification permits a release to skip full verification. */
|
|
46
|
+
export declare function assertCertifiedFilesPresent(releaseRoot: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Restore changed or missing product payload files from the installed package without replacing the
|
|
49
|
+
* immutable release directory. Dependency layers retain their own verification and repair policy.
|
|
50
|
+
*/
|
|
51
|
+
export declare function repairMaterializedRelease(packageRoot: string, releaseRoot: string): Promise<MaterializedRelease>;
|
|
40
52
|
/**
|
|
41
53
|
* Load metadata for a release whose bytes were already certified by the
|
|
42
54
|
* current parent process or an authenticated live supervisor. Callers must
|
|
@@ -10,6 +10,14 @@ import { dependencyReference, materializeDependencyLayer, readCertifiedDependenc
|
|
|
10
10
|
const RELEASE_FILE_IO_CONCURRENCY = 32;
|
|
11
11
|
const certificationReady = new WeakSet();
|
|
12
12
|
export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
|
|
13
|
+
/** Reports that installed package bytes cannot safely reproduce an approved release. */
|
|
14
|
+
export class ReleaseRepairUnavailableError extends Error {
|
|
15
|
+
code = "RELEASE_REPAIR_UNAVAILABLE";
|
|
16
|
+
constructor(message, options = {}) {
|
|
17
|
+
super(message, options);
|
|
18
|
+
this.name = "ReleaseRepairUnavailableError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
14
22
|
const payload = await discoverReleasePayload(packageRoot, {
|
|
15
23
|
onSourceRead: (path, bytes) => options.onOperation?.({ operation: "source-read", path, bytes }),
|
|
@@ -71,7 +79,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
71
79
|
const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
|
|
72
80
|
if (await lstat(releaseRoot).catch(() => null)) {
|
|
73
81
|
await rm(candidate, { recursive: true, force: true });
|
|
74
|
-
return certificationReadyRelease(await
|
|
82
|
+
return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
|
|
75
83
|
}
|
|
76
84
|
await writeFile(resolve(candidate, RELEASE_MANIFEST_FILENAME), JSON.stringify(identity, null, 2), { mode: 0o400, flag: "wx" });
|
|
77
85
|
try {
|
|
@@ -81,7 +89,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
81
89
|
if (!await lstat(releaseRoot).catch(() => null))
|
|
82
90
|
throw error;
|
|
83
91
|
await rm(candidate, { recursive: true, force: true });
|
|
84
|
-
return certificationReadyRelease(await
|
|
92
|
+
return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
|
|
85
93
|
}
|
|
86
94
|
return certificationReadyRelease({ ...identity, releaseRoot: await realpath(releaseRoot) });
|
|
87
95
|
}
|
|
@@ -102,6 +110,61 @@ export async function readMaterializedRelease(releaseRoot, selectedStoreRoot) {
|
|
|
102
110
|
const manifest = JSON.parse(await readFile(resolve(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
103
111
|
return certificationReadyRelease(await verifyMaterializedRelease(canonical, manifest, selectedStoreRoot));
|
|
104
112
|
}
|
|
113
|
+
/** Fast presence check before trusted certification permits a release to skip full verification. */
|
|
114
|
+
export async function assertCertifiedFilesPresent(releaseRoot) {
|
|
115
|
+
const canonical = await realpath(releaseRoot);
|
|
116
|
+
const manifest = await readReleaseManifest(canonical);
|
|
117
|
+
await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
|
|
118
|
+
const metadata = await lstat(resolveWithin(canonical, file.path));
|
|
119
|
+
if (!metadata.isFile() || metadata.isSymbolicLink())
|
|
120
|
+
throw new Error(`certified release file is missing: ${file.path}`);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Restore changed or missing product payload files from the installed package without replacing the
|
|
125
|
+
* immutable release directory. Dependency layers retain their own verification and repair policy.
|
|
126
|
+
*/
|
|
127
|
+
export async function repairMaterializedRelease(packageRoot, releaseRoot) {
|
|
128
|
+
const canonical = await realpath(releaseRoot);
|
|
129
|
+
let manifest;
|
|
130
|
+
let payload;
|
|
131
|
+
try {
|
|
132
|
+
manifest = await readReleaseManifest(canonical);
|
|
133
|
+
payload = await discoverReleasePayload(packageRoot);
|
|
134
|
+
assertCurrentLaunchContract(payload);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
throw repairUnavailable("release repair metadata is unavailable", error);
|
|
138
|
+
}
|
|
139
|
+
if (payload.packageVersion !== manifest.packageVersion) {
|
|
140
|
+
throw repairUnavailable(`installed package version ${payload.packageVersion} does not match release ${manifest.packageVersion}`);
|
|
141
|
+
}
|
|
142
|
+
const payloadPaths = new Set(payload.paths.filter(path => !path.startsWith("node_modules/")));
|
|
143
|
+
try {
|
|
144
|
+
await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
|
|
145
|
+
const destination = resolveWithin(canonical, file.path);
|
|
146
|
+
if (!await certifiedFileDiffers(destination, file))
|
|
147
|
+
return;
|
|
148
|
+
if (!payloadPaths.has(file.path))
|
|
149
|
+
throw repairUnavailable(`installed package does not carry ${file.path}`);
|
|
150
|
+
const source = resolveWithin(payload.packageRoot, file.path);
|
|
151
|
+
const sourceMetadata = await lstat(source);
|
|
152
|
+
if (!sourceMetadata.isFile() || sourceMetadata.isSymbolicLink())
|
|
153
|
+
throw repairUnavailable(`installed package file is not regular: ${file.path}`);
|
|
154
|
+
const bytes = payload.cachedFiles.get(file.path) ?? await readFile(source);
|
|
155
|
+
if (bytes.length !== file.bytes || createHash("sha256").update(bytes).digest("hex") !== file.sha256) {
|
|
156
|
+
throw repairUnavailable(`installed package bytes do not match release file ${file.path}`);
|
|
157
|
+
}
|
|
158
|
+
await replaceReleaseFile(destination, bytes, file);
|
|
159
|
+
});
|
|
160
|
+
return await verifyMaterializedRelease(canonical, manifest);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof ReleaseRepairUnavailableError)
|
|
164
|
+
throw error;
|
|
165
|
+
throw repairUnavailable("release repair could not restore the certified payload", error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
105
168
|
/**
|
|
106
169
|
* Load metadata for a release whose bytes were already certified by the
|
|
107
170
|
* current parent process or an authenticated live supervisor. Callers must
|
|
@@ -197,6 +260,65 @@ async function verifyFile(root, file, options) {
|
|
|
197
260
|
if (digest !== file.sha256)
|
|
198
261
|
throw new Error(`release file digest mismatch: ${file.path}`);
|
|
199
262
|
}
|
|
263
|
+
async function verifyOrRepairMaterializedRelease(packageRoot, releaseRoot, expected) {
|
|
264
|
+
try {
|
|
265
|
+
return await verifyMaterializedRelease(releaseRoot, expected);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
await repairMaterializedRelease(packageRoot, releaseRoot);
|
|
269
|
+
return await verifyMaterializedRelease(releaseRoot, expected);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function readReleaseManifest(releaseRoot) {
|
|
273
|
+
const manifest = JSON.parse(await readFile(resolveWithin(releaseRoot, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
274
|
+
validateManifest(manifest);
|
|
275
|
+
if (releaseRoot.split(sep).at(-1) !== manifest.releaseId)
|
|
276
|
+
throw new Error(`release directory does not match identity ${manifest.releaseId}`);
|
|
277
|
+
return manifest;
|
|
278
|
+
}
|
|
279
|
+
async function certifiedFileDiffers(path, file) {
|
|
280
|
+
const metadata = await lstat(path).catch((error) => {
|
|
281
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
282
|
+
return null;
|
|
283
|
+
throw error;
|
|
284
|
+
});
|
|
285
|
+
if (metadata === null || !metadata.isFile() || metadata.isSymbolicLink() || metadata.size !== file.bytes)
|
|
286
|
+
return true;
|
|
287
|
+
if ((metadata.mode & 0o222) !== 0 || ((metadata.mode & 0o111) !== 0) !== file.executable)
|
|
288
|
+
return true;
|
|
289
|
+
return createHash("sha256").update(await readFile(path)).digest("hex") !== file.sha256;
|
|
290
|
+
}
|
|
291
|
+
async function replaceReleaseFile(path, bytes, file) {
|
|
292
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
293
|
+
const temporary = resolve(dirname(path), `.a1-repair-${randomUUID()}`);
|
|
294
|
+
const mode = file.executable ? 0o500 : 0o400;
|
|
295
|
+
let existing = null;
|
|
296
|
+
try {
|
|
297
|
+
await writeFile(temporary, bytes, { flag: "wx", mode: 0o600 });
|
|
298
|
+
existing = await lstat(path).catch((error) => {
|
|
299
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
300
|
+
return null;
|
|
301
|
+
throw error;
|
|
302
|
+
});
|
|
303
|
+
if (existing !== null) {
|
|
304
|
+
if (!existing.isFile() || existing.isSymbolicLink())
|
|
305
|
+
throw repairUnavailable(`release file cannot be safely replaced: ${file.path}`);
|
|
306
|
+
// Platform: Windows refuses rename-over for a read-only destination.
|
|
307
|
+
await chmod(path, 0o600);
|
|
308
|
+
}
|
|
309
|
+
await rename(temporary, path);
|
|
310
|
+
await chmod(path, mode);
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
await rm(temporary, { force: true }).catch(() => { });
|
|
314
|
+
if (existing !== null)
|
|
315
|
+
await chmod(path, mode).catch(() => { });
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function repairUnavailable(message, cause) {
|
|
320
|
+
return new ReleaseRepairUnavailableError(message, cause === undefined ? {} : { cause });
|
|
321
|
+
}
|
|
200
322
|
function certificationReadyRelease(release) {
|
|
201
323
|
certificationReady.add(release);
|
|
202
324
|
return release;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { UpdateChannel } from "./types.js";
|
|
2
|
+
import { type UpdateTransaction } from "./update-transaction.js";
|
|
3
|
+
export declare const UPDATE_EXECUTION_LEASE_FILENAME = "update-execution.json";
|
|
4
|
+
declare const LEASE_SCHEMA = 1;
|
|
5
|
+
export type UpdateExecutionMode = "manual" | "automatic";
|
|
6
|
+
export interface UpdateExecutionOwner {
|
|
7
|
+
readonly schema: typeof LEASE_SCHEMA;
|
|
8
|
+
readonly ownerId: string;
|
|
9
|
+
readonly pid: number;
|
|
10
|
+
readonly pidStartIdentity: string;
|
|
11
|
+
readonly packageRoot: string;
|
|
12
|
+
readonly channel: UpdateChannel;
|
|
13
|
+
readonly targetVersion: string;
|
|
14
|
+
readonly mode: UpdateExecutionMode;
|
|
15
|
+
readonly acquiredAt: string;
|
|
16
|
+
}
|
|
17
|
+
export type UpdateExecutionLeaseResult = {
|
|
18
|
+
readonly status: "acquired";
|
|
19
|
+
readonly owner: UpdateExecutionOwner;
|
|
20
|
+
release(): Promise<void>;
|
|
21
|
+
} | {
|
|
22
|
+
readonly status: "busy";
|
|
23
|
+
readonly owner: UpdateExecutionOwner;
|
|
24
|
+
}
|
|
25
|
+
/** A dead owner whose active transaction names another target; only a manual update may settle it. */
|
|
26
|
+
| {
|
|
27
|
+
readonly status: "interrupted";
|
|
28
|
+
readonly owner: UpdateExecutionOwner;
|
|
29
|
+
readonly transaction: UpdateTransaction;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Serializes every package replacement for one A1 data root. A dead owner is moved aside before a
|
|
33
|
+
* new exclusive create, so concurrent recovery attempts cannot both acquire the installation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function acquireUpdateExecutionLease(input: {
|
|
36
|
+
readonly dataDir: string;
|
|
37
|
+
readonly packageRoot: string;
|
|
38
|
+
readonly channel: UpdateChannel;
|
|
39
|
+
readonly targetVersion: string;
|
|
40
|
+
readonly mode: UpdateExecutionMode;
|
|
41
|
+
}): Promise<UpdateExecutionLeaseResult>;
|
|
42
|
+
export declare function readUpdateExecutionOwner(dataDir: string): Promise<UpdateExecutionOwner | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Removes a dead owner's lease once its transaction has been settled. A lease that another owner
|
|
45
|
+
* holds by now, or whose process turned out to be alive, is left alone.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reclaimDeadLease(dataDir: string, owner: UpdateExecutionOwner): Promise<void>;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import lockfile from "proper-lockfile";
|
|
5
|
+
import { processIsAlive } from "./process-cleanup.js";
|
|
6
|
+
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
7
|
+
export const UPDATE_EXECUTION_LEASE_FILENAME = "update-execution.json";
|
|
8
|
+
const LEASE_SCHEMA = 1;
|
|
9
|
+
const MAX_LEASE_BYTES = 8_192;
|
|
10
|
+
/**
|
|
11
|
+
* Serializes every package replacement for one A1 data root. A dead owner is moved aside before a
|
|
12
|
+
* new exclusive create, so concurrent recovery attempts cannot both acquire the installation.
|
|
13
|
+
*/
|
|
14
|
+
export async function acquireUpdateExecutionLease(input) {
|
|
15
|
+
const path = resolve(input.dataDir, UPDATE_EXECUTION_LEASE_FILENAME);
|
|
16
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
17
|
+
const releaseAcquisition = await lockfile.lock(path, {
|
|
18
|
+
realpath: false,
|
|
19
|
+
stale: 60_000,
|
|
20
|
+
update: 20_000,
|
|
21
|
+
retries: { retries: 20, factor: 1.2, minTimeout: 20, maxTimeout: 100, randomize: true },
|
|
22
|
+
});
|
|
23
|
+
const owner = {
|
|
24
|
+
schema: LEASE_SCHEMA,
|
|
25
|
+
ownerId: randomUUID(),
|
|
26
|
+
pid: process.pid,
|
|
27
|
+
pidStartIdentity: currentProcessStartIdentity(),
|
|
28
|
+
packageRoot: resolve(input.packageRoot),
|
|
29
|
+
channel: input.channel,
|
|
30
|
+
targetVersion: input.targetVersion,
|
|
31
|
+
mode: input.mode,
|
|
32
|
+
acquiredAt: new Date().toISOString(),
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
36
|
+
try {
|
|
37
|
+
const file = await open(path, "wx", 0o600);
|
|
38
|
+
try {
|
|
39
|
+
await file.writeFile(`${JSON.stringify(owner)}\n`);
|
|
40
|
+
await file.sync();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await file.close();
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
status: "acquired",
|
|
47
|
+
owner,
|
|
48
|
+
release: async () => await releaseLease(path, owner),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (!hasCode(error, "EEXIST"))
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
const existing = await readUpdateExecutionOwner(input.dataDir);
|
|
56
|
+
if (existing === null)
|
|
57
|
+
continue;
|
|
58
|
+
// Security: an uninspectable or reused pid stays conservative. A process known to be absent is
|
|
59
|
+
// reclaimed only when its durable transaction agrees with both owners; a transaction that names
|
|
60
|
+
// another target is handed to a manual caller to settle, while automatic work fails closed.
|
|
61
|
+
if (processIsAlive(existing.pid))
|
|
62
|
+
return { status: "busy", owner: existing };
|
|
63
|
+
const foreign = await foreignActiveTransaction(input.dataDir, existing, input);
|
|
64
|
+
if (foreign !== null) {
|
|
65
|
+
return input.mode === "manual"
|
|
66
|
+
? { status: "interrupted", owner: existing, transaction: foreign }
|
|
67
|
+
: { status: "busy", owner: existing };
|
|
68
|
+
}
|
|
69
|
+
const stale = `${path}.stale-${existing.ownerId}-${randomUUID()}`;
|
|
70
|
+
try {
|
|
71
|
+
await rename(path, stale);
|
|
72
|
+
await rm(stale, { force: true });
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (!hasCode(error, "ENOENT"))
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const existing = await readUpdateExecutionOwner(input.dataDir);
|
|
80
|
+
if (existing !== null)
|
|
81
|
+
return { status: "busy", owner: existing };
|
|
82
|
+
throw new Error("could not acquire update execution lease");
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await releaseAcquisition();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function readUpdateExecutionOwner(dataDir) {
|
|
89
|
+
const path = resolve(dataDir, UPDATE_EXECUTION_LEASE_FILENAME);
|
|
90
|
+
let source;
|
|
91
|
+
try {
|
|
92
|
+
source = await readFile(path, "utf8");
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (hasCode(error, "ENOENT"))
|
|
96
|
+
return null;
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
if (Buffer.byteLength(source, "utf8") > MAX_LEASE_BYTES)
|
|
100
|
+
throw new Error("update execution lease is oversized");
|
|
101
|
+
const value = JSON.parse(source);
|
|
102
|
+
validateOwner(value);
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
async function releaseLease(path, owner) {
|
|
106
|
+
const current = await readUpdateExecutionOwner(dirname(path));
|
|
107
|
+
if (current === null || current.ownerId !== owner.ownerId || current.pid !== owner.pid
|
|
108
|
+
|| current.pidStartIdentity !== owner.pidStartIdentity)
|
|
109
|
+
return;
|
|
110
|
+
const released = `${path}.released-${owner.ownerId}-${randomUUID()}`;
|
|
111
|
+
try {
|
|
112
|
+
await rename(path, released);
|
|
113
|
+
await rm(released, { force: true });
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (!hasCode(error, "ENOENT"))
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function validateOwner(value) {
|
|
121
|
+
if (value.schema !== LEASE_SCHEMA || typeof value.ownerId !== "string" || value.ownerId.length === 0
|
|
122
|
+
|| !Number.isSafeInteger(value.pid) || value.pid <= 0 || typeof value.pidStartIdentity !== "string"
|
|
123
|
+
|| typeof value.packageRoot !== "string" || !["stable", "next"].includes(value.channel)
|
|
124
|
+
|| typeof value.targetVersion !== "string" || !["manual", "automatic"].includes(value.mode)
|
|
125
|
+
|| typeof value.acquiredAt !== "string") {
|
|
126
|
+
throw new Error("invalid update execution lease");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Removes a dead owner's lease once its transaction has been settled. A lease that another owner
|
|
131
|
+
* holds by now, or whose process turned out to be alive, is left alone.
|
|
132
|
+
*/
|
|
133
|
+
export async function reclaimDeadLease(dataDir, owner) {
|
|
134
|
+
if (processIsAlive(owner.pid))
|
|
135
|
+
return;
|
|
136
|
+
await releaseLease(resolve(dataDir, UPDATE_EXECUTION_LEASE_FILENAME), owner);
|
|
137
|
+
}
|
|
138
|
+
/** The active transaction when it or the request disagrees with the dead owner, else null. */
|
|
139
|
+
async function foreignActiveTransaction(dataDir, owner, requested) {
|
|
140
|
+
const transaction = await new UpdateTransactionStore(dataDir).read();
|
|
141
|
+
if (transaction?.status !== "active")
|
|
142
|
+
return null;
|
|
143
|
+
const matches = transaction.channel === owner.channel && transaction.targetVersion === owner.targetVersion
|
|
144
|
+
&& samePath(transaction.packageRoot, owner.packageRoot)
|
|
145
|
+
&& requested.channel === owner.channel && requested.targetVersion === owner.targetVersion
|
|
146
|
+
&& samePath(requested.packageRoot, owner.packageRoot);
|
|
147
|
+
return matches ? null : transaction;
|
|
148
|
+
}
|
|
149
|
+
function samePath(left, right) {
|
|
150
|
+
const canonicalLeft = resolve(left);
|
|
151
|
+
const canonicalRight = resolve(right);
|
|
152
|
+
return process.platform === "win32"
|
|
153
|
+
? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase()
|
|
154
|
+
: canonicalLeft === canonicalRight;
|
|
155
|
+
}
|
|
156
|
+
function currentProcessStartIdentity() {
|
|
157
|
+
return `${process.pid}:${Math.floor(Date.now() - process.uptime() * 1_000)}`;
|
|
158
|
+
}
|
|
159
|
+
function hasCode(error, code) {
|
|
160
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
161
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { UpdateChannel } from "./types.js";
|
|
2
|
+
import { type RegistryFetcher } from "./latest-release.js";
|
|
2
3
|
import { type UpdateActivationPhase, type UpdateMaterializationProgress } from "./update-activation.js";
|
|
3
4
|
import { type UpdateRecoveryState, type UpdateTransaction, type UpdateTransactionPhase } from "./update-transaction.js";
|
|
5
|
+
import { type UpdateExecutionOwner } from "./update-execution-lease.js";
|
|
4
6
|
import { resolveNpmCli, type ProtectedPackageReplacementResult } from "./update-recovery.js";
|
|
5
7
|
export declare const PRODUCT_PACKAGE: string;
|
|
6
8
|
export type { UpdateChannel } from "./types.js";
|
|
@@ -61,10 +63,17 @@ export interface SelfUpdateOptions {
|
|
|
61
63
|
channel?: UpdateChannel;
|
|
62
64
|
/** A specific preview to install, named by its development number or full version. */
|
|
63
65
|
target?: string;
|
|
66
|
+
/** Internal exact channel target selected by the automatic update probe. */
|
|
67
|
+
resolvedTarget?: string;
|
|
68
|
+
mode?: "manual" | "automatic";
|
|
69
|
+
onLeaseAcquired?: (owner: UpdateExecutionOwner) => void;
|
|
70
|
+
onLeaseBusy?: (owner: UpdateExecutionOwner) => void;
|
|
64
71
|
environment?: NodeJS.ProcessEnv;
|
|
65
72
|
fileSystem?: UpdateFileSystem;
|
|
66
73
|
output?: UpdateOutput;
|
|
67
74
|
runner?: UpdateProcessRunner;
|
|
75
|
+
/** Reads the registry's package document when a preview is named; defaults to a bounded fetch. */
|
|
76
|
+
fetcher?: RegistryFetcher;
|
|
68
77
|
lifecycle?: UpdateLifecycleCoordinator;
|
|
69
78
|
transactionStore?: UpdateTransactionJournal;
|
|
70
79
|
/** Test seam for protected global package replacement. */
|
|
@@ -80,6 +89,7 @@ export interface SelfUpdateOptions {
|
|
|
80
89
|
export type { UpdateActivationPhase, UpdateMaterializationProgress } from "./update-activation.js";
|
|
81
90
|
export interface UpdateLifecycleCoordinator {
|
|
82
91
|
targetIsActive(targetVersion: string): Promise<boolean>;
|
|
92
|
+
verifyAutomaticSafety?(): Promise<void>;
|
|
83
93
|
shutdownVerifiedOwners(targetVersion: string): Promise<{
|
|
84
94
|
priorActiveVersion: string | null;
|
|
85
95
|
}>;
|
|
@@ -123,6 +133,7 @@ export interface UpdatePackageReplacementInput {
|
|
|
123
133
|
* The update therefore keeps checking for this long before it calls the package locked.
|
|
124
134
|
*/
|
|
125
135
|
export declare const PACKAGE_UNLOCK_PATIENCE_MS = 15000;
|
|
136
|
+
export declare const UPDATE_ALREADY_RUNNING_EXIT_CODE = 75;
|
|
126
137
|
/** Whether a rename failure means something holds the tree, as opposed to the tree being wrong. */
|
|
127
138
|
export declare function isPackageLockError(error: unknown): boolean;
|
|
128
139
|
/** What the user reads when the package stayed held for the whole patience window. */
|
|
@@ -131,15 +142,9 @@ export declare function lockedPackageDiagnostic(packageRoot: string, waitedMs: n
|
|
|
131
142
|
export declare const CHILD_DIAGNOSTIC_LIMIT = 8000;
|
|
132
143
|
export declare function createNpmProcessRunner(platform?: NodeJS.Platform): UpdateProcessRunner;
|
|
133
144
|
export type UpdateOwnershipAction = "clean-dead-record" | "leave-running" | "end-session";
|
|
134
|
-
/**
|
|
135
|
-
* What an update does about the owner it found. A cohort running from retained immutable
|
|
136
|
-
* content keeps its work: the installation replaces files it does not read, and it listens on
|
|
137
|
-
* its own endpoint. A cohort running from the mutable installation is the one exception,
|
|
138
|
-
* because that installation is exactly what is about to be replaced under it.
|
|
139
|
-
*/
|
|
140
145
|
export declare function planUpdateOwnership(ownership: "live-verified" | "dead", runsFromRetainedRelease: boolean): UpdateOwnershipAction;
|
|
141
146
|
export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput, patience?: UpdateUnlockPatience): UpdateLifecycleCoordinator;
|
|
142
147
|
/** Renders the terminal-only update meter; the visual preview shares this exact frame. */
|
|
143
|
-
export declare function renderUpdateProgressBar(percent: number): string;
|
|
148
|
+
export declare function renderUpdateProgressBar(percent: number, accent?: string): string;
|
|
144
149
|
export declare function runSelfUpdate(options: SelfUpdateOptions): Promise<number>;
|
|
145
150
|
export declare function assertUpdatePerformanceBudget(evidence: UpdatePerformanceEvidence, maximumPostNpmDurationMs?: number): void;
|