@remnic/cli 1.0.17 → 1.0.18

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 +78 -4
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -88,7 +88,9 @@ import {
88
88
  formatProcedureStatsText,
89
89
  parseXrayCliOptions,
90
90
  renderXray,
91
+ OFFLINE_SYNC_APPLY_MAX_BODY_BYTES,
91
92
  OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
93
+ OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES,
92
94
  applyOfflineSyncSnapshot,
93
95
  buildOfflineSyncChangeset,
94
96
  buildOfflineSyncSnapshot,
@@ -6689,6 +6691,16 @@ async function fetchOfflineFiles(args) {
6689
6691
  }
6690
6692
  var OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES = 16 * 1024 * 1024;
6691
6693
  var OFFLINE_SYNC_FILES_CONTENT_MAX_BATCH_BYTES = 8 * 1024 * 1024;
6694
+ var OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES = Math.floor(OFFLINE_SYNC_APPLY_MAX_BODY_BYTES / 2);
6695
+ var OFFLINE_SYNC_DIRECT_PUSH_INLINE_MARGIN_BYTES = 256 * 1024;
6696
+ var OFFLINE_SYNC_INLINE_CONTENT_MAX_BYTES = Math.max(
6697
+ 1,
6698
+ Math.floor((OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES - OFFLINE_SYNC_DIRECT_PUSH_INLINE_MARGIN_BYTES) * 3 / 4)
6699
+ );
6700
+ var OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES = Math.min(
6701
+ OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES,
6702
+ OFFLINE_SYNC_INLINE_CONTENT_MAX_BYTES
6703
+ );
6692
6704
  function parseOfflineHeaderNumber(headers, name) {
6693
6705
  const raw = headers.get(name);
6694
6706
  if (raw === null) throw new Error(`offline file content response omitted ${name}`);
@@ -6837,7 +6849,7 @@ function shouldDirectHydrateOfflineFile(options) {
6837
6849
  function offlineDirectPushFiles(options) {
6838
6850
  const base = offlineFileStateMap(options.baseFiles);
6839
6851
  return options.currentFiles.filter((current) => {
6840
- if (current.bytes < OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES) return false;
6852
+ if (current.bytes < OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES) return false;
6841
6853
  return current.sha256 !== base.get(current.path)?.sha256;
6842
6854
  }).sort((left, right) => right.bytes - left.bytes || left.path.localeCompare(right.path));
6843
6855
  }
@@ -6850,6 +6862,7 @@ function resolveOfflineDirectHydrationPath(memoryDir, relPath) {
6850
6862
  }
6851
6863
  return target;
6852
6864
  }
6865
+ var OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES = OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES;
6853
6866
  async function pushOfflineFileContent(args) {
6854
6867
  if (args.readFileChunks) {
6855
6868
  return pushOfflineFileContentFromChunkReader(args);
@@ -6862,7 +6875,7 @@ async function pushOfflineFileContent(args) {
6862
6875
  path: args.file.path,
6863
6876
  offset,
6864
6877
  length: Math.min(
6865
- OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
6878
+ OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES,
6866
6879
  Math.max(1, args.file.bytes - offset)
6867
6880
  ),
6868
6881
  includeTranscripts: args.includeTranscripts,
@@ -6907,7 +6920,7 @@ async function pushOfflineFileContentFromChunkReader(args) {
6907
6920
  root: path11.resolve(args.memoryDir),
6908
6921
  path: args.file.path,
6909
6922
  filePath,
6910
- chunkSize: OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES
6923
+ chunkSize: OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES
6911
6924
  });
6912
6925
  let offset = 0;
6913
6926
  let pending = null;
@@ -7126,7 +7139,39 @@ async function hydrateOfflineSnapshotContent(args) {
7126
7139
  })
7127
7140
  };
7128
7141
  }
7129
- async function pushOfflineChanges(args) {
7142
+ function chunkOfflineChangesetApplyBatches(changeset, namespace, maxRequestBytes = OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES) {
7143
+ if (!Number.isInteger(maxRequestBytes) || maxRequestBytes < 1) {
7144
+ throw new Error("offline sync apply max request bytes must be a positive integer");
7145
+ }
7146
+ const chunks = [];
7147
+ let current = [];
7148
+ const requestBytesFor = (changes) => Buffer.byteLength(JSON.stringify({
7149
+ namespace,
7150
+ changeset: {
7151
+ ...changeset,
7152
+ changes
7153
+ }
7154
+ }), "utf-8");
7155
+ for (const change of changeset.changes) {
7156
+ const withChange = [...current, change];
7157
+ if (current.length > 0 && requestBytesFor(withChange) > maxRequestBytes) {
7158
+ chunks.push({ ...changeset, changes: current });
7159
+ current = [];
7160
+ }
7161
+ const singleBytes = requestBytesFor([...current, change]);
7162
+ if (singleBytes > maxRequestBytes) {
7163
+ throw new Error(
7164
+ `offline sync change for ${change.path} exceeds the apply request size budget; retry after direct-push threshold is lowered`
7165
+ );
7166
+ }
7167
+ current.push(change);
7168
+ }
7169
+ if (current.length > 0) {
7170
+ chunks.push({ ...changeset, changes: current });
7171
+ }
7172
+ return chunks;
7173
+ }
7174
+ async function postOfflineChangesBatch(args) {
7130
7175
  return fetchOfflineJson(
7131
7176
  offlineEndpoint(args.remoteUrl, "/remnic/v1/offline-sync/apply"),
7132
7177
  args.token,
@@ -7139,6 +7184,31 @@ async function pushOfflineChanges(args) {
7139
7184
  }
7140
7185
  );
7141
7186
  }
7187
+ async function pushOfflineChanges(args) {
7188
+ let namespace = args.namespace ?? "";
7189
+ let appliedUpserts = 0;
7190
+ let appliedDeletes = 0;
7191
+ let skipped = 0;
7192
+ const conflicts = [];
7193
+ for (const changeset of chunkOfflineChangesetApplyBatches(args.changeset, args.namespace)) {
7194
+ const result = await postOfflineChangesBatch({
7195
+ ...args,
7196
+ changeset
7197
+ });
7198
+ namespace = result.namespace || namespace;
7199
+ appliedUpserts += result.appliedUpserts;
7200
+ appliedDeletes += result.appliedDeletes;
7201
+ skipped += result.skipped;
7202
+ conflicts.push(...result.conflicts);
7203
+ }
7204
+ return {
7205
+ namespace,
7206
+ appliedUpserts,
7207
+ appliedDeletes,
7208
+ skipped,
7209
+ conflicts
7210
+ };
7211
+ }
7142
7212
  function parseOfflineIntervalMs(args) {
7143
7213
  const raw = resolveRequiredValueFlag(args, "--interval-ms");
7144
7214
  if (raw === void 0) return 6e4;
@@ -10604,12 +10674,16 @@ if (argv1Base.endsWith("remnic.ts") || argv1Base.endsWith("remnic.js") || argv1B
10604
10674
  }
10605
10675
  export {
10606
10676
  BENCHMARK_CATALOG,
10677
+ OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES,
10678
+ OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES,
10679
+ OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES,
10607
10680
  TAXONOMY_RESOLVE_BOOLEAN_FLAGS,
10608
10681
  TAXONOMY_RESOLVE_VALUE_FLAGS,
10609
10682
  __benchDatasetTestHooks,
10610
10683
  buildBenchRuntimeProfileRequest,
10611
10684
  buildPackageBenchExecutionPlans,
10612
10685
  buildQueryRecallRequest,
10686
+ chunkOfflineChangesetApplyBatches,
10613
10687
  chunkOfflineFileContentBatches,
10614
10688
  extractXrayRawArgs,
10615
10689
  formatOfflineLargeFilePushFailureMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,8 +26,8 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/plugin-pi": "^1.0.3",
30
- "@remnic/core": "^1.1.23",
29
+ "@remnic/plugin-pi": "^1.0.4",
30
+ "@remnic/core": "^1.1.24",
31
31
  "@remnic/server": "^1.0.5"
32
32
  },
33
33
  "peerDependencies": {
@@ -76,11 +76,11 @@
76
76
  "@remnic/bench": "1.0.1",
77
77
  "@remnic/export-weclone": "1.0.1",
78
78
  "@remnic/import-weclone": "1.0.1",
79
+ "@remnic/import-claude": "0.1.0",
79
80
  "@remnic/import-chatgpt": "0.1.0",
80
81
  "@remnic/import-gemini": "0.1.0",
81
- "@remnic/import-claude": "0.1.0",
82
- "@remnic/import-lossless-claw": "0.1.1",
83
82
  "@remnic/import-mem0": "0.1.0",
83
+ "@remnic/import-lossless-claw": "0.1.1",
84
84
  "@remnic/import-supermemory": "0.1.2"
85
85
  },
86
86
  "license": "MIT",