@remnic/cli 9.44.0 → 9.45.0

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.
Files changed (2) hide show
  1. package/dist/index.js +48 -7
  2. package/package.json +29 -29
package/dist/index.js CHANGED
@@ -259,6 +259,10 @@ import {
259
259
  readConvergeCursor,
260
260
  writeConvergeCursor
261
261
  } from "@remnic/core/reconcile/cursor.js";
262
+ import {
263
+ buildReconcileManifest,
264
+ collapseActiveFactDuplicates
265
+ } from "@remnic/core/reconcile/manifest.js";
262
266
 
263
267
  // src/offline-storage-io.ts
264
268
  import { mkdtemp, readdir, lstat, rm } from "fs/promises";
@@ -769,6 +773,8 @@ async function computeConvergePlan(options = {}) {
769
773
  const localTombstones = /* @__PURE__ */ new Map();
770
774
  const peerMap = /* @__PURE__ */ new Map();
771
775
  const peerTombstones = /* @__PURE__ */ new Map();
776
+ const localManifests = /* @__PURE__ */ new Map();
777
+ const peerManifests = /* @__PURE__ */ new Map();
772
778
  if (options.baseFilesByNamespace) {
773
779
  for (const [ns, files] of options.baseFilesByNamespace) {
774
780
  namespacesToPlan.add(ns);
@@ -826,6 +832,26 @@ async function computeConvergePlan(options = {}) {
826
832
  bytes: record.bytes
827
833
  }));
828
834
  localMap.set(ns, files);
835
+ try {
836
+ const io = await createOfflineStorageIo(rootInfo.rootDir);
837
+ localManifests.set(
838
+ ns,
839
+ await buildReconcileManifest({
840
+ files,
841
+ readFile: async (file) => {
842
+ const readFile2 = io.readFile;
843
+ if (!readFile2) throw new Error("offline storage cannot read reconciliation manifest files");
844
+ return await readFile2({
845
+ root: rootInfo.rootDir,
846
+ path: file.path,
847
+ filePath: path2.join(rootInfo.rootDir, file.path)
848
+ });
849
+ }
850
+ })
851
+ );
852
+ } catch {
853
+ localManifests.delete(ns);
854
+ }
829
855
  const tombstones = await readLocalTombstones(rootInfo.rootDir);
830
856
  localTombstones.set(ns, tombstones);
831
857
  } catch {
@@ -833,7 +859,8 @@ async function computeConvergePlan(options = {}) {
833
859
  }
834
860
  }
835
861
  }
836
- if (!options.peerFilesByNamespace && options.peerUrl) {
862
+ const peerUrl = options.peerUrl;
863
+ if (!options.peerFilesByNamespace && peerUrl) {
837
864
  let resolvedToken;
838
865
  if (options.peerToken) {
839
866
  try {
@@ -846,9 +873,23 @@ async function computeConvergePlan(options = {}) {
846
873
  }
847
874
  const fetchFn = options.fetchImpl ?? globalThis.fetch;
848
875
  for (const ns of namespacesToPlan) {
849
- const peerData = await fetchPeerSnapshot(options.peerUrl, ns, resolvedToken, fetchFn);
876
+ const peerData = await fetchPeerSnapshot(peerUrl, ns, resolvedToken, fetchFn);
850
877
  peerMap.set(ns, peerData.files);
851
878
  peerTombstones.set(ns, peerData.tombstones);
879
+ peerManifests.set(
880
+ ns,
881
+ await buildReconcileManifest({
882
+ files: peerData.files,
883
+ cachedFiles: localManifests.get(ns)?.files,
884
+ readFile: async (file) => {
885
+ const remote = await fetchPeerFileContent(peerUrl, ns, file.path, resolvedToken, fetchFn);
886
+ if (!remote || remote.sha256 !== file.sha256) {
887
+ throw new Error(`failed to read peer reconciliation manifest file: ${file.path}`);
888
+ }
889
+ return remote.content;
890
+ }
891
+ })
892
+ );
852
893
  }
853
894
  }
854
895
  const memoryDir = options.cursorDir ?? config?.memoryDir;
@@ -872,7 +913,8 @@ async function computeConvergePlan(options = {}) {
872
913
  peerTombstonedFileSha256: peerTombstones.get(ns) ?? []
873
914
  });
874
915
  }
875
- return planReconciliation(inputs, { conflictPolicy: options.conflictPolicy });
916
+ const plan = planReconciliation(inputs, { conflictPolicy: options.conflictPolicy });
917
+ return collapseActiveFactDuplicates(plan, localManifests, peerManifests);
876
918
  }
