@triedotdev/mcp 1.0.163 → 1.0.165
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/dist/{chunk-62JD7MIS.js → chunk-5LRDF2WB.js} +15 -35
- package/dist/chunk-5LRDF2WB.js.map +1 -0
- package/dist/{chunk-XSKLOBD2.js → chunk-CDG2GVBP.js} +601 -70
- package/dist/chunk-CDG2GVBP.js.map +1 -0
- package/dist/{chunk-5BYSJ7XT.js → chunk-GTKYBOXL.js} +13 -2
- package/dist/{chunk-5BYSJ7XT.js.map → chunk-GTKYBOXL.js.map} +1 -1
- package/dist/{chunk-ACU3IXZG.js → chunk-HC5P6FZD.js} +7 -7
- package/dist/{chunk-HFVPHQL3.js → chunk-IPNPHPNN.js} +9 -9
- package/dist/{chunk-GL62CXU4.js → chunk-IS5UBN2R.js} +86 -12
- package/dist/chunk-IS5UBN2R.js.map +1 -0
- package/dist/{chunk-LLDZDU2Y.js → chunk-LR5M4RTN.js} +79 -1
- package/dist/chunk-LR5M4RTN.js.map +1 -0
- package/dist/{chunk-ERMLZJTK.js → chunk-M7HMBZ3R.js} +13 -13
- package/dist/chunk-OBQ74FOU.js +27 -0
- package/dist/chunk-OBQ74FOU.js.map +1 -0
- package/dist/{chunk-5TRCQAOE.js → chunk-RQ6QZBIN.js} +16 -5
- package/dist/chunk-RQ6QZBIN.js.map +1 -0
- package/dist/{chunk-IRZXBQVQ.js → chunk-SS2O3MTC.js} +134 -101
- package/dist/chunk-SS2O3MTC.js.map +1 -0
- package/dist/{chunk-LR46VMIE.js → chunk-WRYQHVPD.js} +5 -5
- package/dist/{chunk-Y4B3VEL7.js → chunk-YAL3SUBG.js} +435 -202
- package/dist/chunk-YAL3SUBG.js.map +1 -0
- package/dist/cli/main.js +215 -57
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +15 -14
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{fast-analyzer-LLZ6FLP5.js → fast-analyzer-54AHLVO5.js} +3 -3
- package/dist/{goal-manager-D6XKE3FY.js → goal-manager-563BNILQ.js} +5 -5
- package/dist/{goal-validator-4DDL7NBP.js → goal-validator-FJEDIYU7.js} +5 -5
- package/dist/{hypothesis-RI3Q33JB.js → hypothesis-4KC7XRBZ.js} +5 -5
- package/dist/index.js +16 -15
- package/dist/index.js.map +1 -1
- package/dist/{issue-store-DUR5UTYK.js → issue-store-FOS4T736.js} +3 -3
- package/dist/{ledger-ZTR63P3L.js → ledger-EDLPF6SB.js} +8 -2
- package/dist/project-state-AHPA77SM.js +28 -0
- package/dist/server/mcp-server.js +16 -15
- package/dist/sync-M2FSWPBC.js +12 -0
- package/dist/{tiered-storage-FHHAJR4P.js → tiered-storage-OP74NPJY.js} +2 -2
- package/dist/tiered-storage-OP74NPJY.js.map +1 -0
- package/dist/{trie-agent-NYSPGZYS.js → trie-agent-TM6ATSNR.js} +12 -12
- package/dist/trie-agent-TM6ATSNR.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-5TRCQAOE.js.map +0 -1
- package/dist/chunk-62JD7MIS.js.map +0 -1
- package/dist/chunk-GL62CXU4.js.map +0 -1
- package/dist/chunk-IRZXBQVQ.js.map +0 -1
- package/dist/chunk-LLDZDU2Y.js.map +0 -1
- package/dist/chunk-XSKLOBD2.js.map +0 -1
- package/dist/chunk-Y4B3VEL7.js.map +0 -1
- /package/dist/{chunk-ACU3IXZG.js.map → chunk-HC5P6FZD.js.map} +0 -0
- /package/dist/{chunk-HFVPHQL3.js.map → chunk-IPNPHPNN.js.map} +0 -0
- /package/dist/{chunk-ERMLZJTK.js.map → chunk-M7HMBZ3R.js.map} +0 -0
- /package/dist/{chunk-LR46VMIE.js.map → chunk-WRYQHVPD.js.map} +0 -0
- /package/dist/{fast-analyzer-LLZ6FLP5.js.map → fast-analyzer-54AHLVO5.js.map} +0 -0
- /package/dist/{goal-manager-D6XKE3FY.js.map → goal-manager-563BNILQ.js.map} +0 -0
- /package/dist/{goal-validator-4DDL7NBP.js.map → goal-validator-FJEDIYU7.js.map} +0 -0
- /package/dist/{hypothesis-RI3Q33JB.js.map → hypothesis-4KC7XRBZ.js.map} +0 -0
- /package/dist/{issue-store-DUR5UTYK.js.map → issue-store-FOS4T736.js.map} +0 -0
- /package/dist/{ledger-ZTR63P3L.js.map → ledger-EDLPF6SB.js.map} +0 -0
- /package/dist/{tiered-storage-FHHAJR4P.js.map → project-state-AHPA77SM.js.map} +0 -0
- /package/dist/{trie-agent-NYSPGZYS.js.map → sync-M2FSWPBC.js.map} +0 -0
|
@@ -5,18 +5,146 @@ import {
|
|
|
5
5
|
getTrieDirectory,
|
|
6
6
|
getWorkingDirectory
|
|
7
7
|
} from "./chunk-VVITXIHN.js";
|
|
8
|
+
import {
|
|
9
|
+
__require
|
|
10
|
+
} from "./chunk-DGUM43GV.js";
|
|
8
11
|
|
|
9
12
|
// src/memory/ledger.ts
|
|
10
13
|
import { createHash } from "crypto";
|
|
11
|
-
import { mkdir, readFile, writeFile, stat, unlink } from "fs/promises";
|
|
12
|
-
import { existsSync as
|
|
14
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile, stat as stat2, unlink as unlink2 } from "fs/promises";
|
|
15
|
+
import { existsSync as existsSync5 } from "fs";
|
|
13
16
|
import { createGzip, createGunzip } from "zlib";
|
|
14
17
|
import { pipeline } from "stream/promises";
|
|
15
18
|
import { createReadStream, createWriteStream } from "fs";
|
|
16
19
|
import { join as join3 } from "path";
|
|
17
20
|
|
|
18
|
-
// src/
|
|
21
|
+
// src/utils/file-lock.ts
|
|
22
|
+
import { open, unlink, readFile, stat, mkdir } from "fs/promises";
|
|
19
23
|
import { existsSync } from "fs";
|
|
24
|
+
import { dirname } from "path";
|
|
25
|
+
var activeLocks = /* @__PURE__ */ new Set();
|
|
26
|
+
function setupCleanupHandler() {
|
|
27
|
+
const cleanup = () => {
|
|
28
|
+
for (const lockPath of activeLocks) {
|
|
29
|
+
try {
|
|
30
|
+
const fs = __require("fs");
|
|
31
|
+
if (fs.existsSync(lockPath)) {
|
|
32
|
+
fs.unlinkSync(lockPath);
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
process.on("exit", cleanup);
|
|
39
|
+
process.on("SIGINT", () => {
|
|
40
|
+
cleanup();
|
|
41
|
+
process.exit(130);
|
|
42
|
+
});
|
|
43
|
+
process.on("SIGTERM", () => {
|
|
44
|
+
cleanup();
|
|
45
|
+
process.exit(143);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
var cleanupHandlerInstalled = false;
|
|
49
|
+
async function withFileLock(filePath, fn, options = {}) {
|
|
50
|
+
const {
|
|
51
|
+
timeout = 1e4,
|
|
52
|
+
retryDelay = 50,
|
|
53
|
+
maxRetryDelay = 500,
|
|
54
|
+
staleTimeout = 3e4
|
|
55
|
+
} = options;
|
|
56
|
+
const lockPath = `${filePath}.lock`;
|
|
57
|
+
const start = Date.now();
|
|
58
|
+
let currentDelay = retryDelay;
|
|
59
|
+
if (!cleanupHandlerInstalled) {
|
|
60
|
+
setupCleanupHandler();
|
|
61
|
+
cleanupHandlerInstalled = true;
|
|
62
|
+
}
|
|
63
|
+
await mkdir(dirname(lockPath), { recursive: true });
|
|
64
|
+
while (true) {
|
|
65
|
+
try {
|
|
66
|
+
await cleanupStaleLock(lockPath, staleTimeout);
|
|
67
|
+
const lockInfo = {
|
|
68
|
+
pid: process.pid,
|
|
69
|
+
timestamp: Date.now(),
|
|
70
|
+
hostname: __require("os").hostname()
|
|
71
|
+
};
|
|
72
|
+
const handle = await open(lockPath, "wx");
|
|
73
|
+
await handle.writeFile(JSON.stringify(lockInfo));
|
|
74
|
+
await handle.close();
|
|
75
|
+
activeLocks.add(lockPath);
|
|
76
|
+
try {
|
|
77
|
+
return await fn();
|
|
78
|
+
} finally {
|
|
79
|
+
activeLocks.delete(lockPath);
|
|
80
|
+
await unlink(lockPath).catch(() => {
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (err.code !== "EEXIST") {
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
if (Date.now() - start > timeout) {
|
|
88
|
+
let lockHolder = "unknown";
|
|
89
|
+
try {
|
|
90
|
+
const content = await readFile(lockPath, "utf-8");
|
|
91
|
+
const info = JSON.parse(content);
|
|
92
|
+
lockHolder = `PID ${info.pid} on ${info.hostname}`;
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Timeout acquiring lock for ${filePath} after ${timeout}ms. Lock held by: ${lockHolder}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
100
|
+
currentDelay = Math.min(currentDelay * 2, maxRetryDelay);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function cleanupStaleLock(lockPath, staleTimeout) {
|
|
105
|
+
if (!existsSync(lockPath)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const content = await readFile(lockPath, "utf-8");
|
|
110
|
+
const info = JSON.parse(content);
|
|
111
|
+
const lockAge = Date.now() - info.timestamp;
|
|
112
|
+
if (lockAge > staleTimeout) {
|
|
113
|
+
console.warn(`[FileLock] Removing stale lock (age: ${Math.round(lockAge / 1e3)}s): ${lockPath}`);
|
|
114
|
+
await unlink(lockPath);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const currentHostname = __require("os").hostname();
|
|
118
|
+
if (info.hostname === currentHostname) {
|
|
119
|
+
if (!isProcessRunning(info.pid)) {
|
|
120
|
+
console.warn(`[FileLock] Removing orphaned lock (PID ${info.pid} not running): ${lockPath}`);
|
|
121
|
+
await unlink(lockPath);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
try {
|
|
127
|
+
const stats = await stat(lockPath);
|
|
128
|
+
const fileAge = Date.now() - stats.mtimeMs;
|
|
129
|
+
if (fileAge > staleTimeout) {
|
|
130
|
+
console.warn(`[FileLock] Removing stale/corrupted lock: ${lockPath}`);
|
|
131
|
+
await unlink(lockPath);
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function isProcessRunning(pid) {
|
|
138
|
+
try {
|
|
139
|
+
process.kill(pid, 0);
|
|
140
|
+
return true;
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/agent/git.ts
|
|
147
|
+
import { existsSync as existsSync2 } from "fs";
|
|
20
148
|
import path from "path";
|
|
21
149
|
|
|
22
150
|
// src/utils/command-runner.ts
|
|
@@ -319,7 +447,7 @@ async function getChangedFilesSinceTimestamp(projectPath, timestamp) {
|
|
|
319
447
|
// src/memory/crypto-keys.ts
|
|
320
448
|
import * as ed25519 from "@noble/ed25519";
|
|
321
449
|
import { randomBytes } from "crypto";
|
|
322
|
-
import { existsSync as
|
|
450
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
323
451
|
import { join } from "path";
|
|
324
452
|
function getKeysDirectory(workDir) {
|
|
325
453
|
const trieDir = getTrieDirectory(workDir || process.cwd());
|
|
@@ -338,7 +466,7 @@ async function generateKeyPair() {
|
|
|
338
466
|
}
|
|
339
467
|
function saveKeyPair(keyPair, workDir) {
|
|
340
468
|
const keysDir = getKeysDirectory(workDir);
|
|
341
|
-
if (!
|
|
469
|
+
if (!existsSync3(keysDir)) {
|
|
342
470
|
mkdirSync(keysDir, { recursive: true });
|
|
343
471
|
}
|
|
344
472
|
const keyPath = getDefaultKeyPath(workDir);
|
|
@@ -353,7 +481,7 @@ function saveKeyPair(keyPair, workDir) {
|
|
|
353
481
|
}
|
|
354
482
|
function loadKeyPair(workDir) {
|
|
355
483
|
const keyPath = getDefaultKeyPath(workDir);
|
|
356
|
-
if (!
|
|
484
|
+
if (!existsSync3(keyPath)) {
|
|
357
485
|
return null;
|
|
358
486
|
}
|
|
359
487
|
try {
|
|
@@ -410,25 +538,29 @@ async function verifyHashSignature(hash, signatureData) {
|
|
|
410
538
|
}
|
|
411
539
|
return await verifySignature(hash, signatureData.signature, signatureData.publicKey);
|
|
412
540
|
}
|
|
541
|
+
function getPublicKey(workDir) {
|
|
542
|
+
const keyPair = loadKeyPair(workDir);
|
|
543
|
+
return keyPair?.publicKey || null;
|
|
544
|
+
}
|
|
413
545
|
function hasSigningKey(workDir) {
|
|
414
546
|
const keyPath = getDefaultKeyPath(workDir);
|
|
415
|
-
return
|
|
547
|
+
return existsSync3(keyPath);
|
|
416
548
|
}
|
|
417
549
|
|
|
418
550
|
// src/memory/git-integration.ts
|
|
419
551
|
import { exec as exec2 } from "child_process";
|
|
420
552
|
import { promisify as promisify2 } from "util";
|
|
421
|
-
import { existsSync as
|
|
553
|
+
import { existsSync as existsSync4 } from "fs";
|
|
422
554
|
import { join as join2 } from "path";
|
|
423
555
|
var execAsync2 = promisify2(exec2);
|
|
424
556
|
async function isGitIntegrationEnabled(workDir) {
|
|
425
557
|
try {
|
|
426
558
|
const gitDir = join2(workDir, ".git");
|
|
427
|
-
if (!
|
|
559
|
+
if (!existsSync4(gitDir)) {
|
|
428
560
|
return false;
|
|
429
561
|
}
|
|
430
562
|
const configPath = join2(getTrieDirectory(workDir), "config.json");
|
|
431
|
-
if (!
|
|
563
|
+
if (!existsSync4(configPath)) {
|
|
432
564
|
return true;
|
|
433
565
|
}
|
|
434
566
|
const config = JSON.parse(await import("fs/promises").then((fs) => fs.readFile(configPath, "utf-8")));
|
|
@@ -473,7 +605,7 @@ async function ensureKeysIgnored(workDir) {
|
|
|
473
605
|
const gitignorePath = join2(workDir, ".gitignore");
|
|
474
606
|
const fs = await import("fs/promises");
|
|
475
607
|
let gitignore = "";
|
|
476
|
-
if (
|
|
608
|
+
if (existsSync4(gitignorePath)) {
|
|
477
609
|
gitignore = await fs.readFile(gitignorePath, "utf-8");
|
|
478
610
|
}
|
|
479
611
|
if (gitignore.includes(".trie/keys/")) {
|
|
@@ -493,6 +625,12 @@ var MANIFEST_FILENAME = "ledger-manifest.json";
|
|
|
493
625
|
var SYNC_STATE_FILENAME = "ledger-sync.json";
|
|
494
626
|
var GENESIS_HASH = "0".repeat(64);
|
|
495
627
|
var LEDGER_VERSION = 2;
|
|
628
|
+
var ConcurrentModificationError = class extends Error {
|
|
629
|
+
constructor(message) {
|
|
630
|
+
super(message);
|
|
631
|
+
this.name = "ConcurrentModificationError";
|
|
632
|
+
}
|
|
633
|
+
};
|
|
496
634
|
async function signLedgerEntry(entry, workDir) {
|
|
497
635
|
try {
|
|
498
636
|
const signatureData = await signHash(entry.hash, workDir);
|
|
@@ -541,42 +679,45 @@ async function appendIssuesToLedger(issues, workDir, author) {
|
|
|
541
679
|
if (issues.length === 0) return null;
|
|
542
680
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
543
681
|
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
544
|
-
await
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
blocks.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
await
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
682
|
+
await mkdir2(memoryDir, { recursive: true });
|
|
683
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
684
|
+
return withFileLock(ledgerPath, async () => {
|
|
685
|
+
const isRepo = await isGitRepo(projectDir);
|
|
686
|
+
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
687
|
+
const blockAuthor = author || lastCommit?.author || "unknown";
|
|
688
|
+
const blocks = await loadLedger(projectDir);
|
|
689
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
690
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
691
|
+
const shouldSign = hasSigningKey(projectDir);
|
|
692
|
+
let entries = issues.map((issue) => ({
|
|
693
|
+
id: issue.id,
|
|
694
|
+
hash: issue.hash,
|
|
695
|
+
severity: issue.severity,
|
|
696
|
+
file: issue.file,
|
|
697
|
+
agent: issue.agent,
|
|
698
|
+
timestamp: issue.timestamp,
|
|
699
|
+
status: "active"
|
|
700
|
+
}));
|
|
701
|
+
if (shouldSign) {
|
|
702
|
+
entries = await Promise.all(entries.map((entry) => signLedgerEntry(entry, projectDir)));
|
|
703
|
+
}
|
|
704
|
+
const previousBlock = blocks[blocks.length - 1];
|
|
705
|
+
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, blockAuthor, lastCommit?.hash, blocks.length);
|
|
706
|
+
if (block !== previousBlock) {
|
|
707
|
+
blocks.push(block);
|
|
708
|
+
}
|
|
709
|
+
block.entries = [...block.entries, ...entries];
|
|
710
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((entry) => entry.hash));
|
|
711
|
+
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
712
|
+
block.updatedAt = now;
|
|
713
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
714
|
+
if (await isGitRepo(projectDir)) {
|
|
715
|
+
await ensureKeysIgnored(projectDir);
|
|
716
|
+
const commitMessage = `ledger: append ${entries.length} ${entries.length === 1 ? "entry" : "entries"}`;
|
|
717
|
+
await autoCommitLedger(projectDir, commitMessage);
|
|
718
|
+
}
|
|
719
|
+
return block;
|
|
720
|
+
}, { timeout: 15e3 });
|
|
580
721
|
}
|
|
581
722
|
async function verifyLedger(workDir) {
|
|
582
723
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
@@ -649,24 +790,80 @@ function createSyncableBlock(date, now, previousHash, author, gitCommit, chainHe
|
|
|
649
790
|
};
|
|
650
791
|
}
|
|
651
792
|
async function loadLedger(projectDir) {
|
|
793
|
+
const result = await loadLedgerWithHash(projectDir);
|
|
794
|
+
return result.blocks;
|
|
795
|
+
}
|
|
796
|
+
async function loadLedgerWithHash(projectDir) {
|
|
652
797
|
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
653
798
|
try {
|
|
654
|
-
if (!
|
|
655
|
-
|
|
799
|
+
if (!existsSync5(ledgerPath)) {
|
|
800
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
801
|
+
}
|
|
802
|
+
const content = await readFile2(ledgerPath, "utf-8");
|
|
656
803
|
const parsed = JSON.parse(content);
|
|
657
|
-
if (
|
|
658
|
-
|
|
804
|
+
if (parsed && parsed._format === "ledger-v2") {
|
|
805
|
+
const file = parsed;
|
|
806
|
+
return {
|
|
807
|
+
blocks: file.blocks || [],
|
|
808
|
+
contentHash: file._contentHash
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
if (!Array.isArray(parsed)) {
|
|
812
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
813
|
+
}
|
|
814
|
+
const blocks = parsed;
|
|
815
|
+
const contentHash = sha256(JSON.stringify(blocks));
|
|
816
|
+
return { blocks, contentHash };
|
|
659
817
|
} catch {
|
|
660
|
-
return [];
|
|
818
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
661
819
|
}
|
|
662
820
|
}
|
|
663
821
|
async function getLedgerBlocks(workDir) {
|
|
664
822
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
665
823
|
return loadLedger(projectDir);
|
|
666
824
|
}
|
|
825
|
+
async function getLedgerBlocksWithHash(workDir) {
|
|
826
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
827
|
+
return loadLedgerWithHash(projectDir);
|
|
828
|
+
}
|
|
829
|
+
async function saveLedgerOptimistic(blocks, expectedHash, workDir) {
|
|
830
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
831
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
832
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
833
|
+
await withFileLock(ledgerPath, async () => {
|
|
834
|
+
await saveLedgerWithConcurrencyCheck(blocks, projectDir, expectedHash);
|
|
835
|
+
}, { timeout: 1e4 });
|
|
836
|
+
}
|
|
667
837
|
async function saveLedger(blocks, projectDir) {
|
|
668
838
|
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
669
|
-
await
|
|
839
|
+
await withFileLock(ledgerPath, async () => {
|
|
840
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
841
|
+
}, { timeout: 1e4 });
|
|
842
|
+
}
|
|
843
|
+
async function saveLedgerInternal(blocks, projectDir) {
|
|
844
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
845
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
846
|
+
}
|
|
847
|
+
async function saveLedgerWithConcurrencyCheck(blocks, projectDir, expectedHash) {
|
|
848
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
849
|
+
const { contentHash: currentHash } = await loadLedgerWithHash(projectDir);
|
|
850
|
+
if (currentHash !== expectedHash) {
|
|
851
|
+
throw new ConcurrentModificationError(
|
|
852
|
+
`Ledger was modified by another process. Expected hash: ${expectedHash.slice(0, 16)}..., Current hash: ${currentHash.slice(0, 16)}...`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
856
|
+
}
|
|
857
|
+
async function saveLedgerWithFormat(ledgerPath, blocks) {
|
|
858
|
+
const blocksJson = JSON.stringify(blocks);
|
|
859
|
+
const contentHash = sha256(blocksJson);
|
|
860
|
+
const ledgerFile = {
|
|
861
|
+
_format: "ledger-v2",
|
|
862
|
+
_contentHash: contentHash,
|
|
863
|
+
_lastModified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
864
|
+
blocks
|
|
865
|
+
};
|
|
866
|
+
await atomicWriteJSON(ledgerPath, ledgerFile);
|
|
670
867
|
}
|
|
671
868
|
function sha256(input) {
|
|
672
869
|
return createHash("sha256").update(input).digest("hex");
|
|
@@ -690,15 +887,15 @@ async function ensureSharedStorageStructure(projectDir) {
|
|
|
690
887
|
const sharedDir = getSharedLedgerDir(projectDir);
|
|
691
888
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
692
889
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
693
|
-
await
|
|
694
|
-
await
|
|
695
|
-
await
|
|
890
|
+
await mkdir2(sharedDir, { recursive: true });
|
|
891
|
+
await mkdir2(activeDir, { recursive: true });
|
|
892
|
+
await mkdir2(archivedDir, { recursive: true });
|
|
696
893
|
}
|
|
697
894
|
async function loadManifest(projectDir) {
|
|
698
895
|
const manifestPath = getManifestPath(projectDir);
|
|
699
896
|
try {
|
|
700
|
-
if (!
|
|
701
|
-
const content = await
|
|
897
|
+
if (!existsSync5(manifestPath)) return null;
|
|
898
|
+
const content = await readFile2(manifestPath, "utf-8");
|
|
702
899
|
return JSON.parse(content);
|
|
703
900
|
} catch {
|
|
704
901
|
return null;
|
|
@@ -735,8 +932,8 @@ async function createDefaultManifest(_projectDir) {
|
|
|
735
932
|
async function loadSyncState(projectDir) {
|
|
736
933
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
737
934
|
try {
|
|
738
|
-
if (!
|
|
739
|
-
const content = await
|
|
935
|
+
if (!existsSync5(syncStatePath)) return null;
|
|
936
|
+
const content = await readFile2(syncStatePath, "utf-8");
|
|
740
937
|
return JSON.parse(content);
|
|
741
938
|
} catch {
|
|
742
939
|
return null;
|
|
@@ -745,7 +942,7 @@ async function loadSyncState(projectDir) {
|
|
|
745
942
|
async function saveSyncState(syncState, projectDir) {
|
|
746
943
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
747
944
|
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
748
|
-
await
|
|
945
|
+
await mkdir2(memoryDir, { recursive: true });
|
|
749
946
|
await atomicWriteJSON(syncStatePath, syncState);
|
|
750
947
|
}
|
|
751
948
|
async function initializeSharedLedger(workDir) {
|
|
@@ -770,54 +967,62 @@ async function initializeSharedLedger(workDir) {
|
|
|
770
967
|
}
|
|
771
968
|
async function syncLedgerFromShared(workDir) {
|
|
772
969
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
970
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
971
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
773
972
|
await initializeSharedLedger(projectDir);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
973
|
+
return withFileLock(ledgerPath, async () => {
|
|
974
|
+
const manifest = await loadManifest(projectDir);
|
|
975
|
+
const localBlocks = await loadLedger(projectDir);
|
|
976
|
+
const sharedBlocks = await loadSharedBlocks(projectDir);
|
|
977
|
+
if (!manifest) {
|
|
978
|
+
throw new Error("Failed to load ledger manifest");
|
|
979
|
+
}
|
|
980
|
+
const mergeResult = await mergeChains(localBlocks, sharedBlocks, "timestamp");
|
|
981
|
+
await saveLedgerInternal(mergeResult.mergedChain, projectDir);
|
|
982
|
+
const syncState = {
|
|
983
|
+
lastSyncTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
984
|
+
conflicts: mergeResult.conflicts,
|
|
985
|
+
localChanges: false,
|
|
986
|
+
sharedChanges: false
|
|
987
|
+
};
|
|
988
|
+
await saveSyncState(syncState, projectDir);
|
|
989
|
+
return mergeResult;
|
|
990
|
+
}, { timeout: 3e4 });
|
|
790
991
|
}
|
|
791
992
|
async function pushLedgerToShared(workDir) {
|
|
792
993
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
994
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
995
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
793
996
|
await initializeSharedLedger(projectDir);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (
|
|
806
|
-
manifest.index.byAuthor[blockAuthor]
|
|
997
|
+
await withFileLock(ledgerPath, async () => {
|
|
998
|
+
const localBlocks = await loadLedger(projectDir);
|
|
999
|
+
const manifest = await loadManifest(projectDir) || await createDefaultManifest(projectDir);
|
|
1000
|
+
const activeDir = getActiveBlocksDir(projectDir);
|
|
1001
|
+
for (const block of localBlocks) {
|
|
1002
|
+
const blockFilename = `${block.date}.json`;
|
|
1003
|
+
const blockPath = join3(activeDir, blockFilename);
|
|
1004
|
+
if (!existsSync5(blockPath) || block.updatedAt > manifest.lastSync) {
|
|
1005
|
+
await atomicWriteJSON(blockPath, block);
|
|
1006
|
+
manifest.index.byDate[block.date] = `active/${blockFilename}`;
|
|
1007
|
+
const blockAuthor = block.author;
|
|
1008
|
+
if (blockAuthor) {
|
|
1009
|
+
if (!manifest.index.byAuthor[blockAuthor]) {
|
|
1010
|
+
manifest.index.byAuthor[blockAuthor] = [];
|
|
1011
|
+
}
|
|
1012
|
+
if (!manifest.index.byAuthor[blockAuthor].includes(`active/${blockFilename}`)) {
|
|
1013
|
+
manifest.index.byAuthor[blockAuthor].push(`active/${blockFilename}`);
|
|
1014
|
+
}
|
|
807
1015
|
}
|
|
808
|
-
if (!manifest.
|
|
809
|
-
manifest.
|
|
1016
|
+
if (!manifest.activeBlocks.includes(blockFilename)) {
|
|
1017
|
+
manifest.activeBlocks.push(blockFilename);
|
|
810
1018
|
}
|
|
811
1019
|
}
|
|
812
|
-
if (!manifest.activeBlocks.includes(blockFilename)) {
|
|
813
|
-
manifest.activeBlocks.push(blockFilename);
|
|
814
|
-
}
|
|
815
1020
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1021
|
+
manifest.lastSync = (/* @__PURE__ */ new Date()).toISOString();
|
|
1022
|
+
manifest.totalBlocks = manifest.activeBlocks.length + manifest.archivedBlocks.length;
|
|
1023
|
+
manifest.totalEntries = localBlocks.reduce((sum, block) => sum + block.entries.length, 0);
|
|
1024
|
+
await saveManifest(manifest, projectDir);
|
|
1025
|
+
}, { timeout: 3e4 });
|
|
821
1026
|
}
|
|
822
1027
|
async function loadSharedBlocks(projectDir) {
|
|
823
1028
|
const manifest = await loadManifest(projectDir);
|
|
@@ -827,8 +1032,8 @@ async function loadSharedBlocks(projectDir) {
|
|
|
827
1032
|
for (const filename of manifest.activeBlocks) {
|
|
828
1033
|
const blockPath = join3(activeDir, filename);
|
|
829
1034
|
try {
|
|
830
|
-
if (
|
|
831
|
-
const content = await
|
|
1035
|
+
if (existsSync5(blockPath)) {
|
|
1036
|
+
const content = await readFile2(blockPath, "utf-8");
|
|
832
1037
|
const block = JSON.parse(content);
|
|
833
1038
|
blocks.push(block);
|
|
834
1039
|
}
|
|
@@ -919,16 +1124,29 @@ function resolveConflict(conflict, strategy) {
|
|
|
919
1124
|
if (!conflict.localBlock || !conflict.remoteBlock) {
|
|
920
1125
|
return null;
|
|
921
1126
|
}
|
|
1127
|
+
const mergedBlock = mergeBlockEntries(conflict.localBlock, conflict.remoteBlock);
|
|
1128
|
+
mergedBlock.conflictResolved = true;
|
|
922
1129
|
switch (strategy) {
|
|
923
|
-
case "longest":
|
|
924
|
-
return conflict.localBlock.entries.length >= conflict.remoteBlock.entries.length ? conflict.localBlock : conflict.remoteBlock;
|
|
925
1130
|
case "timestamp":
|
|
926
|
-
|
|
1131
|
+
if (conflict.remoteBlock.updatedAt > conflict.localBlock.updatedAt) {
|
|
1132
|
+
mergedBlock.author = `${conflict.remoteBlock.author}+${conflict.localBlock.author}`;
|
|
1133
|
+
} else {
|
|
1134
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1135
|
+
}
|
|
1136
|
+
break;
|
|
1137
|
+
case "longest":
|
|
1138
|
+
if (conflict.remoteBlock.entries.length > conflict.localBlock.entries.length) {
|
|
1139
|
+
mergedBlock.author = `${conflict.remoteBlock.author}+${conflict.localBlock.author}`;
|
|
1140
|
+
} else {
|
|
1141
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1142
|
+
}
|
|
1143
|
+
break;
|
|
927
1144
|
case "manual":
|
|
928
|
-
return { ...conflict.localBlock, conflictResolved: false };
|
|
929
1145
|
default:
|
|
930
|
-
|
|
1146
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1147
|
+
break;
|
|
931
1148
|
}
|
|
1149
|
+
return mergedBlock;
|
|
932
1150
|
}
|
|
933
1151
|
function mergeBlockEntries(localBlock, remoteBlock) {
|
|
934
1152
|
const entryMap = /* @__PURE__ */ new Map();
|
|
@@ -959,7 +1177,7 @@ function mergeBlockEntries(localBlock, remoteBlock) {
|
|
|
959
1177
|
async function migrateLegacyLedger(workDir) {
|
|
960
1178
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
961
1179
|
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
962
|
-
if (!
|
|
1180
|
+
if (!existsSync5(legacyLedgerPath)) {
|
|
963
1181
|
return false;
|
|
964
1182
|
}
|
|
965
1183
|
try {
|
|
@@ -994,7 +1212,7 @@ async function migrateLegacyLedger(workDir) {
|
|
|
994
1212
|
async function detectLegacyLedger(workDir) {
|
|
995
1213
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
996
1214
|
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
997
|
-
if (!
|
|
1215
|
+
if (!existsSync5(legacyLedgerPath)) {
|
|
998
1216
|
return false;
|
|
999
1217
|
}
|
|
1000
1218
|
try {
|
|
@@ -1047,7 +1265,7 @@ async function compressOldBlocks(workDir) {
|
|
|
1047
1265
|
}
|
|
1048
1266
|
for (const [monthKey, blockFiles] of blocksByMonth) {
|
|
1049
1267
|
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1050
|
-
if (
|
|
1268
|
+
if (existsSync5(archivePath)) {
|
|
1051
1269
|
continue;
|
|
1052
1270
|
}
|
|
1053
1271
|
console.log(`Archiving ${blockFiles.length} blocks for ${monthKey}...`);
|
|
@@ -1055,9 +1273,9 @@ async function compressOldBlocks(workDir) {
|
|
|
1055
1273
|
for (const blockFile of blockFiles) {
|
|
1056
1274
|
const blockPath = join3(activeDir, blockFile);
|
|
1057
1275
|
try {
|
|
1058
|
-
const stats = await
|
|
1276
|
+
const stats = await stat2(blockPath);
|
|
1059
1277
|
originalSize += stats.size;
|
|
1060
|
-
const content = await
|
|
1278
|
+
const content = await readFile2(blockPath, "utf-8");
|
|
1061
1279
|
const block = JSON.parse(content);
|
|
1062
1280
|
monthlyBlocks.push(block);
|
|
1063
1281
|
} catch (error) {
|
|
@@ -1072,13 +1290,13 @@ async function compressOldBlocks(workDir) {
|
|
|
1072
1290
|
createGzip({ level: manifest.compressionConfig.compressionLevel }),
|
|
1073
1291
|
createWriteStream(tempPath)
|
|
1074
1292
|
);
|
|
1075
|
-
const compressedStats = await
|
|
1293
|
+
const compressedStats = await stat2(tempPath);
|
|
1076
1294
|
compressedSize += compressedStats.size;
|
|
1077
|
-
await writeFile(archivePath, await
|
|
1078
|
-
await
|
|
1295
|
+
await writeFile(archivePath, await readFile2(tempPath));
|
|
1296
|
+
await unlink2(tempPath);
|
|
1079
1297
|
for (const blockFile of blockFiles) {
|
|
1080
1298
|
const blockPath = join3(activeDir, blockFile);
|
|
1081
|
-
await
|
|
1299
|
+
await unlink2(blockPath);
|
|
1082
1300
|
const index = manifest.activeBlocks.indexOf(blockFile);
|
|
1083
1301
|
if (index > -1) {
|
|
1084
1302
|
manifest.activeBlocks.splice(index, 1);
|
|
@@ -1099,7 +1317,7 @@ async function compressOldBlocks(workDir) {
|
|
|
1099
1317
|
async function loadArchivedBlocks(projectDir, monthKey) {
|
|
1100
1318
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
1101
1319
|
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1102
|
-
if (!
|
|
1320
|
+
if (!existsSync5(archivePath)) {
|
|
1103
1321
|
return [];
|
|
1104
1322
|
}
|
|
1105
1323
|
try {
|
|
@@ -1141,8 +1359,8 @@ async function getStorageStats(workDir) {
|
|
|
1141
1359
|
for (const blockFile of manifest.activeBlocks) {
|
|
1142
1360
|
const blockPath = join3(activeDir, blockFile);
|
|
1143
1361
|
try {
|
|
1144
|
-
if (
|
|
1145
|
-
const stats = await
|
|
1362
|
+
if (existsSync5(blockPath)) {
|
|
1363
|
+
const stats = await stat2(blockPath);
|
|
1146
1364
|
activeSize += stats.size;
|
|
1147
1365
|
}
|
|
1148
1366
|
} catch {
|
|
@@ -1151,8 +1369,8 @@ async function getStorageStats(workDir) {
|
|
|
1151
1369
|
for (const archiveFile of manifest.archivedBlocks) {
|
|
1152
1370
|
const archivePath = join3(archivedDir, archiveFile);
|
|
1153
1371
|
try {
|
|
1154
|
-
if (
|
|
1155
|
-
const stats = await
|
|
1372
|
+
if (existsSync5(archivePath)) {
|
|
1373
|
+
const stats = await stat2(archivePath);
|
|
1156
1374
|
archivedSize += stats.size;
|
|
1157
1375
|
}
|
|
1158
1376
|
} catch {
|
|
@@ -1201,94 +1419,100 @@ async function correctLedgerEntries(entryIds, reason, correctionType = "correcte
|
|
|
1201
1419
|
};
|
|
1202
1420
|
}
|
|
1203
1421
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1204
|
-
const
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
entriesToCorrect.push(entry);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
if (entriesToCorrect.length === 0) {
|
|
1214
|
-
return {
|
|
1215
|
-
success: false,
|
|
1216
|
-
correctedEntries: 0,
|
|
1217
|
-
error: "No active entries found with the provided IDs"
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
try {
|
|
1221
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1222
|
-
const isRepo = await isGitRepo(projectDir);
|
|
1223
|
-
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
1224
|
-
const correctionAuthor = author || lastCommit?.author || "unknown";
|
|
1225
|
-
const correctionEntries = entriesToCorrect.map((entry) => {
|
|
1226
|
-
const correctionId = `correction-${entry.id}-${Date.now()}`;
|
|
1227
|
-
return {
|
|
1228
|
-
id: correctionId,
|
|
1229
|
-
hash: sha256(`${correctionId}:${entry.hash}:${reason}:${now}`),
|
|
1230
|
-
severity: "info",
|
|
1231
|
-
file: entry.file,
|
|
1232
|
-
agent: "ledger-correction",
|
|
1233
|
-
timestamp: now,
|
|
1234
|
-
status: "active",
|
|
1235
|
-
correction: `Correcting entry ${entry.id}: ${reason}`,
|
|
1236
|
-
correctedBy: entry.id
|
|
1237
|
-
};
|
|
1238
|
-
});
|
|
1422
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1423
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
1424
|
+
return withFileLock(ledgerPath, async () => {
|
|
1425
|
+
const blocks = await loadLedger(projectDir);
|
|
1426
|
+
const entriesToCorrect = [];
|
|
1239
1427
|
for (const block of blocks) {
|
|
1240
|
-
let blockModified = false;
|
|
1241
1428
|
for (const entry of block.entries) {
|
|
1242
1429
|
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1243
|
-
entry
|
|
1244
|
-
entry.correctionTimestamp = now;
|
|
1245
|
-
entry.correction = reason;
|
|
1246
|
-
blockModified = true;
|
|
1430
|
+
entriesToCorrect.push(entry);
|
|
1247
1431
|
}
|
|
1248
1432
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1433
|
+
}
|
|
1434
|
+
if (entriesToCorrect.length === 0) {
|
|
1435
|
+
return {
|
|
1436
|
+
success: false,
|
|
1437
|
+
correctedEntries: 0,
|
|
1438
|
+
error: "No active entries found with the provided IDs"
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
try {
|
|
1442
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1443
|
+
const isRepo = await isGitRepo(projectDir);
|
|
1444
|
+
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
1445
|
+
const correctionAuthor = author || lastCommit?.author || "unknown";
|
|
1446
|
+
const correctionEntries = entriesToCorrect.map((entry) => {
|
|
1447
|
+
const correctionId = `correction-${entry.id}-${Date.now()}`;
|
|
1448
|
+
return {
|
|
1449
|
+
id: correctionId,
|
|
1450
|
+
hash: sha256(`${correctionId}:${entry.hash}:${reason}:${now}`),
|
|
1451
|
+
severity: "info",
|
|
1452
|
+
file: entry.file,
|
|
1453
|
+
agent: "ledger-correction",
|
|
1454
|
+
timestamp: now,
|
|
1455
|
+
status: "active",
|
|
1456
|
+
correction: `Correcting entry ${entry.id}: ${reason}`,
|
|
1457
|
+
correctedBy: entry.id
|
|
1458
|
+
};
|
|
1459
|
+
});
|
|
1460
|
+
for (const block of blocks) {
|
|
1461
|
+
let blockModified = false;
|
|
1462
|
+
for (const entry of block.entries) {
|
|
1463
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1464
|
+
entry.status = correctionType;
|
|
1465
|
+
entry.correctionTimestamp = now;
|
|
1466
|
+
entry.correction = reason;
|
|
1467
|
+
blockModified = true;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (blockModified) {
|
|
1471
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((e) => e.hash));
|
|
1472
|
+
block.blockHash = computeBlockHash(
|
|
1473
|
+
block.previousHash,
|
|
1474
|
+
block.merkleRoot,
|
|
1475
|
+
block.date,
|
|
1476
|
+
block.version
|
|
1477
|
+
);
|
|
1478
|
+
block.updatedAt = now;
|
|
1479
|
+
}
|
|
1258
1480
|
}
|
|
1481
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
1482
|
+
const correctionBlock = await appendCorrectionEntries(
|
|
1483
|
+
correctionEntries,
|
|
1484
|
+
projectDir,
|
|
1485
|
+
correctionAuthor
|
|
1486
|
+
);
|
|
1487
|
+
return {
|
|
1488
|
+
success: true,
|
|
1489
|
+
correctedEntries: entriesToCorrect.length,
|
|
1490
|
+
...correctionBlock && { correctionBlock }
|
|
1491
|
+
};
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
return {
|
|
1494
|
+
success: false,
|
|
1495
|
+
correctedEntries: 0,
|
|
1496
|
+
error: `Failed to correct entries: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1497
|
+
};
|
|
1259
1498
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
agent: entry.agent,
|
|
1271
|
-
category: "correction",
|
|
1272
|
-
timestamp: entry.timestamp,
|
|
1273
|
-
project: "",
|
|
1274
|
-
resolved: false,
|
|
1275
|
-
resolvedAt: void 0
|
|
1276
|
-
})),
|
|
1277
|
-
projectDir,
|
|
1278
|
-
correctionAuthor
|
|
1279
|
-
);
|
|
1280
|
-
return {
|
|
1281
|
-
success: true,
|
|
1282
|
-
correctedEntries: entriesToCorrect.length,
|
|
1283
|
-
...correctionBlock && { correctionBlock }
|
|
1284
|
-
};
|
|
1285
|
-
} catch (error) {
|
|
1286
|
-
return {
|
|
1287
|
-
success: false,
|
|
1288
|
-
correctedEntries: 0,
|
|
1289
|
-
error: `Failed to correct entries: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1290
|
-
};
|
|
1499
|
+
}, { timeout: 15e3 });
|
|
1500
|
+
}
|
|
1501
|
+
async function appendCorrectionEntries(correctionEntries, projectDir, author) {
|
|
1502
|
+
const blocks = await loadLedger(projectDir);
|
|
1503
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1504
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1505
|
+
const previousBlock = blocks[blocks.length - 1];
|
|
1506
|
+
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, author, void 0, blocks.length);
|
|
1507
|
+
if (block !== previousBlock) {
|
|
1508
|
+
blocks.push(block);
|
|
1291
1509
|
}
|
|
1510
|
+
block.entries = [...block.entries, ...correctionEntries];
|
|
1511
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((entry) => entry.hash));
|
|
1512
|
+
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
1513
|
+
block.updatedAt = now;
|
|
1514
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
1515
|
+
return block;
|
|
1292
1516
|
}
|
|
1293
1517
|
async function getLedgerEntries(workDir, includeStatus) {
|
|
1294
1518
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
@@ -1355,6 +1579,7 @@ async function getCorrectionStats(workDir) {
|
|
|
1355
1579
|
}
|
|
1356
1580
|
|
|
1357
1581
|
export {
|
|
1582
|
+
withFileLock,
|
|
1358
1583
|
formatAuditLog,
|
|
1359
1584
|
getAuditStatistics,
|
|
1360
1585
|
getRecentAuditLogs,
|
|
@@ -1367,6 +1592,12 @@ export {
|
|
|
1367
1592
|
getWorkingTreeDiff,
|
|
1368
1593
|
isGitRepo,
|
|
1369
1594
|
getChangedFilesSinceTimestamp,
|
|
1595
|
+
generateKeyPair,
|
|
1596
|
+
saveKeyPair,
|
|
1597
|
+
loadKeyPair,
|
|
1598
|
+
getPublicKey,
|
|
1599
|
+
hasSigningKey,
|
|
1600
|
+
ConcurrentModificationError,
|
|
1370
1601
|
signLedgerEntry,
|
|
1371
1602
|
verifyLedgerEntry,
|
|
1372
1603
|
verifyBlockSignatures,
|
|
@@ -1374,6 +1605,8 @@ export {
|
|
|
1374
1605
|
verifyLedger,
|
|
1375
1606
|
computeMerkleRoot,
|
|
1376
1607
|
getLedgerBlocks,
|
|
1608
|
+
getLedgerBlocksWithHash,
|
|
1609
|
+
saveLedgerOptimistic,
|
|
1377
1610
|
initializeSharedLedger,
|
|
1378
1611
|
syncLedgerFromShared,
|
|
1379
1612
|
pushLedgerToShared,
|
|
@@ -1389,4 +1622,4 @@ export {
|
|
|
1389
1622
|
getEntryCorrectionHistory,
|
|
1390
1623
|
getCorrectionStats
|
|
1391
1624
|
};
|
|
1392
|
-
//# sourceMappingURL=chunk-
|
|
1625
|
+
//# sourceMappingURL=chunk-YAL3SUBG.js.map
|