@triedotdev/mcp 1.0.164 → 1.0.166
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-5TRCQAOE.js → chunk-2Z3TQNNK.js} +16 -5
- package/dist/chunk-2Z3TQNNK.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-D6E4Q4I6.js → chunk-HOGKPDZA.js} +302 -58
- package/dist/chunk-HOGKPDZA.js.map +1 -0
- package/dist/{chunk-LR46VMIE.js → chunk-JEZ7XJQN.js} +5 -5
- package/dist/{chunk-ERMLZJTK.js → chunk-JNUOW2JS.js} +13 -13
- package/dist/{chunk-62JD7MIS.js → chunk-LG5CBK6A.js} +15 -35
- package/dist/chunk-LG5CBK6A.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-Y4B3VEL7.js → chunk-MVVPJ73K.js} +438 -202
- package/dist/chunk-MVVPJ73K.js.map +1 -0
- package/dist/chunk-OBQ74FOU.js +27 -0
- package/dist/chunk-OBQ74FOU.js.map +1 -0
- package/dist/{chunk-IRZXBQVQ.js → chunk-S36IO3EE.js} +134 -101
- package/dist/chunk-S36IO3EE.js.map +1 -0
- package/dist/{chunk-HFVPHQL3.js → chunk-TQOO6A4G.js} +9 -9
- package/dist/{chunk-OKK4QNK3.js → chunk-UXRW2YSP.js} +86 -12
- package/dist/chunk-UXRW2YSP.js.map +1 -0
- package/dist/{chunk-ACU3IXZG.js → chunk-ZKKKLRZZ.js} +7 -7
- 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-NJQO3TFD.js} +3 -3
- package/dist/{goal-manager-D6XKE3FY.js → goal-manager-DVX24UPZ.js} +5 -5
- package/dist/{goal-validator-4DDL7NBP.js → goal-validator-6Y5CDEMJ.js} +5 -5
- package/dist/{hypothesis-RI3Q33JB.js → hypothesis-UKPGOYY2.js} +5 -5
- package/dist/index.js +16 -15
- package/dist/index.js.map +1 -1
- package/dist/{issue-store-DUR5UTYK.js → issue-store-UZAPI5DU.js} +3 -3
- package/dist/{ledger-ZTR63P3L.js → ledger-CNFCJKHX.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-6SWUHCVO.js} +12 -12
- package/dist/trie-agent-6SWUHCVO.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-D6E4Q4I6.js.map +0 -1
- package/dist/chunk-IRZXBQVQ.js.map +0 -1
- package/dist/chunk-LLDZDU2Y.js.map +0 -1
- package/dist/chunk-OKK4QNK3.js.map +0 -1
- package/dist/chunk-Y4B3VEL7.js.map +0 -1
- /package/dist/{chunk-LR46VMIE.js.map → chunk-JEZ7XJQN.js.map} +0 -0
- /package/dist/{chunk-ERMLZJTK.js.map → chunk-JNUOW2JS.js.map} +0 -0
- /package/dist/{chunk-HFVPHQL3.js.map → chunk-TQOO6A4G.js.map} +0 -0
- /package/dist/{chunk-ACU3IXZG.js.map → chunk-ZKKKLRZZ.js.map} +0 -0
- /package/dist/{fast-analyzer-LLZ6FLP5.js.map → fast-analyzer-NJQO3TFD.js.map} +0 -0
- /package/dist/{goal-manager-D6XKE3FY.js.map → goal-manager-DVX24UPZ.js.map} +0 -0
- /package/dist/{goal-validator-4DDL7NBP.js.map → goal-validator-6Y5CDEMJ.js.map} +0 -0
- /package/dist/{hypothesis-RI3Q33JB.js.map → hypothesis-UKPGOYY2.js.map} +0 -0
- /package/dist/{issue-store-DUR5UTYK.js.map → issue-store-UZAPI5DU.js.map} +0 -0
- /package/dist/{ledger-ZTR63P3L.js.map → ledger-CNFCJKHX.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,48 @@ 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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
+
// Include semantic content for ambient awareness
|
|
701
|
+
issue: issue.issue,
|
|
702
|
+
fix: issue.fix
|
|
703
|
+
}));
|
|
704
|
+
if (shouldSign) {
|
|
705
|
+
entries = await Promise.all(entries.map((entry) => signLedgerEntry(entry, projectDir)));
|
|
706
|
+
}
|
|
707
|
+
const previousBlock = blocks[blocks.length - 1];
|
|
708
|
+
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, blockAuthor, lastCommit?.hash, blocks.length);
|
|
709
|
+
if (block !== previousBlock) {
|
|
710
|
+
blocks.push(block);
|
|
711
|
+
}
|
|
712
|
+
block.entries = [...block.entries, ...entries];
|
|
713
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((entry) => entry.hash));
|
|
714
|
+
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
715
|
+
block.updatedAt = now;
|
|
716
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
717
|
+
if (await isGitRepo(projectDir)) {
|
|
718
|
+
await ensureKeysIgnored(projectDir);
|
|
719
|
+
const commitMessage = `ledger: append ${entries.length} ${entries.length === 1 ? "entry" : "entries"}`;
|
|
720
|
+
await autoCommitLedger(projectDir, commitMessage);
|
|
721
|
+
}
|
|
722
|
+
return block;
|
|
723
|
+
}, { timeout: 15e3 });
|
|
580
724
|
}
|
|
581
725
|
async function verifyLedger(workDir) {
|
|
582
726
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
@@ -649,24 +793,80 @@ function createSyncableBlock(date, now, previousHash, author, gitCommit, chainHe
|
|
|
649
793
|
};
|
|
650
794
|
}
|
|
651
795
|
async function loadLedger(projectDir) {
|
|
796
|
+
const result = await loadLedgerWithHash(projectDir);
|
|
797
|
+
return result.blocks;
|
|
798
|
+
}
|
|
799
|
+
async function loadLedgerWithHash(projectDir) {
|
|
652
800
|
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
653
801
|
try {
|
|
654
|
-
if (!
|
|
655
|
-
|
|
802
|
+
if (!existsSync5(ledgerPath)) {
|
|
803
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
804
|
+
}
|
|
805
|
+
const content = await readFile2(ledgerPath, "utf-8");
|
|
656
806
|
const parsed = JSON.parse(content);
|
|
657
|
-
if (
|
|
658
|
-
|
|
807
|
+
if (parsed && parsed._format === "ledger-v2") {
|
|
808
|
+
const file = parsed;
|
|
809
|
+
return {
|
|
810
|
+
blocks: file.blocks || [],
|
|
811
|
+
contentHash: file._contentHash
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
if (!Array.isArray(parsed)) {
|
|
815
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
816
|
+
}
|
|
817
|
+
const blocks = parsed;
|
|
818
|
+
const contentHash = sha256(JSON.stringify(blocks));
|
|
819
|
+
return { blocks, contentHash };
|
|
659
820
|
} catch {
|
|
660
|
-
return [];
|
|
821
|
+
return { blocks: [], contentHash: sha256("[]") };
|
|
661
822
|
}
|
|
662
823
|
}
|
|
663
824
|
async function getLedgerBlocks(workDir) {
|
|
664
825
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
665
826
|
return loadLedger(projectDir);
|
|
666
827
|
}
|
|
828
|
+
async function getLedgerBlocksWithHash(workDir) {
|
|
829
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
830
|
+
return loadLedgerWithHash(projectDir);
|
|
831
|
+
}
|
|
832
|
+
async function saveLedgerOptimistic(blocks, expectedHash, workDir) {
|
|
833
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
834
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
835
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
836
|
+
await withFileLock(ledgerPath, async () => {
|
|
837
|
+
await saveLedgerWithConcurrencyCheck(blocks, projectDir, expectedHash);
|
|
838
|
+
}, { timeout: 1e4 });
|
|
839
|
+
}
|
|
667
840
|
async function saveLedger(blocks, projectDir) {
|
|
668
841
|
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
669
|
-
await
|
|
842
|
+
await withFileLock(ledgerPath, async () => {
|
|
843
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
844
|
+
}, { timeout: 1e4 });
|
|
845
|
+
}
|
|
846
|
+
async function saveLedgerInternal(blocks, projectDir) {
|
|
847
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
848
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
849
|
+
}
|
|
850
|
+
async function saveLedgerWithConcurrencyCheck(blocks, projectDir, expectedHash) {
|
|
851
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
852
|
+
const { contentHash: currentHash } = await loadLedgerWithHash(projectDir);
|
|
853
|
+
if (currentHash !== expectedHash) {
|
|
854
|
+
throw new ConcurrentModificationError(
|
|
855
|
+
`Ledger was modified by another process. Expected hash: ${expectedHash.slice(0, 16)}..., Current hash: ${currentHash.slice(0, 16)}...`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
await saveLedgerWithFormat(ledgerPath, blocks);
|
|
859
|
+
}
|
|
860
|
+
async function saveLedgerWithFormat(ledgerPath, blocks) {
|
|
861
|
+
const blocksJson = JSON.stringify(blocks);
|
|
862
|
+
const contentHash = sha256(blocksJson);
|
|
863
|
+
const ledgerFile = {
|
|
864
|
+
_format: "ledger-v2",
|
|
865
|
+
_contentHash: contentHash,
|
|
866
|
+
_lastModified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
867
|
+
blocks
|
|
868
|
+
};
|
|
869
|
+
await atomicWriteJSON(ledgerPath, ledgerFile);
|
|
670
870
|
}
|
|
671
871
|
function sha256(input) {
|
|
672
872
|
return createHash("sha256").update(input).digest("hex");
|
|
@@ -690,15 +890,15 @@ async function ensureSharedStorageStructure(projectDir) {
|
|
|
690
890
|
const sharedDir = getSharedLedgerDir(projectDir);
|
|
691
891
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
692
892
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
693
|
-
await
|
|
694
|
-
await
|
|
695
|
-
await
|
|
893
|
+
await mkdir2(sharedDir, { recursive: true });
|
|
894
|
+
await mkdir2(activeDir, { recursive: true });
|
|
895
|
+
await mkdir2(archivedDir, { recursive: true });
|
|
696
896
|
}
|
|
697
897
|
async function loadManifest(projectDir) {
|
|
698
898
|
const manifestPath = getManifestPath(projectDir);
|
|
699
899
|
try {
|
|
700
|
-
if (!
|
|
701
|
-
const content = await
|
|
900
|
+
if (!existsSync5(manifestPath)) return null;
|
|
901
|
+
const content = await readFile2(manifestPath, "utf-8");
|
|
702
902
|
return JSON.parse(content);
|
|
703
903
|
} catch {
|
|
704
904
|
return null;
|
|
@@ -735,8 +935,8 @@ async function createDefaultManifest(_projectDir) {
|
|
|
735
935
|
async function loadSyncState(projectDir) {
|
|
736
936
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
737
937
|
try {
|
|
738
|
-
if (!
|
|
739
|
-
const content = await
|
|
938
|
+
if (!existsSync5(syncStatePath)) return null;
|
|
939
|
+
const content = await readFile2(syncStatePath, "utf-8");
|
|
740
940
|
return JSON.parse(content);
|
|
741
941
|
} catch {
|
|
742
942
|
return null;
|
|
@@ -745,7 +945,7 @@ async function loadSyncState(projectDir) {
|
|
|
745
945
|
async function saveSyncState(syncState, projectDir) {
|
|
746
946
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
747
947
|
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
748
|
-
await
|
|
948
|
+
await mkdir2(memoryDir, { recursive: true });
|
|
749
949
|
await atomicWriteJSON(syncStatePath, syncState);
|
|
750
950
|
}
|
|
751
951
|
async function initializeSharedLedger(workDir) {
|
|
@@ -770,54 +970,62 @@ async function initializeSharedLedger(workDir) {
|
|
|
770
970
|
}
|
|
771
971
|
async function syncLedgerFromShared(workDir) {
|
|
772
972
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
973
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
974
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
773
975
|
await initializeSharedLedger(projectDir);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
976
|
+
return withFileLock(ledgerPath, async () => {
|
|
977
|
+
const manifest = await loadManifest(projectDir);
|
|
978
|
+
const localBlocks = await loadLedger(projectDir);
|
|
979
|
+
const sharedBlocks = await loadSharedBlocks(projectDir);
|
|
980
|
+
if (!manifest) {
|
|
981
|
+
throw new Error("Failed to load ledger manifest");
|
|
982
|
+
}
|
|
983
|
+
const mergeResult = await mergeChains(localBlocks, sharedBlocks, "timestamp");
|
|
984
|
+
await saveLedgerInternal(mergeResult.mergedChain, projectDir);
|
|
985
|
+
const syncState = {
|
|
986
|
+
lastSyncTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
987
|
+
conflicts: mergeResult.conflicts,
|
|
988
|
+
localChanges: false,
|
|
989
|
+
sharedChanges: false
|
|
990
|
+
};
|
|
991
|
+
await saveSyncState(syncState, projectDir);
|
|
992
|
+
return mergeResult;
|
|
993
|
+
}, { timeout: 3e4 });
|
|
790
994
|
}
|
|
791
995
|
async function pushLedgerToShared(workDir) {
|
|
792
996
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
997
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
998
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
793
999
|
await initializeSharedLedger(projectDir);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (
|
|
806
|
-
manifest.index.byAuthor[blockAuthor]
|
|
1000
|
+
await withFileLock(ledgerPath, async () => {
|
|
1001
|
+
const localBlocks = await loadLedger(projectDir);
|
|
1002
|
+
const manifest = await loadManifest(projectDir) || await createDefaultManifest(projectDir);
|
|
1003
|
+
const activeDir = getActiveBlocksDir(projectDir);
|
|
1004
|
+
for (const block of localBlocks) {
|
|
1005
|
+
const blockFilename = `${block.date}.json`;
|
|
1006
|
+
const blockPath = join3(activeDir, blockFilename);
|
|
1007
|
+
if (!existsSync5(blockPath) || block.updatedAt > manifest.lastSync) {
|
|
1008
|
+
await atomicWriteJSON(blockPath, block);
|
|
1009
|
+
manifest.index.byDate[block.date] = `active/${blockFilename}`;
|
|
1010
|
+
const blockAuthor = block.author;
|
|
1011
|
+
if (blockAuthor) {
|
|
1012
|
+
if (!manifest.index.byAuthor[blockAuthor]) {
|
|
1013
|
+
manifest.index.byAuthor[blockAuthor] = [];
|
|
1014
|
+
}
|
|
1015
|
+
if (!manifest.index.byAuthor[blockAuthor].includes(`active/${blockFilename}`)) {
|
|
1016
|
+
manifest.index.byAuthor[blockAuthor].push(`active/${blockFilename}`);
|
|
1017
|
+
}
|
|
807
1018
|
}
|
|
808
|
-
if (!manifest.
|
|
809
|
-
manifest.
|
|
1019
|
+
if (!manifest.activeBlocks.includes(blockFilename)) {
|
|
1020
|
+
manifest.activeBlocks.push(blockFilename);
|
|
810
1021
|
}
|
|
811
1022
|
}
|
|
812
|
-
if (!manifest.activeBlocks.includes(blockFilename)) {
|
|
813
|
-
manifest.activeBlocks.push(blockFilename);
|
|
814
|
-
}
|
|
815
1023
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1024
|
+
manifest.lastSync = (/* @__PURE__ */ new Date()).toISOString();
|
|
1025
|
+
manifest.totalBlocks = manifest.activeBlocks.length + manifest.archivedBlocks.length;
|
|
1026
|
+
manifest.totalEntries = localBlocks.reduce((sum, block) => sum + block.entries.length, 0);
|
|
1027
|
+
await saveManifest(manifest, projectDir);
|
|
1028
|
+
}, { timeout: 3e4 });
|
|
821
1029
|
}
|
|
822
1030
|
async function loadSharedBlocks(projectDir) {
|
|
823
1031
|
const manifest = await loadManifest(projectDir);
|
|
@@ -827,8 +1035,8 @@ async function loadSharedBlocks(projectDir) {
|
|
|
827
1035
|
for (const filename of manifest.activeBlocks) {
|
|
828
1036
|
const blockPath = join3(activeDir, filename);
|
|
829
1037
|
try {
|
|
830
|
-
if (
|
|
831
|
-
const content = await
|
|
1038
|
+
if (existsSync5(blockPath)) {
|
|
1039
|
+
const content = await readFile2(blockPath, "utf-8");
|
|
832
1040
|
const block = JSON.parse(content);
|
|
833
1041
|
blocks.push(block);
|
|
834
1042
|
}
|
|
@@ -919,16 +1127,29 @@ function resolveConflict(conflict, strategy) {
|
|
|
919
1127
|
if (!conflict.localBlock || !conflict.remoteBlock) {
|
|
920
1128
|
return null;
|
|
921
1129
|
}
|
|
1130
|
+
const mergedBlock = mergeBlockEntries(conflict.localBlock, conflict.remoteBlock);
|
|
1131
|
+
mergedBlock.conflictResolved = true;
|
|
922
1132
|
switch (strategy) {
|
|
923
|
-
case "longest":
|
|
924
|
-
return conflict.localBlock.entries.length >= conflict.remoteBlock.entries.length ? conflict.localBlock : conflict.remoteBlock;
|
|
925
1133
|
case "timestamp":
|
|
926
|
-
|
|
1134
|
+
if (conflict.remoteBlock.updatedAt > conflict.localBlock.updatedAt) {
|
|
1135
|
+
mergedBlock.author = `${conflict.remoteBlock.author}+${conflict.localBlock.author}`;
|
|
1136
|
+
} else {
|
|
1137
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1138
|
+
}
|
|
1139
|
+
break;
|
|
1140
|
+
case "longest":
|
|
1141
|
+
if (conflict.remoteBlock.entries.length > conflict.localBlock.entries.length) {
|
|
1142
|
+
mergedBlock.author = `${conflict.remoteBlock.author}+${conflict.localBlock.author}`;
|
|
1143
|
+
} else {
|
|
1144
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1145
|
+
}
|
|
1146
|
+
break;
|
|
927
1147
|
case "manual":
|
|
928
|
-
return { ...conflict.localBlock, conflictResolved: false };
|
|
929
1148
|
default:
|
|
930
|
-
|
|
1149
|
+
mergedBlock.author = `${conflict.localBlock.author}+${conflict.remoteBlock.author}`;
|
|
1150
|
+
break;
|
|
931
1151
|
}
|
|
1152
|
+
return mergedBlock;
|
|
932
1153
|
}
|
|
933
1154
|
function mergeBlockEntries(localBlock, remoteBlock) {
|
|
934
1155
|
const entryMap = /* @__PURE__ */ new Map();
|
|
@@ -959,7 +1180,7 @@ function mergeBlockEntries(localBlock, remoteBlock) {
|
|
|
959
1180
|
async function migrateLegacyLedger(workDir) {
|
|
960
1181
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
961
1182
|
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
962
|
-
if (!
|
|
1183
|
+
if (!existsSync5(legacyLedgerPath)) {
|
|
963
1184
|
return false;
|
|
964
1185
|
}
|
|
965
1186
|
try {
|
|
@@ -994,7 +1215,7 @@ async function migrateLegacyLedger(workDir) {
|
|
|
994
1215
|
async function detectLegacyLedger(workDir) {
|
|
995
1216
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
996
1217
|
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
997
|
-
if (!
|
|
1218
|
+
if (!existsSync5(legacyLedgerPath)) {
|
|
998
1219
|
return false;
|
|
999
1220
|
}
|
|
1000
1221
|
try {
|
|
@@ -1047,7 +1268,7 @@ async function compressOldBlocks(workDir) {
|
|
|
1047
1268
|
}
|
|
1048
1269
|
for (const [monthKey, blockFiles] of blocksByMonth) {
|
|
1049
1270
|
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1050
|
-
if (
|
|
1271
|
+
if (existsSync5(archivePath)) {
|
|
1051
1272
|
continue;
|
|
1052
1273
|
}
|
|
1053
1274
|
console.log(`Archiving ${blockFiles.length} blocks for ${monthKey}...`);
|
|
@@ -1055,9 +1276,9 @@ async function compressOldBlocks(workDir) {
|
|
|
1055
1276
|
for (const blockFile of blockFiles) {
|
|
1056
1277
|
const blockPath = join3(activeDir, blockFile);
|
|
1057
1278
|
try {
|
|
1058
|
-
const stats = await
|
|
1279
|
+
const stats = await stat2(blockPath);
|
|
1059
1280
|
originalSize += stats.size;
|
|
1060
|
-
const content = await
|
|
1281
|
+
const content = await readFile2(blockPath, "utf-8");
|
|
1061
1282
|
const block = JSON.parse(content);
|
|
1062
1283
|
monthlyBlocks.push(block);
|
|
1063
1284
|
} catch (error) {
|
|
@@ -1072,13 +1293,13 @@ async function compressOldBlocks(workDir) {
|
|
|
1072
1293
|
createGzip({ level: manifest.compressionConfig.compressionLevel }),
|
|
1073
1294
|
createWriteStream(tempPath)
|
|
1074
1295
|
);
|
|
1075
|
-
const compressedStats = await
|
|
1296
|
+
const compressedStats = await stat2(tempPath);
|
|
1076
1297
|
compressedSize += compressedStats.size;
|
|
1077
|
-
await writeFile(archivePath, await
|
|
1078
|
-
await
|
|
1298
|
+
await writeFile(archivePath, await readFile2(tempPath));
|
|
1299
|
+
await unlink2(tempPath);
|
|
1079
1300
|
for (const blockFile of blockFiles) {
|
|
1080
1301
|
const blockPath = join3(activeDir, blockFile);
|
|
1081
|
-
await
|
|
1302
|
+
await unlink2(blockPath);
|
|
1082
1303
|
const index = manifest.activeBlocks.indexOf(blockFile);
|
|
1083
1304
|
if (index > -1) {
|
|
1084
1305
|
manifest.activeBlocks.splice(index, 1);
|
|
@@ -1099,7 +1320,7 @@ async function compressOldBlocks(workDir) {
|
|
|
1099
1320
|
async function loadArchivedBlocks(projectDir, monthKey) {
|
|
1100
1321
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
1101
1322
|
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1102
|
-
if (!
|
|
1323
|
+
if (!existsSync5(archivePath)) {
|
|
1103
1324
|
return [];
|
|
1104
1325
|
}
|
|
1105
1326
|
try {
|
|
@@ -1141,8 +1362,8 @@ async function getStorageStats(workDir) {
|
|
|
1141
1362
|
for (const blockFile of manifest.activeBlocks) {
|
|
1142
1363
|
const blockPath = join3(activeDir, blockFile);
|
|
1143
1364
|
try {
|
|
1144
|
-
if (
|
|
1145
|
-
const stats = await
|
|
1365
|
+
if (existsSync5(blockPath)) {
|
|
1366
|
+
const stats = await stat2(blockPath);
|
|
1146
1367
|
activeSize += stats.size;
|
|
1147
1368
|
}
|
|
1148
1369
|
} catch {
|
|
@@ -1151,8 +1372,8 @@ async function getStorageStats(workDir) {
|
|
|
1151
1372
|
for (const archiveFile of manifest.archivedBlocks) {
|
|
1152
1373
|
const archivePath = join3(archivedDir, archiveFile);
|
|
1153
1374
|
try {
|
|
1154
|
-
if (
|
|
1155
|
-
const stats = await
|
|
1375
|
+
if (existsSync5(archivePath)) {
|
|
1376
|
+
const stats = await stat2(archivePath);
|
|
1156
1377
|
archivedSize += stats.size;
|
|
1157
1378
|
}
|
|
1158
1379
|
} catch {
|
|
@@ -1201,94 +1422,100 @@ async function correctLedgerEntries(entryIds, reason, correctionType = "correcte
|
|
|
1201
1422
|
};
|
|
1202
1423
|
}
|
|
1203
1424
|
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
|
-
});
|
|
1425
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1426
|
+
const ledgerPath = join3(memoryDir, LEDGER_FILENAME);
|
|
1427
|
+
return withFileLock(ledgerPath, async () => {
|
|
1428
|
+
const blocks = await loadLedger(projectDir);
|
|
1429
|
+
const entriesToCorrect = [];
|
|
1239
1430
|
for (const block of blocks) {
|
|
1240
|
-
let blockModified = false;
|
|
1241
1431
|
for (const entry of block.entries) {
|
|
1242
1432
|
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1243
|
-
entry
|
|
1244
|
-
entry.correctionTimestamp = now;
|
|
1245
|
-
entry.correction = reason;
|
|
1246
|
-
blockModified = true;
|
|
1433
|
+
entriesToCorrect.push(entry);
|
|
1247
1434
|
}
|
|
1248
1435
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1436
|
+
}
|
|
1437
|
+
if (entriesToCorrect.length === 0) {
|
|
1438
|
+
return {
|
|
1439
|
+
success: false,
|
|
1440
|
+
correctedEntries: 0,
|
|
1441
|
+
error: "No active entries found with the provided IDs"
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
try {
|
|
1445
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1446
|
+
const isRepo = await isGitRepo(projectDir);
|
|
1447
|
+
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
1448
|
+
const correctionAuthor = author || lastCommit?.author || "unknown";
|
|
1449
|
+
const correctionEntries = entriesToCorrect.map((entry) => {
|
|
1450
|
+
const correctionId = `correction-${entry.id}-${Date.now()}`;
|
|
1451
|
+
return {
|
|
1452
|
+
id: correctionId,
|
|
1453
|
+
hash: sha256(`${correctionId}:${entry.hash}:${reason}:${now}`),
|
|
1454
|
+
severity: "info",
|
|
1455
|
+
file: entry.file,
|
|
1456
|
+
agent: "ledger-correction",
|
|
1457
|
+
timestamp: now,
|
|
1458
|
+
status: "active",
|
|
1459
|
+
correction: `Correcting entry ${entry.id}: ${reason}`,
|
|
1460
|
+
correctedBy: entry.id
|
|
1461
|
+
};
|
|
1462
|
+
});
|
|
1463
|
+
for (const block of blocks) {
|
|
1464
|
+
let blockModified = false;
|
|
1465
|
+
for (const entry of block.entries) {
|
|
1466
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1467
|
+
entry.status = correctionType;
|
|
1468
|
+
entry.correctionTimestamp = now;
|
|
1469
|
+
entry.correction = reason;
|
|
1470
|
+
blockModified = true;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (blockModified) {
|
|
1474
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((e) => e.hash));
|
|
1475
|
+
block.blockHash = computeBlockHash(
|
|
1476
|
+
block.previousHash,
|
|
1477
|
+
block.merkleRoot,
|
|
1478
|
+
block.date,
|
|
1479
|
+
block.version
|
|
1480
|
+
);
|
|
1481
|
+
block.updatedAt = now;
|
|
1482
|
+
}
|
|
1258
1483
|
}
|
|
1484
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
1485
|
+
const correctionBlock = await appendCorrectionEntries(
|
|
1486
|
+
correctionEntries,
|
|
1487
|
+
projectDir,
|
|
1488
|
+
correctionAuthor
|
|
1489
|
+
);
|
|
1490
|
+
return {
|
|
1491
|
+
success: true,
|
|
1492
|
+
correctedEntries: entriesToCorrect.length,
|
|
1493
|
+
...correctionBlock && { correctionBlock }
|
|
1494
|
+
};
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
return {
|
|
1497
|
+
success: false,
|
|
1498
|
+
correctedEntries: 0,
|
|
1499
|
+
error: `Failed to correct entries: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1500
|
+
};
|
|
1259
1501
|
}
|
|
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
|
-
};
|
|
1502
|
+
}, { timeout: 15e3 });
|
|
1503
|
+
}
|
|
1504
|
+
async function appendCorrectionEntries(correctionEntries, projectDir, author) {
|
|
1505
|
+
const blocks = await loadLedger(projectDir);
|
|
1506
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1507
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1508
|
+
const previousBlock = blocks[blocks.length - 1];
|
|
1509
|
+
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, author, void 0, blocks.length);
|
|
1510
|
+
if (block !== previousBlock) {
|
|
1511
|
+
blocks.push(block);
|
|
1291
1512
|
}
|
|
1513
|
+
block.entries = [...block.entries, ...correctionEntries];
|
|
1514
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((entry) => entry.hash));
|
|
1515
|
+
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
1516
|
+
block.updatedAt = now;
|
|
1517
|
+
await saveLedgerInternal(blocks, projectDir);
|
|
1518
|
+
return block;
|
|
1292
1519
|
}
|
|
1293
1520
|
async function getLedgerEntries(workDir, includeStatus) {
|
|
1294
1521
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
@@ -1355,6 +1582,7 @@ async function getCorrectionStats(workDir) {
|
|
|
1355
1582
|
}
|
|
1356
1583
|
|
|
1357
1584
|
export {
|
|
1585
|
+
withFileLock,
|
|
1358
1586
|
formatAuditLog,
|
|
1359
1587
|
getAuditStatistics,
|
|
1360
1588
|
getRecentAuditLogs,
|
|
@@ -1367,6 +1595,12 @@ export {
|
|
|
1367
1595
|
getWorkingTreeDiff,
|
|
1368
1596
|
isGitRepo,
|
|
1369
1597
|
getChangedFilesSinceTimestamp,
|
|
1598
|
+
generateKeyPair,
|
|
1599
|
+
saveKeyPair,
|
|
1600
|
+
loadKeyPair,
|
|
1601
|
+
getPublicKey,
|
|
1602
|
+
hasSigningKey,
|
|
1603
|
+
ConcurrentModificationError,
|
|
1370
1604
|
signLedgerEntry,
|
|
1371
1605
|
verifyLedgerEntry,
|
|
1372
1606
|
verifyBlockSignatures,
|
|
@@ -1374,6 +1608,8 @@ export {
|
|
|
1374
1608
|
verifyLedger,
|
|
1375
1609
|
computeMerkleRoot,
|
|
1376
1610
|
getLedgerBlocks,
|
|
1611
|
+
getLedgerBlocksWithHash,
|
|
1612
|
+
saveLedgerOptimistic,
|
|
1377
1613
|
initializeSharedLedger,
|
|
1378
1614
|
syncLedgerFromShared,
|
|
1379
1615
|
pushLedgerToShared,
|
|
@@ -1389,4 +1625,4 @@ export {
|
|
|
1389
1625
|
getEntryCorrectionHistory,
|
|
1390
1626
|
getCorrectionStats
|
|
1391
1627
|
};
|
|
1392
|
-
//# sourceMappingURL=chunk-
|
|
1628
|
+
//# sourceMappingURL=chunk-MVVPJ73K.js.map
|