agentera 3.0.0-dev.32 → 3.0.0-dev.39
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/bundle/CHANGELOG.md +75 -5
- package/bundle/references/adapters/package-publication.json +43 -0
- package/bundle/references/adapters/package-surface-characterization.md +12 -0
- package/bundle/references/analysis/evidence-tier-authority.yaml +23 -21
- package/bundle/references/analysis/verification-policy.yaml +1 -0
- package/bundle/references/artifacts/artifact-registry-interface-model.yaml +11 -2
- package/bundle/references/artifacts/glossary-entry-contract.yaml +1241 -0
- package/bundle/references/artifacts/state-storage-authority.yaml +149 -80
- package/bundle/references/cli/agent-ready-state-contract.yaml +84 -2
- package/bundle/references/cli/audience-namespace-cli-migration.yaml +12 -1
- package/bundle/references/cli/prime-consumer-compatibility.yaml +7 -1
- package/bundle/skills/agentera/SKILL.md +26 -26
- package/bundle/skills/agentera/capabilities/audit/schemas/validation.yaml +26 -8
- package/bundle/skills/agentera/capabilities/build/schemas/artifacts.yaml +21 -4
- package/bundle/skills/agentera/capabilities/build/schemas/exit.yaml +5 -4
- package/bundle/skills/agentera/capabilities/build/schemas/validation.yaml +67 -3
- package/bundle/skills/agentera/capabilities/discuss/schemas/artifacts.yaml +3 -2
- package/bundle/skills/agentera/capabilities/discuss/schemas/validation.yaml +64 -0
- package/bundle/skills/agentera/capabilities/plan/schemas/artifacts.yaml +4 -1
- package/bundle/skills/agentera/capabilities/plan/schemas/validation.yaml +30 -0
- package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +5 -2
- package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +3 -2
- package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +30 -0
- package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +82 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +19 -4
- package/bundle/skills/agentera/schemas/artifacts/progress.yaml +92 -0
- package/dist/analytics/extractCorpus/evidenceTiers.js +46 -3
- package/dist/analytics/extractCorpus/evidenceTiers.js.map +1 -1
- package/dist/analytics/extractCorpus/tierReader.js +1 -1
- package/dist/analytics/extractCorpus/tierReader.js.map +1 -1
- package/dist/analytics/glossaryAdviceResolution.js +167 -0
- package/dist/analytics/glossaryAdviceResolution.js.map +1 -0
- package/dist/analytics/glossaryInputAcquisition.js +320 -0
- package/dist/analytics/glossaryInputAcquisition.js.map +1 -0
- package/dist/analytics/personalGlossaryAdmission.js +134 -0
- package/dist/analytics/personalGlossaryAdmission.js.map +1 -0
- package/dist/analytics/personalGlossaryProfile.js +219 -0
- package/dist/analytics/personalGlossaryProfile.js.map +1 -0
- package/dist/audit/terminologyDrift.js +282 -0
- package/dist/audit/terminologyDrift.js.map +1 -0
- package/dist/capabilities/audit/instructions.js +2 -1
- package/dist/capabilities/audit/instructions.js.map +1 -1
- package/dist/capabilities/build/instructions.js +41 -2
- package/dist/capabilities/build/instructions.js.map +1 -1
- package/dist/capabilities/discuss/instructions.js +14 -1
- package/dist/capabilities/discuss/instructions.js.map +1 -1
- package/dist/capabilities/index.js +1 -1
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/orchestrate/instructions.js +2 -2
- package/dist/capabilities/plan/instructions.js +19 -2
- package/dist/capabilities/plan/instructions.js.map +1 -1
- package/dist/capabilities/profile/instructions.js +67 -1
- package/dist/capabilities/profile/instructions.js.map +1 -1
- package/dist/cli/capabilityContext/bespoke.js +4 -3
- package/dist/cli/capabilityContext/bespoke.js.map +1 -1
- package/dist/cli/capabilityContext/build.js +77 -24
- package/dist/cli/capabilityContext/build.js.map +1 -1
- package/dist/cli/capabilityContext/closeout.js +2 -2
- package/dist/cli/capabilityContext/closeout.js.map +1 -1
- package/dist/cli/capabilityContext/evidence.js +2 -6
- package/dist/cli/capabilityContext/evidence.js.map +1 -1
- package/dist/cli/capabilityContext/planState.js +59 -78
- package/dist/cli/capabilityContext/planState.js.map +1 -1
- package/dist/cli/capabilityContext/projectVerification.js +410 -0
- package/dist/cli/capabilityContext/projectVerification.js.map +1 -0
- package/dist/cli/capabilityContext/startup.js +21 -4
- package/dist/cli/capabilityContext/startup.js.map +1 -1
- package/dist/cli/commands/glossaryAdvice.js +139 -0
- package/dist/cli/commands/glossaryAdvice.js.map +1 -0
- package/dist/cli/commands/personalGlossary.js +177 -0
- package/dist/cli/commands/personalGlossary.js.map +1 -0
- package/dist/cli/commands/prime/buildExecutionRequest.js +165 -0
- package/dist/cli/commands/prime/buildExecutionRequest.js.map +1 -0
- package/dist/cli/commands/prime/collectEntityOrientation.js +18 -2
- package/dist/cli/commands/prime/collectEntityOrientation.js.map +1 -1
- package/dist/cli/commands/prime/collectOrientationState.js +7 -1
- package/dist/cli/commands/prime/collectOrientationState.js.map +1 -1
- package/dist/cli/commands/prime/orientationOutput.js +12 -2
- package/dist/cli/commands/prime/orientationOutput.js.map +1 -1
- package/dist/cli/commands/prime.js +37 -2
- package/dist/cli/commands/prime.js.map +1 -1
- package/dist/cli/commands/profileGrounding.js +77 -0
- package/dist/cli/commands/profileGrounding.js.map +1 -0
- package/dist/cli/commands/query.js +14 -4
- package/dist/cli/commands/query.js.map +1 -1
- package/dist/cli/commands/schema.js +10 -0
- package/dist/cli/commands/schema.js.map +1 -1
- package/dist/cli/commands/state/write.js +4 -1
- package/dist/cli/commands/state/write.js.map +1 -1
- package/dist/cli/dispatch/lifecycle.js +45 -0
- package/dist/cli/dispatch/lifecycle.js.map +1 -1
- package/dist/cli/dispatch/prime.js +22 -1
- package/dist/cli/dispatch/prime.js.map +1 -1
- package/dist/cli/dispatch/shared.js +1 -1
- package/dist/cli/dispatch/shared.js.map +1 -1
- package/dist/cli/help.js +23 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/migrationRequired.js +2 -0
- package/dist/cli/migrationRequired.js.map +1 -1
- package/dist/cli/orientation/attention.js +3 -1
- package/dist/cli/orientation/attention.js.map +1 -1
- package/dist/cli/orientation.js +2 -2
- package/dist/cli/orientation.js.map +1 -1
- package/dist/registries/artifactProtocolIds.js +2 -0
- package/dist/registries/artifactProtocolIds.js.map +1 -1
- package/dist/registries/artifactRegistry.js +2 -0
- package/dist/registries/artifactRegistry.js.map +1 -1
- package/dist/registries/evidenceTierContract.js +26 -16
- package/dist/registries/evidenceTierContract.js.map +1 -1
- package/dist/registries/glossaryAdviceContract.js +79 -0
- package/dist/registries/glossaryAdviceContract.js.map +1 -0
- package/dist/registries/glossaryCaveatContract.js +68 -0
- package/dist/registries/glossaryCaveatContract.js.map +1 -0
- package/dist/registries/glossaryConsumerContractValidation.js +792 -0
- package/dist/registries/glossaryConsumerContractValidation.js.map +1 -0
- package/dist/registries/glossaryEntryContract.js +747 -0
- package/dist/registries/glossaryEntryContract.js.map +1 -0
- package/dist/registries/glossaryTermIdentity.js +18 -0
- package/dist/registries/glossaryTermIdentity.js.map +1 -0
- package/dist/registries/glossaryTermOccurrence.js +21 -0
- package/dist/registries/glossaryTermOccurrence.js.map +1 -0
- package/dist/state/changelog.js +222 -0
- package/dist/state/changelog.js.map +1 -0
- package/dist/state/decisionEntities.js +18 -11
- package/dist/state/decisionEntities.js.map +1 -1
- package/dist/state/decisionMigrationProvenance.js +84 -1
- package/dist/state/decisionMigrationProvenance.js.map +1 -1
- package/dist/state/decisionOverlay.js +9 -32
- package/dist/state/decisionOverlay.js.map +1 -1
- package/dist/state/decisionRevision.js +69 -444
- package/dist/state/decisionRevision.js.map +1 -1
- package/dist/state/decisionRevisionMigration.js +90 -0
- package/dist/state/decisionRevisionMigration.js.map +1 -0
- package/dist/state/directRetrieval.js +3 -52
- package/dist/state/directRetrieval.js.map +1 -1
- package/dist/state/entityCutover.js +6 -6
- package/dist/state/entityCutover.js.map +1 -1
- package/dist/state/entityGlossaryAuthority.js +14 -0
- package/dist/state/entityGlossaryAuthority.js.map +1 -0
- package/dist/state/entityMigrationIdentity.js +6 -0
- package/dist/state/entityMigrationIdentity.js.map +1 -0
- package/dist/state/entityMigrationPreview.js +14 -23
- package/dist/state/entityMigrationPreview.js.map +1 -1
- package/dist/state/entityPublicationContext.js +374 -71
- package/dist/state/entityPublicationContext.js.map +1 -1
- package/dist/state/entityStorage.js +45 -29
- package/dist/state/entityStorage.js.map +1 -1
- package/dist/state/healthEntities.js +2 -4
- package/dist/state/healthEntities.js.map +1 -1
- package/dist/state/listRetrieval.js +7 -38
- package/dist/state/listRetrieval.js.map +1 -1
- package/dist/state/migrationSourceBinding.js +1 -1
- package/dist/state/migrationSourceBinding.js.map +1 -1
- package/dist/state/objectiveExperimentEntities.js +9 -9
- package/dist/state/objectiveExperimentEntities.js.map +1 -1
- package/dist/state/planEntities.js +57 -16
- package/dist/state/planEntities.js.map +1 -1
- package/dist/state/progressEntities.js +177 -39
- package/dist/state/progressEntities.js.map +1 -1
- package/dist/state/progressGlossaryCaveat.js +169 -0
- package/dist/state/progressGlossaryCaveat.js.map +1 -0
- package/dist/state/progressPublicationOrder.js +20 -0
- package/dist/state/progressPublicationOrder.js.map +1 -0
- package/dist/state/safeProjectFile.js +4 -1
- package/dist/state/safeProjectFile.js.map +1 -1
- package/dist/state/startupProjection.js +2 -70
- package/dist/state/startupProjection.js.map +1 -1
- package/dist/state/stateStorageAuthority.js +48 -0
- package/dist/state/stateStorageAuthority.js.map +1 -0
- package/dist/state/summarySourceRowAuthority.js +1 -1
- package/dist/state/summarySourceRowAuthority.js.map +1 -1
- package/dist/state/todoDocsEntities.js +8 -9
- package/dist/state/todoDocsEntities.js.map +1 -1
- package/dist/state/write/explain.js +101 -92
- package/dist/state/write/explain.js.map +1 -1
- package/dist/state/write/glossaryPublication.js +264 -0
- package/dist/state/write/glossaryPublication.js.map +1 -0
- package/dist/state/write/index.js +1 -0
- package/dist/state/write/index.js.map +1 -1
- package/dist/state/write/operations.js +25 -27
- package/dist/state/write/operations.js.map +1 -1
- package/dist/state/write/planEvaluation.js +6 -2
- package/dist/state/write/planEvaluation.js.map +1 -1
- package/dist/state/write/transaction.js +7 -4
- package/dist/state/write/transaction.js.map +1 -1
- package/dist/upgrade/upgradeOrchestrator.js +15 -10
- package/dist/upgrade/upgradeOrchestrator.js.map +1 -1
- package/dist/validate/crossCapability.js +3 -0
- package/dist/validate/crossCapability.js.map +1 -1
- package/dist/validate/v1LegacyCruft.js +141 -0
- package/dist/validate/v1LegacyCruft.js.map +1 -0
- package/dist/validate/vocabularyAuthority.js +2 -0
- package/dist/validate/vocabularyAuthority.js.map +1 -1
- package/package.json +4 -3
- package/dist/state/decisionRevisionPublication.js +0 -532
- package/dist/state/decisionRevisionPublication.js.map +0 -1
|
@@ -7,6 +7,7 @@ const DIRECTORY_FLAGS = fs.constants.O_RDONLY
|
|
|
7
7
|
| (fs.constants.O_DIRECTORY ?? 0)
|
|
8
8
|
| (fs.constants.O_NOFOLLOW ?? 0);
|
|
9
9
|
const FILE_FLAGS = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0);
|
|
10
|
+
const RECOVERY_IGNORE_BYTES = "*\n!.gitignore\n";
|
|
10
11
|
function samePublished(left, right) {
|
|
11
12
|
return left.dev === right.dev
|
|
12
13
|
&& left.ino === right.ino
|
|
@@ -75,6 +76,9 @@ function matchesPublished(fd, expected) {
|
|
|
75
76
|
return false;
|
|
76
77
|
return createHash("sha256").update(readDescriptor(fd)).digest("hex") === expected.sha256;
|
|
77
78
|
}
|
|
79
|
+
function matchesExpected(fd, expected) {
|
|
80
|
+
return Buffer.isBuffer(expected) ? readDescriptor(fd).equals(expected) : matchesPublished(fd, expected);
|
|
81
|
+
}
|
|
78
82
|
function openMatches(parentFd, name, expectedFd, flags) {
|
|
79
83
|
let observed;
|
|
80
84
|
try {
|
|
@@ -89,6 +93,34 @@ function openMatches(parentFd, name, expectedFd, flags) {
|
|
|
89
93
|
fs.closeSync(observed);
|
|
90
94
|
}
|
|
91
95
|
}
|
|
96
|
+
function entryExists(parentFd, name) {
|
|
97
|
+
let fd;
|
|
98
|
+
try {
|
|
99
|
+
fd = fs.openSync(fdPath(parentFd, name), FILE_FLAGS);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return error.code !== "ENOENT";
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
if (fd !== undefined)
|
|
107
|
+
fs.closeSync(fd);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function pathBytesEqual(parentFd, name, expected) {
|
|
111
|
+
let fd;
|
|
112
|
+
try {
|
|
113
|
+
fd = fs.openSync(fdPath(parentFd, name), FILE_FLAGS);
|
|
114
|
+
return readDescriptor(fd).equals(expected);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
if (fd !== undefined)
|
|
121
|
+
fs.closeSync(fd);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
92
124
|
function syncDirectory(fd) {
|
|
93
125
|
fs.fsyncSync(fd);
|
|
94
126
|
}
|
|
@@ -495,26 +527,29 @@ export class EntityPublicationContext {
|
|
|
495
527
|
}
|
|
496
528
|
}
|
|
497
529
|
}
|
|
498
|
-
replaceExisting(relativeTarget, expectedBytes, bytes) {
|
|
499
|
-
return this.replaceOwned(relativeTarget, expectedBytes, bytes);
|
|
530
|
+
replaceExisting(relativeTarget, expectedBytes, bytes, maxEntityBytes) {
|
|
531
|
+
return this.replaceOwned(relativeTarget, expectedBytes, bytes, maxEntityBytes);
|
|
500
532
|
}
|
|
501
|
-
restoreExact(relativeTarget, expected, bytes) {
|
|
533
|
+
restoreExact(relativeTarget, expected, bytes, maxEntityBytes) {
|
|
502
534
|
// Recovery stays bound to the pinned root and exact archived target even when context invalidation caused the primary failure.
|
|
503
|
-
return this.replaceOwned(relativeTarget, expected, bytes, false);
|
|
535
|
+
return this.replaceOwned(relativeTarget, expected, bytes, maxEntityBytes, false);
|
|
504
536
|
}
|
|
505
|
-
replaceOwned(relativeTarget, expected, bytes, verifyContext = true) {
|
|
537
|
+
replaceOwned(relativeTarget, expected, bytes, maxEntityBytes, verifyContext = true) {
|
|
506
538
|
const segments = relativeTarget.split(path.sep).filter(Boolean);
|
|
507
539
|
if (segments.length < 2 || path.isAbsolute(relativeTarget) || segments.includes("..") || segments.includes("."))
|
|
508
540
|
throw new Error(`unsafe entity replacement target '${relativeTarget}'`);
|
|
509
541
|
const directories = [];
|
|
510
542
|
let targetFd;
|
|
511
543
|
let stageFd;
|
|
544
|
+
let backupFd;
|
|
512
545
|
let stageName;
|
|
513
546
|
let backupName;
|
|
514
547
|
let displacedName;
|
|
515
548
|
let displacedFd;
|
|
516
|
-
let
|
|
517
|
-
let
|
|
549
|
+
let displacementActive = false;
|
|
550
|
+
let committedResult;
|
|
551
|
+
let recovery;
|
|
552
|
+
let baselineBytes;
|
|
518
553
|
try {
|
|
519
554
|
let parentFd = this.rootFd;
|
|
520
555
|
for (const name of segments.slice(0, -1)) {
|
|
@@ -527,93 +562,360 @@ export class EntityPublicationContext {
|
|
|
527
562
|
targetFd = fs.openSync(fdPath(directoryFd, targetName), FILE_FLAGS);
|
|
528
563
|
this.assertBoundary(directories, undefined, { name: targetName, fd: targetFd }, verifyContext);
|
|
529
564
|
const previousBytes = readDescriptor(targetFd);
|
|
530
|
-
|
|
565
|
+
baselineBytes = previousBytes;
|
|
566
|
+
if (!matchesExpected(targetFd, expected))
|
|
531
567
|
throw new Error(`entity '${relativeTarget}' ownership changed before replacement; preserve both changes and retry explicitly`);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
568
|
+
if (!Number.isSafeInteger(maxEntityBytes) || maxEntityBytes < 1 || previousBytes.length > maxEntityBytes)
|
|
569
|
+
throw new Error(`entity '${relativeTarget}' exact ${previousBytes.length}-byte baseline exceeds the authority-owned ${maxEntityBytes}-byte recovery limit`);
|
|
570
|
+
recovery = this.createRecoveryAttempt(directoryFd);
|
|
571
|
+
backupName = "original.previous";
|
|
572
|
+
backupFd = fs.openSync(fdPath(recovery.attemptFd, backupName), fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
573
|
+
fs.writeFileSync(backupFd, previousBytes);
|
|
574
|
+
fs.fsyncSync(backupFd);
|
|
575
|
+
const backupStat = fs.fstatSync(backupFd, { bigint: true });
|
|
576
|
+
const attemptStat = fs.fstatSync(recovery.attemptFd, { bigint: true });
|
|
577
|
+
if (!backupStat.isFile() || backupStat.dev !== attemptStat.dev || backupStat.uid !== attemptStat.uid || backupStat.nlink !== 1n || (backupStat.mode & 63n) !== 0n || !openMatches(recovery.attemptFd, backupName, backupFd, FILE_FLAGS) || !readDescriptor(backupFd).equals(previousBytes))
|
|
578
|
+
throw new Error(`entity '${relativeTarget}' exact prior-byte snapshot could not be verified before replacement`);
|
|
579
|
+
stageName = "replacement.tmp";
|
|
580
|
+
stageFd = fs.openSync(fdPath(recovery.attemptFd, stageName), fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
538
581
|
fs.writeFileSync(stageFd, bytes, "utf8");
|
|
539
582
|
fs.fsyncSync(stageFd);
|
|
540
|
-
this.assertBoundary(directories,
|
|
541
|
-
if (
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
583
|
+
this.assertBoundary(directories, undefined, { name: targetName, fd: targetFd }, verifyContext);
|
|
584
|
+
if (!openMatches(recovery.attemptFd, stageName, stageFd, FILE_FLAGS) || !openMatches(recovery.attemptFd, backupName, backupFd, FILE_FLAGS) || !readDescriptor(backupFd).equals(previousBytes))
|
|
585
|
+
throw new Error(`entity '${relativeTarget}' private recovery attempt changed before displacement`);
|
|
586
|
+
displacedName = "original.displaced";
|
|
587
|
+
fs.renameSync(fdPath(directoryFd, targetName), fdPath(recovery.attemptFd, displacedName));
|
|
588
|
+
displacementActive = true;
|
|
589
|
+
displacedFd = fs.openSync(fdPath(recovery.attemptFd, displacedName), FILE_FLAGS);
|
|
590
|
+
if (!openMatches(recovery.attemptFd, displacedName, targetFd, FILE_FLAGS) || !matchesExpected(displacedFd, expected))
|
|
591
|
+
throw new Error(`entity '${relativeTarget}' ownership changed after staging; competing bytes were preserved at the target`);
|
|
592
|
+
try {
|
|
593
|
+
fs.linkSync(fdPath(recovery.attemptFd, stageName), fdPath(directoryFd, targetName));
|
|
545
594
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (typeof expected === "string" || !matchesPublished(displacedFd, expected)) {
|
|
551
|
-
try {
|
|
552
|
-
fs.linkSync(fdPath(directoryFd, displacedName), fdPath(directoryFd, targetName));
|
|
553
|
-
if (!openMatches(directoryFd, targetName, displacedFd, FILE_FLAGS))
|
|
554
|
-
throw new Error("competing entity could not be returned to its pathname");
|
|
555
|
-
syncDirectory(directoryFd);
|
|
556
|
-
}
|
|
557
|
-
catch (restoreError) {
|
|
558
|
-
throw new Error(`entity '${relativeTarget}' ownership changed immediately before restoration; competing bytes remain at '${displacedName}'`, { cause: restoreError });
|
|
559
|
-
}
|
|
560
|
-
throw new Error(`entity '${relativeTarget}' ownership changed immediately before restoration; competing bytes were preserved at the target and '${displacedName}'`);
|
|
561
|
-
}
|
|
562
|
-
displacedOwned = true;
|
|
563
|
-
try {
|
|
564
|
-
fs.linkSync(fdPath(directoryFd, stageName), fdPath(directoryFd, targetName));
|
|
565
|
-
}
|
|
566
|
-
catch (publishError) {
|
|
567
|
-
if (publishError.code === "EEXIST")
|
|
568
|
-
throw new Error(`entity '${relativeTarget}' ownership changed during no-clobber restoration publication; competing bytes were preserved`, { cause: publishError });
|
|
569
|
-
throw publishError;
|
|
570
|
-
}
|
|
571
|
-
replacementVisible = true;
|
|
572
|
-
this.removeOwnedFile(directoryFd, stageName, stageFd);
|
|
573
|
-
stageName = undefined;
|
|
574
|
-
this.removeOwnedFile(directoryFd, displacedName, targetFd);
|
|
575
|
-
displacedName = undefined;
|
|
595
|
+
catch (publishError) {
|
|
596
|
+
if (publishError.code === "EEXIST")
|
|
597
|
+
throw new Error(`entity '${relativeTarget}' ownership changed during no-clobber publication; competing target bytes were preserved`, { cause: publishError });
|
|
598
|
+
throw publishError;
|
|
576
599
|
}
|
|
600
|
+
if (!this.removeOwnedFile(recovery.attemptFd, stageName, stageFd))
|
|
601
|
+
throw new Error(`entity '${relativeTarget}' replacement stage changed before cleanup`);
|
|
602
|
+
stageName = undefined;
|
|
577
603
|
syncDirectory(directoryFd);
|
|
604
|
+
syncDirectory(recovery.attemptFd);
|
|
578
605
|
this.assertBoundary(directories, undefined, { name: targetName, fd: stageFd }, verifyContext);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
606
|
+
if (!openMatches(recovery.attemptFd, displacedName, targetFd, FILE_FLAGS) || !matchesExpected(displacedFd, expected))
|
|
607
|
+
throw new Error(`entity '${relativeTarget}' ownership changed through the publication boundary; competing bytes remain recoverable`);
|
|
608
|
+
committedResult = { previousBytes: previousBytes.toString("utf8"), publishedIdentity: publishedIdentity(stageFd) };
|
|
609
|
+
this.cleanupCommittedReplacement(recovery.attemptFd, [[displacedName, targetFd], [backupName, backupFd]]);
|
|
610
|
+
displacementActive = false;
|
|
611
|
+
return committedResult;
|
|
582
612
|
}
|
|
583
613
|
catch (error) {
|
|
614
|
+
if (committedResult)
|
|
615
|
+
return committedResult;
|
|
584
616
|
const directoryFd = directories.at(-1)?.fd;
|
|
585
|
-
if (
|
|
586
|
-
|
|
587
|
-
const ownsPrior = backupName !== undefined && openMatches(directoryFd, backupName, targetFd, FILE_FLAGS);
|
|
588
|
-
if (ownsReplacement && ownsPrior) {
|
|
589
|
-
fs.renameSync(fdPath(directoryFd, backupName), fdPath(directoryFd, segments.at(-1)));
|
|
590
|
-
backupName = undefined;
|
|
591
|
-
syncDirectory(directoryFd);
|
|
592
|
-
}
|
|
593
|
-
else if (ownsReplacement) {
|
|
594
|
-
throw new Error(`entity '${relativeTarget}' replacement failed and its descriptor-pinned prior value could not be restored`, { cause: error });
|
|
595
|
-
}
|
|
617
|
+
if (displacementActive && directoryFd !== undefined && targetFd !== undefined && recovery) {
|
|
618
|
+
throw this.recoverDisplacedReplacement({ directoryFd, recoveryFd: recovery.attemptFd, recoveryPath: recovery.relativePath, relativeTarget, baselineBytes: baselineBytes, backupFd, stageFd, stageName, backupName, displacedFd, displacedName, retainStage: !verifyContext, primary: error });
|
|
596
619
|
}
|
|
620
|
+
const cleanupFailures = [];
|
|
621
|
+
for (const [name, fd, label] of [[stageName, stageFd, "stage"], [backupName, backupFd, "backup"]])
|
|
622
|
+
if (name && fd !== undefined) {
|
|
623
|
+
try {
|
|
624
|
+
if (!recovery || !this.removeOwnedFile(recovery.attemptFd, name, fd))
|
|
625
|
+
cleanupFailures.push(`${label} '${recovery ? path.join(recovery.relativePath, name) : name}' changed`);
|
|
626
|
+
}
|
|
627
|
+
catch (cleanupError) {
|
|
628
|
+
cleanupFailures.push(`${label} '${recovery ? path.join(recovery.relativePath, name) : name}': ${cleanupError.message}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (cleanupFailures.length)
|
|
632
|
+
throw new Error(`${error.message}; cleanup retained attempt state: ${cleanupFailures.join("; ")}`, { cause: error });
|
|
597
633
|
throw error;
|
|
598
634
|
}
|
|
599
635
|
finally {
|
|
600
|
-
const directoryFd = directories.at(-1)?.fd;
|
|
601
|
-
if (directoryFd !== undefined && stageFd !== undefined && stageName !== undefined)
|
|
602
|
-
this.removeOwnedFile(directoryFd, stageName, stageFd);
|
|
603
|
-
if (directoryFd !== undefined && targetFd !== undefined && backupName !== undefined)
|
|
604
|
-
this.removeOwnedFile(directoryFd, backupName, targetFd);
|
|
605
|
-
if (directoryFd !== undefined && targetFd !== undefined && displacedName !== undefined && displacedOwned)
|
|
606
|
-
this.removeOwnedFile(directoryFd, displacedName, targetFd);
|
|
607
636
|
if (displacedFd !== undefined)
|
|
608
637
|
fs.closeSync(displacedFd);
|
|
638
|
+
if (backupFd !== undefined)
|
|
639
|
+
fs.closeSync(backupFd);
|
|
609
640
|
if (stageFd !== undefined)
|
|
610
641
|
fs.closeSync(stageFd);
|
|
611
642
|
if (targetFd !== undefined)
|
|
612
643
|
fs.closeSync(targetFd);
|
|
613
644
|
for (const entry of directories.reverse())
|
|
614
645
|
fs.closeSync(entry.fd);
|
|
646
|
+
if (recovery)
|
|
647
|
+
this.closeRecoveryAttempt(recovery);
|
|
615
648
|
}
|
|
616
649
|
}
|
|
650
|
+
createRecoveryAttempt(targetDirectoryFd) {
|
|
651
|
+
const rootName = ".entity-recovery";
|
|
652
|
+
const agenteraFd = fs.openSync(fdPath(this.rootFd, ".agentera"), DIRECTORY_FLAGS);
|
|
653
|
+
let rootFd;
|
|
654
|
+
let ignoreFd;
|
|
655
|
+
let ignoreCreated = false;
|
|
656
|
+
let attemptFd;
|
|
657
|
+
let attemptName;
|
|
658
|
+
let rootCreated = false;
|
|
659
|
+
try {
|
|
660
|
+
try {
|
|
661
|
+
rootFd = fs.openSync(fdPath(agenteraFd, rootName), DIRECTORY_FLAGS);
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
if (error.code !== "ENOENT")
|
|
665
|
+
throw new Error(`private entity recovery root '.agentera/${rootName}' cannot be opened safely: ${error.message}`, { cause: error });
|
|
666
|
+
fs.mkdirSync(fdPath(agenteraFd, rootName), 0o700);
|
|
667
|
+
rootCreated = true;
|
|
668
|
+
syncDirectory(agenteraFd);
|
|
669
|
+
rootFd = fs.openSync(fdPath(agenteraFd, rootName), DIRECTORY_FLAGS);
|
|
670
|
+
}
|
|
671
|
+
const rootStat = fs.fstatSync(rootFd, { bigint: true });
|
|
672
|
+
if ((rootStat.mode & 18n) !== 0n || (typeof process.getuid === "function" && rootStat.uid !== BigInt(process.getuid())))
|
|
673
|
+
throw new Error(`private entity recovery root '.agentera/${rootName}' must be owned by the current user and not group/world-writable`);
|
|
674
|
+
if (rootStat.dev !== fs.fstatSync(targetDirectoryFd, { bigint: true }).dev)
|
|
675
|
+
throw new Error("private entity recovery root must share the canonical entity target filesystem");
|
|
676
|
+
try {
|
|
677
|
+
ignoreFd = fs.openSync(fdPath(rootFd, ".gitignore"), FILE_FLAGS);
|
|
678
|
+
}
|
|
679
|
+
catch (error) {
|
|
680
|
+
if (error.code !== "ENOENT")
|
|
681
|
+
throw new Error(`private entity recovery ignore marker cannot be opened safely: ${error.message}`, { cause: error });
|
|
682
|
+
ignoreFd = fs.openSync(fdPath(rootFd, ".gitignore"), fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
683
|
+
ignoreCreated = true;
|
|
684
|
+
fs.writeFileSync(ignoreFd, RECOVERY_IGNORE_BYTES, "utf8");
|
|
685
|
+
fs.fsyncSync(ignoreFd);
|
|
686
|
+
}
|
|
687
|
+
const ignoreStat = fs.fstatSync(ignoreFd, { bigint: true });
|
|
688
|
+
if (!ignoreStat.isFile() || (ignoreStat.mode & 18n) !== 0n || (typeof process.getuid === "function" && ignoreStat.uid !== BigInt(process.getuid())) || !readDescriptor(ignoreFd).equals(Buffer.from(RECOVERY_IGNORE_BYTES)))
|
|
689
|
+
throw new Error("private entity recovery ignore marker must be an owner-controlled regular file with the authoritative rules");
|
|
690
|
+
syncDirectory(rootFd);
|
|
691
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
692
|
+
const candidate = `entity-${process.pid}-${randomUUID()}`;
|
|
693
|
+
try {
|
|
694
|
+
fs.mkdirSync(fdPath(rootFd, candidate), 0o700);
|
|
695
|
+
attemptName = candidate;
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
if (error.code !== "EEXIST")
|
|
700
|
+
throw error;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if (!attemptName)
|
|
704
|
+
throw new Error("could not allocate a unique private entity recovery attempt after 8 tries");
|
|
705
|
+
attemptFd = fs.openSync(fdPath(rootFd, attemptName), DIRECTORY_FLAGS);
|
|
706
|
+
const attemptStat = fs.fstatSync(attemptFd, { bigint: true });
|
|
707
|
+
if ((attemptStat.mode & 63n) !== 0n || attemptStat.uid !== rootStat.uid || attemptStat.dev !== rootStat.dev)
|
|
708
|
+
throw new Error("private entity recovery attempt must be owner-only, owner-controlled, and on the recovery-root filesystem");
|
|
709
|
+
syncDirectory(rootFd);
|
|
710
|
+
return { agenteraFd, rootFd, attemptFd, ignoreFd, attemptName, relativePath: `.agentera/${rootName}/${attemptName}` };
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
if (attemptFd !== undefined)
|
|
714
|
+
fs.closeSync(attemptFd);
|
|
715
|
+
if (rootFd !== undefined && attemptName) {
|
|
716
|
+
try {
|
|
717
|
+
fs.rmdirSync(fdPath(rootFd, attemptName));
|
|
718
|
+
}
|
|
719
|
+
catch { /* preserve unexpected contents */ }
|
|
720
|
+
}
|
|
721
|
+
if (rootFd !== undefined && ignoreFd !== undefined && ignoreCreated) {
|
|
722
|
+
try {
|
|
723
|
+
this.removeOwnedFile(rootFd, ".gitignore", ignoreFd);
|
|
724
|
+
}
|
|
725
|
+
catch { /* preserve changed marker */ }
|
|
726
|
+
}
|
|
727
|
+
if (ignoreFd !== undefined)
|
|
728
|
+
fs.closeSync(ignoreFd);
|
|
729
|
+
if (rootFd !== undefined)
|
|
730
|
+
fs.closeSync(rootFd);
|
|
731
|
+
if (rootCreated) {
|
|
732
|
+
try {
|
|
733
|
+
fs.rmdirSync(fdPath(agenteraFd, rootName));
|
|
734
|
+
}
|
|
735
|
+
catch { /* preserve non-empty or changed root */ }
|
|
736
|
+
}
|
|
737
|
+
fs.closeSync(agenteraFd);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
closeRecoveryAttempt(recovery) {
|
|
742
|
+
try {
|
|
743
|
+
if (openMatches(recovery.rootFd, recovery.attemptName, recovery.attemptFd, DIRECTORY_FLAGS)) {
|
|
744
|
+
try {
|
|
745
|
+
fs.rmdirSync(fdPath(recovery.rootFd, recovery.attemptName));
|
|
746
|
+
syncDirectory(recovery.rootFd);
|
|
747
|
+
}
|
|
748
|
+
catch { /* best-effort private cleanup retains non-empty or changed attempt */ }
|
|
749
|
+
}
|
|
750
|
+
const remaining = fs.readdirSync(fdPath(recovery.rootFd));
|
|
751
|
+
if (remaining.length === 1 && remaining[0] === ".gitignore" && openMatches(recovery.rootFd, ".gitignore", recovery.ignoreFd, FILE_FLAGS)) {
|
|
752
|
+
try {
|
|
753
|
+
this.removeOwnedFile(recovery.rootFd, ".gitignore", recovery.ignoreFd);
|
|
754
|
+
syncDirectory(recovery.rootFd);
|
|
755
|
+
}
|
|
756
|
+
catch { /* preserve marker on cleanup failure */ }
|
|
757
|
+
}
|
|
758
|
+
if (openMatches(recovery.agenteraFd, ".entity-recovery", recovery.rootFd, DIRECTORY_FLAGS)) {
|
|
759
|
+
try {
|
|
760
|
+
fs.rmdirSync(fdPath(recovery.agenteraFd, ".entity-recovery"));
|
|
761
|
+
syncDirectory(recovery.agenteraFd);
|
|
762
|
+
}
|
|
763
|
+
catch { /* best-effort private cleanup retains non-empty or changed root */ }
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
/* committed or already-recovered state must not be reclassified by private cleanup */
|
|
768
|
+
}
|
|
769
|
+
finally {
|
|
770
|
+
for (const fd of [recovery.attemptFd, recovery.ignoreFd, recovery.rootFd, recovery.agenteraFd]) {
|
|
771
|
+
try {
|
|
772
|
+
fs.closeSync(fd);
|
|
773
|
+
}
|
|
774
|
+
catch { /* best-effort descriptor close */ }
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
cleanupCommittedReplacement(directoryFd, links) {
|
|
779
|
+
for (const [name, fd] of links) {
|
|
780
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
781
|
+
try {
|
|
782
|
+
this.removeOwnedFile(directoryFd, name, fd);
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
catch { /* committed publication remains authoritative; retry once and otherwise retain recovery link */ }
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
789
|
+
try {
|
|
790
|
+
syncDirectory(directoryFd);
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
catch { /* cleanup durability is best-effort after the durable commit boundary */ }
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
recoverDisplacedReplacement(state) {
|
|
797
|
+
const { directoryFd, recoveryFd, recoveryPath, relativeTarget, baselineBytes, backupFd, stageFd, displacedFd, retainStage, primary } = state;
|
|
798
|
+
const targetName = path.basename(relativeTarget);
|
|
799
|
+
const failures = [];
|
|
800
|
+
const retained = new Set();
|
|
801
|
+
let { stageName, backupName, displacedName } = state;
|
|
802
|
+
const publicPath = (name) => path.join(recoveryPath, name).split(path.sep).join("/");
|
|
803
|
+
const exactBaseline = () => Boolean(backupName && backupFd !== undefined && openMatches(recoveryFd, backupName, backupFd, FILE_FLAGS) && readDescriptor(backupFd).equals(baselineBytes));
|
|
804
|
+
const exactDisplaced = () => Boolean(displacedName && displacedFd !== undefined && openMatches(recoveryFd, displacedName, displacedFd, FILE_FLAGS));
|
|
805
|
+
const exactStage = () => Boolean(stageName && stageFd !== undefined && openMatches(recoveryFd, stageName, stageFd, FILE_FLAGS));
|
|
806
|
+
const remove = (name, fd, label) => {
|
|
807
|
+
try {
|
|
808
|
+
if (this.removeOwnedFile(recoveryFd, name, fd))
|
|
809
|
+
return true;
|
|
810
|
+
failures.push(`${label} '${publicPath(name)}' changed before cleanup`);
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
failures.push(`${label} '${publicPath(name)}' cleanup failed: ${error.message}`);
|
|
814
|
+
}
|
|
815
|
+
retained.add(publicPath(name));
|
|
816
|
+
return false;
|
|
817
|
+
};
|
|
818
|
+
const sync = (label) => { try {
|
|
819
|
+
syncDirectory(directoryFd);
|
|
820
|
+
syncDirectory(recoveryFd);
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
failures.push(`${label} directory fsync failed: ${error.message}`);
|
|
825
|
+
return false;
|
|
826
|
+
} };
|
|
827
|
+
const displacedBytes = exactDisplaced() ? readDescriptor(displacedFd) : undefined;
|
|
828
|
+
const displacedCompetitor = Boolean(displacedBytes && !displacedBytes.equals(baselineBytes));
|
|
829
|
+
const source = displacedCompetitor ? displacedName : exactBaseline() ? backupName : exactDisplaced() && displacedBytes?.equals(baselineBytes) ? displacedName : undefined;
|
|
830
|
+
const sourceFd = source === backupName ? backupFd : source === displacedName ? displacedFd : undefined;
|
|
831
|
+
if (stageFd !== undefined && openMatches(directoryFd, targetName, stageFd, FILE_FLAGS) && source && sourceFd !== undefined) {
|
|
832
|
+
try {
|
|
833
|
+
if (this.removeOwnedFile(directoryFd, targetName, stageFd))
|
|
834
|
+
sync("replacement removal");
|
|
835
|
+
else
|
|
836
|
+
failures.push(`published replacement '${relativeTarget}' changed before cleanup`);
|
|
837
|
+
}
|
|
838
|
+
catch (error) {
|
|
839
|
+
failures.push(`published replacement '${relativeTarget}' cleanup failed: ${error.message}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
let restoredBaseline = pathBytesEqual(directoryFd, targetName, baselineBytes);
|
|
843
|
+
let restoredCompetitor = Boolean(displacedCompetitor && displacedFd !== undefined && openMatches(directoryFd, targetName, displacedFd, FILE_FLAGS));
|
|
844
|
+
let occupied = !restoredBaseline && !restoredCompetitor && entryExists(directoryFd, targetName);
|
|
845
|
+
if (!restoredBaseline && !restoredCompetitor && !occupied && source && sourceFd !== undefined) {
|
|
846
|
+
try {
|
|
847
|
+
fs.linkSync(fdPath(recoveryFd, source), fdPath(directoryFd, targetName));
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
if (error.code !== "EEXIST")
|
|
851
|
+
failures.push(`restoration from '${publicPath(source)}' failed: ${error.message}`);
|
|
852
|
+
}
|
|
853
|
+
restoredBaseline = pathBytesEqual(directoryFd, targetName, baselineBytes);
|
|
854
|
+
restoredCompetitor = Boolean(displacedCompetitor && displacedFd !== undefined && openMatches(directoryFd, targetName, displacedFd, FILE_FLAGS));
|
|
855
|
+
occupied = !restoredBaseline && !restoredCompetitor && entryExists(directoryFd, targetName);
|
|
856
|
+
}
|
|
857
|
+
if (restoredBaseline || restoredCompetitor) {
|
|
858
|
+
const durable = sync(restoredBaseline ? "exact baseline restoration" : "competitor restoration");
|
|
859
|
+
if (durable) {
|
|
860
|
+
if (retainStage && exactStage())
|
|
861
|
+
retained.add(publicPath(stageName));
|
|
862
|
+
else if (exactStage() && remove(stageName, stageFd, "replacement stage"))
|
|
863
|
+
stageName = undefined;
|
|
864
|
+
if (restoredCompetitor) {
|
|
865
|
+
if (exactBaseline())
|
|
866
|
+
retained.add(publicPath(backupName));
|
|
867
|
+
else
|
|
868
|
+
failures.push("exact baseline snapshot is unavailable");
|
|
869
|
+
if (exactDisplaced() && remove(displacedName, displacedFd, "canonicalized competitor duplicate"))
|
|
870
|
+
displacedName = undefined;
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
if (displacedCompetitor && exactDisplaced())
|
|
874
|
+
retained.add(publicPath(displacedName));
|
|
875
|
+
else if (exactDisplaced() && remove(displacedName, displacedFd, "displaced baseline duplicate"))
|
|
876
|
+
displacedName = undefined;
|
|
877
|
+
if (exactBaseline() && remove(backupName, backupFd, "baseline snapshot duplicate"))
|
|
878
|
+
backupName = undefined;
|
|
879
|
+
}
|
|
880
|
+
sync("restoration cleanup");
|
|
881
|
+
}
|
|
882
|
+
for (const name of [backupName, displacedName, stageName])
|
|
883
|
+
if (name && entryExists(recoveryFd, name))
|
|
884
|
+
retained.add(publicPath(name));
|
|
885
|
+
const outcome = restoredCompetitor
|
|
886
|
+
? `recovery restored concurrent competitor bytes at '${relativeTarget}' and retained the exact discovered baseline snapshot at ${exactBaseline() ? `'${publicPath(backupName)}'` : "an unavailable path"}`
|
|
887
|
+
: `recovery restored byte-verified exact prior bytes at '${relativeTarget}'`;
|
|
888
|
+
return new Error(`${primary.message}; ${outcome}${retained.size ? `; retained recovery files: ${[...retained].join(", ")}` : ""}${failures.length ? `; recovery issues: ${failures.join("; ")}` : ""}`, { cause: primary });
|
|
889
|
+
}
|
|
890
|
+
if (occupied) {
|
|
891
|
+
if (exactBaseline())
|
|
892
|
+
retained.add(publicPath(backupName));
|
|
893
|
+
else
|
|
894
|
+
failures.push("exact baseline snapshot is unavailable");
|
|
895
|
+
if (displacedCompetitor && exactDisplaced())
|
|
896
|
+
retained.add(publicPath(displacedName));
|
|
897
|
+
if (sync("competing-target retention")) {
|
|
898
|
+
if (retainStage && exactStage())
|
|
899
|
+
retained.add(publicPath(stageName));
|
|
900
|
+
else if (exactStage() && remove(stageName, stageFd, "replacement stage"))
|
|
901
|
+
stageName = undefined;
|
|
902
|
+
if (!displacedCompetitor && exactDisplaced() && remove(displacedName, displacedFd, "displaced baseline duplicate"))
|
|
903
|
+
displacedName = undefined;
|
|
904
|
+
sync("retention cleanup");
|
|
905
|
+
}
|
|
906
|
+
for (const name of [backupName, displacedName, stageName])
|
|
907
|
+
if (name && entryExists(recoveryFd, name))
|
|
908
|
+
retained.add(publicPath(name));
|
|
909
|
+
return new Error(`${primary.message}; recovery preserved the competing canonical target and retained the exact discovered baseline snapshot and any displaced competitor at ${[...retained].join(", ") || "unavailable paths"}; preserve each role and resolve ownership explicitly${failures.length ? `; recovery issues: ${failures.join("; ")}` : ""}`, { cause: primary });
|
|
910
|
+
}
|
|
911
|
+
if (exactBaseline())
|
|
912
|
+
retained.add(publicPath(backupName));
|
|
913
|
+
if (exactDisplaced())
|
|
914
|
+
retained.add(publicPath(displacedName));
|
|
915
|
+
if (exactStage())
|
|
916
|
+
retained.add(publicPath(stageName));
|
|
917
|
+
return new Error(`${primary.message}; recovery could not restore the absent canonical target; retained role-labeled recovery files: ${[...retained].join(", ") || "none"}; restore the exact baseline or competitor explicitly before retrying${failures.length ? `; recovery issues: ${failures.join("; ")}` : ""}`, { cause: primary });
|
|
918
|
+
}
|
|
617
919
|
close() {
|
|
618
920
|
if (this.closed)
|
|
619
921
|
return;
|
|
@@ -641,8 +943,9 @@ export class EntityPublicationContext {
|
|
|
641
943
|
}
|
|
642
944
|
removeOwnedFile(parentFd, name, expectedFd) {
|
|
643
945
|
if (!openMatches(parentFd, name, expectedFd, FILE_FLAGS))
|
|
644
|
-
return;
|
|
946
|
+
return false;
|
|
645
947
|
fs.unlinkSync(fdPath(parentFd, name));
|
|
948
|
+
return true;
|
|
646
949
|
}
|
|
647
950
|
removeCreatedDirectories(directories) {
|
|
648
951
|
for (const entry of [...directories].reverse()) {
|