@remnic/cli 10.0.13 → 10.0.14

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 +55 -9
  2. package/package.json +32 -32
package/dist/index.js CHANGED
@@ -2944,9 +2944,20 @@ async function createOfflineStorageForPath(memoryDir, filePath, configured, secu
2944
2944
  }
2945
2945
  return storage;
2946
2946
  }
2947
- async function createOfflineStorageIo(memoryDir, configuredStorage, persistedExclusion) {
2947
+ async function createOfflineStorageIo(memoryDir, configuredStorage, persistedExclusion, captureWriteLockBackoff, signal) {
2948
2948
  await cleanupOrphanedOfflineDecryptStaging(memoryDir);
2949
2949
  const { storage, secureStoreKey } = configuredStorage ?? await createConfiguredOfflineStorage(memoryDir);
2950
+ if (captureWriteLockBackoff || signal) {
2951
+ storage.factHashIndexLockOptions = {
2952
+ ...storage.factHashIndexLockOptions,
2953
+ ...captureWriteLockBackoff ? {
2954
+ captureWriteLockInitialDelayMs: captureWriteLockBackoff.initialDelayMs,
2955
+ captureWriteLockMaxDelayMs: captureWriteLockBackoff.maxDelayMs,
2956
+ captureWriteLockDeadlineMs: captureWriteLockBackoff.deadlineMs
2957
+ } : {},
2958
+ ...signal ? { captureWriteLockSignal: signal } : {}
2959
+ };
2960
+ }
2950
2961
  return {
2951
2962
  // With persisted classifications the warm path skips the per-file
2952
2963
  // read+parse the plain exclusion performs for every candidate.
@@ -2965,8 +2976,15 @@ async function createOfflineStorageIo(memoryDir, configuredStorage, persistedExc
2965
2976
  chunkSize
2966
2977
  }),
2967
2978
  writeFile: async ({ filePath, content }) => storage.writeOfflineSyncFile(filePath, content),
2968
- writeStagingFile: async ({ filePath, content, finalAadFilePath }) => storage.writeOfflineSyncStagingFile(filePath, content, finalAadFilePath === void 0 ? void 0 : { aadFilePath: finalAadFilePath }),
2969
- readStagingFile: async ({ filePath, finalAadFilePath }) => storage.readOfflineSyncFile(filePath, finalAadFilePath === void 0 ? void 0 : { aadFilePath: finalAadFilePath }),
2979
+ writeStagingFile: async ({ filePath, content, finalAadFilePath }) => storage.writeOfflineSyncStagingFile(
2980
+ filePath,
2981
+ content,
2982
+ finalAadFilePath === void 0 ? void 0 : { aadFilePath: finalAadFilePath }
2983
+ ),
2984
+ readStagingFile: async ({ filePath, finalAadFilePath }) => storage.readOfflineSyncFile(
2985
+ filePath,
2986
+ finalAadFilePath === void 0 ? void 0 : { aadFilePath: finalAadFilePath }
2987
+ ),
2970
2988
  writeFileChunks: async ({ filePath, chunks }) => storage.writeOfflineSyncFileChunks(filePath, chunks),
2971
2989
  deleteFile: async ({ filePath, mtimeMs }) => storage.deleteOfflineSyncFile(filePath, mtimeMs ?? null),
2972
2990
  recordDeletionRevision: async ({ filePath, mtimeMs }) => storage.recordReplicatedDeletionRevision(filePath, mtimeMs)
@@ -4518,6 +4536,32 @@ function resolveCredentialChannel(input, env, hooks) {
4518
4536
  return { ok: true, token: void 0, tokenFromArgv: false };
4519
4537
  }
4520
4538
 
4539
+ // src/converge-capture-lock.ts
4540
+ import { resolveConvergeCaptureWriteLockBackoff } from "@remnic/core";
4541
+ import { isTombstoneBlockedCaptureWriteLockBusy } from "@remnic/core/capture-write-lock-backoff.js";
4542
+ function createConvergeStorageIoOpener(convergeConfig, signal) {
4543
+ const backoff = resolveConvergeCaptureWriteLockBackoff(convergeConfig);
4544
+ return (rootDir) => createOfflineStorageIo(rootDir, void 0, void 0, backoff, signal);
4545
+ }
4546
+ function handleConvergeTransferResults(results, batch, onFailed) {
4547
+ for (let index = 0; index < results.length; index += 1) {
4548
+ const result = results[index];
4549
+ if (!result || result.status === "fulfilled") continue;
4550
+ if (isTombstoneBlockedCaptureWriteLockBusy(result.reason)) {
4551
+ onFailed();
4552
+ const entry = batch[index];
4553
+ const namespace = entry?.namespace ?? "unknown";
4554
+ const entryPath = entry?.path ?? "";
4555
+ process.stderr.write(
4556
+ `converge: tombstone-blocked capture write lock remained busy for ${namespace} ${entryPath}; continuing the cycle
4557
+ `
4558
+ );
4559
+ continue;
4560
+ }
4561
+ throw result.reason;
4562
+ }
4563
+ }
4564
+
4521
4565
  // src/converge.ts
4522
4566
  async function discoverCursorNamespaces(memoryDir, peerUrl) {
4523
4567
  const cursorDir = path7.join(path7.resolve(memoryDir), ".remnic", "state", "converge-cursors");
@@ -4853,6 +4897,7 @@ async function executeConvergeApply(options = {}) {
4853
4897
  }
4854
4898
  const fetchFn = options.fetchImpl ?? globalThis.fetch;
4855
4899
  const timeoutMs = options.peerRequestTimeoutMs ?? config?.converge.peerRequestTimeoutMs ?? envConvergePeerRequestTimeoutMs2() ?? DEFAULT_PEER_REQUEST_TIMEOUT_MS;
4900
+ const openStorageIo = createConvergeStorageIoOpener(config?.converge, options.signal);
4856
4901
  const rootMap = /* @__PURE__ */ new Map();
4857
4902
  if (config) {
4858
4903
  try {
@@ -4924,7 +4969,7 @@ async function executeConvergeApply(options = {}) {
4924
4969
  actualTransfers.failed += 1;
4925
4970
  return;
4926
4971
  }
4927
- const io = await createOfflineStorageIo(rootDir);
4972
+ const io = await openStorageIo(rootDir);
4928
4973
  const expectedLocalSha256 = entry.action === "conflict" ? entry.localSha256 : entry.baseSha256;
4929
4974
  let transferComplete = false;
4930
4975
  let transferRejected = false;
@@ -5011,7 +5056,7 @@ async function executeConvergeApply(options = {}) {
5011
5056
  if (rootDir) {
5012
5057
  try {
5013
5058
  const filePath = path7.join(rootDir, localPath);
5014
- const io = await createOfflineStorageIo(rootDir);
5059
+ const io = await openStorageIo(rootDir);
5015
5060
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
5016
5061
  if (current.sha256 !== entry.localSha256) {
5017
5062
  throw new Error(`local file changed during push: ${localPath}`);
@@ -5108,7 +5153,7 @@ async function executeConvergeApply(options = {}) {
5108
5153
  const rootDir = rootMap.get(entry.namespace);
5109
5154
  if (rootDir && entry.localSha256) {
5110
5155
  try {
5111
- const io = await createOfflineStorageIo(rootDir);
5156
+ const io = await openStorageIo(rootDir);
5112
5157
  const filePath = path7.join(rootDir, localPath);
5113
5158
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
5114
5159
  if (current.sha256 === entry.localSha256) {
@@ -5163,7 +5208,7 @@ async function executeConvergeApply(options = {}) {
5163
5208
  const rootDir = rootMap.get(entry.namespace);
5164
5209
  if (rootDir) {
5165
5210
  try {
5166
- const io = await createOfflineStorageIo(rootDir);
5211
+ const io = await openStorageIo(rootDir);
5167
5212
  const filePath = path7.join(rootDir, localPath);
5168
5213
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
5169
5214
  if (current.sha256 === entry.localSha256) {
@@ -5213,8 +5258,9 @@ async function executeConvergeApply(options = {}) {
5213
5258
  options.signal?.throwIfAborted();
5214
5259
  const batch = actionable.slice(i, i + TRANSFER_BATCH_SIZE);
5215
5260
  const results = await Promise.allSettled(batch.map((entry) => executeTransferEntry(entry)));
5216
- const rejected = results.find((result) => result.status === "rejected");
5217
- if (rejected && rejected.status === "rejected") throw rejected.reason;
5261
+ handleConvergeTransferResults(results, batch, () => {
5262
+ actualTransfers.failed += 1;
5263
+ });
5218
5264
  }
5219
5265
  options.signal?.throwIfAborted();
5220
5266
  if (options.peerUrl && peerMutatedNamespaces.size > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "10.0.13",
3
+ "version": "10.0.14",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,26 +26,26 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/server": "^10.0.13",
30
- "@remnic/plugin-pi": "^10.0.13",
31
- "@remnic/core": "^10.0.13"
29
+ "@remnic/plugin-pi": "^10.0.14",
30
+ "@remnic/server": "^10.0.14",
31
+ "@remnic/core": "^10.0.14"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^10.0.13",
35
- "@remnic/plugin-openclaw": "^10.0.13",
36
- "@remnic/export-weclone": "^10.0.13",
37
- "@remnic/import-weclone": "^10.0.13",
38
- "@remnic/import-chatgpt": "^10.0.13",
39
- "@remnic/import-claude": "^10.0.13",
40
- "@remnic/import-gemini": "^10.0.13",
41
- "@remnic/import-lossless-claw": "^10.0.13",
42
- "@remnic/import-mem0": "^10.0.13",
43
- "@remnic/import-supermemory": "^10.0.13",
44
- "@remnic/import-okf": "^10.0.13",
45
- "@remnic/connector-limitless": "^10.0.13",
46
- "@remnic/connector-bee": "^10.0.13",
47
- "@remnic/connector-omi": "^10.0.13",
48
- "@remnic/capture-audio": "^10.0.13"
34
+ "@remnic/bench": "^10.0.14",
35
+ "@remnic/plugin-openclaw": "^10.0.14",
36
+ "@remnic/export-weclone": "^10.0.14",
37
+ "@remnic/import-weclone": "^10.0.14",
38
+ "@remnic/import-chatgpt": "^10.0.14",
39
+ "@remnic/import-claude": "^10.0.14",
40
+ "@remnic/import-gemini": "^10.0.14",
41
+ "@remnic/import-lossless-claw": "^10.0.14",
42
+ "@remnic/import-mem0": "^10.0.14",
43
+ "@remnic/import-supermemory": "^10.0.14",
44
+ "@remnic/import-okf": "^10.0.14",
45
+ "@remnic/connector-limitless": "^10.0.14",
46
+ "@remnic/connector-bee": "^10.0.14",
47
+ "@remnic/connector-omi": "^10.0.14",
48
+ "@remnic/capture-audio": "^10.0.14"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "@remnic/bench": {
@@ -97,19 +97,19 @@
97
97
  "devDependencies": {
98
98
  "tsup": "^8.5.1",
99
99
  "typescript": "^5.9.3",
100
- "@remnic/bench": "10.0.13",
101
- "@remnic/plugin-openclaw": "10.0.13",
102
- "@remnic/export-weclone": "10.0.13",
103
- "@remnic/import-chatgpt": "10.0.13",
104
- "@remnic/import-weclone": "10.0.13",
105
- "@remnic/import-claude": "10.0.13",
106
- "@remnic/import-gemini": "10.0.13",
107
- "@remnic/import-mem0": "10.0.13",
108
- "@remnic/import-supermemory": "10.0.13",
109
- "@remnic/connector-omi": "10.0.13",
110
- "@remnic/connector-limitless": "10.0.13",
111
- "@remnic/import-lossless-claw": "10.0.13",
112
- "@remnic/connector-bee": "10.0.13"
100
+ "@remnic/bench": "10.0.14",
101
+ "@remnic/plugin-openclaw": "10.0.14",
102
+ "@remnic/export-weclone": "10.0.14",
103
+ "@remnic/import-weclone": "10.0.14",
104
+ "@remnic/import-chatgpt": "10.0.14",
105
+ "@remnic/import-claude": "10.0.14",
106
+ "@remnic/import-gemini": "10.0.14",
107
+ "@remnic/import-lossless-claw": "10.0.14",
108
+ "@remnic/import-supermemory": "10.0.14",
109
+ "@remnic/import-mem0": "10.0.14",
110
+ "@remnic/connector-omi": "10.0.14",
111
+ "@remnic/connector-bee": "10.0.14",
112
+ "@remnic/connector-limitless": "10.0.14"
113
113
  },
114
114
  "license": "MIT",
115
115
  "repository": {