@remnic/core 1.1.15 → 1.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.js +2 -2
- package/dist/access-http.d.ts +1 -1
- package/dist/access-http.js +5 -5
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +4 -4
- package/dist/access-schema.d.ts +17 -3
- package/dist/access-schema.js +3 -1
- package/dist/{access-service-BCMine1s.d.ts → access-service-DZXc7qwR.d.ts} +11 -1
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +2 -2
- package/dist/{chunk-VWFIQOTJ.js → chunk-66H2DZYB.js} +8 -1
- package/dist/chunk-66H2DZYB.js.map +1 -0
- package/dist/{chunk-BNATB54A.js → chunk-BYACCC5C.js} +3 -3
- package/dist/{chunk-ZYRMKWVW.js → chunk-CDQNR7SV.js} +4 -4
- package/dist/{chunk-HJ2WMBFB.js → chunk-GCR4JFKK.js} +15 -4
- package/dist/chunk-GCR4JFKK.js.map +1 -0
- package/dist/{chunk-GSP6ZKOY.js → chunk-HMZYQPT5.js} +85 -19
- package/dist/chunk-HMZYQPT5.js.map +1 -0
- package/dist/{chunk-5D2G67ZQ.js → chunk-VLQWOGYM.js} +29 -3
- package/dist/chunk-VLQWOGYM.js.map +1 -0
- package/dist/{cli-B71zQ6XK.d.ts → cli-kVwab1_L.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +6 -6
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/offline-sync.d.ts +10 -1
- package/dist/offline-sync.js +3 -1
- package/package.json +1 -1
- package/src/access-http.test.ts +73 -0
- package/src/access-http.ts +15 -0
- package/src/access-schema.ts +12 -0
- package/src/access-service-namespace.test.ts +64 -1
- package/src/access-service.ts +44 -0
- package/src/index.ts +1 -0
- package/src/offline-sync.test.ts +174 -0
- package/src/offline-sync.ts +110 -18
- package/dist/chunk-5D2G67ZQ.js.map +0 -1
- package/dist/chunk-GSP6ZKOY.js.map +0 -1
- package/dist/chunk-HJ2WMBFB.js.map +0 -1
- package/dist/chunk-VWFIQOTJ.js.map +0 -1
- /package/dist/{chunk-BNATB54A.js.map → chunk-BYACCC5C.js.map} +0 -0
- /package/dist/{chunk-ZYRMKWVW.js.map → chunk-CDQNR7SV.js.map} +0 -0
package/src/offline-sync.ts
CHANGED
|
@@ -127,6 +127,14 @@ export interface OfflineSyncFileWriteTarget extends OfflineSyncFileTarget {
|
|
|
127
127
|
content: Buffer;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
interface OfflineSyncFileRecordOptions {
|
|
131
|
+
root: SafeArchiveRoot;
|
|
132
|
+
relPath: string;
|
|
133
|
+
filePath: string;
|
|
134
|
+
includeContent: boolean;
|
|
135
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
136
|
+
}
|
|
137
|
+
|
|
130
138
|
const SYNC_INTERNAL_DIR = ".offline-sync";
|
|
131
139
|
const EXCLUDED_FILE_NAMES = new Set([
|
|
132
140
|
".sync-state.json",
|
|
@@ -135,6 +143,9 @@ const EXCLUDED_FILE_NAMES = new Set([
|
|
|
135
143
|
const EXCLUDED_REL_PATHS = new Set([
|
|
136
144
|
"state/fact-hashes.ready",
|
|
137
145
|
"state/fact-hashes.txt",
|
|
146
|
+
"state/lcm.sqlite",
|
|
147
|
+
"state/lcm.sqlite-shm",
|
|
148
|
+
"state/lcm.sqlite-wal",
|
|
138
149
|
]);
|
|
139
150
|
|
|
140
151
|
const EXCLUDED_FILE_PREFIXES = [
|
|
@@ -406,6 +417,24 @@ function filterBaseFilesForMode(
|
|
|
406
417
|
return files.filter((file) => !shouldExcludeRelPath(file.path, includeTranscripts));
|
|
407
418
|
}
|
|
408
419
|
|
|
420
|
+
async function readOfflineSyncFileRecord(
|
|
421
|
+
options: OfflineSyncFileRecordOptions,
|
|
422
|
+
): Promise<OfflineSyncFileRecord> {
|
|
423
|
+
const relPath = validateArchiveRelativePath(options.relPath, "offlineSyncFile.path");
|
|
424
|
+
const bytes = options.readFile
|
|
425
|
+
? await options.readFile({ root: options.root.abs, path: relPath, filePath: options.filePath })
|
|
426
|
+
: await readFile(options.filePath);
|
|
427
|
+
const digest = sha256Buffer(bytes);
|
|
428
|
+
const st = await stat(options.filePath);
|
|
429
|
+
return {
|
|
430
|
+
path: relPath,
|
|
431
|
+
sha256: digest.sha256,
|
|
432
|
+
bytes: digest.bytes,
|
|
433
|
+
mtimeMs: st.mtimeMs,
|
|
434
|
+
...(options.includeContent ? { contentBase64: bytes.toString("base64") } : {}),
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
409
438
|
export async function buildOfflineSyncSnapshot(options: {
|
|
410
439
|
root: string;
|
|
411
440
|
sourceId: string;
|
|
@@ -432,18 +461,13 @@ export async function buildOfflineSyncSnapshot(options: {
|
|
|
432
461
|
continue;
|
|
433
462
|
}
|
|
434
463
|
if (!entry.isFile()) continue;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
:
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
sha256: digest.sha256,
|
|
443
|
-
bytes: digest.bytes,
|
|
444
|
-
mtimeMs: st.mtimeMs,
|
|
445
|
-
...(options.includeContent === true ? { contentBase64: bytes.toString("base64") } : {}),
|
|
446
|
-
});
|
|
464
|
+
files.push(await readOfflineSyncFileRecord({
|
|
465
|
+
root,
|
|
466
|
+
relPath: relPosix,
|
|
467
|
+
filePath: abs,
|
|
468
|
+
includeContent: options.includeContent === true,
|
|
469
|
+
readFile: options.readFile,
|
|
470
|
+
}));
|
|
447
471
|
}
|
|
448
472
|
}
|
|
449
473
|
|
|
@@ -459,6 +483,53 @@ export async function buildOfflineSyncSnapshot(options: {
|
|
|
459
483
|
};
|
|
460
484
|
}
|
|
461
485
|
|
|
486
|
+
export async function buildOfflineSyncSnapshotForPaths(options: {
|
|
487
|
+
root: string;
|
|
488
|
+
sourceId: string;
|
|
489
|
+
paths: readonly string[];
|
|
490
|
+
includeContent?: boolean;
|
|
491
|
+
includeTranscripts?: boolean;
|
|
492
|
+
now?: Date;
|
|
493
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
494
|
+
}): Promise<OfflineSyncSnapshot> {
|
|
495
|
+
const rootAbs = path.resolve(options.root);
|
|
496
|
+
const root = await prepareSafeArchiveRoot(rootAbs, "buildOfflineSyncSnapshotForPaths", "root");
|
|
497
|
+
const includeTranscripts = options.includeTranscripts !== false;
|
|
498
|
+
const files: OfflineSyncFileRecord[] = [];
|
|
499
|
+
const seen = new Set<string>();
|
|
500
|
+
|
|
501
|
+
for (const rawPath of options.paths) {
|
|
502
|
+
const relPath = normalizeRelativePath(rawPath, "paths[]");
|
|
503
|
+
if (seen.has(relPath)) continue;
|
|
504
|
+
seen.add(relPath);
|
|
505
|
+
if (shouldExcludeRelPath(relPath, includeTranscripts)) {
|
|
506
|
+
throw new Error(`offline sync snapshot path is excluded: ${relPath}`);
|
|
507
|
+
}
|
|
508
|
+
const filePath = await resolveSafeArchiveTarget(root, relPath);
|
|
509
|
+
const st = await lstat(filePath).catch((error: unknown) => {
|
|
510
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
511
|
+
throw error;
|
|
512
|
+
});
|
|
513
|
+
if (!st || st.isSymbolicLink() || !st.isFile()) continue;
|
|
514
|
+
files.push(await readOfflineSyncFileRecord({
|
|
515
|
+
root,
|
|
516
|
+
relPath,
|
|
517
|
+
filePath,
|
|
518
|
+
includeContent: options.includeContent === true,
|
|
519
|
+
readFile: options.readFile,
|
|
520
|
+
}));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
525
|
+
schemaVersion: 1,
|
|
526
|
+
createdAt: (options.now ?? new Date()).toISOString(),
|
|
527
|
+
sourceId: normalizeSourceId(options.sourceId, "sourceId"),
|
|
528
|
+
includeTranscripts,
|
|
529
|
+
files: files.sort(compareByPath),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
462
533
|
export async function buildOfflineSyncChangeset(options: {
|
|
463
534
|
root: string;
|
|
464
535
|
sourceId: string;
|
|
@@ -475,7 +546,7 @@ export async function buildOfflineSyncChangeset(options: {
|
|
|
475
546
|
const current = await buildOfflineSyncSnapshot({
|
|
476
547
|
root: options.root,
|
|
477
548
|
sourceId: options.sourceId,
|
|
478
|
-
includeContent:
|
|
549
|
+
includeContent: false,
|
|
479
550
|
includeTranscripts,
|
|
480
551
|
now: options.now,
|
|
481
552
|
readFile: options.readFile,
|
|
@@ -487,11 +558,24 @@ export async function buildOfflineSyncChangeset(options: {
|
|
|
487
558
|
const baseEntry = base.get(relPath);
|
|
488
559
|
const currentEntry = currentMap.get(relPath);
|
|
489
560
|
if (currentEntry && currentEntry.sha256 !== baseEntry?.sha256) {
|
|
561
|
+
const file = await buildOfflineSyncSnapshotForPaths({
|
|
562
|
+
root: options.root,
|
|
563
|
+
sourceId: options.sourceId,
|
|
564
|
+
paths: [relPath],
|
|
565
|
+
includeContent: true,
|
|
566
|
+
includeTranscripts,
|
|
567
|
+
now: options.now,
|
|
568
|
+
readFile: options.readFile,
|
|
569
|
+
});
|
|
570
|
+
const record = file.files[0];
|
|
571
|
+
if (!record || typeof record.contentBase64 !== "string" || record.sha256 !== currentEntry.sha256) {
|
|
572
|
+
throw new Error(`offline sync file changed while building changeset: ${relPath}`);
|
|
573
|
+
}
|
|
490
574
|
changes.push({
|
|
491
575
|
type: "upsert",
|
|
492
576
|
path: relPath,
|
|
493
577
|
...(baseEntry ? { baseSha256: baseEntry.sha256 } : {}),
|
|
494
|
-
file:
|
|
578
|
+
file: record as OfflineSyncFileRecord & { contentBase64: string },
|
|
495
579
|
});
|
|
496
580
|
continue;
|
|
497
581
|
}
|
|
@@ -535,13 +619,15 @@ export async function applyOfflineSyncSnapshot(options: {
|
|
|
535
619
|
writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;
|
|
536
620
|
deleteFile?: (target: OfflineSyncFileTarget) => Promise<void>;
|
|
537
621
|
}): Promise<OfflineSyncApplySnapshotResult> {
|
|
538
|
-
const snapshot = normalizeOfflineSyncSnapshot(options.snapshot
|
|
622
|
+
const snapshot = normalizeOfflineSyncSnapshot(options.snapshot);
|
|
539
623
|
const baseMap = byPath(filterBaseFilesForMode(
|
|
540
624
|
normalizeFileStates(options.baseFiles),
|
|
541
625
|
snapshot.includeTranscripts,
|
|
542
626
|
));
|
|
543
627
|
const incomingMap = byPath(snapshot.files);
|
|
544
|
-
const incomingBuffers = verifyRecordContents(snapshot.files, "offline sync snapshot"
|
|
628
|
+
const incomingBuffers = verifyRecordContents(snapshot.files, "offline sync snapshot", {
|
|
629
|
+
requireContent: false,
|
|
630
|
+
});
|
|
545
631
|
const root = await ensureSyncRoot(options.root, "applyOfflineSyncSnapshot");
|
|
546
632
|
const current = await buildOfflineSyncSnapshot({
|
|
547
633
|
root: root.abs,
|
|
@@ -582,7 +668,9 @@ export async function applyOfflineSyncSnapshot(options: {
|
|
|
582
668
|
reason: "local_deleted_remote_modified",
|
|
583
669
|
baseSha256: base.sha256,
|
|
584
670
|
incomingSha256: incoming.sha256,
|
|
585
|
-
incomingBuffer:
|
|
671
|
+
incomingBuffer: options.writeConflictCopies === false
|
|
672
|
+
? incomingBuffers.get(relPath)
|
|
673
|
+
: requiredBuffer(incomingBuffers, relPath),
|
|
586
674
|
writeConflictCopies: options.writeConflictCopies !== false,
|
|
587
675
|
sourceId: snapshot.sourceId,
|
|
588
676
|
writeFile: options.writeFile,
|
|
@@ -615,7 +703,9 @@ export async function applyOfflineSyncSnapshot(options: {
|
|
|
615
703
|
baseSha256: base?.sha256,
|
|
616
704
|
localSha256: currentEntry?.sha256,
|
|
617
705
|
incomingSha256: incoming.sha256,
|
|
618
|
-
incomingBuffer:
|
|
706
|
+
incomingBuffer: options.writeConflictCopies === false
|
|
707
|
+
? incomingBuffers.get(relPath)
|
|
708
|
+
: requiredBuffer(incomingBuffers, relPath),
|
|
619
709
|
writeConflictCopies: options.writeConflictCopies !== false,
|
|
620
710
|
sourceId: snapshot.sourceId,
|
|
621
711
|
writeFile: options.writeFile,
|
|
@@ -774,10 +864,12 @@ export async function applyOfflineSyncChangeset(options: {
|
|
|
774
864
|
function verifyRecordContents(
|
|
775
865
|
records: readonly OfflineSyncFileRecord[],
|
|
776
866
|
context: string,
|
|
867
|
+
options: { requireContent?: boolean } = {},
|
|
777
868
|
): Map<string, Buffer> {
|
|
778
869
|
const buffers = new Map<string, Buffer>();
|
|
779
870
|
for (const record of records) {
|
|
780
871
|
if (typeof record.contentBase64 !== "string") {
|
|
872
|
+
if (options.requireContent === false) continue;
|
|
781
873
|
throw new Error(`${context}: contentBase64 is required for ${record.path}`);
|
|
782
874
|
}
|
|
783
875
|
const buffer = Buffer.from(record.contentBase64, "base64");
|