chat-recall 0.7.14 → 0.7.16
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 +424 -289
- 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 +331 -196
- package/dist/scan-worker.js +63 -3
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +17 -4
- package/dist/sync-client.js.map +1 -1
- package/dist/sync-ledger.d.ts +8 -1
- package/dist/sync-ledger.d.ts.map +1 -1
- package/dist/sync-ledger.js +121 -18
- package/dist/sync-ledger.js.map +1 -1
- package/dist/watch.js +373 -236
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4259,7 +4259,7 @@ var require_pino = __commonJS({
|
|
|
4259
4259
|
messageKey,
|
|
4260
4260
|
errorKey,
|
|
4261
4261
|
nestedKey,
|
|
4262
|
-
base:
|
|
4262
|
+
base: base4,
|
|
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 (base4 !== null) {
|
|
4302
4302
|
if (name === void 0) {
|
|
4303
|
-
chindings = coreChindings(
|
|
4303
|
+
chindings = coreChindings(base4);
|
|
4304
4304
|
} else {
|
|
4305
|
-
chindings = coreChindings(Object.assign({},
|
|
4305
|
+
chindings = coreChindings(Object.assign({}, base4, { name }));
|
|
4306
4306
|
}
|
|
4307
4307
|
}
|
|
4308
4308
|
const time2 = timestamp instanceof Function ? timestamp : timestamp ? epochTime : nullTime;
|
|
@@ -4834,57 +4834,57 @@ function freshDefaults() {
|
|
|
4834
4834
|
team: defaultTeam()
|
|
4835
4835
|
};
|
|
4836
4836
|
}
|
|
4837
|
-
function mergeSources(
|
|
4837
|
+
function mergeSources(base4, partial) {
|
|
4838
4838
|
if (!partial)
|
|
4839
|
-
return
|
|
4839
|
+
return base4;
|
|
4840
4840
|
const legacyGemini = partial.enabled?.gemini;
|
|
4841
4841
|
const inherited = legacyGemini && typeof legacyGemini === "object" ? (({ sessions: _s, plans: _p, ...rest }) => rest)(legacyGemini) : {};
|
|
4842
4842
|
const enabled = {
|
|
4843
|
-
claude: { ...
|
|
4844
|
-
opencode: { ...
|
|
4845
|
-
codex: { ...
|
|
4846
|
-
agy: { ...
|
|
4847
|
-
cursor: { ...
|
|
4848
|
-
shared: { ...
|
|
4849
|
-
common: { ...
|
|
4843
|
+
claude: { ...base4.enabled.claude, ...partial.enabled?.claude ?? {} },
|
|
4844
|
+
opencode: { ...base4.enabled.opencode, ...partial.enabled?.opencode ?? {} },
|
|
4845
|
+
codex: { ...base4.enabled.codex, ...partial.enabled?.codex ?? {} },
|
|
4846
|
+
agy: { ...base4.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
|
|
4847
|
+
cursor: { ...base4.enabled.cursor, ...partial.enabled?.cursor ?? {} },
|
|
4848
|
+
shared: { ...base4.enabled.shared, ...partial.enabled?.shared ?? {} },
|
|
4849
|
+
common: { ...base4.enabled.common, ...partial.enabled?.common ?? {} }
|
|
4850
4850
|
};
|
|
4851
4851
|
return {
|
|
4852
|
-
claudeHome: partial.claudeHome ??
|
|
4853
|
-
geminiHome: partial.geminiHome ??
|
|
4854
|
-
codexHome: partial.codexHome ??
|
|
4855
|
-
opencodeDbPath: partial.opencodeDbPath ??
|
|
4856
|
-
agyHome: partial.agyHome ??
|
|
4857
|
-
cursorHome: partial.cursorHome ??
|
|
4858
|
-
cursorIdeHome: partial.cursorIdeHome ??
|
|
4859
|
-
extraClaudeHomes: partial.extraClaudeHomes ??
|
|
4860
|
-
approvedHomes: partial.approvedHomes ??
|
|
4861
|
-
declinedHomes: partial.declinedHomes ??
|
|
4862
|
-
homesGrandfathered: partial.homesGrandfathered ??
|
|
4852
|
+
claudeHome: partial.claudeHome ?? base4.claudeHome,
|
|
4853
|
+
geminiHome: partial.geminiHome ?? base4.geminiHome,
|
|
4854
|
+
codexHome: partial.codexHome ?? base4.codexHome,
|
|
4855
|
+
opencodeDbPath: partial.opencodeDbPath ?? base4.opencodeDbPath,
|
|
4856
|
+
agyHome: partial.agyHome ?? base4.agyHome,
|
|
4857
|
+
cursorHome: partial.cursorHome ?? base4.cursorHome,
|
|
4858
|
+
cursorIdeHome: partial.cursorIdeHome ?? base4.cursorIdeHome,
|
|
4859
|
+
extraClaudeHomes: partial.extraClaudeHomes ?? base4.extraClaudeHomes,
|
|
4860
|
+
approvedHomes: partial.approvedHomes ?? base4.approvedHomes,
|
|
4861
|
+
declinedHomes: partial.declinedHomes ?? base4.declinedHomes,
|
|
4862
|
+
homesGrandfathered: partial.homesGrandfathered ?? base4.homesGrandfathered,
|
|
4863
4863
|
enabled
|
|
4864
4864
|
};
|
|
4865
4865
|
}
|
|
4866
|
-
function mergePrivacy(
|
|
4866
|
+
function mergePrivacy(base4, partial) {
|
|
4867
4867
|
if (!partial)
|
|
4868
|
-
return
|
|
4869
|
-
return { ...
|
|
4868
|
+
return base4;
|
|
4869
|
+
return { ...base4, ...partial };
|
|
4870
4870
|
}
|
|
4871
|
-
function mergeSync(
|
|
4871
|
+
function mergeSync(base4, partial) {
|
|
4872
4872
|
if (!partial)
|
|
4873
|
-
return
|
|
4873
|
+
return base4;
|
|
4874
4874
|
return {
|
|
4875
|
-
...
|
|
4875
|
+
...base4,
|
|
4876
4876
|
...partial,
|
|
4877
|
-
upload: { ...
|
|
4877
|
+
upload: { ...base4.upload, ...partial.upload ?? {} }
|
|
4878
4878
|
};
|
|
4879
4879
|
}
|
|
4880
|
-
function mergeTeam(
|
|
4880
|
+
function mergeTeam(base4, partial) {
|
|
4881
4881
|
if (!partial)
|
|
4882
|
-
return
|
|
4882
|
+
return base4;
|
|
4883
4883
|
return {
|
|
4884
|
-
...
|
|
4884
|
+
...base4,
|
|
4885
4885
|
...partial,
|
|
4886
|
-
publishAllowed: { ...
|
|
4887
|
-
vault: { ...
|
|
4886
|
+
publishAllowed: { ...base4.publishAllowed, ...partial.publishAllowed ?? {} },
|
|
4887
|
+
vault: { ...base4.vault, ...partial.vault ?? {} }
|
|
4888
4888
|
};
|
|
4889
4889
|
}
|
|
4890
4890
|
function settingsFilePath() {
|
|
@@ -5002,15 +5002,15 @@ function loadSettings() {
|
|
|
5002
5002
|
}
|
|
5003
5003
|
try {
|
|
5004
5004
|
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
5005
|
-
const
|
|
5005
|
+
const base4 = freshDefaults();
|
|
5006
5006
|
const result = {
|
|
5007
5007
|
v: SCHEMA_VERSION,
|
|
5008
|
-
embedding: { ...
|
|
5009
|
-
summary: { ...
|
|
5010
|
-
sources: mergeSources(
|
|
5011
|
-
privacy: mergePrivacy(
|
|
5012
|
-
sync: mergeSync(
|
|
5013
|
-
team: mergeTeam(
|
|
5008
|
+
embedding: { ...base4.embedding, ...parsed.embedding ?? {} },
|
|
5009
|
+
summary: { ...base4.summary, ...parsed.summary ?? {} },
|
|
5010
|
+
sources: mergeSources(base4.sources, parsed.sources),
|
|
5011
|
+
privacy: mergePrivacy(base4.privacy, parsed.privacy),
|
|
5012
|
+
sync: mergeSync(base4.sync, parsed.sync),
|
|
5013
|
+
team: mergeTeam(base4.team, parsed.team)
|
|
5014
5014
|
};
|
|
5015
5015
|
if (mtimeMs >= 0)
|
|
5016
5016
|
_settingsCache = { mtimeMs, size, value: structuredClone(result) };
|
|
@@ -5216,10 +5216,10 @@ function legacyAutoSyncedHomes() {
|
|
|
5216
5216
|
{ base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5217
5217
|
{ base: join3(home, ".cursor"), marker: "chats" }
|
|
5218
5218
|
];
|
|
5219
|
-
for (const { base:
|
|
5220
|
-
add(
|
|
5221
|
-
const parent = dirname3(
|
|
5222
|
-
const leaf =
|
|
5219
|
+
for (const { base: base4, marker } of bases) {
|
|
5220
|
+
add(base4, marker);
|
|
5221
|
+
const parent = dirname3(base4);
|
|
5222
|
+
const leaf = base4.slice(parent.length + 1);
|
|
5223
5223
|
try {
|
|
5224
5224
|
for (const e of readdirSync2(parent, { withFileTypes: true })) {
|
|
5225
5225
|
if (!e.isDirectory())
|
|
@@ -5417,17 +5417,17 @@ function cursorHomeDir() {
|
|
|
5417
5417
|
function cursorIdeHomeDir() {
|
|
5418
5418
|
return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
|
|
5419
5419
|
}
|
|
5420
|
-
function siblingHomes(
|
|
5420
|
+
function siblingHomes(base4, overridden, suffix) {
|
|
5421
5421
|
const roots = [];
|
|
5422
5422
|
const add = (home) => {
|
|
5423
5423
|
const root = suffix ? join4(home, suffix) : home;
|
|
5424
5424
|
if (existsSync4(root) && !roots.includes(root))
|
|
5425
5425
|
roots.push(root);
|
|
5426
5426
|
};
|
|
5427
|
-
add(
|
|
5427
|
+
add(base4);
|
|
5428
5428
|
if (!overridden) {
|
|
5429
|
-
const parent = dirname4(
|
|
5430
|
-
const leaf = basename(
|
|
5429
|
+
const parent = dirname4(base4);
|
|
5430
|
+
const leaf = basename(base4);
|
|
5431
5431
|
try {
|
|
5432
5432
|
for (const entry of readdirSync3(parent, { withFileTypes: true })) {
|
|
5433
5433
|
if (!entry.isDirectory())
|
|
@@ -5937,9 +5937,9 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
|
|
|
5937
5937
|
content.metadata.durationMs += obj.durationMs;
|
|
5938
5938
|
}
|
|
5939
5939
|
if (msgType === "file-history-snapshot") {
|
|
5940
|
-
const
|
|
5941
|
-
if (
|
|
5942
|
-
const backups =
|
|
5940
|
+
const snapshot2 = obj.snapshot;
|
|
5941
|
+
if (snapshot2 && typeof snapshot2 === "object") {
|
|
5942
|
+
const backups = snapshot2.trackedFileBackups;
|
|
5943
5943
|
if (backups && typeof backups === "object") {
|
|
5944
5944
|
for (const filePath2 of Object.keys(backups)) {
|
|
5945
5945
|
filesModified.add(filePath2);
|
|
@@ -8353,7 +8353,64 @@ var init_opencode = __esm({
|
|
|
8353
8353
|
});
|
|
8354
8354
|
|
|
8355
8355
|
// ../engine/dist/core/backends/tail-read.js
|
|
8356
|
-
import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync4 } from "node:fs";
|
|
8356
|
+
import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync4, fstatSync } from "node:fs";
|
|
8357
|
+
function readAt(fd, pos, len) {
|
|
8358
|
+
const buf = Buffer.alloc(len);
|
|
8359
|
+
let got = 0;
|
|
8360
|
+
while (got < len) {
|
|
8361
|
+
const n = readSync2(fd, buf, got, len - got, pos + got);
|
|
8362
|
+
if (n <= 0)
|
|
8363
|
+
break;
|
|
8364
|
+
got += n;
|
|
8365
|
+
}
|
|
8366
|
+
return buf.subarray(0, got);
|
|
8367
|
+
}
|
|
8368
|
+
function linesBeforeOffset(path, offset) {
|
|
8369
|
+
if (offset <= 0)
|
|
8370
|
+
return 0;
|
|
8371
|
+
let fd = null;
|
|
8372
|
+
try {
|
|
8373
|
+
fd = openSync2(path, "r");
|
|
8374
|
+
const ino = fstatSync(fd).ino;
|
|
8375
|
+
const known = lineCounts.get(path);
|
|
8376
|
+
let pos = 0;
|
|
8377
|
+
let lines = 0;
|
|
8378
|
+
if (known && known.ino === ino && known.offset <= offset) {
|
|
8379
|
+
const edgeStart2 = Math.max(0, known.offset - EDGE_BYTES);
|
|
8380
|
+
if (readAt(fd, edgeStart2, known.offset - edgeStart2).equals(known.edge)) {
|
|
8381
|
+
pos = known.offset;
|
|
8382
|
+
lines = known.lines;
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
|
|
8386
|
+
while (pos < offset) {
|
|
8387
|
+
const n = readSync2(fd, block, 0, Math.min(SKIP_SCAN_BYTES, offset - pos), pos);
|
|
8388
|
+
if (n <= 0)
|
|
8389
|
+
return void 0;
|
|
8390
|
+
for (let i = block.indexOf(10, 0); i >= 0 && i < n; i = block.indexOf(10, i + 1))
|
|
8391
|
+
lines++;
|
|
8392
|
+
pos += n;
|
|
8393
|
+
}
|
|
8394
|
+
const edgeStart = Math.max(0, offset - EDGE_BYTES);
|
|
8395
|
+
const edge = readAt(fd, edgeStart, offset - edgeStart);
|
|
8396
|
+
if (lineCounts.size >= LINE_COUNT_CACHE_MAX && !lineCounts.has(path)) {
|
|
8397
|
+
const oldest = lineCounts.keys().next().value;
|
|
8398
|
+
if (oldest !== void 0)
|
|
8399
|
+
lineCounts.delete(oldest);
|
|
8400
|
+
}
|
|
8401
|
+
lineCounts.set(path, { offset, lines, ino, edge });
|
|
8402
|
+
return lines;
|
|
8403
|
+
} catch {
|
|
8404
|
+
return void 0;
|
|
8405
|
+
} finally {
|
|
8406
|
+
if (fd !== null) {
|
|
8407
|
+
try {
|
|
8408
|
+
closeSync2(fd);
|
|
8409
|
+
} catch {
|
|
8410
|
+
}
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
}
|
|
8357
8414
|
function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
8358
8415
|
let size = 0;
|
|
8359
8416
|
try {
|
|
@@ -8381,7 +8438,7 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
|
8381
8438
|
const lastNl = slice.lastIndexOf(10);
|
|
8382
8439
|
if (lastNl >= 0) {
|
|
8383
8440
|
const text = slice.subarray(0, lastNl + 1).toString("utf-8");
|
|
8384
|
-
return { text, newOffset: offset + lastNl + 1 };
|
|
8441
|
+
return { text, newOffset: offset + lastNl + 1, baseLine: linesBeforeOffset(path, offset) };
|
|
8385
8442
|
}
|
|
8386
8443
|
if (offset + got >= size)
|
|
8387
8444
|
return { text: "", newOffset: offset };
|
|
@@ -8414,12 +8471,15 @@ function findNewline(fd, from, size) {
|
|
|
8414
8471
|
}
|
|
8415
8472
|
return -1;
|
|
8416
8473
|
}
|
|
8417
|
-
var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
|
|
8474
|
+
var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES, lineCounts, LINE_COUNT_CACHE_MAX, EDGE_BYTES;
|
|
8418
8475
|
var init_tail_read = __esm({
|
|
8419
8476
|
"../engine/dist/core/backends/tail-read.js"() {
|
|
8420
8477
|
"use strict";
|
|
8421
8478
|
TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
|
|
8422
8479
|
SKIP_SCAN_BYTES = 1024 * 1024;
|
|
8480
|
+
lineCounts = /* @__PURE__ */ new Map();
|
|
8481
|
+
LINE_COUNT_CACHE_MAX = 2048;
|
|
8482
|
+
EDGE_BYTES = 64;
|
|
8423
8483
|
}
|
|
8424
8484
|
});
|
|
8425
8485
|
|
|
@@ -12776,8 +12836,8 @@ function _clearServerRulePack() {
|
|
|
12776
12836
|
serverPack = null;
|
|
12777
12837
|
}
|
|
12778
12838
|
function activeRules() {
|
|
12779
|
-
const
|
|
12780
|
-
return serverPack && serverPack.rules.length > 0 ? [...
|
|
12839
|
+
const base4 = settingsView().rules;
|
|
12840
|
+
return serverPack && serverPack.rules.length > 0 ? [...base4, ...serverPack.rules] : base4;
|
|
12781
12841
|
}
|
|
12782
12842
|
function isRedactionEnabled() {
|
|
12783
12843
|
const env = isEnvRedactionRequested();
|
|
@@ -18535,7 +18595,7 @@ var init_extractor_version = __esm({
|
|
|
18535
18595
|
});
|
|
18536
18596
|
|
|
18537
18597
|
// src/sync-ledger.ts
|
|
18538
|
-
import { readFileSync as readFileSync29, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync6 } from "node:fs";
|
|
18598
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync6, statSync as statSync35 } from "node:fs";
|
|
18539
18599
|
import { join as join38, dirname as dirname15 } from "node:path";
|
|
18540
18600
|
function toRow(entry) {
|
|
18541
18601
|
if (entry === void 0) return void 0;
|
|
@@ -18544,32 +18604,94 @@ function toRow(entry) {
|
|
|
18544
18604
|
function ledgerFilePath() {
|
|
18545
18605
|
return ledgerPath();
|
|
18546
18606
|
}
|
|
18547
|
-
function
|
|
18548
|
-
|
|
18607
|
+
function stampOf(path) {
|
|
18608
|
+
try {
|
|
18609
|
+
const st = statSync35(path);
|
|
18610
|
+
return `${st.mtimeMs}:${st.size}`;
|
|
18611
|
+
} catch {
|
|
18612
|
+
return null;
|
|
18613
|
+
}
|
|
18614
|
+
}
|
|
18615
|
+
function readDisk(path) {
|
|
18549
18616
|
try {
|
|
18550
|
-
const parsed = JSON.parse(readFileSync29(
|
|
18551
|
-
|
|
18617
|
+
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
18618
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
18552
18619
|
} catch {
|
|
18553
|
-
|
|
18620
|
+
return {};
|
|
18621
|
+
}
|
|
18622
|
+
}
|
|
18623
|
+
function snapshot(data) {
|
|
18624
|
+
const out = /* @__PURE__ */ new Map();
|
|
18625
|
+
for (const [server, rows] of Object.entries(data)) {
|
|
18626
|
+
for (const [id, entry] of Object.entries(rows)) out.set(rowKey(server, id), JSON.stringify(entry));
|
|
18627
|
+
}
|
|
18628
|
+
return out;
|
|
18629
|
+
}
|
|
18630
|
+
function mergeOnto(disk) {
|
|
18631
|
+
const out = {};
|
|
18632
|
+
const put = (server, id, entry) => {
|
|
18633
|
+
(out[server] ??= {})[id] = entry;
|
|
18634
|
+
};
|
|
18635
|
+
const mine = cache3 ?? {};
|
|
18636
|
+
for (const [server, rows] of Object.entries(disk)) {
|
|
18637
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
18638
|
+
const k = rowKey(server, id);
|
|
18639
|
+
const own = mine[server]?.[id];
|
|
18640
|
+
const was = base.get(k);
|
|
18641
|
+
if (own === void 0) {
|
|
18642
|
+
if (was === void 0) put(server, id, entry);
|
|
18643
|
+
} else {
|
|
18644
|
+
put(server, id, JSON.stringify(own) === was ? entry : own);
|
|
18645
|
+
}
|
|
18646
|
+
}
|
|
18554
18647
|
}
|
|
18648
|
+
for (const [server, rows] of Object.entries(mine)) {
|
|
18649
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
18650
|
+
if (disk[server]?.[id] !== void 0) continue;
|
|
18651
|
+
if (JSON.stringify(entry) !== base.get(rowKey(server, id))) put(server, id, entry);
|
|
18652
|
+
}
|
|
18653
|
+
}
|
|
18654
|
+
return out;
|
|
18655
|
+
}
|
|
18656
|
+
function load() {
|
|
18657
|
+
const path = ledgerPath();
|
|
18658
|
+
const stamp = stampOf(path);
|
|
18659
|
+
if (cache3 && stamp === diskStamp) return cache3;
|
|
18660
|
+
const disk = readDisk(path);
|
|
18661
|
+
const merged = cache3 ? mergeOnto(disk) : disk;
|
|
18662
|
+
if (cache3) {
|
|
18663
|
+
for (const server of Object.keys(cache3)) if (!(server in merged)) delete cache3[server];
|
|
18664
|
+
for (const [server, rows] of Object.entries(merged)) {
|
|
18665
|
+
const target = cache3[server] ?? (cache3[server] = {});
|
|
18666
|
+
for (const id of Object.keys(target)) if (!(id in rows)) delete target[id];
|
|
18667
|
+
Object.assign(target, rows);
|
|
18668
|
+
}
|
|
18669
|
+
} else {
|
|
18670
|
+
cache3 = merged;
|
|
18671
|
+
}
|
|
18672
|
+
base = snapshot(disk);
|
|
18673
|
+
diskStamp = stamp;
|
|
18555
18674
|
return cache3;
|
|
18556
18675
|
}
|
|
18557
|
-
function persistNow(
|
|
18676
|
+
function persistNow() {
|
|
18558
18677
|
const path = ledgerPath();
|
|
18559
18678
|
mkdirSync6(dirname15(path), { recursive: true });
|
|
18560
|
-
|
|
18561
|
-
|
|
18679
|
+
load();
|
|
18680
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
18681
|
+
writeFileSync5(tmp, JSON.stringify(cache3 ?? {}));
|
|
18562
18682
|
renameSync4(tmp, path);
|
|
18683
|
+
base = snapshot(cache3 ?? {});
|
|
18684
|
+
diskStamp = stampOf(path);
|
|
18563
18685
|
pendingWrite = false;
|
|
18564
18686
|
}
|
|
18565
|
-
function persist(
|
|
18687
|
+
function persist(_data) {
|
|
18566
18688
|
pendingWrite = true;
|
|
18567
18689
|
if (flushTimer) return;
|
|
18568
18690
|
flushTimer = setTimeout(() => {
|
|
18569
18691
|
flushTimer = null;
|
|
18570
18692
|
if (pendingWrite) {
|
|
18571
18693
|
try {
|
|
18572
|
-
persistNow(
|
|
18694
|
+
persistNow();
|
|
18573
18695
|
} catch {
|
|
18574
18696
|
}
|
|
18575
18697
|
}
|
|
@@ -18603,7 +18725,7 @@ function pruneLedgerTargets(configuredServers) {
|
|
|
18603
18725
|
}
|
|
18604
18726
|
const rows = names.reduce((n, s) => n + Object.keys(orphans[s]).length, 0);
|
|
18605
18727
|
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.`);
|
|
18606
|
-
persistNow(
|
|
18728
|
+
persistNow();
|
|
18607
18729
|
return names.length;
|
|
18608
18730
|
}
|
|
18609
18731
|
function flushLedger() {
|
|
@@ -18612,7 +18734,7 @@ function flushLedger() {
|
|
|
18612
18734
|
flushTimer = null;
|
|
18613
18735
|
}
|
|
18614
18736
|
if (!pendingWrite || !cache3) return;
|
|
18615
|
-
persistNow(
|
|
18737
|
+
persistNow();
|
|
18616
18738
|
}
|
|
18617
18739
|
function loadItemVersionsAll() {
|
|
18618
18740
|
if (itemVersionsCache) return itemVersionsCache;
|
|
@@ -18672,6 +18794,7 @@ function persistLedgerData(server, serverData) {
|
|
|
18672
18794
|
const data = load();
|
|
18673
18795
|
data[server] = serverData;
|
|
18674
18796
|
persist(data);
|
|
18797
|
+
flushLedger();
|
|
18675
18798
|
}
|
|
18676
18799
|
function markSynced(server, rows) {
|
|
18677
18800
|
if (rows.length === 0) return;
|
|
@@ -18688,13 +18811,13 @@ function markSynced(server, rows) {
|
|
|
18688
18811
|
const o = (cursorFromCaller ? r.offset : void 0) ?? prevOS.o ?? 0;
|
|
18689
18812
|
const s = (cursorFromCaller ? r.size : void 0) ?? prevOS.s ?? 0;
|
|
18690
18813
|
const ev = extractorVersionForId(r.id);
|
|
18691
|
-
const
|
|
18814
|
+
const base4 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
|
|
18692
18815
|
if (o > 0 || s > 0) {
|
|
18693
|
-
|
|
18694
|
-
|
|
18816
|
+
base4.o = o;
|
|
18817
|
+
base4.s = s;
|
|
18695
18818
|
}
|
|
18696
|
-
if (r.hash !== void 0)
|
|
18697
|
-
forServer[r.id] =
|
|
18819
|
+
if (r.hash !== void 0) base4.h = r.hash;
|
|
18820
|
+
forServer[r.id] = base4;
|
|
18698
18821
|
}
|
|
18699
18822
|
persist(data);
|
|
18700
18823
|
}
|
|
@@ -18769,7 +18892,7 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
|
|
|
18769
18892
|
if (row.m >= Math.floor(mtime)) return "skip";
|
|
18770
18893
|
return "full";
|
|
18771
18894
|
}
|
|
18772
|
-
var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
18895
|
+
var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
18773
18896
|
var init_sync_ledger = __esm({
|
|
18774
18897
|
"src/sync-ledger.ts"() {
|
|
18775
18898
|
"use strict";
|
|
@@ -18777,6 +18900,9 @@ var init_sync_ledger = __esm({
|
|
|
18777
18900
|
init_extractor_version();
|
|
18778
18901
|
ledgerPath = () => join38(getDataDir(), "sync-ledger.json");
|
|
18779
18902
|
cache3 = null;
|
|
18903
|
+
base = /* @__PURE__ */ new Map();
|
|
18904
|
+
diskStamp = null;
|
|
18905
|
+
rowKey = (server, id) => `${server}\0${id}`;
|
|
18780
18906
|
LEDGER_FLUSH_MS = 2e3;
|
|
18781
18907
|
pendingWrite = false;
|
|
18782
18908
|
flushTimer = null;
|
|
@@ -18815,7 +18941,7 @@ var init_sync_fields = __esm({
|
|
|
18815
18941
|
});
|
|
18816
18942
|
|
|
18817
18943
|
// ../engine/dist/core/index-lock.js
|
|
18818
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as
|
|
18944
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as statSync36, closeSync as closeSync8 } from "fs";
|
|
18819
18945
|
import { dirname as dirname16, join as join39 } from "path";
|
|
18820
18946
|
function acquireIndexLock(opts) {
|
|
18821
18947
|
const lockPath = lockFilePath(opts.file);
|
|
@@ -18865,7 +18991,7 @@ function lockFilePath(file = ".compact.lock") {
|
|
|
18865
18991
|
}
|
|
18866
18992
|
function stealStaleLock(lockPath, staleAfterMs) {
|
|
18867
18993
|
try {
|
|
18868
|
-
const s =
|
|
18994
|
+
const s = statSync36(lockPath);
|
|
18869
18995
|
const age = Date.now() - s.mtimeMs;
|
|
18870
18996
|
let payload;
|
|
18871
18997
|
try {
|
|
@@ -18937,7 +19063,7 @@ var init_http = __esm({
|
|
|
18937
19063
|
CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18938
19064
|
} catch {
|
|
18939
19065
|
try {
|
|
18940
|
-
if (true) CLI_VERSION = "0.7.
|
|
19066
|
+
if (true) CLI_VERSION = "0.7.16";
|
|
18941
19067
|
} catch {
|
|
18942
19068
|
}
|
|
18943
19069
|
}
|
|
@@ -19435,7 +19561,7 @@ var init_telemetry = __esm({
|
|
|
19435
19561
|
init_sync_client();
|
|
19436
19562
|
cliVersion2 = () => {
|
|
19437
19563
|
try {
|
|
19438
|
-
return true ? "0.7.
|
|
19564
|
+
return true ? "0.7.16" : "dev";
|
|
19439
19565
|
} catch {
|
|
19440
19566
|
return "dev";
|
|
19441
19567
|
}
|
|
@@ -19611,11 +19737,11 @@ __export(server_probe_exports, {
|
|
|
19611
19737
|
function probeOk(r) {
|
|
19612
19738
|
return r.kind === "ok" && r.apiVersion >= 2;
|
|
19613
19739
|
}
|
|
19614
|
-
async function probeServer(
|
|
19740
|
+
async function probeServer(base4, opts = {}) {
|
|
19615
19741
|
const f = opts.fetchImpl || fetch;
|
|
19616
19742
|
let res;
|
|
19617
19743
|
try {
|
|
19618
|
-
res = await f(`${
|
|
19744
|
+
res = await f(`${base4.replace(/\/+$/, "")}/api/capabilities`, {
|
|
19619
19745
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
19620
19746
|
});
|
|
19621
19747
|
} catch (err) {
|
|
@@ -19652,34 +19778,34 @@ async function probeServer(base3, opts = {}) {
|
|
|
19652
19778
|
raw: parsed
|
|
19653
19779
|
};
|
|
19654
19780
|
}
|
|
19655
|
-
function probeAdvice(r,
|
|
19781
|
+
function probeAdvice(r, base4) {
|
|
19656
19782
|
switch (r.kind) {
|
|
19657
19783
|
case "ok":
|
|
19658
19784
|
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`;
|
|
19659
19785
|
case "moved":
|
|
19660
|
-
return `HTTP ${r.status} \u2014 nothing is served at ${
|
|
19786
|
+
return `HTTP ${r.status} \u2014 nothing is served at ${base4}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
|
|
19661
19787
|
case "unauthorized":
|
|
19662
|
-
return `HTTP ${r.status} \u2014 ${
|
|
19788
|
+
return `HTTP ${r.status} \u2014 ${base4} rejected the credential. Re-login: \`chat-recall login ` + base4 + "`";
|
|
19663
19789
|
case "server_error":
|
|
19664
|
-
return `HTTP ${r.status} \u2014 ${
|
|
19790
|
+
return `HTTP ${r.status} \u2014 ${base4} is up but failing. Nothing to fix locally; check the server.`;
|
|
19665
19791
|
case "not_api":
|
|
19666
|
-
return `${
|
|
19792
|
+
return `${base4} 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`;
|
|
19667
19793
|
case "unreachable":
|
|
19668
19794
|
switch (r.reason) {
|
|
19669
19795
|
case "dns":
|
|
19670
|
-
return `cannot resolve the host in ${
|
|
19796
|
+
return `cannot resolve the host in ${base4} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
|
|
19671
19797
|
case "refused":
|
|
19672
|
-
return `${
|
|
19798
|
+
return `${base4} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
|
|
19673
19799
|
case "tls":
|
|
19674
|
-
return `the TLS certificate at ${
|
|
19800
|
+
return `the TLS certificate at ${base4} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
|
|
19675
19801
|
case "timeout":
|
|
19676
|
-
return `${
|
|
19802
|
+
return `${base4} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
|
|
19677
19803
|
case "reset":
|
|
19678
|
-
return `the connection to ${
|
|
19804
|
+
return `the connection to ${base4} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
|
|
19679
19805
|
case "bad_port":
|
|
19680
|
-
return `${
|
|
19806
|
+
return `${base4} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
|
|
19681
19807
|
default:
|
|
19682
|
-
return `no HTTP response from ${
|
|
19808
|
+
return `no HTTP response from ${base4} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
|
|
19683
19809
|
}
|
|
19684
19810
|
}
|
|
19685
19811
|
}
|
|
@@ -19712,8 +19838,8 @@ function describeCause(err) {
|
|
|
19712
19838
|
const code2 = e.code;
|
|
19713
19839
|
if (typeof code2 === "string" && code2 && !parts.includes(code2)) parts.push(code2);
|
|
19714
19840
|
}
|
|
19715
|
-
const
|
|
19716
|
-
return parts.length ? `${
|
|
19841
|
+
const base4 = err instanceof Error ? err.message : String(err);
|
|
19842
|
+
return parts.length ? `${base4}: ${parts.join(" \u2192 ")}` : base4;
|
|
19717
19843
|
}
|
|
19718
19844
|
function describeUnreachable(err) {
|
|
19719
19845
|
const reason = classifyUnreachable(err);
|
|
@@ -19826,7 +19952,7 @@ __export(auto_update_exports, {
|
|
|
19826
19952
|
import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
|
|
19827
19953
|
import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
|
|
19828
19954
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19829
|
-
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as
|
|
19955
|
+
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as statSync37, mkdirSync as mkdirSync9 } from "node:fs";
|
|
19830
19956
|
import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
|
|
19831
19957
|
import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
|
|
19832
19958
|
import { createHash as createHash8 } from "node:crypto";
|
|
@@ -19858,12 +19984,12 @@ function installedVersion() {
|
|
|
19858
19984
|
return null;
|
|
19859
19985
|
}
|
|
19860
19986
|
}
|
|
19861
|
-
function planAutoUpdate(
|
|
19987
|
+
function planAutoUpdate(base4, caps, ownVersion3, flag) {
|
|
19862
19988
|
const rel = caps.cli;
|
|
19863
19989
|
if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
|
|
19864
19990
|
if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
|
|
19865
19991
|
if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
|
|
19866
|
-
return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(
|
|
19992
|
+
return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base4, rel.version), fallbackUrl: tarballUrl(base4), sha256: rel.sha256, from: ownVersion3, to: rel.version };
|
|
19867
19993
|
}
|
|
19868
19994
|
function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
|
|
19869
19995
|
const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
|
|
@@ -19879,7 +20005,7 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
|
|
|
19879
20005
|
if (!MKDTEMP_SUFFIX.test(name)) continue;
|
|
19880
20006
|
const dir = join41(root, name);
|
|
19881
20007
|
try {
|
|
19882
|
-
const st =
|
|
20008
|
+
const st = statSync37(dir);
|
|
19883
20009
|
if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
|
|
19884
20010
|
const inside = readdirSync30(dir);
|
|
19885
20011
|
if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
|
|
@@ -20043,20 +20169,20 @@ function clearUpdateFailures() {
|
|
|
20043
20169
|
function stopAutoUpdates() {
|
|
20044
20170
|
stopRequested = true;
|
|
20045
20171
|
}
|
|
20046
|
-
async function runAutoUpdate(
|
|
20172
|
+
async function runAutoUpdate(base4, authHeaders, ownVersion3, deps) {
|
|
20047
20173
|
let caps;
|
|
20048
20174
|
try {
|
|
20049
|
-
const res = await fetchWithTimeout(`${
|
|
20175
|
+
const res = await fetchWithTimeout(`${base4.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
|
|
20050
20176
|
if (!res.ok) return { updated: false, reason: `capabilities HTTP ${res.status}` };
|
|
20051
20177
|
caps = await res.json();
|
|
20052
20178
|
} catch (e) {
|
|
20053
20179
|
return { updated: false, reason: `capabilities unreachable: ${e instanceof Error ? e.message : e}` };
|
|
20054
20180
|
}
|
|
20055
20181
|
const effectiveOwn = newerOf(ownVersion3, installedVersion());
|
|
20056
|
-
const plan = planAutoUpdate(
|
|
20182
|
+
const plan = planAutoUpdate(base4, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20057
20183
|
if (!plan.update) {
|
|
20058
|
-
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(
|
|
20059
|
-
reportedStandingSkip.add(
|
|
20184
|
+
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base4)) {
|
|
20185
|
+
reportedStandingSkip.add(base4);
|
|
20060
20186
|
try {
|
|
20061
20187
|
const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
|
|
20062
20188
|
reportClientEvent2("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
|
|
@@ -20112,7 +20238,7 @@ var init_auto_update = __esm({
|
|
|
20112
20238
|
"use strict";
|
|
20113
20239
|
init_http();
|
|
20114
20240
|
init_index_lock();
|
|
20115
|
-
tarballUrl = (
|
|
20241
|
+
tarballUrl = (base4, version2) => `${base4.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
|
|
20116
20242
|
GIVE_UP_AFTER = 3;
|
|
20117
20243
|
reportedStandingSkip = /* @__PURE__ */ new Set();
|
|
20118
20244
|
STOPPING_REASON = "not installed: this process is shutting down";
|
|
@@ -20157,7 +20283,7 @@ __export(sync_client_exports, {
|
|
|
20157
20283
|
syncSessions: () => syncSessions
|
|
20158
20284
|
});
|
|
20159
20285
|
import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
|
|
20160
|
-
import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as
|
|
20286
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as statSync38 } from "node:fs";
|
|
20161
20287
|
import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
|
|
20162
20288
|
import { createHash as createHash9 } from "node:crypto";
|
|
20163
20289
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -20220,15 +20346,15 @@ function loadAllCredentials() {
|
|
|
20220
20346
|
return [];
|
|
20221
20347
|
}
|
|
20222
20348
|
async function fetchTenantSecurityConfig(cred) {
|
|
20223
|
-
const
|
|
20224
|
-
const cached = _securityConfigCache.get(
|
|
20349
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20350
|
+
const cached = _securityConfigCache.get(base4);
|
|
20225
20351
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20226
20352
|
const headers = { "content-type": "application/json" };
|
|
20227
20353
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
20228
20354
|
try {
|
|
20229
20355
|
const [rulesRes, settingsRes] = await Promise.all([
|
|
20230
|
-
fetchWithTimeout(`${
|
|
20231
|
-
fetchWithTimeout(`${
|
|
20356
|
+
fetchWithTimeout(`${base4}/api/secrets/rules`, { headers }),
|
|
20357
|
+
fetchWithTimeout(`${base4}/api/teams/security-config`, { headers }).catch(() => null)
|
|
20232
20358
|
]);
|
|
20233
20359
|
let tenantRules = [];
|
|
20234
20360
|
let rulePackVersion = null;
|
|
@@ -20253,18 +20379,18 @@ async function fetchTenantSecurityConfig(cred) {
|
|
|
20253
20379
|
const body2 = await settingsRes.json().catch(() => ({}));
|
|
20254
20380
|
if (typeof body2.verifySecrets === "boolean") verifySecrets = body2.verifySecrets;
|
|
20255
20381
|
if (typeof body2.telemetry === "boolean") {
|
|
20256
|
-
setTelemetryEligible(
|
|
20382
|
+
setTelemetryEligible(base4, body2.telemetry);
|
|
20257
20383
|
try {
|
|
20258
20384
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20259
20385
|
updateCollectorHealth({
|
|
20260
|
-
telemetryEligible: { ...prior, [
|
|
20386
|
+
telemetryEligible: { ...prior, [base4]: { allowed: body2.telemetry, at: Date.now() } }
|
|
20261
20387
|
});
|
|
20262
20388
|
} catch {
|
|
20263
20389
|
}
|
|
20264
20390
|
}
|
|
20265
20391
|
}
|
|
20266
20392
|
const config2 = { tenantRules, verifySecrets, rulePackVersion };
|
|
20267
|
-
_securityConfigCache.set(
|
|
20393
|
+
_securityConfigCache.set(base4, { fetchedAt: Date.now(), config: config2 });
|
|
20268
20394
|
return config2;
|
|
20269
20395
|
} catch {
|
|
20270
20396
|
return { tenantRules: [], verifySecrets: true, rulePackVersion: null };
|
|
@@ -20283,18 +20409,18 @@ function readPersistedSyncConfigs() {
|
|
|
20283
20409
|
return {};
|
|
20284
20410
|
}
|
|
20285
20411
|
}
|
|
20286
|
-
function persistSyncConfig(
|
|
20412
|
+
function persistSyncConfig(base4, config2) {
|
|
20287
20413
|
try {
|
|
20288
20414
|
const all = readPersistedSyncConfigs();
|
|
20289
|
-
all[
|
|
20415
|
+
all[base4] = { fetchedAt: Date.now(), config: config2 };
|
|
20290
20416
|
mkdirSync10(dirname19(syncConfigCachePath()), { recursive: true });
|
|
20291
20417
|
writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
|
|
20292
20418
|
} catch {
|
|
20293
20419
|
}
|
|
20294
20420
|
}
|
|
20295
20421
|
async function fetchTenantSyncConfig(cred) {
|
|
20296
|
-
const
|
|
20297
|
-
const cached = _syncConfigCache.get(
|
|
20422
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20423
|
+
const cached = _syncConfigCache.get(base4);
|
|
20298
20424
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20299
20425
|
const headers = { "content-type": "application/json" };
|
|
20300
20426
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
@@ -20302,7 +20428,7 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20302
20428
|
try {
|
|
20303
20429
|
const sources3 = discoverSessionSources();
|
|
20304
20430
|
if (sources3.length > 0) {
|
|
20305
|
-
await fetchWithTimeout(`${
|
|
20431
|
+
await fetchWithTimeout(`${base4}/api/sync-config/sources`, {
|
|
20306
20432
|
method: "POST",
|
|
20307
20433
|
headers,
|
|
20308
20434
|
body: JSON.stringify({ device: deviceLabel(), sources: sources3 })
|
|
@@ -20310,12 +20436,12 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20310
20436
|
}
|
|
20311
20437
|
} catch {
|
|
20312
20438
|
}
|
|
20313
|
-
const res = await fetchWithTimeout(`${
|
|
20439
|
+
const res = await fetchWithTimeout(`${base4}/api/sync-config`, { headers });
|
|
20314
20440
|
if (res.status === 404) {
|
|
20315
|
-
_syncConfigCache.set(
|
|
20441
|
+
_syncConfigCache.set(base4, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
|
|
20316
20442
|
return EMPTY_SYNC_CONFIG;
|
|
20317
20443
|
}
|
|
20318
|
-
if (!res.ok) return lastKnownSyncConfig(
|
|
20444
|
+
if (!res.ok) return lastKnownSyncConfig(base4, `HTTP ${res.status}`);
|
|
20319
20445
|
const body2 = await res.json().catch(() => ({}));
|
|
20320
20446
|
const config2 = {
|
|
20321
20447
|
excludeTools: Array.isArray(body2.excludeTools) ? body2.excludeTools.filter((t) => typeof t === "string") : [],
|
|
@@ -20352,22 +20478,22 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20352
20478
|
if (decided > 0) console.error(`[sync] applied ${decided} folder decision(s) from the dashboard`);
|
|
20353
20479
|
} catch {
|
|
20354
20480
|
}
|
|
20355
|
-
_syncConfigCache.set(
|
|
20356
|
-
persistSyncConfig(
|
|
20481
|
+
_syncConfigCache.set(base4, { fetchedAt: Date.now(), config: config2 });
|
|
20482
|
+
persistSyncConfig(base4, config2);
|
|
20357
20483
|
return config2;
|
|
20358
20484
|
} catch (err) {
|
|
20359
20485
|
if (isSyncRulesRefusal(err)) throw err;
|
|
20360
|
-
return lastKnownSyncConfig(
|
|
20486
|
+
return lastKnownSyncConfig(base4, err instanceof Error ? err.message : "request failed");
|
|
20361
20487
|
}
|
|
20362
20488
|
}
|
|
20363
|
-
function lastKnownSyncConfig(
|
|
20364
|
-
const remembered = _syncConfigCache.get(
|
|
20489
|
+
function lastKnownSyncConfig(base4, reason) {
|
|
20490
|
+
const remembered = _syncConfigCache.get(base4) ?? readPersistedSyncConfigs()[base4];
|
|
20365
20491
|
if (remembered) {
|
|
20366
20492
|
console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
|
|
20367
20493
|
return remembered.config;
|
|
20368
20494
|
}
|
|
20369
20495
|
const refusal = new Error(
|
|
20370
|
-
`Cannot read the server-side sync rules from ${
|
|
20496
|
+
`Cannot read the server-side sync rules from ${base4} (${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.`
|
|
20371
20497
|
);
|
|
20372
20498
|
refusal[SYNC_RULES_REFUSAL] = true;
|
|
20373
20499
|
throw refusal;
|
|
@@ -20488,25 +20614,25 @@ async function syncSessions(opts = {}) {
|
|
|
20488
20614
|
${errors.join("\n ")}`);
|
|
20489
20615
|
return { ...agg, perTarget };
|
|
20490
20616
|
}
|
|
20491
|
-
async function postFieldBatch(
|
|
20617
|
+
async function postFieldBatch(base4, token, rows) {
|
|
20492
20618
|
const headers = { "content-type": "application/json" };
|
|
20493
20619
|
if (token) headers.authorization = `Bearer ${token}`;
|
|
20494
20620
|
const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
20495
20621
|
for (let attempt = 0; ; attempt++) {
|
|
20496
|
-
const res = await fetchWithTimeout(`${
|
|
20622
|
+
const res = await fetchWithTimeout(`${base4}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
|
|
20497
20623
|
if (res.ok) return;
|
|
20498
20624
|
const retryable = res.status === 429 || res.status >= 500;
|
|
20499
|
-
if (!retryable || attempt >= 5) throw new Error(`${
|
|
20625
|
+
if (!retryable || attempt >= 5) throw new Error(`${base4}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
20500
20626
|
const ra = Number(res.headers.get("retry-after")) || 0;
|
|
20501
20627
|
await sleep2(ra > 0 ? ra * 1e3 : [500, 1500, 4e3, 8e3, 15e3][Math.min(attempt, 4)]);
|
|
20502
20628
|
}
|
|
20503
20629
|
}
|
|
20504
20630
|
async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
20505
|
-
const
|
|
20506
|
-
const ledger = getSyncedRows(
|
|
20631
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20632
|
+
const ledger = getSyncedRows(base4);
|
|
20507
20633
|
let pushed = 0, absent = 0, scanned = 0;
|
|
20508
20634
|
for (const field of SYNC_FIELDS) {
|
|
20509
|
-
const needFull = !!opts.force || fieldNeedsFullPass(
|
|
20635
|
+
const needFull = !!opts.force || fieldNeedsFullPass(base4, field);
|
|
20510
20636
|
const candidates2 = needFull ? fullRefs() : opts.convRefs ?? fullRefs();
|
|
20511
20637
|
const send = [];
|
|
20512
20638
|
const mtimeById = /* @__PURE__ */ new Map();
|
|
@@ -20522,15 +20648,15 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
|
20522
20648
|
}
|
|
20523
20649
|
for (let i = 0; i < send.length; i += 200) {
|
|
20524
20650
|
const chunk = send.slice(i, i + 200);
|
|
20525
|
-
await postFieldBatch(
|
|
20526
|
-
markFieldCoverage(
|
|
20651
|
+
await postFieldBatch(base4, cred.token, chunk);
|
|
20652
|
+
markFieldCoverage(base4, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
|
|
20527
20653
|
pushed += chunk.length;
|
|
20528
20654
|
}
|
|
20529
20655
|
if (absentIds.length) {
|
|
20530
|
-
markFieldCoverage(
|
|
20656
|
+
markFieldCoverage(base4, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
|
|
20531
20657
|
absent += absentIds.length;
|
|
20532
20658
|
}
|
|
20533
|
-
if (needFull) markFieldFullPassDone(
|
|
20659
|
+
if (needFull) markFieldFullPassDone(base4, field);
|
|
20534
20660
|
}
|
|
20535
20661
|
return { pushed, absent, scanned };
|
|
20536
20662
|
}
|
|
@@ -20568,7 +20694,7 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
|
|
|
20568
20694
|
if (!name.startsWith("cr-batchscan-")) continue;
|
|
20569
20695
|
const p = join42(dir, name);
|
|
20570
20696
|
try {
|
|
20571
|
-
if (now -
|
|
20697
|
+
if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
|
|
20572
20698
|
rmSync6(p, { recursive: true, force: true });
|
|
20573
20699
|
reaped++;
|
|
20574
20700
|
} catch {
|
|
@@ -20647,25 +20773,25 @@ function serverIngestConcurrency(serverUrl) {
|
|
|
20647
20773
|
return advertisedIngestConc.get(serverUrl.replace(/\/$/, "")) ?? null;
|
|
20648
20774
|
}
|
|
20649
20775
|
async function verifyServerApi(serverUrl, minApi) {
|
|
20650
|
-
const
|
|
20651
|
-
const seen = capsOk.get(
|
|
20776
|
+
const base4 = serverUrl.replace(/\/$/, "");
|
|
20777
|
+
const seen = capsOk.get(base4);
|
|
20652
20778
|
if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
|
|
20653
20779
|
const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
20654
|
-
const probe = await probeServer2(
|
|
20780
|
+
const probe = await probeServer2(base4, {
|
|
20655
20781
|
fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
|
|
20656
20782
|
});
|
|
20657
|
-
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe,
|
|
20783
|
+
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base4));
|
|
20658
20784
|
const caps = probe.raw;
|
|
20659
20785
|
if ((caps.apiVersion ?? 0) < minApi) {
|
|
20660
20786
|
throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
|
|
20661
20787
|
}
|
|
20662
20788
|
const adv = caps.limits?.ingestConcurrencyPerTenant;
|
|
20663
20789
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
20664
|
-
advertisedIngestConc.set(
|
|
20790
|
+
advertisedIngestConc.set(base4, Math.floor(adv));
|
|
20665
20791
|
}
|
|
20666
|
-
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(
|
|
20667
|
-
else advertisedGzipBody.delete(
|
|
20668
|
-
capsOk.set(
|
|
20792
|
+
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base4);
|
|
20793
|
+
else advertisedGzipBody.delete(base4);
|
|
20794
|
+
capsOk.set(base4, Date.now());
|
|
20669
20795
|
return true;
|
|
20670
20796
|
}
|
|
20671
20797
|
async function syncToTarget(cred, opts = {}) {
|
|
@@ -20730,7 +20856,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20730
20856
|
const ac = new AbortController();
|
|
20731
20857
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20732
20858
|
try {
|
|
20733
|
-
const res = await fetch(`${
|
|
20859
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20734
20860
|
method: "POST",
|
|
20735
20861
|
// No token ⇒ no auth header: a no-auth local server (AUTH_PROVIDER=none)
|
|
20736
20862
|
// accepts it as the single 'default' tenant.
|
|
@@ -20746,26 +20872,26 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20746
20872
|
acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
|
|
20747
20873
|
const body3 = await res.json().catch(() => ({}));
|
|
20748
20874
|
if (typeof body3.telemetry === "boolean") {
|
|
20749
|
-
setTelemetryEligible(
|
|
20875
|
+
setTelemetryEligible(base4, body3.telemetry);
|
|
20750
20876
|
try {
|
|
20751
20877
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20752
20878
|
updateCollectorHealth({
|
|
20753
|
-
telemetryEligible: { ...prior, [
|
|
20879
|
+
telemetryEligible: { ...prior, [base4]: { allowed: body3.telemetry, at: Date.now() } }
|
|
20754
20880
|
});
|
|
20755
20881
|
} catch {
|
|
20756
20882
|
}
|
|
20757
20883
|
}
|
|
20758
20884
|
const adv = body3.limits?.ingestConcurrencyPerTenant;
|
|
20759
20885
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
20760
|
-
setServerIngestConcurrency(
|
|
20886
|
+
setServerIngestConcurrency(base4, Math.floor(adv));
|
|
20761
20887
|
}
|
|
20762
20888
|
if (body3.cli && body3.cli.version) {
|
|
20763
20889
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20764
20890
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20765
|
-
const plan = planAutoUpdate2(
|
|
20891
|
+
const plan = planAutoUpdate2(base4, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20766
20892
|
sweepStaleStaging2();
|
|
20767
20893
|
if (plan.update) {
|
|
20768
|
-
void runAutoUpdate2(
|
|
20894
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion).catch(() => {
|
|
20769
20895
|
});
|
|
20770
20896
|
}
|
|
20771
20897
|
}
|
|
@@ -20774,7 +20900,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20774
20900
|
const retryable = res.status === 429 || res.status >= 500;
|
|
20775
20901
|
const text = await res.text().catch(() => "");
|
|
20776
20902
|
if (res.status === 401 || res.status === 403) {
|
|
20777
|
-
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${
|
|
20903
|
+
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base4}`);
|
|
20778
20904
|
}
|
|
20779
20905
|
if (res.status === 402) {
|
|
20780
20906
|
let detail = text;
|
|
@@ -20795,7 +20921,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20795
20921
|
const retryAfterS = Number(res.headers.get("retry-after"));
|
|
20796
20922
|
const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
|
|
20797
20923
|
const why = res.status === 524 ? ` \u2014 the proxy cut the request at its time limit; the batch is too big for one request` : "";
|
|
20798
|
-
console.error(`[sync] HTTP ${res.status} from ${
|
|
20924
|
+
console.error(`[sync] HTTP ${res.status} from ${base4}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
|
|
20799
20925
|
await sleep2(waitMs);
|
|
20800
20926
|
continue;
|
|
20801
20927
|
} catch (err) {
|
|
@@ -20863,11 +20989,11 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20863
20989
|
}
|
|
20864
20990
|
};
|
|
20865
20991
|
};
|
|
20866
|
-
const
|
|
20867
|
-
const ledger = opts.useLedger ? getSyncedRows(
|
|
20992
|
+
const base4 = cred.serverUrl.replace(/\/$/, "");
|
|
20993
|
+
const ledger = opts.useLedger ? getSyncedRows(base4) : null;
|
|
20868
20994
|
const convBatch = makeBatcher("conversations", 25, false, (rows) => {
|
|
20869
20995
|
try {
|
|
20870
|
-
markSynced(
|
|
20996
|
+
markSynced(base4, rows.map((r) => {
|
|
20871
20997
|
const off = typeof r.from_offset === "number" ? r.from_offset : 0;
|
|
20872
20998
|
const hash = shippedHash.get(r.session_id);
|
|
20873
20999
|
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 };
|
|
@@ -20882,7 +21008,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20882
21008
|
const ac = new AbortController();
|
|
20883
21009
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20884
21010
|
try {
|
|
20885
|
-
const res = await fetch(`${
|
|
21011
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20886
21012
|
method: "POST",
|
|
20887
21013
|
headers,
|
|
20888
21014
|
body: JSON.stringify({ conversations: convs }),
|
|
@@ -20893,10 +21019,10 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20893
21019
|
if (body2.cli && body2.cli.version) {
|
|
20894
21020
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20895
21021
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20896
|
-
const plan = planAutoUpdate2(
|
|
21022
|
+
const plan = planAutoUpdate2(base4, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20897
21023
|
sweepStaleStaging2();
|
|
20898
21024
|
if (plan.update) {
|
|
20899
|
-
void runAutoUpdate2(
|
|
21025
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion).catch(() => {
|
|
20900
21026
|
});
|
|
20901
21027
|
}
|
|
20902
21028
|
}
|
|
@@ -20904,7 +21030,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20904
21030
|
const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
|
|
20905
21031
|
if (acked.length > 0) {
|
|
20906
21032
|
try {
|
|
20907
|
-
markSynced(
|
|
21033
|
+
markSynced(base4, acked.map((c) => ({
|
|
20908
21034
|
id: c.session_id,
|
|
20909
21035
|
mtime: c.mtime,
|
|
20910
21036
|
offset: c.from_offset,
|
|
@@ -20922,13 +21048,14 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20922
21048
|
clearTimeout(timer);
|
|
20923
21049
|
}
|
|
20924
21050
|
};
|
|
20925
|
-
const postChunkedHead = async (
|
|
21051
|
+
const postChunkedHead = async (head, fileSize) => {
|
|
21052
|
+
const conv = { ...head, chunk_head: true };
|
|
20926
21053
|
const headers = { "content-type": "application/json" };
|
|
20927
21054
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
20928
21055
|
const ac = new AbortController();
|
|
20929
21056
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20930
21057
|
try {
|
|
20931
|
-
const res = await fetch(`${
|
|
21058
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20932
21059
|
method: "POST",
|
|
20933
21060
|
headers,
|
|
20934
21061
|
body: JSON.stringify({ conversations: [conv] }),
|
|
@@ -20938,14 +21065,15 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20938
21065
|
const body2 = await res.json().catch(() => ({}));
|
|
20939
21066
|
const id = conv.session_id;
|
|
20940
21067
|
const guarded = (body2.shrink_guarded ?? []).find((g) => g.session_id === id);
|
|
21068
|
+
const staged = (body2.rebuild_staged ?? []).includes(id);
|
|
20941
21069
|
const mtime = conv.mtime;
|
|
20942
21070
|
try {
|
|
20943
|
-
if (!guarded) {
|
|
20944
|
-
markSynced(
|
|
21071
|
+
if (staged || !guarded) {
|
|
21072
|
+
markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
|
|
20945
21073
|
} else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
|
|
20946
|
-
markSynced(
|
|
21074
|
+
markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
|
|
20947
21075
|
} else {
|
|
20948
|
-
markSynced(
|
|
21076
|
+
markSynced(base4, [{ id, mtime }]);
|
|
20949
21077
|
}
|
|
20950
21078
|
} catch {
|
|
20951
21079
|
}
|
|
@@ -21011,7 +21139,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21011
21139
|
const m = process.memoryUsage();
|
|
21012
21140
|
console.error(`[sync-trace] ${msg} \xB7 heap=${(m.heapUsed / 1048576).toFixed(0)}MB rss=${(m.rss / 1048576).toFixed(0)}MB`);
|
|
21013
21141
|
} : null;
|
|
21014
|
-
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${
|
|
21142
|
+
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base4}`);
|
|
21015
21143
|
if (upload.sessionMeta) {
|
|
21016
21144
|
try {
|
|
21017
21145
|
await reconcileFieldsForTarget(
|
|
@@ -21026,7 +21154,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21026
21154
|
{ convRefs: slice }
|
|
21027
21155
|
);
|
|
21028
21156
|
} catch (e) {
|
|
21029
|
-
console.error(`[sync] field reconcile (${
|
|
21157
|
+
console.error(`[sync] field reconcile (${base4}): ${e instanceof Error ? e.message : e}`);
|
|
21030
21158
|
}
|
|
21031
21159
|
}
|
|
21032
21160
|
trace?.("field reconcile done");
|
|
@@ -21068,7 +21196,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21068
21196
|
}
|
|
21069
21197
|
trace?.("conversation walk start");
|
|
21070
21198
|
const walkStartedAt = Date.now();
|
|
21071
|
-
const firstWalk = (ledger ?? getSyncedRows(
|
|
21199
|
+
const firstWalk = (ledger ?? getSyncedRows(base4)).size === 0;
|
|
21072
21200
|
reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false, first: firstWalk });
|
|
21073
21201
|
walkProgressForUpload = { done: 0, total: slice.length, complete: false };
|
|
21074
21202
|
let traceN = 0;
|
|
@@ -21108,7 +21236,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21108
21236
|
continue;
|
|
21109
21237
|
} else {
|
|
21110
21238
|
try {
|
|
21111
|
-
markSynced(
|
|
21239
|
+
markSynced(base4, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21112
21240
|
} catch {
|
|
21113
21241
|
}
|
|
21114
21242
|
skipped++;
|
|
@@ -21131,7 +21259,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21131
21259
|
const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
|
|
21132
21260
|
const priorCursor = ackRow?.s ?? 0;
|
|
21133
21261
|
const safeToAck = off > 0 && off <= priorCursor;
|
|
21134
|
-
markSynced(
|
|
21262
|
+
markSynced(base4, [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 }]);
|
|
21135
21263
|
} catch {
|
|
21136
21264
|
}
|
|
21137
21265
|
}
|
|
@@ -21141,7 +21269,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21141
21269
|
if (!built) {
|
|
21142
21270
|
if (ledger) {
|
|
21143
21271
|
try {
|
|
21144
|
-
markSynced(
|
|
21272
|
+
markSynced(base4, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21145
21273
|
} catch {
|
|
21146
21274
|
}
|
|
21147
21275
|
}
|
|
@@ -21198,7 +21326,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21198
21326
|
walked++;
|
|
21199
21327
|
if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
|
|
21200
21328
|
}
|
|
21201
|
-
const itemVersions = loadItemVersions(
|
|
21329
|
+
const itemVersions = loadItemVersions(base4);
|
|
21202
21330
|
const seenTools = /* @__PURE__ */ new Set();
|
|
21203
21331
|
const seenItemKeys = /* @__PURE__ */ new Map();
|
|
21204
21332
|
const registry = buildSourceRegistry();
|
|
@@ -21292,7 +21420,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21292
21420
|
await appendFlush();
|
|
21293
21421
|
for (const id of appendResults.fullResyncNeeded) {
|
|
21294
21422
|
try {
|
|
21295
|
-
markFullResync(
|
|
21423
|
+
markFullResync(base4, id);
|
|
21296
21424
|
} catch {
|
|
21297
21425
|
}
|
|
21298
21426
|
}
|
|
@@ -21305,7 +21433,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21305
21433
|
await findingsBatch.drain();
|
|
21306
21434
|
await drainInflight();
|
|
21307
21435
|
{
|
|
21308
|
-
const sentHashes = loadInventoryHashes(
|
|
21436
|
+
const sentHashes = loadInventoryHashes(base4);
|
|
21309
21437
|
const entries = [];
|
|
21310
21438
|
const hashes = {};
|
|
21311
21439
|
for (const [sourceType, ids] of inventory) {
|
|
@@ -21319,7 +21447,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21319
21447
|
if (entries.length > 0) {
|
|
21320
21448
|
try {
|
|
21321
21449
|
await post({ inventory: entries });
|
|
21322
|
-
saveInventoryHashes(
|
|
21450
|
+
saveInventoryHashes(base4, hashes);
|
|
21323
21451
|
} catch (err) {
|
|
21324
21452
|
console.error(`[sync] toolkit inventory not sent (it is retried next sync): ${err instanceof Error ? err.message : err}`);
|
|
21325
21453
|
}
|
|
@@ -21330,7 +21458,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21330
21458
|
const rec = {};
|
|
21331
21459
|
for (const t of seenTools) rec[t] = extractorVersionForTool(t);
|
|
21332
21460
|
for (const [k, v] of seenItemKeys) rec[k] = v;
|
|
21333
|
-
saveItemVersions(
|
|
21461
|
+
saveItemVersions(base4, rec);
|
|
21334
21462
|
} catch {
|
|
21335
21463
|
}
|
|
21336
21464
|
}
|
|
@@ -21343,9 +21471,9 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21343
21471
|
}
|
|
21344
21472
|
if (tombstones.length > 0) {
|
|
21345
21473
|
await post({ tombstones });
|
|
21346
|
-
const data = getLedgerData(
|
|
21474
|
+
const data = getLedgerData(base4);
|
|
21347
21475
|
for (const t of tombstones) delete data[t.session_id];
|
|
21348
|
-
persistLedgerData(
|
|
21476
|
+
persistLedgerData(base4, data);
|
|
21349
21477
|
}
|
|
21350
21478
|
}
|
|
21351
21479
|
if (opts.prune) await post({ prune_empty_sessions: true });
|
|
@@ -21673,8 +21801,13 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
|
|
|
21673
21801
|
const backend = getBackendForId(ref.prefixedId);
|
|
21674
21802
|
if (!backend?.readFromOffset) return null;
|
|
21675
21803
|
const mapPath = opts.mapPath ?? ((p) => p);
|
|
21676
|
-
const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
|
|
21804
|
+
const { text: tailText, newOffset, skippedBytes, baseLine } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
|
|
21677
21805
|
if (newOffset <= fromOffset) return null;
|
|
21806
|
+
const fileSize = backend.fileSize?.(ref.prefixedId) ?? 0;
|
|
21807
|
+
const position = {
|
|
21808
|
+
...typeof baseLine === "number" ? { base_line: baseLine } : {},
|
|
21809
|
+
final: fileSize > 0 && newOffset >= fileSize
|
|
21810
|
+
};
|
|
21678
21811
|
const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
|
|
21679
21812
|
const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
|
|
21680
21813
|
const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
|
|
@@ -21687,6 +21820,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
|
|
|
21687
21820
|
append: true,
|
|
21688
21821
|
base_offset: fromOffset,
|
|
21689
21822
|
from_offset: newOffset,
|
|
21823
|
+
...position,
|
|
21690
21824
|
redacted_text: "",
|
|
21691
21825
|
envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
|
|
21692
21826
|
mtime: Math.floor(ref.mtime) || 0
|
|
@@ -21733,6 +21867,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
|
|
|
21733
21867
|
// equals its stored synced-through offset.
|
|
21734
21868
|
from_offset: newOffset,
|
|
21735
21869
|
// where it ends → new synced-through offset.
|
|
21870
|
+
...position,
|
|
21736
21871
|
redacted_text: redactedText,
|
|
21737
21872
|
envelope,
|
|
21738
21873
|
mtime: Math.floor(ref.mtime) || 0
|
|
@@ -21816,7 +21951,7 @@ var init_sync_client = __esm({
|
|
|
21816
21951
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21817
21952
|
} catch {
|
|
21818
21953
|
try {
|
|
21819
|
-
if (true) ownVersion = "0.7.
|
|
21954
|
+
if (true) ownVersion = "0.7.16";
|
|
21820
21955
|
} catch {
|
|
21821
21956
|
}
|
|
21822
21957
|
}
|
|
@@ -21910,9 +22045,9 @@ async function refreshUpdateCheck(force = false) {
|
|
|
21910
22045
|
const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
21911
22046
|
for (const cred of loadAllCredentials2()) {
|
|
21912
22047
|
if (!cred.serverUrl) continue;
|
|
21913
|
-
const
|
|
22048
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
21914
22049
|
try {
|
|
21915
|
-
const res = await fetchWithTimeout(`${
|
|
22050
|
+
const res = await fetchWithTimeout(`${base4}/api/capabilities`, {
|
|
21916
22051
|
headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
|
|
21917
22052
|
}, 5e3);
|
|
21918
22053
|
if (!res.ok) continue;
|
|
@@ -21967,11 +22102,11 @@ var init_update_notice = __esm({
|
|
|
21967
22102
|
});
|
|
21968
22103
|
|
|
21969
22104
|
// ../engine/dist/core/which.js
|
|
21970
|
-
import { existsSync as existsSync42, statSync as
|
|
22105
|
+
import { existsSync as existsSync42, statSync as statSync39, accessSync, constants } from "node:fs";
|
|
21971
22106
|
import { join as join44, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
|
|
21972
22107
|
function executable(p) {
|
|
21973
22108
|
try {
|
|
21974
|
-
if (!
|
|
22109
|
+
if (!statSync39(p).isFile())
|
|
21975
22110
|
return false;
|
|
21976
22111
|
} catch {
|
|
21977
22112
|
return false;
|
|
@@ -22300,7 +22435,7 @@ __export(companions_exports, {
|
|
|
22300
22435
|
uninstallCodeindex: () => uninstallCodeindex,
|
|
22301
22436
|
unregisterCodeindexMcp: () => unregisterCodeindexMcp
|
|
22302
22437
|
});
|
|
22303
|
-
import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as
|
|
22438
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync40, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "node:fs";
|
|
22304
22439
|
import { homedir as homedir17 } from "node:os";
|
|
22305
22440
|
import { join as join47 } from "node:path";
|
|
22306
22441
|
import { execSync as execSync3 } from "node:child_process";
|
|
@@ -22351,7 +22486,7 @@ function checkCodeindexStatus() {
|
|
|
22351
22486
|
status.installed = true;
|
|
22352
22487
|
status.path = foundPath;
|
|
22353
22488
|
try {
|
|
22354
|
-
status.size =
|
|
22489
|
+
status.size = statSync40(foundPath).size;
|
|
22355
22490
|
} catch {
|
|
22356
22491
|
}
|
|
22357
22492
|
try {
|
|
@@ -22501,7 +22636,7 @@ __export(install_skills_exports, {
|
|
|
22501
22636
|
skillsSourceDir: () => skillsSourceDir,
|
|
22502
22637
|
uninstallSkills: () => uninstallSkills
|
|
22503
22638
|
});
|
|
22504
|
-
import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as
|
|
22639
|
+
import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as statSync41, realpathSync as realpathSync4 } from "fs";
|
|
22505
22640
|
import { createHash as createHash11 } from "crypto";
|
|
22506
22641
|
import { join as join48, basename as basename21 } from "path";
|
|
22507
22642
|
function skillsSourceDir() {
|
|
@@ -22720,10 +22855,10 @@ function parseAgentText(content, format, fallbackName) {
|
|
|
22720
22855
|
return { name: fm.name || fallbackName, description: fm.description || "", tools: fm.tools, body: body2.trim() };
|
|
22721
22856
|
}
|
|
22722
22857
|
function readCommand(filePath, format) {
|
|
22723
|
-
return parseCommandText(readFileSync40(filePath, "utf-8"), format,
|
|
22858
|
+
return parseCommandText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
|
|
22724
22859
|
}
|
|
22725
22860
|
function readAgent(filePath, format) {
|
|
22726
|
-
return parseAgentText(readFileSync40(filePath, "utf-8"), format,
|
|
22861
|
+
return parseAgentText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
|
|
22727
22862
|
}
|
|
22728
22863
|
function readInstructions(filePath, name) {
|
|
22729
22864
|
const content = readFileSync40(filePath, "utf-8");
|
|
@@ -22823,7 +22958,7 @@ function emit(type, art, toTool, projectPath) {
|
|
|
22823
22958
|
content: stringifyFrontmatter({ name: art.name, description: art.description, ...art.tools ? { tools: art.tools } : {} }, art.body)
|
|
22824
22959
|
};
|
|
22825
22960
|
}
|
|
22826
|
-
function
|
|
22961
|
+
function base2(filePath) {
|
|
22827
22962
|
return basename22(filePath).replace(/\.(md|toml)$/, "");
|
|
22828
22963
|
}
|
|
22829
22964
|
var init_artifact_codec = __esm({
|
|
@@ -23308,11 +23443,11 @@ __export(toolkit_pull_exports, {
|
|
|
23308
23443
|
platformRefusal: () => platformRefusal,
|
|
23309
23444
|
portableCommand: () => portableCommand
|
|
23310
23445
|
});
|
|
23311
|
-
import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as
|
|
23446
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync42, writeFileSync as writeFileSync16 } from "node:fs";
|
|
23312
23447
|
import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
|
|
23313
23448
|
function isExecutableFile(p) {
|
|
23314
23449
|
try {
|
|
23315
|
-
return
|
|
23450
|
+
return statSync42(p).isFile();
|
|
23316
23451
|
} catch {
|
|
23317
23452
|
return false;
|
|
23318
23453
|
}
|
|
@@ -23326,9 +23461,9 @@ function portableCommand(cmd) {
|
|
|
23326
23461
|
}
|
|
23327
23462
|
if (isExecutableFile(cmd))
|
|
23328
23463
|
return { command: cmd, rewritten: false };
|
|
23329
|
-
const
|
|
23330
|
-
if (onPath(
|
|
23331
|
-
return { command:
|
|
23464
|
+
const base4 = basename24(cmd);
|
|
23465
|
+
if (onPath(base4))
|
|
23466
|
+
return { command: base4, rewritten: true };
|
|
23332
23467
|
return { missing: cmd };
|
|
23333
23468
|
}
|
|
23334
23469
|
function entryFromSpec(spec) {
|
|
@@ -24162,8 +24297,8 @@ import { homedir as homedir20 } from "os";
|
|
|
24162
24297
|
import { join as join53 } from "path";
|
|
24163
24298
|
function opencodeConfigDir() {
|
|
24164
24299
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
24165
|
-
const
|
|
24166
|
-
return join53(
|
|
24300
|
+
const base4 = xdg && xdg.trim() ? xdg : join53(homedir20(), ".config");
|
|
24301
|
+
return join53(base4, "opencode");
|
|
24167
24302
|
}
|
|
24168
24303
|
function opencodePluginDir() {
|
|
24169
24304
|
return join53(opencodeConfigDir(), "plugins");
|
|
@@ -24750,17 +24885,17 @@ async function collectCode(opts) {
|
|
|
24750
24885
|
return r;
|
|
24751
24886
|
};
|
|
24752
24887
|
log6(`indexing ${ws} + analyses \u2026`);
|
|
24753
|
-
const
|
|
24888
|
+
const base4 = await runMcp(bin, ws, [
|
|
24754
24889
|
call(1, "index_workspace", { path: ws }),
|
|
24755
24890
|
call(2, "status"),
|
|
24756
24891
|
call(3, "get_tree"),
|
|
24757
24892
|
...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
|
|
24758
24893
|
]);
|
|
24759
|
-
const st = parseJson(
|
|
24760
|
-
const tree = parseJson(
|
|
24894
|
+
const st = parseJson(base4, 2, {});
|
|
24895
|
+
const tree = parseJson(base4, 3, []);
|
|
24761
24896
|
const A = {};
|
|
24762
24897
|
ANALYSES.forEach((a, i) => {
|
|
24763
|
-
A[a] = parseJson(
|
|
24898
|
+
A[a] = parseJson(base4, 10 + i, {});
|
|
24764
24899
|
});
|
|
24765
24900
|
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;
|
|
24766
24901
|
if (tree.length === 0) {
|
|
@@ -25589,7 +25724,7 @@ __export(project_tasks_exports, {
|
|
|
25589
25724
|
pushProjectTaskStatuses: () => pushProjectTaskStatuses,
|
|
25590
25725
|
renderTeamTasksMd: () => renderTeamTasksMd
|
|
25591
25726
|
});
|
|
25592
|
-
import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as
|
|
25727
|
+
import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync43 } from "node:fs";
|
|
25593
25728
|
import { join as join55, dirname as dirname25 } from "node:path";
|
|
25594
25729
|
import { homedir as homedir21 } from "node:os";
|
|
25595
25730
|
function isTeamTaskStatus(v) {
|
|
@@ -25660,14 +25795,14 @@ function writeLedger(l) {
|
|
|
25660
25795
|
} catch {
|
|
25661
25796
|
}
|
|
25662
25797
|
}
|
|
25663
|
-
async function pushProjectTaskStatuses(
|
|
25798
|
+
async function pushProjectTaskStatuses(base4, authHeaders, opts = {}) {
|
|
25664
25799
|
const out = { files: 0, pushedTasks: 0 };
|
|
25665
25800
|
const ledger = readLedger();
|
|
25666
25801
|
let ledgerDirty = false;
|
|
25667
25802
|
for (const fk of FILE_KINDS) {
|
|
25668
25803
|
let projects;
|
|
25669
25804
|
try {
|
|
25670
|
-
const res = await fetchWithTimeout(`${
|
|
25805
|
+
const res = await fetchWithTimeout(`${base4}${fk.trackedPath}`, { headers: authHeaders });
|
|
25671
25806
|
if (!res.ok) continue;
|
|
25672
25807
|
projects = (await res.json()).projects || [];
|
|
25673
25808
|
} catch {
|
|
@@ -25678,7 +25813,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
25678
25813
|
if (!existsSync51(file)) continue;
|
|
25679
25814
|
let mtime;
|
|
25680
25815
|
try {
|
|
25681
|
-
mtime =
|
|
25816
|
+
mtime = statSync43(file).mtimeMs;
|
|
25682
25817
|
} catch {
|
|
25683
25818
|
continue;
|
|
25684
25819
|
}
|
|
@@ -25695,7 +25830,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
25695
25830
|
continue;
|
|
25696
25831
|
}
|
|
25697
25832
|
try {
|
|
25698
|
-
const res = await fetchWithTimeout(`${
|
|
25833
|
+
const res = await fetchWithTimeout(`${base4}${fk.statusPath}`, {
|
|
25699
25834
|
method: "POST",
|
|
25700
25835
|
headers: { "content-type": "application/json", ...authHeaders },
|
|
25701
25836
|
body: JSON.stringify({ project, items: changed.map((t) => ({ id: t.id, status: t.intent })) })
|
|
@@ -25821,16 +25956,16 @@ ${block}`);
|
|
|
25821
25956
|
return { status: "error", result: JSON.stringify({ error: e instanceof Error ? e.message : "failed" }) };
|
|
25822
25957
|
}
|
|
25823
25958
|
}
|
|
25824
|
-
async function runPull(
|
|
25959
|
+
async function runPull(base4, opts) {
|
|
25825
25960
|
const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
|
|
25826
25961
|
const { hostname: hostname3 } = await import("node:os");
|
|
25827
|
-
const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") ===
|
|
25962
|
+
const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base4.replace(/\/+$/, ""));
|
|
25828
25963
|
const headers = creds?.token ? { authorization: `Bearer ${creds.token}` } : {};
|
|
25829
25964
|
const wanted = opts.types?.length ? opts.types : ["mcp", "skill", "command", "agent", "instructions"];
|
|
25830
25965
|
const rows = [];
|
|
25831
25966
|
for (const type of wanted) {
|
|
25832
25967
|
try {
|
|
25833
|
-
const res = await fetchWithTimeout(`${
|
|
25968
|
+
const res = await fetchWithTimeout(`${base4}/api/toolkit/browse/${type}?limit=1000`, { headers });
|
|
25834
25969
|
if (!res.ok) continue;
|
|
25835
25970
|
const body2 = await res.json();
|
|
25836
25971
|
for (const it of body2.items || []) rows.push(it);
|
|
@@ -25899,20 +26034,20 @@ async function runIntent(intent, ctx) {
|
|
|
25899
26034
|
async function drainSyncIntents(opts = {}) {
|
|
25900
26035
|
const out = { processed: 0, done: 0, errored: 0 };
|
|
25901
26036
|
for (const cred of loadAllCredentials()) {
|
|
25902
|
-
const
|
|
26037
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
25903
26038
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
25904
26039
|
let pending;
|
|
25905
26040
|
try {
|
|
25906
|
-
const res = await fetchWithTimeout(`${
|
|
26041
|
+
const res = await fetchWithTimeout(`${base4}/api/sync-intents/pending`, { headers: authHeaders });
|
|
25907
26042
|
if (!res.ok) continue;
|
|
25908
26043
|
const data = await res.json();
|
|
25909
26044
|
pending = data.intents || [];
|
|
25910
26045
|
if (data.cli && data.cli.version) {
|
|
25911
26046
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
25912
|
-
const plan = planAutoUpdate2(
|
|
26047
|
+
const plan = planAutoUpdate2(base4, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
25913
26048
|
sweepStaleStaging2();
|
|
25914
26049
|
if (plan.update) {
|
|
25915
|
-
void runAutoUpdate2(
|
|
26050
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion2).catch(() => {
|
|
25916
26051
|
});
|
|
25917
26052
|
}
|
|
25918
26053
|
}
|
|
@@ -25920,7 +26055,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25920
26055
|
continue;
|
|
25921
26056
|
}
|
|
25922
26057
|
for (const intent of pending) {
|
|
25923
|
-
const { status, result } = await runIntent(intent, { base:
|
|
26058
|
+
const { status, result } = await runIntent(intent, { base: base4 });
|
|
25924
26059
|
out.processed++;
|
|
25925
26060
|
if (status === "done") out.done++;
|
|
25926
26061
|
else out.errored++;
|
|
@@ -25929,7 +26064,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25929
26064
|
console.error(`[sync-intent] ${label}: ${status}`);
|
|
25930
26065
|
}
|
|
25931
26066
|
try {
|
|
25932
|
-
await fetchWithTimeout(`${
|
|
26067
|
+
await fetchWithTimeout(`${base4}/api/sync-intents/${intent.id}/ack`, {
|
|
25933
26068
|
method: "POST",
|
|
25934
26069
|
headers: { "content-type": "application/json", ...authHeaders },
|
|
25935
26070
|
body: JSON.stringify({ status, result })
|
|
@@ -25938,7 +26073,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25938
26073
|
}
|
|
25939
26074
|
}
|
|
25940
26075
|
try {
|
|
25941
|
-
await pushProjectTaskStatuses(
|
|
26076
|
+
await pushProjectTaskStatuses(base4, authHeaders, { verbose: opts.verbose });
|
|
25942
26077
|
} catch {
|
|
25943
26078
|
}
|
|
25944
26079
|
}
|
|
@@ -25978,7 +26113,7 @@ var init_intent_drain = __esm({
|
|
|
25978
26113
|
ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25979
26114
|
} catch {
|
|
25980
26115
|
try {
|
|
25981
|
-
if (true) ownVersion2 = "0.7.
|
|
26116
|
+
if (true) ownVersion2 = "0.7.16";
|
|
25982
26117
|
} catch {
|
|
25983
26118
|
}
|
|
25984
26119
|
}
|
|
@@ -26179,7 +26314,7 @@ var team_merge_exports = {};
|
|
|
26179
26314
|
__export(team_merge_exports, {
|
|
26180
26315
|
mergePullResult: () => mergePullResult
|
|
26181
26316
|
});
|
|
26182
|
-
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as
|
|
26317
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync44 } from "fs";
|
|
26183
26318
|
import { dirname as dirname27, join as join57 } from "path";
|
|
26184
26319
|
import { createHash as createHash14 } from "crypto";
|
|
26185
26320
|
function installPathFor(art, tool) {
|
|
@@ -26316,7 +26451,7 @@ function readFileIfExists(path) {
|
|
|
26316
26451
|
try {
|
|
26317
26452
|
if (!existsSync53(path))
|
|
26318
26453
|
return null;
|
|
26319
|
-
const s =
|
|
26454
|
+
const s = statSync44(path);
|
|
26320
26455
|
if (!s.isFile())
|
|
26321
26456
|
return null;
|
|
26322
26457
|
return readFileSync46(path);
|
|
@@ -26400,7 +26535,7 @@ var init_vault_crypto = __esm({
|
|
|
26400
26535
|
});
|
|
26401
26536
|
|
|
26402
26537
|
// ../engine/dist/core/vault-source.js
|
|
26403
|
-
import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as
|
|
26538
|
+
import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as statSync45 } from "fs";
|
|
26404
26539
|
import { extname, join as join58 } from "path";
|
|
26405
26540
|
function* walkVaultSources() {
|
|
26406
26541
|
const t = loadSettings().team.vault;
|
|
@@ -26416,7 +26551,7 @@ function* walkVaultSources() {
|
|
|
26416
26551
|
}
|
|
26417
26552
|
function safeStat(path) {
|
|
26418
26553
|
try {
|
|
26419
|
-
const s =
|
|
26554
|
+
const s = statSync45(path);
|
|
26420
26555
|
if (!s.isFile())
|
|
26421
26556
|
return null;
|
|
26422
26557
|
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
@@ -26501,8 +26636,8 @@ function* walkCodex() {
|
|
|
26501
26636
|
const s = safeStat(path);
|
|
26502
26637
|
if (!s)
|
|
26503
26638
|
continue;
|
|
26504
|
-
const
|
|
26505
|
-
yield { sessionId: `codex_${
|
|
26639
|
+
const base4 = e.name.replace(/\.jsonl$/, "");
|
|
26640
|
+
yield { sessionId: `codex_${base4}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
|
|
26506
26641
|
}
|
|
26507
26642
|
}
|
|
26508
26643
|
}
|
|
@@ -26519,7 +26654,7 @@ function* walkAgy() {
|
|
|
26519
26654
|
for (const rawId of sessionDirs) {
|
|
26520
26655
|
const sessionPath = join58(brainDir, rawId);
|
|
26521
26656
|
try {
|
|
26522
|
-
if (!
|
|
26657
|
+
if (!statSync45(sessionPath).isDirectory())
|
|
26523
26658
|
continue;
|
|
26524
26659
|
} catch {
|
|
26525
26660
|
continue;
|
|
@@ -26644,11 +26779,11 @@ function vaultEnable(passphrase, opts = {}) {
|
|
|
26644
26779
|
}
|
|
26645
26780
|
async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
|
|
26646
26781
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
26647
|
-
const
|
|
26782
|
+
const base4 = remote.baseUrl.replace(/\/+$/, "");
|
|
26648
26783
|
const headers = { authorization: `Bearer ${remote.token}`, "content-type": "application/json" };
|
|
26649
26784
|
let published = { saltHex: null, keyId: null };
|
|
26650
26785
|
try {
|
|
26651
|
-
const r = await doFetch(`${
|
|
26786
|
+
const r = await doFetch(`${base4}/api/vault/salt`, { headers });
|
|
26652
26787
|
if (r.ok)
|
|
26653
26788
|
published = await r.json();
|
|
26654
26789
|
} catch {
|
|
@@ -26662,7 +26797,7 @@ async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
|
|
|
26662
26797
|
}
|
|
26663
26798
|
const result = vaultEnable(passphrase);
|
|
26664
26799
|
try {
|
|
26665
|
-
const r = await doFetch(`${
|
|
26800
|
+
const r = await doFetch(`${base4}/api/vault/salt`, {
|
|
26666
26801
|
method: "POST",
|
|
26667
26802
|
headers,
|
|
26668
26803
|
body: JSON.stringify({ saltHex: result.saltHex, keyId: result.keyId })
|
|
@@ -26755,8 +26890,8 @@ async function vaultSync(passphrase) {
|
|
|
26755
26890
|
return report;
|
|
26756
26891
|
}
|
|
26757
26892
|
function importedPathFor(tool, sessionId) {
|
|
26758
|
-
const
|
|
26759
|
-
return join59(IMPORTED_ROOT(), tool, `${
|
|
26893
|
+
const base4 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
|
|
26894
|
+
return join59(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
|
|
26760
26895
|
}
|
|
26761
26896
|
async function vaultRestore(passphrase, opts = {}) {
|
|
26762
26897
|
const ctx = context2();
|
|
@@ -27157,8 +27292,8 @@ var init_decision_guard = __esm({
|
|
|
27157
27292
|
isManifest = (p) => {
|
|
27158
27293
|
if (!p)
|
|
27159
27294
|
return false;
|
|
27160
|
-
const
|
|
27161
|
-
return MANIFESTS.includes(
|
|
27295
|
+
const base4 = p.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
27296
|
+
return MANIFESTS.includes(base4);
|
|
27162
27297
|
};
|
|
27163
27298
|
}
|
|
27164
27299
|
});
|
|
@@ -27756,8 +27891,8 @@ async function deviceLogin(opts, onPrompt) {
|
|
|
27756
27891
|
throw new Error("device-auth timed out \u2014 the code expired before approval.");
|
|
27757
27892
|
}
|
|
27758
27893
|
async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
27759
|
-
const
|
|
27760
|
-
const startRes = await fetch(`${
|
|
27894
|
+
const base4 = serverUrl.replace(/\/+$/, "");
|
|
27895
|
+
const startRes = await fetch(`${base4}/api/auth/device/code`, {
|
|
27761
27896
|
method: "POST",
|
|
27762
27897
|
headers: { "content-type": "application/json" },
|
|
27763
27898
|
body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID })
|
|
@@ -27765,7 +27900,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
|
27765
27900
|
if (!startRes.ok) {
|
|
27766
27901
|
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
|
|
27767
27902
|
}
|
|
27768
|
-
const start = await readDeviceCode(startRes,
|
|
27903
|
+
const start = await readDeviceCode(startRes, base4);
|
|
27769
27904
|
onPrompt({
|
|
27770
27905
|
url: start.verification_uri_complete || start.verification_uri,
|
|
27771
27906
|
userCode: start.user_code,
|
|
@@ -27775,7 +27910,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
|
27775
27910
|
let interval = (start.interval ?? 5) * 1e3;
|
|
27776
27911
|
while (Date.now() < deadline) {
|
|
27777
27912
|
await sleep(interval);
|
|
27778
|
-
const res = await fetch(`${
|
|
27913
|
+
const res = await fetch(`${base4}/api/auth/device/token`, {
|
|
27779
27914
|
method: "POST",
|
|
27780
27915
|
headers: { "content-type": "application/json" },
|
|
27781
27916
|
body: JSON.stringify({
|
|
@@ -27877,7 +28012,7 @@ async function selectSessions(target, opts, fetchImpl = fetch) {
|
|
|
27877
28012
|
const qs = new URLSearchParams({ limit: String(PAGE), offset: String(offset) });
|
|
27878
28013
|
if (opts.project) qs.set("project", opts.project);
|
|
27879
28014
|
if (opts.tool) qs.set("tool", opts.tool);
|
|
27880
|
-
const res = await fetchImpl(`${
|
|
28015
|
+
const res = await fetchImpl(`${base3(target.serverUrl)}/api/conversations/recent?${qs}`, {
|
|
27881
28016
|
headers: { authorization: `Bearer ${target.token}` }
|
|
27882
28017
|
});
|
|
27883
28018
|
if (!res.ok) throw new Error(`list failed: HTTP ${res.status}`);
|
|
@@ -27906,7 +28041,7 @@ async function bulkDelete(ids, opts = {}) {
|
|
|
27906
28041
|
try {
|
|
27907
28042
|
for (let i = 0; i < unique.length; i += BATCH) {
|
|
27908
28043
|
const slice = unique.slice(i, i + BATCH);
|
|
27909
|
-
const res = await fetchImpl(`${
|
|
28044
|
+
const res = await fetchImpl(`${base3(t.serverUrl)}/api/sync`, {
|
|
27910
28045
|
method: "POST",
|
|
27911
28046
|
headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
|
|
27912
28047
|
body: JSON.stringify({
|
|
@@ -27942,7 +28077,7 @@ async function bulkDelete(ids, opts = {}) {
|
|
|
27942
28077
|
return out;
|
|
27943
28078
|
}
|
|
27944
28079
|
async function listTombstones(target, limit = 500, fetchImpl = fetch) {
|
|
27945
|
-
const res = await fetchImpl(`${
|
|
28080
|
+
const res = await fetchImpl(`${base3(target.serverUrl)}/api/data/tombstones?limit=${limit}`, {
|
|
27946
28081
|
headers: { authorization: `Bearer ${target.token}` }
|
|
27947
28082
|
});
|
|
27948
28083
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -27959,7 +28094,7 @@ async function restoreSessions(ids, opts = {}) {
|
|
|
27959
28094
|
try {
|
|
27960
28095
|
let restored = 0, notDeleted = 0;
|
|
27961
28096
|
for (let i = 0; i < unique.length; i += BATCH) {
|
|
27962
|
-
const res = await fetchImpl(`${
|
|
28097
|
+
const res = await fetchImpl(`${base3(t.serverUrl)}/api/data/restore`, {
|
|
27963
28098
|
method: "POST",
|
|
27964
28099
|
headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
|
|
27965
28100
|
body: JSON.stringify({ session_ids: unique.slice(i, i + BATCH) })
|
|
@@ -27982,7 +28117,7 @@ function needsConfirmation(opts) {
|
|
|
27982
28117
|
const enumeratedByCaller = opts.argvIds > 0 && !opts.project && !opts.stdin;
|
|
27983
28118
|
return !enumeratedByCaller;
|
|
27984
28119
|
}
|
|
27985
|
-
var PAGE, BATCH,
|
|
28120
|
+
var PAGE, BATCH, base3;
|
|
27986
28121
|
var init_bulk_delete = __esm({
|
|
27987
28122
|
"src/bulk-delete.ts"() {
|
|
27988
28123
|
"use strict";
|
|
@@ -27990,7 +28125,7 @@ var init_bulk_delete = __esm({
|
|
|
27990
28125
|
init_sync_ledger();
|
|
27991
28126
|
PAGE = 200;
|
|
27992
28127
|
BATCH = 250;
|
|
27993
|
-
|
|
28128
|
+
base3 = (url) => url.replace(/\/+$/, "");
|
|
27994
28129
|
}
|
|
27995
28130
|
});
|
|
27996
28131
|
|
|
@@ -28355,13 +28490,13 @@ function requireTarget() {
|
|
|
28355
28490
|
}
|
|
28356
28491
|
return t;
|
|
28357
28492
|
}
|
|
28358
|
-
async function readJsonBody(res,
|
|
28493
|
+
async function readJsonBody(res, base4, what) {
|
|
28359
28494
|
const text = await res.text().catch(() => "");
|
|
28360
28495
|
try {
|
|
28361
28496
|
return JSON.parse(text);
|
|
28362
28497
|
} catch {
|
|
28363
28498
|
throw new Error(
|
|
28364
|
-
`${
|
|
28499
|
+
`${base4} answered ${res.status} with something that is not JSON (${JSON.stringify(text.trim().slice(0, 60))}) for ${what} \u2014 a proxy, a captive portal or a host that no longer serves the chat-recall API`
|
|
28365
28500
|
);
|
|
28366
28501
|
}
|
|
28367
28502
|
}
|
|
@@ -29167,9 +29302,9 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
29167
29302
|
return;
|
|
29168
29303
|
}
|
|
29169
29304
|
const target = firstTarget();
|
|
29170
|
-
const
|
|
29305
|
+
const base4 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
|
|
29171
29306
|
try {
|
|
29172
|
-
const res = await fetch(`${
|
|
29307
|
+
const res = await fetch(`${base4}/api/contact/feedback`, {
|
|
29173
29308
|
method: "POST",
|
|
29174
29309
|
headers: {
|
|
29175
29310
|
"content-type": "application/json",
|
|
@@ -29196,14 +29331,14 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
29196
29331
|
console.log(chalk2.dim(" Add --email <address> next time if you want a reply."));
|
|
29197
29332
|
}
|
|
29198
29333
|
} catch (err) {
|
|
29199
|
-
console.error(chalk2.red(`Could not reach ${
|
|
29334
|
+
console.error(chalk2.red(`Could not reach ${base4}.`));
|
|
29200
29335
|
console.error(chalk2.dim(" Your message, so it is not lost:"));
|
|
29201
29336
|
console.error(chalk2.dim(` ${message}`));
|
|
29202
29337
|
process.exitCode = 1;
|
|
29203
29338
|
}
|
|
29204
29339
|
});
|
|
29205
29340
|
program.command("doctor").description("Quick health check across login, server, credentials, hooks, MCP server, and codeindex").option("--purge-local", "Remove the legacy local index (cache.db + vector/KG files) left by pre-thin-collector versions \u2014 storage is server-side now. Requires login; keeps credentials, sync ledger, identity, and the audit log.").action(async (opts) => {
|
|
29206
|
-
const { existsSync: existsSync58, readFileSync: readFileSync50, statSync:
|
|
29341
|
+
const { existsSync: existsSync58, readFileSync: readFileSync50, statSync: statSync46 } = await import("fs");
|
|
29207
29342
|
const { execSync: execSync6 } = await import("child_process");
|
|
29208
29343
|
const rows = [];
|
|
29209
29344
|
const note = (ok, label, detail) => rows.push({ ok, label, detail });
|
|
@@ -29271,7 +29406,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
29271
29406
|
note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
|
|
29272
29407
|
} else {
|
|
29273
29408
|
try {
|
|
29274
|
-
const mode =
|
|
29409
|
+
const mode = statSync46(credFile).mode & 511;
|
|
29275
29410
|
const isWin = process.platform === "win32";
|
|
29276
29411
|
const safe = isWin || (mode & 63) === 0;
|
|
29277
29412
|
note(safe, "Credentials file", isWin ? `${credFile} (Windows ACL \u2014 POSIX mode does not apply)` : `${credFile} (mode ${mode.toString(8).padStart(3, "0")}${safe ? "" : " \u2014 should be 600"})`);
|
|
@@ -29801,13 +29936,13 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
|
|
|
29801
29936
|
}
|
|
29802
29937
|
const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
29803
29938
|
for (const t of targets) {
|
|
29804
|
-
const
|
|
29939
|
+
const base4 = t.serverUrl.replace(/\/+$/, "");
|
|
29805
29940
|
const headers = {};
|
|
29806
29941
|
if (t.token) headers.authorization = `Bearer ${t.token}`;
|
|
29807
29942
|
let deleted = 0, missing = 0, failed = 0;
|
|
29808
29943
|
for (const id of ids) {
|
|
29809
29944
|
try {
|
|
29810
|
-
const res = await fetchWithTimeout2(`${
|
|
29945
|
+
const res = await fetchWithTimeout2(`${base4}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
|
|
29811
29946
|
if (res.ok) deleted++;
|
|
29812
29947
|
else if (res.status === 404) missing++;
|
|
29813
29948
|
else failed++;
|
|
@@ -29815,7 +29950,7 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
|
|
|
29815
29950
|
failed++;
|
|
29816
29951
|
}
|
|
29817
29952
|
}
|
|
29818
|
-
const line = ` ${
|
|
29953
|
+
const line = ` ${base4}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
|
|
29819
29954
|
console.log(failed ? chalk2.yellow(line) : chalk2.green(line));
|
|
29820
29955
|
if (failed) process.exitCode = 1;
|
|
29821
29956
|
}
|
|
@@ -30597,7 +30732,7 @@ program.command("task-hook").description("Move a board card when a prompt or a c
|
|
|
30597
30732
|
}
|
|
30598
30733
|
});
|
|
30599
30734
|
program.command("install-hooks").description("Install Claude Code hooks (Stop + PreCompact + UserPromptSubmit + SessionEnd + SessionStart) into every Claude profile").option("--uninstall", "Remove hooks instead of installing them").option("--no-resume-hint", "Don't install the UserPromptSubmit resume-hint hook").option("--no-escalate", "Don't install the SessionEnd learnings-escalation hook").option("--no-wakeup", "Don't install the SessionStart wake-up hook").option("--no-guard", "Don't install the PreToolUse decision guard").option("--no-task-board", "Don't install the hooks that claim and close board cards").action(async (opts) => {
|
|
30600
|
-
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync:
|
|
30735
|
+
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync46, unlinkSync: unlinkSync4 } = await import("fs");
|
|
30601
30736
|
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
30602
30737
|
const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
|
|
30603
30738
|
const here = fileURLToPath6(new URL(".", import.meta.url));
|
|
@@ -30812,18 +30947,18 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30812
30947
|
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
30813
30948
|
}
|
|
30814
30949
|
if (installGuard) await addGuardToOtherTools(installedGuardHook);
|
|
30815
|
-
const saveSz =
|
|
30950
|
+
const saveSz = statSync46(installedSaveHook).size;
|
|
30816
30951
|
console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
|
|
30817
30952
|
if (installResume) {
|
|
30818
|
-
const resumeSz =
|
|
30953
|
+
const resumeSz = statSync46(installedResumeHook).size;
|
|
30819
30954
|
console.log(chalk2.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
|
|
30820
30955
|
}
|
|
30821
30956
|
if (installEscalate) {
|
|
30822
|
-
const escalateSz =
|
|
30957
|
+
const escalateSz = statSync46(installedEscalateHook).size;
|
|
30823
30958
|
console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
|
|
30824
30959
|
}
|
|
30825
30960
|
if (installGuard) {
|
|
30826
|
-
const guardSz =
|
|
30961
|
+
const guardSz = statSync46(installedGuardHook).size;
|
|
30827
30962
|
console.log(chalk2.green(`\u2713 Installed chat-recall decision guard (${guardSz} bytes)`));
|
|
30828
30963
|
}
|
|
30829
30964
|
if (installTaskBoard) {
|
|
@@ -30831,7 +30966,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30831
30966
|
console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
|
|
30832
30967
|
}
|
|
30833
30968
|
if (installWakeup) {
|
|
30834
|
-
const wakeupSz =
|
|
30969
|
+
const wakeupSz = statSync46(installedWakeupHook).size;
|
|
30835
30970
|
console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
|
|
30836
30971
|
for (const f of wakeupSkipped) {
|
|
30837
30972
|
console.log(chalk2.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));
|
|
@@ -30948,10 +31083,10 @@ program.command("service <action>").description("Manage the per-user background
|
|
|
30948
31083
|
});
|
|
30949
31084
|
async function runLogin(serverUrl, opts) {
|
|
30950
31085
|
const { saveCredentials: saveCredentials2, loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
30951
|
-
const
|
|
31086
|
+
const base4 = serverUrl.replace(/\/+$/, "");
|
|
30952
31087
|
const verifyToken = async (token) => {
|
|
30953
31088
|
try {
|
|
30954
|
-
const res = await fetch(`${
|
|
31089
|
+
const res = await fetch(`${base4}/api/status`, {
|
|
30955
31090
|
headers: token ? { authorization: `Bearer ${token}` } : {},
|
|
30956
31091
|
signal: AbortSignal.timeout(1e4)
|
|
30957
31092
|
});
|
|
@@ -30967,36 +31102,36 @@ async function runLogin(serverUrl, opts) {
|
|
|
30967
31102
|
return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
|
|
30968
31103
|
}
|
|
30969
31104
|
const { probeServer: identifyServer, probeOk: identified, probeAdvice: identifyAdvice } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
30970
|
-
const who = await identifyServer(
|
|
30971
|
-
if (!identified(who)) return failLogin(opts, identifyAdvice(who,
|
|
31105
|
+
const who = await identifyServer(base4, { timeoutMs: 1e4 });
|
|
31106
|
+
if (!identified(who)) return failLogin(opts, identifyAdvice(who, base4));
|
|
30972
31107
|
const check = await verifyToken(token);
|
|
30973
31108
|
if (!check.ok) {
|
|
30974
31109
|
if (check.status === 401 || check.status === 403) {
|
|
30975
31110
|
console.error(chalk2.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
|
|
30976
|
-
return failLogin(opts, `${
|
|
31111
|
+
return failLogin(opts, `${base4} rejected the token (HTTP ${check.status})`);
|
|
30977
31112
|
}
|
|
30978
31113
|
if (check.status !== void 0 && check.status >= 500) {
|
|
30979
|
-
return failLogin(opts, `${
|
|
31114
|
+
return failLogin(opts, `${base4} answered HTTP ${check.status} \u2014 the server is up but failing. The token was never checked. Nothing to fix locally.`);
|
|
30980
31115
|
}
|
|
30981
31116
|
if (check.status === 404 || check.status === 410) {
|
|
30982
|
-
return failLogin(opts, `${
|
|
31117
|
+
return failLogin(opts, `${base4} does not serve the chat-recall API (HTTP ${check.status}). If the server moved, log in to its new URL.`);
|
|
30983
31118
|
}
|
|
30984
|
-
return failLogin(opts, `could not verify the token against ${
|
|
31119
|
+
return failLogin(opts, `could not verify the token against ${base4}: ${check.error || `HTTP ${check.status}`}`);
|
|
30985
31120
|
}
|
|
30986
31121
|
saveCredentials2({ serverUrl, token });
|
|
30987
31122
|
console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` server: ${serverUrl} (token verified)`));
|
|
30988
31123
|
return;
|
|
30989
31124
|
}
|
|
30990
|
-
const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") ===
|
|
31125
|
+
const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base4);
|
|
30991
31126
|
if (existing && (await verifyToken(existing.token)).ok) {
|
|
30992
31127
|
console.log(chalk2.green("\u2713 Already connected.") + chalk2.dim(` server: ${serverUrl} (existing token verified)`));
|
|
30993
31128
|
return;
|
|
30994
31129
|
}
|
|
30995
31130
|
try {
|
|
30996
31131
|
const { probeServer: probeServer2, probeOk: probeOk2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
30997
|
-
const identified = await probeServer2(
|
|
31132
|
+
const identified = await probeServer2(base4, { timeoutMs: 8e3 });
|
|
30998
31133
|
if (probeOk2(identified)) {
|
|
30999
|
-
const probe = await fetch(`${
|
|
31134
|
+
const probe = await fetch(`${base4}/api/status`, { signal: AbortSignal.timeout(8e3) });
|
|
31000
31135
|
if (probe.status === 200) {
|
|
31001
31136
|
saveCredentials2({ serverUrl, token: "" });
|
|
31002
31137
|
console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
|
|
@@ -31017,16 +31152,16 @@ async function runLogin(serverUrl, opts) {
|
|
|
31017
31152
|
if (!issuer) {
|
|
31018
31153
|
const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
31019
31154
|
for (let attempt = 1; attempt <= 3 && !authProvider; attempt++) {
|
|
31020
|
-
const probe = await probeServer2(
|
|
31155
|
+
const probe = await probeServer2(base4, { timeoutMs: 15e3 });
|
|
31021
31156
|
if (probeOk2(probe)) {
|
|
31022
31157
|
const caps = probe.raw;
|
|
31023
31158
|
authProvider = caps?.authProvider;
|
|
31024
31159
|
if (caps?.oidcIssuer) issuer = caps.oidcIssuer;
|
|
31025
31160
|
capsError = null;
|
|
31026
31161
|
} else if (probe.kind === "moved" || probe.kind === "not_api") {
|
|
31027
|
-
return failLogin(opts, probeAdvice2(probe,
|
|
31162
|
+
return failLogin(opts, probeAdvice2(probe, base4));
|
|
31028
31163
|
} else {
|
|
31029
|
-
capsError = probeAdvice2(probe,
|
|
31164
|
+
capsError = probeAdvice2(probe, base4);
|
|
31030
31165
|
}
|
|
31031
31166
|
if (!authProvider && attempt < 3) await new Promise((r) => setTimeout(r, attempt * 1e3));
|
|
31032
31167
|
}
|
|
@@ -31035,7 +31170,7 @@ async function runLogin(serverUrl, opts) {
|
|
|
31035
31170
|
authProvider = "better-auth";
|
|
31036
31171
|
if (capsError) {
|
|
31037
31172
|
console.log(` ${chalk2.yellow("Could not read the server's capabilities")} (${capsError}) \u2014 assuming the standard sign-in flow.`);
|
|
31038
|
-
void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(
|
|
31173
|
+
void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base4, {
|
|
31039
31174
|
step: "cli_login_probe",
|
|
31040
31175
|
reason: capsError?.slice(0, 40) ?? null,
|
|
31041
31176
|
message: capsError
|
|
@@ -31057,20 +31192,20 @@ async function runLogin(serverUrl, opts) {
|
|
|
31057
31192
|
console.log(chalk2.dim(` If the page asks for a code: ${chalk2.bold(p.userCode)}`));
|
|
31058
31193
|
console.log(chalk2.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
|
|
31059
31194
|
};
|
|
31060
|
-
const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(
|
|
31195
|
+
const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base4, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
|
|
31061
31196
|
const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };
|
|
31062
|
-
const meRes = await fetch(`${
|
|
31063
|
-
if (!meRes.ok) throw new Error(`${
|
|
31064
|
-
const me = await readJsonBody(meRes,
|
|
31197
|
+
const meRes = await fetch(`${base4}/api/me`, { headers: authHdr });
|
|
31198
|
+
if (!meRes.ok) throw new Error(`${base4}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
|
|
31199
|
+
const me = await readJsonBody(meRes, base4, "GET /api/me");
|
|
31065
31200
|
const teams = me.teams || [];
|
|
31066
31201
|
let slug = opts.team;
|
|
31067
31202
|
if (!slug) {
|
|
31068
31203
|
if (teams.length === 1) slug = teams[0].team_slug;
|
|
31069
31204
|
else if (teams.length === 0) {
|
|
31070
31205
|
const name = me.user?.email?.split("@")[0]?.replace(/[^a-z0-9]/gi, "") || "workspace";
|
|
31071
|
-
const created = await fetch(`${
|
|
31206
|
+
const created = await fetch(`${base4}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
|
|
31072
31207
|
if (!created.ok) throw new Error(`workspace create failed: HTTP ${created.status} ${await created.text().catch(() => "")}`);
|
|
31073
|
-
slug = (await readJsonBody(created,
|
|
31208
|
+
slug = (await readJsonBody(created, base4, "POST /api/teams")).slug;
|
|
31074
31209
|
console.log(chalk2.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
|
|
31075
31210
|
} else {
|
|
31076
31211
|
teams.forEach((t) => console.error(` ${t.team_slug} ${chalk2.dim(t.name)}`));
|
|
@@ -31078,13 +31213,13 @@ async function runLogin(serverUrl, opts) {
|
|
|
31078
31213
|
}
|
|
31079
31214
|
}
|
|
31080
31215
|
const deviceId = opts.deviceId || (await import("node:os")).hostname();
|
|
31081
|
-
const mint = await fetch(`${
|
|
31216
|
+
const mint = await fetch(`${base4}/api/teams/${encodeURIComponent(slug)}/tokens`, {
|
|
31082
31217
|
method: "POST",
|
|
31083
31218
|
headers: authHdr,
|
|
31084
31219
|
body: JSON.stringify({ device_id: deviceId })
|
|
31085
31220
|
});
|
|
31086
31221
|
if (!mint.ok) throw new Error(`token mint failed: HTTP ${mint.status} ${await mint.text().catch(() => "")}`);
|
|
31087
|
-
const { token } = await readJsonBody(mint,
|
|
31222
|
+
const { token } = await readJsonBody(mint, base4, "POST /api/teams/:slug/tokens");
|
|
31088
31223
|
saveCredentials2({ serverUrl, token });
|
|
31089
31224
|
console.log(chalk2.green(`\u2713 Logged in to ${chalk2.bold(slug)}`) + chalk2.dim(` (device: ${deviceId}) server: ${serverUrl}`));
|
|
31090
31225
|
if (!opts.fromInit) {
|
|
@@ -31561,16 +31696,16 @@ program.command("update").description("Update this CLI now from the server you s
|
|
|
31561
31696
|
}
|
|
31562
31697
|
let installFailed = false;
|
|
31563
31698
|
for (const cred of targets) {
|
|
31564
|
-
const
|
|
31699
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
31565
31700
|
const headers = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
31566
|
-
const r = await runAutoUpdate2(
|
|
31701
|
+
const r = await runAutoUpdate2(base4, headers, cliVersion3());
|
|
31567
31702
|
if (r.updated) {
|
|
31568
|
-
console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${
|
|
31703
|
+
console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base4})`));
|
|
31569
31704
|
continue;
|
|
31570
31705
|
}
|
|
31571
31706
|
const benign = /already current|no CLI release|disabled/.test(r.reason);
|
|
31572
31707
|
if (/install failed|download failed|checksum/.test(r.reason)) installFailed = true;
|
|
31573
|
-
console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${
|
|
31708
|
+
console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base4})`));
|
|
31574
31709
|
}
|
|
31575
31710
|
const check = await refreshUpdateCheck2(true);
|
|
31576
31711
|
if (installFailed) {
|