@timurproko/a1 0.1.8-dev.218 → 0.1.8-dev.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +6 -0
- package/bin/guardian.js +3 -0
- package/bin/release-cleanup.js +8 -0
- package/bin/ui.js +4 -0
- package/bin/warmup.js +18 -0
- package/dist/features/owned-ui/run.js +2 -0
- package/dist/foundation/launch-guardian/main.js +2 -0
- package/dist/foundation/release/bootstrap.d.ts +3 -1
- package/dist/foundation/release/bootstrap.js +9 -0
- package/dist/foundation/release/cohort-state.d.ts +61 -1
- package/dist/foundation/release/cohort-state.js +192 -11
- package/dist/foundation/release/dependency-layer.d.ts +78 -0
- package/dist/foundation/release/dependency-layer.js +487 -0
- package/dist/foundation/release/endpoints.d.ts +1 -1
- package/dist/foundation/release/endpoints.js +8 -8
- package/dist/foundation/release/index.d.ts +2 -0
- package/dist/foundation/release/index.js +2 -0
- package/dist/foundation/release/release-gc.d.ts +59 -7
- package/dist/foundation/release/release-gc.js +533 -19
- package/dist/foundation/release/release-store.d.ts +3 -1
- package/dist/foundation/release/release-store.js +60 -10
- package/dist/foundation/release/release.d.ts +4 -1
- package/dist/foundation/release/release.js +9 -2
- package/dist/foundation/release/update.d.ts +10 -2
- package/dist/foundation/release/update.js +27 -4
- package/dist/foundation/release/warmup.d.ts +3 -0
- package/dist/foundation/release/warmup.js +35 -0
- package/dist/foundation/startup/index.d.ts +1 -0
- package/dist/foundation/startup/index.js +1 -0
- package/dist/foundation/startup/startup-runtime.d.ts +36 -0
- package/dist/foundation/startup/startup-runtime.js +140 -0
- package/dist/integrations/pi/engine/runtime-integration.js +5 -0
- package/dist/native/darwin-arm64/manifest.json +3 -3
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +2 -2
- package/dist/product-identity.js +4 -1
- package/dist/product-identity.json +8 -1
- package/dist/runtime-payload-inventory.json +122328 -0
- package/package.json +2 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { chmod, lstat, mkdir, readFile, realpath, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { PRODUCT_PACKAGE_NAME, createReleaseIdentity,
|
|
4
|
+
import { PRODUCT_PACKAGE_NAME, createReleaseIdentity, discoverReleasePayload, releaseFileIdentity, resolveWithin, } from "./release.js";
|
|
5
5
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
|
|
6
6
|
import { mapWithConcurrency } from "./concurrency.js";
|
|
7
|
+
import { dependencyReference, materializeDependencyLayer, readCertifiedDependencyLayer, selectPublishedDependencyRuntimePayload, verifyDependencyLayer, } from "./dependency-layer.js";
|
|
7
8
|
const RELEASE_FILE_IO_CONCURRENCY = 32;
|
|
8
9
|
const certificationReady = new WeakSet();
|
|
9
10
|
export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
|
|
@@ -13,15 +14,32 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
13
14
|
});
|
|
14
15
|
const storeRoot = resolve(dataDir, "releases");
|
|
15
16
|
await mkdir(storeRoot, { recursive: true, mode: 0o700 });
|
|
16
|
-
|
|
17
|
+
const dependencyPaths = payload.paths.filter(path => path.startsWith("node_modules/"));
|
|
18
|
+
const productPaths = payload.paths.filter(path => !path.startsWith("node_modules/"));
|
|
19
|
+
const selectedDependencies = await selectPublishedDependencyRuntimePayload(payload.packageRoot, dependencyPaths);
|
|
20
|
+
options.onRuntimeInventory?.(selectedDependencies.inventory);
|
|
21
|
+
const layerOperations = [];
|
|
22
|
+
const layer = await materializeDependencyLayer(payload.packageRoot, dataDir, selectedDependencies.paths, {
|
|
23
|
+
inventory: selectedDependencies.inventory,
|
|
24
|
+
cachedFiles: payload.cachedFiles,
|
|
25
|
+
onOperation: event => layerOperations.push(event),
|
|
26
|
+
...(options.writeCandidateFile === undefined ? {} : { writeCandidateFile: options.writeCandidateFile }),
|
|
27
|
+
});
|
|
28
|
+
const layerReferences = layer === null ? [] : [dependencyReference(layer)];
|
|
29
|
+
options.onProgress?.({
|
|
30
|
+
phase: "copying",
|
|
31
|
+
fileCount: productPaths.length + (layer?.reused === false ? layer.files.length : 0),
|
|
32
|
+
});
|
|
33
|
+
for (const event of layerOperations)
|
|
34
|
+
options.onOperation?.(event);
|
|
17
35
|
const candidate = resolveWithin(storeRoot, `.candidate-${randomUUID()}`);
|
|
18
36
|
await mkdir(candidate, { recursive: false, mode: 0o700 });
|
|
19
37
|
try {
|
|
20
|
-
const directories = [...new Set(
|
|
38
|
+
const directories = [...new Set(productPaths.map(path => dirname(resolveWithin(candidate, path))))];
|
|
21
39
|
await mapWithConcurrency(directories, RELEASE_FILE_IO_CONCURRENCY, async (directory) => {
|
|
22
40
|
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
23
41
|
});
|
|
24
|
-
const files = await mapWithConcurrency(
|
|
42
|
+
const files = await mapWithConcurrency(productPaths, RELEASE_FILE_IO_CONCURRENCY, async (path) => {
|
|
25
43
|
const source = resolveWithin(payload.packageRoot, path);
|
|
26
44
|
const destination = resolveWithin(candidate, path);
|
|
27
45
|
const metadata = await lstat(source);
|
|
@@ -41,7 +59,12 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
|
|
|
41
59
|
options.onOperation?.({ operation: "candidate-write", path, bytes: bytes.length });
|
|
42
60
|
return releaseFileIdentity(path, bytes, (metadata.mode & 0o111) !== 0);
|
|
43
61
|
});
|
|
44
|
-
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files);
|
|
62
|
+
const identity = createReleaseIdentity(payload.packageRoot, payload.packageVersion, files, layerReferences);
|
|
63
|
+
if (layer !== null) {
|
|
64
|
+
const binding = resolveWithin(candidate, "node_modules");
|
|
65
|
+
const target = resolveWithin(layer.layerRoot, "node_modules");
|
|
66
|
+
await symlink(target, binding, process.platform === "win32" ? "junction" : "dir");
|
|
67
|
+
}
|
|
45
68
|
const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
|
|
46
69
|
if (await lstat(releaseRoot).catch(() => null)) {
|
|
47
70
|
await rm(candidate, { recursive: true, force: true });
|
|
@@ -84,7 +107,8 @@ export async function readMaterializedRelease(releaseRoot) {
|
|
|
84
107
|
*/
|
|
85
108
|
export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
86
109
|
const canonical = await realpath(record.releaseRoot);
|
|
87
|
-
|
|
110
|
+
const canonicalStoreRoot = await realpath(selectedStoreRoot);
|
|
111
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
88
112
|
const manifest = JSON.parse(await readFile(resolveWithin(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
|
|
89
113
|
validateManifest(manifest);
|
|
90
114
|
if (manifest.releaseId !== record.releaseId || manifest.contentDigest !== record.contentDigest
|
|
@@ -93,12 +117,13 @@ export async function readCertifiedReleaseManifest(record, selectedStoreRoot) {
|
|
|
93
117
|
}
|
|
94
118
|
if (canonical.split(sep).at(-1) !== manifest.releaseId)
|
|
95
119
|
throw new Error(`release directory does not match identity ${manifest.releaseId}`);
|
|
120
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], false);
|
|
96
121
|
return { ...manifest, releaseRoot: canonical };
|
|
97
122
|
}
|
|
98
123
|
export async function verifyMaterializedRelease(releaseRoot, expected, selectedStoreRoot, options = {}) {
|
|
99
124
|
const canonical = await realpath(releaseRoot);
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
const canonicalStoreRoot = selectedStoreRoot ? await realpath(selectedStoreRoot) : await realpath(dirname(canonical));
|
|
126
|
+
assertContained(canonicalStoreRoot, canonical, "release root is outside the selected release store");
|
|
102
127
|
const manifestPath = resolveWithin(canonical, RELEASE_MANIFEST_FILENAME);
|
|
103
128
|
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
104
129
|
validateManifest(manifest);
|
|
@@ -111,9 +136,10 @@ export async function verifyMaterializedRelease(releaseRoot, expected, selectedS
|
|
|
111
136
|
await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
|
|
112
137
|
await verifyFile(canonical, file, options);
|
|
113
138
|
});
|
|
114
|
-
const recomputed =
|
|
139
|
+
const recomputed = createReleaseIdentity(manifest.packageRoot, manifest.packageVersion, manifest.files, manifest.dependencyLayers ?? []).contentDigest;
|
|
115
140
|
if (recomputed !== manifest.contentDigest)
|
|
116
141
|
throw new Error(`release content digest mismatch for ${manifest.releaseId}`);
|
|
142
|
+
await verifyReleaseDependencies(canonical, canonicalStoreRoot, manifest.dependencyLayers ?? [], true, options);
|
|
117
143
|
return { ...manifest, releaseRoot: canonical };
|
|
118
144
|
}
|
|
119
145
|
export async function assertImmutableExecutionRoot(release, dataDir) {
|
|
@@ -135,6 +161,25 @@ export async function resolveReleaseEntryPoint(release, entryPoint) {
|
|
|
135
161
|
assertContained(release.releaseRoot, canonical, "entry point resolves outside the selected release root");
|
|
136
162
|
return canonical;
|
|
137
163
|
}
|
|
164
|
+
async function verifyReleaseDependencies(releaseRoot, storeRoot, references, fullVerification, options = {}) {
|
|
165
|
+
if (references.length === 0)
|
|
166
|
+
return;
|
|
167
|
+
if (references.length !== 1)
|
|
168
|
+
throw new Error("release currently supports exactly one dependency layer");
|
|
169
|
+
const dataDir = dirname(storeRoot);
|
|
170
|
+
const reference = references[0];
|
|
171
|
+
const layer = fullVerification
|
|
172
|
+
? await verifyDependencyLayer(dataDir, reference, event => options.onOperation?.({ operation: event.operation, path: event.path, bytes: event.bytes }))
|
|
173
|
+
: await readCertifiedDependencyLayer(dataDir, reference.layerId, reference);
|
|
174
|
+
const binding = resolveWithin(releaseRoot, reference.binding);
|
|
175
|
+
const metadata = await lstat(binding);
|
|
176
|
+
if (!metadata.isSymbolicLink())
|
|
177
|
+
throw new Error(`release dependency binding is not managed: ${binding}`);
|
|
178
|
+
const target = await realpath(binding);
|
|
179
|
+
const expected = await realpath(resolveWithin(layer.layerRoot, "node_modules"));
|
|
180
|
+
if (target !== expected)
|
|
181
|
+
throw new Error(`release dependency binding targets unexpected content: ${target}`);
|
|
182
|
+
}
|
|
138
183
|
async function verifyFile(root, file, options) {
|
|
139
184
|
const path = resolveWithin(root, file.path);
|
|
140
185
|
const metadata = await lstat(path).catch(() => null);
|
|
@@ -159,6 +204,11 @@ function validateManifest(value) {
|
|
|
159
204
|
throw new Error(PRODUCT_TEXT.diagnostic("release identity is invalid"));
|
|
160
205
|
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
161
206
|
throw new Error("release manifest contains no files");
|
|
207
|
+
if (value.dependencyLayers !== undefined && (!Array.isArray(value.dependencyLayers) || value.dependencyLayers.length === 0
|
|
208
|
+
|| value.dependencyLayers.some(layer => !/^dependencies-[a-f0-9]{32}$/.test(layer.layerId)
|
|
209
|
+
|| !/^[a-f0-9]{64}$/.test(layer.contentDigest) || layer.binding !== "node_modules"))) {
|
|
210
|
+
throw new Error("release dependency-layer references are invalid");
|
|
211
|
+
}
|
|
162
212
|
for (const file of value.files) {
|
|
163
213
|
if (typeof file.path !== "string" || file.path.length === 0 || file.path.includes("\\") || file.path.startsWith("/") || file.path.split("/").includes("..")) {
|
|
164
214
|
throw new Error(`invalid release manifest path: ${String(file.path)}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DependencyLayerReference } from "./dependency-layer.js";
|
|
1
2
|
export declare const PRODUCT_PACKAGE_NAME: string;
|
|
2
3
|
export interface ReleaseFileIdentity {
|
|
3
4
|
readonly path: string;
|
|
@@ -12,6 +13,8 @@ export interface ReleaseIdentity {
|
|
|
12
13
|
readonly releaseId: string;
|
|
13
14
|
readonly packageRoot: string;
|
|
14
15
|
readonly files: readonly ReleaseFileIdentity[];
|
|
16
|
+
/** Absent for legacy full-copy releases. */
|
|
17
|
+
readonly dependencyLayers?: readonly DependencyLayerReference[];
|
|
15
18
|
}
|
|
16
19
|
export interface DiscoveredReleasePayload {
|
|
17
20
|
readonly packageRoot: string;
|
|
@@ -31,7 +34,7 @@ export declare function discoverReleasePayload(packageRoot: string, options?: Di
|
|
|
31
34
|
* content.
|
|
32
35
|
*/
|
|
33
36
|
export declare function deriveReleaseIdentity(packageRoot: string): Promise<ReleaseIdentity>;
|
|
34
|
-
export declare function createReleaseIdentity(packageRoot: string, packageVersion: string, files: readonly ReleaseFileIdentity[]): ReleaseIdentity;
|
|
37
|
+
export declare function createReleaseIdentity(packageRoot: string, packageVersion: string, files: readonly ReleaseFileIdentity[], dependencyLayers?: readonly DependencyLayerReference[]): ReleaseIdentity;
|
|
35
38
|
export declare function releaseFileIdentity(path: string, bytes: Uint8Array, executable: boolean): ReleaseFileIdentity;
|
|
36
39
|
export declare function digestManifestFiles(files: readonly ReleaseFileIdentity[]): string;
|
|
37
40
|
export declare function resolveWithin(root: string, candidate: string): string;
|
|
@@ -51,8 +51,14 @@ export async function deriveReleaseIdentity(packageRoot) {
|
|
|
51
51
|
});
|
|
52
52
|
return createReleaseIdentity(payload.packageRoot, payload.packageVersion, files);
|
|
53
53
|
}
|
|
54
|
-
export function createReleaseIdentity(packageRoot, packageVersion, files) {
|
|
55
|
-
const
|
|
54
|
+
export function createReleaseIdentity(packageRoot, packageVersion, files, dependencyLayers = []) {
|
|
55
|
+
const productDigest = digestManifestFiles(files);
|
|
56
|
+
const contentDigest = dependencyLayers.length === 0
|
|
57
|
+
? productDigest
|
|
58
|
+
: createHash("sha256")
|
|
59
|
+
.update(`product\0${productDigest}\n`)
|
|
60
|
+
.update(dependencyLayers.map(layer => `${layer.layerId}\0${layer.contentDigest}\0${layer.binding}\n`).join(""))
|
|
61
|
+
.digest("hex");
|
|
56
62
|
return {
|
|
57
63
|
packageName: PRODUCT_PACKAGE_NAME,
|
|
58
64
|
packageVersion,
|
|
@@ -60,6 +66,7 @@ export function createReleaseIdentity(packageRoot, packageVersion, files) {
|
|
|
60
66
|
releaseId: `${packageVersion}-${contentDigest.slice(0, 20)}`,
|
|
61
67
|
packageRoot,
|
|
62
68
|
files: [...files].sort(compareReleaseFiles),
|
|
69
|
+
...(dependencyLayers.length === 0 ? {} : { dependencyLayers: [...dependencyLayers] }),
|
|
63
70
|
};
|
|
64
71
|
}
|
|
65
72
|
export function releaseFileIdentity(path, bytes, executable) {
|
|
@@ -18,7 +18,7 @@ export interface UpdateOutput {
|
|
|
18
18
|
stdout(message: string): void;
|
|
19
19
|
stderr(message: string): void;
|
|
20
20
|
}
|
|
21
|
-
export type UpdateMeasuredPhase = "package-version" | "target-resolution" | "global-root" | "ownership-release" | "npm-install" | "materialized" | "certified" | "active-reference-committed" | "supervisor-verified" | "transaction-complete";
|
|
21
|
+
export type UpdateMeasuredPhase = "package-version" | "target-resolution" | "global-root" | "ownership-release" | "npm-install" | "materialized" | "certified" | "active-reference-committed" | "warmup" | "supervisor-verified" | "transaction-complete";
|
|
22
22
|
export interface UpdatePhaseTimingEvent {
|
|
23
23
|
readonly phase: UpdateMeasuredPhase;
|
|
24
24
|
readonly durationMs: number;
|
|
@@ -28,6 +28,12 @@ export interface UpdatePerformanceEvidence {
|
|
|
28
28
|
readonly sourceReads: number;
|
|
29
29
|
readonly candidateWrites: number;
|
|
30
30
|
readonly verificationReads: number;
|
|
31
|
+
readonly layerWrites?: number;
|
|
32
|
+
readonly layerReusedFiles?: number;
|
|
33
|
+
readonly layerReusedBytes?: number;
|
|
34
|
+
readonly payloadExcludedFiles?: number;
|
|
35
|
+
readonly payloadExcludedBytes?: number;
|
|
36
|
+
readonly warmupDurationMs?: number;
|
|
31
37
|
readonly postNpmDurationMs: number;
|
|
32
38
|
}
|
|
33
39
|
export interface SelfUpdateOptions {
|
|
@@ -41,6 +47,8 @@ export interface SelfUpdateOptions {
|
|
|
41
47
|
runner?: UpdateProcessRunner;
|
|
42
48
|
lifecycle?: UpdateLifecycleCoordinator;
|
|
43
49
|
transactionStore?: UpdateTransactionJournal;
|
|
50
|
+
/** Test or embedding seam for post-activation release maintenance. */
|
|
51
|
+
maintenance?: () => Promise<void>;
|
|
44
52
|
progress?: boolean;
|
|
45
53
|
onPhaseTiming?: (event: UpdatePhaseTimingEvent) => void;
|
|
46
54
|
now?: () => number;
|
|
@@ -61,7 +69,7 @@ export interface UpdateLifecycleCoordinator {
|
|
|
61
69
|
priorActiveVersion: string | null;
|
|
62
70
|
}>;
|
|
63
71
|
verifyPackageUnlocked(packageRoot: string): Promise<void>;
|
|
64
|
-
activateInstalled(packageRoot: string, targetVersion: string, phase: (phase: UpdateActivationPhase) => Promise<void>, onMaterializing?: (progress: UpdateMaterializationProgress) => void): Promise<void>;
|
|
72
|
+
activateInstalled(packageRoot: string, targetVersion: string, phase: (phase: UpdateActivationPhase) => Promise<void>, onMaterializing?: (progress: UpdateMaterializationProgress) => void, onWarmup?: (state: "started" | "completed") => void): Promise<void>;
|
|
65
73
|
}
|
|
66
74
|
export interface UpdateTransactionJournal {
|
|
67
75
|
readonly path: string;
|
|
@@ -11,6 +11,8 @@ import { encodeFrame, LineFrameDecoder } from "../protocol/index.js";
|
|
|
11
11
|
import { CohortStateStore } from "./cohort-state.js";
|
|
12
12
|
import { cleanupVerifiedOwner, processIsAlive } from "./process-cleanup.js";
|
|
13
13
|
import { materializeRelease, readMaterializedRelease } from "./release-store.js";
|
|
14
|
+
import { scheduleReleaseCleanup } from "./release-gc.js";
|
|
15
|
+
import { warmMaterializedRelease } from "./warmup.js";
|
|
14
16
|
import { UpdateTransactionStore } from "./update-transaction.js";
|
|
15
17
|
export const PRODUCT_PACKAGE = PRODUCT_TEXT.packageName;
|
|
16
18
|
const UPDATE_DIST_TAGS = { stable: "latest", next: "next" };
|
|
@@ -138,7 +140,7 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
138
140
|
throw new Error(PRODUCT_TEXT.diagnostic(`package remains locked after verified shutdown: ${errorMessage(error)}`));
|
|
139
141
|
}
|
|
140
142
|
},
|
|
141
|
-
async activateInstalled(packageRoot, targetVersion, phase, onMaterializing) {
|
|
143
|
+
async activateInstalled(packageRoot, targetVersion, phase, onMaterializing, onWarmup) {
|
|
142
144
|
let total = 0;
|
|
143
145
|
let completed = 0;
|
|
144
146
|
const candidate = await materializeRelease(packageRoot, paths.dataDir, {
|
|
@@ -147,7 +149,7 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
147
149
|
onMaterializing?.({ completed, total });
|
|
148
150
|
},
|
|
149
151
|
onOperation: event => {
|
|
150
|
-
if (event.operation !== "candidate-write")
|
|
152
|
+
if (event.operation !== "candidate-write" && event.operation !== "layer-write")
|
|
151
153
|
return;
|
|
152
154
|
completed += 1;
|
|
153
155
|
onMaterializing?.({ completed, total });
|
|
@@ -162,6 +164,9 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
|
|
|
162
164
|
await phase("certified");
|
|
163
165
|
await stateStore.activate(candidate.releaseId);
|
|
164
166
|
await phase("active-reference-committed");
|
|
167
|
+
onWarmup?.("started");
|
|
168
|
+
await warmMaterializedRelease(candidate, environment);
|
|
169
|
+
onWarmup?.("completed");
|
|
165
170
|
await startSupervisor(candidate, environment);
|
|
166
171
|
await waitForVerifiedEndpoint(resolveCohortEndpoint(paths, candidate.releaseId, environment).endpointMetadataPath, candidate, 8_000);
|
|
167
172
|
},
|
|
@@ -364,6 +369,9 @@ export async function runSelfUpdate(options) {
|
|
|
364
369
|
const paths = resolveProductPaths(environment);
|
|
365
370
|
const lifecycle = options.lifecycle ?? createUpdateLifecycleCoordinator(environment, fileSystem, output);
|
|
366
371
|
const transactionStore = options.transactionStore ?? new UpdateTransactionStore(paths.dataDir);
|
|
372
|
+
const maintenance = options.maintenance ?? (options.lifecycle
|
|
373
|
+
? async () => { }
|
|
374
|
+
: async () => await scheduleReleaseCleanup(paths.dataDir, paths));
|
|
367
375
|
let transaction = await transactionStore.read();
|
|
368
376
|
try {
|
|
369
377
|
if (await lifecycle.targetIsActive(targetVersion)) {
|
|
@@ -371,6 +379,7 @@ export async function runSelfUpdate(options) {
|
|
|
371
379
|
await transactionStore.advance("supervisor-verified");
|
|
372
380
|
await transactionStore.finish("completed");
|
|
373
381
|
}
|
|
382
|
+
await maintenance();
|
|
374
383
|
await transactionStore.clearCompleted();
|
|
375
384
|
output.stdout(`${PRODUCT_TEXT.commandName} is up to date — no update needed.\n`);
|
|
376
385
|
return 0;
|
|
@@ -432,11 +441,24 @@ export async function runSelfUpdate(options) {
|
|
|
432
441
|
const span = MATERIALIZE_PROGRESS.to - MATERIALIZE_PROGRESS.from;
|
|
433
442
|
const done = total > 0 ? Math.min(1, completed / total) : 0;
|
|
434
443
|
progress.set(MATERIALIZE_PROGRESS.from + span * done);
|
|
444
|
+
}, state => {
|
|
445
|
+
if (state === "started") {
|
|
446
|
+
activationPhaseStartedAt = now();
|
|
447
|
+
progress.set(98, 99);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
options.onPhaseTiming?.({ phase: "warmup", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
|
|
451
|
+
activationPhaseStartedAt = now();
|
|
452
|
+
progress.set(99);
|
|
453
|
+
}
|
|
435
454
|
});
|
|
436
455
|
options.onPhaseTiming?.({ phase: "supervisor-verified", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
|
|
437
456
|
const transactionStartedAt = now();
|
|
438
457
|
await transactionStore.advance("supervisor-verified");
|
|
439
458
|
await transactionStore.finish("completed");
|
|
459
|
+
// Invariant: successful output follows the durable cleanup disposition. Slow recursive
|
|
460
|
+
// removal belongs to the detached worker started by this maintenance coordinator.
|
|
461
|
+
await maintenance();
|
|
440
462
|
await transactionStore.clearCompleted();
|
|
441
463
|
options.onPhaseTiming?.({ phase: "transaction-complete", durationMs: Math.max(0, now() - transactionStartedAt) });
|
|
442
464
|
progress.finish();
|
|
@@ -461,8 +483,9 @@ export function assertUpdatePerformanceBudget(evidence, maximumPostNpmDurationMs
|
|
|
461
483
|
failures.push("fixture contains no payload files");
|
|
462
484
|
if (evidence.sourceReads !== evidence.fileCount)
|
|
463
485
|
failures.push(`source payload read count is ${evidence.sourceReads} for ${evidence.fileCount} files`);
|
|
464
|
-
|
|
465
|
-
|
|
486
|
+
const totalWrites = evidence.candidateWrites + (evidence.layerWrites ?? 0);
|
|
487
|
+
if (totalWrites !== evidence.fileCount)
|
|
488
|
+
failures.push(`runtime payload write count is ${totalWrites} for ${evidence.fileCount} files`);
|
|
466
489
|
if (evidence.verificationReads > 0)
|
|
467
490
|
failures.push(`fresh certification reread ${evidence.verificationReads} candidate files`);
|
|
468
491
|
if (evidence.postNpmDurationMs > maximumPostNpmDurationMs)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MaterializedRelease } from "./release-store.js";
|
|
2
|
+
/** Import the exact immutable startup graph in a terminal-free bounded child process. */
|
|
3
|
+
export declare function warmMaterializedRelease(release: MaterializedRelease, environment: NodeJS.ProcessEnv, timeoutMs?: number): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { releaseEnvironment } from "./bootstrap.js";
|
|
4
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
5
|
+
/** Import the exact immutable startup graph in a terminal-free bounded child process. */
|
|
6
|
+
export async function warmMaterializedRelease(release, environment, timeoutMs = 15_000) {
|
|
7
|
+
const entry = resolve(release.releaseRoot, "bin", "warmup.js");
|
|
8
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
9
|
+
const child = spawn(process.execPath, [entry], {
|
|
10
|
+
env: { ...releaseEnvironment(environment, release), [PRODUCT_IDENTITY.environment.immutableWarmup]: "1" },
|
|
11
|
+
stdio: "ignore",
|
|
12
|
+
windowsHide: true,
|
|
13
|
+
});
|
|
14
|
+
let settled = false;
|
|
15
|
+
const finish = (error) => {
|
|
16
|
+
if (settled)
|
|
17
|
+
return;
|
|
18
|
+
settled = true;
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
if (error)
|
|
21
|
+
rejectPromise(error);
|
|
22
|
+
else
|
|
23
|
+
resolvePromise();
|
|
24
|
+
};
|
|
25
|
+
child.once("error", error => finish(error));
|
|
26
|
+
child.once("close", (code, signal) => finish(code === 0
|
|
27
|
+
? undefined
|
|
28
|
+
: new Error(`immutable startup warmup exited with ${code === null ? signal ?? "unknown status" : `status ${code}`}`)));
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
child.kill();
|
|
31
|
+
finish(new Error(`immutable startup warmup exceeded ${timeoutMs}ms`));
|
|
32
|
+
}, timeoutMs);
|
|
33
|
+
timer.unref?.();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./startup-runtime.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./startup-runtime.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
2
|
+
export type StartupPhase = "command-invoked" | "bootstrap-start" | "bootstrap-selected" | "guardian-start" | "guardian-connected" | "ui-entry" | "ui-modules-loaded" | "pi-services" | "resource-discovery" | "session-created" | "settings-loaded" | "first-input-ready-render";
|
|
3
|
+
export interface StartupPerformanceEvidence {
|
|
4
|
+
readonly profileId: "a1" | "pi";
|
|
5
|
+
readonly launchKind: "post-update" | "warm";
|
|
6
|
+
readonly events: readonly StartupTraceEvent[];
|
|
7
|
+
}
|
|
8
|
+
export interface StartupTraceEvent {
|
|
9
|
+
readonly schema: typeof PRODUCT_IDENTITY.evidence.startupTraceSchema;
|
|
10
|
+
readonly traceId: string;
|
|
11
|
+
readonly phase: StartupPhase;
|
|
12
|
+
readonly elapsedMs: number;
|
|
13
|
+
readonly processId: number;
|
|
14
|
+
readonly profileId: string;
|
|
15
|
+
readonly releaseId: string | null;
|
|
16
|
+
readonly dependencyLayerIds: readonly string[];
|
|
17
|
+
readonly nodeVersion: string;
|
|
18
|
+
readonly fileReadOperations: number;
|
|
19
|
+
}
|
|
20
|
+
/** Initialize opt-in startup tracing from a caller-provided evidence path. */
|
|
21
|
+
export declare function initializeStartupTrace(environment: NodeJS.ProcessEnv, profileId: string, startedAtMs?: number): void;
|
|
22
|
+
/** Append one redacted phase event; ordinary launches without trace context perform no I/O. */
|
|
23
|
+
export declare function markStartupPhase(environment: NodeJS.ProcessEnv, phase: StartupPhase): Promise<void>;
|
|
24
|
+
export declare function assertImmutableWarmupEnvironment(environment: NodeJS.ProcessEnv): void;
|
|
25
|
+
/** Enable the compile cache directly from launch environment without loading profile services. */
|
|
26
|
+
export declare function enableEnvironmentCompileCache(environment: NodeJS.ProcessEnv): string | null;
|
|
27
|
+
/** Enable Node's supported persistent compile cache in an immutable-identity namespace. */
|
|
28
|
+
export declare function enableStartupCompileCache(dataDir: string, releaseId: string | null, dependencyLayerIds: readonly string[]): string | null;
|
|
29
|
+
export declare function startupCompileCachePath(dataDir: string, releaseId: string | null, dependencyLayerIds: readonly string[]): string;
|
|
30
|
+
/** Retain current compile namespaces plus a bounded number of recent fallbacks. */
|
|
31
|
+
export declare function collectCompileCaches(dataDir: string, protectedPaths: readonly string[], keepRecent?: number): Promise<void>;
|
|
32
|
+
export declare function assertStartupPerformanceBudget(evidence: StartupPerformanceEvidence, budgets?: {
|
|
33
|
+
readonly postUpdateMs: number;
|
|
34
|
+
readonly warmMs: number;
|
|
35
|
+
}): void;
|
|
36
|
+
export declare function parseStartupTrace(source: string): readonly StartupTraceEvent[];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFile, mkdir, readdir, rm } from "node:fs/promises";
|
|
3
|
+
import { constants as moduleConstants, enableCompileCache } from "node:module";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { homedir, platform } from "node:os";
|
|
6
|
+
import { performance } from "node:perf_hooks";
|
|
7
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
8
|
+
/** Initialize opt-in startup tracing from a caller-provided evidence path. */
|
|
9
|
+
export function initializeStartupTrace(environment, profileId, startedAtMs = performance.timeOrigin + performance.now()) {
|
|
10
|
+
const configured = environment[PRODUCT_IDENTITY.environment.startupTrace];
|
|
11
|
+
if (!configured || parseContext(configured) !== null)
|
|
12
|
+
return;
|
|
13
|
+
const context = {
|
|
14
|
+
path: resolve(configured),
|
|
15
|
+
traceId: randomUUID(),
|
|
16
|
+
startedAtMs,
|
|
17
|
+
profileId,
|
|
18
|
+
releaseId: environment[PRODUCT_IDENTITY.environment.releaseId] ?? null,
|
|
19
|
+
dependencyLayerIds: parseLayerIds(environment[PRODUCT_IDENTITY.environment.releaseLayers]),
|
|
20
|
+
};
|
|
21
|
+
environment[PRODUCT_IDENTITY.environment.startupTrace] = JSON.stringify(context);
|
|
22
|
+
}
|
|
23
|
+
/** Append one redacted phase event; ordinary launches without trace context perform no I/O. */
|
|
24
|
+
export async function markStartupPhase(environment, phase) {
|
|
25
|
+
const context = parseContext(environment[PRODUCT_IDENTITY.environment.startupTrace]);
|
|
26
|
+
if (context === null)
|
|
27
|
+
return;
|
|
28
|
+
const event = {
|
|
29
|
+
schema: PRODUCT_IDENTITY.evidence.startupTraceSchema,
|
|
30
|
+
traceId: context.traceId,
|
|
31
|
+
phase,
|
|
32
|
+
elapsedMs: Math.max(0, performance.timeOrigin + performance.now() - context.startedAtMs),
|
|
33
|
+
processId: process.pid,
|
|
34
|
+
profileId: context.profileId,
|
|
35
|
+
releaseId: environment[PRODUCT_IDENTITY.environment.releaseId] ?? context.releaseId,
|
|
36
|
+
dependencyLayerIds: parseLayerIds(environment[PRODUCT_IDENTITY.environment.releaseLayers]).length > 0
|
|
37
|
+
? parseLayerIds(environment[PRODUCT_IDENTITY.environment.releaseLayers])
|
|
38
|
+
: context.dependencyLayerIds,
|
|
39
|
+
nodeVersion: process.version,
|
|
40
|
+
fileReadOperations: process.resourceUsage().fsRead,
|
|
41
|
+
};
|
|
42
|
+
await mkdir(dirname(context.path), { recursive: true, mode: 0o700 });
|
|
43
|
+
await appendFile(context.path, `${JSON.stringify(event)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
44
|
+
}
|
|
45
|
+
export function assertImmutableWarmupEnvironment(environment) {
|
|
46
|
+
if (environment[PRODUCT_IDENTITY.environment.immutableWarmup] !== "1") {
|
|
47
|
+
throw new Error("warmup entry is private to verified update activation");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Enable the compile cache directly from launch environment without loading profile services. */
|
|
51
|
+
export function enableEnvironmentCompileCache(environment) {
|
|
52
|
+
const home = environment.HOME ?? environment.USERPROFILE ?? homedir();
|
|
53
|
+
const dataDir = resolve(environment[PRODUCT_IDENTITY.environment.dataDir]
|
|
54
|
+
?? (platform() === "win32"
|
|
55
|
+
? resolve(environment.LOCALAPPDATA ?? home, PRODUCT_IDENTITY.state.windowsControlDirectory)
|
|
56
|
+
: resolve(environment.XDG_DATA_HOME ?? resolve(home, ".local", "share"), PRODUCT_IDENTITY.state.unixControlDirectory)));
|
|
57
|
+
return enableStartupCompileCache(dataDir, environment[PRODUCT_IDENTITY.environment.releaseId] ?? null, parseLayerIds(environment[PRODUCT_IDENTITY.environment.releaseLayers]));
|
|
58
|
+
}
|
|
59
|
+
/** Enable Node's supported persistent compile cache in an immutable-identity namespace. */
|
|
60
|
+
export function enableStartupCompileCache(dataDir, releaseId, dependencyLayerIds) {
|
|
61
|
+
try {
|
|
62
|
+
const path = startupCompileCachePath(dataDir, releaseId, dependencyLayerIds);
|
|
63
|
+
const result = enableCompileCache(path);
|
|
64
|
+
return result.status === moduleConstants.compileCacheStatus.FAILED ? null : result.directory ?? path;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function startupCompileCachePath(dataDir, releaseId, dependencyLayerIds) {
|
|
71
|
+
const layers = createHash("sha256").update(dependencyLayerIds.join("\0")).digest("hex").slice(0, 20);
|
|
72
|
+
const content = dependencyLayerIds.length > 0
|
|
73
|
+
? `layers-${layers}`
|
|
74
|
+
: `release-${(releaseId ?? "mutable").replace(/[^0-9A-Za-z.+_-]/g, "_")}`;
|
|
75
|
+
// Performance: stable layer paths share compiled dependency entries across product releases;
|
|
76
|
+
// Node's own cache key still isolates each release-specific module path and source bytes.
|
|
77
|
+
return resolve(dataDir, "cache", "compile", `${process.versions.modules ?? "node"}-${process.version.replace(/[^0-9A-Za-z.-]/g, "_")}-${content}`);
|
|
78
|
+
}
|
|
79
|
+
/** Retain current compile namespaces plus a bounded number of recent fallbacks. */
|
|
80
|
+
export async function collectCompileCaches(dataDir, protectedPaths, keepRecent = 2) {
|
|
81
|
+
const root = resolve(dataDir, "cache", "compile");
|
|
82
|
+
const protectedSet = new Set(protectedPaths.map(path => resolve(path)));
|
|
83
|
+
const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
|
|
84
|
+
const candidates = await Promise.all(entries.filter(entry => entry.isDirectory()).map(async (entry) => {
|
|
85
|
+
const path = resolve(root, entry.name);
|
|
86
|
+
const metadata = await import("node:fs/promises").then(fs => fs.stat(path));
|
|
87
|
+
return { path, mtimeMs: metadata.mtimeMs };
|
|
88
|
+
}));
|
|
89
|
+
const retainedRecent = new Set(candidates.filter(item => !protectedSet.has(item.path))
|
|
90
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs)
|
|
91
|
+
.slice(0, keepRecent)
|
|
92
|
+
.map(item => item.path));
|
|
93
|
+
for (const candidate of candidates) {
|
|
94
|
+
if (!protectedSet.has(candidate.path) && !retainedRecent.has(candidate.path))
|
|
95
|
+
await rm(candidate.path, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function assertStartupPerformanceBudget(evidence, budgets = { postUpdateMs: 5_000, warmMs: 3_000 }) {
|
|
99
|
+
const events = [...evidence.events].sort((left, right) => left.elapsedMs - right.elapsedMs);
|
|
100
|
+
const ready = events.findLast(event => event.phase === "first-input-ready-render");
|
|
101
|
+
if (!ready)
|
|
102
|
+
throw new Error(`startup budget failed for ${evidence.profileId}: first input-ready render was not recorded`);
|
|
103
|
+
const budget = evidence.launchKind === "post-update" ? budgets.postUpdateMs : budgets.warmMs;
|
|
104
|
+
if (ready.elapsedMs <= budget)
|
|
105
|
+
return;
|
|
106
|
+
const intervals = events.map((event, index) => ({
|
|
107
|
+
phase: event.phase,
|
|
108
|
+
durationMs: event.elapsedMs - (events[index - 1]?.elapsedMs ?? 0),
|
|
109
|
+
})).sort((left, right) => right.durationMs - left.durationMs);
|
|
110
|
+
throw new Error(`startup budget failed for ${evidence.profileId} ${evidence.launchKind}: ${Math.round(ready.elapsedMs)}ms exceeds ${budget}ms; dominant phases: ${intervals.slice(0, 3).map(item => `${item.phase} ${Math.round(item.durationMs)}ms`).join(", ")}`);
|
|
111
|
+
}
|
|
112
|
+
export function parseStartupTrace(source) {
|
|
113
|
+
const events = source.split(/\r?\n/).filter(Boolean).map(line => JSON.parse(line));
|
|
114
|
+
for (const event of events) {
|
|
115
|
+
if (event.schema !== PRODUCT_IDENTITY.evidence.startupTraceSchema || typeof event.traceId !== "string"
|
|
116
|
+
|| typeof event.phase !== "string" || !Number.isFinite(event.elapsedMs) || !Array.isArray(event.dependencyLayerIds)) {
|
|
117
|
+
throw new Error("startup trace contains an invalid event");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return events.sort((left, right) => left.elapsedMs - right.elapsedMs);
|
|
121
|
+
}
|
|
122
|
+
function parseContext(value) {
|
|
123
|
+
if (!value?.startsWith("{"))
|
|
124
|
+
return null;
|
|
125
|
+
try {
|
|
126
|
+
const context = JSON.parse(value);
|
|
127
|
+
if (typeof context.path !== "string" || typeof context.traceId !== "string" || !Number.isFinite(context.startedAtMs)
|
|
128
|
+
|| typeof context.profileId !== "string" || !Array.isArray(context.dependencyLayerIds))
|
|
129
|
+
return null;
|
|
130
|
+
return context;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function parseLayerIds(value) {
|
|
137
|
+
if (!value)
|
|
138
|
+
return [];
|
|
139
|
+
return [...new Set(value.split(",").filter(id => /^dependencies-[a-f0-9]{32}$/.test(id)))].sort();
|
|
140
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, resolveModelScopeWithDiagnostics, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { resolvePiProjectTrustPreflight, } from "./project-trust-preflight.js";
|
|
3
|
+
import { markStartupPhase } from "../../../foundation/startup/index.js";
|
|
3
4
|
/**
|
|
4
5
|
* Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
|
|
5
6
|
* into a scoped model list, keep the resolver's warnings (e.g. "No models match
|
|
@@ -41,7 +42,10 @@ export async function createPiRuntimeServicesAfterTrust(options) {
|
|
|
41
42
|
...(options.projectTrustPrompt === undefined ? {} : { prompt: options.projectTrustPrompt }),
|
|
42
43
|
});
|
|
43
44
|
const settingsManager = createSettingsManager(options.cwd, options.agentDir, trust.trusted);
|
|
45
|
+
await markStartupPhase(process.env, "settings-loaded");
|
|
44
46
|
const services = await createServices({ cwd: options.cwd, agentDir: options.agentDir, settingsManager });
|
|
47
|
+
await markStartupPhase(process.env, "pi-services");
|
|
48
|
+
await markStartupPhase(process.env, "resource-discovery");
|
|
45
49
|
return { services, trust };
|
|
46
50
|
}
|
|
47
51
|
export async function createPiRuntimeIntegration(options) {
|
|
@@ -66,6 +70,7 @@ export async function createPiRuntimeIntegration(options) {
|
|
|
66
70
|
...(modelScope.thinkingLevel && !hasExistingSession ? { thinkingLevel: modelScope.thinkingLevel } : {}),
|
|
67
71
|
...(modelScope.scopedModels.length > 0 ? { scopedModels: [...modelScope.scopedModels] } : {}),
|
|
68
72
|
});
|
|
73
|
+
await markStartupPhase(process.env, "session-created");
|
|
69
74
|
return {
|
|
70
75
|
...created,
|
|
71
76
|
services,
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "unsupported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-03T16:39:37.425Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"size":
|
|
11
|
+
"sha256": "7524bf568992517f50ed53196c861c5edeba0d7275633bb4735ab45bd5963a28",
|
|
12
|
+
"size": 356368
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
15
15
|
"language": "Rust",
|
|
Binary file
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-03T16:39:27.399Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "ee8a00eaaf79c707459bbbfb52518e9739314967049fbe5fa625f36ce33db9ee",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-03T16:40:47.022Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "2fe463dcdeaedfbc9df817ac61e973f27500e9527d05bd781553da89d6767926",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
declare const ENVIRONMENT_KEYS: readonly ["certificationTarball", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "fixture", "fixtureInput", "fixtureToken", "inputAcknowledgement", "internalPackaging", "launchProfile", "nativePi", "paneId", "piParityIntentionalMutation", "piPortRoot", "piSourceLedgerPath", "piSourceScanRoot", "probeTrace", "processGuardianPath", "profileHome", "protocolVersion", "releaseDigest", "releaseId", "releaseRoot", "releaseRunnerLabel", "runtimeDir", "structuredFlowLimits", "terminalArgumentsJson", "terminalExecutable", "terminalSessionId"];
|
|
1
|
+
declare const ENVIRONMENT_KEYS: readonly ["certificationTarball", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "fixture", "fixtureInput", "fixtureToken", "inputAcknowledgement", "immutableWarmup", "internalPackaging", "launchProfile", "nativePi", "paneId", "piParityIntentionalMutation", "piPortRoot", "piSourceLedgerPath", "piSourceScanRoot", "probeTrace", "processGuardianPath", "profileHome", "protocolVersion", "releaseDigest", "releaseId", "releaseLayers", "releaseRoot", "releaseRunnerLabel", "runtimeDir", "structuredFlowLimits", "startupTrace", "terminalArgumentsJson", "terminalExecutable", "terminalSessionId"];
|
|
2
2
|
declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
|
|
3
3
|
declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile"];
|
|
4
4
|
declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
|
|
5
5
|
declare const MANIFEST_KEYS: readonly ["releaseFilename", "packageFilename"];
|
|
6
6
|
declare const PROTOCOL_KEYS: readonly ["namespace", "controlEnvelope", "supervisorSchema", "nativeHostSchema", "structuredAgentSchema", "controlStoreSchema", "releaseCohortSchema", "updateJournalSchema"];
|
|
7
|
-
declare const EVIDENCE_KEYS: readonly ["nativeSpikeSchema", "terminalProvenanceSchema", "terminalProofSchema", "stableReleaseSchema", "previewReleaseSchema", "releaseCertificationSchema", "previewPlatformVerdictSchema", "piSourceLedgerSchema", "piComponentParitySchema", "piEventFrameParitySchema"];
|
|
7
|
+
declare const EVIDENCE_KEYS: readonly ["nativeSpikeSchema", "terminalProvenanceSchema", "terminalProofSchema", "stableReleaseSchema", "previewReleaseSchema", "releaseCertificationSchema", "previewPlatformVerdictSchema", "piSourceLedgerSchema", "piComponentParitySchema", "piEventFrameParitySchema", "startupTraceSchema", "dependencyLayerSchema", "dependencyLayerCertificationSchema", "runtimePayloadSchema"];
|
|
8
8
|
declare const ARTIFACT_KEYS: readonly ["cliEntry", "supervisorEntry", "guardianEntry", "uiEntry", "nativeExecutable", "nativeCrate", "processGuardianExecutable", "releaseTarballStem", "diagnosticStem"];
|
|
9
9
|
type StringRecord<Keys extends readonly string[]> = {
|
|
10
10
|
readonly [Key in Keys[number]]: string;
|