@remnic/cli 10.0.7 → 10.0.9

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 +453 -148
  2. package/package.json +32 -32
package/dist/index.js CHANGED
@@ -2365,9 +2365,7 @@ import {
2365
2365
  readConvergeCursor,
2366
2366
  writeConvergeCursor
2367
2367
  } from "@remnic/core/reconcile/cursor.js";
2368
- import {
2369
- collapseActiveFactDuplicates
2370
- } from "@remnic/core/reconcile/manifest.js";
2368
+ import { collapseActiveFactDuplicates } from "@remnic/core/reconcile/manifest.js";
2371
2369
  import {
2372
2370
  planReconciliation
2373
2371
  } from "@remnic/core/reconcile/plan.js";
@@ -3364,6 +3362,133 @@ async function planLocalNamespaceCensus(args) {
3364
3362
  return { files, manifest, tombstones: tombstonedFileDigests(evidence, manifest) };
3365
3363
  }
3366
3364
 
3365
+ // src/converge-namespace-exclusions.ts
3366
+ var TRANSPORT_MARKERS = [
3367
+ "aborted due to timeout",
3368
+ "operation was aborted",
3369
+ "fetch failed",
3370
+ "econnrefused",
3371
+ "econnreset",
3372
+ "enotfound",
3373
+ "etimedout",
3374
+ "eai_again",
3375
+ "socket hang up",
3376
+ "und_err_"
3377
+ ];
3378
+ function hasHttpStatus(message) {
3379
+ let from = 0;
3380
+ while (from < message.length) {
3381
+ const at = message.indexOf("HTTP ", from);
3382
+ if (at < 0) return false;
3383
+ const start = at + 5;
3384
+ if (start + 2 < message.length && message.charCodeAt(start) >= 48 && message.charCodeAt(start) <= 57 && message.charCodeAt(start + 1) >= 48 && message.charCodeAt(start + 1) <= 57 && message.charCodeAt(start + 2) >= 48 && message.charCodeAt(start + 2) <= 57) {
3385
+ return true;
3386
+ }
3387
+ from = start;
3388
+ }
3389
+ return false;
3390
+ }
3391
+ function hasTransportMarker(message) {
3392
+ const lower = message.toLowerCase();
3393
+ for (const marker of TRANSPORT_MARKERS) {
3394
+ if (lower.includes(marker)) return true;
3395
+ }
3396
+ return false;
3397
+ }
3398
+ var PEER_PATH_ERROR_PREFIXES = [
3399
+ "failed to read peer reconciliation manifest file:",
3400
+ "failed to read peer tombstone evidence:",
3401
+ "offline file content response changed during transfer:"
3402
+ ];
3403
+ function errorEmbedsPeerPath(message) {
3404
+ for (const prefix of PEER_PATH_ERROR_PREFIXES) {
3405
+ if (message.startsWith(prefix)) return true;
3406
+ }
3407
+ return false;
3408
+ }
3409
+ function isPeerTransportCensusFailure(error) {
3410
+ const seen = /* @__PURE__ */ new Set();
3411
+ let current = error;
3412
+ while (current !== void 0 && current !== null && !seen.has(current)) {
3413
+ seen.add(current);
3414
+ if (!(current instanceof Error)) return false;
3415
+ if (hasHttpStatus(current.message)) return false;
3416
+ if (current.name === "TimeoutError") return true;
3417
+ if (!errorEmbedsPeerPath(current.message) && hasTransportMarker(current.message)) return true;
3418
+ current = "cause" in current ? current.cause : void 0;
3419
+ }
3420
+ return false;
3421
+ }
3422
+ function recordNamespaceCensusFailure(excluded, namespace, side, error, signal) {
3423
+ if (signal?.aborted) throw error;
3424
+ if (side === "peer" && isPeerTransportCensusFailure(error)) throw error;
3425
+ const message = error instanceof Error && error.message.length > 0 ? error.message : String(error);
3426
+ excluded.set(namespace, { namespace, side, error: message, cause: error });
3427
+ }
3428
+ function excludeNamespaceGroup(excluded, namespaces, side, error, signal, drop) {
3429
+ for (const namespace of namespaces) {
3430
+ recordNamespaceCensusFailure(excluded, namespace, side, error, signal);
3431
+ drop(namespace);
3432
+ }
3433
+ }
3434
+ function singleLine(value) {
3435
+ let out = "";
3436
+ let pendingSpace = false;
3437
+ for (let index = 0; index < value.length; index += 1) {
3438
+ const char = value[index] ?? "";
3439
+ const code = char.charCodeAt(0);
3440
+ const isSpace = code === 32 || code === 9 || code === 10 || code === 13;
3441
+ if (isSpace) {
3442
+ pendingSpace = out.length > 0;
3443
+ continue;
3444
+ }
3445
+ if (code < 32 || code === 127 || code >= 128 && code <= 159) continue;
3446
+ if (pendingSpace) {
3447
+ out += " ";
3448
+ pendingSpace = false;
3449
+ }
3450
+ out += char;
3451
+ }
3452
+ return out;
3453
+ }
3454
+ function loggedCensusFailure(failure) {
3455
+ const message = failure.error.length > 0 ? failure.error : String(failure.cause);
3456
+ const cleaned = singleLine(message);
3457
+ return `${singleLine(failure.namespace)} (${failure.side}): ${cleaned.length > 0 ? cleaned : "namespace census failed"}`;
3458
+ }
3459
+ function compareNamespaceSide(left, right) {
3460
+ if (left.namespace < right.namespace) return -1;
3461
+ if (left.namespace > right.namespace) return 1;
3462
+ if (left.side < right.side) return -1;
3463
+ if (left.side > right.side) return 1;
3464
+ return 0;
3465
+ }
3466
+ function listNamespaceExclusions(excluded) {
3467
+ return [...excluded.values()].map(({ namespace, side, error }) => ({ namespace, side, error })).sort(compareNamespaceSide);
3468
+ }
3469
+ function finishConvergeApplyResult(result, excludedNamespaces) {
3470
+ if (excludedNamespaces.length === 0) return result;
3471
+ const failedTransfers = result.status === "applied" && result.transfers.failed > 0;
3472
+ const keepStatus = result.status === "stopped_unresolved_conflicts" || result.status === "dry_run" || failedTransfers;
3473
+ return {
3474
+ ...result,
3475
+ excludedNamespaces,
3476
+ converged: false,
3477
+ status: keepStatus ? result.status : "partial"
3478
+ };
3479
+ }
3480
+ function throwIfEveryNamespaceCensusFailed(plannedNamespaces, excluded) {
3481
+ if (plannedNamespaces > 0 || excluded.size === 0) return;
3482
+ const failures = [...excluded.values()].sort(compareNamespaceSide);
3483
+ const only = failures[0];
3484
+ if (failures.length === 1 && only) {
3485
+ const message = only.error.length > 0 ? only.error : String(only.cause);
3486
+ const cleaned = singleLine(message);
3487
+ throw new Error(cleaned.length > 0 ? cleaned : "namespace census failed", { cause: only.cause });
3488
+ }
3489
+ throw new Error(`converge: every namespace census failed: ${failures.map(loggedCensusFailure).join("; ")}`);
3490
+ }
3491
+
3367
3492
  // src/converge-peer-census.ts
