chat-recall 0.7.13 → 0.7.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +371 -307
- package/dist/hostile-server.d.ts.map +1 -1
- package/dist/hostile-server.js +10 -1
- package/dist/hostile-server.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +278 -214
- package/dist/scan-worker.js +23 -24
- package/dist/sync-ledger.d.ts.map +1 -1
- package/dist/sync-ledger.js +112 -17
- package/dist/sync-ledger.js.map +1 -1
- package/dist/watch.js +320 -254
- package/package.json +1 -1
package/dist/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);
|
|
@@ -10956,12 +10956,14 @@ function lineKey(line) {
|
|
|
10956
10956
|
try {
|
|
10957
10957
|
const o = JSON.parse(line);
|
|
10958
10958
|
if (typeof o?.uuid === "string" && o.uuid)
|
|
10959
|
-
return { key: `u:${o.uuid}`,
|
|
10960
|
-
|
|
10961
|
-
return { key: `m:${o.type}`, singleton: true };
|
|
10959
|
+
return { key: `u:${o.uuid}`, meta: false, json: true };
|
|
10960
|
+
return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
|
|
10962
10961
|
} catch {
|
|
10963
10962
|
}
|
|
10964
|
-
return { key: `h:${sha1(line)}`,
|
|
10963
|
+
return { key: `h:${sha1(line)}`, meta: false, json: false };
|
|
10964
|
+
}
|
|
10965
|
+
function isHalfWritten(line, currentLines) {
|
|
10966
|
+
return currentLines.some((c) => c.length > line.length && c.startsWith(line));
|
|
10965
10967
|
}
|
|
10966
10968
|
function mergeLineText(shadowText, currentText) {
|
|
10967
10969
|
const shadowLines = shadowText.split("\n").filter((l) => l.trim());
|
|
@@ -10969,27 +10971,24 @@ function mergeLineText(shadowText, currentText) {
|
|
|
10969
10971
|
const currentKeys = /* @__PURE__ */ new Set();
|
|
10970
10972
|
for (const l of currentLines)
|
|
10971
10973
|
currentKeys.add(lineKey(l).key);
|
|
10972
|
-
const
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
let recovered = 0;
|
|
10976
|
-
for (const k of shadowKeys)
|
|
10977
|
-
if (!currentKeys.has(k))
|
|
10978
|
-
recovered++;
|
|
10979
|
-
if (recovered === 0) {
|
|
10980
|
-
return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
|
|
10981
|
-
}
|
|
10982
|
-
const out = [];
|
|
10983
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
10974
|
+
const kept = [];
|
|
10975
|
+
const keptKeys = /* @__PURE__ */ new Set();
|
|
10976
|
+
const counted = /* @__PURE__ */ new Set();
|
|
10984
10977
|
for (const l of shadowLines) {
|
|
10985
|
-
const { key: key2,
|
|
10986
|
-
if (
|
|
10987
|
-
continue;
|
|
10988
|
-
if (emitted.has(key2))
|
|
10978
|
+
const { key: key2, meta, json } = lineKey(l);
|
|
10979
|
+
if (!json && isHalfWritten(l, currentLines))
|
|
10989
10980
|
continue;
|
|
10990
|
-
|
|
10991
|
-
|
|
10981
|
+
kept.push(l);
|
|
10982
|
+
keptKeys.add(key2);
|
|
10983
|
+
if (!meta && !currentKeys.has(key2))
|
|
10984
|
+
counted.add(key2);
|
|
10985
|
+
}
|
|
10986
|
+
const recovered = counted.size;
|
|
10987
|
+
if (recovered === 0) {
|
|
10988
|
+
return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
|
|
10992
10989
|
}
|
|
10990
|
+
const out = [...kept];
|
|
10991
|
+
const emitted = keptKeys;
|
|
10993
10992
|
for (const l of currentLines) {
|
|
10994
10993
|
const { key: key2 } = lineKey(l);
|
|
10995
10994
|
if (emitted.has(key2))
|
|
@@ -11174,14 +11173,14 @@ function shadowSizeOnDisk(tool, rawId) {
|
|
|
11174
11173
|
return 0;
|
|
11175
11174
|
}
|
|
11176
11175
|
}
|
|
11177
|
-
var WHOLE_FILE_TOOLS,
|
|
11176
|
+
var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
|
|
11178
11177
|
var init_shadow = __esm({
|
|
11179
11178
|
"../engine/dist/transcript/shadow.js"() {
|
|
11180
11179
|
"use strict";
|
|
11181
11180
|
init_paths();
|
|
11182
11181
|
init_raw();
|
|
11183
11182
|
WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
|
|
11184
|
-
|
|
11183
|
+
STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
|
|
11185
11184
|
lastLiveFingerprint = /* @__PURE__ */ new Map();
|
|
11186
11185
|
}
|
|
11187
11186
|
});
|
|
@@ -12777,8 +12776,8 @@ function _clearServerRulePack() {
|
|
|
12777
12776
|
serverPack = null;
|
|
12778
12777
|
}
|
|
12779
12778
|
function activeRules() {
|
|
12780
|
-
const
|
|
12781
|
-
return serverPack && serverPack.rules.length > 0 ? [...
|
|
12779
|
+
const base4 = settingsView().rules;
|
|
12780
|
+
return serverPack && serverPack.rules.length > 0 ? [...base4, ...serverPack.rules] : base4;
|
|
12782
12781
|
}
|
|
12783
12782
|
function isRedactionEnabled() {
|
|
12784
12783
|
const env = isEnvRedactionRequested();
|
|
@@ -18536,7 +18535,7 @@ var init_extractor_version = __esm({
|
|
|
18536
18535
|
});
|
|
18537
18536
|
|
|
18538
18537
|
// src/sync-ledger.ts
|
|
18539
|
-
import { readFileSync as readFileSync29, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync6 } from "node:fs";
|
|
18538
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync6, statSync as statSync35 } from "node:fs";
|
|
18540
18539
|
import { join as join38, dirname as dirname15 } from "node:path";
|
|
18541
18540
|
function toRow(entry) {
|
|
18542
18541
|
if (entry === void 0) return void 0;
|
|
@@ -18545,32 +18544,94 @@ function toRow(entry) {
|
|
|
18545
18544
|
function ledgerFilePath() {
|
|
18546
18545
|
return ledgerPath();
|
|
18547
18546
|
}
|
|
18548
|
-
function
|
|
18549
|
-
|
|
18547
|
+
function stampOf(path) {
|
|
18548
|
+
try {
|
|
18549
|
+
const st = statSync35(path);
|
|
18550
|
+
return `${st.mtimeMs}:${st.size}`;
|
|
18551
|
+
} catch {
|
|
18552
|
+
return null;
|
|
18553
|
+
}
|
|
18554
|
+
}
|
|
18555
|
+
function readDisk(path) {
|
|
18550
18556
|
try {
|
|
18551
|
-
const parsed = JSON.parse(readFileSync29(
|
|
18552
|
-
|
|
18557
|
+
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
18558
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
18553
18559
|
} catch {
|
|
18554
|
-
|
|
18560
|
+
return {};
|
|
18555
18561
|
}
|
|
18562
|
+
}
|
|
18563
|
+
function snapshot(data) {
|
|
18564
|
+
const out = /* @__PURE__ */ new Map();
|
|
18565
|
+
for (const [server, rows] of Object.entries(data)) {
|
|
18566
|
+
for (const [id, entry] of Object.entries(rows)) out.set(rowKey(server, id), JSON.stringify(entry));
|
|
18567
|
+
}
|
|
18568
|
+
return out;
|
|
18569
|
+
}
|
|
18570
|
+
function mergeOnto(disk) {
|
|
18571
|
+
const out = {};
|
|
18572
|
+
const put = (server, id, entry) => {
|
|
18573
|
+
(out[server] ??= {})[id] = entry;
|
|
18574
|
+
};
|
|
18575
|
+
const mine = cache3 ?? {};
|
|
18576
|
+
for (const [server, rows] of Object.entries(disk)) {
|
|
18577
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
18578
|
+
const k = rowKey(server, id);
|
|
18579
|
+
const own = mine[server]?.[id];
|
|
18580
|
+
const was = base.get(k);
|
|
18581
|
+
if (own === void 0) {
|
|
18582
|
+
if (was === void 0) put(server, id, entry);
|
|
18583
|
+
} else {
|
|
18584
|
+
put(server, id, JSON.stringify(own) === was ? entry : own);
|
|
18585
|
+
}
|
|
18586
|
+
}
|
|
18587
|
+
}
|
|
18588
|
+
for (const [server, rows] of Object.entries(mine)) {
|
|
18589
|
+
for (const [id, entry] of Object.entries(rows)) {
|
|
18590
|
+
if (disk[server]?.[id] !== void 0) continue;
|
|
18591
|
+
if (JSON.stringify(entry) !== base.get(rowKey(server, id))) put(server, id, entry);
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
return out;
|
|
18595
|
+
}
|
|
18596
|
+
function load() {
|
|
18597
|
+
const path = ledgerPath();
|
|
18598
|
+
const stamp = stampOf(path);
|
|
18599
|
+
if (cache3 && stamp === diskStamp) return cache3;
|
|
18600
|
+
const disk = readDisk(path);
|
|
18601
|
+
const merged = cache3 ? mergeOnto(disk) : disk;
|
|
18602
|
+
if (cache3) {
|
|
18603
|
+
for (const server of Object.keys(cache3)) if (!(server in merged)) delete cache3[server];
|
|
18604
|
+
for (const [server, rows] of Object.entries(merged)) {
|
|
18605
|
+
const target = cache3[server] ?? (cache3[server] = {});
|
|
18606
|
+
for (const id of Object.keys(target)) if (!(id in rows)) delete target[id];
|
|
18607
|
+
Object.assign(target, rows);
|
|
18608
|
+
}
|
|
18609
|
+
} else {
|
|
18610
|
+
cache3 = merged;
|
|
18611
|
+
}
|
|
18612
|
+
base = snapshot(disk);
|
|
18613
|
+
diskStamp = stamp;
|
|
18556
18614
|
return cache3;
|
|
18557
18615
|
}
|
|
18558
|
-
function persistNow(
|
|
18616
|
+
function persistNow() {
|
|
18559
18617
|
const path = ledgerPath();
|
|
18560
18618
|
mkdirSync6(dirname15(path), { recursive: true });
|
|
18561
|
-
|
|
18562
|
-
|
|
18619
|
+
load();
|
|
18620
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
18621
|
+
writeFileSync5(tmp, JSON.stringify(cache3 ?? {}));
|
|
18563
18622
|
renameSync4(tmp, path);
|
|
18623
|
+
base = snapshot(cache3 ?? {});
|
|
18624
|
+
diskStamp = stampOf(path);
|
|
18564
18625
|
pendingWrite = false;
|
|
18565
18626
|
}
|
|
18566
|
-
function persist(
|
|
18627
|
+
function persist(_data) {
|
|
18567
18628
|
pendingWrite = true;
|
|
18568
18629
|
if (flushTimer) return;
|
|
18569
18630
|
flushTimer = setTimeout(() => {
|
|
18570
18631
|
flushTimer = null;
|
|
18571
18632
|
if (pendingWrite) {
|
|
18572
18633
|
try {
|
|
18573
|
-
persistNow(
|
|
18634
|
+
persistNow();
|
|
18574
18635
|
} catch {
|
|
18575
18636
|
}
|
|
18576
18637
|
}
|
|
@@ -18604,7 +18665,7 @@ function pruneLedgerTargets(configuredServers) {
|
|
|
18604
18665
|
}
|
|
18605
18666
|
const rows = names.reduce((n, s) => n + Object.keys(orphans[s]).length, 0);
|
|
18606
18667
|
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.`);
|
|
18607
|
-
persistNow(
|
|
18668
|
+
persistNow();
|
|
18608
18669
|
return names.length;
|
|
18609
18670
|
}
|
|
18610
18671
|
function flushLedger() {
|
|
@@ -18613,7 +18674,7 @@ function flushLedger() {
|
|
|
18613
18674
|
flushTimer = null;
|
|
18614
18675
|
}
|
|
18615
18676
|
if (!pendingWrite || !cache3) return;
|
|
18616
|
-
persistNow(
|
|
18677
|
+
persistNow();
|
|
18617
18678
|
}
|
|
18618
18679
|
function loadItemVersionsAll() {
|
|
18619
18680
|
if (itemVersionsCache) return itemVersionsCache;
|
|
@@ -18689,13 +18750,13 @@ function markSynced(server, rows) {
|
|
|
18689
18750
|
const o = (cursorFromCaller ? r.offset : void 0) ?? prevOS.o ?? 0;
|
|
18690
18751
|
const s = (cursorFromCaller ? r.size : void 0) ?? prevOS.s ?? 0;
|
|
18691
18752
|
const ev = extractorVersionForId(r.id);
|
|
18692
|
-
const
|
|
18753
|
+
const base4 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
|
|
18693
18754
|
if (o > 0 || s > 0) {
|
|
18694
|
-
|
|
18695
|
-
|
|
18755
|
+
base4.o = o;
|
|
18756
|
+
base4.s = s;
|
|
18696
18757
|
}
|
|
18697
|
-
if (r.hash !== void 0)
|
|
18698
|
-
forServer[r.id] =
|
|
18758
|
+
if (r.hash !== void 0) base4.h = r.hash;
|
|
18759
|
+
forServer[r.id] = base4;
|
|
18699
18760
|
}
|
|
18700
18761
|
persist(data);
|
|
18701
18762
|
}
|
|
@@ -18770,7 +18831,7 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
|
|
|
18770
18831
|
if (row.m >= Math.floor(mtime)) return "skip";
|
|
18771
18832
|
return "full";
|
|
18772
18833
|
}
|
|
18773
|
-
var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
18834
|
+
var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
|
|
18774
18835
|
var init_sync_ledger = __esm({
|
|
18775
18836
|
"src/sync-ledger.ts"() {
|
|
18776
18837
|
"use strict";
|
|
@@ -18778,6 +18839,9 @@ var init_sync_ledger = __esm({
|
|
|
18778
18839
|
init_extractor_version();
|
|
18779
18840
|
ledgerPath = () => join38(getDataDir(), "sync-ledger.json");
|
|
18780
18841
|
cache3 = null;
|
|
18842
|
+
base = /* @__PURE__ */ new Map();
|
|
18843
|
+
diskStamp = null;
|
|
18844
|
+
rowKey = (server, id) => `${server}\0${id}`;
|
|
18781
18845
|
LEDGER_FLUSH_MS = 2e3;
|
|
18782
18846
|
pendingWrite = false;
|
|
18783
18847
|
flushTimer = null;
|
|
@@ -18816,7 +18880,7 @@ var init_sync_fields = __esm({
|
|
|
18816
18880
|
});
|
|
18817
18881
|
|
|
18818
18882
|
// ../engine/dist/core/index-lock.js
|
|
18819
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as
|
|
18883
|
+
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";
|
|
18820
18884
|
import { dirname as dirname16, join as join39 } from "path";
|
|
18821
18885
|
function acquireIndexLock(opts) {
|
|
18822
18886
|
const lockPath = lockFilePath(opts.file);
|
|
@@ -18866,7 +18930,7 @@ function lockFilePath(file = ".compact.lock") {
|
|
|
18866
18930
|
}
|
|
18867
18931
|
function stealStaleLock(lockPath, staleAfterMs) {
|
|
18868
18932
|
try {
|
|
18869
|
-
const s =
|
|
18933
|
+
const s = statSync36(lockPath);
|
|
18870
18934
|
const age = Date.now() - s.mtimeMs;
|
|
18871
18935
|
let payload;
|
|
18872
18936
|
try {
|
|
@@ -18938,7 +19002,7 @@ var init_http = __esm({
|
|
|
18938
19002
|
CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18939
19003
|
} catch {
|
|
18940
19004
|
try {
|
|
18941
|
-
if (true) CLI_VERSION = "0.7.
|
|
19005
|
+
if (true) CLI_VERSION = "0.7.15";
|
|
18942
19006
|
} catch {
|
|
18943
19007
|
}
|
|
18944
19008
|
}
|
|
@@ -19436,7 +19500,7 @@ var init_telemetry = __esm({
|
|
|
19436
19500
|
init_sync_client();
|
|
19437
19501
|
cliVersion2 = () => {
|
|
19438
19502
|
try {
|
|
19439
|
-
return true ? "0.7.
|
|
19503
|
+
return true ? "0.7.15" : "dev";
|
|
19440
19504
|
} catch {
|
|
19441
19505
|
return "dev";
|
|
19442
19506
|
}
|
|
@@ -19612,11 +19676,11 @@ __export(server_probe_exports, {
|
|
|
19612
19676
|
function probeOk(r) {
|
|
19613
19677
|
return r.kind === "ok" && r.apiVersion >= 2;
|
|
19614
19678
|
}
|
|
19615
|
-
async function probeServer(
|
|
19679
|
+
async function probeServer(base4, opts = {}) {
|
|
19616
19680
|
const f = opts.fetchImpl || fetch;
|
|
19617
19681
|
let res;
|
|
19618
19682
|
try {
|
|
19619
|
-
res = await f(`${
|
|
19683
|
+
res = await f(`${base4.replace(/\/+$/, "")}/api/capabilities`, {
|
|
19620
19684
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
19621
19685
|
});
|
|
19622
19686
|
} catch (err) {
|
|
@@ -19653,34 +19717,34 @@ async function probeServer(base3, opts = {}) {
|
|
|
19653
19717
|
raw: parsed
|
|
19654
19718
|
};
|
|
19655
19719
|
}
|
|
19656
|
-
function probeAdvice(r,
|
|
19720
|
+
function probeAdvice(r, base4) {
|
|
19657
19721
|
switch (r.kind) {
|
|
19658
19722
|
case "ok":
|
|
19659
19723
|
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`;
|
|
19660
19724
|
case "moved":
|
|
19661
|
-
return `HTTP ${r.status} \u2014 nothing is served at ${
|
|
19725
|
+
return `HTTP ${r.status} \u2014 nothing is served at ${base4}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
|
|
19662
19726
|
case "unauthorized":
|
|
19663
|
-
return `HTTP ${r.status} \u2014 ${
|
|
19727
|
+
return `HTTP ${r.status} \u2014 ${base4} rejected the credential. Re-login: \`chat-recall login ` + base4 + "`";
|
|
19664
19728
|
case "server_error":
|
|
19665
|
-
return `HTTP ${r.status} \u2014 ${
|
|
19729
|
+
return `HTTP ${r.status} \u2014 ${base4} is up but failing. Nothing to fix locally; check the server.`;
|
|
19666
19730
|
case "not_api":
|
|
19667
|
-
return `${
|
|
19731
|
+
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`;
|
|
19668
19732
|
case "unreachable":
|
|
19669
19733
|
switch (r.reason) {
|
|
19670
19734
|
case "dns":
|
|
19671
|
-
return `cannot resolve the host in ${
|
|
19735
|
+
return `cannot resolve the host in ${base4} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
|
|
19672
19736
|
case "refused":
|
|
19673
|
-
return `${
|
|
19737
|
+
return `${base4} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
|
|
19674
19738
|
case "tls":
|
|
19675
|
-
return `the TLS certificate at ${
|
|
19739
|
+
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.`;
|
|
19676
19740
|
case "timeout":
|
|
19677
|
-
return `${
|
|
19741
|
+
return `${base4} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
|
|
19678
19742
|
case "reset":
|
|
19679
|
-
return `the connection to ${
|
|
19743
|
+
return `the connection to ${base4} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
|
|
19680
19744
|
case "bad_port":
|
|
19681
|
-
return `${
|
|
19745
|
+
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`;
|
|
19682
19746
|
default:
|
|
19683
|
-
return `no HTTP response from ${
|
|
19747
|
+
return `no HTTP response from ${base4} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
|
|
19684
19748
|
}
|
|
19685
19749
|
}
|
|
19686
19750
|
}
|
|
@@ -19713,8 +19777,8 @@ function describeCause(err) {
|
|
|
19713
19777
|
const code2 = e.code;
|
|
19714
19778
|
if (typeof code2 === "string" && code2 && !parts.includes(code2)) parts.push(code2);
|
|
19715
19779
|
}
|
|
19716
|
-
const
|
|
19717
|
-
return parts.length ? `${
|
|
19780
|
+
const base4 = err instanceof Error ? err.message : String(err);
|
|
19781
|
+
return parts.length ? `${base4}: ${parts.join(" \u2192 ")}` : base4;
|
|
19718
19782
|
}
|
|
19719
19783
|
function describeUnreachable(err) {
|
|
19720
19784
|
const reason = classifyUnreachable(err);
|
|
@@ -19827,7 +19891,7 @@ __export(auto_update_exports, {
|
|
|
19827
19891
|
import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
|
|
19828
19892
|
import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
|
|
19829
19893
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19830
|
-
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as
|
|
19894
|
+
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as statSync37, mkdirSync as mkdirSync9 } from "node:fs";
|
|
19831
19895
|
import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
|
|
19832
19896
|
import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
|
|
19833
19897
|
import { createHash as createHash8 } from "node:crypto";
|
|
@@ -19859,12 +19923,12 @@ function installedVersion() {
|
|
|
19859
19923
|
return null;
|
|
19860
19924
|
}
|
|
19861
19925
|
}
|
|
19862
|
-
function planAutoUpdate(
|
|
19926
|
+
function planAutoUpdate(base4, caps, ownVersion3, flag) {
|
|
19863
19927
|
const rel = caps.cli;
|
|
19864
19928
|
if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
|
|
19865
19929
|
if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
|
|
19866
19930
|
if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
|
|
19867
|
-
return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(
|
|
19931
|
+
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 };
|
|
19868
19932
|
}
|
|
19869
19933
|
function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
|
|
19870
19934
|
const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
|
|
@@ -19880,7 +19944,7 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
|
|
|
19880
19944
|
if (!MKDTEMP_SUFFIX.test(name)) continue;
|
|
19881
19945
|
const dir = join41(root, name);
|
|
19882
19946
|
try {
|
|
19883
|
-
const st =
|
|
19947
|
+
const st = statSync37(dir);
|
|
19884
19948
|
if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
|
|
19885
19949
|
const inside = readdirSync30(dir);
|
|
19886
19950
|
if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
|
|
@@ -20044,20 +20108,20 @@ function clearUpdateFailures() {
|
|
|
20044
20108
|
function stopAutoUpdates() {
|
|
20045
20109
|
stopRequested = true;
|
|
20046
20110
|
}
|
|
20047
|
-
async function runAutoUpdate(
|
|
20111
|
+
async function runAutoUpdate(base4, authHeaders, ownVersion3, deps) {
|
|
20048
20112
|
let caps;
|
|
20049
20113
|
try {
|
|
20050
|
-
const res = await fetchWithTimeout(`${
|
|
20114
|
+
const res = await fetchWithTimeout(`${base4.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
|
|
20051
20115
|
if (!res.ok) return { updated: false, reason: `capabilities HTTP ${res.status}` };
|
|
20052
20116
|
caps = await res.json();
|
|
20053
20117
|
} catch (e) {
|
|
20054
20118
|
return { updated: false, reason: `capabilities unreachable: ${e instanceof Error ? e.message : e}` };
|
|
20055
20119
|
}
|
|
20056
20120
|
const effectiveOwn = newerOf(ownVersion3, installedVersion());
|
|
20057
|
-
const plan = planAutoUpdate(
|
|
20121
|
+
const plan = planAutoUpdate(base4, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20058
20122
|
if (!plan.update) {
|
|
20059
|
-
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(
|
|
20060
|
-
reportedStandingSkip.add(
|
|
20123
|
+
if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base4)) {
|
|
20124
|
+
reportedStandingSkip.add(base4);
|
|
20061
20125
|
try {
|
|
20062
20126
|
const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
|
|
20063
20127
|
reportClientEvent2("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
|
|
@@ -20113,7 +20177,7 @@ var init_auto_update = __esm({
|
|
|
20113
20177
|
"use strict";
|
|
20114
20178
|
init_http();
|
|
20115
20179
|
init_index_lock();
|
|
20116
|
-
tarballUrl = (
|
|
20180
|
+
tarballUrl = (base4, version2) => `${base4.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
|
|
20117
20181
|
GIVE_UP_AFTER = 3;
|
|
20118
20182
|
reportedStandingSkip = /* @__PURE__ */ new Set();
|
|
20119
20183
|
STOPPING_REASON = "not installed: this process is shutting down";
|
|
@@ -20158,7 +20222,7 @@ __export(sync_client_exports, {
|
|
|
20158
20222
|
syncSessions: () => syncSessions
|
|
20159
20223
|
});
|
|
20160
20224
|
import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
|
|
20161
|
-
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
|
|
20225
|
+
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";
|
|
20162
20226
|
import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
|
|
20163
20227
|
import { createHash as createHash9 } from "node:crypto";
|
|
20164
20228
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -20221,15 +20285,15 @@ function loadAllCredentials() {
|
|
|
20221
20285
|
return [];
|
|
20222
20286
|
}
|
|
20223
20287
|
async function fetchTenantSecurityConfig(cred) {
|
|
20224
|
-
const
|
|
20225
|
-
const cached = _securityConfigCache.get(
|
|
20288
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20289
|
+
const cached = _securityConfigCache.get(base4);
|
|
20226
20290
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20227
20291
|
const headers = { "content-type": "application/json" };
|
|
20228
20292
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
20229
20293
|
try {
|
|
20230
20294
|
const [rulesRes, settingsRes] = await Promise.all([
|
|
20231
|
-
fetchWithTimeout(`${
|
|
20232
|
-
fetchWithTimeout(`${
|
|
20295
|
+
fetchWithTimeout(`${base4}/api/secrets/rules`, { headers }),
|
|
20296
|
+
fetchWithTimeout(`${base4}/api/teams/security-config`, { headers }).catch(() => null)
|
|
20233
20297
|
]);
|
|
20234
20298
|
let tenantRules = [];
|
|
20235
20299
|
let rulePackVersion = null;
|
|
@@ -20254,18 +20318,18 @@ async function fetchTenantSecurityConfig(cred) {
|
|
|
20254
20318
|
const body2 = await settingsRes.json().catch(() => ({}));
|
|
20255
20319
|
if (typeof body2.verifySecrets === "boolean") verifySecrets = body2.verifySecrets;
|
|
20256
20320
|
if (typeof body2.telemetry === "boolean") {
|
|
20257
|
-
setTelemetryEligible(
|
|
20321
|
+
setTelemetryEligible(base4, body2.telemetry);
|
|
20258
20322
|
try {
|
|
20259
20323
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20260
20324
|
updateCollectorHealth({
|
|
20261
|
-
telemetryEligible: { ...prior, [
|
|
20325
|
+
telemetryEligible: { ...prior, [base4]: { allowed: body2.telemetry, at: Date.now() } }
|
|
20262
20326
|
});
|
|
20263
20327
|
} catch {
|
|
20264
20328
|
}
|
|
20265
20329
|
}
|
|
20266
20330
|
}
|
|
20267
20331
|
const config2 = { tenantRules, verifySecrets, rulePackVersion };
|
|
20268
|
-
_securityConfigCache.set(
|
|
20332
|
+
_securityConfigCache.set(base4, { fetchedAt: Date.now(), config: config2 });
|
|
20269
20333
|
return config2;
|
|
20270
20334
|
} catch {
|
|
20271
20335
|
return { tenantRules: [], verifySecrets: true, rulePackVersion: null };
|
|
@@ -20284,18 +20348,18 @@ function readPersistedSyncConfigs() {
|
|
|
20284
20348
|
return {};
|
|
20285
20349
|
}
|
|
20286
20350
|
}
|
|
20287
|
-
function persistSyncConfig(
|
|
20351
|
+
function persistSyncConfig(base4, config2) {
|
|
20288
20352
|
try {
|
|
20289
20353
|
const all = readPersistedSyncConfigs();
|
|
20290
|
-
all[
|
|
20354
|
+
all[base4] = { fetchedAt: Date.now(), config: config2 };
|
|
20291
20355
|
mkdirSync10(dirname19(syncConfigCachePath()), { recursive: true });
|
|
20292
20356
|
writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
|
|
20293
20357
|
} catch {
|
|
20294
20358
|
}
|
|
20295
20359
|
}
|
|
20296
20360
|
async function fetchTenantSyncConfig(cred) {
|
|
20297
|
-
const
|
|
20298
|
-
const cached = _syncConfigCache.get(
|
|
20361
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20362
|
+
const cached = _syncConfigCache.get(base4);
|
|
20299
20363
|
if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
|
|
20300
20364
|
const headers = { "content-type": "application/json" };
|
|
20301
20365
|
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
@@ -20303,7 +20367,7 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20303
20367
|
try {
|
|
20304
20368
|
const sources3 = discoverSessionSources();
|
|
20305
20369
|
if (sources3.length > 0) {
|
|
20306
|
-
await fetchWithTimeout(`${
|
|
20370
|
+
await fetchWithTimeout(`${base4}/api/sync-config/sources`, {
|
|
20307
20371
|
method: "POST",
|
|
20308
20372
|
headers,
|
|
20309
20373
|
body: JSON.stringify({ device: deviceLabel(), sources: sources3 })
|
|
@@ -20311,12 +20375,12 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20311
20375
|
}
|
|
20312
20376
|
} catch {
|
|
20313
20377
|
}
|
|
20314
|
-
const res = await fetchWithTimeout(`${
|
|
20378
|
+
const res = await fetchWithTimeout(`${base4}/api/sync-config`, { headers });
|
|
20315
20379
|
if (res.status === 404) {
|
|
20316
|
-
_syncConfigCache.set(
|
|
20380
|
+
_syncConfigCache.set(base4, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
|
|
20317
20381
|
return EMPTY_SYNC_CONFIG;
|
|
20318
20382
|
}
|
|
20319
|
-
if (!res.ok) return lastKnownSyncConfig(
|
|
20383
|
+
if (!res.ok) return lastKnownSyncConfig(base4, `HTTP ${res.status}`);
|
|
20320
20384
|
const body2 = await res.json().catch(() => ({}));
|
|
20321
20385
|
const config2 = {
|
|
20322
20386
|
excludeTools: Array.isArray(body2.excludeTools) ? body2.excludeTools.filter((t) => typeof t === "string") : [],
|
|
@@ -20353,22 +20417,22 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20353
20417
|
if (decided > 0) console.error(`[sync] applied ${decided} folder decision(s) from the dashboard`);
|
|
20354
20418
|
} catch {
|
|
20355
20419
|
}
|
|
20356
|
-
_syncConfigCache.set(
|
|
20357
|
-
persistSyncConfig(
|
|
20420
|
+
_syncConfigCache.set(base4, { fetchedAt: Date.now(), config: config2 });
|
|
20421
|
+
persistSyncConfig(base4, config2);
|
|
20358
20422
|
return config2;
|
|
20359
20423
|
} catch (err) {
|
|
20360
20424
|
if (isSyncRulesRefusal(err)) throw err;
|
|
20361
|
-
return lastKnownSyncConfig(
|
|
20425
|
+
return lastKnownSyncConfig(base4, err instanceof Error ? err.message : "request failed");
|
|
20362
20426
|
}
|
|
20363
20427
|
}
|
|
20364
|
-
function lastKnownSyncConfig(
|
|
20365
|
-
const remembered = _syncConfigCache.get(
|
|
20428
|
+
function lastKnownSyncConfig(base4, reason) {
|
|
20429
|
+
const remembered = _syncConfigCache.get(base4) ?? readPersistedSyncConfigs()[base4];
|
|
20366
20430
|
if (remembered) {
|
|
20367
20431
|
console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
|
|
20368
20432
|
return remembered.config;
|
|
20369
20433
|
}
|
|
20370
20434
|
const refusal = new Error(
|
|
20371
|
-
`Cannot read the server-side sync rules from ${
|
|
20435
|
+
`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.`
|
|
20372
20436
|
);
|
|
20373
20437
|
refusal[SYNC_RULES_REFUSAL] = true;
|
|
20374
20438
|
throw refusal;
|
|
@@ -20489,25 +20553,25 @@ async function syncSessions(opts = {}) {
|
|
|
20489
20553
|
${errors.join("\n ")}`);
|
|
20490
20554
|
return { ...agg, perTarget };
|
|
20491
20555
|
}
|
|
20492
|
-
async function postFieldBatch(
|
|
20556
|
+
async function postFieldBatch(base4, token, rows) {
|
|
20493
20557
|
const headers = { "content-type": "application/json" };
|
|
20494
20558
|
if (token) headers.authorization = `Bearer ${token}`;
|
|
20495
20559
|
const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
20496
20560
|
for (let attempt = 0; ; attempt++) {
|
|
20497
|
-
const res = await fetchWithTimeout(`${
|
|
20561
|
+
const res = await fetchWithTimeout(`${base4}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
|
|
20498
20562
|
if (res.ok) return;
|
|
20499
20563
|
const retryable = res.status === 429 || res.status >= 500;
|
|
20500
|
-
if (!retryable || attempt >= 5) throw new Error(`${
|
|
20564
|
+
if (!retryable || attempt >= 5) throw new Error(`${base4}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
20501
20565
|
const ra = Number(res.headers.get("retry-after")) || 0;
|
|
20502
20566
|
await sleep2(ra > 0 ? ra * 1e3 : [500, 1500, 4e3, 8e3, 15e3][Math.min(attempt, 4)]);
|
|
20503
20567
|
}
|
|
20504
20568
|
}
|
|
20505
20569
|
async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
20506
|
-
const
|
|
20507
|
-
const ledger = getSyncedRows(
|
|
20570
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
20571
|
+
const ledger = getSyncedRows(base4);
|
|
20508
20572
|
let pushed = 0, absent = 0, scanned = 0;
|
|
20509
20573
|
for (const field of SYNC_FIELDS) {
|
|
20510
|
-
const needFull = !!opts.force || fieldNeedsFullPass(
|
|
20574
|
+
const needFull = !!opts.force || fieldNeedsFullPass(base4, field);
|
|
20511
20575
|
const candidates2 = needFull ? fullRefs() : opts.convRefs ?? fullRefs();
|
|
20512
20576
|
const send = [];
|
|
20513
20577
|
const mtimeById = /* @__PURE__ */ new Map();
|
|
@@ -20523,15 +20587,15 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
|
|
|
20523
20587
|
}
|
|
20524
20588
|
for (let i = 0; i < send.length; i += 200) {
|
|
20525
20589
|
const chunk = send.slice(i, i + 200);
|
|
20526
|
-
await postFieldBatch(
|
|
20527
|
-
markFieldCoverage(
|
|
20590
|
+
await postFieldBatch(base4, cred.token, chunk);
|
|
20591
|
+
markFieldCoverage(base4, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
|
|
20528
20592
|
pushed += chunk.length;
|
|
20529
20593
|
}
|
|
20530
20594
|
if (absentIds.length) {
|
|
20531
|
-
markFieldCoverage(
|
|
20595
|
+
markFieldCoverage(base4, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
|
|
20532
20596
|
absent += absentIds.length;
|
|
20533
20597
|
}
|
|
20534
|
-
if (needFull) markFieldFullPassDone(
|
|
20598
|
+
if (needFull) markFieldFullPassDone(base4, field);
|
|
20535
20599
|
}
|
|
20536
20600
|
return { pushed, absent, scanned };
|
|
20537
20601
|
}
|
|
@@ -20569,7 +20633,7 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
|
|
|
20569
20633
|
if (!name.startsWith("cr-batchscan-")) continue;
|
|
20570
20634
|
const p = join42(dir, name);
|
|
20571
20635
|
try {
|
|
20572
|
-
if (now -
|
|
20636
|
+
if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
|
|
20573
20637
|
rmSync6(p, { recursive: true, force: true });
|
|
20574
20638
|
reaped++;
|
|
20575
20639
|
} catch {
|
|
@@ -20648,25 +20712,25 @@ function serverIngestConcurrency(serverUrl) {
|
|
|
20648
20712
|
return advertisedIngestConc.get(serverUrl.replace(/\/$/, "")) ?? null;
|
|
20649
20713
|
}
|
|
20650
20714
|
async function verifyServerApi(serverUrl, minApi) {
|
|
20651
|
-
const
|
|
20652
|
-
const seen = capsOk.get(
|
|
20715
|
+
const base4 = serverUrl.replace(/\/$/, "");
|
|
20716
|
+
const seen = capsOk.get(base4);
|
|
20653
20717
|
if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
|
|
20654
20718
|
const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
20655
|
-
const probe = await probeServer2(
|
|
20719
|
+
const probe = await probeServer2(base4, {
|
|
20656
20720
|
fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
|
|
20657
20721
|
});
|
|
20658
|
-
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe,
|
|
20722
|
+
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base4));
|
|
20659
20723
|
const caps = probe.raw;
|
|
20660
20724
|
if ((caps.apiVersion ?? 0) < minApi) {
|
|
20661
20725
|
throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
|
|
20662
20726
|
}
|
|
20663
20727
|
const adv = caps.limits?.ingestConcurrencyPerTenant;
|
|
20664
20728
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
20665
|
-
advertisedIngestConc.set(
|
|
20729
|
+
advertisedIngestConc.set(base4, Math.floor(adv));
|
|
20666
20730
|
}
|
|
20667
|
-
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(
|
|
20668
|
-
else advertisedGzipBody.delete(
|
|
20669
|
-
capsOk.set(
|
|
20731
|
+
if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base4);
|
|
20732
|
+
else advertisedGzipBody.delete(base4);
|
|
20733
|
+
capsOk.set(base4, Date.now());
|
|
20670
20734
|
return true;
|
|
20671
20735
|
}
|
|
20672
20736
|
async function syncToTarget(cred, opts = {}) {
|
|
@@ -20731,7 +20795,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20731
20795
|
const ac = new AbortController();
|
|
20732
20796
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20733
20797
|
try {
|
|
20734
|
-
const res = await fetch(`${
|
|
20798
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20735
20799
|
method: "POST",
|
|
20736
20800
|
// No token ⇒ no auth header: a no-auth local server (AUTH_PROVIDER=none)
|
|
20737
20801
|
// accepts it as the single 'default' tenant.
|
|
@@ -20747,26 +20811,26 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20747
20811
|
acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
|
|
20748
20812
|
const body3 = await res.json().catch(() => ({}));
|
|
20749
20813
|
if (typeof body3.telemetry === "boolean") {
|
|
20750
|
-
setTelemetryEligible(
|
|
20814
|
+
setTelemetryEligible(base4, body3.telemetry);
|
|
20751
20815
|
try {
|
|
20752
20816
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20753
20817
|
updateCollectorHealth({
|
|
20754
|
-
telemetryEligible: { ...prior, [
|
|
20818
|
+
telemetryEligible: { ...prior, [base4]: { allowed: body3.telemetry, at: Date.now() } }
|
|
20755
20819
|
});
|
|
20756
20820
|
} catch {
|
|
20757
20821
|
}
|
|
20758
20822
|
}
|
|
20759
20823
|
const adv = body3.limits?.ingestConcurrencyPerTenant;
|
|
20760
20824
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
20761
|
-
setServerIngestConcurrency(
|
|
20825
|
+
setServerIngestConcurrency(base4, Math.floor(adv));
|
|
20762
20826
|
}
|
|
20763
20827
|
if (body3.cli && body3.cli.version) {
|
|
20764
20828
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20765
20829
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20766
|
-
const plan = planAutoUpdate2(
|
|
20830
|
+
const plan = planAutoUpdate2(base4, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20767
20831
|
sweepStaleStaging2();
|
|
20768
20832
|
if (plan.update) {
|
|
20769
|
-
void runAutoUpdate2(
|
|
20833
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion).catch(() => {
|
|
20770
20834
|
});
|
|
20771
20835
|
}
|
|
20772
20836
|
}
|
|
@@ -20775,7 +20839,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20775
20839
|
const retryable = res.status === 429 || res.status >= 500;
|
|
20776
20840
|
const text = await res.text().catch(() => "");
|
|
20777
20841
|
if (res.status === 401 || res.status === 403) {
|
|
20778
|
-
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${
|
|
20842
|
+
throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base4}`);
|
|
20779
20843
|
}
|
|
20780
20844
|
if (res.status === 402) {
|
|
20781
20845
|
let detail = text;
|
|
@@ -20796,7 +20860,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20796
20860
|
const retryAfterS = Number(res.headers.get("retry-after"));
|
|
20797
20861
|
const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
|
|
20798
20862
|
const why = res.status === 524 ? ` \u2014 the proxy cut the request at its time limit; the batch is too big for one request` : "";
|
|
20799
|
-
console.error(`[sync] HTTP ${res.status} from ${
|
|
20863
|
+
console.error(`[sync] HTTP ${res.status} from ${base4}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
|
|
20800
20864
|
await sleep2(waitMs);
|
|
20801
20865
|
continue;
|
|
20802
20866
|
} catch (err) {
|
|
@@ -20864,11 +20928,11 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20864
20928
|
}
|
|
20865
20929
|
};
|
|
20866
20930
|
};
|
|
20867
|
-
const
|
|
20868
|
-
const ledger = opts.useLedger ? getSyncedRows(
|
|
20931
|
+
const base4 = cred.serverUrl.replace(/\/$/, "");
|
|
20932
|
+
const ledger = opts.useLedger ? getSyncedRows(base4) : null;
|
|
20869
20933
|
const convBatch = makeBatcher("conversations", 25, false, (rows) => {
|
|
20870
20934
|
try {
|
|
20871
|
-
markSynced(
|
|
20935
|
+
markSynced(base4, rows.map((r) => {
|
|
20872
20936
|
const off = typeof r.from_offset === "number" ? r.from_offset : 0;
|
|
20873
20937
|
const hash = shippedHash.get(r.session_id);
|
|
20874
20938
|
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 };
|
|
@@ -20883,7 +20947,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20883
20947
|
const ac = new AbortController();
|
|
20884
20948
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20885
20949
|
try {
|
|
20886
|
-
const res = await fetch(`${
|
|
20950
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20887
20951
|
method: "POST",
|
|
20888
20952
|
headers,
|
|
20889
20953
|
body: JSON.stringify({ conversations: convs }),
|
|
@@ -20894,10 +20958,10 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20894
20958
|
if (body2.cli && body2.cli.version) {
|
|
20895
20959
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
20896
20960
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
20897
|
-
const plan = planAutoUpdate2(
|
|
20961
|
+
const plan = planAutoUpdate2(base4, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
20898
20962
|
sweepStaleStaging2();
|
|
20899
20963
|
if (plan.update) {
|
|
20900
|
-
void runAutoUpdate2(
|
|
20964
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion).catch(() => {
|
|
20901
20965
|
});
|
|
20902
20966
|
}
|
|
20903
20967
|
}
|
|
@@ -20905,7 +20969,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20905
20969
|
const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
|
|
20906
20970
|
if (acked.length > 0) {
|
|
20907
20971
|
try {
|
|
20908
|
-
markSynced(
|
|
20972
|
+
markSynced(base4, acked.map((c) => ({
|
|
20909
20973
|
id: c.session_id,
|
|
20910
20974
|
mtime: c.mtime,
|
|
20911
20975
|
offset: c.from_offset,
|
|
@@ -20929,7 +20993,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20929
20993
|
const ac = new AbortController();
|
|
20930
20994
|
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20931
20995
|
try {
|
|
20932
|
-
const res = await fetch(`${
|
|
20996
|
+
const res = await fetch(`${base4}/api/sync`, {
|
|
20933
20997
|
method: "POST",
|
|
20934
20998
|
headers,
|
|
20935
20999
|
body: JSON.stringify({ conversations: [conv] }),
|
|
@@ -20942,11 +21006,11 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20942
21006
|
const mtime = conv.mtime;
|
|
20943
21007
|
try {
|
|
20944
21008
|
if (!guarded) {
|
|
20945
|
-
markSynced(
|
|
21009
|
+
markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
|
|
20946
21010
|
} else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
|
|
20947
|
-
markSynced(
|
|
21011
|
+
markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
|
|
20948
21012
|
} else {
|
|
20949
|
-
markSynced(
|
|
21013
|
+
markSynced(base4, [{ id, mtime }]);
|
|
20950
21014
|
}
|
|
20951
21015
|
} catch {
|
|
20952
21016
|
}
|
|
@@ -21012,7 +21076,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21012
21076
|
const m = process.memoryUsage();
|
|
21013
21077
|
console.error(`[sync-trace] ${msg} \xB7 heap=${(m.heapUsed / 1048576).toFixed(0)}MB rss=${(m.rss / 1048576).toFixed(0)}MB`);
|
|
21014
21078
|
} : null;
|
|
21015
|
-
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${
|
|
21079
|
+
trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base4}`);
|
|
21016
21080
|
if (upload.sessionMeta) {
|
|
21017
21081
|
try {
|
|
21018
21082
|
await reconcileFieldsForTarget(
|
|
@@ -21027,7 +21091,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21027
21091
|
{ convRefs: slice }
|
|
21028
21092
|
);
|
|
21029
21093
|
} catch (e) {
|
|
21030
|
-
console.error(`[sync] field reconcile (${
|
|
21094
|
+
console.error(`[sync] field reconcile (${base4}): ${e instanceof Error ? e.message : e}`);
|
|
21031
21095
|
}
|
|
21032
21096
|
}
|
|
21033
21097
|
trace?.("field reconcile done");
|
|
@@ -21069,7 +21133,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21069
21133
|
}
|
|
21070
21134
|
trace?.("conversation walk start");
|
|
21071
21135
|
const walkStartedAt = Date.now();
|
|
21072
|
-
const firstWalk = (ledger ?? getSyncedRows(
|
|
21136
|
+
const firstWalk = (ledger ?? getSyncedRows(base4)).size === 0;
|
|
21073
21137
|
reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false, first: firstWalk });
|
|
21074
21138
|
walkProgressForUpload = { done: 0, total: slice.length, complete: false };
|
|
21075
21139
|
let traceN = 0;
|
|
@@ -21109,7 +21173,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21109
21173
|
continue;
|
|
21110
21174
|
} else {
|
|
21111
21175
|
try {
|
|
21112
|
-
markSynced(
|
|
21176
|
+
markSynced(base4, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21113
21177
|
} catch {
|
|
21114
21178
|
}
|
|
21115
21179
|
skipped++;
|
|
@@ -21132,7 +21196,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21132
21196
|
const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
|
|
21133
21197
|
const priorCursor = ackRow?.s ?? 0;
|
|
21134
21198
|
const safeToAck = off > 0 && off <= priorCursor;
|
|
21135
|
-
markSynced(
|
|
21199
|
+
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 }]);
|
|
21136
21200
|
} catch {
|
|
21137
21201
|
}
|
|
21138
21202
|
}
|
|
@@ -21142,7 +21206,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21142
21206
|
if (!built) {
|
|
21143
21207
|
if (ledger) {
|
|
21144
21208
|
try {
|
|
21145
|
-
markSynced(
|
|
21209
|
+
markSynced(base4, [{ id: ref.prefixedId, mtime: ref.mtime }]);
|
|
21146
21210
|
} catch {
|
|
21147
21211
|
}
|
|
21148
21212
|
}
|
|
@@ -21199,7 +21263,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21199
21263
|
walked++;
|
|
21200
21264
|
if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
|
|
21201
21265
|
}
|
|
21202
|
-
const itemVersions = loadItemVersions(
|
|
21266
|
+
const itemVersions = loadItemVersions(base4);
|
|
21203
21267
|
const seenTools = /* @__PURE__ */ new Set();
|
|
21204
21268
|
const seenItemKeys = /* @__PURE__ */ new Map();
|
|
21205
21269
|
const registry = buildSourceRegistry();
|
|
@@ -21293,7 +21357,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21293
21357
|
await appendFlush();
|
|
21294
21358
|
for (const id of appendResults.fullResyncNeeded) {
|
|
21295
21359
|
try {
|
|
21296
|
-
markFullResync(
|
|
21360
|
+
markFullResync(base4, id);
|
|
21297
21361
|
} catch {
|
|
21298
21362
|
}
|
|
21299
21363
|
}
|
|
@@ -21306,7 +21370,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21306
21370
|
await findingsBatch.drain();
|
|
21307
21371
|
await drainInflight();
|
|
21308
21372
|
{
|
|
21309
|
-
const sentHashes = loadInventoryHashes(
|
|
21373
|
+
const sentHashes = loadInventoryHashes(base4);
|
|
21310
21374
|
const entries = [];
|
|
21311
21375
|
const hashes = {};
|
|
21312
21376
|
for (const [sourceType, ids] of inventory) {
|
|
@@ -21320,7 +21384,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21320
21384
|
if (entries.length > 0) {
|
|
21321
21385
|
try {
|
|
21322
21386
|
await post({ inventory: entries });
|
|
21323
|
-
saveInventoryHashes(
|
|
21387
|
+
saveInventoryHashes(base4, hashes);
|
|
21324
21388
|
} catch (err) {
|
|
21325
21389
|
console.error(`[sync] toolkit inventory not sent (it is retried next sync): ${err instanceof Error ? err.message : err}`);
|
|
21326
21390
|
}
|
|
@@ -21331,7 +21395,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21331
21395
|
const rec = {};
|
|
21332
21396
|
for (const t of seenTools) rec[t] = extractorVersionForTool(t);
|
|
21333
21397
|
for (const [k, v] of seenItemKeys) rec[k] = v;
|
|
21334
|
-
saveItemVersions(
|
|
21398
|
+
saveItemVersions(base4, rec);
|
|
21335
21399
|
} catch {
|
|
21336
21400
|
}
|
|
21337
21401
|
}
|
|
@@ -21344,9 +21408,9 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21344
21408
|
}
|
|
21345
21409
|
if (tombstones.length > 0) {
|
|
21346
21410
|
await post({ tombstones });
|
|
21347
|
-
const data = getLedgerData(
|
|
21411
|
+
const data = getLedgerData(base4);
|
|
21348
21412
|
for (const t of tombstones) delete data[t.session_id];
|
|
21349
|
-
persistLedgerData(
|
|
21413
|
+
persistLedgerData(base4, data);
|
|
21350
21414
|
}
|
|
21351
21415
|
}
|
|
21352
21416
|
if (opts.prune) await post({ prune_empty_sessions: true });
|
|
@@ -21817,7 +21881,7 @@ var init_sync_client = __esm({
|
|
|
21817
21881
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21818
21882
|
} catch {
|
|
21819
21883
|
try {
|
|
21820
|
-
if (true) ownVersion = "0.7.
|
|
21884
|
+
if (true) ownVersion = "0.7.15";
|
|
21821
21885
|
} catch {
|
|
21822
21886
|
}
|
|
21823
21887
|
}
|
|
@@ -21911,9 +21975,9 @@ async function refreshUpdateCheck(force = false) {
|
|
|
21911
21975
|
const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
21912
21976
|
for (const cred of loadAllCredentials2()) {
|
|
21913
21977
|
if (!cred.serverUrl) continue;
|
|
21914
|
-
const
|
|
21978
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
21915
21979
|
try {
|
|
21916
|
-
const res = await fetchWithTimeout(`${
|
|
21980
|
+
const res = await fetchWithTimeout(`${base4}/api/capabilities`, {
|
|
21917
21981
|
headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
|
|
21918
21982
|
}, 5e3);
|
|
21919
21983
|
if (!res.ok) continue;
|
|
@@ -21968,11 +22032,11 @@ var init_update_notice = __esm({
|
|
|
21968
22032
|
});
|
|
21969
22033
|
|
|
21970
22034
|
// ../engine/dist/core/which.js
|
|
21971
|
-
import { existsSync as existsSync42, statSync as
|
|
22035
|
+
import { existsSync as existsSync42, statSync as statSync39, accessSync, constants } from "node:fs";
|
|
21972
22036
|
import { join as join44, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
|
|
21973
22037
|
function executable(p) {
|
|
21974
22038
|
try {
|
|
21975
|
-
if (!
|
|
22039
|
+
if (!statSync39(p).isFile())
|
|
21976
22040
|
return false;
|
|
21977
22041
|
} catch {
|
|
21978
22042
|
return false;
|
|
@@ -22301,7 +22365,7 @@ __export(companions_exports, {
|
|
|
22301
22365
|
uninstallCodeindex: () => uninstallCodeindex,
|
|
22302
22366
|
unregisterCodeindexMcp: () => unregisterCodeindexMcp
|
|
22303
22367
|
});
|
|
22304
|
-
import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as
|
|
22368
|
+
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";
|
|
22305
22369
|
import { homedir as homedir17 } from "node:os";
|
|
22306
22370
|
import { join as join47 } from "node:path";
|
|
22307
22371
|
import { execSync as execSync3 } from "node:child_process";
|
|
@@ -22352,7 +22416,7 @@ function checkCodeindexStatus() {
|
|
|
22352
22416
|
status.installed = true;
|
|
22353
22417
|
status.path = foundPath;
|
|
22354
22418
|
try {
|
|
22355
|
-
status.size =
|
|
22419
|
+
status.size = statSync40(foundPath).size;
|
|
22356
22420
|
} catch {
|
|
22357
22421
|
}
|
|
22358
22422
|
try {
|
|
@@ -22502,7 +22566,7 @@ __export(install_skills_exports, {
|
|
|
22502
22566
|
skillsSourceDir: () => skillsSourceDir,
|
|
22503
22567
|
uninstallSkills: () => uninstallSkills
|
|
22504
22568
|
});
|
|
22505
|
-
import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as
|
|
22569
|
+
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";
|
|
22506
22570
|
import { createHash as createHash11 } from "crypto";
|
|
22507
22571
|
import { join as join48, basename as basename21 } from "path";
|
|
22508
22572
|
function skillsSourceDir() {
|
|
@@ -22721,10 +22785,10 @@ function parseAgentText(content, format, fallbackName) {
|
|
|
22721
22785
|
return { name: fm.name || fallbackName, description: fm.description || "", tools: fm.tools, body: body2.trim() };
|
|
22722
22786
|
}
|
|
22723
22787
|
function readCommand(filePath, format) {
|
|
22724
|
-
return parseCommandText(readFileSync40(filePath, "utf-8"), format,
|
|
22788
|
+
return parseCommandText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
|
|
22725
22789
|
}
|
|
22726
22790
|
function readAgent(filePath, format) {
|
|
22727
|
-
return parseAgentText(readFileSync40(filePath, "utf-8"), format,
|
|
22791
|
+
return parseAgentText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
|
|
22728
22792
|
}
|
|
22729
22793
|
function readInstructions(filePath, name) {
|
|
22730
22794
|
const content = readFileSync40(filePath, "utf-8");
|
|
@@ -22824,7 +22888,7 @@ function emit(type, art, toTool, projectPath) {
|
|
|
22824
22888
|
content: stringifyFrontmatter({ name: art.name, description: art.description, ...art.tools ? { tools: art.tools } : {} }, art.body)
|
|
22825
22889
|
};
|
|
22826
22890
|
}
|
|
22827
|
-
function
|
|
22891
|
+
function base2(filePath) {
|
|
22828
22892
|
return basename22(filePath).replace(/\.(md|toml)$/, "");
|
|
22829
22893
|
}
|
|
22830
22894
|
var init_artifact_codec = __esm({
|
|
@@ -23309,11 +23373,11 @@ __export(toolkit_pull_exports, {
|
|
|
23309
23373
|
platformRefusal: () => platformRefusal,
|
|
23310
23374
|
portableCommand: () => portableCommand
|
|
23311
23375
|
});
|
|
23312
|
-
import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as
|
|
23376
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync42, writeFileSync as writeFileSync16 } from "node:fs";
|
|
23313
23377
|
import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
|
|
23314
23378
|
function isExecutableFile(p) {
|
|
23315
23379
|
try {
|
|
23316
|
-
return
|
|
23380
|
+
return statSync42(p).isFile();
|
|
23317
23381
|
} catch {
|
|
23318
23382
|
return false;
|
|
23319
23383
|
}
|
|
@@ -23327,9 +23391,9 @@ function portableCommand(cmd) {
|
|
|
23327
23391
|
}
|
|
23328
23392
|
if (isExecutableFile(cmd))
|
|
23329
23393
|
return { command: cmd, rewritten: false };
|
|
23330
|
-
const
|
|
23331
|
-
if (onPath(
|
|
23332
|
-
return { command:
|
|
23394
|
+
const base4 = basename24(cmd);
|
|
23395
|
+
if (onPath(base4))
|
|
23396
|
+
return { command: base4, rewritten: true };
|
|
23333
23397
|
return { missing: cmd };
|
|
23334
23398
|
}
|
|
23335
23399
|
function entryFromSpec(spec) {
|
|
@@ -24163,8 +24227,8 @@ import { homedir as homedir20 } from "os";
|
|
|
24163
24227
|
import { join as join53 } from "path";
|
|
24164
24228
|
function opencodeConfigDir() {
|
|
24165
24229
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
24166
|
-
const
|
|
24167
|
-
return join53(
|
|
24230
|
+
const base4 = xdg && xdg.trim() ? xdg : join53(homedir20(), ".config");
|
|
24231
|
+
return join53(base4, "opencode");
|
|
24168
24232
|
}
|
|
24169
24233
|
function opencodePluginDir() {
|
|
24170
24234
|
return join53(opencodeConfigDir(), "plugins");
|
|
@@ -24751,17 +24815,17 @@ async function collectCode(opts) {
|
|
|
24751
24815
|
return r;
|
|
24752
24816
|
};
|
|
24753
24817
|
log6(`indexing ${ws} + analyses \u2026`);
|
|
24754
|
-
const
|
|
24818
|
+
const base4 = await runMcp(bin, ws, [
|
|
24755
24819
|
call(1, "index_workspace", { path: ws }),
|
|
24756
24820
|
call(2, "status"),
|
|
24757
24821
|
call(3, "get_tree"),
|
|
24758
24822
|
...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
|
|
24759
24823
|
]);
|
|
24760
|
-
const st = parseJson(
|
|
24761
|
-
const tree = parseJson(
|
|
24824
|
+
const st = parseJson(base4, 2, {});
|
|
24825
|
+
const tree = parseJson(base4, 3, []);
|
|
24762
24826
|
const A = {};
|
|
24763
24827
|
ANALYSES.forEach((a, i) => {
|
|
24764
|
-
A[a] = parseJson(
|
|
24828
|
+
A[a] = parseJson(base4, 10 + i, {});
|
|
24765
24829
|
});
|
|
24766
24830
|
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;
|
|
24767
24831
|
if (tree.length === 0) {
|
|
@@ -25590,7 +25654,7 @@ __export(project_tasks_exports, {
|
|
|
25590
25654
|
pushProjectTaskStatuses: () => pushProjectTaskStatuses,
|
|
25591
25655
|
renderTeamTasksMd: () => renderTeamTasksMd
|
|
25592
25656
|
});
|
|
25593
|
-
import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as
|
|
25657
|
+
import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync43 } from "node:fs";
|
|
25594
25658
|
import { join as join55, dirname as dirname25 } from "node:path";
|
|
25595
25659
|
import { homedir as homedir21 } from "node:os";
|
|
25596
25660
|
function isTeamTaskStatus(v) {
|
|
@@ -25661,14 +25725,14 @@ function writeLedger(l) {
|
|
|
25661
25725
|
} catch {
|
|
25662
25726
|
}
|
|
25663
25727
|
}
|
|
25664
|
-
async function pushProjectTaskStatuses(
|
|
25728
|
+
async function pushProjectTaskStatuses(base4, authHeaders, opts = {}) {
|
|
25665
25729
|
const out = { files: 0, pushedTasks: 0 };
|
|
25666
25730
|
const ledger = readLedger();
|
|
25667
25731
|
let ledgerDirty = false;
|
|
25668
25732
|
for (const fk of FILE_KINDS) {
|
|
25669
25733
|
let projects;
|
|
25670
25734
|
try {
|
|
25671
|
-
const res = await fetchWithTimeout(`${
|
|
25735
|
+
const res = await fetchWithTimeout(`${base4}${fk.trackedPath}`, { headers: authHeaders });
|
|
25672
25736
|
if (!res.ok) continue;
|
|
25673
25737
|
projects = (await res.json()).projects || [];
|
|
25674
25738
|
} catch {
|
|
@@ -25679,7 +25743,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
25679
25743
|
if (!existsSync51(file)) continue;
|
|
25680
25744
|
let mtime;
|
|
25681
25745
|
try {
|
|
25682
|
-
mtime =
|
|
25746
|
+
mtime = statSync43(file).mtimeMs;
|
|
25683
25747
|
} catch {
|
|
25684
25748
|
continue;
|
|
25685
25749
|
}
|
|
@@ -25696,7 +25760,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
25696
25760
|
continue;
|
|
25697
25761
|
}
|
|
25698
25762
|
try {
|
|
25699
|
-
const res = await fetchWithTimeout(`${
|
|
25763
|
+
const res = await fetchWithTimeout(`${base4}${fk.statusPath}`, {
|
|
25700
25764
|
method: "POST",
|
|
25701
25765
|
headers: { "content-type": "application/json", ...authHeaders },
|
|
25702
25766
|
body: JSON.stringify({ project, items: changed.map((t) => ({ id: t.id, status: t.intent })) })
|
|
@@ -25822,16 +25886,16 @@ ${block}`);
|
|
|
25822
25886
|
return { status: "error", result: JSON.stringify({ error: e instanceof Error ? e.message : "failed" }) };
|
|
25823
25887
|
}
|
|
25824
25888
|
}
|
|
25825
|
-
async function runPull(
|
|
25889
|
+
async function runPull(base4, opts) {
|
|
25826
25890
|
const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
|
|
25827
25891
|
const { hostname: hostname3 } = await import("node:os");
|
|
25828
|
-
const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") ===
|
|
25892
|
+
const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base4.replace(/\/+$/, ""));
|
|
25829
25893
|
const headers = creds?.token ? { authorization: `Bearer ${creds.token}` } : {};
|
|
25830
25894
|
const wanted = opts.types?.length ? opts.types : ["mcp", "skill", "command", "agent", "instructions"];
|
|
25831
25895
|
const rows = [];
|
|
25832
25896
|
for (const type of wanted) {
|
|
25833
25897
|
try {
|
|
25834
|
-
const res = await fetchWithTimeout(`${
|
|
25898
|
+
const res = await fetchWithTimeout(`${base4}/api/toolkit/browse/${type}?limit=1000`, { headers });
|
|
25835
25899
|
if (!res.ok) continue;
|
|
25836
25900
|
const body2 = await res.json();
|
|
25837
25901
|
for (const it of body2.items || []) rows.push(it);
|
|
@@ -25900,20 +25964,20 @@ async function runIntent(intent, ctx) {
|
|
|
25900
25964
|
async function drainSyncIntents(opts = {}) {
|
|
25901
25965
|
const out = { processed: 0, done: 0, errored: 0 };
|
|
25902
25966
|
for (const cred of loadAllCredentials()) {
|
|
25903
|
-
const
|
|
25967
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
25904
25968
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
25905
25969
|
let pending;
|
|
25906
25970
|
try {
|
|
25907
|
-
const res = await fetchWithTimeout(`${
|
|
25971
|
+
const res = await fetchWithTimeout(`${base4}/api/sync-intents/pending`, { headers: authHeaders });
|
|
25908
25972
|
if (!res.ok) continue;
|
|
25909
25973
|
const data = await res.json();
|
|
25910
25974
|
pending = data.intents || [];
|
|
25911
25975
|
if (data.cli && data.cli.version) {
|
|
25912
25976
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
25913
|
-
const plan = planAutoUpdate2(
|
|
25977
|
+
const plan = planAutoUpdate2(base4, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
25914
25978
|
sweepStaleStaging2();
|
|
25915
25979
|
if (plan.update) {
|
|
25916
|
-
void runAutoUpdate2(
|
|
25980
|
+
void runAutoUpdate2(base4, authHeaders, ownVersion2).catch(() => {
|
|
25917
25981
|
});
|
|
25918
25982
|
}
|
|
25919
25983
|
}
|
|
@@ -25921,7 +25985,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25921
25985
|
continue;
|
|
25922
25986
|
}
|
|
25923
25987
|
for (const intent of pending) {
|
|
25924
|
-
const { status, result } = await runIntent(intent, { base:
|
|
25988
|
+
const { status, result } = await runIntent(intent, { base: base4 });
|
|
25925
25989
|
out.processed++;
|
|
25926
25990
|
if (status === "done") out.done++;
|
|
25927
25991
|
else out.errored++;
|
|
@@ -25930,7 +25994,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25930
25994
|
console.error(`[sync-intent] ${label}: ${status}`);
|
|
25931
25995
|
}
|
|
25932
25996
|
try {
|
|
25933
|
-
await fetchWithTimeout(`${
|
|
25997
|
+
await fetchWithTimeout(`${base4}/api/sync-intents/${intent.id}/ack`, {
|
|
25934
25998
|
method: "POST",
|
|
25935
25999
|
headers: { "content-type": "application/json", ...authHeaders },
|
|
25936
26000
|
body: JSON.stringify({ status, result })
|
|
@@ -25939,7 +26003,7 @@ async function drainSyncIntents(opts = {}) {
|
|
|
25939
26003
|
}
|
|
25940
26004
|
}
|
|
25941
26005
|
try {
|
|
25942
|
-
await pushProjectTaskStatuses(
|
|
26006
|
+
await pushProjectTaskStatuses(base4, authHeaders, { verbose: opts.verbose });
|
|
25943
26007
|
} catch {
|
|
25944
26008
|
}
|
|
25945
26009
|
}
|
|
@@ -25979,7 +26043,7 @@ var init_intent_drain = __esm({
|
|
|
25979
26043
|
ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25980
26044
|
} catch {
|
|
25981
26045
|
try {
|
|
25982
|
-
if (true) ownVersion2 = "0.7.
|
|
26046
|
+
if (true) ownVersion2 = "0.7.15";
|
|
25983
26047
|
} catch {
|
|
25984
26048
|
}
|
|
25985
26049
|
}
|
|
@@ -26180,7 +26244,7 @@ var team_merge_exports = {};
|
|
|
26180
26244
|
__export(team_merge_exports, {
|
|
26181
26245
|
mergePullResult: () => mergePullResult
|
|
26182
26246
|
});
|
|
26183
|
-
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as
|
|
26247
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync44 } from "fs";
|
|
26184
26248
|
import { dirname as dirname27, join as join57 } from "path";
|
|
26185
26249
|
import { createHash as createHash14 } from "crypto";
|
|
26186
26250
|
function installPathFor(art, tool) {
|
|
@@ -26317,7 +26381,7 @@ function readFileIfExists(path) {
|
|
|
26317
26381
|
try {
|
|
26318
26382
|
if (!existsSync53(path))
|
|
26319
26383
|
return null;
|
|
26320
|
-
const s =
|
|
26384
|
+
const s = statSync44(path);
|
|
26321
26385
|
if (!s.isFile())
|
|
26322
26386
|
return null;
|
|
26323
26387
|
return readFileSync46(path);
|
|
@@ -26401,7 +26465,7 @@ var init_vault_crypto = __esm({
|
|
|
26401
26465
|
});
|
|
26402
26466
|
|
|
26403
26467
|
// ../engine/dist/core/vault-source.js
|
|
26404
|
-
import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as
|
|
26468
|
+
import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as statSync45 } from "fs";
|
|
26405
26469
|
import { extname, join as join58 } from "path";
|
|
26406
26470
|
function* walkVaultSources() {
|
|
26407
26471
|
const t = loadSettings().team.vault;
|
|
@@ -26417,7 +26481,7 @@ function* walkVaultSources() {
|
|
|
26417
26481
|
}
|
|
26418
26482
|
function safeStat(path) {
|
|
26419
26483
|
try {
|
|
26420
|
-
const s =
|
|
26484
|
+
const s = statSync45(path);
|
|
26421
26485
|
if (!s.isFile())
|
|
26422
26486
|
return null;
|
|
26423
26487
|
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
@@ -26502,8 +26566,8 @@ function* walkCodex() {
|
|
|
26502
26566
|
const s = safeStat(path);
|
|
26503
26567
|
if (!s)
|
|
26504
26568
|
continue;
|
|
26505
|
-
const
|
|
26506
|
-
yield { sessionId: `codex_${
|
|
26569
|
+
const base4 = e.name.replace(/\.jsonl$/, "");
|
|
26570
|
+
yield { sessionId: `codex_${base4}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
|
|
26507
26571
|
}
|
|
26508
26572
|
}
|
|
26509
26573
|
}
|
|
@@ -26520,7 +26584,7 @@ function* walkAgy() {
|
|
|
26520
26584
|
for (const rawId of sessionDirs) {
|
|
26521
26585
|
const sessionPath = join58(brainDir, rawId);
|
|
26522
26586
|
try {
|
|
26523
|
-
if (!
|
|
26587
|
+
if (!statSync45(sessionPath).isDirectory())
|
|
26524
26588
|
continue;
|
|
26525
26589
|
} catch {
|
|
26526
26590
|
continue;
|
|
@@ -26645,11 +26709,11 @@ function vaultEnable(passphrase, opts = {}) {
|
|
|
26645
26709
|
}
|
|
26646
26710
|
async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
|
|
26647
26711
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
26648
|
-
const
|
|
26712
|
+
const base4 = remote.baseUrl.replace(/\/+$/, "");
|
|
26649
26713
|
const headers = { authorization: `Bearer ${remote.token}`, "content-type": "application/json" };
|
|
26650
26714
|
let published = { saltHex: null, keyId: null };
|
|
26651
26715
|
try {
|
|
26652
|
-
const r = await doFetch(`${
|
|
26716
|
+
const r = await doFetch(`${base4}/api/vault/salt`, { headers });
|
|
26653
26717
|
if (r.ok)
|
|
26654
26718
|
published = await r.json();
|
|
26655
26719
|
} catch {
|
|
@@ -26663,7 +26727,7 @@ async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
|
|
|
26663
26727
|
}
|
|
26664
26728
|
const result = vaultEnable(passphrase);
|
|
26665
26729
|
try {
|
|
26666
|
-
const r = await doFetch(`${
|
|
26730
|
+
const r = await doFetch(`${base4}/api/vault/salt`, {
|
|
26667
26731
|
method: "POST",
|
|
26668
26732
|
headers,
|
|
26669
26733
|
body: JSON.stringify({ saltHex: result.saltHex, keyId: result.keyId })
|
|
@@ -26756,8 +26820,8 @@ async function vaultSync(passphrase) {
|
|
|
26756
26820
|
return report;
|
|
26757
26821
|
}
|
|
26758
26822
|
function importedPathFor(tool, sessionId) {
|
|
26759
|
-
const
|
|
26760
|
-
return join59(IMPORTED_ROOT(), tool, `${
|
|
26823
|
+
const base4 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
|
|
26824
|
+
return join59(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
|
|
26761
26825
|
}
|
|
26762
26826
|
async function vaultRestore(passphrase, opts = {}) {
|
|
26763
26827
|
const ctx = context2();
|
|
@@ -27158,8 +27222,8 @@ var init_decision_guard = __esm({
|
|
|
27158
27222
|
isManifest = (p) => {
|
|
27159
27223
|
if (!p)
|
|
27160
27224
|
return false;
|
|
27161
|
-
const
|
|
27162
|
-
return MANIFESTS.includes(
|
|
27225
|
+
const base4 = p.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
27226
|
+
return MANIFESTS.includes(base4);
|
|
27163
27227
|
};
|
|
27164
27228
|
}
|
|
27165
27229
|
});
|
|
@@ -27757,8 +27821,8 @@ async function deviceLogin(opts, onPrompt) {
|
|
|
27757
27821
|
throw new Error("device-auth timed out \u2014 the code expired before approval.");
|
|
27758
27822
|
}
|
|
27759
27823
|
async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
27760
|
-
const
|
|
27761
|
-
const startRes = await fetch(`${
|
|
27824
|
+
const base4 = serverUrl.replace(/\/+$/, "");
|
|
27825
|
+
const startRes = await fetch(`${base4}/api/auth/device/code`, {
|
|
27762
27826
|
method: "POST",
|
|
27763
27827
|
headers: { "content-type": "application/json" },
|
|
27764
27828
|
body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID })
|
|
@@ -27766,7 +27830,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
|
27766
27830
|
if (!startRes.ok) {
|
|
27767
27831
|
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
|
|
27768
27832
|
}
|
|
27769
|
-
const start = await readDeviceCode(startRes,
|
|
27833
|
+
const start = await readDeviceCode(startRes, base4);
|
|
27770
27834
|
onPrompt({
|
|
27771
27835
|
url: start.verification_uri_complete || start.verification_uri,
|
|
27772
27836
|
userCode: start.user_code,
|
|
@@ -27776,7 +27840,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
|
27776
27840
|
let interval = (start.interval ?? 5) * 1e3;
|
|
27777
27841
|
while (Date.now() < deadline) {
|
|
27778
27842
|
await sleep(interval);
|
|
27779
|
-
const res = await fetch(`${
|
|
27843
|
+
const res = await fetch(`${base4}/api/auth/device/token`, {
|
|
27780
27844
|
method: "POST",
|
|
27781
27845
|
headers: { "content-type": "application/json" },
|
|
27782
27846
|
body: JSON.stringify({
|
|
@@ -27878,7 +27942,7 @@ async function selectSessions(target, opts, fetchImpl = fetch) {
|
|
|
27878
27942
|
const qs = new URLSearchParams({ limit: String(PAGE), offset: String(offset) });
|
|
27879
27943
|
if (opts.project) qs.set("project", opts.project);
|
|
27880
27944
|
if (opts.tool) qs.set("tool", opts.tool);
|
|
27881
|
-
const res = await fetchImpl(`${
|
|
27945
|
+
const res = await fetchImpl(`${base3(target.serverUrl)}/api/conversations/recent?${qs}`, {
|
|
27882
27946
|
headers: { authorization: `Bearer ${target.token}` }
|
|
27883
27947
|
});
|
|
27884
27948
|
if (!res.ok) throw new Error(`list failed: HTTP ${res.status}`);
|
|
@@ -27907,7 +27971,7 @@ async function bulkDelete(ids, opts = {}) {
|
|
|
27907
27971
|
try {
|
|
27908
27972
|
for (let i = 0; i < unique.length; i += BATCH) {
|
|
27909
27973
|
const slice = unique.slice(i, i + BATCH);
|
|
27910
|
-
const res = await fetchImpl(`${
|
|
27974
|
+
const res = await fetchImpl(`${base3(t.serverUrl)}/api/sync`, {
|
|
27911
27975
|
method: "POST",
|
|
27912
27976
|
headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
|
|
27913
27977
|
body: JSON.stringify({
|
|
@@ -27943,7 +28007,7 @@ async function bulkDelete(ids, opts = {}) {
|
|
|
27943
28007
|
return out;
|
|
27944
28008
|
}
|
|
27945
28009
|
async function listTombstones(target, limit = 500, fetchImpl = fetch) {
|
|
27946
|
-
const res = await fetchImpl(`${
|
|
28010
|
+
const res = await fetchImpl(`${base3(target.serverUrl)}/api/data/tombstones?limit=${limit}`, {
|
|
27947
28011
|
headers: { authorization: `Bearer ${target.token}` }
|
|
27948
28012
|
});
|
|
27949
28013
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -27960,7 +28024,7 @@ async function restoreSessions(ids, opts = {}) {
|
|
|
27960
28024
|
try {
|
|
27961
28025
|
let restored = 0, notDeleted = 0;
|
|
27962
28026
|
for (let i = 0; i < unique.length; i += BATCH) {
|
|
27963
|
-
const res = await fetchImpl(`${
|
|
28027
|
+
const res = await fetchImpl(`${base3(t.serverUrl)}/api/data/restore`, {
|
|
27964
28028
|
method: "POST",
|
|
27965
28029
|
headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
|
|
27966
28030
|
body: JSON.stringify({ session_ids: unique.slice(i, i + BATCH) })
|
|
@@ -27983,7 +28047,7 @@ function needsConfirmation(opts) {
|
|
|
27983
28047
|
const enumeratedByCaller = opts.argvIds > 0 && !opts.project && !opts.stdin;
|
|
27984
28048
|
return !enumeratedByCaller;
|
|
27985
28049
|
}
|
|
27986
|
-
var PAGE, BATCH,
|
|
28050
|
+
var PAGE, BATCH, base3;
|
|
27987
28051
|
var init_bulk_delete = __esm({
|
|
27988
28052
|
"src/bulk-delete.ts"() {
|
|
27989
28053
|
"use strict";
|
|
@@ -27991,7 +28055,7 @@ var init_bulk_delete = __esm({
|
|
|
27991
28055
|
init_sync_ledger();
|
|
27992
28056
|
PAGE = 200;
|
|
27993
28057
|
BATCH = 250;
|
|
27994
|
-
|
|
28058
|
+
base3 = (url) => url.replace(/\/+$/, "");
|
|
27995
28059
|
}
|
|
27996
28060
|
});
|
|
27997
28061
|
|
|
@@ -28356,13 +28420,13 @@ function requireTarget() {
|
|
|
28356
28420
|
}
|
|
28357
28421
|
return t;
|
|
28358
28422
|
}
|
|
28359
|
-
async function readJsonBody(res,
|
|
28423
|
+
async function readJsonBody(res, base4, what) {
|
|
28360
28424
|
const text = await res.text().catch(() => "");
|
|
28361
28425
|
try {
|
|
28362
28426
|
return JSON.parse(text);
|
|
28363
28427
|
} catch {
|
|
28364
28428
|
throw new Error(
|
|
28365
|
-
`${
|
|
28429
|
+
`${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`
|
|
28366
28430
|
);
|
|
28367
28431
|
}
|
|
28368
28432
|
}
|
|
@@ -29168,9 +29232,9 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
29168
29232
|
return;
|
|
29169
29233
|
}
|
|
29170
29234
|
const target = firstTarget();
|
|
29171
|
-
const
|
|
29235
|
+
const base4 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
|
|
29172
29236
|
try {
|
|
29173
|
-
const res = await fetch(`${
|
|
29237
|
+
const res = await fetch(`${base4}/api/contact/feedback`, {
|
|
29174
29238
|
method: "POST",
|
|
29175
29239
|
headers: {
|
|
29176
29240
|
"content-type": "application/json",
|
|
@@ -29197,14 +29261,14 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
29197
29261
|
console.log(chalk2.dim(" Add --email <address> next time if you want a reply."));
|
|
29198
29262
|
}
|
|
29199
29263
|
} catch (err) {
|
|
29200
|
-
console.error(chalk2.red(`Could not reach ${
|
|
29264
|
+
console.error(chalk2.red(`Could not reach ${base4}.`));
|
|
29201
29265
|
console.error(chalk2.dim(" Your message, so it is not lost:"));
|
|
29202
29266
|
console.error(chalk2.dim(` ${message}`));
|
|
29203
29267
|
process.exitCode = 1;
|
|
29204
29268
|
}
|
|
29205
29269
|
});
|
|
29206
29270
|
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) => {
|
|
29207
|
-
const { existsSync: existsSync58, readFileSync: readFileSync50, statSync:
|
|
29271
|
+
const { existsSync: existsSync58, readFileSync: readFileSync50, statSync: statSync46 } = await import("fs");
|
|
29208
29272
|
const { execSync: execSync6 } = await import("child_process");
|
|
29209
29273
|
const rows = [];
|
|
29210
29274
|
const note = (ok, label, detail) => rows.push({ ok, label, detail });
|
|
@@ -29272,7 +29336,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
29272
29336
|
note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
|
|
29273
29337
|
} else {
|
|
29274
29338
|
try {
|
|
29275
|
-
const mode =
|
|
29339
|
+
const mode = statSync46(credFile).mode & 511;
|
|
29276
29340
|
const isWin = process.platform === "win32";
|
|
29277
29341
|
const safe = isWin || (mode & 63) === 0;
|
|
29278
29342
|
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"})`);
|
|
@@ -29802,13 +29866,13 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
|
|
|
29802
29866
|
}
|
|
29803
29867
|
const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
29804
29868
|
for (const t of targets) {
|
|
29805
|
-
const
|
|
29869
|
+
const base4 = t.serverUrl.replace(/\/+$/, "");
|
|
29806
29870
|
const headers = {};
|
|
29807
29871
|
if (t.token) headers.authorization = `Bearer ${t.token}`;
|
|
29808
29872
|
let deleted = 0, missing = 0, failed = 0;
|
|
29809
29873
|
for (const id of ids) {
|
|
29810
29874
|
try {
|
|
29811
|
-
const res = await fetchWithTimeout2(`${
|
|
29875
|
+
const res = await fetchWithTimeout2(`${base4}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
|
|
29812
29876
|
if (res.ok) deleted++;
|
|
29813
29877
|
else if (res.status === 404) missing++;
|
|
29814
29878
|
else failed++;
|
|
@@ -29816,7 +29880,7 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
|
|
|
29816
29880
|
failed++;
|
|
29817
29881
|
}
|
|
29818
29882
|
}
|
|
29819
|
-
const line = ` ${
|
|
29883
|
+
const line = ` ${base4}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
|
|
29820
29884
|
console.log(failed ? chalk2.yellow(line) : chalk2.green(line));
|
|
29821
29885
|
if (failed) process.exitCode = 1;
|
|
29822
29886
|
}
|
|
@@ -30598,7 +30662,7 @@ program.command("task-hook").description("Move a board card when a prompt or a c
|
|
|
30598
30662
|
}
|
|
30599
30663
|
});
|
|
30600
30664
|
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) => {
|
|
30601
|
-
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync:
|
|
30665
|
+
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync46, unlinkSync: unlinkSync4 } = await import("fs");
|
|
30602
30666
|
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
30603
30667
|
const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
|
|
30604
30668
|
const here = fileURLToPath6(new URL(".", import.meta.url));
|
|
@@ -30813,18 +30877,18 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30813
30877
|
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
30814
30878
|
}
|
|
30815
30879
|
if (installGuard) await addGuardToOtherTools(installedGuardHook);
|
|
30816
|
-
const saveSz =
|
|
30880
|
+
const saveSz = statSync46(installedSaveHook).size;
|
|
30817
30881
|
console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
|
|
30818
30882
|
if (installResume) {
|
|
30819
|
-
const resumeSz =
|
|
30883
|
+
const resumeSz = statSync46(installedResumeHook).size;
|
|
30820
30884
|
console.log(chalk2.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
|
|
30821
30885
|
}
|
|
30822
30886
|
if (installEscalate) {
|
|
30823
|
-
const escalateSz =
|
|
30887
|
+
const escalateSz = statSync46(installedEscalateHook).size;
|
|
30824
30888
|
console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
|
|
30825
30889
|
}
|
|
30826
30890
|
if (installGuard) {
|
|
30827
|
-
const guardSz =
|
|
30891
|
+
const guardSz = statSync46(installedGuardHook).size;
|
|
30828
30892
|
console.log(chalk2.green(`\u2713 Installed chat-recall decision guard (${guardSz} bytes)`));
|
|
30829
30893
|
}
|
|
30830
30894
|
if (installTaskBoard) {
|
|
@@ -30832,7 +30896,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30832
30896
|
console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
|
|
30833
30897
|
}
|
|
30834
30898
|
if (installWakeup) {
|
|
30835
|
-
const wakeupSz =
|
|
30899
|
+
const wakeupSz = statSync46(installedWakeupHook).size;
|
|
30836
30900
|
console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
|
|
30837
30901
|
for (const f of wakeupSkipped) {
|
|
30838
30902
|
console.log(chalk2.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));
|
|
@@ -30949,10 +31013,10 @@ program.command("service <action>").description("Manage the per-user background
|
|
|
30949
31013
|
});
|
|
30950
31014
|
async function runLogin(serverUrl, opts) {
|
|
30951
31015
|
const { saveCredentials: saveCredentials2, loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
30952
|
-
const
|
|
31016
|
+
const base4 = serverUrl.replace(/\/+$/, "");
|
|
30953
31017
|
const verifyToken = async (token) => {
|
|
30954
31018
|
try {
|
|
30955
|
-
const res = await fetch(`${
|
|
31019
|
+
const res = await fetch(`${base4}/api/status`, {
|
|
30956
31020
|
headers: token ? { authorization: `Bearer ${token}` } : {},
|
|
30957
31021
|
signal: AbortSignal.timeout(1e4)
|
|
30958
31022
|
});
|
|
@@ -30968,36 +31032,36 @@ async function runLogin(serverUrl, opts) {
|
|
|
30968
31032
|
return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
|
|
30969
31033
|
}
|
|
30970
31034
|
const { probeServer: identifyServer, probeOk: identified, probeAdvice: identifyAdvice } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
30971
|
-
const who = await identifyServer(
|
|
30972
|
-
if (!identified(who)) return failLogin(opts, identifyAdvice(who,
|
|
31035
|
+
const who = await identifyServer(base4, { timeoutMs: 1e4 });
|
|
31036
|
+
if (!identified(who)) return failLogin(opts, identifyAdvice(who, base4));
|
|
30973
31037
|
const check = await verifyToken(token);
|
|
30974
31038
|
if (!check.ok) {
|
|
30975
31039
|
if (check.status === 401 || check.status === 403) {
|
|
30976
31040
|
console.error(chalk2.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
|
|
30977
|
-
return failLogin(opts, `${
|
|
31041
|
+
return failLogin(opts, `${base4} rejected the token (HTTP ${check.status})`);
|
|
30978
31042
|
}
|
|
30979
31043
|
if (check.status !== void 0 && check.status >= 500) {
|
|
30980
|
-
return failLogin(opts, `${
|
|
31044
|
+
return failLogin(opts, `${base4} answered HTTP ${check.status} \u2014 the server is up but failing. The token was never checked. Nothing to fix locally.`);
|
|
30981
31045
|
}
|
|
30982
31046
|
if (check.status === 404 || check.status === 410) {
|
|
30983
|
-
return failLogin(opts, `${
|
|
31047
|
+
return failLogin(opts, `${base4} does not serve the chat-recall API (HTTP ${check.status}). If the server moved, log in to its new URL.`);
|
|
30984
31048
|
}
|
|
30985
|
-
return failLogin(opts, `could not verify the token against ${
|
|
31049
|
+
return failLogin(opts, `could not verify the token against ${base4}: ${check.error || `HTTP ${check.status}`}`);
|
|
30986
31050
|
}
|
|
30987
31051
|
saveCredentials2({ serverUrl, token });
|
|
30988
31052
|
console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` server: ${serverUrl} (token verified)`));
|
|
30989
31053
|
return;
|
|
30990
31054
|
}
|
|
30991
|
-
const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") ===
|
|
31055
|
+
const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base4);
|
|
30992
31056
|
if (existing && (await verifyToken(existing.token)).ok) {
|
|
30993
31057
|
console.log(chalk2.green("\u2713 Already connected.") + chalk2.dim(` server: ${serverUrl} (existing token verified)`));
|
|
30994
31058
|
return;
|
|
30995
31059
|
}
|
|
30996
31060
|
try {
|
|
30997
31061
|
const { probeServer: probeServer2, probeOk: probeOk2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
30998
|
-
const identified = await probeServer2(
|
|
31062
|
+
const identified = await probeServer2(base4, { timeoutMs: 8e3 });
|
|
30999
31063
|
if (probeOk2(identified)) {
|
|
31000
|
-
const probe = await fetch(`${
|
|
31064
|
+
const probe = await fetch(`${base4}/api/status`, { signal: AbortSignal.timeout(8e3) });
|
|
31001
31065
|
if (probe.status === 200) {
|
|
31002
31066
|
saveCredentials2({ serverUrl, token: "" });
|
|
31003
31067
|
console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
|
|
@@ -31018,16 +31082,16 @@ async function runLogin(serverUrl, opts) {
|
|
|
31018
31082
|
if (!issuer) {
|
|
31019
31083
|
const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
31020
31084
|
for (let attempt = 1; attempt <= 3 && !authProvider; attempt++) {
|
|
31021
|
-
const probe = await probeServer2(
|
|
31085
|
+
const probe = await probeServer2(base4, { timeoutMs: 15e3 });
|
|
31022
31086
|
if (probeOk2(probe)) {
|
|
31023
31087
|
const caps = probe.raw;
|
|
31024
31088
|
authProvider = caps?.authProvider;
|
|
31025
31089
|
if (caps?.oidcIssuer) issuer = caps.oidcIssuer;
|
|
31026
31090
|
capsError = null;
|
|
31027
31091
|
} else if (probe.kind === "moved" || probe.kind === "not_api") {
|
|
31028
|
-
return failLogin(opts, probeAdvice2(probe,
|
|
31092
|
+
return failLogin(opts, probeAdvice2(probe, base4));
|
|
31029
31093
|
} else {
|
|
31030
|
-
capsError = probeAdvice2(probe,
|
|
31094
|
+
capsError = probeAdvice2(probe, base4);
|
|
31031
31095
|
}
|
|
31032
31096
|
if (!authProvider && attempt < 3) await new Promise((r) => setTimeout(r, attempt * 1e3));
|
|
31033
31097
|
}
|
|
@@ -31036,7 +31100,7 @@ async function runLogin(serverUrl, opts) {
|
|
|
31036
31100
|
authProvider = "better-auth";
|
|
31037
31101
|
if (capsError) {
|
|
31038
31102
|
console.log(` ${chalk2.yellow("Could not read the server's capabilities")} (${capsError}) \u2014 assuming the standard sign-in flow.`);
|
|
31039
|
-
void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(
|
|
31103
|
+
void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base4, {
|
|
31040
31104
|
step: "cli_login_probe",
|
|
31041
31105
|
reason: capsError?.slice(0, 40) ?? null,
|
|
31042
31106
|
message: capsError
|
|
@@ -31058,20 +31122,20 @@ async function runLogin(serverUrl, opts) {
|
|
|
31058
31122
|
console.log(chalk2.dim(` If the page asks for a code: ${chalk2.bold(p.userCode)}`));
|
|
31059
31123
|
console.log(chalk2.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
|
|
31060
31124
|
};
|
|
31061
|
-
const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(
|
|
31125
|
+
const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base4, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
|
|
31062
31126
|
const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };
|
|
31063
|
-
const meRes = await fetch(`${
|
|
31064
|
-
if (!meRes.ok) throw new Error(`${
|
|
31065
|
-
const me = await readJsonBody(meRes,
|
|
31127
|
+
const meRes = await fetch(`${base4}/api/me`, { headers: authHdr });
|
|
31128
|
+
if (!meRes.ok) throw new Error(`${base4}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
|
|
31129
|
+
const me = await readJsonBody(meRes, base4, "GET /api/me");
|
|
31066
31130
|
const teams = me.teams || [];
|
|
31067
31131
|
let slug = opts.team;
|
|
31068
31132
|
if (!slug) {
|
|
31069
31133
|
if (teams.length === 1) slug = teams[0].team_slug;
|
|
31070
31134
|
else if (teams.length === 0) {
|
|
31071
31135
|
const name = me.user?.email?.split("@")[0]?.replace(/[^a-z0-9]/gi, "") || "workspace";
|
|
31072
|
-
const created = await fetch(`${
|
|
31136
|
+
const created = await fetch(`${base4}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
|
|
31073
31137
|
if (!created.ok) throw new Error(`workspace create failed: HTTP ${created.status} ${await created.text().catch(() => "")}`);
|
|
31074
|
-
slug = (await readJsonBody(created,
|
|
31138
|
+
slug = (await readJsonBody(created, base4, "POST /api/teams")).slug;
|
|
31075
31139
|
console.log(chalk2.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
|
|
31076
31140
|
} else {
|
|
31077
31141
|
teams.forEach((t) => console.error(` ${t.team_slug} ${chalk2.dim(t.name)}`));
|
|
@@ -31079,13 +31143,13 @@ async function runLogin(serverUrl, opts) {
|
|
|
31079
31143
|
}
|
|
31080
31144
|
}
|
|
31081
31145
|
const deviceId = opts.deviceId || (await import("node:os")).hostname();
|
|
31082
|
-
const mint = await fetch(`${
|
|
31146
|
+
const mint = await fetch(`${base4}/api/teams/${encodeURIComponent(slug)}/tokens`, {
|
|
31083
31147
|
method: "POST",
|
|
31084
31148
|
headers: authHdr,
|
|
31085
31149
|
body: JSON.stringify({ device_id: deviceId })
|
|
31086
31150
|
});
|
|
31087
31151
|
if (!mint.ok) throw new Error(`token mint failed: HTTP ${mint.status} ${await mint.text().catch(() => "")}`);
|
|
31088
|
-
const { token } = await readJsonBody(mint,
|
|
31152
|
+
const { token } = await readJsonBody(mint, base4, "POST /api/teams/:slug/tokens");
|
|
31089
31153
|
saveCredentials2({ serverUrl, token });
|
|
31090
31154
|
console.log(chalk2.green(`\u2713 Logged in to ${chalk2.bold(slug)}`) + chalk2.dim(` (device: ${deviceId}) server: ${serverUrl}`));
|
|
31091
31155
|
if (!opts.fromInit) {
|
|
@@ -31562,16 +31626,16 @@ program.command("update").description("Update this CLI now from the server you s
|
|
|
31562
31626
|
}
|
|
31563
31627
|
let installFailed = false;
|
|
31564
31628
|
for (const cred of targets) {
|
|
31565
|
-
const
|
|
31629
|
+
const base4 = cred.serverUrl.replace(/\/+$/, "");
|
|
31566
31630
|
const headers = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
31567
|
-
const r = await runAutoUpdate2(
|
|
31631
|
+
const r = await runAutoUpdate2(base4, headers, cliVersion3());
|
|
31568
31632
|
if (r.updated) {
|
|
31569
|
-
console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${
|
|
31633
|
+
console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base4})`));
|
|
31570
31634
|
continue;
|
|
31571
31635
|
}
|
|
31572
31636
|
const benign = /already current|no CLI release|disabled/.test(r.reason);
|
|
31573
31637
|
if (/install failed|download failed|checksum/.test(r.reason)) installFailed = true;
|
|
31574
|
-
console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${
|
|
31638
|
+
console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base4})`));
|
|
31575
31639
|
}
|
|
31576
31640
|
const check = await refreshUpdateCheck2(true);
|
|
31577
31641
|
if (installFailed) {
|