877
919
  async function executeConvergeApply(options = {}) {
878
920
  const conflictPolicy = options.conflictPolicy ?? "manual";
@@ -1156,10 +1198,9 @@ async function updateCursorsForPlan(plan, options) {
1156
1198
  const namespaces = new Set(plan.byNamespace.map((n) => n.namespace));
1157
1199
  for (const ns of namespaces) {
1158
1200
  const cursorPath = defaultConvergeCursorPath(memoryDir, peerUrl, ns);
1159
- const nsEntries = plan.entries.filter((e) => e.namespace === ns);
1160
- const baseFiles = nsEntries.map((e) => ({
1161
- path: e.path,
1162
- sha256: e.localSha256 ?? e.peerSha256 ?? "unknown"
1201
+ const baseFiles = plan.entries.filter((entry) => entry.namespace === ns && entry.reason !== "semantic_duplicate").map((entry) => ({
1202
+ path: entry.path,
1203
+ sha256: entry.localSha256 ?? entry.peerSha256 ?? "unknown"
1163
1204
  }));
1164
1205
  const cursorState = {
1165
1206
  version: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "9.44.0",
3
+ "version": "9.45.0",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,24 +26,24 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/plugin-pi": "^9.44.0",
30
- "@remnic/core": "^9.44.0",
31
- "@remnic/server": "^9.44.0"
29
+ "@remnic/plugin-pi": "^9.45.0",
30
+ "@remnic/core": "^9.45.0",
31
+ "@remnic/server": "^9.45.0"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^9.44.0",
35
- "@remnic/export-weclone": "^9.44.0",
36
- "@remnic/import-weclone": "^9.44.0",
37
- "@remnic/import-chatgpt": "^9.44.0",
38
- "@remnic/import-claude": "^9.44.0",
39
- "@remnic/import-gemini": "^9.44.0",
40
- "@remnic/import-lossless-claw": "^9.44.0",
41
- "@remnic/import-mem0": "^9.44.0",
42
- "@remnic/import-supermemory": "^9.44.0",
43
- "@remnic/connector-limitless": "^9.44.0",
44
- "@remnic/connector-bee": "^9.44.0",
45
- "@remnic/connector-omi": "^9.44.0",
46
- "@remnic/capture-audio": "^9.44.0"
34
+ "@remnic/bench": "^9.45.0",
35
+ "@remnic/export-weclone": "^9.45.0",
36
+ "@remnic/import-weclone": "^9.45.0",
37
+ "@remnic/import-chatgpt": "^9.45.0",
38
+ "@remnic/import-claude": "^9.45.0",
39
+ "@remnic/import-gemini": "^9.45.0",
40
+ "@remnic/import-lossless-claw": "^9.45.0",
41
+ "@remnic/import-mem0": "^9.45.0",
42
+ "@remnic/import-supermemory": "^9.45.0",
43
+ "@remnic/connector-limitless": "^9.45.0",
44
+ "@remnic/connector-bee": "^9.45.0",
45
+ "@remnic/connector-omi": "^9.45.0",
46
+ "@remnic/capture-audio": "^9.45.0"
47
47
  },
48
48
  "peerDependenciesMeta": {
49
49
  "@remnic/bench": {
@@ -89,18 +89,18 @@
89
89
  "devDependencies": {
90
90
  "tsup": "^8.5.1",
91
91
  "typescript": "^5.9.3",
92
- "@remnic/bench": "9.44.0",
93
- "@remnic/export-weclone": "9.44.0",
94
- "@remnic/import-weclone": "9.44.0",
95
- "@remnic/import-chatgpt": "9.44.0",
96
- "@remnic/import-gemini": "9.44.0",
97
- "@remnic/import-mem0": "9.44.0",
98
- "@remnic/import-supermemory": "9.44.0",
99
- "@remnic/import-lossless-claw": "9.44.0",
100
- "@remnic/connector-limitless": "9.44.0",
101
- "@remnic/connector-bee": "9.44.0",
102
- "@remnic/import-claude": "9.44.0",
103
- "@remnic/connector-omi": "9.44.0"
92
+ "@remnic/bench": "9.45.0",
93
+ "@remnic/export-weclone": "9.45.0",
94
+ "@remnic/import-weclone": "9.45.0",
95
+ "@remnic/import-chatgpt": "9.45.0",
96
+ "@remnic/import-claude": "9.45.0",
97
+ "@remnic/import-gemini": "9.45.0",
98
+ "@remnic/import-lossless-claw": "9.45.0",
99
+ "@remnic/import-mem0": "9.45.0",
100
+ "@remnic/connector-limitless": "9.45.0",
101
+ "@remnic/import-supermemory": "9.45.0",
102
+ "@remnic/connector-omi": "9.45.0",
103
+ "@remnic/connector-bee": "9.45.0"
104
104
  },
105
105
  "license": "MIT",
106
106
  "repository": {