@remnic/cli 9.3.749 → 9.3.750

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 +116 -11
  2. package/package.json +28 -28
package/dist/index.js CHANGED
@@ -96,6 +96,7 @@ import {
96
96
  applyOfflineSyncFileContentChunk,
97
97
  applyOfflineSyncSnapshot,
98
98
  buildOfflineSyncChangesetFromSnapshot,
99
+ compileOfflineSyncExcludeGlobs,
99
100
  buildOfflineSyncSnapshotFromBase,
100
101
  defaultOfflineSyncStatePath,
101
102
  normalizeOfflineSyncSnapshot,
@@ -8438,12 +8439,14 @@ async function runOfflineSyncOnce(options) {
8438
8439
  includeContent: false,
8439
8440
  includeTranscripts: options.includeTranscripts,
8440
8441
  readFile: storageIo.readFile,
8441
- readFileDigest: storageIo.readFileDigest
8442
+ readFileDigest: storageIo.readFileDigest,
8443
+ userExcludeRegexps: options.userExcludeRegexps
8442
8444
  });
8443
8445
  const pendingSummary = summarizeOfflineSyncPendingFiles({
8444
8446
  baseFiles,
8445
8447
  currentFiles: currentSnapshotForPush.files,
8446
- includeTranscripts: options.includeTranscripts
8448
+ includeTranscripts: options.includeTranscripts,
8449
+ userExcludeRegexps: options.userExcludeRegexps
8447
8450
  });
8448
8451
  const baseByPath = offlineFileStateMap(baseFiles);
8449
8452
  let directPushAppliedUpserts = 0;
