@timurproko/a1 0.2.7-dev.810 → 0.2.7-dev.828
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 +7 -1
- package/bin/automatic-update.js +7 -0
- package/bin/tab-launch-context.d.ts +1 -0
- package/bin/tab-launch-context.js +3 -3
- package/bin/ui.js +96 -66
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
- package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
- package/dist/app/session-shell/session-shell-root.d.ts +14 -8
- package/dist/app/session-shell/session-shell-root.js +9 -5
- package/dist/app/session-shell/session-shell.js +70 -20
- package/dist/app/session-shell/session-viewport-controller.js +6 -4
- package/dist/app/session-shell/shell-host.js +6 -3
- package/dist/app/session-shell/transcript-document.js +1 -1
- package/dist/cli/tabs.d.ts +1 -1
- package/dist/cli/tabs.js +10 -14
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +5 -14
- package/dist/composition/owned-ui.js +93 -64
- package/dist/composition/resident-tab-bridge.d.ts +10 -1
- package/dist/composition/resident-tab-bridge.js +21 -0
- package/dist/composition/settings-route-host.d.ts +12 -2
- package/dist/composition/settings-route-host.js +27 -9
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +242 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
- 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/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/settings-app.d.ts +10 -3
- package/dist/features/owned-ui/settings-app.js +98 -45
- 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/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +2 -0
- package/dist/foundation/launch-context/index.d.ts +2 -0
- package/dist/foundation/launch-context/index.js +1 -0
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- 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 +1 -9
- package/dist/foundation/release/bootstrap.js +7 -3
- 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 +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -2
- package/dist/foundation/release/latest-release.js +1 -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.js +19 -4
- package/dist/foundation/release/update-execution-lease.d.ts +36 -0
- package/dist/foundation/release/update-execution-lease.js +145 -0
- package/dist/foundation/release/update.d.ts +9 -7
- package/dist/foundation/release/update.js +244 -175
- package/dist/foundation/resident-tabs/bridge-client.d.ts +10 -3
- package/dist/foundation/resident-tabs/bridge-client.js +20 -8
- package/dist/foundation/resident-tabs/maintenance.d.ts +2 -6
- package/dist/foundation/resident-tabs/maintenance.js +5 -23
- 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 +3 -6
- package/dist/foundation/resident-tabs/progress-observation.js +43 -12
- package/dist/foundation/resident-tabs/release-holds.js +3 -14
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/settings.d.ts +0 -1
- package/dist/foundation/resident-tabs/settings.js +0 -2
- 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 +3 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
- package/dist/integrations/pi/components/shell-footer-status.js +19 -3
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
- package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +5 -1
- package/dist/integrations/pi/engine/adapter.js +7 -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/ports.d.ts +1 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +13 -0
- package/dist/integrations/pi/engine/settings-port.js +1 -14
- package/dist/integrations/pi/engine/workflow-runner.js +9 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +2 -2
- package/dist/product-identity.json +2 -0
- 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/line-input.d.ts +3 -0
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +5 -4
- package/dist/ui/components/prompt-input.js +8 -6
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/submitted-prompt.d.ts +2 -2
- package/dist/ui/components/submitted-prompt.js +3 -3
- 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 +1 -1
- package/dist/ui/components/transcript-viewport.js +141 -24
- package/dist/ui/settings/declarations.d.ts +67 -17
- package/dist/ui/settings/declarations.js +47 -15
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +22 -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/resident-tabs.md +52 -21
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/docs/validation.md +25 -5
- package/package.json +1 -1
|
@@ -1,5 +1,139 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by scripts/pi/sync-progress-palette.ts from
|
|
3
|
-
* (MIT), commit 4a080252f06caf63a525d7873c5d94d3c23887e0. Do not edit directly.
|
|
2
|
+
* Generated by scripts/pi/sync-progress-palette.ts from A1's named accent projection
|
|
3
|
+
* over pinned Pi 1.4.0 (MIT), commit 4a080252f06caf63a525d7873c5d94d3c23887e0. Do not edit directly.
|
|
4
4
|
*/
|
|
5
|
-
export const
|
|
5
|
+
export const SHARED_PROGRESS_PALETTE = Object.freeze({
|
|
6
|
+
"blue": {
|
|
7
|
+
"dark": {
|
|
8
|
+
"truecolor": {
|
|
9
|
+
"accent": "\u001b[38;2;130;160;241m",
|
|
10
|
+
"border": "\u001b[38;2;116;102;197m"
|
|
11
|
+
},
|
|
12
|
+
"256color": {
|
|
13
|
+
"accent": "\u001b[38;5;111m",
|
|
14
|
+
"border": "\u001b[38;5;98m"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"light": {
|
|
18
|
+
"truecolor": {
|
|
19
|
+
"accent": "\u001b[38;2;63;96;226m",
|
|
20
|
+
"border": "\u001b[38;2;83;67;154m"
|
|
21
|
+
},
|
|
22
|
+
"256color": {
|
|
23
|
+
"accent": "\u001b[38;5;62m",
|
|
24
|
+
"border": "\u001b[38;5;60m"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"purple": {
|
|
29
|
+
"dark": {
|
|
30
|
+
"truecolor": {
|
|
31
|
+
"accent": "\u001b[38;2;167;152;215m",
|
|
32
|
+
"border": "\u001b[38;2;105;115;167m"
|
|
33
|
+
},
|
|
34
|
+
"256color": {
|
|
35
|
+
"accent": "\u001b[38;5;140m",
|
|
36
|
+
"border": "\u001b[38;5;61m"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"light": {
|
|
40
|
+
"truecolor": {
|
|
41
|
+
"accent": "\u001b[38;2;116;89;180m",
|
|
42
|
+
"border": "\u001b[38;2;68;77;130m"
|
|
43
|
+
},
|
|
44
|
+
"256color": {
|
|
45
|
+
"accent": "\u001b[38;5;97m",
|
|
46
|
+
"border": "\u001b[38;5;60m"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"cyan": {
|
|
51
|
+
"dark": {
|
|
52
|
+
"truecolor": {
|
|
53
|
+
"accent": "\u001b[38;2;93;179;186m",
|
|
54
|
+
"border": "\u001b[38;2;74;128;146m"
|
|
55
|
+
},
|
|
56
|
+
"256color": {
|
|
57
|
+
"accent": "\u001b[38;5;73m",
|
|
58
|
+
"border": "\u001b[38;5;66m"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"light": {
|
|
62
|
+
"truecolor": {
|
|
63
|
+
"accent": "\u001b[38;2;40;122;129m",
|
|
64
|
+
"border": "\u001b[38;2;40;89;106m"
|
|
65
|
+
},
|
|
66
|
+
"256color": {
|
|
67
|
+
"accent": "\u001b[38;5;30m",
|
|
68
|
+
"border": "\u001b[38;5;23m"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"green": {
|
|
73
|
+
"dark": {
|
|
74
|
+
"truecolor": {
|
|
75
|
+
"accent": "\u001b[38;2;104;183;141m",
|
|
76
|
+
"border": "\u001b[38;2;75;132;120m"
|
|
77
|
+
},
|
|
78
|
+
"256color": {
|
|
79
|
+
"accent": "\u001b[38;5;72m",
|
|
80
|
+
"border": "\u001b[38;5;66m"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"light": {
|
|
84
|
+
"truecolor": {
|
|
85
|
+
"accent": "\u001b[38;2;51;126;88m",
|
|
86
|
+
"border": "\u001b[38;2;41;93;82m"
|
|
87
|
+
},
|
|
88
|
+
"256color": {
|
|
89
|
+
"accent": "\u001b[38;5;65m",
|
|
90
|
+
"border": "\u001b[38;5;23m"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"orange": {
|
|
95
|
+
"dark": {
|
|
96
|
+
"truecolor": {
|
|
97
|
+
"accent": "\u001b[38;2;231;135;83m",
|
|
98
|
+
"border": "\u001b[38;2;178;89;79m"
|
|
99
|
+
},
|
|
100
|
+
"256color": {
|
|
101
|
+
"accent": "\u001b[38;5;173m",
|
|
102
|
+
"border": "\u001b[38;5;131m"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"light": {
|
|
106
|
+
"truecolor": {
|
|
107
|
+
"accent": "\u001b[38;2;174;80;16m",
|
|
108
|
+
"border": "\u001b[38;2;133;57;49m"
|
|
109
|
+
},
|
|
110
|
+
"256color": {
|
|
111
|
+
"accent": "\u001b[38;5;130m",
|
|
112
|
+
"border": "\u001b[38;5;95m"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"pink": {
|
|
117
|
+
"dark": {
|
|
118
|
+
"truecolor": {
|
|
119
|
+
"accent": "\u001b[38;2;223;122;199m",
|
|
120
|
+
"border": "\u001b[38;2;150;91;171m"
|
|
121
|
+
},
|
|
122
|
+
"256color": {
|
|
123
|
+
"accent": "\u001b[38;5;176m",
|
|
124
|
+
"border": "\u001b[38;5;97m"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"light": {
|
|
128
|
+
"truecolor": {
|
|
129
|
+
"accent": "\u001b[38;2;178;56;153m",
|
|
130
|
+
"border": "\u001b[38;2;114;61;131m"
|
|
131
|
+
},
|
|
132
|
+
"256color": {
|
|
133
|
+
"accent": "\u001b[38;5;132m",
|
|
134
|
+
"border": "\u001b[38;5;60m"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
export const SHARED_PROGRESS_ACCENT_ANSI = SHARED_PROGRESS_PALETTE.blue.dark.truecolor.accent;
|
|
@@ -316,14 +316,29 @@ async function residentReleaseHolds(dataDir, reader) {
|
|
|
316
316
|
const everything = () => [...new Set(records.map(record => record.releaseId))].map(releaseId => ({ authority: "resident", releaseId }));
|
|
317
317
|
if (!report.complete)
|
|
318
318
|
return everything();
|
|
319
|
-
|
|
320
|
-
|
|
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);
|
|
321
331
|
const holds = new Set();
|
|
322
332
|
for (const root of report.releaseRoots) {
|
|
323
|
-
|
|
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);
|
|
324
339
|
if (releaseId)
|
|
325
340
|
holds.add(releaseId);
|
|
326
|
-
else if (
|
|
341
|
+
else if (key.startsWith(`${store}${sep}`))
|
|
327
342
|
return everything();
|
|
328
343
|
}
|
|
329
344
|
return [...holds].sort().map(releaseId => ({ authority: "resident", releaseId }));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { UpdateChannel } from "./types.js";
|
|
2
|
+
export declare const UPDATE_EXECUTION_LEASE_FILENAME = "update-execution.json";
|
|
3
|
+
declare const LEASE_SCHEMA = 1;
|
|
4
|
+
export type UpdateExecutionMode = "manual" | "automatic";
|
|
5
|
+
export interface UpdateExecutionOwner {
|
|
6
|
+
readonly schema: typeof LEASE_SCHEMA;
|
|
7
|
+
readonly ownerId: string;
|
|
8
|
+
readonly pid: number;
|
|
9
|
+
readonly pidStartIdentity: string;
|
|
10
|
+
readonly packageRoot: string;
|
|
11
|
+
readonly channel: UpdateChannel;
|
|
12
|
+
readonly targetVersion: string;
|
|
13
|
+
readonly mode: UpdateExecutionMode;
|
|
14
|
+
readonly acquiredAt: string;
|
|
15
|
+
}
|
|
16
|
+
export type UpdateExecutionLeaseResult = {
|
|
17
|
+
readonly status: "acquired";
|
|
18
|
+
readonly owner: UpdateExecutionOwner;
|
|
19
|
+
release(): Promise<void>;
|
|
20
|
+
} | {
|
|
21
|
+
readonly status: "busy";
|
|
22
|
+
readonly owner: UpdateExecutionOwner;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Serializes every package replacement for one A1 data root. A dead owner is moved aside before a
|
|
26
|
+
* new exclusive create, so concurrent recovery attempts cannot both acquire the installation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function acquireUpdateExecutionLease(input: {
|
|
29
|
+
readonly dataDir: string;
|
|
30
|
+
readonly packageRoot: string;
|
|
31
|
+
readonly channel: UpdateChannel;
|
|
32
|
+
readonly targetVersion: string;
|
|
33
|
+
readonly mode: UpdateExecutionMode;
|
|
34
|
+
}): Promise<UpdateExecutionLeaseResult>;
|
|
35
|
+
export declare function readUpdateExecutionOwner(dataDir: string): Promise<UpdateExecutionOwner | null>;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
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.
|
|
60
|
+
if (processIsAlive(existing.pid))
|
|
61
|
+
return { status: "busy", owner: existing };
|
|
62
|
+
if (!await staleLeaseMatchesTransaction(input.dataDir, existing, input))
|
|
63
|
+
return { status: "busy", owner: existing };
|
|
64
|
+
const stale = `${path}.stale-${existing.ownerId}-${randomUUID()}`;
|
|
65
|
+
try {
|
|
66
|
+
await rename(path, stale);
|
|
67
|
+
await rm(stale, { force: true });
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (!hasCode(error, "ENOENT"))
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const existing = await readUpdateExecutionOwner(input.dataDir);
|
|
75
|
+
if (existing !== null)
|
|
76
|
+
return { status: "busy", owner: existing };
|
|
77
|
+
throw new Error("could not acquire update execution lease");
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await releaseAcquisition();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export async function readUpdateExecutionOwner(dataDir) {
|
|
84
|
+
const path = resolve(dataDir, UPDATE_EXECUTION_LEASE_FILENAME);
|
|
85
|
+
let source;
|
|
86
|
+
try {
|
|
87
|
+
source = await readFile(path, "utf8");
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (hasCode(error, "ENOENT"))
|
|
91
|
+
return null;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
if (Buffer.byteLength(source, "utf8") > MAX_LEASE_BYTES)
|
|
95
|
+
throw new Error("update execution lease is oversized");
|
|
96
|
+
const value = JSON.parse(source);
|
|
97
|
+
validateOwner(value);
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
async function releaseLease(path, owner) {
|
|
101
|
+
const current = await readUpdateExecutionOwner(dirname(path));
|
|
102
|
+
if (current === null || current.ownerId !== owner.ownerId || current.pid !== owner.pid
|
|
103
|
+
|| current.pidStartIdentity !== owner.pidStartIdentity)
|
|
104
|
+
return;
|
|
105
|
+
const released = `${path}.released-${owner.ownerId}-${randomUUID()}`;
|
|
106
|
+
try {
|
|
107
|
+
await rename(path, released);
|
|
108
|
+
await rm(released, { force: true });
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (!hasCode(error, "ENOENT"))
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function validateOwner(value) {
|
|
116
|
+
if (value.schema !== LEASE_SCHEMA || typeof value.ownerId !== "string" || value.ownerId.length === 0
|
|
117
|
+
|| !Number.isSafeInteger(value.pid) || value.pid <= 0 || typeof value.pidStartIdentity !== "string"
|
|
118
|
+
|| typeof value.packageRoot !== "string" || !["stable", "next"].includes(value.channel)
|
|
119
|
+
|| typeof value.targetVersion !== "string" || !["manual", "automatic"].includes(value.mode)
|
|
120
|
+
|| typeof value.acquiredAt !== "string") {
|
|
121
|
+
throw new Error("invalid update execution lease");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function staleLeaseMatchesTransaction(dataDir, owner, requested) {
|
|
125
|
+
const transaction = await new UpdateTransactionStore(dataDir).read();
|
|
126
|
+
if (transaction?.status !== "active")
|
|
127
|
+
return true;
|
|
128
|
+
return transaction.channel === owner.channel && transaction.targetVersion === owner.targetVersion
|
|
129
|
+
&& samePath(transaction.packageRoot, owner.packageRoot)
|
|
130
|
+
&& requested.channel === owner.channel && requested.targetVersion === owner.targetVersion
|
|
131
|
+
&& samePath(requested.packageRoot, owner.packageRoot);
|
|
132
|
+
}
|
|
133
|
+
function samePath(left, right) {
|
|
134
|
+
const canonicalLeft = resolve(left);
|
|
135
|
+
const canonicalRight = resolve(right);
|
|
136
|
+
return process.platform === "win32"
|
|
137
|
+
? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase()
|
|
138
|
+
: canonicalLeft === canonicalRight;
|
|
139
|
+
}
|
|
140
|
+
function currentProcessStartIdentity() {
|
|
141
|
+
return `${process.pid}:${Math.floor(Date.now() - process.uptime() * 1_000)}`;
|
|
142
|
+
}
|
|
143
|
+
function hasCode(error, code) {
|
|
144
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
145
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { UpdateChannel } from "./types.js";
|
|
2
2
|
import { type UpdateActivationPhase, type UpdateMaterializationProgress } from "./update-activation.js";
|
|
3
3
|
import { type UpdateRecoveryState, type UpdateTransaction, type UpdateTransactionPhase } from "./update-transaction.js";
|
|
4
|
+
import { type UpdateExecutionOwner } from "./update-execution-lease.js";
|
|
4
5
|
import { resolveNpmCli, type ProtectedPackageReplacementResult } from "./update-recovery.js";
|
|
5
6
|
export declare const PRODUCT_PACKAGE: string;
|
|
6
7
|
export type { UpdateChannel } from "./types.js";
|
|
@@ -61,6 +62,11 @@ export interface SelfUpdateOptions {
|
|
|
61
62
|
channel?: UpdateChannel;
|
|
62
63
|
/** A specific preview to install, named by its development number or full version. */
|
|
63
64
|
target?: string;
|
|
65
|
+
/** Internal exact channel target selected by the automatic update probe. */
|
|
66
|
+
resolvedTarget?: string;
|
|
67
|
+
mode?: "manual" | "automatic";
|
|
68
|
+
onLeaseAcquired?: (owner: UpdateExecutionOwner) => void;
|
|
69
|
+
onLeaseBusy?: (owner: UpdateExecutionOwner) => void;
|
|
64
70
|
environment?: NodeJS.ProcessEnv;
|
|
65
71
|
fileSystem?: UpdateFileSystem;
|
|
66
72
|
output?: UpdateOutput;
|
|
@@ -80,6 +86,7 @@ export interface SelfUpdateOptions {
|
|
|
80
86
|
export type { UpdateActivationPhase, UpdateMaterializationProgress } from "./update-activation.js";
|
|
81
87
|
export interface UpdateLifecycleCoordinator {
|
|
82
88
|
targetIsActive(targetVersion: string): Promise<boolean>;
|
|
89
|
+
verifyAutomaticSafety?(): Promise<void>;
|
|
83
90
|
shutdownVerifiedOwners(targetVersion: string): Promise<{
|
|
84
91
|
priorActiveVersion: string | null;
|
|
85
92
|
}>;
|
|
@@ -123,6 +130,7 @@ export interface UpdatePackageReplacementInput {
|
|
|
123
130
|
* The update therefore keeps checking for this long before it calls the package locked.
|
|
124
131
|
*/
|
|
125
132
|
export declare const PACKAGE_UNLOCK_PATIENCE_MS = 15000;
|
|
133
|
+
export declare const UPDATE_ALREADY_RUNNING_EXIT_CODE = 75;
|
|
126
134
|
/** Whether a rename failure means something holds the tree, as opposed to the tree being wrong. */
|
|
127
135
|
export declare function isPackageLockError(error: unknown): boolean;
|
|
128
136
|
/** What the user reads when the package stayed held for the whole patience window. */
|
|
@@ -131,15 +139,9 @@ export declare function lockedPackageDiagnostic(packageRoot: string, waitedMs: n
|
|
|
131
139
|
export declare const CHILD_DIAGNOSTIC_LIMIT = 8000;
|
|
132
140
|
export declare function createNpmProcessRunner(platform?: NodeJS.Platform): UpdateProcessRunner;
|
|
133
141
|
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
142
|
export declare function planUpdateOwnership(ownership: "live-verified" | "dead", runsFromRetainedRelease: boolean): UpdateOwnershipAction;
|
|
141
143
|
export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput, patience?: UpdateUnlockPatience): UpdateLifecycleCoordinator;
|
|
142
144
|
/** Renders the terminal-only update meter; the visual preview shares this exact frame. */
|
|
143
|
-
export declare function renderUpdateProgressBar(percent: number): string;
|
|
145
|
+
export declare function renderUpdateProgressBar(percent: number, accent?: string): string;
|
|
144
146
|
export declare function runSelfUpdate(options: SelfUpdateOptions): Promise<number>;
|
|
145
147
|
export declare function assertUpdatePerformanceBudget(evidence: UpdatePerformanceEvidence, maximumPostNpmDurationMs?: number): void;
|