@remnic/cli 1.0.13 → 1.0.15

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 +34 -25
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -97,6 +97,7 @@ import {
97
97
  offlineSyncStateFromSnapshot,
98
98
  readOfflineSyncState,
99
99
  summarizeOfflineSyncChangeset,
100
+ summarizeOfflineSyncPendingChanges,
100
101
  writeOfflineSyncState,
101
102
  buildActionConfidenceInputFromOptions,
102
103
  evaluateActionConfidence,
@@ -6670,6 +6671,7 @@ async function fetchOfflineFiles(args) {
6670
6671
  );
6671
6672
  }
6672
6673
  var OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES = 16 * 1024 * 1024;
6674
+ var OFFLINE_SYNC_FILES_CONTENT_MAX_BATCH_BYTES = 8 * 1024 * 1024;
6673
6675
  function parseOfflineHeaderNumber(headers, name) {
6674
6676
  const raw = headers.get(name);
6675
6677
  if (raw === null) throw new Error(`offline file content response omitted ${name}`);
@@ -6761,16 +6763,16 @@ async function readFirstOfflineSyncState(paths) {
6761
6763
  function offlineFileStateMap(files) {
6762
6764
  return new Map(files.map((file) => [file.path, file]));
6763
6765
  }
6764
- function offlineSnapshotContentPathsForApply(options) {
6766
+ function offlineSnapshotContentFilesForApply(options) {
6765
6767
  const base = offlineFileStateMap(options.baseFiles);
6766
6768
  const current = options.currentFiles ? offlineFileStateMap(options.currentFiles) : null;
6767
- const paths = [];
6769
+ const files = [];
6768
6770
  for (const incoming of options.snapshot.files) {
6769
6771
  if (current?.get(incoming.path)?.sha256 === incoming.sha256) continue;
6770
6772
  if (base.get(incoming.path)?.sha256 === incoming.sha256) continue;
6771
- paths.push(incoming.path);
6773
+ files.push(incoming);
6772
6774
  }
6773
- return paths.sort();
6775
+ return files.sort((left, right) => left.path.localeCompare(right.path));
6774
6776
  }
6775
6777
  function shouldDirectHydrateOfflineFile(options) {
6776
6778
  if (options.incoming.bytes < OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES) return false;
@@ -6852,19 +6854,22 @@ async function directHydrateLargeOfflineFiles(args) {
6852
6854
  }
6853
6855
  return hydrated;
6854
6856
  }
6855
- function chunkOfflineFilePaths(paths) {
6857
+ function chunkOfflineFileContentBatches(files) {
6856
6858
  const chunks = [];
6857
6859
  let current = [];
6858
- let currentBytes = 256;
6859
- for (const relPath of paths) {
6860
- const cost = Buffer.byteLength(JSON.stringify(relPath), "utf-8") + 1;
6861
- if (current.length > 0 && (current.length >= 1e3 || currentBytes + cost > 96e3)) {
6860
+ let currentPathBytes = 256;
6861
+ let currentContentBytes = 0;
6862
+ for (const file of files) {
6863
+ const pathCost = Buffer.byteLength(JSON.stringify(file.path), "utf-8") + 1;
6864
+ if (current.length > 0 && (current.length >= 1e3 || currentPathBytes + pathCost > 96e3 || currentContentBytes + file.bytes > OFFLINE_SYNC_FILES_CONTENT_MAX_BATCH_BYTES)) {
6862
6865
  chunks.push(current);
6863
6866
  current = [];
6864
- currentBytes = 256;
6867
+ currentPathBytes = 256;
6868
+ currentContentBytes = 0;
6865
6869
  }
6866
- current.push(relPath);
6867
- currentBytes += cost;
6870
+ current.push(file);
6871
+ currentPathBytes += pathCost;
6872
+ currentContentBytes += file.bytes;
6868
6873
  }
6869
6874
  if (current.length > 0) chunks.push(current);
6870
6875
  return chunks;
@@ -6878,32 +6883,34 @@ function isMissingOfflineContentError(error) {
6878
6883
  return /^missing decoded content for /.test(message);
6879
6884
  }
6880
6885
  async function hydrateOfflineSnapshotContent(args) {
6881
- const neededPaths = offlineSnapshotContentPathsForApply({
6882
- snapshot: args.snapshot,
6886
+ const snapshot = normalizeOfflineSyncSnapshot(args.snapshot);
6887
+ const neededFiles = offlineSnapshotContentFilesForApply({
6888
+ snapshot,
6883
6889
  baseFiles: args.baseFiles,
6884
6890
  currentFiles: args.currentFiles
6885
6891
  });
6886
- if (neededPaths.length === 0) return args.snapshot;
6887
- const expectedByPath = new Map(args.snapshot.files.map((file) => [file.path, file]));
6892
+ if (neededFiles.length === 0) return { ...args.snapshot, files: snapshot.files };
6893
+ const expectedByPath = new Map(snapshot.files.map((file) => [file.path, file]));
6888
6894
  const contentByPath = /* @__PURE__ */ new Map();
6895
+ const updatedByPath = /* @__PURE__ */ new Map();
6889
6896
  try {
6890
- for (const batch of chunkOfflineFilePaths(neededPaths)) {
6897
+ for (const batch of chunkOfflineFileContentBatches(neededFiles)) {
6891
6898
  const partial = await fetchOfflineFiles({
6892
6899
  remoteUrl: args.remoteUrl,
6893
6900
  token: args.token,
6894
6901
  namespace: args.namespace,
6895
6902
  includeTranscripts: args.includeTranscripts,
6896
- paths: batch
6903
+ paths: batch.map((file) => file.path)
6897
6904
  });
6898
6905
  for (const file of partial.files) {
6899
6906
  const expected = expectedByPath.get(file.path);
6900
6907
  if (!expected) continue;
6901
- if (file.sha256 !== expected.sha256 || file.bytes !== expected.bytes) {
6902
- throw new Error(`remote file changed while fetching offline content: ${file.path}`);
6903
- }
6904
6908
  if (typeof file.contentBase64 !== "string") {
6905
6909
  throw new Error(`remote offline content response omitted contentBase64 for ${file.path}`);
6906
6910
  }
6911
+ if (file.sha256 !== expected.sha256 || file.bytes !== expected.bytes || file.mtimeMs !== expected.mtimeMs) {
6912
+ updatedByPath.set(file.path, file);
6913
+ }
6907
6914
  contentByPath.set(file.path, file.contentBase64);
6908
6915
  }
6909
6916
  }
@@ -6917,7 +6924,7 @@ async function hydrateOfflineSnapshotContent(args) {
6917
6924
  includeContent: true
6918
6925
  });
6919
6926
  }
6920
- const missing = neededPaths.filter((relPath) => !contentByPath.has(relPath));
6927
+ const missing = neededFiles.map((file) => file.path).filter((relPath) => !contentByPath.has(relPath));
6921
6928
  if (missing.length > 0) {
6922
6929
  throw new Error(
6923
6930
  `remote offline content response omitted ${missing.length} changed file${missing.length === 1 ? "" : "s"}; retry sync`
@@ -6925,7 +6932,9 @@ async function hydrateOfflineSnapshotContent(args) {
6925
6932
  }
6926
6933
  return {
6927
6934
  ...args.snapshot,
6928
- files: args.snapshot.files.map((file) => {
6935
+ files: snapshot.files.map((file) => {
6936
+ const updated = updatedByPath.get(file.path);
6937
+ if (updated) return updated;
6929
6938
  const contentBase64 = contentByPath.get(file.path);
6930
6939
  return contentBase64 === void 0 ? file : { ...file, contentBase64 };
6931
6940
  })
@@ -7269,14 +7278,13 @@ Environment fallbacks:
7269
7278
  });
7270
7279
  }
7271
7280
  const storageIo = await createOfflineStorageIo(memoryDir);
7272
- const changeset = await buildOfflineSyncChangeset({
7281
+ const summary = await summarizeOfflineSyncPendingChanges({
7273
7282
  root: memoryDir,
7274
7283
  sourceId: localOfflineSourceId(memoryDir),
7275
7284
  baseFiles: state?.baseFiles ?? [],
7276
7285
  includeTranscripts,
7277
7286
  readFile: storageIo.readFile
7278
7287
  });
7279
- const summary = summarizeOfflineSyncChangeset(changeset);
7280
7288
  if (json) {
7281
7289
  console.log(JSON.stringify({ statePath: statePath ?? null, state, pending: summary }, null, 2));
7282
7290
  } else {
@@ -10228,6 +10236,7 @@ export {
10228
10236
  buildBenchRuntimeProfileRequest,
10229
10237
  buildPackageBenchExecutionPlans,
10230
10238
  buildQueryRecallRequest,
10239
+ chunkOfflineFileContentBatches,
10231
10240
  extractXrayRawArgs,
10232
10241
  getBenchUsageText,
10233
10242
  hasFlag,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/plugin-pi": "^1.0.0",
30
- "@remnic/core": "^1.1.19",
31
- "@remnic/server": "^1.0.5"
29
+ "@remnic/plugin-pi": "^1.0.1",
30
+ "@remnic/server": "^1.0.5",
31
+ "@remnic/core": "^1.1.21"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@remnic/bench": "^1.0.0",
@@ -78,10 +78,10 @@
78
78
  "@remnic/import-weclone": "1.0.1",
79
79
  "@remnic/import-chatgpt": "0.1.0",
80
80
  "@remnic/import-claude": "0.1.0",
81
+ "@remnic/import-gemini": "0.1.0",
81
82
  "@remnic/import-lossless-claw": "0.1.1",
82
- "@remnic/import-supermemory": "0.1.2",
83
83
  "@remnic/import-mem0": "0.1.0",
84
- "@remnic/import-gemini": "0.1.0"
84
+ "@remnic/import-supermemory": "0.1.2"
85
85
  },
86
86
  "license": "MIT",
87
87
  "repository": {