@@ -8457,6 +8460,9 @@ async function runOfflineSyncOnce(options) {
8457
8460
  currentFiles: currentSnapshotForPush.files,
8458
8461
  baseFiles
8459
8462
  })) {
8463
+ if (options.skipLargeFilePaths?.has(file.path)) {
8464
+ continue;
8465
+ }
8460
8466
  let result;
8461
8467
  try {
8462
8468
  result = await pushOfflineFileContent({
@@ -8561,6 +8567,7 @@ async function runOfflineSyncOnce(options) {
8561
8567
  partial: true,
8562
8568
  pendingSummary,
8563
8569
  remoteFileCount: partial?.remoteFileCount ?? null,
8570
+ largeFilePushFailures: [...directPushFailures],
8564
8571
  deferred: {
8565
8572
  localChangedDuringPush: [...directPushDeferredPaths].sort(),
8566
8573
  remoteChangedDuringHydrate: [...partial?.remoteDeferredPaths ?? []].sort(),
@@ -8581,7 +8588,8 @@ async function runOfflineSyncOnce(options) {
8581
8588
  includeContent: false,
8582
8589
  includeTranscripts: options.includeTranscripts,
8583
8590
  readFile: storageIo.readFile,
8584
- readFileDigest: storageIo.readFileDigest
8591
+ readFileDigest: storageIo.readFileDigest,
8592
+ userExcludeRegexps: options.userExcludeRegexps
8585
8593
  }) : currentSnapshotForPush;
8586
8594
  let changesetRetryCount = 0;
8587
8595
  for (; ; ) {
@@ -8591,9 +8599,18 @@ async function runOfflineSyncOnce(options) {
8591
8599
  sourceId: localSourceId,
8592
8600
  currentFiles: currentSnapshotForChangeset.files,
8593
8601
  baseFiles,
8594
- excludePaths: [...directPushedPaths, ...directPushDeferredPaths],
8602
+ // 3-strikes skipped large files must be excluded here too — the
8603
+ // direct-push loop skips them, but without this line the changeset
8604
+ // path would still try to upsert them inline (Cursor review, PR
8605
+ // #1793, High severity).
8606
+ excludePaths: [
8607
+ ...directPushedPaths,
8608
+ ...directPushDeferredPaths,
8609
+ ...options.skipLargeFilePaths ?? []
8610
+ ],
8595
8611
  includeTranscripts: options.includeTranscripts,
8596
- readFile: storageIo.readFile
8612
+ readFile: storageIo.readFile,
8613
+ userExcludeRegexps: options.userExcludeRegexps
8597
8614
  });
8598
8615
  break;
8599
8616
  } catch (error) {
@@ -8624,7 +8641,8 @@ async function runOfflineSyncOnce(options) {
8624
8641
  includeContent: false,
8625
8642
  includeTranscripts: options.includeTranscripts,
8626
8643
  readFile: storageIo.readFile,
8627
- readFileDigest: storageIo.readFileDigest
8644
+ readFileDigest: storageIo.readFileDigest,
8645
+ userExcludeRegexps: options.userExcludeRegexps
8628
8646
  });
8629
8647
  }
8630
8648
  }
@@ -8741,9 +8759,10 @@ async function runOfflineSyncOnce(options) {
8741
8759
  includeContent: false,
8742
8760
  includeTranscripts: options.includeTranscripts,
8743
8761
  readFile: storageIo.readFile,
8744
- readFileDigest: storageIo.readFileDigest
8762
+ readFileDigest: storageIo.readFileDigest,
8763
+ excludeNodeLocalState: false
8745
8764
  });
8746
- const applyCurrentSnapshot = directHydratedPaths.size > 0 ? await buildCurrentSnapshotForApply() : currentSnapshot;
8765
+ const applyCurrentSnapshot = await buildCurrentSnapshotForApply();
8747
8766
  let remoteSnapshot;
8748
8767
  try {
8749
8768
  remoteSnapshot = await hydrateOfflineSnapshotContent({
@@ -8854,6 +8873,7 @@ async function runOfflineSyncOnce(options) {
8854
8873
  partial: false,
8855
8874
  pendingSummary,
8856
8875
  remoteFileCount: remoteSnapshot.files.length,
8876
+ largeFilePushFailures: [...directPushFailures],
8857
8877
  deferred: {
8858
8878
  localChangedDuringPush: [...directPushDeferredPaths].sort(),
8859
8879
  remoteChangedDuringHydrate: [...remoteDeferredPaths].sort(),
@@ -8917,6 +8937,53 @@ function assertOfflineStateMatches(options) {
8917
8937
  );
8918
8938
  }
8919
8939
  }
8940
+ var OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES = 3;
8941
+ function advanceOfflineLargeFileFailureCounts(options) {
8942
+ const threshold = options.threshold ?? OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES;
8943
+ const next = /* @__PURE__ */ new Map();
8944
+ const newlySkipped = [];
8945
+ for (const failure of options.failures) {
8946
+ const count = (options.counts.get(failure.path) ?? 0) + 1;
8947
+ next.set(failure.path, count);
8948
+ if (count >= threshold) newlySkipped.push(failure.path);
8949
+ }
8950
+ return { counts: next, newlySkipped };
8951
+ }
8952
+ function resolveOfflineSyncUserExcludes(rest) {
8953
+ const globs = [];
8954
+ for (let i = 0; i < rest.length; i += 1) {
8955
+ if (rest[i] !== "--exclude") continue;
8956
+ const value = rest[i + 1];
8957
+ if (value === void 0 || value.startsWith("--") || value.trim().length === 0) {
8958
+ throw new Error("--exclude requires a glob argument");
8959
+ }
8960
+ globs.push(value.trim());
8961
+ i += 1;
8962
+ }
8963
+ const configPath = resolveConfigPath();
8964
+ try {
8965
+ const raw = fs7.existsSync(configPath) ? JSON.parse(fs7.readFileSync(configPath, "utf8")) : {};
8966
+ const remnicCfg = raw.remnic ?? raw.engram ?? raw;
8967
+ const configured = remnicCfg.offlineSyncExcludes;
8968
+ if (configured !== void 0 && configured !== null) {
8969
+ if (!Array.isArray(configured)) {
8970
+ throw new Error("offlineSyncExcludes config must be an array of non-empty glob strings");
8971
+ }
8972
+ for (const entry of configured) {
8973
+ if (typeof entry !== "string" || entry.trim().length === 0) {
8974
+ throw new Error("offlineSyncExcludes config must contain only non-empty glob strings");
8975
+ }
8976
+ globs.push(entry.trim());
8977
+ }
8978
+ }
8979
+ } catch (error) {
8980
+ if (error instanceof SyntaxError) {
8981
+ throw new Error(`cannot read offlineSyncExcludes from ${configPath}: ${error.message}`);
8982
+ }
8983
+ throw error;
8984
+ }
8985
+ return compileOfflineSyncExcludeGlobs(globs);
8986
+ }
8920
8987
  async function cmdOffline(action, rest, json) {
8921
8988
  if (action === "help" || action === "--help" || action === "-h" || rest.includes("--help") || rest.includes("-h")) {
8922
8989
  console.log(`Usage: remnic offline <prepare|sync|status|watch> [options]
@@ -8929,6 +8996,9 @@ Options:
8929
8996
  --state <path> Override offline sync state file
8930
8997
  --no-transcripts Exclude transcripts/ from the offline cache
8931
8998
  --interval-ms <ms> Watch interval (default 60000)
8999
+ --exclude <glob> Extra push-side exclude (repeatable; additive to the
9000
+ built-in node-local state excludes and to the
9001
+ offlineSyncExcludes config key)
8932
9002
  --json JSON output
8933
9003
 
8934
9004
  Environment fallbacks:
@@ -8940,6 +9010,7 @@ Environment fallbacks:
8940
9010
  const includeTranscripts = !hasFlag(rest, "--no-transcripts");
8941
9011
  const stateOverride = resolveRequiredValueFlag(rest, "--state");
8942
9012
  const statePathExplicit = stateOverride !== void 0;
9013
+ const userExcludeRegexps = resolveOfflineSyncUserExcludes(rest);
8943
9014
  const needsRemote = action === "prepare" || action === "sync" || action === "watch";
8944
9015
  const remoteUrl = needsRemote ? resolveOfflineRemoteUrl(rest) : resolveOptionalOfflineRemoteUrl(rest);
8945
9016
  const token = needsRemote ? resolveOfflineToken(rest) : void 0;
@@ -9016,7 +9087,8 @@ Environment fallbacks:
9016
9087
  namespace,
9017
9088
  includeTranscripts,
9018
9089
  statePath,
9019
- statePathExplicit
9090
+ statePathExplicit,
9091
+ userExcludeRegexps
9020
9092
  });
9021
9093
  if (json) {
9022
9094
  console.log(JSON.stringify(offlineSyncResultJsonSummary(result), null, 2));
@@ -9057,7 +9129,8 @@ Environment fallbacks:
9057
9129
  baseCapturedAt: state ? new Date(state.lastSyncedAt) : void 0,
9058
9130
  includeTranscripts,
9059
9131
  readFile: storageIo.readFile,
9060
- readFileDigest: storageIo.readFileDigest
9132
+ readFileDigest: storageIo.readFileDigest,
9133
+ userExcludeRegexps
9061
9134
  });
9062
9135
  if (json) {
9063
9136
  console.log(JSON.stringify({
@@ -9084,6 +9157,8 @@ Environment fallbacks:
9084
9157
  cancelSleep?.();
9085
9158
  console.log("Stopping offline sync watcher.");
9086
9159
  });
9160
+ let largeFileFailureCounts = /* @__PURE__ */ new Map();
9161
+ const skippedLargeFiles = /* @__PURE__ */ new Set();
9087
9162
  while (!stopped) {
9088
9163
  try {
9089
9164
  const result = await runOfflineSyncOnce({
@@ -9093,14 +9168,42 @@ Environment fallbacks:
9093
9168
  namespace,
9094
9169
  includeTranscripts,
9095
9170
  statePath,
9096
- statePathExplicit
9171
+ statePathExplicit,
9172
+ userExcludeRegexps,
9173
+ skipLargeFilePaths: skippedLargeFiles
9097
9174
  });
9175
+ const advanced = advanceOfflineLargeFileFailureCounts({
9176
+ counts: largeFileFailureCounts,
9177
+ failures: result.largeFilePushFailures
9178
+ });
9179
+ largeFileFailureCounts = advanced.counts;
9180
+ for (const path12 of advanced.newlySkipped) {
9181
+ if (skippedLargeFiles.has(path12)) continue;
9182
+ skippedLargeFiles.add(path12);
9183
+ console.warn(
9184
+ `offline sync: permanently skipping ${path12} after ${OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES} failed large-file pushes for this watcher process (see issue #1786; use --exclude or offlineSyncExcludes to silence permanently)`
9185
+ );
9186
+ }
9098
9187
  const pulled = result.pull ? result.pull.upserted + result.pull.deleted : 0;
9099
9188
  const conflicts = (result.pushed?.conflicts.length ?? 0) + (result.pull?.conflicts.length ?? 0);
9100
9189
  console.log(
9101
9190
  `[${(/* @__PURE__ */ new Date()).toISOString()}] sync ${result.partial ? "partial" : "ok"}: pushed=${result.pushed ? result.pushed.appliedUpserts + result.pushed.appliedDeletes : 0}, pulled=${pulled}, conflicts=${conflicts}, deferred=${result.deferred.total}${result.pullError ? `, pullError=${result.pullError}` : ""}`
9102
9191
  );
9103
9192
  } catch (error) {
9193
+ if (error instanceof OfflineLargeFilePushError) {
9194
+ const advanced = advanceOfflineLargeFileFailureCounts({
9195
+ counts: largeFileFailureCounts,
9196
+ failures: error.failures
9197
+ });
9198
+ largeFileFailureCounts = advanced.counts;
9199
+ for (const path12 of advanced.newlySkipped) {
9200
+ if (skippedLargeFiles.has(path12)) continue;
9201
+ skippedLargeFiles.add(path12);
9202
+ console.warn(
9203
+ `offline sync: permanently skipping ${path12} after ${OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES} failed large-file pushes for this watcher process (see issue #1786; use --exclude or offlineSyncExcludes to silence permanently)`
9204
+ );
9205
+ }
9206
+ }
9104
9207
  console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] sync waiting: ${error instanceof Error ? error.message : String(error)}`);
9105
9208
  }
9106
9209
  if (stopped) break;
@@ -12112,6 +12215,7 @@ if (argv1Base.endsWith("remnic.ts") || argv1Base.endsWith("remnic.js") || argv1B
12112
12215
  }
12113
12216
  export {
12114
12217
  BENCHMARK_CATALOG,
12218
+ OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES,
12115
12219
  OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES,
12116
12220
  OFFLINE_SYNC_CHANGESET_RETRY_MAX,
12117
12221
  OFFLINE_SYNC_CONTENT_MISSING_RETRY_DELAY_MS,
@@ -12126,6 +12230,7 @@ export {
12126
12230
  TAXONOMY_RESOLVE_VALUE_FLAGS,
12127
12231
  __benchDatasetTestHooks,
12128
12232
  advanceOfflineBaseFilesForSuccessfulPush,
12233
+ advanceOfflineLargeFileFailureCounts,
12129
12234
  buildBenchRuntimeProfileRequest,
12130
12235
  buildPackageBenchExecutionPlans,
12131
12236
  buildQueryRecallRequest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "9.3.749",
3
+ "version": "9.3.750",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,23 +26,23 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/server": "^9.3.749",
30
- "@remnic/plugin-pi": "^9.3.749",
31
- "@remnic/core": "^9.3.749"
29
+ "@remnic/plugin-pi": "^9.3.750",
30
+ "@remnic/core": "^9.3.750",
31
+ "@remnic/server": "^9.3.750"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^9.3.749",
35
- "@remnic/export-weclone": "^9.3.749",
36
- "@remnic/import-weclone": "^9.3.749",
37
- "@remnic/import-chatgpt": "^9.3.749",
38
- "@remnic/import-claude": "^9.3.749",
39
- "@remnic/import-gemini": "^9.3.749",
40
- "@remnic/import-lossless-claw": "^9.3.749",
41
- "@remnic/import-mem0": "^9.3.749",
42
- "@remnic/import-supermemory": "^9.3.749",
43
- "@remnic/connector-limitless": "^9.3.749",
44
- "@remnic/connector-bee": "^9.3.749",
45
- "@remnic/connector-omi": "^9.3.749"
34
+ "@remnic/bench": "^9.3.750",
35
+ "@remnic/export-weclone": "^9.3.750",
36
+ "@remnic/import-weclone": "^9.3.750",
37
+ "@remnic/import-chatgpt": "^9.3.750",
38
+ "@remnic/import-claude": "^9.3.750",
39
+ "@remnic/import-gemini": "^9.3.750",
40
+ "@remnic/import-lossless-claw": "^9.3.750",
41
+ "@remnic/import-mem0": "^9.3.750",
42
+ "@remnic/import-supermemory": "^9.3.750",
43
+ "@remnic/connector-limitless": "^9.3.750",
44
+ "@remnic/connector-bee": "^9.3.750",
45
+ "@remnic/connector-omi": "^9.3.750"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "@remnic/bench": {
@@ -85,18 +85,18 @@
85
85
  "devDependencies": {
86
86
  "tsup": "^8.5.1",
87
87
  "typescript": "^5.9.3",
88
- "@remnic/bench": "9.3.749",
89
- "@remnic/export-weclone": "9.3.749",
90
- "@remnic/import-weclone": "9.3.749",
91
- "@remnic/import-chatgpt": "9.3.749",
92
- "@remnic/import-claude": "9.3.749",
93
- "@remnic/import-lossless-claw": "9.3.749",
94
- "@remnic/import-mem0": "9.3.749",
95
- "@remnic/import-supermemory": "9.3.749",
96
- "@remnic/connector-bee": "9.3.749",
97
- "@remnic/connector-limitless": "9.3.749",
98
- "@remnic/import-gemini": "9.3.749",
99
- "@remnic/connector-omi": "9.3.749"
88
+ "@remnic/bench": "9.3.750",
89
+ "@remnic/export-weclone": "9.3.750",
90
+ "@remnic/import-weclone": "9.3.750",
91
+ "@remnic/import-chatgpt": "9.3.750",
92
+ "@remnic/import-gemini": "9.3.750",
93
+ "@remnic/import-lossless-claw": "9.3.750",
94
+ "@remnic/import-mem0": "9.3.750",
95
+ "@remnic/import-supermemory": "9.3.750",
96
+ "@remnic/connector-limitless": "9.3.750",
97
+ "@remnic/import-claude": "9.3.750",
98
+ "@remnic/connector-bee": "9.3.750",
99
+ "@remnic/connector-omi": "9.3.750"
100
100
  },
101
101
  "license": "MIT",
102
102
  "repository": {