3368
3493
  import { createHash as createHash4 } from "crypto";
3369
3494
  import { parseFrontmatter as parseFrontmatter2 } from "@remnic/core/storage.js";
@@ -3376,10 +3501,10 @@ import {
3376
3501
  // src/converge-peer-transport.ts
3377
3502
  import { createHash as createHash3 } from "crypto";
3378
3503
  import {
3379
- isInternalRemnicStatePath as isInternalRemnicStatePath3,
3380
3504
  OFFLINE_SYNC_CHANGESET_FORMAT,
3381
3505
  OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
3382
- OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2
3506
+ OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2,
3507
+ isInternalRemnicStatePath as isInternalRemnicStatePath3
3383
3508
  } from "@remnic/core";
3384
3509
  import { normalizeConvergePeerUrl as normalizeConvergePeerUrl2 } from "@remnic/core/reconcile/cursor.js";
3385
3510
 
@@ -3609,6 +3734,73 @@ async function fetchPeerManifestStream(peerUrl, namespace, token, fetchImpl = gl
3609
3734
  }
3610
3735
  return null;
3611
3736
  }
3737
+ var SNAPSHOT_ERROR_BODY_CAP_BYTES = 512;
3738
+ function collapseWhitespace(value, max) {
3739
+ let out = "";
3740
+ let pendingSpace = false;
3741
+ for (let index = 0; index < value.length; index += 1) {
3742
+ const char = value[index] ?? "";
3743
+ const code = char.charCodeAt(0);
3744
+ const isSpace = code === 32 || code === 9 || code === 10 || code === 13;
3745
+ if (isSpace) {
3746
+ pendingSpace = out.length > 0;
3747
+ continue;
3748
+ }
3749
+ if (code < 32 || code === 127 || code >= 128 && code <= 159) continue;
3750
+ if (pendingSpace) {
3751
+ if (out.length + 1 >= max) break;
3752
+ out += " ";
3753
+ pendingSpace = false;
3754
+ }
3755
+ if (out.length >= max) break;
3756
+ out += char;
3757
+ }
3758
+ return out;
3759
+ }
3760
+ async function readBoundedBody(response, maxBytes) {
3761
+ const body = response.body;
3762
+ if (!body) return "";
3763
+ const reader = body.getReader();
3764
+ const chunks = [];
3765
+ let received = 0;
3766
+ try {
3767
+ while (received < maxBytes) {
3768
+ const { done, value } = await reader.read();
3769
+ if (done || !value || value.byteLength === 0) break;
3770
+ const remaining = maxBytes - received;
3771
+ if (value.byteLength > remaining) {
3772
+ chunks.push(value.subarray(0, remaining));
3773
+ received += remaining;
3774
+ break;
3775
+ }
3776
+ chunks.push(value);
3777
+ received += value.byteLength;
3778
+ }
3779
+ } catch (error) {
3780
+ if (isPeerTransportCensusFailure(error)) throw error;
3781
+ return "";
3782
+ } finally {
3783
+ await reader.cancel().catch(() => void 0);
3784
+ }
3785
+ return Buffer.concat(chunks).toString("utf8");
3786
+ }
3787
+ async function readSnapshotErrorDetail(response) {
3788
+ const raw = await readBoundedBody(response, SNAPSHOT_ERROR_BODY_CAP_BYTES);
3789
+ const trimmed = raw.trim();
3790
+ if (trimmed.length === 0) return "";
3791
+ let detail = trimmed;
3792
+ if (trimmed.startsWith("{")) {
3793
+ try {
3794
+ const parsed = JSON.parse(trimmed);
3795
+ if (parsed !== null && typeof parsed === "object" && "error" in parsed) {
3796
+ const error = parsed.error;
3797
+ if (typeof error === "string" && error.trim().length > 0) detail = error.trim();
3798
+ }
3799
+ } catch {
3800
+ }
3801
+ }
3802
+ return collapseWhitespace(detail, 160);
3803
+ }
3612
3804
  async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal) {
3613
3805
  const base = normalizePeerBaseUrl(peerUrl);
3614
3806
  const routes = [
@@ -3622,6 +3814,15 @@ async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalTh
3622
3814
  ];
3623
3815
  const headers = token ? { authorization: `Bearer ${token}` } : {};
3624
3816
  let lastFailure = "no snapshot route responded";
3817
+ let transportFailure;
3818
+ const noteTransportFailure = (error) => {
3819
+ if (transportFailure === void 0 && isPeerTransportCensusFailure(error)) transportFailure = error;
3820
+ lastFailure = error instanceof Error && error.message.length > 0 ? error.message : String(error);
3821
+ };
3822
+ const invalidSnapshot = (detail) => {
3823
+ if (transportFailure) throw transportFailure;
3824
+ return new Error(`invalid peer snapshot for namespace ${namespace}: ${detail}`);
3825
+ };
3625
3826
  for (const route of routes) {
3626
3827
  let response;
3627
3828
  try {
@@ -3633,25 +3834,38 @@ async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalTh
3633
3834
  );
3634
3835
  } catch (error) {
3635
3836
  if (signal?.aborted) throw error;
3636
- lastFailure = error instanceof Error ? error.message : String(error);
3837
+ noteTransportFailure(error);
3637
3838
  continue;
3638
3839
  }
3639
3840
  if (!response.ok) {
3640
- lastFailure = `HTTP ${response.status}`;
3841
+ let detail = "";
3842
+ try {
3843
+ detail = await readSnapshotErrorDetail(response);
3844
+ } catch (error) {
3845
+ if (signal?.aborted) throw error;
3846
+ noteTransportFailure(error);
3847
+ continue;
3848
+ }
3849
+ lastFailure = detail.length > 0 ? `HTTP ${response.status} ${detail}` : `HTTP ${response.status}`;
3641
3850
  continue;
3642
3851
  }
3643
3852
  let data;
3644
3853
  try {
3645
3854
  data = await response.json();
3646
- } catch {
3647
- throw new Error(`invalid peer snapshot for namespace ${namespace}: response was not JSON`);
3855
+ } catch (error) {
3856
+ if (signal?.aborted) throw error;
3857
+ if (isPeerTransportCensusFailure(error)) {
3858
+ noteTransportFailure(error);
3859
+ continue;
3860
+ }
3861
+ throw invalidSnapshot("response was not JSON");
3648
3862
  }
3649
3863
  if (!data || typeof data !== "object" || !("files" in data) || !Array.isArray(data.files)) {
3650
- throw new Error(`invalid peer snapshot for namespace ${namespace}: files must be an array`);
3864
+ throw invalidSnapshot("files must be an array");
3651
3865
  }
3652
3866
  const files = data.files.map((item, index) => {
3653
3867
  if (!item || typeof item !== "object" || !("path" in item) || typeof item.path !== "string" || !("sha256" in item) || typeof item.sha256 !== "string") {
3654
- throw new Error(`invalid peer snapshot for namespace ${namespace}: malformed file at index ${index}`);
3868
+ throw invalidSnapshot(`malformed file at index ${index}`);
3655
3869
  }
3656
3870
  return {
3657
3871
  path: item.path,
@@ -3662,17 +3876,18 @@ async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalTh
3662
3876
  }).filter((file) => !isInternalRemnicStatePath3(file.path));
3663
3877
  const rawTombstones = "tombstones" in data ? data.tombstones : void 0;
3664
3878
  if (rawTombstones !== void 0 && !Array.isArray(rawTombstones)) {
3665
- throw new Error(`invalid peer snapshot for namespace ${namespace}: tombstones must be an array`);
3879
+ throw invalidSnapshot("tombstones must be an array");
3666
3880
  }
3667
3881
  const tombstones = /* @__PURE__ */ new Set();
3668
3882
  for (const tombstone of rawTombstones ?? []) {
3669
3883
  if (typeof tombstone !== "string" || !/^[0-9a-f]{64}$/i.test(tombstone)) {
3670
- throw new Error(`invalid peer snapshot for namespace ${namespace}: malformed tombstone`);
3884
+ throw invalidSnapshot("malformed tombstone");
3671
3885
  }
3672
3886
  tombstones.add(tombstone.toLowerCase());
3673
3887
  }
3674
3888
  return { files, tombstones };
3675
3889
  }
3890
+ if (transportFailure) throw transportFailure;
3676
3891
  throw new Error(`failed to fetch peer snapshot for namespace ${namespace}: ${lastFailure}`);
3677
3892
  }
