@timurproko/a1 0.2.7-dev.1252 → 0.2.7-dev.1264
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/bin/ui.js +3 -3
- package/dist/app/session-shell/clipboard-diagnostics.d.ts +2 -2
- package/dist/app/session-shell/clipboard-image.d.ts +0 -1
- package/dist/app/session-shell/clipboard-image.js +1 -1
- package/dist/app/session-shell/dialog-coordinator.d.ts +6 -5
- package/dist/app/session-shell/dialog-coordinator.js +3 -4
- package/dist/app/session-shell/dock-chrome.d.ts +11 -6
- package/dist/app/session-shell/dock-chrome.js +5 -8
- package/dist/app/session-shell/editor-hyperlink-budget.d.ts +0 -2
- package/dist/app/session-shell/editor-hyperlink-budget.js +1 -1
- package/dist/app/session-shell/exit-notice.d.ts +2 -2
- package/dist/app/session-shell/exit-notice.js +1 -1
- package/dist/app/session-shell/extension-surface.d.ts +8 -7
- package/dist/app/session-shell/helper-pool.d.ts +3 -4
- package/dist/app/session-shell/helper-pool.js +1 -1
- package/dist/app/session-shell/image-preparation-client.d.ts +4 -3
- package/dist/app/session-shell/image-preparation.d.ts +0 -2
- package/dist/app/session-shell/image-preparation.js +2 -2
- package/dist/app/session-shell/image-source.d.ts +2 -3
- package/dist/app/session-shell/image-source.js +2 -2
- package/dist/app/session-shell/input-surface-switch.d.ts +9 -7
- package/dist/app/session-shell/input-surface-switch.js +1 -2
- package/dist/app/session-shell/login-interaction.d.ts +5 -4
- package/dist/app/session-shell/login-interaction.js +2 -3
- package/dist/app/session-shell/paste-executor.d.ts +4 -8
- package/dist/app/session-shell/paste-executor.js +2 -2
- package/dist/app/session-shell/paste-protocol.d.ts +2 -2
- package/dist/app/session-shell/path-chip-presentation.d.ts +0 -5
- package/dist/app/session-shell/path-chip-presentation.js +3 -3
- package/dist/app/session-shell/prompt-chips.d.ts +8 -7
- package/dist/app/session-shell/prompt-history-controller.d.ts +3 -3
- package/dist/app/session-shell/prompt-submission.d.ts +5 -2
- package/dist/app/session-shell/prompt-submission.js +13 -14
- package/dist/app/session-shell/quit-outro-effects.d.ts +3 -3
- package/dist/app/session-shell/quit-outro-effects.js +1 -4
- package/dist/app/session-shell/quit-outro.d.ts +9 -12
- package/dist/app/session-shell/quit-outro.js +11 -11
- package/dist/app/session-shell/resource-labels.d.ts +2 -0
- package/dist/app/session-shell/resource-labels.js +158 -0
- package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
- package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
- package/dist/app/session-shell/response-copy-transport.js +1 -1
- package/dist/app/session-shell/session-lifecycle.d.ts +2 -1
- package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
- package/dist/app/session-shell/session-prompt-history.js +80 -0
- package/dist/app/session-shell/session-selectors.d.ts +46 -0
- package/dist/app/session-shell/session-selectors.js +269 -0
- package/dist/app/session-shell/session-shell-root.d.ts +76 -113
- package/dist/app/session-shell/session-shell-root.js +103 -329
- package/dist/app/session-shell/session-shell.d.ts +14 -37
- package/dist/app/session-shell/session-shell.js +177 -612
- package/dist/app/session-shell/session-viewport-controller.d.ts +9 -16
- package/dist/app/session-shell/session-viewport-controller.js +42 -320
- package/dist/app/session-shell/settings-bindings.d.ts +25 -3
- package/dist/app/session-shell/settings-bindings.js +26 -2
- package/dist/app/session-shell/share-operation.d.ts +3 -3
- package/dist/app/session-shell/share-operation.js +4 -6
- package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
- package/dist/app/session-shell/shell-clipboard.js +119 -0
- package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
- package/dist/app/session-shell/shell-editor-factory.js +13 -15
- package/dist/app/session-shell/shell-host.d.ts +16 -17
- package/dist/app/session-shell/shell-host.js +18 -19
- package/dist/app/session-shell/shell-layout.d.ts +2 -2
- package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
- package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
- package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
- package/dist/app/session-shell/suggestion-surface.js +1 -1
- package/dist/app/session-shell/system-clipboard.d.ts +2 -14
- package/dist/app/session-shell/system-clipboard.js +2 -7
- package/dist/app/session-shell/transcript-document.d.ts +11 -8
- package/dist/app/session-shell/transcript-document.js +13 -14
- package/dist/app/session-shell/viewport-layout.d.ts +2 -2
- package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
- package/dist/app/session-shell/viewport-pointer.js +361 -0
- package/dist/app/session-shell/workflow-output.d.ts +5 -3
- package/dist/app/session-shell/workflow-output.js +13 -15
- package/dist/cli/package-messages.d.ts +0 -1
- package/dist/cli/package-messages.js +1 -1
- package/dist/cli/session-context.d.ts +1 -1
- package/dist/cli/uninstall.d.ts +3 -2
- package/dist/composition/owned-settings.d.ts +2 -1
- package/dist/composition/owned-settings.js +0 -3
- package/dist/composition/owned-ui.d.ts +1 -1
- package/dist/composition/owned-ui.js +77 -73
- package/dist/composition/reference-selection-options.d.ts +1 -1
- package/dist/composition/reference-selection-options.js +1 -1
- package/dist/composition/session-runtime-registry.d.ts +2 -1
- package/dist/composition/settings-route-host.js +3 -10
- package/dist/composition/settings-search-input.d.ts +2 -1
- package/dist/composition/startup-route.d.ts +2 -2
- package/dist/composition/startup-route.js +2 -2
- package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
- package/dist/contracts/owned-ui/index.d.ts +2 -1
- package/dist/contracts/owned-ui/index.js +1 -1
- package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
- package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
- package/dist/contracts/owned-ui/validation.d.ts +1 -11
- package/dist/contracts/owned-ui/validation.js +8 -104
- package/dist/contracts/presentation/component.d.ts +17 -0
- package/dist/contracts/presentation/component.js +8 -0
- package/dist/contracts/presentation/index.d.ts +6 -15
- package/dist/contracts/presentation/index.js +2 -0
- package/dist/contracts/presentation/terminal.d.ts +284 -0
- package/dist/contracts/presentation/terminal.js +1 -0
- package/dist/contracts/presentation/theme.d.ts +46 -0
- package/dist/contracts/presentation/theme.js +1 -0
- package/dist/features/launch/development-launch.js +1 -1
- package/dist/features/launch/runtime-selection.d.ts +2 -2
- package/dist/features/launch/runtime-selection.js +2 -2
- package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
- package/dist/features/owned-ui/project-trust-prompt.js +1 -1
- package/dist/features/owned-ui/release-note-state.d.ts +2 -1
- package/dist/features/owned-ui/settings-app.js +49 -425
- package/dist/features/owned-ui/settings-edits.d.ts +31 -0
- package/dist/features/owned-ui/settings-edits.js +139 -0
- package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
- package/dist/features/owned-ui/settings-model-dialog.js +194 -0
- package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
- package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
- package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
- package/dist/features/prompt-history/service.d.ts +3 -2
- package/dist/foundation/lifecycle/claim-gate.d.ts +2 -1
- package/dist/foundation/release/automatic-update.d.ts +5 -14
- package/dist/foundation/release/automatic-update.js +15 -22
- package/dist/foundation/release/bootstrap.js +4 -4
- package/dist/foundation/release/dependency-certification.d.ts +0 -1
- package/dist/foundation/release/dependency-certification.js +1 -1
- package/dist/foundation/release/dependency-layer.js +3 -3
- package/dist/foundation/release/early-accent.d.ts +2 -2
- package/dist/foundation/release/early-accent.js +1 -1
- package/dist/foundation/release/index.d.ts +2 -2
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -18
- package/dist/foundation/release/latest-release.js +11 -11
- package/dist/foundation/release/managed-path.d.ts +10 -0
- package/dist/foundation/release/managed-path.js +26 -0
- package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
- package/dist/foundation/release/ordinary-launch-selection.js +1 -1
- package/dist/foundation/release/release-gc.d.ts +3 -3
- package/dist/foundation/release/release-gc.js +10 -12
- package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +23 -23
- package/dist/foundation/release/{cohort-state.js → release-state.js} +8 -8
- package/dist/foundation/release/restart-certification.js +3 -4
- package/dist/foundation/release/update-activation.js +3 -3
- package/dist/foundation/release/update-execution-lease.d.ts +2 -3
- package/dist/foundation/release/update-execution-lease.js +2 -8
- package/dist/foundation/release/update-installer.d.ts +45 -0
- package/dist/foundation/release/update-installer.js +57 -0
- package/dist/foundation/release/update-launch.d.ts +2 -2
- package/dist/foundation/release/update-recovery.js +2 -9
- package/dist/foundation/release/update.d.ts +4 -25
- package/dist/foundation/release/update.js +164 -183
- package/dist/foundation/startup/startup-budget.d.ts +5 -5
- package/dist/foundation/startup/startup-budget.js +2 -2
- package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
- package/dist/integrations/pi/components/bracketed-paste.js +123 -0
- package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
- package/dist/integrations/pi/components/index.d.ts +15 -9
- package/dist/integrations/pi/components/index.js +9 -5
- package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
- package/dist/integrations/pi/components/lazy-selectors.js +1 -1
- package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
- package/dist/integrations/pi/components/modal-frame.js +3 -23
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
- package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
- package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
- package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
- package/dist/integrations/pi/components/shell-footer-status.d.ts +2 -2
- package/dist/integrations/pi/components/shell-footer-status.js +3 -2
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -5
- package/dist/integrations/pi/components/shell-presenters-info.js +2 -1
- package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -227
- package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
- package/dist/integrations/pi/components/skills-command.d.ts +1 -9
- package/dist/integrations/pi/components/skills-command.js +5 -5
- package/dist/integrations/pi/components/theme.d.ts +9 -2
- package/dist/integrations/pi/components/theme.js +17 -2
- package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
- package/dist/integrations/pi/engine/adapter.d.ts +4 -13
- package/dist/integrations/pi/engine/adapter.js +9 -61
- package/dist/integrations/pi/engine/changelog.d.ts +2 -2
- package/dist/integrations/pi/engine/changelog.js +3 -3
- package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
- package/dist/integrations/pi/engine/compaction-progress.js +1 -1
- package/dist/integrations/pi/engine/conformance.d.ts +2 -1
- package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
- package/dist/integrations/pi/engine/default-model-setting.js +1 -2
- package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
- package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
- package/dist/integrations/pi/engine/host.js +3 -3
- package/dist/integrations/pi/engine/message-values.d.ts +0 -1
- package/dist/integrations/pi/engine/message-values.js +1 -1
- package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
- package/dist/integrations/pi/engine/pending-delivery.js +3 -3
- package/dist/integrations/pi/engine/ports.d.ts +7 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
- package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -2
- package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
- package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
- package/dist/integrations/pi/engine/public-main-entry.js +1 -1
- package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
- package/dist/integrations/pi/engine/repository-metadata.js +189 -0
- package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
- package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
- package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
- package/dist/integrations/pi/engine/repository-pr.js +1 -1
- package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
- package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
- package/dist/integrations/pi/engine/session-runtime.d.ts +7 -12
- package/dist/integrations/pi/engine/session-runtime.js +21 -178
- package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
- package/dist/integrations/pi/engine/settings-bridge.js +1 -1
- package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
- package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
- package/dist/integrations/pi/engine/view-state.d.ts +21 -2
- package/dist/integrations/pi/engine/view-state.js +129 -1
- package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
- package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -2
- package/dist/integrations/pi/engine/workflow-runner.js +254 -365
- package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
- package/dist/integrations/pi/engine/workflow-share.js +168 -0
- package/dist/integrations/pi/engine/workflow-support.d.ts +0 -20
- package/dist/integrations/pi/engine/workflow-support.js +4 -65
- package/dist/integrations/pi/engine/workflows.d.ts +5 -5
- package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
- package/dist/integrations/pi/session-presenters/components.js +75 -0
- package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
- package/dist/integrations/pi/session-presenters/index.js +1 -0
- package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
- package/dist/integrations/pi/session-presenters/presentation.js +10 -0
- package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
- package/dist/integrations/pi/session-presenters/presenters.js +3 -1
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
- package/dist/integrations/pi/tui-runtime/adapter.js +56 -174
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +9 -44
- package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/index.js +1 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +4 -10
- package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
- package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
- package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
- package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
- package/dist/ui/components/index.d.ts +2 -2
- package/dist/ui/components/index.js +1 -1
- package/dist/ui/components/selection-copy.d.ts +0 -1
- package/dist/ui/components/selection-copy.js +0 -12
- package/dist/ui/components/theme.d.ts +4 -19
- package/dist/ui/components/theme.js +6 -0
- package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
- package/dist/ui/components/transcript-viewport-frame.js +314 -0
- package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
- package/dist/ui/components/transcript-viewport-types.js +1 -0
- package/dist/ui/components/transcript-viewport.d.ts +2 -147
- package/dist/ui/components/transcript-viewport.js +87 -315
- package/dist/ui/settings/declarations.d.ts +2 -3
- package/dist/ui/settings/declarations.js +2 -3
- package/dist/ui/settings/early-accent.js +1 -1
- package/dist/ui/settings/index.d.ts +3 -5
- package/dist/ui/settings/index.js +2 -3
- package/dist/ui/settings/manager.d.ts +0 -4
- package/dist/ui/settings/manager.js +6 -62
- package/dist/ui/settings/resolution.d.ts +6 -18
- package/dist/ui/settings/resolution.js +4 -103
- package/dist/ui/settings/sections.d.ts +0 -4
- package/dist/ui/settings/sections.js +1 -1
- package/docs/architecture/boundaries.md +2 -2
- package/docs/architecture/internal-naming.md +2 -0
- package/docs/architecture/project-structure.md +4 -2
- package/docs/validation.md +1 -1
- package/package.json +3 -4
- package/dist/integrations/pi/components/components.d.ts +0 -4
- package/dist/integrations/pi/components/components.js +0 -48
- package/dist/integrations/pi/components/conformance.d.ts +0 -18
- package/dist/integrations/pi/components/conformance.js +0 -70
- package/dist/integrations/pi/components/shell-components.d.ts +0 -7
- package/dist/integrations/pi/components/shell-components.js +0 -7
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
- package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
- package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
- package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
- package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
- package/dist/integrations/pi/engine/usage-view.js +0 -56
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -166
- package/dist/ui/settings/migrations.d.ts +0 -19
- package/dist/ui/settings/migrations.js +0 -225
- package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
- package/dist/ui/settings/progress-palette.generated.js +0 -139
- /package/dist/{integrations/pi/tui-runtime/contracts.js → contracts/owned-ui/shell-components.js} +0 -0
- /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
- /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
|
@@ -6,21 +6,22 @@ import { basename, dirname, resolve } from "node:path";
|
|
|
6
6
|
import { valid as validSemver } from "semver";
|
|
7
7
|
import { readLaunchContext } from "../launch-context/index.js";
|
|
8
8
|
import { resolveProductPaths } from "../lifecycle/index.js";
|
|
9
|
-
import {
|
|
9
|
+
import { ReleaseStateStore } from "./release-state.js";
|
|
10
10
|
import { processIsAlive } from "./process-liveness.js";
|
|
11
11
|
import { isNewerRelease, releaseChannelOf } from "./latest-release.js";
|
|
12
12
|
import { readCertifiedReleaseManifest, resolveReleaseEntryPoint } from "./release-store.js";
|
|
13
13
|
import { acquireUpdateExecutionLease, readUpdateExecutionOwner } from "./update-execution-lease.js";
|
|
14
14
|
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
15
15
|
import { runSelfUpdate, UPDATE_ALREADY_RUNNING_EXIT_CODE } from "./update.js";
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import { errorMessage, samePath } from "./managed-path.js";
|
|
17
|
+
const AUTOMATIC_UPDATE_STATE_SCHEMA = 1;
|
|
18
|
+
const AUTOMATIC_UPDATE_RETRY_MS = 24 * 60 * 60_000;
|
|
18
19
|
const AUTOMATIC_UPDATE_STATE_FILENAME = "automatic-update.json";
|
|
19
20
|
const MAX_STATE_BYTES = 16_384;
|
|
20
21
|
const OBSERVER_POLL_MS = 250;
|
|
21
22
|
const WORKER_START_GRACE_MS = 5_000;
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const ACTIVE_RELEASE_FALLBACK_MS = 5_000;
|
|
24
|
+
class AutomaticUpdateStateStore {
|
|
24
25
|
path;
|
|
25
26
|
constructor(dataDir) { this.path = resolve(dataDir, AUTOMATIC_UPDATE_STATE_FILENAME); }
|
|
26
27
|
async read() {
|
|
@@ -65,7 +66,7 @@ export class AutomaticUpdateStateStore {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
/** Read the approved active release only when this process executes an older release on that channel. */
|
|
68
|
-
|
|
69
|
+
async function readNewerActiveRelease(runningVersion, environment = process.env) {
|
|
69
70
|
const channel = releaseChannelOf(runningVersion);
|
|
70
71
|
if (channel === null)
|
|
71
72
|
return null;
|
|
@@ -83,7 +84,7 @@ export function observeNewerActiveRelease(options) {
|
|
|
83
84
|
if (releaseChannelOf(options.runningVersion) === null || options.signal?.aborted)
|
|
84
85
|
return () => { };
|
|
85
86
|
const paths = resolveProductPaths(options.environment ?? process.env);
|
|
86
|
-
const store = new
|
|
87
|
+
const store = new ReleaseStateStore(paths.dataDir);
|
|
87
88
|
let closed = false;
|
|
88
89
|
let checking = false;
|
|
89
90
|
let requested = false;
|
|
@@ -193,8 +194,8 @@ export async function requestAutomaticUpdate(options) {
|
|
|
193
194
|
let worker = options.worker;
|
|
194
195
|
if (worker === undefined) {
|
|
195
196
|
const launch = readLaunchContext(environment, "release");
|
|
196
|
-
const
|
|
197
|
-
const record = launch.releaseId === undefined ? undefined :
|
|
197
|
+
const releaseState = await new ReleaseStateStore(paths.dataDir).read();
|
|
198
|
+
const record = launch.releaseId === undefined ? undefined : releaseState.releases[launch.releaseId];
|
|
198
199
|
if (!record || record.approval !== "approved" || record.releaseRoot !== launch.releaseRoot
|
|
199
200
|
|| record.contentDigest !== launch.releaseDigest)
|
|
200
201
|
return null;
|
|
@@ -248,8 +249,8 @@ export async function runAutomaticUpdateWorker(argv, environment = process.env)
|
|
|
248
249
|
let verified = false;
|
|
249
250
|
try {
|
|
250
251
|
const launch = readLaunchContext(environment, "release");
|
|
251
|
-
const
|
|
252
|
-
const record = launch.releaseId === undefined ? undefined :
|
|
252
|
+
const releaseState = await new ReleaseStateStore(paths.dataDir).read();
|
|
253
|
+
const record = launch.releaseId === undefined ? undefined : releaseState.releases[launch.releaseId];
|
|
253
254
|
if (record?.approval === "approved" && samePath(record.releaseRoot, launch.releaseRoot ?? "")
|
|
254
255
|
&& record.contentDigest === launch.releaseDigest) {
|
|
255
256
|
const selected = await readCertifiedReleaseManifest(record, resolve(paths.dataDir, "releases"));
|
|
@@ -360,11 +361,11 @@ function activeNewerPackageVersion(active, runningVersion, channel, minimum) {
|
|
|
360
361
|
? active.packageVersion : null;
|
|
361
362
|
}
|
|
362
363
|
async function activeRelease(dataDir) {
|
|
363
|
-
const
|
|
364
|
-
const activeId =
|
|
364
|
+
const releaseState = await new ReleaseStateStore(dataDir).read();
|
|
365
|
+
const activeId = releaseState.references.active;
|
|
365
366
|
if (activeId === null)
|
|
366
367
|
return null;
|
|
367
|
-
const active =
|
|
368
|
+
const active = releaseState.releases[activeId];
|
|
368
369
|
return active?.approval === "approved" ? active : null;
|
|
369
370
|
}
|
|
370
371
|
function state(parsed, status, attemptedAt, installedVersion, failure) {
|
|
@@ -406,14 +407,6 @@ function validateState(value) {
|
|
|
406
407
|
throw new Error("invalid automatic update state");
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
|
-
function samePath(left, right) {
|
|
410
|
-
const canonicalLeft = resolve(left);
|
|
411
|
-
const canonicalRight = resolve(right);
|
|
412
|
-
return process.platform === "win32"
|
|
413
|
-
? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase()
|
|
414
|
-
: canonicalLeft === canonicalRight;
|
|
415
|
-
}
|
|
416
410
|
function hasCode(error, code) {
|
|
417
411
|
return error instanceof Error && "code" in error && error.code === code;
|
|
418
412
|
}
|
|
419
|
-
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
@@ -4,7 +4,7 @@ import { deliverExitNotice, restoreAfterOwnedExit } from "../terminal-cleanup/in
|
|
|
4
4
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { selectOrdinaryLaunchReleaseId } from "./ordinary-launch-selection.js";
|
|
7
|
-
import {
|
|
7
|
+
import { ReleaseStateStore } from "./release-state.js";
|
|
8
8
|
import { assertLaunchProfileId, resolveProductPaths, sessionSelectionArguments } from "../lifecycle/index.js";
|
|
9
9
|
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
10
10
|
import { selectUpdateLaunchRelease } from "./update-launch.js";
|
|
@@ -39,7 +39,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
|
|
|
39
39
|
output.write(`${PRODUCT_TEXT.diagnostic("active release changed repeatedly during startup; launch could not converge")}\n`);
|
|
40
40
|
return 1;
|
|
41
41
|
};
|
|
42
|
-
const stateStore = new
|
|
42
|
+
const stateStore = new ReleaseStateStore(paths.dataDir);
|
|
43
43
|
let state = await stateStore.read();
|
|
44
44
|
const launchDuringUpdate = async () => {
|
|
45
45
|
// Concurrency: read the journal before touching npm's mutable tree, including package.json.
|
|
@@ -55,7 +55,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
|
|
|
55
55
|
const duringUpdate = await launchDuringUpdate();
|
|
56
56
|
if (duringUpdate !== null)
|
|
57
57
|
return duringUpdate;
|
|
58
|
-
// Performance: reconciliation is serialized by
|
|
58
|
+
// Performance: reconciliation is serialized by the release state lock, but launch never awaits a physical
|
|
59
59
|
// backlog. The scheduler commits a plan and starts a detached bounded worker.
|
|
60
60
|
const scheduleMaintenance = options.scheduleMaintenance ?? scheduleReleaseCleanup;
|
|
61
61
|
void scheduleMaintenance(paths.dataDir, paths).catch(() => { });
|
|
@@ -149,7 +149,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
|
|
|
149
149
|
return await launchSelected(candidate);
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
|
-
* Start the retained active
|
|
152
|
+
* Start the retained active release when the installed payload cannot be taken. The
|
|
153
153
|
* release already carries certified evidence, so this reuses it rather than
|
|
154
154
|
* certifying anything new, and reports no selection when there is nothing safe
|
|
155
155
|
* to fall back to.
|
|
@@ -3,7 +3,7 @@ import { chmod, lstat, mkdir, readFile, readdir, realpath, rename, rm, writeFile
|
|
|
3
3
|
import { basename, dirname, resolve } from "node:path";
|
|
4
4
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
5
5
|
import { immutablePlatformPolicy } from "./immutable-platform.js";
|
|
6
|
-
|
|
6
|
+
const DEPENDENCY_CERTIFICATIONS_DIRECTORY = "dependency-certifications";
|
|
7
7
|
/** Derive the canonical record path without accepting path components as layer identities. */
|
|
8
8
|
export function dependencyLayerCertificationPath(dataDir, layerId) {
|
|
9
9
|
assertLayerId(layerId);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname, posix,
|
|
3
|
+
import { dirname, posix, resolve, sep } from "node:path";
|
|
4
4
|
import { mapWithConcurrency } from "./concurrency.js";
|
|
5
5
|
import { assertImmutableFileMode } from "./immutable-platform.js";
|
|
6
6
|
import { readDependencyCertification, writeDependencyCertification as writeLayerCertification } from "./dependency-certification.js";
|
|
7
7
|
export { dependencyLayerCertificationPath, legacyDependencyLayerCertificationPath } from "./dependency-certification.js";
|
|
8
8
|
import { digestManifestFiles, releaseFileIdentity, resolveWithin } from "./release.js";
|
|
9
9
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
10
|
+
import { isDirectChild } from "./managed-path.js";
|
|
10
11
|
const LAYER_IO_CONCURRENCY = 32;
|
|
11
12
|
export const DEPENDENCY_LAYER_SCHEMA = PRODUCT_IDENTITY.evidence.dependencyLayerSchema;
|
|
12
13
|
export const DEPENDENCY_LAYER_MANIFEST = "dependency-layer-manifest.json";
|
|
@@ -460,7 +461,6 @@ function isKnownRuntimePath(path) {
|
|
|
460
461
|
}
|
|
461
462
|
function isJavaScriptModule(path) { return /\.(?:js|mjs|cjs)$/.test(path); }
|
|
462
463
|
function assertDirectChild(parent, child) {
|
|
463
|
-
|
|
464
|
-
if (path.length === 0 || path === ".." || path.startsWith(`..${sep}`) || path.includes(sep))
|
|
464
|
+
if (!isDirectChild(parent, child))
|
|
465
465
|
throw new Error(`dependency layer escapes managed storage: ${child}`);
|
|
466
466
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type UiAccentColor = "blue" | "purple" | "cyan" | "green" | "orange" | "pink";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
type EarlyAccentAppearance = "dark" | "light";
|
|
3
|
+
type EarlyAccentMode = "truecolor" | "256color";
|
|
4
4
|
/** Read only global profile data, never project settings or the interactive resource pipeline. */
|
|
5
5
|
export declare function resolveEarlyAccentColor(environment?: NodeJS.ProcessEnv, home?: () => string): UiAccentColor;
|
|
6
6
|
export declare function earlyAccentPalette(color: UiAccentColor, appearance?: EarlyAccentAppearance, mode?: EarlyAccentMode): {
|
|
@@ -2,7 +2,7 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { isAbsolute, join } from "node:path";
|
|
4
4
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
5
|
-
import { SHARED_PROGRESS_PALETTE } from "
|
|
5
|
+
import { SHARED_PROGRESS_PALETTE } from "../../contracts/presentation/index.js";
|
|
6
6
|
const MAX_SETTINGS_BYTES = 256 * 1024;
|
|
7
7
|
/** Read only global profile data, never project settings or the interactive resource pipeline. */
|
|
8
8
|
export function resolveEarlyAccentColor(environment = process.env, home = homedir) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { certifyMaterializedRelease, recordParentCertifiedRelease, releaseEnvironment, runBootstrap, } from "./bootstrap.js";
|
|
2
2
|
export type { BootstrapOptions } from "./bootstrap.js";
|
|
3
3
|
export { selectOrdinaryLaunchReleaseId } from "./ordinary-launch-selection.js";
|
|
4
|
-
export {
|
|
5
|
-
export type { ActiveReleaseTransactionReference,
|
|
4
|
+
export { ReleaseStateStore, RELEASE_STATE_SCHEMA, RELEASE_RETENTION_MS, emptyState, planProtectedReleases, withinRetention } from "./release-state.js";
|
|
5
|
+
export type { ActiveReleaseTransactionReference, ReleaseState, OrphanReleaseDisposition, ProtectedReleaseInputs, ProtectedReleasePlan, ReleaseApproval, ReleaseCleanupDiagnostic, ReleaseCleanupDisposition, ReleaseCleanupStage, ReleaseCleanupState, ReleaseCleanupWorkerRun, ReleaseCleanupWorkerStatus, ReleaseCleanupWorkerSummary, ReleaseRecord, ReleaseReferences, ReleaseRetentionReconciliation, } from "./release-state.js";
|
|
6
6
|
export { DEPENDENCY_LAYER_MANIFEST, DEPENDENCY_LAYER_SCHEMA, RUNTIME_PAYLOAD_INVENTORY, dependencyLayerCertificationPath, dependencyReference, generateDependencyRuntimePayload, legacyDependencyLayerCertificationPath, materializeDependencyLayer, readCertifiedDependencyLayer, selectDependencyRuntimePayload, selectPublishedDependencyRuntimePayload, verifyDependencyLayer, } from "./dependency-layer.js";
|
|
7
7
|
export type { DependencyLayerIdentity, DependencyLayerOperationEvent, DependencyLayerReference, GeneratedRuntimePayload, MaterializeDependencyLayerOptions, MaterializedDependencyLayer, PublishedRuntimeFileClassification, PublishedRuntimePayload, ReadCertifiedDependencyLayerOptions, RuntimePayloadExclusion, RuntimePayloadInventory, SelectedRuntimePayload, } from "./dependency-layer.js";
|
|
8
8
|
export { processIsAlive } from "./process-liveness.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { certifyMaterializedRelease, recordParentCertifiedRelease, releaseEnvironment, runBootstrap, } from "./bootstrap.js";
|
|
2
2
|
export { selectOrdinaryLaunchReleaseId } from "./ordinary-launch-selection.js";
|
|
3
|
-
export {
|
|
3
|
+
export { ReleaseStateStore, RELEASE_STATE_SCHEMA, RELEASE_RETENTION_MS, emptyState, planProtectedReleases, withinRetention } from "./release-state.js";
|
|
4
4
|
export { DEPENDENCY_LAYER_MANIFEST, DEPENDENCY_LAYER_SCHEMA, RUNTIME_PAYLOAD_INVENTORY, dependencyLayerCertificationPath, dependencyReference, generateDependencyRuntimePayload, legacyDependencyLayerCertificationPath, materializeDependencyLayer, readCertifiedDependencyLayer, selectDependencyRuntimePayload, selectPublishedDependencyRuntimePayload, verifyDependencyLayer, } from "./dependency-layer.js";
|
|
5
5
|
export { processIsAlive } from "./process-liveness.js";
|
|
6
6
|
export { PRODUCT_PACKAGE_NAME, createReleaseIdentity, deriveReleaseIdentity, digestManifestFiles, discoverReleasePayload, packageRootFromModule, releaseFileIdentity, resolveWithin, } from "./release.js";
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/** Published release channels: stable `X.Y.Z` on dist-tag `latest`, development `X.Y.Z-dev.N` on `next`. */
|
|
2
2
|
export type ReleaseChannel = "stable" | "development";
|
|
3
|
-
export type StartupUpdateMode = "auto" | "
|
|
4
|
-
export interface DistTagVersions {
|
|
5
|
-
readonly release: string | null;
|
|
6
|
-
readonly develop: string | null;
|
|
7
|
-
readonly error: string | null;
|
|
8
|
-
}
|
|
3
|
+
export type StartupUpdateMode = "auto" | "manual";
|
|
9
4
|
export type RegistryFetcher = (url: string) => Promise<{
|
|
10
5
|
readonly ok: boolean;
|
|
11
6
|
readonly status: number;
|
|
@@ -19,14 +14,13 @@ export interface AvailableRelease {
|
|
|
19
14
|
/** The GitHub Release page; development releases have none. */
|
|
20
15
|
readonly changelogUrl: string | null;
|
|
21
16
|
}
|
|
22
|
-
export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
23
17
|
export declare const UPDATE_CHECK_INTERVAL_MS: number;
|
|
24
18
|
/** One published version with the pull request its manifest records, when it records one. */
|
|
25
19
|
export interface PublishedVersion {
|
|
26
20
|
readonly version: string;
|
|
27
21
|
readonly pullRequest: number | null;
|
|
28
22
|
}
|
|
29
|
-
|
|
23
|
+
interface PublishedVersions {
|
|
30
24
|
readonly versions: readonly PublishedVersion[];
|
|
31
25
|
readonly error: string | null;
|
|
32
26
|
}
|
|
@@ -37,23 +31,14 @@ export interface PublishedVersions {
|
|
|
37
31
|
export declare function releaseChannelOf(version: string): ReleaseChannel | null;
|
|
38
32
|
/** True only when both versions are valid semver and the candidate sorts strictly above the current one. */
|
|
39
33
|
export declare function isNewerRelease(candidate: string, current: string): boolean;
|
|
40
|
-
export declare function distTagsUrl(registry?: string): string;
|
|
41
|
-
/** Read `latest` (required) and `next` (optional) from a dist-tags object. */
|
|
42
|
-
export declare function parseDistTags(metadata: unknown, source: string): DistTagVersions;
|
|
43
|
-
export declare function queryRegistryDistTags(fetcher: RegistryFetcher, registry?: string): Promise<DistTagVersions>;
|
|
44
|
-
export declare function packageDocumentUrl(registry?: string): string;
|
|
45
|
-
/** The pull request a published or installed manifest records under `publication.pullRequest`, when valid. */
|
|
46
|
-
export declare function recordedPullRequest(manifest: unknown): number | null;
|
|
47
34
|
/**
|
|
48
35
|
* Every published version with its recorded pull request, read from the registry's full package
|
|
49
36
|
* document: its per-version manifests keep the publication metadata that version lists and the
|
|
50
37
|
* abbreviated install document leave out.
|
|
51
38
|
*/
|
|
52
39
|
export declare function queryPublishedVersions(fetcher: RegistryFetcher, registry?: string): Promise<PublishedVersions>;
|
|
53
|
-
export declare function parsePublishedVersions(document: unknown): PublishedVersions;
|
|
54
40
|
/** Forget the startup release check so the next interactive launch asks the registry again. */
|
|
55
41
|
export declare function invalidateReleaseCheckCache(configDir: string): Promise<void>;
|
|
56
|
-
export declare function unavailableDistTags(error: string): DistTagVersions;
|
|
57
42
|
export declare function createRegistryFetcher(timeoutMs: number, signal?: AbortSignal): RegistryFetcher;
|
|
58
43
|
export interface StartupReleaseCheckOptions {
|
|
59
44
|
readonly runningVersion: string;
|
|
@@ -77,4 +62,4 @@ export declare function startupReleaseCheckSkipReason(options: StartupReleaseChe
|
|
|
77
62
|
* answer replaces the cache. Every failure resolves to null: the notice is best-effort.
|
|
78
63
|
*/
|
|
79
64
|
export declare function checkForNewerRelease(options: StartupReleaseCheckOptions): Promise<AvailableRelease | null>;
|
|
80
|
-
export
|
|
65
|
+
export {};
|
|
@@ -4,7 +4,8 @@ import { join } from "node:path";
|
|
|
4
4
|
import { gt, prerelease, valid as validSemver } from "semver";
|
|
5
5
|
import lockfile from "proper-lockfile";
|
|
6
6
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
|
|
7
|
-
|
|
7
|
+
import { errorMessage } from "./managed-path.js";
|
|
8
|
+
const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
8
9
|
export const UPDATE_CHECK_INTERVAL_MS = 15 * 60_000;
|
|
9
10
|
const STARTUP_FETCH_TIMEOUT_MS = 3_000;
|
|
10
11
|
const CACHE_FILENAME = "update-check.json";
|
|
@@ -30,11 +31,11 @@ export function isNewerRelease(candidate, current) {
|
|
|
30
31
|
const right = validSemver(current);
|
|
31
32
|
return left !== null && right !== null && gt(left, right);
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
function distTagsUrl(registry = DEFAULT_NPM_REGISTRY) {
|
|
34
35
|
return `${registry.replace(/\/+$/u, "")}/-/package/${encodeURIComponent(PRODUCT_TEXT.packageName)}/dist-tags`;
|
|
35
36
|
}
|
|
36
37
|
/** Read `latest` (required) and `next` (optional) from a dist-tags object. */
|
|
37
|
-
|
|
38
|
+
function parseDistTags(metadata, source) {
|
|
38
39
|
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
|
|
39
40
|
return unavailableDistTags(`${source} returned a non-object dist-tags value`);
|
|
40
41
|
}
|
|
@@ -48,7 +49,7 @@ export function parseDistTags(metadata, source) {
|
|
|
48
49
|
return unavailableDistTags(errorMessage(error));
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
async function queryRegistryDistTags(fetcher, registry) {
|
|
52
53
|
try {
|
|
53
54
|
const response = await fetcher(distTagsUrl(registry));
|
|
54
55
|
if (!response.ok)
|
|
@@ -59,11 +60,11 @@ export async function queryRegistryDistTags(fetcher, registry) {
|
|
|
59
60
|
return unavailableDistTags(errorMessage(error));
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
+
function packageDocumentUrl(registry = DEFAULT_NPM_REGISTRY) {
|
|
63
64
|
return `${registry.replace(/\/+$/u, "")}/${encodeURIComponent(PRODUCT_TEXT.packageName)}`;
|
|
64
65
|
}
|
|
65
66
|
/** The pull request a published or installed manifest records under `publication.pullRequest`, when valid. */
|
|
66
|
-
|
|
67
|
+
function recordedPullRequest(manifest) {
|
|
67
68
|
if (typeof manifest !== "object" || manifest === null)
|
|
68
69
|
return null;
|
|
69
70
|
const publication = manifest.publication;
|
|
@@ -91,7 +92,7 @@ export async function queryPublishedVersions(fetcher, registry) {
|
|
|
91
92
|
return { versions: [], error: errorMessage(error) };
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
function parsePublishedVersions(document) {
|
|
95
96
|
if (typeof document !== "object" || document === null || Array.isArray(document)) {
|
|
96
97
|
return { versions: [], error: "registry returned a non-object package document" };
|
|
97
98
|
}
|
|
@@ -108,7 +109,7 @@ export function parsePublishedVersions(document) {
|
|
|
108
109
|
export async function invalidateReleaseCheckCache(configDir) {
|
|
109
110
|
await withCacheLock(configDir, async () => { await rm(join(configDir, CACHE_FILENAME), { force: true }); });
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
+
function unavailableDistTags(error) {
|
|
112
113
|
return { release: null, develop: null, error };
|
|
113
114
|
}
|
|
114
115
|
export function createRegistryFetcher(timeoutMs, signal) {
|
|
@@ -126,7 +127,7 @@ export function startupReleaseCheckSkipReason(options) {
|
|
|
126
127
|
return "ci";
|
|
127
128
|
if (!options.interactive)
|
|
128
129
|
return "not-interactive";
|
|
129
|
-
if (options.settingMode === "
|
|
130
|
+
if (options.settingMode === "manual")
|
|
130
131
|
return "disabled-by-setting";
|
|
131
132
|
if (releaseChannelOf(options.runningVersion) === null)
|
|
132
133
|
return "unpublished-version";
|
|
@@ -170,7 +171,7 @@ export async function checkForNewerRelease(options) {
|
|
|
170
171
|
return null;
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
|
-
|
|
174
|
+
function availableRelease(channel, version) {
|
|
174
175
|
return {
|
|
175
176
|
channel,
|
|
176
177
|
version,
|
|
@@ -255,4 +256,3 @@ function parseVersion(value, source) {
|
|
|
255
256
|
throw new Error(`${source} did not provide a valid semantic version`);
|
|
256
257
|
return parsed;
|
|
257
258
|
}
|
|
258
|
-
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Whether two paths name the same location; Windows paths compare without case. */
|
|
2
|
+
export declare function samePath(left: string, right: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Whether `child` is exactly one non-empty segment below `parent`.
|
|
5
|
+
* Invariant: release storage operations act only on such paths, never on the root itself, another drive, a nested descendant,
|
|
6
|
+
* or anything reached through `..`. `path.relative` already ignores case on Windows.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isDirectChild(parent: string, child: string): boolean;
|
|
9
|
+
/** The message of a thrown value, whatever was thrown. */
|
|
10
|
+
export declare function errorMessage(error: unknown): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
+
/** Whether two paths name the same location; Windows paths compare without case. */
|
|
3
|
+
export function samePath(left, right) {
|
|
4
|
+
const canonicalLeft = resolve(left);
|
|
5
|
+
const canonicalRight = resolve(right);
|
|
6
|
+
return process.platform === "win32"
|
|
7
|
+
? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase()
|
|
8
|
+
: canonicalLeft === canonicalRight;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Whether `child` is exactly one non-empty segment below `parent`.
|
|
12
|
+
* Invariant: release storage operations act only on such paths, never on the root itself, another drive, a nested descendant,
|
|
13
|
+
* or anything reached through `..`. `path.relative` already ignores case on Windows.
|
|
14
|
+
*/
|
|
15
|
+
export function isDirectChild(parent, child) {
|
|
16
|
+
const fromParent = relative(resolve(parent), resolve(child));
|
|
17
|
+
return fromParent.length > 0
|
|
18
|
+
&& fromParent !== ".."
|
|
19
|
+
&& !fromParent.startsWith(`..${sep}`)
|
|
20
|
+
&& !isAbsolute(fromParent)
|
|
21
|
+
&& !fromParent.includes(sep);
|
|
22
|
+
}
|
|
23
|
+
/** The message of a thrown value, whatever was thrown. */
|
|
24
|
+
export function errorMessage(error) {
|
|
25
|
+
return error instanceof Error ? error.message : String(error);
|
|
26
|
+
}
|
|
@@ -8,7 +8,7 @@ interface ApprovedOrdinaryLaunchRelease extends OrdinaryLaunchRelease {
|
|
|
8
8
|
/**
|
|
9
9
|
* Ordinary launch has no rollback authority: it may advance to its installed package, but an
|
|
10
10
|
* older package installation must follow a newer approved active release instead of moving the
|
|
11
|
-
* shared selector backward. Explicit update/rollback uses
|
|
11
|
+
* shared selector backward. Explicit update/rollback uses ReleaseStateStore.activate directly.
|
|
12
12
|
*/
|
|
13
13
|
export declare function selectOrdinaryLaunchReleaseId(candidate: OrdinaryLaunchRelease, active?: ApprovedOrdinaryLaunchRelease): string;
|
|
14
14
|
export {};
|
|
@@ -2,7 +2,7 @@ import { compare, valid as validSemver } from "semver";
|
|
|
2
2
|
/**
|
|
3
3
|
* Ordinary launch has no rollback authority: it may advance to its installed package, but an
|
|
4
4
|
* older package installation must follow a newer approved active release instead of moving the
|
|
5
|
-
* shared selector backward. Explicit update/rollback uses
|
|
5
|
+
* shared selector backward. Explicit update/rollback uses ReleaseStateStore.activate directly.
|
|
6
6
|
*/
|
|
7
7
|
export function selectOrdinaryLaunchReleaseId(candidate, active) {
|
|
8
8
|
if (!active || active.approval !== "approved")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { rename, rm } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { ReleaseStateStore, type OrphanReleaseDisposition } from "./release-state.js";
|
|
3
3
|
import type { ProductPaths } from "../lifecycle/index.js";
|
|
4
4
|
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
5
5
|
export interface ReleaseCleanupLimits {
|
|
@@ -58,11 +58,11 @@ interface DiscoveredCleanupInputs {
|
|
|
58
58
|
}[];
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* Reconcile age-based retention under the
|
|
61
|
+
* Reconcile age-based retention under the release-state lock and durably detach obsolete releases.
|
|
62
62
|
* This operation intentionally reads only manifests and directory metadata, never payload bytes.
|
|
63
63
|
*/
|
|
64
64
|
export declare function prepareReleaseCleanup(dataDir: string, _paths?: ProductPaths, options?: ReleaseCleanupOptions): Promise<{
|
|
65
|
-
readonly store:
|
|
65
|
+
readonly store: ReleaseStateStore;
|
|
66
66
|
readonly discovered: DiscoveredCleanupInputs;
|
|
67
67
|
}>;
|
|
68
68
|
/** Run a bounded, restart-safe physical cleanup pass over detached releases and abandoned artifacts. */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat } from "node:fs/promises";
|
|
4
|
-
import { dirname,
|
|
4
|
+
import { dirname, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
6
|
+
import { ReleaseStateStore, } from "./release-state.js";
|
|
7
7
|
import { resolveProductPaths } from "../lifecycle/index.js";
|
|
8
8
|
import { RELEASE_MANIFEST_FILENAME } from "./release-store.js";
|
|
9
9
|
import { DEPENDENCY_LAYER_MANIFEST, readCertifiedDependencyLayer } from "./dependency-layer.js";
|
|
@@ -15,6 +15,7 @@ import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
|
15
15
|
import { collectCompileCaches, startupCompileCachePath } from "../startup/index.js";
|
|
16
16
|
import { readLaunchContext } from "../launch-context/index.js";
|
|
17
17
|
import { processIsAlive } from "./process-liveness.js";
|
|
18
|
+
import { errorMessage, isDirectChild } from "./managed-path.js";
|
|
18
19
|
const DEFAULT_CANDIDATE_AGE_MS = 60 * 60 * 1_000;
|
|
19
20
|
const DEFAULT_LIMITS = { maxItems: 8, maxDurationMs: 2_000, concurrency: 1 };
|
|
20
21
|
const DEFAULT_WORKER_DURATION_MS = 10 * 60 * 1_000;
|
|
@@ -24,11 +25,11 @@ const WORKER_KEEP_ENV = "RELEASE_CLEANUP_KEEP";
|
|
|
24
25
|
const WORKER_RUN_ID_ENV = "RELEASE_CLEANUP_RUN_ID";
|
|
25
26
|
const WORKER_LEASE_FILENAME = "release-cleanup-worker.lock";
|
|
26
27
|
/**
|
|
27
|
-
* Reconcile age-based retention under the
|
|
28
|
+
* Reconcile age-based retention under the release-state lock and durably detach obsolete releases.
|
|
28
29
|
* This operation intentionally reads only manifests and directory metadata, never payload bytes.
|
|
29
30
|
*/
|
|
30
31
|
export async function prepareReleaseCleanup(dataDir, _paths, options = {}) {
|
|
31
|
-
const store = new
|
|
32
|
+
const store = new ReleaseStateStore(dataDir);
|
|
32
33
|
const discovered = await discoverCleanupInputs(dataDir, store, options);
|
|
33
34
|
const transactionStore = options.transactionStore ?? new UpdateTransactionStore(dataDir);
|
|
34
35
|
await store.reconcileRetention(async () => await protectionInputs(transactionStore, options), discovered.orphans);
|
|
@@ -152,7 +153,7 @@ export async function scheduleReleaseCleanup(dataDir, paths, options = {}) {
|
|
|
152
153
|
/** Entry used by the private cleanup executable shipped in every immutable release. */
|
|
153
154
|
export async function runReleaseCleanupWorker(environment = process.env, workerOptions = {}) {
|
|
154
155
|
const paths = resolveProductPaths(environment);
|
|
155
|
-
const store = new
|
|
156
|
+
const store = new ReleaseStateStore(paths.dataDir);
|
|
156
157
|
const runId = environment[WORKER_RUN_ID_ENV] ?? randomUUID();
|
|
157
158
|
const now = workerOptions.now ?? Date.now;
|
|
158
159
|
const sleep = workerOptions.sleep ?? (async (durationMs) => await new Promise(resolvePromise => setTimeout(resolvePromise, durationMs)));
|
|
@@ -638,12 +639,6 @@ async function protectedCompileCachePaths(dataDir, state) {
|
|
|
638
639
|
}
|
|
639
640
|
return paths;
|
|
640
641
|
}
|
|
641
|
-
function assertDirectChild(parent, child) {
|
|
642
|
-
const fromParent = relative(parent, child);
|
|
643
|
-
if (fromParent.length === 0 || fromParent === ".." || fromParent.startsWith(`..${sep}`) || isAbsolute(fromParent) || fromParent.includes(sep)) {
|
|
644
|
-
throw new Error(`cleanup path is not a direct child of its managed root: ${child}`);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
642
|
function normalizeLimits(input) {
|
|
648
643
|
const limits = { ...DEFAULT_LIMITS, ...input };
|
|
649
644
|
for (const [name, value] of Object.entries(limits)) {
|
|
@@ -748,4 +743,7 @@ function artifactName(path) { return path.split(sep).at(-1) ?? "artifact"; }
|
|
|
748
743
|
function certificationReleaseId(path) {
|
|
749
744
|
return /^certification-(.+)\.json$/.exec(artifactName(path))?.[1] ?? null;
|
|
750
745
|
}
|
|
751
|
-
function
|
|
746
|
+
function assertDirectChild(parent, child) {
|
|
747
|
+
if (!isDirectChild(parent, child))
|
|
748
|
+
throw new Error(`cleanup path is not a direct child of its managed root: ${child}`);
|
|
749
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MaterializedRelease } from "./release-store.js";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const RELEASE_STATE_SCHEMA: string;
|
|
3
3
|
/** How long a release stays on disk after its approval, so processes started from it keep their files. */
|
|
4
4
|
export declare const RELEASE_RETENTION_MS: number;
|
|
5
5
|
export type ReleaseApproval = "candidate" | "approved" | "rejected";
|
|
@@ -66,8 +66,8 @@ export interface ReleaseCleanupState {
|
|
|
66
66
|
readonly diagnostics: readonly ReleaseCleanupDiagnostic[];
|
|
67
67
|
readonly workerRuns: readonly ReleaseCleanupWorkerRun[];
|
|
68
68
|
}
|
|
69
|
-
export interface
|
|
70
|
-
readonly schema: typeof
|
|
69
|
+
export interface ReleaseState {
|
|
70
|
+
readonly schema: typeof RELEASE_STATE_SCHEMA;
|
|
71
71
|
readonly revision: number;
|
|
72
72
|
readonly releases: Readonly<Record<string, ReleaseRecord>>;
|
|
73
73
|
readonly references: ReleaseReferences;
|
|
@@ -93,7 +93,7 @@ export interface OrphanReleaseDisposition {
|
|
|
93
93
|
readonly trashPath?: string | null;
|
|
94
94
|
}
|
|
95
95
|
export interface ReleaseRetentionReconciliation {
|
|
96
|
-
readonly state:
|
|
96
|
+
readonly state: ReleaseState;
|
|
97
97
|
readonly plan: ProtectedReleasePlan;
|
|
98
98
|
readonly detachedReleaseIds: readonly string[];
|
|
99
99
|
}
|
|
@@ -104,33 +104,33 @@ export interface ReleaseRetentionReconciliation {
|
|
|
104
104
|
* Nothing asks which processes are running: a session that outlives the window and two updates
|
|
105
105
|
* can lose its release files, which is the documented trade-off.
|
|
106
106
|
*/
|
|
107
|
-
export declare function planProtectedReleases(state:
|
|
107
|
+
export declare function planProtectedReleases(state: ReleaseState, inputs?: ProtectedReleaseInputs): ProtectedReleasePlan;
|
|
108
108
|
/**
|
|
109
109
|
* Whether a release is young enough to keep. An approved release counts from its approval; a
|
|
110
110
|
* candidate still awaiting certification counts from its materialization so concurrent work is
|
|
111
111
|
* not collected under it. A record without a readable timestamp is treated as old.
|
|
112
112
|
*/
|
|
113
113
|
export declare function withinRetention(release: ReleaseRecord, now: number, retentionMs?: number): boolean;
|
|
114
|
-
/** Serializes validated
|
|
115
|
-
export declare class
|
|
114
|
+
/** Serializes validated release-state revisions under a lock and commits each update atomically. */
|
|
115
|
+
export declare class ReleaseStateStore {
|
|
116
116
|
readonly path: string;
|
|
117
117
|
constructor(dataDir: string);
|
|
118
|
-
read(): Promise<
|
|
119
|
-
update(operation: (current:
|
|
120
|
-
recordCandidate(release: MaterializedRelease): Promise<
|
|
121
|
-
approve(releaseId: string, diagnosticsPath: string): Promise<
|
|
122
|
-
activate(releaseId: string): Promise<
|
|
118
|
+
read(): Promise<ReleaseState>;
|
|
119
|
+
update(operation: (current: ReleaseState) => ReleaseState | Promise<ReleaseState>): Promise<ReleaseState>;
|
|
120
|
+
recordCandidate(release: MaterializedRelease): Promise<ReleaseState>;
|
|
121
|
+
approve(releaseId: string, diagnosticsPath: string): Promise<ReleaseState>;
|
|
122
|
+
activate(releaseId: string): Promise<ReleaseState>;
|
|
123
123
|
/** Atomically advances ordinary launch without allowing an older installation to roll active state back. */
|
|
124
|
-
activateForLaunch(releaseId: string, replaceUnlaunchableActiveId?: string): Promise<
|
|
125
|
-
rollback(): Promise<
|
|
124
|
+
activateForLaunch(releaseId: string, replaceUnlaunchableActiveId?: string): Promise<ReleaseState>;
|
|
125
|
+
rollback(): Promise<ReleaseState>;
|
|
126
126
|
/** Atomically replace legacy retention history and detach every currently collectible record. */
|
|
127
|
-
reconcileRetention(resolveInputs: (current:
|
|
128
|
-
markCleanupTrash(releaseId: string, trashPath: string): Promise<
|
|
129
|
-
recordCleanupFailure(releaseId: string, stage: string, error: unknown): Promise<
|
|
130
|
-
completeCleanup(releaseId: string): Promise<
|
|
131
|
-
recordCleanupWorkerScheduled(runId: string): Promise<
|
|
132
|
-
recordCleanupWorkerStarted(runId: string, pid: number): Promise<
|
|
133
|
-
recordCleanupWorkerProgress(runId: string, summary: ReleaseCleanupWorkerSummary): Promise<
|
|
134
|
-
recordCleanupWorkerFinished(runId: string, status: Exclude<ReleaseCleanupWorkerStatus, "scheduled" | "running">, summary: ReleaseCleanupWorkerSummary, error?: unknown): Promise<
|
|
127
|
+
reconcileRetention(resolveInputs: (current: ReleaseState) => ProtectedReleaseInputs | Promise<ProtectedReleaseInputs>, orphans?: readonly OrphanReleaseDisposition[]): Promise<ReleaseRetentionReconciliation>;
|
|
128
|
+
markCleanupTrash(releaseId: string, trashPath: string): Promise<ReleaseState>;
|
|
129
|
+
recordCleanupFailure(releaseId: string, stage: string, error: unknown): Promise<ReleaseState>;
|
|
130
|
+
completeCleanup(releaseId: string): Promise<ReleaseState>;
|
|
131
|
+
recordCleanupWorkerScheduled(runId: string): Promise<ReleaseState>;
|
|
132
|
+
recordCleanupWorkerStarted(runId: string, pid: number): Promise<ReleaseState>;
|
|
133
|
+
recordCleanupWorkerProgress(runId: string, summary: ReleaseCleanupWorkerSummary): Promise<ReleaseState>;
|
|
134
|
+
recordCleanupWorkerFinished(runId: string, status: Exclude<ReleaseCleanupWorkerStatus, "scheduled" | "running">, summary: ReleaseCleanupWorkerSummary, error?: unknown): Promise<ReleaseState>;
|
|
135
135
|
}
|
|
136
|
-
export declare function emptyState():
|
|
136
|
+
export declare function emptyState(): ReleaseState;
|