chat-recall 0.7.13 → 0.7.15
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/cli.js +371 -307
- package/dist/hostile-server.d.ts.map +1 -1
- package/dist/hostile-server.js +10 -1
- package/dist/hostile-server.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +278 -214
- package/dist/scan-worker.js +23 -24
- package/dist/sync-ledger.d.ts.map +1 -1
- package/dist/sync-ledger.js +112 -17
- package/dist/sync-ledger.js.map +1 -1
- package/dist/watch.js +320 -254
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -4259,7 +4259,7 @@ var require_pino = __commonJS({
|
|
|
4259
4259
|
messageKey,
|
|
4260
4260
|
errorKey,
|
|
4261
4261
|
nestedKey,
|
|
4262
|
-
base,
|
|
4262
|
+
base: base2,
|
|
4263
4263
|
name,
|
|
4264
4264
|
level: level2,
|
|
4265
4265
|
customLevels,
|
|
@@ -4298,11 +4298,11 @@ var require_pino = __commonJS({
|
|
|
4298
4298
|
[formattersSym]: allFormatters
|
|
4299
4299
|
});
|
|
4300
4300
|
let chindings = "";
|
|
4301
|
-
if (
|
|
4301
|
+
if (base2 !== null) {
|
|
4302
4302
|
if (name === void 0) {
|
|
4303
|
-
chindings = coreChindings(
|
|
4303
|
+
chindings = coreChindings(base2);
|
|
4304
4304
|
} else {
|
|
4305
|
-
chindings = coreChindings(Object.assign({},
|
|
4305
|
+
chindings = coreChindings(Object.assign({}, base2, { name }));
|
|
4306
4306
|
}
|
|
4307
4307
|
}
|
|
4308
4308
|
const time2 = timestamp instanceof Function ? timestamp : timestamp ? epochTime : nullTime;
|
|
@@ -5271,57 +5271,57 @@ function freshDefaults() {
|
|
|
5271
5271
|
team: defaultTeam()
|
|
5272
5272
|
};
|
|
5273
5273
|
}
|
|
5274
|
-
function mergeSources(
|
|
5274
|
+
function mergeSources(base2, partial) {
|
|
5275
5275
|
if (!partial)
|
|
5276
|
-
return
|
|
5276
|
+
return base2;
|
|
5277
5277
|
const legacyGemini = partial.enabled?.gemini;
|
|
5278
5278
|
const inherited = legacyGemini && typeof legacyGemini === "object" ? (({ sessions: _s, plans: _p, ...rest }) => rest)(legacyGemini) : {};
|
|
5279
5279
|
const enabled = {
|
|
5280
|
-
claude: { ...
|
|
5281
|
-
opencode: { ...
|
|
5282
|
-
codex: { ...
|
|
5283
|
-
agy: { ...
|
|
5284
|
-
cursor: { ...
|
|
5285
|
-
shared: { ...
|
|
5286
|
-
common: { ...
|
|
5280
|
+
claude: { ...base2.enabled.claude, ...partial.enabled?.claude ?? {} },
|
|
5281
|
+
opencode: { ...base2.enabled.opencode, ...partial.enabled?.opencode ?? {} },
|
|
5282
|
+
codex: { ...base2.enabled.codex, ...partial.enabled?.codex ?? {} },
|
|
5283
|
+
agy: { ...base2.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
|
|
5284
|
+
cursor: { ...base2.enabled.cursor, ...partial.enabled?.cursor ?? {} },
|
|
5285
|
+
shared: { ...base2.enabled.shared, ...partial.enabled?.shared ?? {} },
|
|
5286
|
+
common: { ...base2.enabled.common, ...partial.enabled?.common ?? {} }
|
|
5287
5287
|
};
|
|
5288
5288
|
return {
|
|
5289
|
-
claudeHome: partial.claudeHome ??
|
|
5290
|
-
geminiHome: partial.geminiHome ??
|
|
5291
|
-
codexHome: partial.codexHome ??
|
|
5292
|
-
opencodeDbPath: partial.opencodeDbPath ??
|
|
5293
|
-
agyHome: partial.agyHome ??
|
|
5294
|
-
cursorHome: partial.cursorHome ??
|
|
5295
|
-
cursorIdeHome: partial.cursorIdeHome ??
|
|
5296
|
-
extraClaudeHomes: partial.extraClaudeHomes ??
|
|
5297
|
-
approvedHomes: partial.approvedHomes ??
|
|
5298
|
-
declinedHomes: partial.declinedHomes ??
|
|
5299
|
-
homesGrandfathered: partial.homesGrandfathered ??
|
|
5289
|
+
claudeHome: partial.claudeHome ?? base2.claudeHome,
|
|
5290
|
+
geminiHome: partial.geminiHome ?? base2.geminiHome,
|
|
5291
|
+
codexHome: partial.codexHome ?? base2.codexHome,
|
|
5292
|
+
opencodeDbPath: partial.opencodeDbPath ?? base2.opencodeDbPath,
|
|
5293
|
+
agyHome: partial.agyHome ?? base2.agyHome,
|
|
5294
|
+
cursorHome: partial.cursorHome ?? base2.cursorHome,
|
|
5295
|
+
cursorIdeHome: partial.cursorIdeHome ?? base2.cursorIdeHome,
|
|
5296
|
+
extraClaudeHomes: partial.extraClaudeHomes ?? base2.extraClaudeHomes,
|
|
5297
|
+
approvedHomes: partial.approvedHomes ?? base2.approvedHomes,
|
|
5298
|
+
declinedHomes: partial.declinedHomes ?? base2.declinedHomes,
|
|
5299
|
+
homesGrandfathered: partial.homesGrandfathered ?? base2.homesGrandfathered,
|
|
5300
5300
|
enabled
|
|
5301
5301
|
};
|
|
5302
5302
|
}
|
|
5303
|
-
function mergePrivacy(
|
|
5303
|
+
function mergePrivacy(base2, partial) {
|
|
5304
5304
|
if (!partial)
|
|
5305
|
-
return
|
|
5306
|
-
return { ...
|
|
5305
|
+
return base2;
|
|
5306
|
+
return { ...base2, ...partial };
|
|
5307
5307
|
}
|
|
5308
|
-
function mergeSync(
|
|
5308
|
+
function mergeSync(base2, partial) {
|
|
5309
5309
|
if (!partial)
|
|
5310
|
-
return
|
|
5310
|
+
return base2;
|
|
5311
5311
|
return {
|
|
5312
|
-
...
|
|
5312
|
+
...base2,
|
|
5313
5313
|
...partial,
|
|
5314
|
-
upload: { ...
|
|
5314
|
+
upload: { ...base2.upload, ...partial.upload ?? {} }
|
|
5315
5315
|
};
|
|
5316
5316
|
}
|
|
5317
|
-
function mergeTeam(
|
|
5317
|
+
function mergeTeam(base2, partial) {
|
|
5318
5318
|
if (!partial)
|
|
5319
|
-
return
|
|
5319
|
+
return base2;
|
|
5320
5320
|
return {
|
|
5321
|
-
...
|
|
5321
|
+
...base2,
|
|
5322
5322
|
...partial,
|
|
5323
|
-
publishAllowed: { ...
|
|
5324
|
-
vault: { ...
|
|
5323
|
+
publishAllowed: { ...base2.publishAllowed, ...partial.publishAllowed ?? {} },
|
|
5324
|
+
vault: { ...base2.vault, ...partial.vault ?? {} }
|
|
5325
5325
|
};
|
|
5326
5326
|
}
|
|
5327
5327
|
function settingsFilePath() {
|
|
@@ -5439,15 +5439,15 @@ function loadSettings() {
|
|
|
5439
5439
|
}
|
|
5440
5440
|
try {
|
|
5441
5441
|
const parsed = JSON.parse(readFileSync3(path2, "utf-8"));
|
|
5442
|
-
const
|
|
5442
|
+
const base2 = freshDefaults();
|
|
5443
5443
|
const result = {
|
|
5444
5444
|
v: SCHEMA_VERSION,
|
|
5445
|
-
embedding: { ...
|
|
5446
|
-
summary: { ...
|
|
5447
|
-
sources: mergeSources(
|
|
5448
|
-
privacy: mergePrivacy(
|
|
5449
|
-
sync: mergeSync(
|
|
5450
|
-
team: mergeTeam(
|
|
5445
|
+
embedding: { ...base2.embedding, ...parsed.embedding ?? {} },
|
|
5446
|
+
summary: { ...base2.summary, ...parsed.summary ?? {} },
|
|
5447
|
+
sources: mergeSources(base2.sources, parsed.sources),
|
|
5448
|
+
privacy: mergePrivacy(base2.privacy, parsed.privacy),
|
|
5449
|
+
sync: mergeSync(base2.sync, parsed.sync),
|
|
5450
|
+
team: mergeTeam(base2.team, parsed.team)
|
|
5451
5451
|
};
|
|
5452
5452
|
if (mtimeMs >= 0)
|
|
5453
5453
|
_settingsCache = { mtimeMs, size, value: structuredClone(result) };
|
|
@@ -5643,10 +5643,10 @@ function legacyAutoSyncedHomes() {
|
|
|
5643
5643
|
{ base: join6(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5644
5644
|
{ base: join6(home, ".cursor"), marker: "chats" }
|
|
5645
5645
|
];
|
|
5646
|
-
for (const { base, marker } of bases) {
|
|
5647
|
-
add(
|
|
5648
|
-
const parent = dirname5(
|
|
5649
|
-
const leaf =
|
|
5646
|
+
for (const { base: base2, marker } of bases) {
|
|
5647
|
+
add(base2, marker);
|
|
5648
|
+
const parent = dirname5(base2);
|
|
5649
|
+
const leaf = base2.slice(parent.length + 1);
|
|
5650
5650
|
try {
|
|
5651
5651
|
for (const e of readdirSync4(parent, { withFileTypes: true })) {
|
|
5652
5652
|
if (!e.isDirectory())
|
|
@@ -5827,17 +5827,17 @@ function cursorHomeDir() {
|
|
|
5827
5827
|
function cursorIdeHomeDir() {
|
|
5828
5828
|
return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join7(homedir3(), "Library", "Application Support", "Cursor") : join7(homedir3(), ".config", "Cursor"));
|
|
5829
5829
|
}
|
|
5830
|
-
function siblingHomes(
|
|
5830
|
+
function siblingHomes(base2, overridden, suffix) {
|
|
5831
5831
|
const roots = [];
|
|
5832
5832
|
const add = (home) => {
|
|
5833
5833
|
const root = suffix ? join7(home, suffix) : home;
|
|
5834
5834
|
if (existsSync5(root) && !roots.includes(root))
|
|
5835
5835
|
roots.push(root);
|
|
5836
5836
|
};
|
|
5837
|
-
add(
|
|
5837
|
+
add(base2);
|
|
5838
5838
|
if (!overridden) {
|
|
5839
|
-
const parent = dirname6(
|
|
5840
|
-
const leaf = basename3(
|
|
5839
|
+
const parent = dirname6(base2);
|
|
5840
|
+
const leaf = basename3(base2);
|
|
5841
5841
|
try {
|
|
5842
5842
|
for (const entry of readdirSync5(parent, { withFileTypes: true })) {
|
|
5843
5843
|
if (!entry.isDirectory())
|
|
@@ -11019,9 +11019,9 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
|
|
|
11019
11019
|
content.metadata.durationMs += obj.durationMs;
|
|
11020
11020
|
}
|
|
11021
11021
|
if (msgType === "file-history-snapshot") {
|
|
11022
|
-
const
|
|
11023
|
-
if (
|
|
11024
|
-
const backups =
|
|
11022
|
+
const snapshot2 = obj.snapshot;
|
|
11023
|
+
if (snapshot2 && typeof snapshot2 === "object") {
|
|
11024
|
+
const backups = snapshot2.trackedFileBackups;
|
|
11025
11025
|
if (backups && typeof backups === "object") {
|
|
11026
11026
|
for (const filePath2 of Object.keys(backups)) {
|
|
11027
11027
|
filesModified.add(filePath2);
|
|
@@ -12022,12 +12022,14 @@ function lineKey(line) {
|
|
|
12022
12022
|
try {
|
|
12023
12023
|
const o = JSON.parse(line);
|
|
12024
12024
|
if (typeof o?.uuid === "string" && o.uuid)
|
|
12025
|
-
return { key: `u:${o.uuid}`,
|
|
12026
|
-
|
|
12027
|
-
return { key: `m:${o.type}`, singleton: true };
|
|
12025
|
+
return { key: `u:${o.uuid}`, meta: false, json: true };
|
|
12026
|
+
return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
|
|
12028
12027
|
} catch {
|
|
12029
12028
|
}
|
|
12030
|
-
return { key: `h:${sha1(line)}`,
|
|
12029
|
+
return { key: `h:${sha1(line)}`, meta: false, json: false };
|
|
12030
|
+
}
|
|
12031
|
+
function isHalfWritten(line, currentLines) {
|
|
12032
|
+
return currentLines.some((c) => c.length > line.length && c.startsWith(line));
|
|
12031
12033
|
}
|
|
12032
12034
|
function mergeLineText(shadowText, currentText) {
|
|
12033
12035
|
const shadowLines = shadowText.split("\n").filter((l) => l.trim());
|
|
@@ -12035,27 +12037,24 @@ function mergeLineText(shadowText, currentText) {
|
|
|
12035
12037
|
const currentKeys = /* @__PURE__ */ new Set();
|
|
12036
12038
|
for (const l of currentLines)
|
|
12037
12039
|
currentKeys.add(lineKey(l).key);
|
|
12038
|
-
const
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
let recovered = 0;
|
|
12042
|
-
for (const k of shadowKeys)
|
|
12043
|
-
if (!currentKeys.has(k))
|
|
12044
|
-
recovered++;
|
|
12045
|
-
if (recovered === 0) {
|
|
12046
|
-
return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
|
|
12047
|
-
}
|
|
12048
|
-
const out = [];
|
|
12049
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
12040
|
+
const kept = [];
|
|
12041
|
+
const keptKeys = /* @__PURE__ */ new Set();
|
|
12042
|
+
const counted = /* @__PURE__ */ new Set();
|
|
12050
12043
|
for (const l of shadowLines) {
|
|
12051
|
-
const { key: key2,
|
|
12052
|
-
if (
|
|
12053
|
-
continue;
|
|
12054
|
-
if (emitted.has(key2))
|
|
12044
|
+
const { key: key2, meta, json } = lineKey(l);
|
|
12045
|
+
if (!json && isHalfWritten(l, currentLines))
|
|
12055
12046
|
continue;
|
|
12056
|
-
|
|
12057
|
-
|
|
12047
|
+
kept.push(l);
|
|
12048
|
+
keptKeys.add(key2);
|
|
12049
|
+
if (!meta && !currentKeys.has(key2))
|
|
12050
|
+
counted.add(key2);
|
|
12058
12051
|
}
|
|
12052
|
+
const recovered = counted.size;
|
|
12053
|
+
if (recovered === 0) {
|
|
12054
|
+
return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
|
|
12055
|
+
}
|
|
12056
|
+
const out = [...kept];
|
|
12057
|
+
const emitted = keptKeys;
|
|
12059
12058
|
for (const l of currentLines) {
|
|
12060
12059
|
const { key: key2 } = lineKey(l);
|
|
12061
12060
|
if (emitted.has(key2))
|
|
@@ -12185,14 +12184,14 @@ function shadowUncompressedBytes(tool, rawId) {
|
|
|
12185
12184
|
}
|
|
12186
12185
|
}
|
|
12187
12186
|
}
|
|
12188
|
-
var WHOLE_FILE_TOOLS,
|
|
12187
|
+
var WHOLE_FILE_TOOLS, STATE_META;
|
|
12189
12188
|
var init_shadow = __esm({
|
|
12190
12189
|
"../engine/dist/transcript/shadow.js"() {
|
|
12191
12190
|
"use strict";
|
|
12192
12191
|
init_paths();
|
|
12193
12192
|
init_raw();
|
|
12194
12193
|
WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
|
|
12195
|
-
|
|
12194
|
+
STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
|
|
12196
12195
|
}
|
|
12197
12196
|
});
|
|
12198
12197
|
|
|
@@ -13291,8 +13290,8 @@ function serverRulePackVersion() {
|
|
|
13291
13290
|
return serverPack ? serverPack.version || "unversioned" : null;
|
|
13292
13291
|
}
|
|
13293
13292
|
function activeRules() {
|
|
13294
|
-
const
|
|
13295
|
-
return serverPack && serverPack.rules.length > 0 ? [...
|
|
13293
|
+
const base2 = settingsView().rules;
|
|
13294
|
+
return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
|
|
13296
13295
|
}
|
|
13297
13296
|
function isRedactionEnabled() {
|
|
13298
13297
|
const env = isEnvRedactionRequested();
|
|
@@ -14155,17 +14154,17 @@ async function collectCode(opts) {
|
|
|
14155
14154
|
return r;
|
|
14156
14155
|
};
|
|
14157
14156
|
log6(`indexing ${ws} + analyses \u2026`);
|
|
14158
|
-
const
|
|
14157
|
+
const base2 = await runMcp(bin, ws, [
|
|
14159
14158
|
call(1, "index_workspace", { path: ws }),
|
|
14160
14159
|
call(2, "status"),
|
|
14161
14160
|
call(3, "get_tree"),
|
|
14162
14161
|
...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
|
|
14163
14162
|
]);
|
|
14164
|
-
const st = parseJson(
|
|
14165
|
-
const tree = parseJson(
|
|
14163
|
+
const st = parseJson(base2, 2, {});
|
|
14164
|
+
const tree = parseJson(base2, 3, []);
|
|
14166
14165
|
const A = {};
|
|
14167
14166
|
ANALYSES.forEach((a, i) => {
|
|
14168
|
-
A[a] = parseJson(
|
|
14167
|
+
A[a] = parseJson(base2, 10 + i, {});
|
|
14169
14168
|
});
|
|
14170
14169
|
const { health, security: sec, duplication: dup, clones, literal_scan: lit, coupling: coup, cycles: cyc, dead_code: dead, unwrap_audit: unwrap, test_coverage: coverage, architecture: arch, crossref, type_drift: typeDrift, db_schema: dbSchema, migration_parity: migParity, manifest_compliance: manifest } = A;
|
|
14171
14170
|
if (tree.length === 0) {
|
|
@@ -19453,38 +19452,100 @@ var init_extractor_version = __esm({
|
|
|
19453
19452
|
});
|
|
19454
19453
|
|
|
19455
19454
|
// src/sync-ledger.ts
|
|
19456
|
-
import { readFileSync as readFileSync34, writeFileSync as writeFileSync7, renameSync as renameSync5, mkdirSync as mkdirSync10 } from "node:fs";
|
|
19455
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync7, renameSync as renameSync5, mkdirSync as mkdirSync10, statSync as statSync40 } from "node:fs";
|
|
19457
19456
|
import { join as join46, dirname as dirname17 } from "node:path";
|
|
19458
19457
|
function toRow(entry) {
|
|
19459
19458
|
if (entry === void 0) return void 0;
|
|
19460
19459
|
return typeof entry === "number" ? { m: entry, v: 0 } : entry;
|
|
19461
19460
|
}
|
|
19462
|
-
function
|
|
19463
|
-
if (cache3) return cache3;
|
|
19461
|
+
function stampOf(path2) {
|
|
19464
19462
|
try {
|
|
19465
|
-
const
|
|
19466
|
-
|
|
19463
|
+
const st = statSync40(path2);
|
|
19464
|
+
return `${st.mtimeMs}:${st.size}`;
|
|
19467
19465
|
} catch {
|
|
19468
|
-
|
|
19466
|
+
return null;
|
|
19467
|
+
}
|
|
19468
|
+
}
|
|
19469
|
+
function readDisk(path2) {
|
|
19470
|
+
try {
|
|
19471
|
+
const parsed = JSON.parse(readFileSync34(path2, "utf-8"));
|
|
19472
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
19473
|
+
} catch {
|
|
19474
|
+
return {};
|
|
19475
|
+
}
|
|
19476
|
+
}
|
|
19477
|
+
function snapshot(data) {
|
|
19478
|
+
const out = /* @__PURE__ */ new Map();
|
|
19479
|
+
for (const [server, rows] of Object.entries(data)) {
|
|
19480
|
+
for (const [id, entry] of Object.entries(rows)) out.set(rowKey(server, id), JSON.stringify(entry));
|
|
19481
|
+
}
|
|
19482
|
+
return out;
|
|
19483
|
+
}
|
|
19484
|
+
function mergeOnto(disk) {
|
|
19485
|
+
const out = {};
|
|
19486
|
+
const put = (server, id, entry) => {
|
|
19487
|
+
(out[server] ??= {})[id] = entry;
|
|
19488
|
+
};
|
|
19489
|
+
const mine = cache3 ?? {};
|
|
19490
|
+
for (const [server, rows] of Object.entries(disk)) {
|
|
19491
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
19492
|
+
const k = rowKey(server, id);
|
|
19493
|
+
const own = mine[server]?.[id];
|
|
19494
|
+
const was = base.get(k);
|
|
19495
|
+
if (own === void 0) {
|
|
19496
|
+
if (was === void 0) put(server, id, entry);
|
|
19497
|
+
} else {
|
|
19498
|
+
put(server, id, JSON.stringify(own) === was ? entry : own);
|
|
19499
|
+
}
|
|
19500
|
+
}
|
|
19501
|
+
}
|
|
19502
|
+
for (const [server, rows] of Object.entries(mine)) {
|
|
19503
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
19504
|
+
if (disk[server]?.[id] !== void 0) continue;
|
|
19505
|
+
if (JSON.stringify(entry) !== base.get(rowKey(server, id))) put(server, id, entry);
|
|
19506
|
+
}
|
|
19469
19507
|
}
|
|
19508
|
+
return out;
|
|
19509
|
+
}
|
|
19510
|
+
function load() {
|
|
19511
|
+
const path2 = ledgerPath();
|
|
19512
|
+
const stamp = stampOf(path2);
|
|
19513
|
+
if (cache3 && stamp === diskStamp) return cache3;
|
|
19514
|
+
const disk = readDisk(path2);
|
|
19515
|
+
const merged = cache3 ? mergeOnto(disk) : disk;
|
|
19516
|
+
if (cache3) {
|
|
19517
|
+
for (const server of Object.keys(cache3)) if (!(server in merged)) delete cache3[server];
|
|
19518
|
+
for (const [server, rows] of Object.entries(merged)) {
|
|
19519
|
+
const target = cache3[server] ?? (cache3[server] = {});
|
|
19520
|
+
for (const id of Object.keys(target)) if (!(id in rows)) delete target[id];
|
|
19521
|
+
Object.assign(target, rows);
|
|
19522
|
+
}
|
|
19523
|
+
} else {
|
|
19524
|
+
cache3 = merged;
|
|
19525
|
+
}
|
|
19526
|
+
base = snapshot(disk);
|
|
19527
|
+
diskStamp = stamp;
|
|
19470
19528
|
return cache3;
|
|
19471
19529
|
}
|
|
19472
|
-
function persistNow(
|
|
19530
|
+
function persistNow() {
|
|
19473
19531
|
const path2 = ledgerPath();
|
|
19474
19532
|
mkdirSync10(dirname17(path2), { recursive: true });
|
|
19475
|
-
|
|
19476
|
-
|
|
19533
|
+
load();
|
|
19534
|
+
const tmp = `${path2}.tmp-${process.pid}`;
|
|
19535
|
+
writeFileSync7(tmp, JSON.stringify(cache3 ?? {}));
|
|
19477
19536
|
renameSync5(tmp, path2);
|
|
19537
|
+
base = snapshot(cache3 ?? {});
|
|
19538
|
+
diskStamp = stampOf(path2);
|
|
19478
19539
|
pendingWrite = false;
|
|
19479
19540
|
}
|
|
19480
|
-
function persist(
|
|
19541
|
+
function persist(_data) {
|
|
19481
19542
|
pendingWrite = true;
|
|
19482
19543
|
if (flushTimer) return;
|
|
19483
19544
|
flushTimer = setTimeout(() => {
|
|
19484
19545
|
flushTimer = null;
|
|
19485
19546
|
if (pendingWrite) {
|
|
19486
19547
|
try {
|
|
19487
|
-
persistNow(
|
|
19548
|
+
persistNow();
|
|
19488
19549
|
} catch {
|
|
19489
19550
|
}
|
|
19490
19551
|
}
|
|
@@ -19518,7 +19579,7 @@ function pruneLedgerTargets(configuredServers) {
|
|
|
19518
19579
|
}
|
|
19519
19580
|
const rows = names.reduce((n, s) => n + Object.keys(orphans[s]).length, 0);
|
|
19520
19581
|
console.error(`[sync] tidied local bookkeeping: ${rows} already-synced marker(s) for ${names.length} server(s) you no longer sync to (${names.join(", ")}) moved to ${side}. Nothing was uploaded there; the file is kept so logging back in re-ships nothing.`);
|
|
19521
|
-
persistNow(
|
|
19582
|
+
persistNow();
|
|
19522
19583
|
return names.length;
|
|
19523
19584
|
}
|
|
19524
19585
|
function flushLedger() {
|
|
@@ -19527,7 +19588,7 @@ function flushLedger() {
|
|
|
19527
19588
|
flushTimer = null;
|
|
19528
19589
|
}
|
|
19529
19590
|
if (!pendingWrite || !cache3) return;
|
|
19530
|
-
persistNow(
|
|
19591
|
+
persistNow();
|
|
19531
19592
|
}
|
|
19532
19593
|
function loadItemVersionsAll() {
|
|
19533
19594
|
if (itemVersionsCache) return itemVersionsCache;
|
|
@@ -19603,13 +19664,13 @@ function markSynced(server, rows) {
|
|
|
19603
19664
|
const o = (cursorFromCaller ? r.offset : void 0) ?? prevOS.o ?? 0;
|
|
19604
19665
|
const s = (cursorFromCaller ? r.size : void 0) ?? prevOS.s ?? 0;
|
|
19605
19666
|
const ev = extractorVersionForId(r.id);
|
|
19606
|
-
const
|
|
19667
|
+
const base2 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
|
|
19607
19668
|
if (o > 0 || s > 0) {
|
|
19608
|
-
|
|
19609
|
-
|
|
19669
|
+
base2.o = o;
|
|
19670
|
+
base2.s = s;
|
|
19610
19671
|
}
|
|
19611
|
-
if (r.hash !== void 0)
|
|
19612
|
-
forServer[r.id] =
|
|
19672
|
+
if (r.hash !== void 0) base2.h = r.hash;
|
|
19673
|
+
forServer[r.id] = base2;
|
|
19613
19674
|
}
|
|
19614
19675
|
persist(data);
|
|
19615
19676
|
}
|
|
@@ -19684,7 +19745,7 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
|
|
|
19684
19745
|
if (row.m >= Math.floor(mtime)) return "skip";
|
|
19685
19746
|
return "full";
|
|
19686
19747
|
}
|
|
19687
|
-
var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
19748
|
+
var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
19688
19749
|
var init_sync_ledger = __esm({
|
|
19689
19750
|
"src/sync-ledger.ts"() {
|
|
19690
19751
|
"use strict";
|
|
@@ -19692,6 +19753,9 @@ var init_sync_ledger = __esm({
|
|
|
19692
19753
|
init_extractor_version();
|
|
19693
19754
|
ledgerPath = () => join46(getDataDir(), "sync-ledger.json");
|
|
19694
19755
|
cache3 = null;
|
|
19756
|
+
base = /* @__PURE__ */ new Map();
|
|
19757
|
+
diskStamp = null;
|
|
19758
|
+
rowKey = (server, id) => `${server}\0${id}`;
|
|
19695
19759
|
LEDGER_FLUSH_MS = 2e3;
|
|
19696
19760
|
pendingWrite = false;
|
|
19697
19761
|
flushTimer = null;
|
|
@@ -19730,7 +19794,7 @@ var init_sync_fields = __esm({
|
|
|
19730
19794
|
});
|
|
19731
19795
|
|
|
19732
19796
|
// ../engine/dist/core/index-lock.js
|
|
19733
|
-
import { existsSync as existsSync43, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as
|
|
19797
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as statSync41, closeSync as closeSync9 } from "fs";
|
|
19734
19798
|
import { dirname as dirname18, join as join47 } from "path";
|
|
19735
19799
|
function acquireIndexLock(opts) {
|
|
19736
19800
|
const lockPath = lockFilePath(opts.file);
|
|
@@ -19780,7 +19844,7 @@ function lockFilePath(file = ".compact.lock") {
|
|
|
19780
19844
|
}
|
|
19781
19845
|
function stealStaleLock(lockPath, staleAfterMs) {
|
|
19782
19846
|
try {
|
|
19783
|
-
const s =
|
|
19847
|
+
const s = statSync41(lockPath);
|
|
19784
19848
|
const age = Date.now() - s.mtimeMs;
|
|
19785
19849
|
let payload;
|
|
19786
19850
|
try {
|
|
@@ -19846,7 +19910,7 @@ var init_http = __esm({
|
|
|
19846
19910
|
CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
19847
19911
|
} catch {
|
|
19848
19912
|
try {
|
|
19849
|
-
if (true) CLI_VERSION = "0.7.
|
|
19913
|
+
if (true) CLI_VERSION = "0.7.15";
|
|
19850
19914
|
} catch {
|
|
19851
19915
|
}
|
|
19852
19916
|
}
|
|
@@ -20344,7 +20408,7 @@ var init_telemetry = __esm({
|
|
|
20344
20408
|
init_sync_client();
|
|
20345
20409
|
cliVersion2 = () => {
|
|
20346
20410
|
try {
|
|
20347
|
-
return true ? "0.7.
|
|
20411
|
+
return true ? "0.7.15" : "dev";
|
|
20348
20412
|
} catch {
|
|
20349
20413
|
return "dev";
|
|
20350
20414
|
}
|
|
@@ -20367,11 +20431,11 @@ __export(server_probe_exports, {
|
|
|
20367
20431
|
function probeOk(r) {
|
|
20368
20432
|
return r.kind === "ok" && r.apiVersion >= 2;
|
|
20369
20433
|
}
|
|
20370
|
-
async function probeServer(
|
|
20434
|
+
async function probeServer(base2, opts = {}) {
|
|
20371
20435
|
const f = opts.fetchImpl || fetch;
|
|
20372
20436
|
let res;
|
|
20373
20437
|
try {
|
|
20374
|
-
res = await f(`${
|
|
20438
|
+
res = await f(`${base2.replace(/\/+$/, "")}/api/capabilities`, {
|
|
20375
20439
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
20376
20440
|
});
|
|
20377
20441
|
} catch (err) {
|
|
@@ -20408,34 +20472,34 @@ async function probeServer(base, opts = {}) {
|
|
|
20408
20472
|
raw: parsed
|
|
20409
20473
|
};
|
|
20410
20474
|
}
|
|
20411
|
-
function probeAdvice(r,
|
|
20475
|
+
function probeAdvice(r, base2) {
|
|
20412
20476
|
switch (r.kind) {
|
|
20413
20477
|
case "ok":
|
|
20414
20478
|
return r.apiVersion >= 2 ? `apiVersion ${r.apiVersion}${r.edition ? `, edition ${r.edition}` : ""}` : `apiVersion ${r.apiVersion} is too old for this CLI \u2014 upgrade the server`;
|
|
20415
20479
|
case "moved":
|
|
20416
|
-
return `HTTP ${r.status} \u2014 nothing is served at ${
|
|
20480
|
+
return `HTTP ${r.status} \u2014 nothing is served at ${base2}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
|
|
20417
20481
|
case "unauthorized":
|
|
20418
|
-
return `HTTP ${r.status} \u2014 ${
|
|
20482
|
+
return `HTTP ${r.status} \u2014 ${base2} rejected the credential. Re-login: \`chat-recall login ` + base2 + "`";
|
|
20419
20483
|
case "server_error":
|
|
20420
|
-
return `HTTP ${r.status} \u2014 ${
|
|
20484
|
+
return `HTTP ${r.status} \u2014 ${base2} is up but failing. Nothing to fix locally; check the server.`;
|
|
20421
20485
|
case "not_api":
|
|
20422
|
-
return `${
|
|
20486
|
+
return `${base2} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
|
|
20423
20487
|
case "unreachable":
|
|
20424
20488
|
switch (r.reason) {
|
|
20425
20489
|
case "dns":
|
|
20426
|
-
return `cannot resolve the host in ${
|
|
20490
|
+
return `cannot resolve the host in ${base2} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
|
|
20427
20491
|
case "refused":
|
|
20428
|
-
return `${
|
|
20492
|
+
return `${base2} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
|
|
20429
20493
|
case "tls":
|
|
20430
|
-
return `the TLS certificate at ${
|
|
20494
|
+
return `the TLS certificate at ${base2} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
|
|
20431
20495
|
case "timeout":
|
|
20432
|
-
return `${
|
|
20496
|
+
return `${base2} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
|
|
20433
20497
|
case "reset":
|
|
20434
|
-
return `the connection to ${
|
|
20498
|
+
return `the connection to ${base2} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
|
|
20435
20499
|
case "bad_port":
|
|
20436
|
-
return `${
|
|
20500
|
+
return `${base2} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
|
|
20437
20501
|
default:
|
|
20438
|
-
return `no HTTP response from ${
|
|
20502
|
+
return `no HTTP response from ${base2} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
|
|
20439
20503
|
}
|
|
20440
20504
|
}
|
|
20441
20505
|
}
|
|
@@ -20468,8 +20532,8 @@ function describeCause(err) {
|
|
|
20468
20532
|
const code = e.code;
|
|
20469
20533
|
if (typeof code === "string" && code && !parts.includes(code)) parts.push(code);
|
|
20470
20534
|
}
|
|
20471
|
-
const
|
|
20472
|
-
return parts.length ? `${
|
|
20535
|
+
const base2 = err instanceof Error ? err.message : String(err);
|
|
20536
|
+
return parts.length ? `${base2}: ${parts.join(" \u2192 ")}` : base2;
|
|
20473
20537
|
}
|
|
20474
20538
|
function describeUnreachable(err) {
|
|
20475
20539
|
const reason = classifyUnreachable(err);
|
|
@@ -20517,7 +20581,7 @@ __export(auto_update_exports, {
|
|
|
20517
20581
|
import { execSync as execSync4, execFileSync as execFileSync3 } from "node:child_process";
|
|
20518
20582
|
import { readFileSync as readFileSync37, existsSync as existsSync45 } from "node:fs";
|
|
20519
20583
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
20520
|
-
import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as
|
|
20584
|
+
import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync42, mkdirSync as mkdirSync12 } from "node:fs";
|
|
20521
20585
|
import { join as join48, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
|
|
20522
20586
|
import { tmpdir as tmpdir3, homedir as homedir17 } from "node:os";
|
|
20523
20587
|
import { createHash as createHash10 } from "node:crypto";
|
|
@@ -20549,12 +20613,12 @@ function installedVersion() {
|
|
|
20549
20613
|
return null;
|
|
20550
20614
|
}
|
|
20551
20615
|
}
|
|
20552
|
-
function planAutoUpdate(
|
|
20616
|
+
function planAutoUpdate(base2, caps, ownVersion2, flag) {
|
|
20553
20617
|
const rel = caps.cli;
|
|
20554
20618
|
if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
|
|
20555
20619
|
if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
|
|
20556
20620
|
if (compareVersions(rel.version, ownVersion2) <= 0) return { update: false, reason: `already current (own ${ownVersion2} >= server ${rel.version})` };
|
|
20557
|
-
return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(
|
|
20621
|
+
return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base2, rel.version), fallbackUrl: tarballUrl(base2), sha256: rel.sha256, from: ownVersion2, to: rel.version };
|
|
20558
20622
|
}
|
|
20559
20623
|
function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
|
|
20560
20624
|
const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
|
|
@@ -20570,7 +20634,7 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
|
|
|
20570
20634
|
if (!MKDTEMP_SUFFIX.test(name)) continue;
|
|
20571
20635
|
const dir = join48(root, name);
|
|
20572
20636
|
try {
|
|
20573
|
-
const st =
|
|
20637
|
+
const st = statSync42(dir);
|
|
20574
20638
|
if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
|
|
20575
20639
|
const inside = readdirSync31(dir);
|
|
20576
20640
|
if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
|
|
@@ -20734,20 +20798,20 @@ function clearUpdateFailures() {
|
|
|
20734
20798
|
function stopAutoUpdates() {
|
|
20735
20799
|
stopRequested = true;
|
|
20736
20800
|
}
|
|
20737
|
-
async function runAutoUpdate(
|
|
20801
|
+
async function runAutoUpdate(base2, authHeaders, ownVersion2, deps) {
|
|
20738
20802
|
let caps;
|
|
20739
20803
|
try {
|
|
20740
|
-
const res = await fetchWithTimeout(`${
|
|
20804
|
+
const res = await fetchWithTimeout(`${base2.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
|
|
20741
20805
|
if (!res.ok) return { updated: false, reason: `capabilities HTTP ${res.status}` };
|
|
20742
20806
|
caps = await res.json();
|
|
20743
20807
|
} catch (e) {
|
|
20744
20808
|
return { updated: false, reason: `capabilities unreachable: ${e instanceof Error ? e.message : e}` };
|
|
20745
20809
|
}
|
|
20746
20810
|
const effectiveOwn = newerOf(ownVersion2, installedVersion());
|
|
20747
|
-
const plan = planAutoUpdate(
|
|
20811
|
+
const plan = planAutoUpdate(base2, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20748
20812
|
if (!plan.update) {
|
|
20749
|
-
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(
|
|
20750
|
-
reportedStandingSkip.add(
|
|
20813
|
+
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base2)) {
|
|
20814
|
+
reportedStandingSkip.add(base2);
|
|
20751
20815
|
try {
|
|
20752
20816
|
const { reportClientEvent: reportClientEvent3 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
|
|
20753
20817
|
reportClientEvent3("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
|
|
@@ -20803,7 +20867,7 @@ var init_auto_update = __esm({
|
|
|
20803
20867
|
"use strict";
|
|
20804
20868
|
init_http();
|
|
20805
20869
|
init_index_lock();
|
|
20806
|
-
tarballUrl = (
|
|
20870
|
+
tarballUrl = (base2, version2) => `${base2.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
|
|
20807
20871
|
GIVE_UP_AFTER = 3;
|
|
20808
20872
|
reportedStandingSkip = /* @__PURE__ */ new Set();
|
|
20809
20873
|
STOPPING_REASON = "not installed: this process is shutting down";
|
|
@@ -20848,7 +20912,7 @@ __export(sync_client_exports, {
|
|
|
20848
20912
|
syncSessions: () => syncSessions
|
|
20849
20913
|
});
|
|
20850
20914
|
import { join as join49, dirname as dirname20, basename as basename22 } from "node:path";
|
|
20851
|
-
import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as
|
|
20915
|
+
import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync43 } from "node:fs";
|
|
20852
20916
|
import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
|
|
20853
20917
|
import { createHash as createHash11 } from "node:crypto";
|
|
20854
20918
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -20911,15 +20975,15 @@ function loadAllCredentials() {
|
|
|
20911
20975
|
return [];
|
|
20912
20976
|
}
|
|
20913
20977
|
async function fetchTenantSecurityConfig(cred) {
|
|
20914
|
-
const
|
|
20915
|
-
const cached = _securityConfigCache.get(
|
|
20978
|
+
const base2 = cred.serverUrl.replace(/\/+$/, "");
|
|
20979
|
+
const cached = _securityConfigCache.get(base2);
|
|
20916
20980
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20917
20981
|
const headers = { "content-type": "application/json" };
|
|
20918
20982
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
20919
20983
|
try {
|
|
20920
20984
|
const [rulesRes, settingsRes] = await Promise.all([
|
|
20921
|
-
fetchWithTimeout(`${
|
|
20922
|
-
fetchWithTimeout(`${
|
|
20985
|
+
fetchWithTimeout(`${base2}/api/secrets/rules`, { headers }),
|
|
20986
|
+
fetchWithTimeout(`${base2}/api/teams/security-config`, { headers }).catch(() => null)
|
|
20923
20987
|
]);
|
|
20924
20988
|
let tenantRules = [];
|
|
20925
20989
|
let rulePackVersion = null;
|
|
@@ -20944,18 +21008,18 @@ async function fetchTenantSecurityConfig(cred) {
|
|
|
20944
21008
|
const body2 = await settingsRes.json().catch(() => ({}));
|
|
20945
21009
|
if (typeof body2.verifySecrets === "boolean") verifySecrets = body2.verifySecrets;
|
|
20946
21010
|
if (typeof body2.telemetry === "boolean") {
|
|
20947
|
-
setTelemetryEligible(
|
|
21011
|
+
setTelemetryEligible(base2, body2.telemetry);
|
|
20948
21012
|
try {
|
|
20949
21013
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20950
21014
|
updateCollectorHealth({
|
|
20951
|
-
telemetryEligible: { ...prior, [
|
|
21015
|
+
telemetryEligible: { ...prior, [base2]: { allowed: body2.telemetry, at: Date.now() } }
|
|
20952
21016
|
});
|
|
20953
21017
|
} catch {
|
|
20954
21018
|
}
|
|
20955
21019
|
}
|
|
20956
21020
|
}
|
|
20957
21021
|
const config2 = { tenantRules, verifySecrets, rulePackVersion };
|
|
20958
|
-
_securityConfigCache.set(
|
|
21022
|
+
_securityConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
|
|
20959
21023
|
return config2;
|
|
20960
21024
|
} catch {
|
|
20961
21025
|
return { tenantRules: [], verifySecrets: true, rulePackVersion: null };
|
|
@@ -20974,18 +21038,18 @@ function readPersistedSyncConfigs() {
|
|
|
20974
21038
|
return {};
|
|
20975
21039
|
}
|
|
20976
21040
|
}
|
|
20977
|
-
function persistSyncConfig(
|
|
21041
|
+
function persistSyncConfig(base2, config2) {
|
|
20978
21042
|
try {
|
|
20979
21043
|
const all = readPersistedSyncConfigs();
|
|
20980
|
-
all[
|
|
21044
|
+
all[base2] = { fetchedAt: Date.now(), config: config2 };
|
|
20981
21045
|
mkdirSync13(dirname20(syncConfigCachePath()), { recursive: true });
|
|
20982
21046
|
writeFileSync10(syncConfigCachePath(), JSON.stringify(all, null, 2));
|
|
20983
21047
|
} catch {
|
|
20984
21048
|
}
|
|
20985
21049
|
}
|
|
20986
21050
|
async function fetchTenantSyncConfig(cred) {
|
|
20987
|
-
const
|
|
20988
|
-
const cached = _syncConfigCache.get(
|
|
21051
|
+
const base2 = cred.serverUrl.replace(/\/+$/, "");
|
|
21052
|
+
const cached = _syncConfigCache.get(base2);
|
|
20989
21053
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20990
21054
|
const headers = { "content-type": "application/json" };
|
|
20991
21055
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
@@ -20993,7 +21057,7 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20993
21057
|
try {
|
|
20994
21058
|
const sources2 = discoverSessionSources();
|
|
20995
21059
|
if (sources2.length > 0) {
|
|
20996
|
-
await fetchWithTimeout(`${
|
|
21060
|
+
await fetchWithTimeout(`${base2}/api/sync-config/sources`, {
|
|
20997
21061
|
method: "POST",
|
|
20998
21062
|
headers,
|
|
20999
21063
|
body: JSON.stringify({ device: deviceLabel(), sources: sources2 })
|
|
@@ -21001,12 +21065,12 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
21001
21065
|
}
|
|
21002
21066
|
} catch {
|
|
21003
21067
|
}
|
|
21004
|
-
const res = await fetchWithTimeout(`${
|
|
21068
|
+
const res = await fetchWithTimeout(`${base2}/api/sync-config`, { headers });
|
|
21005
21069
|
if (res.status === 404) {
|
|
21006
|
-
_syncConfigCache.set(
|
|
21070
|
+
_syncConfigCache.set(base2, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
|
|
21007
21071
|
return EMPTY_SYNC_CONFIG;
|
|
21008
21072
|
}
|
|
21009
|
-
if (!res.ok) return lastKnownSyncConfig(
|
|
21073
|
+
if (!res.ok) return lastKnownSyncConfig(base2, `HTTP ${res.status}`);
|
|
21010
21074
|
const body2 = await res.json().catch(() => ({}));
|
|
21011
21075
|
const config2 = {
|
|
21012
21076
|
excludeTools: Array.isArray(body2.excludeTools) ? body2.excludeTools.filter((t) => typeof t === "string") : [],
|
|
@@ -21043,22 +21107,22 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
21043
21107
|
if (decided > 0) console.error(`[sync] applied ${decided} folder decision(s) from the dashboard`);
|
|
21044
21108
|
} catch {
|
|
21045
21109
|
}
|
|
21046
|
-
_syncConfigCache.set(
|
|
21047
|
-
persistSyncConfig(
|
|
21110
|
+
_syncConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
|
|
21111
|
+
persistSyncConfig(base2, config2);
|
|
21048
21112
|
return config2;
|
|
21049
21113
|
} catch (err) {
|
|
21050
21114
|
if (isSyncRulesRefusal(err)) throw err;
|
|
21051
|
-
return lastKnownSyncConfig(
|
|
21115
|
+
return lastKnownSyncConfig(base2, err instanceof Error ? err.message : "request failed");
|
|
21052
21116
|
}
|
|
21053
21117
|
}
|
|
21054
|
-
function lastKnownSyncConfig(
|
|
21055
|
-
const remembered = _syncConfigCache.get(
|
|
21118
|
+
function lastKnownSyncConfig(base2, reason) {
|
|
21119
|
+
const remembered = _syncConfigCache.get(base2) ?? readPersistedSyncConfigs()[base2];
|
|
21056
21120
|
if (remembered) {
|
|
21057
21121
|
console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
|
|
21058
21122
|
return remembered.config;
|
|
21059
21123
|
}
|
|
21060
21124
|
const refusal = new Error(
|
|
21061
|
-
`Cannot read the server-side sync rules from ${
|
|
21125
|
+
`Cannot read the server-side sync rules from ${base2} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
|
|
21062
21126
|
);
|
|
21063
21127
|
refusal[SYNC_RULES_REFUSAL] = true;
|
|
21064
21128
|
throw refusal;
|
|
@@ -21179,25 +21243,25 @@ async function syncSessions(opts = {}) {
|
|
|
21179
21243
|
${errors.join("\n ")}`);
|
|
21180
21244
|
return { ...agg, perTarget };
|
|
21181
21245
|
}
|
|
21182
|
-
async function postFieldBatch(
|
|
21246
|
+
async function postFieldBatch(base2, token, rows) {
|
|
21183
21247
|
const headers = { "content-type": "application/json" };
|
|
21184
21248
|
if (token) headers.authorization = `Bearer ${token}`;
|
|
21185
21249
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21186
21250
|
for (let attempt = 0; ; attempt++) {
|
|
21187
|
-
const res = await fetchWithTimeout(`${
|
|
21251
|
+
const res = await fetchWithTimeout(`${base2}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
|
|
21188
21252
|
if (res.ok) return;
|
|
21189
21253
|
const retryable = res.status === 429 || res.status >= 500;
|
|
21190
|
-
if (!retryable || attempt >= 5) throw new Error(`${
|
|
21254
|
+
if (!retryable || attempt >= 5) throw new Error(`${base2}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
21191
21255
|
const ra = Number(res.headers.get("retry-after")) || 0;
|
|
21192
21256
|
await sleep(ra > 0 ? ra * 1e3 : [500, 1500, 4e3, 8e3, 15e3][Math.min(attempt, 4)]);
|
|
21193
21257
|
}
|
|
21194
21258
|
}
|
|
21195
21259
|
async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
21196
|
-
const
|
|
21197
|
-
const ledger = getSyncedRows(
|
|
21260
|
+
const base2 = cred.serverUrl.replace(/\/+$/, "");
|
|
21261
|
+
const ledger = getSyncedRows(base2);
|
|
21198
21262
|
let pushed = 0, absent = 0, scanned = 0;
|
|
21199
21263
|
for (const field of SYNC_FIELDS) {
|
|
21200
|
-
const needFull = !!opts.force || fieldNeedsFullPass(
|
|
21264
|
+
const needFull = !!opts.force || fieldNeedsFullPass(base2, field);
|
|
21201
21265
|
const candidates2 = needFull ? fullRefs() : opts.convRefs ?? fullRefs();
|
|
21202
21266
|
const send = [];
|
|
21203
21267
|
const mtimeById = /* @__PURE__ */ new Map();
|
|
@@ -21213,15 +21277,15 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
|
21213
21277
|
}
|
|
21214
21278
|
for (let i = 0; i < send.length; i += 200) {
|
|
21215
21279
|
const chunk = send.slice(i, i + 200);
|
|
21216
|
-
await postFieldBatch(
|
|
21217
|
-
markFieldCoverage(
|
|
21280
|
+
await postFieldBatch(base2, cred.token, chunk);
|
|
21281
|
+
markFieldCoverage(base2, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
|
|
21218
21282
|
pushed += chunk.length;
|
|
21219
21283
|
}
|
|
21220
21284
|
if (absentIds.length) {
|
|
21221
|
-
markFieldCoverage(
|
|
21285
|
+
markFieldCoverage(base2, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
|
|
21222
21286
|
absent += absentIds.length;
|
|
21223
21287
|
}
|
|
21224
|
-
if (needFull) markFieldFullPassDone(
|
|
21288
|
+
if (needFull) markFieldFullPassDone(base2, field);
|
|
21225
21289
|
}
|
|
21226
21290
|
return { pushed, absent, scanned };
|
|
21227
21291
|
}
|
|
@@ -21259,7 +21323,7 @@ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now(
|
|
|
21259
21323
|
if (!name.startsWith("cr-batchscan-")) continue;
|
|
21260
21324
|
const p = join49(dir, name);
|
|
21261
21325
|
try {
|
|
21262
|
-
if (now -
|
|
21326
|
+
if (now - statSync43(p).mtimeMs < maxAgeMs) continue;
|
|
21263
21327
|
rmSync6(p, { recursive: true, force: true });
|
|
21264
21328
|
reaped++;
|
|
21265
21329
|
} catch {
|
|
@@ -21338,25 +21402,25 @@ function serverIngestConcurrency(serverUrl) {
|
|
|
21338
21402
|
return advertisedIngestConc.get(serverUrl.replace(/\/$/, "")) ?? null;
|
|
21339
21403
|
}
|
|
21340
21404
|
async function verifyServerApi(serverUrl, minApi) {
|
|
21341
|
-
const
|
|
21342
|
-
const seen = capsOk.get(
|
|
21405
|
+
const base2 = serverUrl.replace(/\/$/, "");
|
|
21406
|
+
const seen = capsOk.get(base2);
|
|
21343
21407
|
if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
|
|
21344
21408
|
const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
21345
|
-
const probe = await probeServer2(
|
|
21409
|
+
const probe = await probeServer2(base2, {
|
|
21346
21410
|
fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
|
|
21347
21411
|
});
|
|
21348
|
-
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe,
|
|
21412
|
+
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base2));
|
|
21349
21413
|
const caps = probe.raw;
|
|
21350
21414
|
if ((caps.apiVersion ?? 0) < minApi) {
|
|
21351
21415
|
throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
|
|
21352
21416
|
}
|
|
21353
21417
|
const adv = caps.limits?.ingestConcurrencyPerTenant;
|
|
21354
21418
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
21355
|
-
advertisedIngestConc.set(
|
|
21419
|
+
advertisedIngestConc.set(base2, Math.floor(adv));
|
|
21356
21420
|
}
|
|
21357
|
-
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(
|
|
21358
|
-
else advertisedGzipBody.delete(
|
|
21359
|
-
capsOk.set(
|
|
21421
|
+
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base2);
|
|
21422
|
+
else advertisedGzipBody.delete(base2);
|
|
21423
|
+
capsOk.set(base2, Date.now());
|
|
21360
21424
|
return true;
|
|
21361
21425
|
}
|
|
21362
21426
|
async function syncToTarget(cred, opts = {}) {
|
|
@@ -21421,7 +21485,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21421
21485
|
const ac = new AbortController();
|
|
21422
21486
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
21423
21487
|
try {
|
|
21424
|
-
const res = await fetch(`${
|
|
21488
|
+
const res = await fetch(`${base2}/api/sync`, {
|
|
21425
21489
|
method: "POST",
|
|
21426
21490
|
// No token ⇒ no auth header: a no-auth local server (AUTH_PROVIDER=none)
|
|
21427
21491
|
// accepts it as the single 'default' tenant.
|
|
@@ -21437,26 +21501,26 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21437
21501
|
acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
|
|
21438
21502
|
const body3 = await res.json().catch(() => ({}));
|
|
21439
21503
|
if (typeof body3.telemetry === "boolean") {
|
|
21440
|
-
setTelemetryEligible(
|
|
21504
|
+
setTelemetryEligible(base2, body3.telemetry);
|
|
21441
21505
|
try {
|
|
21442
21506
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
21443
21507
|
updateCollectorHealth({
|
|
21444
|
-
telemetryEligible: { ...prior, [
|
|
21508
|
+
telemetryEligible: { ...prior, [base2]: { allowed: body3.telemetry, at: Date.now() } }
|
|
21445
21509
|
});
|
|
21446
21510
|
} catch {
|
|
21447
21511
|
}
|
|
21448
21512
|
}
|
|
21449
21513
|
const adv = body3.limits?.ingestConcurrencyPerTenant;
|
|
21450
21514
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
21451
|
-
setServerIngestConcurrency(
|
|
21515
|
+
setServerIngestConcurrency(base2, Math.floor(adv));
|
|
21452
21516
|
}
|
|
21453
21517
|
if (body3.cli && body3.cli.version) {
|
|
21454
21518
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
21455
21519
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
21456
|
-
const plan = planAutoUpdate2(
|
|
21520
|
+
const plan = planAutoUpdate2(base2, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
21457
21521
|
sweepStaleStaging2();
|
|
21458
21522
|
if (plan.update) {
|
|
21459
|
-
void runAutoUpdate2(
|
|
21523
|
+
void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
|
|
21460
21524
|
});
|
|
21461
21525
|
}
|
|
21462
21526
|
}
|
|
@@ -21465,7 +21529,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21465
21529
|
const retryable = res.status === 429 || res.status >= 500;
|
|
21466
21530
|
const text = await res.text().catch(() => "");
|
|
21467
21531
|
if (res.status === 401 || res.status === 403) {
|
|
21468
|
-
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${
|
|
21532
|
+
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base2}`);
|
|
21469
21533
|
}
|
|
21470
21534
|
if (res.status === 402) {
|
|
21471
21535
|
let detail = text;
|
|
@@ -21486,7 +21550,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21486
21550
|
const retryAfterS = Number(res.headers.get("retry-after"));
|
|
21487
21551
|
const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
|
|
21488
21552
|
const why = res.status === 524 ? ` \u2014 the proxy cut the request at its time limit; the batch is too big for one request` : "";
|
|
21489
|
-
console.error(`[sync] HTTP ${res.status} from ${
|
|
21553
|
+
console.error(`[sync] HTTP ${res.status} from ${base2}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
|
|
21490
21554
|
await sleep(waitMs);
|
|
21491
21555
|
continue;
|
|
21492
21556
|
} catch (err) {
|
|
@@ -21554,11 +21618,11 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21554
21618
|
}
|
|
21555
21619
|
};
|
|
21556
21620
|
};
|
|
21557
|
-
const
|
|
21558
|
-
const ledger = opts.useLedger ? getSyncedRows(
|
|
21621
|
+
const base2 = cred.serverUrl.replace(/\/$/, "");
|
|
21622
|
+
const ledger = opts.useLedger ? getSyncedRows(base2) : null;
|
|
21559
21623
|
const convBatch = makeBatcher("conversations", 25, false, (rows) => {
|
|
21560
21624
|
try {
|
|
21561
|
-
markSynced(
|
|
21625
|
+
markSynced(base2, rows.map((r) => {
|
|
21562
21626
|
const off = typeof r.from_offset === "number" ? r.from_offset : 0;
|
|
21563
21627
|
const hash = shippedHash.get(r.session_id);
|
|
21564
21628
|
return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true } : { id: r.session_id, mtime: r.mtime, hash };
|
|
@@ -21573,7 +21637,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21573
21637
|
const ac = new AbortController();
|
|
21574
21638
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
21575
21639
|
try {
|
|
21576
|
-
const res = await fetch(`${
|
|
21640
|
+
const res = await fetch(`${base2}/api/sync`, {
|
|
21577
21641
|
method: "POST",
|
|
21578
21642
|
headers,
|
|
21579
21643
|
body: JSON.stringify({ conversations: convs }),
|
|
@@ -21584,10 +21648,10 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21584
21648
|
if (body2.cli && body2.cli.version) {
|
|
21585
21649
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
21586
21650
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
21587
|
-
const plan = planAutoUpdate2(
|
|
21651
|
+
const plan = planAutoUpdate2(base2, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
21588
21652
|
sweepStaleStaging2();
|
|
21589
21653
|
if (plan.update) {
|
|
21590
|
-
void runAutoUpdate2(
|
|
21654
|
+
void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
|
|
21591
21655
|
});
|
|
21592
21656
|
}
|
|
21593
21657
|
}
|
|
@@ -21595,7 +21659,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21595
21659
|
const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
|
|
21596
21660
|
if (acked.length > 0) {
|
|
21597
21661
|
try {
|
|
21598
|
-
markSynced(
|
|
21662
|
+
markSynced(base2, acked.map((c) => ({
|
|
21599
21663
|
id: c.session_id,
|
|
21600
21664
|
mtime: c.mtime,
|
|
21601
21665
|
offset: c.from_offset,
|
|
@@ -21619,7 +21683,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21619
21683
|
const ac = new AbortController();
|
|
21620
21684
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
21621
21685
|
try {
|
|
21622
|
-
const res = await fetch(`${
|
|
21686
|
+
const res = await fetch(`${base2}/api/sync`, {
|
|
21623
21687
|
method: "POST",
|
|
21624
21688
|
headers,
|
|
21625
21689
|
body: JSON.stringify({ conversations: [conv] }),
|
|
@@ -21632,11 +21696,11 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21632
21696
|
const mtime = conv.mtime;
|
|
21633
21697
|
try {
|
|
21634
21698
|
if (!guarded) {
|
|
21635
|
-
markSynced(
|
|
21699
|
+
markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
|
|
21636
21700
|
} else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
|
|
21637
|
-
markSynced(
|
|
21701
|
+
markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
|
|
21638
21702
|
} else {
|
|
21639
|
-
markSynced(
|
|
21703
|
+
markSynced(base2, [{ id, mtime }]);
|
|
21640
21704
|
}
|
|
21641
21705
|
} catch {
|
|
21642
21706
|
}
|
|
@@ -21702,7 +21766,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21702
21766
|
const m = process.memoryUsage();
|
|
21703
21767
|
console.error(`[sync-trace] ${msg} \xB7 heap=${(m.heapUsed / 1048576).toFixed(0)}MB rss=${(m.rss / 1048576).toFixed(0)}MB`);
|
|
21704
21768
|
} : null;
|
|
21705
|
-
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${
|
|
21769
|
+
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base2}`);
|
|
21706
21770
|
if (upload.sessionMeta) {
|
|
21707
21771
|
try {
|
|
21708
21772
|
await reconcileFieldsForTarget(
|
|
@@ -21717,7 +21781,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21717
21781
|
{ convRefs: slice }
|
|
21718
21782
|
);
|
|
21719
21783
|
} catch (e) {
|
|
21720
|
-
console.error(`[sync] field reconcile (${
|
|
21784
|
+
console.error(`[sync] field reconcile (${base2}): ${e instanceof Error ? e.message : e}`);
|
|
21721
21785
|
}
|
|
21722
21786
|
}
|
|
21723
21787
|
trace?.("field reconcile done");
|
|
@@ -21759,7 +21823,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21759
21823
|
}
|
|
21760
21824
|
trace?.("conversation walk start");
|
|
21761
21825
|
const walkStartedAt = Date.now();
|
|
21762
|
-
const firstWalk = (ledger ?? getSyncedRows(
|
|
21826
|
+
const firstWalk = (ledger ?? getSyncedRows(base2)).size === 0;
|
|
21763
21827
|
reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false, first: firstWalk });
|
|
21764
21828
|
walkProgressForUpload = { done: 0, total: slice.length, complete: false };
|
|
21765
21829
|
let traceN = 0;
|
|
@@ -21799,7 +21863,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21799
21863
|
continue;
|
|
21800
21864
|
} else {
|
|
21801
21865
|
try {
|
|
21802
|
-
markSynced(
|
|
21866
|
+
markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21803
21867
|
} catch {
|
|
21804
21868
|
}
|
|
21805
21869
|
skipped++;
|
|
@@ -21822,7 +21886,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21822
21886
|
const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
|
|
21823
21887
|
const priorCursor = ackRow?.s ?? 0;
|
|
21824
21888
|
const safeToAck = off > 0 && off <= priorCursor;
|
|
21825
|
-
markSynced(
|
|
21889
|
+
markSynced(base2, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
|
|
21826
21890
|
} catch {
|
|
21827
21891
|
}
|
|
21828
21892
|
}
|
|
@@ -21832,7 +21896,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21832
21896
|
if (!built) {
|
|
21833
21897
|
if (ledger) {
|
|
21834
21898
|
try {
|
|
21835
|
-
markSynced(
|
|
21899
|
+
markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21836
21900
|
} catch {
|
|
21837
21901
|
}
|
|
21838
21902
|
}
|
|
@@ -21889,7 +21953,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21889
21953
|
walked++;
|
|
21890
21954
|
if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
|
|
21891
21955
|
}
|
|
21892
|
-
const itemVersions = loadItemVersions(
|
|
21956
|
+
const itemVersions = loadItemVersions(base2);
|
|
21893
21957
|
const seenTools = /* @__PURE__ */ new Set();
|
|
21894
21958
|
const seenItemKeys = /* @__PURE__ */ new Map();
|
|
21895
21959
|
const registry = buildSourceRegistry();
|
|
@@ -21983,7 +22047,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21983
22047
|
await appendFlush();
|
|
21984
22048
|
for (const id of appendResults.fullResyncNeeded) {
|
|
21985
22049
|
try {
|
|
21986
|
-
markFullResync(
|
|
22050
|
+
markFullResync(base2, id);
|
|
21987
22051
|
} catch {
|
|
21988
22052
|
}
|
|
21989
22053
|
}
|
|
@@ -21996,7 +22060,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21996
22060
|
await findingsBatch.drain();
|
|
21997
22061
|
await drainInflight();
|
|
21998
22062
|
{
|
|
21999
|
-
const sentHashes = loadInventoryHashes(
|
|
22063
|
+
const sentHashes = loadInventoryHashes(base2);
|
|
22000
22064
|
const entries = [];
|
|
22001
22065
|
const hashes = {};
|
|
22002
22066
|
for (const [sourceType, ids] of inventory) {
|
|
@@ -22010,7 +22074,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
22010
22074
|
if (entries.length > 0) {
|
|
22011
22075
|
try {
|
|
22012
22076
|
await post({ inventory: entries });
|
|
22013
|
-
saveInventoryHashes(
|
|
22077
|
+
saveInventoryHashes(base2, hashes);
|
|
22014
22078
|
} catch (err) {
|
|
22015
22079
|
console.error(`[sync] toolkit inventory not sent (it is retried next sync): ${err instanceof Error ? err.message : err}`);
|
|
22016
22080
|
}
|
|
@@ -22021,7 +22085,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
22021
22085
|
const rec = {};
|
|
22022
22086
|
for (const t of seenTools) rec[t] = extractorVersionForTool(t);
|
|
22023
22087
|
for (const [k, v] of seenItemKeys) rec[k] = v;
|
|
22024
|
-
saveItemVersions(
|
|
22088
|
+
saveItemVersions(base2, rec);
|
|
22025
22089
|
} catch {
|
|
22026
22090
|
}
|
|
22027
22091
|
}
|
|
@@ -22034,9 +22098,9 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
22034
22098
|
}
|
|
22035
22099
|
if (tombstones.length > 0) {
|
|
22036
22100
|
await post({ tombstones });
|
|
22037
|
-
const data = getLedgerData(
|
|
22101
|
+
const data = getLedgerData(base2);
|
|
22038
22102
|
for (const t of tombstones) delete data[t.session_id];
|
|
22039
|
-
persistLedgerData(
|
|
22103
|
+
persistLedgerData(base2, data);
|
|
22040
22104
|
}
|
|
22041
22105
|
}
|
|
22042
22106
|
if (opts.prune) await post({ prune_empty_sessions: true });
|
|
@@ -22507,7 +22571,7 @@ var init_sync_client = __esm({
|
|
|
22507
22571
|
ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
22508
22572
|
} catch {
|
|
22509
22573
|
try {
|
|
22510
|
-
if (true) ownVersion = "0.7.
|
|
22574
|
+
if (true) ownVersion = "0.7.15";
|
|
22511
22575
|
} catch {
|
|
22512
22576
|
}
|
|
22513
22577
|
}
|
|
@@ -22666,9 +22730,9 @@ async function refreshUpdateCheck(force = false) {
|
|
|
22666
22730
|
const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
22667
22731
|
for (const cred of loadAllCredentials2()) {
|
|
22668
22732
|
if (!cred.serverUrl) continue;
|
|
22669
|
-
const
|
|
22733
|
+
const base2 = cred.serverUrl.replace(/\/+$/, "");
|
|
22670
22734
|
try {
|
|
22671
|
-
const res = await fetchWithTimeout(`${
|
|
22735
|
+
const res = await fetchWithTimeout(`${base2}/api/capabilities`, {
|
|
22672
22736
|
headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
|
|
22673
22737
|
}, 5e3);
|
|
22674
22738
|
if (!res.ok) continue;
|
|
@@ -23057,7 +23121,7 @@ __export(install_skills_exports, {
|
|
|
23057
23121
|
skillsSourceDir: () => skillsSourceDir,
|
|
23058
23122
|
uninstallSkills: () => uninstallSkills
|
|
23059
23123
|
});
|
|
23060
|
-
import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as
|
|
23124
|
+
import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync44, realpathSync as realpathSync4 } from "fs";
|
|
23061
23125
|
import { createHash as createHash12 } from "crypto";
|
|
23062
23126
|
import { join as join52, basename as basename23 } from "path";
|
|
23063
23127
|
function skillsSourceDir() {
|
|
@@ -23298,18 +23362,18 @@ function usableDir(dir) {
|
|
|
23298
23362
|
if (!isDir(dir)) {
|
|
23299
23363
|
const missing = [];
|
|
23300
23364
|
let cur = dir;
|
|
23301
|
-
let
|
|
23365
|
+
let base2 = false;
|
|
23302
23366
|
while (missing.length < MAX_MISSING_LEVELS) {
|
|
23303
23367
|
const parent = dirname(cur);
|
|
23304
23368
|
if (parent === cur) break;
|
|
23305
23369
|
missing.push(cur);
|
|
23306
23370
|
if (isDir(parent)) {
|
|
23307
|
-
|
|
23371
|
+
base2 = true;
|
|
23308
23372
|
break;
|
|
23309
23373
|
}
|
|
23310
23374
|
cur = parent;
|
|
23311
23375
|
}
|
|
23312
|
-
if (!
|
|
23376
|
+
if (!base2) return false;
|
|
23313
23377
|
for (let i = missing.length - 1; i >= 0; i--) {
|
|
23314
23378
|
try {
|
|
23315
23379
|
mkdirSync(missing[i]);
|
|
@@ -29498,7 +29562,7 @@ function installCrashGuards() {
|
|
|
29498
29562
|
reportClientEvent2("mcp_crash", { message: err.stack || err.message });
|
|
29499
29563
|
});
|
|
29500
29564
|
}
|
|
29501
|
-
var RELAY_VERSION = true ? "0.7.
|
|
29565
|
+
var RELAY_VERSION = true ? "0.7.15" : "0.0.0";
|
|
29502
29566
|
function daemonIdleSecs() {
|
|
29503
29567
|
const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
|
|
29504
29568
|
if (raw === void 0) return 900;
|