@evomap/evolver-proxy 2.0.0-beta.19 → 2.0.0-beta.22
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/dist/bin/evolver-proxy.d.ts +12 -0
- package/dist/bin/evolver-proxy.js +257 -56
- package/dist/daemon/proxyDaemon.d.ts +12 -0
- package/dist/daemon/proxyDaemon.js +313 -18
- package/dist/daemon/systemdNotifier.d.ts +2 -0
- package/dist/daemon/systemdNotifier.js +11 -1
- package/dist/llm/upstream.js +54 -7
- package/dist/private/accountAssetCompatibility.d.ts +1 -0
- package/dist/private/accountAssetCompatibility.js +3 -3
- package/dist/private/adapterLoader.js +4 -3
- package/dist/router/messagesRoute.js +9 -3
- package/dist/router/providerRoutes.js +7 -3
- package/dist/selfUpdate/bootstrap.d.ts +162 -0
- package/dist/selfUpdate/bootstrap.js +3524 -0
- package/dist/selfUpdate/bootstrapReadiness.d.ts +9 -0
- package/dist/selfUpdate/bootstrapReadiness.js +153 -0
- package/dist/selfUpdate/builtinKey.d.ts +4 -0
- package/dist/selfUpdate/builtinKey.js +16 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.d.ts +45 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.js +61 -0
- package/dist/selfUpdate/executor.d.ts +18 -7
- package/dist/selfUpdate/executor.js +159 -59
- package/dist/selfUpdate/failureCodes.d.ts +4 -0
- package/dist/selfUpdate/failureCodes.js +7 -0
- package/dist/selfUpdate/index.d.ts +2 -1
- package/dist/selfUpdate/index.js +2 -1
- package/dist/selfUpdate/migration.d.ts +158 -0
- package/dist/selfUpdate/migration.js +2672 -0
- package/dist/selfUpdate/policy.d.ts +19 -2
- package/dist/selfUpdate/policy.js +76 -2
- package/dist/selfUpdate/recoveryChildStartGate.d.ts +29 -0
- package/dist/selfUpdate/recoveryChildStartGate.js +319 -0
- package/dist/selfUpdate/releaseBinary.d.ts +3 -0
- package/dist/selfUpdate/releaseBinary.js +50 -4
- package/dist/selfUpdate/transaction.d.ts +8 -0
- package/dist/selfUpdate/transaction.js +166 -18
- package/dist/selfUpdate/unixController.d.ts +8 -0
- package/dist/selfUpdate/unixController.js +366 -38
- package/dist/selfUpdate/windowsController.d.ts +14 -2
- package/dist/selfUpdate/windowsController.js +484 -103
- package/dist/selfUpdate/windowsUpdater.d.ts +25 -0
- package/dist/selfUpdate/windowsUpdater.js +174 -7
- package/package.json +3 -3
|
@@ -38,6 +38,22 @@ export interface ApplyWindowsUpdaterOptions {
|
|
|
38
38
|
workerExecPath?: string;
|
|
39
39
|
platform?: NodeJS.Platform;
|
|
40
40
|
renameFn?: typeof rename;
|
|
41
|
+
/** Test seam; production always evaluates the native Windows owner/writer policy. */
|
|
42
|
+
assertHelperTrust?: WindowsUpdaterHelperTrust;
|
|
43
|
+
}
|
|
44
|
+
export interface WindowsUpdaterHelperTrustPaths {
|
|
45
|
+
stateDir: string;
|
|
46
|
+
directory: string;
|
|
47
|
+
helperPath: string;
|
|
48
|
+
pendingPath: string;
|
|
49
|
+
}
|
|
50
|
+
export type WindowsUpdaterHelperTrust = (paths: WindowsUpdaterHelperTrustPaths) => void | Promise<void>;
|
|
51
|
+
export interface RevalidateWindowsUpdaterHelperOptions {
|
|
52
|
+
stateDir: string;
|
|
53
|
+
helperPath?: string;
|
|
54
|
+
platform?: NodeJS.Platform;
|
|
55
|
+
/** Test seam; production always evaluates the native Windows owner/writer policy. */
|
|
56
|
+
assertHelperTrust?: WindowsUpdaterHelperTrust;
|
|
41
57
|
}
|
|
42
58
|
export interface BindWindowsManagedExecutableOptions {
|
|
43
59
|
stateDir: string;
|
|
@@ -55,6 +71,12 @@ export interface BoundWindowsManagedExecutable {
|
|
|
55
71
|
export declare function resolveWindowsUpdaterPaths(stateDirInput: string): WindowsUpdaterPaths;
|
|
56
72
|
/** Bind a fixed executable below the private state root without following directory links. */
|
|
57
73
|
export declare function bindWindowsManagedExecutable(options: BindWindowsManagedExecutableOptions): Promise<BoundWindowsManagedExecutable>;
|
|
74
|
+
/**
|
|
75
|
+
* Bind the fixed helper and verify the persisted executable generation before
|
|
76
|
+
* the lifecycle owner creates a worker process. The worker repeats this check
|
|
77
|
+
* after consuming fd4 and before any target mutation.
|
|
78
|
+
*/
|
|
79
|
+
export declare function revalidatePendingWindowsUpdaterHelper(options: RevalidateWindowsUpdaterHelperOptions): Promise<BoundWindowsManagedExecutable>;
|
|
58
80
|
/**
|
|
59
81
|
* Prepare a launcher-consumed update descriptor. This function never mutates
|
|
60
82
|
* the live executable and never spawns a competing relaunch process.
|
|
@@ -76,4 +98,7 @@ export declare function maybeRunWindowsUpdaterWorkerFromArgv(options?: {
|
|
|
76
98
|
env?: NodeJS.ProcessEnv;
|
|
77
99
|
platform?: NodeJS.Platform;
|
|
78
100
|
processExecPath?: string;
|
|
101
|
+
startupGateConsumed?: boolean;
|
|
102
|
+
/** Test seam; production always evaluates the native Windows owner/writer policy. */
|
|
103
|
+
assertHelperTrust?: WindowsUpdaterHelperTrust;
|
|
79
104
|
}): Promise<number | undefined>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
1
2
|
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
3
|
import { constants } from 'node:fs';
|
|
3
4
|
import { link, lstat, mkdir, open, realpath, rename, rm, } from 'node:fs/promises';
|
|
4
|
-
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from 'node:path';
|
|
5
|
+
import { basename, dirname, isAbsolute, join, parse, relative, resolve, win32 } from 'node:path';
|
|
5
6
|
import { SELF_UPDATE_FAILURE_CODES, SelfUpdateFailureError, selfUpdateFailure, } from './failureCodes.js';
|
|
6
7
|
export const WINDOWS_UPDATER_WORKER_ARG = '--evolver-windows-updater-worker';
|
|
7
8
|
const WORK_ITEM_SCHEMA_VERSION = 1;
|
|
@@ -9,6 +10,7 @@ const RESULT_SCHEMA_VERSION = 1;
|
|
|
9
10
|
const MAX_WORK_ITEM_BYTES = 64 * 1024;
|
|
10
11
|
const COPY_BUFFER_BYTES = 1024 * 1024;
|
|
11
12
|
const OPERATION_ID_PATTERN = /^[0-9a-f]{32}$/;
|
|
13
|
+
const HOST_WINDOWS_SYSTEM_ROOT = process.env['SystemRoot']?.trim() || 'C:\\Windows';
|
|
12
14
|
/** Paths are fixed so the stable controller never consumes descriptor-supplied executable paths. */
|
|
13
15
|
export function resolveWindowsUpdaterPaths(stateDirInput) {
|
|
14
16
|
const stateDir = assertAbsoluteCleanPath(stateDirInput, 'state_dir');
|
|
@@ -37,6 +39,28 @@ export async function bindWindowsManagedExecutable(options) {
|
|
|
37
39
|
}
|
|
38
40
|
return { stateDir, executablePath };
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Bind the fixed helper and verify the persisted executable generation before
|
|
44
|
+
* the lifecycle owner creates a worker process. The worker repeats this check
|
|
45
|
+
* after consuming fd4 and before any target mutation.
|
|
46
|
+
*/
|
|
47
|
+
export async function revalidatePendingWindowsUpdaterHelper(options) {
|
|
48
|
+
assertWindowsPlatform(options.platform ?? process.platform);
|
|
49
|
+
const requestedPaths = resolveWindowsUpdaterPaths(options.stateDir);
|
|
50
|
+
const bound = await bindWindowsManagedExecutable({
|
|
51
|
+
stateDir: options.stateDir,
|
|
52
|
+
executablePath: options.helperPath ?? requestedPaths.helperPath,
|
|
53
|
+
relativePath: ['windows-updater', 'updater.exe'],
|
|
54
|
+
label: 'helper_revalidation',
|
|
55
|
+
mismatchLabel: 'helper',
|
|
56
|
+
platform: options.platform,
|
|
57
|
+
});
|
|
58
|
+
const paths = resolveWindowsUpdaterPaths(bound.stateDir);
|
|
59
|
+
const workItem = await readWorkItem(paths.pendingPath);
|
|
60
|
+
await assertWindowsUpdaterHelperTrust({ ...paths, stateDir: bound.stateDir }, options.assertHelperTrust);
|
|
61
|
+
await assertHelperLaunchSnapshot(bound.executablePath, workItem.helper_identity);
|
|
62
|
+
return bound;
|
|
63
|
+
}
|
|
40
64
|
/**
|
|
41
65
|
* Prepare a launcher-consumed update descriptor. This function never mutates
|
|
42
66
|
* the live executable and never spawns a competing relaunch process.
|
|
@@ -99,6 +123,9 @@ export async function prepareWindowsExecutableSwap(options) {
|
|
|
99
123
|
});
|
|
100
124
|
}
|
|
101
125
|
const helperIdentity = await snapshotRegularFile(paths.helperPath, 'helper');
|
|
126
|
+
if (helperIdentity.nlink !== '1') {
|
|
127
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.COPY_FAILED, 'windows_updater_helper_unsafe');
|
|
128
|
+
}
|
|
102
129
|
if (!safeDigestEqual(helperIdentity.sha256, helperSourceIdentity.sha256)) {
|
|
103
130
|
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.COPY_FAILED, 'windows_updater_helper_digest_mismatch');
|
|
104
131
|
}
|
|
@@ -150,7 +177,7 @@ export async function applyPendingWindowsExecutableSwap(options = {}) {
|
|
|
150
177
|
let workItem;
|
|
151
178
|
try {
|
|
152
179
|
workItem = await readWorkItem(paths.pendingPath);
|
|
153
|
-
await validateWorkItem(workItem, paths, stateDir);
|
|
180
|
+
await validateWorkItem(workItem, paths, stateDir, options.assertHelperTrust);
|
|
154
181
|
}
|
|
155
182
|
catch (error) {
|
|
156
183
|
const result = {
|
|
@@ -219,11 +246,14 @@ export async function maybeRunWindowsUpdaterWorkerFromArgv(options = {}) {
|
|
|
219
246
|
return 64;
|
|
220
247
|
if (argv.length !== 2 || argv[0] !== 'proxy' || argv[1] !== WINDOWS_UPDATER_WORKER_ARG)
|
|
221
248
|
return 64;
|
|
249
|
+
if (options.startupGateConsumed !== true)
|
|
250
|
+
return 64;
|
|
222
251
|
try {
|
|
223
252
|
const result = await applyPendingWindowsExecutableSwap({
|
|
224
253
|
stateDir: options.env?.['EVOLVER_SELF_UPDATE_STATE_DIR']?.trim() || undefined,
|
|
225
254
|
platform: options.platform,
|
|
226
255
|
workerExecPath: options.processExecPath,
|
|
256
|
+
...(options.assertHelperTrust ? { assertHelperTrust: options.assertHelperTrust } : {}),
|
|
227
257
|
});
|
|
228
258
|
return result.status === 'completed' ? 0 : 1;
|
|
229
259
|
}
|
|
@@ -250,7 +280,7 @@ async function commitPreparedHelper(helperTempPath, helperPath, sourceIdentity)
|
|
|
250
280
|
}
|
|
251
281
|
await rename(helperTempPath, helperPath);
|
|
252
282
|
}
|
|
253
|
-
async function validateWorkItem(workItem, paths, stateDir) {
|
|
283
|
+
async function validateWorkItem(workItem, paths, stateDir, assertHelperTrust) {
|
|
254
284
|
if (!OPERATION_ID_PATTERN.test(workItem.operation_id)) {
|
|
255
285
|
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_operation_id_invalid');
|
|
256
286
|
}
|
|
@@ -276,7 +306,8 @@ async function validateWorkItem(workItem, paths, stateDir) {
|
|
|
276
306
|
workItem.target_path = targetPath;
|
|
277
307
|
workItem.backup_path = backupPath;
|
|
278
308
|
workItem.source_path = sourcePath;
|
|
279
|
-
await
|
|
309
|
+
await assertWindowsUpdaterHelperTrust({ ...paths, stateDir }, assertHelperTrust);
|
|
310
|
+
await assertHelperLaunchSnapshot(paths.helperPath, workItem.helper_identity);
|
|
280
311
|
await assertSnapshot(backupPath, workItem.backup_identity, 'backup');
|
|
281
312
|
await assertSnapshot(sourcePath, workItem.source_identity, 'source');
|
|
282
313
|
}
|
|
@@ -344,6 +375,16 @@ function parseIdentity(value) {
|
|
|
344
375
|
ctime_ns: requireString(value, 'ctime_ns'),
|
|
345
376
|
sha256: requireString(value, 'sha256'),
|
|
346
377
|
};
|
|
378
|
+
const generationKeys = ['birthtime_ns', 'nlink', 'mode'];
|
|
379
|
+
const suppliedGenerationKeys = generationKeys.filter((key) => value[key] !== undefined);
|
|
380
|
+
if (suppliedGenerationKeys.length !== 0 && suppliedGenerationKeys.length !== generationKeys.length) {
|
|
381
|
+
throw new Error('identity_generation');
|
|
382
|
+
}
|
|
383
|
+
if (suppliedGenerationKeys.length === generationKeys.length) {
|
|
384
|
+
identity.birthtime_ns = requireString(value, 'birthtime_ns');
|
|
385
|
+
identity.nlink = requireString(value, 'nlink');
|
|
386
|
+
identity.mode = requireString(value, 'mode');
|
|
387
|
+
}
|
|
347
388
|
assertSha256(identity.sha256, 'identity_sha256');
|
|
348
389
|
return identity;
|
|
349
390
|
}
|
|
@@ -379,6 +420,12 @@ async function assertSnapshot(path, expected, label) {
|
|
|
379
420
|
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, `windows_updater_${label}_changed`);
|
|
380
421
|
}
|
|
381
422
|
}
|
|
423
|
+
async function assertHelperLaunchSnapshot(path, expected) {
|
|
424
|
+
const actual = await snapshotRegularFile(path, 'helper');
|
|
425
|
+
if (actual.nlink !== '1' || !sameHelperLaunchIdentity(actual, expected)) {
|
|
426
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_helper_changed');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
382
429
|
async function copyRegularFileExclusive(sourcePath, destinationPath, expectedSource, mode) {
|
|
383
430
|
const source = await openNoFollow(sourcePath, constants.O_RDONLY);
|
|
384
431
|
let destination;
|
|
@@ -540,6 +587,88 @@ async function assertNoSymlinkedParent(path, label) {
|
|
|
540
587
|
}
|
|
541
588
|
}
|
|
542
589
|
}
|
|
590
|
+
async function assertWindowsUpdaterHelperTrust(paths, override) {
|
|
591
|
+
const pending = await lstat(paths.pendingPath, { bigint: true }).catch((error) => {
|
|
592
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_pending_unreadable', {
|
|
593
|
+
cause: error,
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
if (!pending.isFile() || pending.isSymbolicLink() || pending.nlink !== 1n) {
|
|
597
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_pending_unsafe');
|
|
598
|
+
}
|
|
599
|
+
try {
|
|
600
|
+
await (override ?? assertNativeWindowsUpdaterHelperTrust)(paths);
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
if (error instanceof SelfUpdateFailureError)
|
|
604
|
+
throw error;
|
|
605
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_helper_acl_untrusted', {
|
|
606
|
+
cause: error,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function assertNativeWindowsUpdaterHelperTrust(paths) {
|
|
611
|
+
if (process.platform !== 'win32')
|
|
612
|
+
return;
|
|
613
|
+
const checks = [
|
|
614
|
+
{ path: paths.helperPath, parentOnly: false },
|
|
615
|
+
{ path: paths.pendingPath, parentOnly: false },
|
|
616
|
+
];
|
|
617
|
+
const root = parse(paths.directory).root;
|
|
618
|
+
let current = paths.directory;
|
|
619
|
+
let leaf = true;
|
|
620
|
+
for (;;) {
|
|
621
|
+
checks.push({ path: current, parentOnly: !leaf });
|
|
622
|
+
leaf = false;
|
|
623
|
+
if (samePath(current, root))
|
|
624
|
+
break;
|
|
625
|
+
current = dirname(current);
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
execFileSync(trustedWindowsSystemExecutable('WindowsPowerShell\\v1.0\\powershell.exe'), ['-NoProfile', '-NonInteractive', '-Command', windowsUpdaterAclScript(checks)], { stdio: 'ignore', timeout: 10_000, windowsHide: true });
|
|
629
|
+
}
|
|
630
|
+
catch (error) {
|
|
631
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_helper_acl_untrusted', {
|
|
632
|
+
cause: error,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function windowsUpdaterAclScript(checks) {
|
|
637
|
+
const encodedChecks = Buffer.from(JSON.stringify(checks), 'utf8').toString('base64');
|
|
638
|
+
const d = String.fromCharCode(36);
|
|
639
|
+
return [
|
|
640
|
+
`${d}ErrorActionPreference = 'Stop'`,
|
|
641
|
+
'try {',
|
|
642
|
+
` ${d}json = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encodedChecks}'))`,
|
|
643
|
+
` ${d}checks = ${d}json | ConvertFrom-Json`,
|
|
644
|
+
` ${d}userSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value`,
|
|
645
|
+
` ${d}trustedInstaller = 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464'`,
|
|
646
|
+
` ${d}trustedOwners = @(${d}userSid, 'S-1-5-18', 'S-1-5-32-544', ${d}trustedInstaller)`,
|
|
647
|
+
` ${d}trustedWriters = @(${d}userSid, 'S-1-5-18', 'S-1-5-32-544', ${d}trustedInstaller, 'S-1-3-0', 'S-1-3-4')`,
|
|
648
|
+
` ${d}parentDanger = [System.Security.AccessControl.FileSystemRights]::Delete -bor [System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor [System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor [System.Security.AccessControl.FileSystemRights]::TakeOwnership`,
|
|
649
|
+
` ${d}contentDanger = [System.Security.AccessControl.FileSystemRights]::WriteData -bor [System.Security.AccessControl.FileSystemRights]::AppendData -bor [System.Security.AccessControl.FileSystemRights]::CreateFiles -bor [System.Security.AccessControl.FileSystemRights]::CreateDirectories`,
|
|
650
|
+
` foreach (${d}check in @(${d}checks)) {`,
|
|
651
|
+
` ${d}acl = Get-Acl -LiteralPath ${d}check.path`,
|
|
652
|
+
` ${d}owner = ${d}acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value`,
|
|
653
|
+
` if (${d}trustedOwners -notcontains ${d}owner) { exit 21 }`,
|
|
654
|
+
` ${d}danger = if (${d}check.parentOnly) { ${d}parentDanger } else { ${d}parentDanger -bor ${d}contentDanger }`,
|
|
655
|
+
` foreach (${d}rule in @(${d}acl.Access)) {`,
|
|
656
|
+
` if (${d}rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { continue }`,
|
|
657
|
+
` if ((${d}rule.PropagationFlags -band [System.Security.AccessControl.PropagationFlags]::InheritOnly) -ne 0) { continue }`,
|
|
658
|
+
` if ((${d}rule.FileSystemRights -band ${d}danger) -eq 0) { continue }`,
|
|
659
|
+
` try { ${d}sid = ${d}rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value } catch { exit 22 }`,
|
|
660
|
+
` if (${d}trustedWriters -notcontains ${d}sid) { exit 23 }`,
|
|
661
|
+
' }',
|
|
662
|
+
' }',
|
|
663
|
+
'} catch { exit 24 }',
|
|
664
|
+
].join('; ');
|
|
665
|
+
}
|
|
666
|
+
function trustedWindowsSystemExecutable(name) {
|
|
667
|
+
if (!win32.isAbsolute(HOST_WINDOWS_SYSTEM_ROOT) || /[\r\n\0]/.test(HOST_WINDOWS_SYSTEM_ROOT)) {
|
|
668
|
+
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, 'windows_updater_system_root_untrusted');
|
|
669
|
+
}
|
|
670
|
+
return win32.join(HOST_WINDOWS_SYSTEM_ROOT, 'System32', name);
|
|
671
|
+
}
|
|
543
672
|
function symlinkTraversalRoot(path) {
|
|
544
673
|
const parsedRoot = parse(path).root;
|
|
545
674
|
if (process.platform !== 'win32')
|
|
@@ -564,7 +693,10 @@ function assertStatIdentity(left, right, label) {
|
|
|
564
693
|
|| left.ino !== right.ino
|
|
565
694
|
|| left.size !== right.size
|
|
566
695
|
|| left.mtimeNs !== right.mtimeNs
|
|
567
|
-
|| left.ctimeNs !== right.ctimeNs
|
|
696
|
+
|| left.ctimeNs !== right.ctimeNs
|
|
697
|
+
|| left.birthtimeNs !== right.birthtimeNs
|
|
698
|
+
|| left.nlink !== right.nlink
|
|
699
|
+
|| left.mode !== right.mode) {
|
|
568
700
|
throw selfUpdateFailure(SELF_UPDATE_FAILURE_CODES.REPLACE_FAILED, `windows_updater_${label}`);
|
|
569
701
|
}
|
|
570
702
|
}
|
|
@@ -577,11 +709,13 @@ function assertDirectoryIdentity(left, right, label) {
|
|
|
577
709
|
}
|
|
578
710
|
}
|
|
579
711
|
function sameStatIdentity(stat, identity) {
|
|
580
|
-
|
|
712
|
+
const strictLegacyIdentity = stat.dev.toString() === identity.dev
|
|
581
713
|
&& stat.ino.toString() === identity.ino
|
|
582
714
|
&& stat.size.toString() === identity.size
|
|
583
715
|
&& stat.mtimeNs.toString() === identity.mtime_ns
|
|
584
716
|
&& stat.ctimeNs.toString() === identity.ctime_ns;
|
|
717
|
+
return strictLegacyIdentity
|
|
718
|
+
&& (!hasStrongGeneration(identity) || statMatchesStrongGeneration(stat, identity));
|
|
585
719
|
}
|
|
586
720
|
function identityFromStat(stat, digest) {
|
|
587
721
|
return {
|
|
@@ -590,15 +724,48 @@ function identityFromStat(stat, digest) {
|
|
|
590
724
|
size: stat.size.toString(),
|
|
591
725
|
mtime_ns: stat.mtimeNs.toString(),
|
|
592
726
|
ctime_ns: stat.ctimeNs.toString(),
|
|
727
|
+
birthtime_ns: stat.birthtimeNs.toString(),
|
|
728
|
+
nlink: stat.nlink.toString(),
|
|
729
|
+
mode: stat.mode.toString(),
|
|
593
730
|
sha256: digest,
|
|
594
731
|
};
|
|
595
732
|
}
|
|
596
733
|
function sameIdentity(left, right) {
|
|
597
|
-
|
|
734
|
+
const strictLegacyIdentity = sameContent(left, right)
|
|
598
735
|
&& left.dev === right.dev
|
|
599
736
|
&& left.ino === right.ino
|
|
600
737
|
&& left.mtime_ns === right.mtime_ns
|
|
601
738
|
&& left.ctime_ns === right.ctime_ns;
|
|
739
|
+
return strictLegacyIdentity
|
|
740
|
+
&& (!hasStrongGeneration(right) || sameStrongGeneration(left, right));
|
|
741
|
+
}
|
|
742
|
+
function sameHelperLaunchIdentity(actual, expected) {
|
|
743
|
+
if (!hasStrongGeneration(expected))
|
|
744
|
+
return sameIdentity(actual, expected);
|
|
745
|
+
return expected.nlink === '1'
|
|
746
|
+
&& sameContent(actual, expected)
|
|
747
|
+
&& actual.dev === expected.dev
|
|
748
|
+
&& actual.ino === expected.ino
|
|
749
|
+
&& actual.mtime_ns === expected.mtime_ns
|
|
750
|
+
&& sameStrongGeneration(actual, expected);
|
|
751
|
+
}
|
|
752
|
+
function hasStrongGeneration(identity) {
|
|
753
|
+
return identity.birthtime_ns !== undefined
|
|
754
|
+
&& identity.nlink !== undefined
|
|
755
|
+
&& identity.mode !== undefined;
|
|
756
|
+
}
|
|
757
|
+
function sameStrongGeneration(left, right) {
|
|
758
|
+
return hasStrongGeneration(left)
|
|
759
|
+
&& hasStrongGeneration(right)
|
|
760
|
+
&& left.birthtime_ns === right.birthtime_ns
|
|
761
|
+
&& left.nlink === right.nlink
|
|
762
|
+
&& left.mode === right.mode;
|
|
763
|
+
}
|
|
764
|
+
function statMatchesStrongGeneration(stat, identity) {
|
|
765
|
+
return hasStrongGeneration(identity)
|
|
766
|
+
&& stat.birthtimeNs.toString() === identity.birthtime_ns
|
|
767
|
+
&& stat.nlink.toString() === identity.nlink
|
|
768
|
+
&& stat.mode.toString() === identity.mode;
|
|
602
769
|
}
|
|
603
770
|
function sameContent(left, right) {
|
|
604
771
|
return left.size === right.size && safeDigestEqual(left.sha256, right.sha256);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver-proxy",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@aws-sdk/client-bedrock-runtime": "^3.1053.0",
|
|
32
|
-
"@evomap/evolver-adapter-public": "2.0.0-beta.
|
|
33
|
-
"@evomap/evolver-core": "2.0.0-beta.
|
|
32
|
+
"@evomap/evolver-adapter-public": "2.0.0-beta.22",
|
|
33
|
+
"@evomap/evolver-core": "2.0.0-beta.22"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|