3678
3893
  function requiredResponseNumber(response, name) {
@@ -3683,7 +3898,7 @@ function requiredResponseNumber(response, name) {
3683
3898
  }
3684
3899
  return value;
3685
3900
  }
3686
- async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal) {
3901
+ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal, propagateTransportErrors = false) {
3687
3902
  assertTransferablePeerPath(filePath);
3688
3903
  const base = normalizePeerBaseUrl(peerUrl);
3689
3904
  const routes = ["/remnic/v1/offline-sync/file-content", "/engram/v1/offline-sync/file-content"];
@@ -3696,6 +3911,7 @@ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, toke
3696
3911
  let expectedBytes;
3697
3912
  let expectedSha256;
3698
3913
  let mtimeMs;
3914
+ let transportFailure;
3699
3915
  for (const route of routes) {
3700
3916
  let routeFailed = false;
3701
3917
  do {
@@ -3719,7 +3935,9 @@ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, toke
3719
3935
  timeoutMs
3720
3936
  );
3721
3937
  if (!response.ok) throw new Error(`offline file content request failed: ${response.status}`);
3722
- } catch {
3938
+ } catch (error) {
3939
+ if (signal?.aborted) throw error;
3940
+ if (propagateTransportErrors && isPeerTransportCensusFailure(error)) transportFailure = error;
3723
3941
  routeFailed = true;
3724
3942
  break;
3725
3943
  }
@@ -3738,7 +3956,9 @@ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, toke
3738
3956
  if (!sha256 || chunkOffset !== offset || chunkBytes !== content.length || encodedPath !== null && decodeURIComponent(encodedPath) !== filePath || expectedBytes !== void 0 && expectedBytes !== totalBytes || expectedSha256 !== void 0 && expectedSha256 !== sha256 || content.length === 0 && offset < totalBytes) {
3739
3957
  throw new Error(`offline file content response changed during transfer: ${filePath}`);
3740
3958
  }
3741
- } catch {
3959
+ } catch (error) {
3960
+ if (signal?.aborted) throw error;
3961
+ if (propagateTransportErrors && isPeerTransportCensusFailure(error)) transportFailure = error;
3742
3962
  routeFailed = true;
3743
3963
  break;
3744
3964
  }
@@ -3757,12 +3977,13 @@ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, toke
3757
3977
  } while (expectedBytes === void 0 || offset < expectedBytes);
3758
3978
  if (!routeFailed && expectedBytes !== void 0 && offset === expectedBytes) break;
3759
3979
  }
3760
- if (expectedBytes === void 0 || expectedSha256 === void 0 || mtimeMs === void 0 || offset !== expectedBytes || hash.digest("hex") !== expectedSha256) {
3761
- return null;
3980
+ if (expectedBytes !== void 0 && expectedSha256 !== void 0 && mtimeMs !== void 0 && offset === expectedBytes && hash.digest("hex") === expectedSha256) {
3981
+ return { sha256: expectedSha256, bytes: expectedBytes, mtimeMs };
3762
3982
  }
3763
- return { sha256: expectedSha256, bytes: expectedBytes, mtimeMs };
3983
+ if (transportFailure) throw transportFailure;
3984
+ return null;
3764
3985
  }
