@remnic/cli 9.43.0 → 9.44.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 +49 -4
  2. package/package.json +29 -29
package/dist/index.js CHANGED
@@ -680,12 +680,18 @@ async function fetchPeerFileContent(peerUrl, namespace, filePath, token, fetchIm
680
680
  }
681
681
  return null;
682
682
  }
683
+ function withoutTrailingSlashes(value) {
684
+ let end = value.length;
685
+ while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
686
+ return value.slice(0, end);
687
+ }
683
688
  async function postPeerFileContent(peerUrl, namespace, filePath, content, metadata, token, fetchImpl = globalThis.fetch) {
684
- const base = peerUrl.replace(/\/+$/, "");
689
+ const base = withoutTrailingSlashes(peerUrl);
685
690
  const routes = [
686
691
  `/remnic/v1/offline-sync/apply-file-content?namespace=${encodeURIComponent(namespace)}`,
687
692
  `/engram/v1/offline-sync/apply-file-content?namespace=${encodeURIComponent(namespace)}`
688
693
  ];
694
+ let previousAttemptFailed = false;
689
695
  for (const route of routes) {
690
696
  try {
691
697
  let offset = 0;
@@ -717,7 +723,9 @@ async function postPeerFileContent(peerUrl, namespace, filePath, content, metada
717
723
  return false;
718
724
  }
719
725
  if (result.done) {
720
- return result.skipped || result.applied && offset + chunk.length === content.length;
726
+ if (result.skipped) return previousAttemptFailed ? "applied" : "skipped";
727
+ if (result.applied && offset + chunk.length === content.length) return "applied";
728
+ return false;
721
729
  }
722
730
  if (result.applied || result.skipped || chunk.length === 0) {
723
731
  return false;
@@ -726,6 +734,30 @@ async function postPeerFileContent(peerUrl, namespace, filePath, content, metada
726
734
  } while (offset < content.length);
727
735
  return false;
728
736
  } catch {
737
+ previousAttemptFailed = true;
738
+ }
739
+ }
740
+ return false;
741
+ }
742
+ async function postPeerConvergenceComplete(peerUrl, namespaces, token, fetchImpl = globalThis.fetch) {
743
+ const base = withoutTrailingSlashes(peerUrl);
744
+ const query = namespaces.map((namespace) => `namespace=${encodeURIComponent(namespace)}`).join("&");
745
+ const routes = [
746
+ "/remnic/v1/offline-sync/convergence-complete",
747
+ "/engram/v1/offline-sync/convergence-complete"
748
+ ];
749
+ for (const route of routes) {
750
+ const response = await fetchImpl(`${base}${route}?${query}`, {
751
+ method: "POST",
752
+ headers: {
753
+ "x-remnic-source-id": encodeURIComponent("remnic-converge"),
754
+ ...token ? { authorization: `Bearer ${token}` } : {}
755
+ }
756
+ }).catch(() => null);
757
+ if (!response?.ok) continue;
758
+ const result = await response.json().catch(() => null);
759
+ if (result && typeof result === "object" && "namespaces" in result && Array.isArray(result.namespaces) && result.namespaces.length === namespaces.length && result.namespaces.every((namespace, index) => namespace === namespaces[index]) && "refreshed" in result && result.refreshed === true) {
760
+ return true;
729
761
  }
730
762
  }
731
763
  return false;
@@ -894,6 +926,7 @@ async function executeConvergeApply(options = {}) {
894
926
  suppressed: 0,
895
927
  failed: 0
896
928
  };
929
+ const peerMutatedNamespaces = /* @__PURE__ */ new Set();
897
930
  let resolvedToken;
898
931
  if (options.peerToken) {
899
932
  try {
@@ -1057,7 +1090,7 @@ async function executeConvergeApply(options = {}) {
1057
1090
  else actualTransfers.pushed += 1;
1058
1091
  } else if (options.peerUrl && entry.localSha256) {
1059
1092
  const expectedPeerSha256 = entry.action === "conflict" ? entry.peerSha256 : entry.baseSha256;
1060
- const ok = await postPeerFileContent(
1093
+ const applied = await postPeerFileContent(
1061
1094
  options.peerUrl,
1062
1095
  entry.namespace,
1063
1096
  entry.path,
@@ -1070,7 +1103,8 @@ async function executeConvergeApply(options = {}) {
1070
1103
  resolvedToken,
1071
1104
  fetchFn
1072
1105
  );
1073
- if (ok) {
1106
+ if (applied) {
1107
+ if (applied === "applied") peerMutatedNamespaces.add(entry.namespace);
1074
1108
  if (entry.action === "conflict") actualTransfers.conflictsResolved += 1;
1075
1109
  else actualTransfers.pushed += 1;
1076
1110
  } else {
@@ -1086,6 +1120,17 @@ async function executeConvergeApply(options = {}) {
1086
1120
  actualTransfers.suppressed += 1;
1087
1121
  }
1088
1122
  }
1123
+ if (options.peerUrl && peerMutatedNamespaces.size > 0) {
1124
+ const namespaces = [...peerMutatedNamespaces].sort();
1125
+ if (!await postPeerConvergenceComplete(
1126
+ options.peerUrl,
1127
+ namespaces,
1128
+ resolvedToken,
1129
+ fetchFn
1130
+ )) {
1131
+ actualTransfers.failed += 1;
1132
+ }
1133
+ }
1089
1134
  let cursorUpdated = false;
1090
1135
  if (actualTransfers.failed === 0) {
1091
1136
  await updateCursorsForPlan(plan, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "9.43.0",
3
+ "version": "9.44.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/server": "^9.43.0",
30
- "@remnic/plugin-pi": "^9.43.0",
31
- "@remnic/core": "^9.43.0"
29
+ "@remnic/plugin-pi": "^9.44.0",
30
+ "@remnic/core": "^9.44.0",
31
+ "@remnic/server": "^9.44.0"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^9.43.0",
35
- "@remnic/export-weclone": "^9.43.0",
36
- "@remnic/import-weclone": "^9.43.0",
37
- "@remnic/import-chatgpt": "^9.43.0",
38
- "@remnic/import-claude": "^9.43.0",
39
- "@remnic/import-gemini": "^9.43.0",
40
- "@remnic/import-lossless-claw": "^9.43.0",
41
- "@remnic/import-mem0": "^9.43.0",
42
- "@remnic/import-supermemory": "^9.43.0",
43
- "@remnic/connector-limitless": "^9.43.0",
44
- "@remnic/connector-bee": "^9.43.0",
45
- "@remnic/connector-omi": "^9.43.0",
46
- "@remnic/capture-audio": "^9.43.0"
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"
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.43.0",
93
- "@remnic/export-weclone": "9.43.0",
94
- "@remnic/import-weclone": "9.43.0",
95
- "@remnic/import-chatgpt": "9.43.0",
96
- "@remnic/import-claude": "9.43.0",
97
- "@remnic/import-lossless-claw": "9.43.0",
98
- "@remnic/import-mem0": "9.43.0",
99
- "@remnic/import-gemini": "9.43.0",
100
- "@remnic/import-supermemory": "9.43.0",
101
- "@remnic/connector-limitless": "9.43.0",
102
- "@remnic/connector-bee": "9.43.0",
103
- "@remnic/connector-omi": "9.43.0"
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"
104
104
  },
105
105
  "license": "MIT",
106
106
  "repository": {