@remnic/cli 9.3.515 → 9.3.517
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.
- package/dist/index.js +111 -24
- package/package.json +22 -22
package/dist/index.js
CHANGED
|
@@ -6704,7 +6704,7 @@ function normalizeOfflineRemoteUrl(raw) {
|
|
|
6704
6704
|
return parsed.toString().replace(/\/$/, "");
|
|
6705
6705
|
}
|
|
6706
6706
|
function resolveOptionalOfflineRemoteUrl(args) {
|
|
6707
|
-
const raw = resolveRequiredValueFlag(args, "--remote-url") ?? process.env.REMNIC_OFFLINE_REMOTE_URL ?? process.env.ENGRAM_OFFLINE_REMOTE_URL;
|
|
6707
|
+
const raw = resolveRequiredValueFlag(args, "--remote-url") ?? resolveRequiredValueFlag(args, "--remote") ?? process.env.REMNIC_OFFLINE_REMOTE_URL ?? process.env.ENGRAM_OFFLINE_REMOTE_URL;
|
|
6708
6708
|
if (!raw || raw.trim().length === 0) return void 0;
|
|
6709
6709
|
return normalizeOfflineRemoteUrl(raw);
|
|
6710
6710
|
}
|
|
@@ -6762,6 +6762,12 @@ function offlineRequestTimeoutMs() {
|
|
|
6762
6762
|
process.env.REMNIC_OFFLINE_REQUEST_TIMEOUT_MS ?? process.env.ENGRAM_OFFLINE_REQUEST_TIMEOUT_MS
|
|
6763
6763
|
);
|
|
6764
6764
|
}
|
|
6765
|
+
function offlineSnapshotPostTimeoutMs() {
|
|
6766
|
+
return parseOfflineSyncRequestTimeoutMs(
|
|
6767
|
+
process.env.REMNIC_OFFLINE_SNAPSHOT_POST_TIMEOUT_MS ?? process.env.ENGRAM_OFFLINE_SNAPSHOT_POST_TIMEOUT_MS,
|
|
6768
|
+
Math.min(offlineRequestTimeoutMs(), 6e4)
|
|
6769
|
+
);
|
|
6770
|
+
}
|
|
6765
6771
|
function offlineFetchHeaders(token, initHeaders, defaultContentType) {
|
|
6766
6772
|
const headers = new Headers(initHeaders);
|
|
6767
6773
|
headers.set("authorization", `Bearer ${token}`);
|
|
@@ -6932,18 +6938,23 @@ async function fetchOfflineSnapshot(args) {
|
|
|
6932
6938
|
const postRequest = offlineSnapshotBasePostRequest(postBody);
|
|
6933
6939
|
if (postRequest) {
|
|
6934
6940
|
const postRequestUsesGzip = new Headers(postRequest.headers).get("content-encoding")?.toLowerCase() === "gzip";
|
|
6941
|
+
const postAbort = new AbortController();
|
|
6942
|
+
const postTimeout = setTimeout(() => postAbort.abort(), offlineSnapshotPostTimeoutMs());
|
|
6935
6943
|
try {
|
|
6936
6944
|
return await fetchOfflineJson(
|
|
6937
6945
|
offlineEndpoint(args.remoteUrl, "/remnic/v1/offline-sync/snapshot"),
|
|
6938
6946
|
args.token,
|
|
6939
6947
|
{
|
|
6940
6948
|
method: "POST",
|
|
6949
|
+
signal: postAbort.signal,
|
|
6941
6950
|
...postRequest
|
|
6942
6951
|
}
|
|
6943
6952
|
);
|
|
6944
6953
|
} catch (error) {
|
|
6945
6954
|
if (!isOfflineSnapshotPostFallbackError(error, { compressed: postRequestUsesGzip })) throw error;
|
|
6946
6955
|
tryStreamSnapshot = true;
|
|
6956
|
+
} finally {
|
|
6957
|
+
clearTimeout(postTimeout);
|
|
6947
6958
|
}
|
|
6948
6959
|
} else {
|
|
6949
6960
|
tryStreamSnapshot = true;
|
|
@@ -7005,6 +7016,9 @@ function offlineSnapshotBasePostRequest(body) {
|
|
|
7005
7016
|
function isOfflineSnapshotPostFallbackError(error, options = {}) {
|
|
7006
7017
|
const message = error instanceof Error ? error.message : String(error);
|
|
7007
7018
|
if (/offline-sync\/snapshot\b.* returned (404|405|413)\b/.test(message)) return true;
|
|
7019
|
+
if (/^offline sync request timed out after \d+ms: POST .*\/offline-sync\/snapshot\b/.test(message) || /^offline sync request failed before response: POST .*\/offline-sync\/snapshot\b/.test(message) || /^(This operation was aborted|The operation was aborted|AbortError)/i.test(message)) {
|
|
7020
|
+
return true;
|
|
7021
|
+
}
|
|
7008
7022
|
if (!options.compressed) return false;
|
|
7009
7023
|
return /offline-sync\/snapshot\b.* returned (400|415)\b/.test(message) && /\b(unsupported_content_encoding|invalid_gzip_body|invalid_json)\b/.test(message);
|
|
7010
7024
|
}
|
|
@@ -7040,6 +7054,8 @@ var OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES = Math.min(
|
|
|
7040
7054
|
);
|
|
7041
7055
|
var OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES = OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES;
|
|
7042
7056
|
var OFFLINE_SYNC_CHANGESET_RETRY_MAX = 1024;
|
|
7057
|
+
var OFFLINE_SYNC_CONTENT_MISSING_RETRY_MAX = 3;
|
|
7058
|
+
var OFFLINE_SYNC_CONTENT_MISSING_RETRY_DELAY_MS = 250;
|
|
7043
7059
|
var OfflineRemoteFileChangedError = class extends Error {
|
|
7044
7060
|
path;
|
|
7045
7061
|
constructor(path12) {
|
|
@@ -7521,10 +7537,53 @@ function isOfflineFilesUnsupportedError(error) {
|
|
|
7521
7537
|
const message = error instanceof Error ? error.message : String(error);
|
|
7522
7538
|
return /offline sync request failed: .* returned 404\b/.test(message);
|
|
7523
7539
|
}
|
|
7540
|
+
var OfflineMissingContentError = class extends Error {
|
|
7541
|
+
missing;
|
|
7542
|
+
constructor(missing) {
|
|
7543
|
+
const preview = missing.slice(0, 8).map((file) => file.path).join(", ");
|
|
7544
|
+
const suffix = missing.length > 8 ? `, ... +${missing.length - 8} more` : "";
|
|
7545
|
+
super(
|
|
7546
|
+
`remote offline content response omitted ${missing.length} changed file${missing.length === 1 ? "" : "s"}: ${preview}${suffix}; retry sync`
|
|
7547
|
+
);
|
|
7548
|
+
this.name = "OfflineMissingContentError";
|
|
7549
|
+
this.missing = missing;
|
|
7550
|
+
}
|
|
7551
|
+
};
|
|
7524
7552
|
function isMissingOfflineContentError(error) {
|
|
7553
|
+
if (error instanceof OfflineMissingContentError) return true;
|
|
7525
7554
|
const message = error instanceof Error ? error.message : String(error);
|
|
7526
7555
|
return /^missing decoded content for /.test(message);
|
|
7527
7556
|
}
|
|
7557
|
+
function formatMissingOfflineContentError(missing) {
|
|
7558
|
+
return new OfflineMissingContentError(missing);
|
|
7559
|
+
}
|
|
7560
|
+
function isOfflineMissingContentDeferrablePath(relPath) {
|
|
7561
|
+
const parts = relPath.split("/");
|
|
7562
|
+
return parts[0] === "profiling" || parts[0] === "namespaces" && parts[2] === "profiling";
|
|
7563
|
+
}
|
|
7564
|
+
function deferMissingOfflineContent(missing, deferredPaths) {
|
|
7565
|
+
if (!deferredPaths) return [...missing];
|
|
7566
|
+
const stillMissing = [];
|
|
7567
|
+
for (const file of missing) {
|
|
7568
|
+
if (isOfflineMissingContentDeferrablePath(file.path)) {
|
|
7569
|
+
deferredPaths.add(file.path);
|
|
7570
|
+
} else {
|
|
7571
|
+
stillMissing.push(file);
|
|
7572
|
+
}
|
|
7573
|
+
}
|
|
7574
|
+
return stillMissing;
|
|
7575
|
+
}
|
|
7576
|
+
function formatMissingDecodedContentError(missing) {
|
|
7577
|
+
const preview = missing.slice(0, 8).map((file) => file.path).join(", ");
|
|
7578
|
+
const suffix = missing.length > 8 ? `, ... +${missing.length - 8} more` : "";
|
|
7579
|
+
return new Error(
|
|
7580
|
+
`remote offline content response omitted ${missing.length} changed file${missing.length === 1 ? "" : "s"}: ${preview}${suffix}; retry sync`
|
|
7581
|
+
);
|
|
7582
|
+
}
|
|
7583
|
+
async function waitForMissingOfflineContentRetry(delayMs) {
|
|
7584
|
+
if (delayMs <= 0) return;
|
|
7585
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
7586
|
+
}
|
|
7528
7587
|
async function hydrateOfflineSnapshotContent(args) {
|
|
7529
7588
|
const snapshot = normalizeOfflineSyncSnapshot(args.snapshot);
|
|
7530
7589
|
const neededFiles = offlineSnapshotContentFilesForApply({
|
|
@@ -7538,26 +7597,47 @@ async function hydrateOfflineSnapshotContent(args) {
|
|
|
7538
7597
|
const expectedByPath = new Map(snapshot.files.map((file) => [file.path, file]));
|
|
7539
7598
|
const contentByPath = /* @__PURE__ */ new Map();
|
|
7540
7599
|
const updatedByPath = /* @__PURE__ */ new Map();
|
|
7600
|
+
const fetchFiles = args.fetchFiles ?? fetchOfflineFiles;
|
|
7601
|
+
const retryMax = args.missingContentRetryMax ?? OFFLINE_SYNC_CONTENT_MISSING_RETRY_MAX;
|
|
7602
|
+
const retryDelayMs = args.missingContentRetryDelayMs ?? OFFLINE_SYNC_CONTENT_MISSING_RETRY_DELAY_MS;
|
|
7603
|
+
if (!Number.isInteger(retryMax) || retryMax < 0) {
|
|
7604
|
+
throw new Error("offline sync missing content retry max must be an integer >= 0");
|
|
7605
|
+
}
|
|
7606
|
+
if (!Number.isInteger(retryDelayMs) || retryDelayMs < 0) {
|
|
7607
|
+
throw new Error("offline sync missing content retry delay must be an integer >= 0");
|
|
7608
|
+
}
|
|
7541
7609
|
try {
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7610
|
+
let pendingFiles = neededFiles;
|
|
7611
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
7612
|
+
for (const batch of chunkOfflineFileContentBatches(pendingFiles)) {
|
|
7613
|
+
const partial = await fetchFiles({
|
|
7614
|
+
remoteUrl: args.remoteUrl,
|
|
7615
|
+
token: args.token,
|
|
7616
|
+
namespace: args.namespace,
|
|
7617
|
+
includeTranscripts: args.includeTranscripts,
|
|
7618
|
+
paths: batch.map((file) => file.path)
|
|
7619
|
+
});
|
|
7620
|
+
for (const file of partial.files) {
|
|
7621
|
+
const expected = expectedByPath.get(file.path);
|
|
7622
|
+
if (!expected) continue;
|
|
7623
|
+
if (typeof file.contentBase64 !== "string") {
|
|
7624
|
+
throw new Error(`remote offline content response omitted contentBase64 for ${file.path}`);
|
|
7625
|
+
}
|
|
7626
|
+
if (file.sha256 !== expected.sha256 || file.bytes !== expected.bytes || file.mtimeMs !== expected.mtimeMs) {
|
|
7627
|
+
updatedByPath.set(file.path, file);
|
|
7628
|
+
}
|
|
7629
|
+
contentByPath.set(file.path, file.contentBase64);
|
|
7558
7630
|
}
|
|
7559
|
-
contentByPath.set(file.path, file.contentBase64);
|
|
7560
7631
|
}
|
|
7632
|
+
const missing2 = pendingFiles.filter((file) => !contentByPath.has(file.path));
|
|
7633
|
+
if (missing2.length === 0) break;
|
|
7634
|
+
if (attempt >= retryMax) {
|
|
7635
|
+
const stillMissing = deferMissingOfflineContent(missing2, args.missingContentDeferredPaths);
|
|
7636
|
+
if (stillMissing.length > 0) throw formatMissingOfflineContentError(stillMissing);
|
|
7637
|
+
break;
|
|
7638
|
+
}
|
|
7639
|
+
pendingFiles = missing2;
|
|
7640
|
+
await waitForMissingOfflineContentRetry(retryDelayMs);
|
|
7561
7641
|
}
|
|
7562
7642
|
} catch (error) {
|
|
7563
7643
|
if (!isOfflineFilesUnsupportedError(error)) throw error;
|
|
@@ -7569,10 +7649,10 @@ async function hydrateOfflineSnapshotContent(args) {
|
|
|
7569
7649
|
includeContent: true
|
|
7570
7650
|
});
|
|
7571
7651
|
}
|
|
7572
|
-
const missing = neededFiles.map((file) => file.path).filter((relPath) => !contentByPath.has(relPath));
|
|
7652
|
+
const missing = neededFiles.map((file) => file.path).filter((relPath) => !contentByPath.has(relPath) && !args.missingContentDeferredPaths?.has(relPath));
|
|
7573
7653
|
if (missing.length > 0) {
|
|
7574
|
-
throw
|
|
7575
|
-
|
|
7654
|
+
throw formatMissingDecodedContentError(
|
|
7655
|
+
neededFiles.filter((file) => missing.includes(file.path))
|
|
7576
7656
|
);
|
|
7577
7657
|
}
|
|
7578
7658
|
return {
|
|
@@ -8231,7 +8311,8 @@ async function runOfflineSyncOnce(options) {
|
|
|
8231
8311
|
snapshot: remoteSnapshotMetadata,
|
|
8232
8312
|
baseFiles,
|
|
8233
8313
|
currentFiles: applyCurrentSnapshot.files,
|
|
8234
|
-
deferredPaths: [...remoteDeferredPaths]
|
|
8314
|
+
deferredPaths: [...remoteDeferredPaths],
|
|
8315
|
+
missingContentDeferredPaths: remoteDeferredPaths
|
|
8235
8316
|
});
|
|
8236
8317
|
} catch (error) {
|
|
8237
8318
|
if (pushed || partialHydration.hydratedFiles.length > 0) {
|
|
@@ -8275,7 +8356,8 @@ async function runOfflineSyncOnce(options) {
|
|
|
8275
8356
|
snapshot: remoteSnapshotMetadata,
|
|
8276
8357
|
baseFiles,
|
|
8277
8358
|
currentFiles: applyCurrentSnapshot.files,
|
|
8278
|
-
deferredPaths: [...remoteDeferredPaths]
|
|
8359
|
+
deferredPaths: [...remoteDeferredPaths],
|
|
8360
|
+
missingContentDeferredPaths: remoteDeferredPaths
|
|
8279
8361
|
});
|
|
8280
8362
|
} catch (retryError) {
|
|
8281
8363
|
if (pushed || partialHydration.hydratedFiles.length > 0) {
|
|
@@ -8397,7 +8479,7 @@ async function cmdOffline(action, rest, json) {
|
|
|
8397
8479
|
console.log(`Usage: remnic offline <prepare|sync|status|watch> [options]
|
|
8398
8480
|
|
|
8399
8481
|
Options:
|
|
8400
|
-
--remote-url <url> Remote Remnic server URL, e.g. http://home:4242
|
|
8482
|
+
--remote-url <url> Remote Remnic server URL, e.g. http://home:4242 (--remote alias accepted)
|
|
8401
8483
|
--token <token> Bearer token for the remote server
|
|
8402
8484
|
--namespace <name> Namespace to sync
|
|
8403
8485
|
--memory-dir <dir> Local memory dir (defaults to resolved memoryDir)
|
|
@@ -11538,6 +11620,8 @@ export {
|
|
|
11538
11620
|
BENCHMARK_CATALOG,
|
|
11539
11621
|
OFFLINE_SYNC_APPLY_MAX_REQUEST_BYTES,
|
|
11540
11622
|
OFFLINE_SYNC_CHANGESET_RETRY_MAX,
|
|
11623
|
+
OFFLINE_SYNC_CONTENT_MISSING_RETRY_DELAY_MS,
|
|
11624
|
+
OFFLINE_SYNC_CONTENT_MISSING_RETRY_MAX,
|
|
11541
11625
|
OFFLINE_SYNC_DIRECT_HYDRATE_MIN_BYTES,
|
|
11542
11626
|
OFFLINE_SYNC_DIRECT_PUSH_MIN_BYTES,
|
|
11543
11627
|
OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES,
|
|
@@ -11560,6 +11644,8 @@ export {
|
|
|
11560
11644
|
formatOfflineRequestForError,
|
|
11561
11645
|
getBenchUsageText,
|
|
11562
11646
|
hasFlag,
|
|
11647
|
+
hydrateOfflineSnapshotContent,
|
|
11648
|
+
isOfflineMissingContentDeferrablePath,
|
|
11563
11649
|
isOfflineSnapshotPostFallbackError,
|
|
11564
11650
|
main,
|
|
11565
11651
|
offlinePartialHydrationForPaths,
|
|
@@ -11579,6 +11665,7 @@ export {
|
|
|
11579
11665
|
resolveConfigPath,
|
|
11580
11666
|
resolveFlag,
|
|
11581
11667
|
resolveMemoryDir,
|
|
11668
|
+
resolveOptionalOfflineRemoteUrl,
|
|
11582
11669
|
resolveSyncSourceDir,
|
|
11583
11670
|
runOfflineSyncOnce,
|
|
11584
11671
|
runTrainingExport,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/cli",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.517",
|
|
4
4
|
"description": "CLI for Remnic memory — init, query, doctor, daemon management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"yaml": "^2.4.2",
|
|
29
|
-
"@remnic/plugin-pi": "^9.3.
|
|
30
|
-
"@remnic/server": "^9.3.
|
|
31
|
-
"@remnic/core": "^9.3.
|
|
29
|
+
"@remnic/plugin-pi": "^9.3.517",
|
|
30
|
+
"@remnic/server": "^9.3.517",
|
|
31
|
+
"@remnic/core": "^9.3.517"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@remnic/bench": "^9.3.
|
|
35
|
-
"@remnic/export-weclone": "^9.3.
|
|
36
|
-
"@remnic/import-weclone": "^9.3.
|
|
37
|
-
"@remnic/import-chatgpt": "^9.3.
|
|
38
|
-
"@remnic/import-claude": "^9.3.
|
|
39
|
-
"@remnic/import-gemini": "^9.3.
|
|
40
|
-
"@remnic/import-lossless-claw": "^9.3.
|
|
41
|
-
"@remnic/import-mem0": "^9.3.
|
|
42
|
-
"@remnic/import-supermemory": "^9.3.
|
|
34
|
+
"@remnic/bench": "^9.3.517",
|
|
35
|
+
"@remnic/export-weclone": "^9.3.517",
|
|
36
|
+
"@remnic/import-weclone": "^9.3.517",
|
|
37
|
+
"@remnic/import-chatgpt": "^9.3.517",
|
|
38
|
+
"@remnic/import-claude": "^9.3.517",
|
|
39
|
+
"@remnic/import-gemini": "^9.3.517",
|
|
40
|
+
"@remnic/import-lossless-claw": "^9.3.517",
|
|
41
|
+
"@remnic/import-mem0": "^9.3.517",
|
|
42
|
+
"@remnic/import-supermemory": "^9.3.517"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"@remnic/bench": {
|
|
@@ -73,15 +73,15 @@
|
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"tsup": "^8.5.1",
|
|
75
75
|
"typescript": "^5.9.3",
|
|
76
|
-
"@remnic/
|
|
77
|
-
"@remnic/
|
|
78
|
-
"@remnic/import-weclone": "9.3.
|
|
79
|
-
"@remnic/import-
|
|
80
|
-
"@remnic/import-
|
|
81
|
-
"@remnic/import-
|
|
82
|
-
"@remnic/import-lossless-claw": "9.3.
|
|
83
|
-
"@remnic/import-
|
|
84
|
-
"@remnic/import-
|
|
76
|
+
"@remnic/export-weclone": "9.3.517",
|
|
77
|
+
"@remnic/bench": "9.3.517",
|
|
78
|
+
"@remnic/import-weclone": "9.3.517",
|
|
79
|
+
"@remnic/import-chatgpt": "9.3.517",
|
|
80
|
+
"@remnic/import-claude": "9.3.517",
|
|
81
|
+
"@remnic/import-gemini": "9.3.517",
|
|
82
|
+
"@remnic/import-lossless-claw": "9.3.517",
|
|
83
|
+
"@remnic/import-supermemory": "9.3.517",
|
|
84
|
+
"@remnic/import-mem0": "9.3.517"
|
|
85
85
|
},
|
|
86
86
|
"license": "MIT",
|
|
87
87
|
"repository": {
|