3765
- async function fetchPeerFileContent(peerUrl, namespace, filePath, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal) {
3986
+ async function fetchPeerFileContent(peerUrl, namespace, filePath, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal, propagateTransportErrors = false) {
3766
3987
  const chunks = [];
3767
3988
  const metadata = await streamPeerFileContent(
3768
3989
  peerUrl,
@@ -3774,7 +3995,8 @@ async function fetchPeerFileContent(peerUrl, namespace, filePath, token, fetchIm
3774
3995
  token,
3775
3996
  fetchImpl,
3776
3997
  timeoutMs,
3777
- signal
3998
+ signal,
3999
+ propagateTransportErrors
3778
4000
  );
3779
4001
  if (!metadata) return null;
3780
4002
  return {
@@ -3973,6 +4195,7 @@ async function planPeerNamespaceCensus(args) {
3973
4195
  } else {
3974
4196
  clientBuilt = true;
3975
4197
  let readFailure;
4198
+ let transportFailure;
3976
4199
  peerManifest = await buildReconcileManifest2({
3977
4200
  files: peerData.files,
3978
4201
  parseMemory: parseFrontmatter2,
@@ -3982,6 +4205,7 @@ async function planPeerNamespaceCensus(args) {
3982
4205
  // rows (sha-keyed) skip the ones already fetched.
3983
4206
  cachedFiles: priorPeerFiles ?? args.localManifestFiles,
3984
4207
  readFile: async (file) => {
4208
+ if (transportFailure) throw transportFailure;
3985
4209
  let remote;
3986
4210
  try {
3987
4211
  remote = await fetchPeerFileContent(
@@ -3991,19 +4215,24 @@ async function planPeerNamespaceCensus(args) {
3991
4215
  args.resolvedToken,
3992
4216
  args.fetchFn,
3993
4217
  args.timeoutMs,
3994
- args.signal
4218
+ args.signal,
4219
+ true
3995
4220
  );
3996
4221
  } catch (error) {
3997
- readFailure = error instanceof Error ? error : new Error(String(error));
3998
- throw readFailure;
4222
+ const failure = error instanceof Error ? error : new Error(String(error));
4223
+ if (isPeerTransportCensusFailure(error)) transportFailure = transportFailure ?? error;
4224
+ if (!transportFailure) readFailure = failure;
4225
+ throw failure;
3999
4226
  }
4000
4227
  if (!remote || remote.sha256 !== file.sha256) {
4001
- readFailure = new Error(`failed to read peer reconciliation manifest file: ${file.path}`);
4002
- throw readFailure;
4228
+ const failure = new Error(`failed to read peer reconciliation manifest file: ${file.path}`);
4229
+ if (!transportFailure) readFailure = failure;
4230
+ throw failure;
4003
4231
  }
4004
4232
  return remote.content;
4005
4233
  }
4006
4234
  });
4235
+ if (transportFailure) throw transportFailure;
4007
4236
  if (readFailure) throw readFailure;
4008
4237
  }
4009
4238
  }
@@ -4012,15 +4241,21 @@ async function planPeerNamespaceCensus(args) {
4012
4241
  for (const tombstonePath of TOMBSTONE_PATHS) {
4013
4242
  const state = peerData.files.find((file) => file.path === tombstonePath);
4014
4243
  if (!state) continue;
4015
- const remote = await fetchPeerFileContent(
4016
- peerUrl,
4017
- ns,
4018
- tombstonePath,
4019
- args.resolvedToken,
4020
- args.fetchFn,
4021
- args.timeoutMs,
4022
- args.signal
4023
- );
4244
+ let remote;
4245
+ try {
4246
+ remote = await fetchPeerFileContent(
4247
+ peerUrl,
4248
+ ns,
4249
+ tombstonePath,
4250
+ args.resolvedToken,
4251
+ args.fetchFn,
4252
+ args.timeoutMs,
4253
+ args.signal,
4254
+ true
4255
+ );
4256
+ } catch (error) {
4257
+ throw new Error(`failed to read peer tombstone evidence: ${tombstonePath}`, { cause: error });
4258
+ }
4024
4259
  if (!remote) {
4025
4260
  throw new Error(`failed to read peer tombstone evidence: ${tombstonePath}`);
4026
4261
  }
@@ -4072,8 +4307,10 @@ async function planPeerNamespaceCensus(args) {
4072
4307
 
4073
4308
  // src/converge-report.ts
4074
4309
  function formatConvergeReport(plan) {
4310
+ const excluded = plan.excludedNamespaces ?? [];
4075
4311
  const lines = [];
4076
- lines.push(`Convergence Status: ${plan.converged ? "CONVERGED" : "DIVERGED"}`);
4312
+ const status = excluded.length > 0 ? "PARTIAL" : plan.converged ? "CONVERGED" : "DIVERGED";
4313
+ lines.push(`Convergence Status: ${status}`);
4077
4314
  lines.push("");
4078
4315
  lines.push("Per-Namespace Summary:");
4079
4316
  if (plan.byNamespace.length === 0) {
@@ -4089,8 +4326,24 @@ function formatConvergeReport(plan) {
4089
4326
  lines.push(` unresolved: ${report.unresolved}`);
4090
4327
  }
4091
4328
  }
4329
+ if (excluded.length > 0) {
4330
+ lines.push("");
4331
+ lines.push("Excluded namespaces (census failed; no pushes, no deletes):");
4332
+ for (const exclusion of excluded) {
4333
+ lines.push(` [${singleLine(exclusion.namespace)}] side=${exclusion.side} ${singleLine(exclusion.error)}`);
4334
+ }
4335
+ }
4092
4336
  return lines.join("\n");
4093
4337
  }
4338
+ function formatConvergeWatchCycleLine(cycle, result) {
4339
+ const transfers = result.transfers;
4340
+ const excluded = result.excludedNamespaces?.length ?? 0;
4341
+ return `converge watch: cycle ${cycle} status=${result.status} pulled=${transfers.pulled} pushed=${transfers.pushed} conflicts=${transfers.conflictsResolved} failed=${transfers.failed} excluded=${excluded}`;
4342
+ }
4343
+ function formatConvergeExclusionLine(cycle, exclusion) {
4344
+ const prefix = cycle === void 0 ? "converge:" : `converge watch: cycle ${cycle}`;
4345
+ return `${prefix} excluded namespace=${singleLine(exclusion.namespace)} side=${exclusion.side}: ${singleLine(exclusion.error)}`;
4346
+ }
4094
4347
  function formatConvergeApplyReport(result) {
4095
4348
  const lines = [];
4096
4349
  lines.push(`Convergence Execution Status: ${result.status.toUpperCase()}`);
@@ -4107,6 +4360,68 @@ function formatConvergeApplyReport(result) {
4107
4360
  return lines.join("\n");
4108
4361
  }
4109
4362
 
4363
+ // src/converge-watch.ts
4364
+ var CONVERGE_WATCH_MIN_INTERVAL_MS = 1e3;
4365
+ var CONVERGE_WATCH_DEFAULT_INTERVAL_MS = 3e5;
4366
+ var CONVERGE_WATCH_MAX_INTERVAL_MS = 2147483647;
4367
+ function sleepAborted(ms, signal) {
4368
+ return new Promise((resolve3) => {
4369
+ const timer = setTimeout(() => {
4370
+ signal.removeEventListener("abort", onAbort);
4371
+ resolve3(true);
4372
+ }, ms);
4373
+ const onAbort = () => {
4374
+ clearTimeout(timer);
4375
+ resolve3(false);
4376
+ };
4377
+ if (signal.aborted) {
4378
+ clearTimeout(timer);
4379
+ resolve3(false);
4380
+ return;
4381
+ }
4382
+ signal.addEventListener("abort", onAbort, { once: true });
4383
+ });
4384
+ }
4385
+ async function convergeWatch(options) {
4386
+ const intervalMs = Math.min(
4387
+ CONVERGE_WATCH_MAX_INTERVAL_MS,
4388
+ Math.max(CONVERGE_WATCH_MIN_INTERVAL_MS, options.intervalMs ?? CONVERGE_WATCH_DEFAULT_INTERVAL_MS)
4389
+ );
4390
+ const { apply, intervalMs: _intervalMs, maxCycles, onCycle, signal, ...applyOptions } = options;
4391
+ const outcome = {
4392
+ cycles: 0,
4393
+ convergedCycles: 0,
4394
+ appliedCycles: 0,
4395
+ failedCycles: 0,
4396
+ lastStatus: "aborted",
4397
+ lastExcludedNamespaces: []
4398
+ };
4399
+ while (maxCycles === void 0 || outcome.cycles < maxCycles) {
4400
+ if (signal?.aborted) break;
4401
+ try {
4402
+ const result = await apply(applyOptions);
4403
+ outcome.cycles += 1;
4404
+ if (result.status === "converged") outcome.convergedCycles += 1;
4405
+ else if (result.status === "stopped_unresolved_conflicts" || result.status === "applied" && result.transfers.failed > 0) {
4406
+ outcome.failedCycles += 1;
4407
+ } else outcome.appliedCycles += 1;
4408
+ outcome.lastStatus = result.status;
4409
+ outcome.lastExcludedNamespaces = result.excludedNamespaces ? [...result.excludedNamespaces] : [];
4410
+ onCycle?.(outcome.cycles, { result });
4411
+ } catch (err) {
4412
+ outcome.cycles += 1;
4413
+ outcome.failedCycles += 1;
4414
+ outcome.lastStatus = "error";
4415
+ outcome.lastExcludedNamespaces = [];
4416
+ onCycle?.(outcome.cycles, { error: err });
4417
+ }
4418
+ if (maxCycles !== void 0 && outcome.cycles >= maxCycles) break;
4419
+ const slept = await sleepAborted(intervalMs, signal ?? new AbortController().signal);
4420
+ if (!slept) break;
4421
+ }
4422
+ return outcome;
4423
+ }
4424
+
4110
4425
  // src/credential-channel.ts
4111
4426
  import * as fs20 from "fs";
4112
4427
  function parseTokenFileFlag(raw) {
@@ -4203,65 +4518,6 @@ function resolveCredentialChannel(input, env, hooks) {
4203
4518
  return { ok: true, token: void 0, tokenFromArgv: false };
4204
4519
  }
4205
4520
 
4206
- // src/converge-watch.ts
4207
- var CONVERGE_WATCH_MIN_INTERVAL_MS = 1e3;
4208
- var CONVERGE_WATCH_DEFAULT_INTERVAL_MS = 3e5;
4209
- var CONVERGE_WATCH_MAX_INTERVAL_MS = 2147483647;
4210
- function sleepAborted(ms, signal) {
4211
- return new Promise((resolve3) => {
4212
- const timer = setTimeout(() => {
4213
- signal.removeEventListener("abort", onAbort);
4214
- resolve3(true);
4215
- }, ms);
4216
- const onAbort = () => {
4217
- clearTimeout(timer);
4218
- resolve3(false);
4219
- };
4220
- if (signal.aborted) {
4221
- clearTimeout(timer);
4222
- resolve3(false);
4223
- return;
4224
- }
4225
- signal.addEventListener("abort", onAbort, { once: true });
4226
- });
4227
- }
4228
- async function convergeWatch(options) {
4229
- const intervalMs = Math.min(
4230
- CONVERGE_WATCH_MAX_INTERVAL_MS,
4231
- Math.max(CONVERGE_WATCH_MIN_INTERVAL_MS, options.intervalMs ?? CONVERGE_WATCH_DEFAULT_INTERVAL_MS)
4232
- );
4233
- const { apply, intervalMs: _intervalMs, maxCycles, onCycle, signal, ...applyOptions } = options;
4234
- const outcome = {
4235
- cycles: 0,
4236
- convergedCycles: 0,
4237
- appliedCycles: 0,
4238
- failedCycles: 0,
4239
- lastStatus: "aborted"
4240
- };
4241
- while (maxCycles === void 0 || outcome.cycles < maxCycles) {
4242
- if (signal?.aborted) break;
4243
- try {
4244
- const result = await apply(applyOptions);
4245
- outcome.cycles += 1;
4246
- if (result.status === "converged") outcome.convergedCycles += 1;
4247
- else if (result.status === "stopped_unresolved_conflicts" || result.status === "applied" && result.transfers.failed > 0) {
4248
- outcome.failedCycles += 1;
4249
- } else outcome.appliedCycles += 1;
4250
- outcome.lastStatus = result.status;
4251
- onCycle?.(outcome.cycles, { result });
4252
- } catch (err) {
4253
- outcome.cycles += 1;
4254
- outcome.failedCycles += 1;
4255
- outcome.lastStatus = "error";
4256
- onCycle?.(outcome.cycles, { error: err });
4257
- }
4258
- if (maxCycles !== void 0 && outcome.cycles >= maxCycles) break;
4259
- const slept = await sleepAborted(intervalMs, signal ?? new AbortController().signal);
4260
- if (!slept) break;
4261
- }
4262
- return outcome;
4263
- }
4264
-
4265
4521
  // src/converge.ts
4266
4522
  async function discoverCursorNamespaces(memoryDir, peerUrl) {
4267
4523
  const cursorDir = path7.join(path7.resolve(memoryDir), ".remnic", "state", "converge-cursors");
@@ -4302,7 +4558,22 @@ async function computeConvergePlan(options = {}) {
4302
4558
  const peerDeletionMtimeMs = /* @__PURE__ */ new Map();
4303
4559
  const localManifests = /* @__PURE__ */ new Map();
4304
4560
  const peerManifests = /* @__PURE__ */ new Map();
4561
+ const excluded = /* @__PURE__ */ new Map();
4562
+ const sharedRootAliases = /* @__PURE__ */ new Map();
4305
4563
  let peerPlatform;
4564
+ const dropPlannedNamespace = (namespace) => {
4565
+ namespacesToPlan.delete(namespace);
4566
+ baseMap.delete(namespace);
4567
+ semanticAgreementMap.delete(namespace);
4568
+ localMap.delete(namespace);
4569
+ localTombstones.delete(namespace);
4570
+ localDeletionMtimeMs.delete(namespace);
4571
+ localManifests.delete(namespace);
4572
+ peerMap.delete(namespace);
4573
+ peerTombstones.delete(namespace);
4574
+ peerDeletionMtimeMs.delete(namespace);
4575
+ peerManifests.delete(namespace);
4576
+ };
4306
4577
  if (options.baseFilesByNamespace) {
4307
4578
  for (const [ns, files] of options.baseFilesByNamespace) {
4308
4579
  namespacesToPlan.add(ns);
@@ -4382,27 +4653,32 @@ async function computeConvergePlan(options = {}) {
4382
4653
  }
4383
4654
  for (const [rootIndex, rootInfo] of roots.entries()) {
4384
4655
  namespacesToPlan.add(rootInfo.namespace);
4385
- const census = await planLocalNamespaceCensus({
4386
- rootDir: rootInfo.rootDir,
4387
- namespace: rootInfo.namespace,
4388
- index: rootIndex + 1,
4389
- total: roots.length,
4390
- citationTemplate: config.inlineSourceAttributionFormat,
4391
- cache,
4392
- signal: options.signal,
4393
- onProgress: options.onProgress,
4394
- memoryDir,
4395
- peerUrl: options.peerUrl,
4396
- userExcludeRegexps
4397
- });
4398
- localMap.set(rootInfo.namespace, census.files);
4399
- localManifests.set(rootInfo.namespace, census.manifest);
4400
- localTombstones.set(rootInfo.namespace, census.tombstones);
4656
+ for (const alias of rootInfo.namespaces) sharedRootAliases.set(alias, rootInfo.namespaces);
4657
+ try {
4658
+ const census = await planLocalNamespaceCensus({
4659
+ rootDir: rootInfo.rootDir,
4660
+ namespace: rootInfo.namespace,
4661
+ index: rootIndex + 1,
4662
+ total: roots.length,
4663
+ citationTemplate: config.inlineSourceAttributionFormat,
4664
+ cache,
4665
+ signal: options.signal,
4666
+ onProgress: options.onProgress,
4667
+ memoryDir,
4668
+ peerUrl: options.peerUrl,
4669
+ userExcludeRegexps
4670
+ });
4671
+ localMap.set(rootInfo.namespace, census.files);
4672
+ localManifests.set(rootInfo.namespace, census.manifest);
4673
+ localTombstones.set(rootInfo.namespace, census.tombstones);
4674
+ } catch (error) {
4675
+ excludeNamespaceGroup(excluded, rootInfo.namespaces, "local", error, options.signal, dropPlannedNamespace);
4676
+ }
4401
4677
  }
4402
4678
  }
4403
4679
  if (memoryDir && peerUrl) {
4404
4680
  for (const namespace of await discoverCursorNamespaces(memoryDir, peerUrl)) {
4405
- namespacesToPlan.add(namespace);
4681
+ if (!excluded.has(namespace)) namespacesToPlan.add(namespace);
4406
4682
  }
4407
4683
  }
4408
4684
  if (!options.peerFilesByNamespace && peerUrl) {
@@ -4422,27 +4698,34 @@ async function computeConvergePlan(options = {}) {
4422
4698
  peerPlatform = capabilities?.platform;
4423
4699
  const sortedNamespaces = [...namespacesToPlan].sort();
4424
4700
  for (const [nsIndex, ns] of sortedNamespaces.entries()) {
4425
- const census = await planPeerNamespaceCensus({
4426
- peerUrl,
4427
- namespace: ns,
4428
- index: nsIndex + 1,
4429
- total: sortedNamespaces.length,
4430
- resolvedToken,
4431
- fetchFn,
4432
- timeoutMs,
4433
- manifestStream: capabilities?.manifestStream === true,
4434
- peerManifestRevision: capabilities?.manifestRevision,
4435
- citationTemplate: config?.inlineSourceAttributionFormat,
4436
- localManifestFiles: localManifests.get(ns)?.files,
4437
- cache,
4438
- signal: options.signal,
4439
- onProgress: options.onProgress
4440
- });
4441
- peerMap.set(ns, census.manifest.files);
4442
- peerManifests.set(ns, census.manifest);
4443
- peerTombstones.set(ns, census.tombstones);
4701
+ if (excluded.has(ns)) continue;
4702
+ try {
4703
+ const census = await planPeerNamespaceCensus({
4704
+ peerUrl,
4705
+ namespace: ns,
4706
+ index: nsIndex + 1,
4707
+ total: sortedNamespaces.length,
4708
+ resolvedToken,
4709
+ fetchFn,
4710
+ timeoutMs,
4711
+ manifestStream: capabilities?.manifestStream === true,
4712
+ peerManifestRevision: capabilities?.manifestRevision,
4713
+ citationTemplate: config?.inlineSourceAttributionFormat,
4714
+ localManifestFiles: localManifests.get(ns)?.files,
4715
+ cache,
4716
+ signal: options.signal,
4717
+ onProgress: options.onProgress
4718
+ });
4719
+ peerMap.set(ns, census.manifest.files);
4720
+ peerManifests.set(ns, census.manifest);
4721
+ peerTombstones.set(ns, census.tombstones);
4722
+ } catch (error) {
4723
+ const aliases = sharedRootAliases.get(ns) ?? [ns];
4724
+ excludeNamespaceGroup(excluded, aliases, "peer", error, options.signal, dropPlannedNamespace);
4725
+ }
4444
4726
  }
4445
4727
  }
4728
+ throwIfEveryNamespaceCensusFailed(namespacesToPlan.size, excluded);
4446
4729
  if (memoryDir && options.peerUrl && (!options.baseFilesByNamespace || !options.semanticAgreementsByNamespace)) {
4447
4730
  for (const ns of namespacesToPlan) {
4448
4731
  const cursorPath = defaultConvergeCursorPath(memoryDir, options.peerUrl, ns);
@@ -4465,6 +4748,7 @@ async function computeConvergePlan(options = {}) {
4465
4748
  }
4466
4749
  const inputs = [];
4467
4750
  for (const ns of [...namespacesToPlan].sort()) {
4751
+ if (excluded.has(ns)) continue;
4468
4752
  const baseFiles = baseMap.get(ns);
4469
4753
  const localFiles = localMap.get(ns) ?? [];
4470
4754
  const peerFiles = peerMap.get(ns) ?? [];
@@ -4482,8 +4766,11 @@ async function computeConvergePlan(options = {}) {
4482
4766
  });
4483
4767
  }
4484
4768
  const conflictPolicy = options.conflictPolicy ?? config?.converge.conflictPolicy ?? DEFAULT_CONVERGE_CONFLICT_POLICY;
4485
- const plan = planReconciliation(inputs, { conflictPolicy, peerPlatform });
4486
- return collapseActiveFactDuplicates(plan, localManifests, peerManifests, semanticAgreementMap);
4769
+ const planned = planReconciliation(inputs, { conflictPolicy, peerPlatform });
4770
+ const plan = collapseActiveFactDuplicates(planned, localManifests, peerManifests, semanticAgreementMap);
4771
+ const excludedNamespaces = listNamespaceExclusions(excluded);
4772
+ if (excludedNamespaces.length === 0) return plan;
4773
+ return { ...plan, excludedNamespaces, converged: false };
4487
4774
  } finally {
4488
4775
  await cache?.close();
4489
4776
  }
@@ -4495,25 +4782,27 @@ async function executeConvergeApply(options = {}) {
4495
4782
  }
4496
4783
  const conflictPolicy = options.conflictPolicy ?? options.config?.converge.conflictPolicy ?? DEFAULT_CONVERGE_CONFLICT_POLICY;
4497
4784
  const plan = await computeConvergePlan({ ...options, conflictPolicy });
4785
+ const excludedNamespaces = plan.excludedNamespaces ?? [];
4786
+ const finish = (result) => finishConvergeApplyResult(result, excludedNamespaces);
4498
4787
  if (plan.converged && !options.dryRun) {
4499
4788
  await updateCursorsForPlan(plan, options);
4500
- return {
4789
+ return finish({
4501
4790
  converged: true,
4502
4791
  status: "converged",
4503
4792
  plan,
4504
4793
  transfers: { pulled: 0, pushed: 0, conflictsResolved: 0, suppressed: 0, failed: 0 },
4505
4794
  cursorUpdated: true
4506
- };
4795
+ });
4507
4796
  }
4508
4797
  const unresolvedCount = plan.byNamespace.reduce((acc, report) => acc + report.unresolved, 0);
4509
4798
  if (unresolvedCount > 0) {
4510
- return {
4799
+ return finish({
4511
4800
  converged: false,
4512
4801
  status: "stopped_unresolved_conflicts",
4513
4802
  plan,
4514
4803
  transfers: { pulled: 0, pushed: 0, conflictsResolved: 0, suppressed: 0, failed: 0 },
4515
4804
  cursorUpdated: false
4516
- };
4805
+ });
4517
4806
  }
4518
4807
  const plannedTransfers = {
4519
4808
  pulled: 0,
@@ -4529,13 +4818,13 @@ async function executeConvergeApply(options = {}) {
4529
4818
  else if (entry.action === "suppress") plannedTransfers.suppressed += 1;
4530
4819
  }
4531
4820
  if (options.dryRun) {
4532
- return {
4821
+ return finish({
4533
4822
  converged: plan.converged,
4534
4823
  status: "dry_run",
4535
4824
  plan,
4536
4825
  transfers: plannedTransfers,
4537
4826
  cursorUpdated: false
4538
- };
4827
+ });
4539
4828
  }
4540
4829
  const actualTransfers = {
4541
4830
  pulled: 0,
@@ -4930,7 +5219,14 @@ async function executeConvergeApply(options = {}) {
4930
5219
  options.signal?.throwIfAborted();
4931
5220
  if (options.peerUrl && peerMutatedNamespaces.size > 0) {
4932
5221
  const namespaces = [...peerMutatedNamespaces].sort();
4933
- if (!await postPeerConvergenceComplete(options.peerUrl, namespaces, resolvedToken, fetchFn, timeoutMs, options.signal)) {
5222
+ if (!await postPeerConvergenceComplete(
5223
+ options.peerUrl,
5224
+ namespaces,
5225
+ resolvedToken,
5226
+ fetchFn,
5227
+ timeoutMs,
5228
+ options.signal
5229
+ )) {
4934
5230
  actualTransfers.failed += 1;
4935
5231
  }
4936
5232
  }
@@ -4940,13 +5236,13 @@ async function executeConvergeApply(options = {}) {
4940
5236
  await updateCursorsForPlan(plan, options);
4941
5237
  cursorUpdated = true;
4942
5238
  }
4943
- return {
5239
+ return finish({
4944
5240
  converged: actualTransfers.failed === 0,
4945
5241
  status: "applied",
4946
5242
  plan,
4947
5243
  transfers: actualTransfers,
4948
5244
  cursorUpdated
4949
- };
5245
+ });
4950
5246
  }
4951
5247
  async function updateCursorsForPlan(plan, options) {
4952
5248
  const peerUrl = normalizeConvergePeerUrl3(options.peerUrl ?? "local");
@@ -5095,24 +5391,25 @@ Subcommands:
5095
5391
  intervalMs: intervalSeconds !== void 0 ? intervalSeconds * 1e3 : void 0,
5096
5392
  ...timeoutMsFlag !== void 0 ? { peerRequestTimeoutMs: timeoutMsFlag } : {},
5097
5393
  signal: controller2.signal,
5098
- onCycle: json ? void 0 : (cycle, event) => {
5394
+ onCycle: (cycle, event) => {
5099
5395
  if (event.error !== void 0) {
5100
5396
  console.error(`converge watch: cycle ${cycle} failed: ${String(event.error)}`);
5101
5397
  return;
5102
5398
  }
5103
5399
  const result = event.result;
5104
5400
  if (!result) return;
5105
- const transfers = result.transfers;
5106
- console.log(
5107
- `converge watch: cycle ${cycle} status=${result.status} pulled=${transfers.pulled} pushed=${transfers.pushed} conflicts=${transfers.conflictsResolved} failed=${transfers.failed}`
5108
- );
5401
+ for (const exclusion of result.excludedNamespaces ?? []) {
5402
+ console.error(formatConvergeExclusionLine(cycle, exclusion));
5403
+ }
5404
+ if (json) return;
5405
+ console.log(formatConvergeWatchCycleLine(cycle, result));
5109
5406
  }
5110
5407
  });
5111
5408
  if (json) {
5112
5409
  console.log(JSON.stringify(outcome, null, 2));
5113
5410
  } else {
5114
5411
  console.log(
5115
- `converge watch stopped after ${outcome.cycles} cycle(s): ${outcome.convergedCycles} converged, ${outcome.appliedCycles} applied, ${outcome.failedCycles} failed (last: ${outcome.lastStatus}).`
5412
+ `converge watch stopped after ${outcome.cycles} cycle(s): ${outcome.convergedCycles} converged, ${outcome.appliedCycles} applied, ${outcome.failedCycles} failed (last: ${outcome.lastStatus}, excluded: ${outcome.lastExcludedNamespaces.length}).`
5116
5413
  );
5117
5414
  }
5118
5415
  } finally {
@@ -5145,6 +5442,10 @@ Subcommands:
5145
5442
  signal: controller.signal,
5146
5443
  onProgress
5147
5444
  });
5445
+ for (const exclusion of plan.excludedNamespaces ?? []) {
5446
+ process.stderr.write(`${formatConvergeExclusionLine(void 0, exclusion)}
5447
+ `);
5448
+ }
5148
5449
  if (json) {
5149
5450
  console.log(JSON.stringify(plan, null, 2));
5150
5451
  } else {
@@ -5162,6 +5463,10 @@ Subcommands:
5162
5463
  signal: controller.signal,
5163
5464
  onProgress
5164
5465
  });
5466
+ for (const exclusion of result.excludedNamespaces ?? []) {
5467
+ process.stderr.write(`${formatConvergeExclusionLine(void 0, exclusion)}
5468
+ `);
5469
+ }
5165
5470
  if (json) {
5166
5471
  console.log(JSON.stringify(result, null, 2));
5167
5472
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "10.0.7",
3
+ "version": "10.0.9",
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.7",
30
- "@remnic/plugin-pi": "^10.0.7",
31
- "@remnic/core": "^10.0.7"
29
+ "@remnic/plugin-pi": "^10.0.9",
30
+ "@remnic/server": "^10.0.9",
31
+ "@remnic/core": "^10.0.9"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^10.0.7",
35
- "@remnic/plugin-openclaw": "^10.0.7",
36
- "@remnic/export-weclone": "^10.0.7",
37
- "@remnic/import-weclone": "^10.0.7",
38
- "@remnic/import-chatgpt": "^10.0.7",
39
- "@remnic/import-claude": "^10.0.7",
40
- "@remnic/import-gemini": "^10.0.7",
41
- "@remnic/import-lossless-claw": "^10.0.7",
42
- "@remnic/import-mem0": "^10.0.7",
43
- "@remnic/import-supermemory": "^10.0.7",
44
- "@remnic/import-okf": "^10.0.7",
45
- "@remnic/connector-limitless": "^10.0.7",
46
- "@remnic/connector-bee": "^10.0.7",
47
- "@remnic/connector-omi": "^10.0.7",
48
- "@remnic/capture-audio": "^10.0.7"
34
+ "@remnic/bench": "^10.0.9",
35
+ "@remnic/plugin-openclaw": "^10.0.9",
36
+ "@remnic/export-weclone": "^10.0.9",
37
+ "@remnic/import-weclone": "^10.0.9",
38
+ "@remnic/import-chatgpt": "^10.0.9",
39
+ "@remnic/import-claude": "^10.0.9",
40
+ "@remnic/import-gemini": "^10.0.9",
41
+ "@remnic/import-lossless-claw": "^10.0.9",
42
+ "@remnic/import-mem0": "^10.0.9",
43
+ "@remnic/import-supermemory": "^10.0.9",
44
+ "@remnic/import-okf": "^10.0.9",
45
+ "@remnic/connector-limitless": "^10.0.9",
46
+ "@remnic/connector-bee": "^10.0.9",
47
+ "@remnic/connector-omi": "^10.0.9",
48
+ "@remnic/capture-audio": "^10.0.9"
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.7",
101
- "@remnic/plugin-openclaw": "10.0.7",
102
- "@remnic/export-weclone": "10.0.7",
103
- "@remnic/import-weclone": "10.0.7",
104
- "@remnic/import-chatgpt": "10.0.7",
105
- "@remnic/import-lossless-claw": "10.0.7",
106
- "@remnic/import-claude": "10.0.7",
107
- "@remnic/import-mem0": "10.0.7",
108
- "@remnic/connector-limitless": "10.0.7",
109
- "@remnic/import-gemini": "10.0.7",
110
- "@remnic/import-supermemory": "10.0.7",
111
- "@remnic/connector-bee": "10.0.7",
112
- "@remnic/connector-omi": "10.0.7"
100
+ "@remnic/bench": "10.0.9",
101
+ "@remnic/plugin-openclaw": "10.0.9",
102
+ "@remnic/import-weclone": "10.0.9",
103
+ "@remnic/export-weclone": "10.0.9",
104
+ "@remnic/import-chatgpt": "10.0.9",
105
+ "@remnic/import-lossless-claw": "10.0.9",
106
+ "@remnic/import-mem0": "10.0.9",
107
+ "@remnic/import-gemini": "10.0.9",
108
+ "@remnic/import-supermemory": "10.0.9",
109
+ "@remnic/import-claude": "10.0.9",
110
+ "@remnic/connector-limitless": "10.0.9",
111
+ "@remnic/connector-bee": "10.0.9",
112
+ "@remnic/connector-omi": "10.0.9"
113
113
  },
114
114
  "license": "MIT",
115
115
  "repository": {