chat-recall 0.6.9 → 0.7.1
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/README.md +2 -2
- package/dist/auto-update.d.ts +34 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +51 -8
- package/dist/auto-update.js.map +1 -1
- package/dist/cli.js +1448 -1661
- package/dist/cli.js.map +1 -1
- package/dist/escalate.js +2 -2
- package/dist/escalate.js.map +1 -1
- package/dist/mcp-relay.js +76 -13
- package/dist/mcp-relay.js.map +1 -1
- package/dist/mcp.js +2573 -2223
- package/dist/mcp.js.map +1 -1
- package/dist/relay-lifecycle.d.ts +69 -0
- package/dist/relay-lifecycle.d.ts.map +1 -0
- package/dist/relay-lifecycle.js +117 -0
- package/dist/relay-lifecycle.js.map +1 -0
- package/dist/repair.d.ts +2 -0
- package/dist/repair.d.ts.map +1 -1
- package/dist/repair.js +23 -3
- package/dist/repair.js.map +1 -1
- package/dist/scan-worker.js +48 -18
- package/dist/service-installer.d.ts.map +1 -1
- package/dist/service-installer.js +7 -2
- package/dist/service-installer.js.map +1 -1
- package/dist/sync-client.d.ts +38 -5
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +191 -58
- package/dist/sync-client.js.map +1 -1
- package/dist/sync-supervisor.d.ts +43 -0
- package/dist/sync-supervisor.d.ts.map +1 -0
- package/dist/sync-supervisor.js +84 -0
- package/dist/sync-supervisor.js.map +1 -0
- package/dist/watch.js +753 -998
- package/package.json +1 -1
- package/skills/chat-recall/SKILL.md +2 -1
- package/skills/chat-recall-memory/SKILL.md +15 -0
package/dist/cli.js
CHANGED
|
@@ -4549,6 +4549,25 @@ var init_paths = __esm({
|
|
|
4549
4549
|
}
|
|
4550
4550
|
});
|
|
4551
4551
|
|
|
4552
|
+
// ../engine/dist/parsers/banners.js
|
|
4553
|
+
function stripInjectedBanners(text) {
|
|
4554
|
+
let result = text;
|
|
4555
|
+
for (const re of INJECTED_BANNERS)
|
|
4556
|
+
result = result.replace(re, " ");
|
|
4557
|
+
return result;
|
|
4558
|
+
}
|
|
4559
|
+
var INJECTED_BANNERS;
|
|
4560
|
+
var init_banners = __esm({
|
|
4561
|
+
"../engine/dist/parsers/banners.js"() {
|
|
4562
|
+
"use strict";
|
|
4563
|
+
INJECTED_BANNERS = [
|
|
4564
|
+
/MCP issues detected\. ?Run \/mcp list for status\.?/g,
|
|
4565
|
+
/Context low[^\n]*Run \/compact[^\n]*/g,
|
|
4566
|
+
/API Error:[^\n]{0,120}/g
|
|
4567
|
+
];
|
|
4568
|
+
}
|
|
4569
|
+
});
|
|
4570
|
+
|
|
4552
4571
|
// ../engine/dist/core/project-path-match.js
|
|
4553
4572
|
function canonicalProjectPath(p) {
|
|
4554
4573
|
return (p || "").trim().replace(/\\/g, "/").replace(/[-_]/g, "/").replace(/\/{2,}/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -5083,866 +5102,247 @@ var init_settings = __esm({
|
|
|
5083
5102
|
}
|
|
5084
5103
|
});
|
|
5085
5104
|
|
|
5086
|
-
// ../engine/dist/core/
|
|
5087
|
-
var
|
|
5088
|
-
__export(
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
isFilePathRedactionEnabled: () => isFilePathRedactionEnabled,
|
|
5097
|
-
isRedactionEnabled: () => isRedactionEnabled,
|
|
5098
|
-
isToolOutputRedactionEnabled: () => isToolOutputRedactionEnabled,
|
|
5099
|
-
redactFilePaths: () => redactFilePaths,
|
|
5100
|
-
redactSecrets: () => redactSecrets,
|
|
5101
|
-
redactToolResultBody: () => redactToolResultBody,
|
|
5102
|
-
scanTextForFindings: () => scanTextForFindings,
|
|
5103
|
-
serverRulePackVersion: () => serverRulePackVersion,
|
|
5104
|
-
validateRedactionRule: () => validateRedactionRule
|
|
5105
|
+
// ../engine/dist/core/home-approval.js
|
|
5106
|
+
var home_approval_exports = {};
|
|
5107
|
+
__export(home_approval_exports, {
|
|
5108
|
+
approveHome: () => approveHome,
|
|
5109
|
+
declineHome: () => declineHome,
|
|
5110
|
+
grandfatherLegacyHomes: () => grandfatherLegacyHomes,
|
|
5111
|
+
homeDecision: () => homeDecision,
|
|
5112
|
+
isHomeSynced: () => isHomeSynced,
|
|
5113
|
+
normalizeHomePath: () => normalizeHomePath,
|
|
5114
|
+
resetHomeDecision: () => resetHomeDecision
|
|
5105
5115
|
});
|
|
5106
|
-
import {
|
|
5107
|
-
import {
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
return
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
function looksLikeSecretToken(t) {
|
|
5117
|
-
if (t.length < 32)
|
|
5118
|
-
return false;
|
|
5119
|
-
if (/^[a-f0-9]+$/i.test(t))
|
|
5120
|
-
return true;
|
|
5121
|
-
return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
|
|
5122
|
-
}
|
|
5123
|
-
function findContextualSecrets(text) {
|
|
5124
|
-
if (!text)
|
|
5125
|
-
return [];
|
|
5126
|
-
const ranges = [];
|
|
5127
|
-
let m;
|
|
5128
|
-
SECRET_CONTEXT_RE.lastIndex = 0;
|
|
5129
|
-
while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
|
|
5130
|
-
const winStart = m.index + m[0].length;
|
|
5131
|
-
const window = text.slice(winStart, winStart + 80);
|
|
5132
|
-
HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
|
|
5133
|
-
let tm;
|
|
5134
|
-
while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
|
|
5135
|
-
if (!looksLikeSecretToken(tm[0]))
|
|
5136
|
-
continue;
|
|
5137
|
-
if (/^[a-f0-9]+$/i.test(tm[0]) && /\b(?:commit|sha-?\d*|hash|digest|checksum|uuid|guid|etag|blob|ref)\b/i.test(window.slice(0, tm.index))) {
|
|
5138
|
-
continue;
|
|
5139
|
-
}
|
|
5140
|
-
ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
|
|
5141
|
-
break;
|
|
5142
|
-
}
|
|
5116
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
|
|
5117
|
+
import { homedir as homedir2 } from "os";
|
|
5118
|
+
import { join as join3, dirname as dirname3 } from "path";
|
|
5119
|
+
function normalizeHomePath(p) {
|
|
5120
|
+
const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
|
|
5121
|
+
const trimmed = expanded.replace(/[/\\]+$/, "");
|
|
5122
|
+
try {
|
|
5123
|
+
return realpathSync(trimmed);
|
|
5124
|
+
} catch {
|
|
5125
|
+
return trimmed;
|
|
5143
5126
|
}
|
|
5144
|
-
|
|
5127
|
+
}
|
|
5128
|
+
function legacyAutoSyncedHomes() {
|
|
5129
|
+
const home = homedir2();
|
|
5145
5130
|
const out = [];
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5131
|
+
const add = (dir, marker) => {
|
|
5132
|
+
if (!existsSync3(join3(dir, marker)))
|
|
5133
|
+
return;
|
|
5134
|
+
const n = normalizeHomePath(dir);
|
|
5135
|
+
if (!out.includes(n))
|
|
5136
|
+
out.push(n);
|
|
5137
|
+
};
|
|
5138
|
+
const bases = [
|
|
5139
|
+
{ base: join3(home, ".claude"), marker: "projects" },
|
|
5140
|
+
{ base: join3(home, ".gemini"), marker: "tmp" },
|
|
5141
|
+
{ base: join3(home, ".codex"), marker: "sessions" },
|
|
5142
|
+
{ base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
|
|
5143
|
+
{ base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5144
|
+
{ base: join3(home, ".cursor"), marker: "chats" }
|
|
5145
|
+
];
|
|
5146
|
+
for (const { base: base3, marker } of bases) {
|
|
5147
|
+
add(base3, marker);
|
|
5148
|
+
const parent = dirname3(base3);
|
|
5149
|
+
const leaf = base3.slice(parent.length + 1);
|
|
5150
|
+
try {
|
|
5151
|
+
for (const e of readdirSync2(parent, { withFileTypes: true })) {
|
|
5152
|
+
if (!e.isDirectory())
|
|
5153
|
+
continue;
|
|
5154
|
+
if (e.name === leaf || !e.name.startsWith(leaf + "-"))
|
|
5155
|
+
continue;
|
|
5156
|
+
if (e.name === ".claude-code")
|
|
5157
|
+
continue;
|
|
5158
|
+
add(join3(parent, e.name), marker);
|
|
5159
|
+
}
|
|
5160
|
+
} catch {
|
|
5151
5161
|
}
|
|
5152
|
-
out.push({ ...r });
|
|
5153
5162
|
}
|
|
5154
5163
|
return out;
|
|
5155
5164
|
}
|
|
5156
|
-
function
|
|
5157
|
-
|
|
5158
|
-
if (v === void 0)
|
|
5159
|
-
return null;
|
|
5160
|
-
return v === "1" || v === "true" || v === "yes";
|
|
5161
|
-
}
|
|
5162
|
-
function settingsView() {
|
|
5163
|
-
let mtimeMs = -1;
|
|
5165
|
+
function grandfatherLegacyHomes() {
|
|
5166
|
+
let s;
|
|
5164
5167
|
try {
|
|
5165
|
-
|
|
5168
|
+
s = loadSettings();
|
|
5166
5169
|
} catch {
|
|
5170
|
+
return { seeded: [] };
|
|
5167
5171
|
}
|
|
5168
|
-
if (
|
|
5169
|
-
return
|
|
5170
|
-
|
|
5171
|
-
const
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
|
|
5178
|
-
} catch {
|
|
5179
|
-
}
|
|
5172
|
+
if (s.sources.homesGrandfathered)
|
|
5173
|
+
return { seeded: [] };
|
|
5174
|
+
const legacy = legacyAutoSyncedHomes();
|
|
5175
|
+
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5176
|
+
const seeded = [];
|
|
5177
|
+
for (const p of legacy) {
|
|
5178
|
+
if (!approved.has(p)) {
|
|
5179
|
+
approved.add(p);
|
|
5180
|
+
seeded.push(p);
|
|
5180
5181
|
}
|
|
5182
|
+
}
|
|
5183
|
+
s.sources.approvedHomes = [...approved];
|
|
5184
|
+
s.sources.homesGrandfathered = true;
|
|
5185
|
+
try {
|
|
5186
|
+
saveSettings(s);
|
|
5181
5187
|
} catch {
|
|
5188
|
+
return { seeded: [] };
|
|
5182
5189
|
}
|
|
5183
|
-
|
|
5184
|
-
return cache;
|
|
5190
|
+
return { seeded };
|
|
5185
5191
|
}
|
|
5186
|
-
function
|
|
5187
|
-
const
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
if (spec.regex.length > MAX_PATTERN_LEN)
|
|
5191
|
-
return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
|
|
5192
|
-
let re;
|
|
5192
|
+
function primaryHomes() {
|
|
5193
|
+
const home = homedir2();
|
|
5194
|
+
const env = process.env;
|
|
5195
|
+
let s;
|
|
5193
5196
|
try {
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
|
|
5197
|
+
s = loadSettings().sources;
|
|
5198
|
+
} catch {
|
|
5199
|
+
s = void 0;
|
|
5198
5200
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
return "reason" in out ? { ok: false, reason: out.reason } : { ok: true };
|
|
5201
|
+
const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
|
|
5202
|
+
const candidates2 = [
|
|
5203
|
+
env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
|
|
5204
|
+
env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
|
|
5205
|
+
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5206
|
+
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5207
|
+
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5208
|
+
dirOf(opencodeDb)
|
|
5209
|
+
];
|
|
5210
|
+
return candidates2.map(normalizeHomePath);
|
|
5210
5211
|
}
|
|
5211
|
-
function
|
|
5212
|
-
const
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
if ("reason" in out)
|
|
5221
|
-
rejected.push(out);
|
|
5222
|
-
else
|
|
5223
|
-
rules.push(out);
|
|
5212
|
+
function homeDecision(path) {
|
|
5213
|
+
const p = normalizeHomePath(path);
|
|
5214
|
+
if (primaryHomes().includes(p))
|
|
5215
|
+
return "primary";
|
|
5216
|
+
let s;
|
|
5217
|
+
try {
|
|
5218
|
+
s = loadSettings().sources;
|
|
5219
|
+
} catch {
|
|
5220
|
+
return "pending";
|
|
5224
5221
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
function _clearServerRulePack() {
|
|
5233
|
-
serverPack = null;
|
|
5222
|
+
if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5223
|
+
return "declined";
|
|
5224
|
+
if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5225
|
+
return "approved";
|
|
5226
|
+
if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5227
|
+
return "approved";
|
|
5228
|
+
return "pending";
|
|
5234
5229
|
}
|
|
5235
|
-
function
|
|
5236
|
-
const
|
|
5237
|
-
return
|
|
5230
|
+
function isHomeSynced(path) {
|
|
5231
|
+
const d = homeDecision(path);
|
|
5232
|
+
return d === "primary" || d === "approved";
|
|
5238
5233
|
}
|
|
5239
|
-
function
|
|
5240
|
-
const
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5234
|
+
function setDecision(path, decision) {
|
|
5235
|
+
const p = normalizeHomePath(path);
|
|
5236
|
+
let s;
|
|
5237
|
+
try {
|
|
5238
|
+
s = loadSettings();
|
|
5239
|
+
} catch {
|
|
5240
|
+
return false;
|
|
5241
|
+
}
|
|
5242
|
+
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5243
|
+
const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
|
|
5244
|
+
approved.delete(p);
|
|
5245
|
+
declined.delete(p);
|
|
5246
|
+
(decision === "approved" ? approved : declined).add(p);
|
|
5247
|
+
s.sources.approvedHomes = [...approved];
|
|
5248
|
+
s.sources.declinedHomes = [...declined];
|
|
5249
|
+
try {
|
|
5250
|
+
saveSettings(s);
|
|
5251
|
+
} catch {
|
|
5252
|
+
return false;
|
|
5253
|
+
}
|
|
5254
|
+
return true;
|
|
5244
5255
|
}
|
|
5245
|
-
function
|
|
5246
|
-
|
|
5256
|
+
function approveHome(path) {
|
|
5257
|
+
return setDecision(path, "approved");
|
|
5247
5258
|
}
|
|
5248
|
-
function
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
const m = args.slice(0, args.length);
|
|
5259
|
-
const secret = secretOf(r, m);
|
|
5260
|
-
if (secret == null)
|
|
5261
|
-
return whole;
|
|
5262
|
-
if (opts.count)
|
|
5263
|
-
opts.count.redactions++;
|
|
5264
|
-
return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
|
|
5265
|
-
});
|
|
5259
|
+
function declineHome(path) {
|
|
5260
|
+
return setDecision(path, "declined");
|
|
5261
|
+
}
|
|
5262
|
+
function resetHomeDecision(path) {
|
|
5263
|
+
const p = normalizeHomePath(path);
|
|
5264
|
+
let s;
|
|
5265
|
+
try {
|
|
5266
|
+
s = loadSettings();
|
|
5267
|
+
} catch {
|
|
5268
|
+
return false;
|
|
5266
5269
|
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5270
|
+
s.sources.approvedHomes = (s.sources.approvedHomes ?? []).filter((x) => normalizeHomePath(x) !== p);
|
|
5271
|
+
s.sources.declinedHomes = (s.sources.declinedHomes ?? []).filter((x) => normalizeHomePath(x) !== p);
|
|
5272
|
+
try {
|
|
5273
|
+
saveSettings(s);
|
|
5274
|
+
} catch {
|
|
5275
|
+
return false;
|
|
5273
5276
|
}
|
|
5274
|
-
return
|
|
5275
|
-
}
|
|
5276
|
-
function maskSecret(s) {
|
|
5277
|
-
if (!s)
|
|
5278
|
-
return "";
|
|
5279
|
-
if (s.length <= 4)
|
|
5280
|
-
return "*".repeat(s.length);
|
|
5281
|
-
return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
|
|
5277
|
+
return true;
|
|
5282
5278
|
}
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
globalRegexCache.set(key2, re);
|
|
5279
|
+
var dirOf;
|
|
5280
|
+
var init_home_approval = __esm({
|
|
5281
|
+
"../engine/dist/core/home-approval.js"() {
|
|
5282
|
+
"use strict";
|
|
5283
|
+
init_settings();
|
|
5284
|
+
dirOf = (filePath) => dirname3(filePath);
|
|
5290
5285
|
}
|
|
5291
|
-
|
|
5286
|
+
});
|
|
5287
|
+
|
|
5288
|
+
// ../engine/dist/core/tool-paths.js
|
|
5289
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3 } from "fs";
|
|
5290
|
+
import { homedir as homedir3 } from "os";
|
|
5291
|
+
import { join as join4, dirname as dirname4, basename } from "path";
|
|
5292
|
+
function claudeHomeDir() {
|
|
5293
|
+
return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
|
|
5292
5294
|
}
|
|
5293
|
-
function
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
const mid = lo + hi >> 1;
|
|
5301
|
-
if (starts[mid] < index)
|
|
5302
|
-
lo = mid + 1;
|
|
5303
|
-
else
|
|
5304
|
-
hi = mid;
|
|
5305
|
-
}
|
|
5306
|
-
return lo + 1;
|
|
5295
|
+
function claudeProjectDirs(opts = {}) {
|
|
5296
|
+
const home = homedir3();
|
|
5297
|
+
const dirs = [];
|
|
5298
|
+
const addHome = (d) => {
|
|
5299
|
+
const p = join4(d, "projects");
|
|
5300
|
+
if (existsSync4(p) && !dirs.includes(p))
|
|
5301
|
+
dirs.push(p);
|
|
5307
5302
|
};
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
if (!
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
for (const r of rules) {
|
|
5317
|
-
const re = globalRegexFor(r.pattern);
|
|
5318
|
-
re.lastIndex = 0;
|
|
5319
|
-
let m;
|
|
5320
|
-
while ((m = re.exec(text)) !== null) {
|
|
5321
|
-
const secret = secretOf(r, m);
|
|
5322
|
-
if (secret == null) {
|
|
5323
|
-
if (re.lastIndex === m.index)
|
|
5324
|
-
re.lastIndex++;
|
|
5325
|
-
continue;
|
|
5326
|
-
}
|
|
5327
|
-
const at = m.index + (m[0].length - secret.length);
|
|
5328
|
-
const span = `${at}:${secret.length}`;
|
|
5329
|
-
if (!seenSpans.has(span)) {
|
|
5330
|
-
seenSpans.add(span);
|
|
5331
|
-
findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
|
|
5303
|
+
addHome(claudeHomeDir());
|
|
5304
|
+
const homeOverridden = !!(process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome);
|
|
5305
|
+
if (!homeOverridden) {
|
|
5306
|
+
try {
|
|
5307
|
+
for (const entry of readdirSync3(home, { withFileTypes: true })) {
|
|
5308
|
+
if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
|
|
5309
|
+
addHome(join4(home, entry.name));
|
|
5310
|
+
}
|
|
5332
5311
|
}
|
|
5333
|
-
|
|
5334
|
-
re.lastIndex++;
|
|
5312
|
+
} catch {
|
|
5335
5313
|
}
|
|
5336
5314
|
}
|
|
5337
|
-
|
|
5338
|
-
const
|
|
5339
|
-
|
|
5315
|
+
if (process.env.CLAUDE_DIRS) {
|
|
5316
|
+
for (const d of process.env.CLAUDE_DIRS.split(",")) {
|
|
5317
|
+
const t = d.trim();
|
|
5318
|
+
if (t)
|
|
5319
|
+
addHome(t.startsWith("~/") ? join4(home, t.slice(2)) : t);
|
|
5320
|
+
}
|
|
5340
5321
|
}
|
|
5341
|
-
|
|
5322
|
+
if (dirs.length === 0)
|
|
5323
|
+
dirs.push(join4(claudeHomeDir(), "projects"));
|
|
5324
|
+
return applyDecisions(dirs, opts.includeExcluded);
|
|
5342
5325
|
}
|
|
5343
|
-
function
|
|
5344
|
-
|
|
5345
|
-
try {
|
|
5346
|
-
mtimeMs = statSync3(settingsFilePath()).mtimeMs;
|
|
5347
|
-
} catch {
|
|
5348
|
-
}
|
|
5349
|
-
if (privacyCache && privacyCache.mtimeMs === mtimeMs)
|
|
5350
|
-
return privacyCache;
|
|
5351
|
-
let redactToolOutputs = false, redactFilePaths2 = false;
|
|
5352
|
-
try {
|
|
5353
|
-
const s = loadSettings();
|
|
5354
|
-
redactToolOutputs = !!s.privacy?.redactToolOutputs;
|
|
5355
|
-
redactFilePaths2 = !!s.privacy?.redactFilePaths;
|
|
5356
|
-
} catch {
|
|
5357
|
-
}
|
|
5358
|
-
privacyCache = { mtimeMs, redactToolOutputs, redactFilePaths: redactFilePaths2 };
|
|
5359
|
-
return privacyCache;
|
|
5326
|
+
function _setSourceExclusionFilter(fn) {
|
|
5327
|
+
_sourceExclusionFilter = fn;
|
|
5360
5328
|
}
|
|
5361
|
-
function
|
|
5362
|
-
|
|
5329
|
+
function geminiHomeDir() {
|
|
5330
|
+
return process.env.CHAT_RECALL_GEMINI_HOME || sources().geminiHome || join4(homedir3(), ".gemini");
|
|
5363
5331
|
}
|
|
5364
|
-
function
|
|
5365
|
-
return
|
|
5332
|
+
function opencodeDbPath() {
|
|
5333
|
+
return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
|
|
5366
5334
|
}
|
|
5367
|
-
function
|
|
5368
|
-
return
|
|
5335
|
+
function codexHomeDir() {
|
|
5336
|
+
return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
|
|
5369
5337
|
}
|
|
5370
|
-
function
|
|
5371
|
-
|
|
5372
|
-
return text;
|
|
5373
|
-
return text.replace(PATH_REGEX, (m) => {
|
|
5374
|
-
if (m.startsWith("[REDACTED:"))
|
|
5375
|
-
return m;
|
|
5376
|
-
const h = createHash("sha256").update(m).digest("hex").slice(0, 12);
|
|
5377
|
-
return `[path:${h}]`;
|
|
5378
|
-
});
|
|
5338
|
+
function agyHomeDir() {
|
|
5339
|
+
return process.env.CHAT_RECALL_AGY_HOME || sources().agyHome || join4(homedir3(), ".gemini", "antigravity-cli");
|
|
5379
5340
|
}
|
|
5380
|
-
function
|
|
5381
|
-
|
|
5382
|
-
if (privacyView().redactFilePaths)
|
|
5383
|
-
out = redactFilePaths(out);
|
|
5384
|
-
return out;
|
|
5341
|
+
function cursorHomeDir() {
|
|
5342
|
+
return process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome || join4(homedir3(), ".cursor");
|
|
5385
5343
|
}
|
|
5386
|
-
function
|
|
5387
|
-
|
|
5388
|
-
return body;
|
|
5389
|
-
return `[redacted: ${toolName} output, ${body.length} chars]`;
|
|
5390
|
-
}
|
|
5391
|
-
var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache, privacyCache, PATH_REGEX;
|
|
5392
|
-
var init_secret_redactor = __esm({
|
|
5393
|
-
"../engine/dist/core/secret-redactor.js"() {
|
|
5394
|
-
"use strict";
|
|
5395
|
-
init_settings();
|
|
5396
|
-
DEFAULT_REDACTION_RULES = [
|
|
5397
|
-
// AWS access tokens. Prefix-anchored: AKIA, ASIA, ABIA (long-term,
|
|
5398
|
-
// STS, EC2). Always exactly 16 hex/alphanumeric after the prefix.
|
|
5399
|
-
// NOTE: this matches only the access-key-ID, NOT the secret key value —
|
|
5400
|
-
// the `env-secret` rule below covers AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN.
|
|
5401
|
-
{ label: "aws-access-token", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
|
|
5402
|
-
// AWS STS session tokens. These are long base64 blobs with NO `NAME=` context
|
|
5403
|
-
// when pasted bare into prose/code, so `env-secret` misses them — but they
|
|
5404
|
-
// reliably start with one of a handful of base64 prefixes (the encoded
|
|
5405
|
-
// `{"...` JSON header). Prefix-anchored → low false-positive. This is the
|
|
5406
|
-
// bare-value form that leaked (`search('IQoJEXAMPLEfakeSESSIONtokenNOTreal01234567890abcDEF==...')`).
|
|
5407
|
-
{ label: "aws-session-token", pattern: /\b(?:IQoJ|FwoG|FwoH|FQoG|FQoH|FwoD)[A-Za-z0-9/+]{40,}={0,2}/g },
|
|
5408
|
-
// Env/shell-assignment secrets — a VAR whose name ends in KEY/SECRET/TOKEN/
|
|
5409
|
-
// PASSWORD/CREDENTIAL, then `=`/`:`, then the value. Zero-width lookbehind so
|
|
5410
|
-
// only the VALUE is redacted (the var name stays for context). This is the
|
|
5411
|
-
// dominant real-world leak vector (`export` dumps, .env files, `curl -H`,
|
|
5412
|
-
// tool output) and is exactly what leaked AWS_SECRET_ACCESS_KEY and
|
|
5413
|
-
// AWS_SESSION_TOKEN in cleartext while the access-key-ID rule above caught
|
|
5414
|
-
// only the (least-sensitive) ID.
|
|
5415
|
-
// Suffix list deliberately broad: DB_PASS (our own helm chart's name!),
|
|
5416
|
-
// NPM_AUTH, SENTRY_DSN, SESSION/COOKIE secrets and BEARER vars all leaked
|
|
5417
|
-
// past the original KEY/SECRET/TOKEN/PASSWORD-only list. Values under 12
|
|
5418
|
-
// chars don't redact, so flag-ish vars (BYPASS=true) stay readable.
|
|
5419
|
-
// Context CONSUMED, secret in group 1 — see RedactionRule.secret. As a
|
|
5420
|
-
// lookbehind this was the second most expensive rule in the collector.
|
|
5421
|
-
{ label: "env-secret", secret: 1, pattern: /\b[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL|AUTH|DSN|SESSION|COOKIE|BEARER|SIGNATURE)S?["']?[ \t]{0,3}[:=][ \t]{0,3}["']?([A-Za-z0-9/+_.~=:@-]{12,})/g },
|
|
5422
|
-
// Credentials embedded in a connection URL: scheme://user:PASSWORD@host.
|
|
5423
|
-
// Redacts only the password segment (keeps scheme/user/host for context).
|
|
5424
|
-
//
|
|
5425
|
-
// Scheme-agnostic on purpose. The old rule allowlisted eight DB schemes, so
|
|
5426
|
-
// `https://user:pass@host`, `ssh://`, `ftp://`, `smtp://` and every other
|
|
5427
|
-
// userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
|
|
5428
|
-
// prefix broke the \b anchor for the one family it did cover. Any scheme
|
|
5429
|
-
// followed by userinfo is a credential, so match the shape, not a list.
|
|
5430
|
-
{ label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
|
|
5431
|
-
// Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
|
|
5432
|
-
// containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
|
|
5433
|
-
// requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
|
|
5434
|
-
// excludes longer blobs/tokens. It catches the value when it appears bare in
|
|
5435
|
-
// prose with NO `NAME=` and NO nearby context word (e.g. inside a markdown
|
|
5436
|
-
// table or quoted in analysis) — the residual leak path the env-secret and
|
|
5437
|
-
// contextual rules both miss. Placed AFTER env-secret/url-password so the
|
|
5438
|
-
// keyed/URL forms keep their more-specific labels.
|
|
5439
|
-
// The three stacked lookaheads each re-scanned the same 40 characters, which
|
|
5440
|
-
// made this the MOST expensive rule of all 106 the collector runs (13.1% of a
|
|
5441
|
-
// 29.5 MB scan). The mixed-case-plus-digit test is now one JS pass.
|
|
5442
|
-
{
|
|
5443
|
-
label: "aws-secret-key",
|
|
5444
|
-
secret: 1,
|
|
5445
|
-
pattern: /(?<![A-Za-z0-9/+])([A-Za-z0-9/+]{40})(?![A-Za-z0-9/+])/g,
|
|
5446
|
-
where: (v) => /[a-z]/.test(v) && /[A-Z]/.test(v) && /[0-9]/.test(v)
|
|
5447
|
-
},
|
|
5448
|
-
// GitHub personal access tokens.
|
|
5449
|
-
{ label: "github-pat", pattern: /\bghp_[A-Za-z0-9]{36}\b/g },
|
|
5450
|
-
// GitHub fine-grained PATs.
|
|
5451
|
-
{ label: "github-fine", pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g },
|
|
5452
|
-
// GitHub OAuth.
|
|
5453
|
-
{ label: "github-oauth", pattern: /\bgho_[A-Za-z0-9]{36}\b/g },
|
|
5454
|
-
// GitHub app + refresh.
|
|
5455
|
-
{ label: "github-app", pattern: /\b(?:ghs|ghu|ghr)_[A-Za-z0-9]{36}\b/g },
|
|
5456
|
-
// Slack bot/user/app tokens.
|
|
5457
|
-
{ label: "slack-token", pattern: /\bxox[abopr]-[A-Za-z0-9-]{10,}\b/g },
|
|
5458
|
-
// Slack incoming webhook URL.
|
|
5459
|
-
{ label: "slack-webhook", pattern: /\bhttps:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+\b/g },
|
|
5460
|
-
// Stripe live + test keys.
|
|
5461
|
-
{ label: "stripe-key", pattern: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g },
|
|
5462
|
-
// Anthropic.
|
|
5463
|
-
{ label: "anthropic-key", pattern: /\bsk-ant-[a-zA-Z0-9_-]{40,}\b/g },
|
|
5464
|
-
// OpenAI. Two shapes, deliberately kept apart. The classic key is
|
|
5465
|
-
// `sk-` + base62 with NO hyphens; the modern scoped keys carry an explicit
|
|
5466
|
-
// proj/svcacct/admin segment first. Allowing hyphens after a bare `sk-`
|
|
5467
|
-
// (which is what admitted sk-proj- in the first place) also matches ordinary
|
|
5468
|
-
// kebab-case text — `sk-fix-the-login-redirect-bug`, `.sk-button-primary`,
|
|
5469
|
-
// `sk-2024-01-01-backup` — and this corpus is full of branch names and CSS
|
|
5470
|
-
// classes. Requiring the segment keeps sk-proj- coverage without that.
|
|
5471
|
-
{ label: "openai-key", pattern: /\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b|\bsk-[A-Za-z0-9]{20,}\b/g },
|
|
5472
|
-
// Google API key (AIza…).
|
|
5473
|
-
{ label: "google-api-key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
5474
|
-
// Generic JWT — three base64url segments.
|
|
5475
|
-
{ label: "jwt", pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
|
|
5476
|
-
// Private key blocks (PEM). The label is a free word run rather than a fixed
|
|
5477
|
-
// list, so ENCRYPTED PRIVATE KEY and PGP PRIVATE KEY BLOCK are covered
|
|
5478
|
-
// without another enumeration, and \1 forces END to name the same kind as
|
|
5479
|
-
// BEGIN.
|
|
5480
|
-
//
|
|
5481
|
-
// The body is [^-], NOT [\s\S]*?. That is a performance boundary, not a
|
|
5482
|
-
// style choice. An unanchored prefix with an unbounded lazy gap re-scans to
|
|
5483
|
-
// end-of-input from every candidate start, so text carrying many BEGIN
|
|
5484
|
-
// markers and no matching END costs O(n^2): 500KB of that measured 810ms
|
|
5485
|
-
// here, and this runs synchronously on the server's event loop over
|
|
5486
|
-
// tenant-supplied text (services/secret-rescan.ts). A PEM body is base64
|
|
5487
|
-
// plus headers and contains no '-', so excluding it makes the scan stop at
|
|
5488
|
-
// the next delimiter instead of at EOF — same matches, linear cost.
|
|
5489
|
-
{ label: "private-key", pattern: /-----BEGIN ([A-Z0-9 ]{0,32})PRIVATE KEY(?: BLOCK)?-----[^-]{0,10000}?-----END \1PRIVATE KEY(?: BLOCK)?-----/g },
|
|
5490
|
-
// PuTTY keys are not PEM. Match the Private-Lines payload — the actual key
|
|
5491
|
-
// material — rather than spanning the whole file, which would need the same
|
|
5492
|
-
// unbounded gap the rule above just removed. Line-anchored and base64-only,
|
|
5493
|
-
// so it cannot run away.
|
|
5494
|
-
{ label: "putty-key", pattern: /^Private-Lines:[ \t]*\d{1,4}[\r\n]+(?:[A-Za-z0-9+/=]{8,120}[\r\n]+){1,400}/gm },
|
|
5495
|
-
// npm tokens (uuid-shaped — overlap with our UUID filter; intentional).
|
|
5496
|
-
{ label: "npm-token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
5497
|
-
// .npmrc auth lines: //registry.npmjs.org/:_authToken=VALUE (the key is not
|
|
5498
|
-
// ALL-CAPS, so the env-secret rule above never matched it). The leading `:`
|
|
5499
|
-
// is required — it is what makes this npmrc syntax rather than any shell var
|
|
5500
|
-
// ending in _AUTH, which env-secret already owns and labels better.
|
|
5501
|
-
// [^\s"'`]+ so the closing quote is not swallowed, and a ${VAR} placeholder
|
|
5502
|
-
// is left alone — redacting it destroys a config example and hides nothing.
|
|
5503
|
-
// The rule the perf profile caught the wedged daemon inside: 98.94% of samples.
|
|
5504
|
-
{ label: "npmrc-auth", secret: 1, pattern: /:_(?:authToken|password|auth)[ \t]*=[ \t]*(?!\$\{)([^\s"'`]{12,})/gi },
|
|
5505
|
-
// HTTP Authorization headers. env-secret requires an ALL-CAPS name, and
|
|
5506
|
-
// `Authorization` is mixed case, so both Bearer and Basic walked straight
|
|
5507
|
-
// through — including short opaque tokens the contextual pass also misses
|
|
5508
|
-
// (it needs 32+ chars AND mixed-case-plus-digit).
|
|
5509
|
-
// Same two guards as npmrc-auth: stop at a quote, and skip the obvious
|
|
5510
|
-
// placeholders that fill documentation (<YOUR_TOKEN>, ${TOKEN}, xxx…).
|
|
5511
|
-
{ label: "auth-header", secret: 1, pattern: /\bauthorization[ \t]*:[ \t]*(?:bearer|basic|token)[ \t]+(?![<$])([^\s"'`<]{8,})/gi },
|
|
5512
|
-
// Vendor tokens with unambiguous prefixes. Each is a fixed literal, so a
|
|
5513
|
-
// false positive is essentially impossible and the cost of listing is low.
|
|
5514
|
-
{ label: "gitlab-pat", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
5515
|
-
{ label: "slack-app-token", pattern: /\bxapp-\d-[A-Z0-9]{8,}-\d{10,}-[a-f0-9]{32,}\b/g },
|
|
5516
|
-
{ label: "sendgrid-key", pattern: /\bSG\.[A-Za-z0-9_-]{20,24}\.[A-Za-z0-9_-]{40,50}\b/g },
|
|
5517
|
-
// (?<![-\w]) so `cache-key-<md5>` is not read as a mailgun key.
|
|
5518
|
-
{ label: "mailgun-key", pattern: /(?<![-\w])key-[a-f0-9]{32}\b/g },
|
|
5519
|
-
{ label: "digitalocean-pat", pattern: /\bdop_v1_[a-f0-9]{64}\b/g },
|
|
5520
|
-
{ label: "huggingface-token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g },
|
|
5521
|
-
{ label: "shopify-token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g },
|
|
5522
|
-
{ label: "pypi-token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}\b/g },
|
|
5523
|
-
// No twilio-sid rule: AC…/SK… are Twilio SIDs, which are public account and
|
|
5524
|
-
// key identifiers, not the auth token. Redacting them hid nothing and
|
|
5525
|
-
// collided with 32-hex etags and MD5 sums.
|
|
5526
|
-
// No discord-bot-token rule either: the shape (word.word.word) matched
|
|
5527
|
-
// ordinary dotted symbols such as a.b.ApplicationContextProviderImpl.
|
|
5528
|
-
{ label: "telegram-bot-token", pattern: /\b\d{8,10}:AA[A-Za-z0-9_-]{32,}\b/g },
|
|
5529
|
-
{ label: "doppler-token", pattern: /\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9_-]{20,}\b/g },
|
|
5530
|
-
// Azure Storage SAS signature — the `sig=` parameter is the actual secret.
|
|
5531
|
-
{ label: "azure-sas", secret: 1, pattern: /[?&]sig=([A-Za-z0-9%/+=]{20,})/g }
|
|
5532
|
-
];
|
|
5533
|
-
SECRET_CONTEXT_RE = /\b(?:secret|password|passwd|credential|access[ _-]?key|private[ _-]?key|api[ _-]?key|auth(?:oriz\w*)?|bearer|token)\b/gi;
|
|
5534
|
-
HIGH_ENTROPY_TOKEN_RE = /[A-Za-z0-9/+_-]{32,}/g;
|
|
5535
|
-
cache = null;
|
|
5536
|
-
CANARY = "the quick brown fox jumps over the lazy dog 0123456789 commit 8ba1f109551bd432803012645ac136ddd475d0fb src/index.ts:42";
|
|
5537
|
-
MAX_PACK_RULES = 200;
|
|
5538
|
-
MAX_PATTERN_LEN = 512;
|
|
5539
|
-
serverPack = null;
|
|
5540
|
-
globalRegexCache = /* @__PURE__ */ new Map();
|
|
5541
|
-
privacyCache = null;
|
|
5542
|
-
PATH_REGEX = /(?<![A-Za-z0-9_])(?:\/[^\s'"`<>]+|[A-Za-z]:\\[^\s'"`<>]+)/g;
|
|
5543
|
-
}
|
|
5544
|
-
});
|
|
5545
|
-
|
|
5546
|
-
// ../engine/dist/parsers/chunker.js
|
|
5547
|
-
function stripInjectedBanners(text) {
|
|
5548
|
-
let result = text;
|
|
5549
|
-
for (const re of INJECTED_BANNERS)
|
|
5550
|
-
result = result.replace(re, " ");
|
|
5551
|
-
return result;
|
|
5552
|
-
}
|
|
5553
|
-
function cleanText(text) {
|
|
5554
|
-
let result = text.replace(/<system-reminder>.*?<\/system-reminder>/gs, "");
|
|
5555
|
-
result = stripInjectedBanners(result);
|
|
5556
|
-
result = result.replace(/\x1b\[[0-9;]*m/g, "");
|
|
5557
|
-
result = result.replace(/\s+/g, " ");
|
|
5558
|
-
return result.trim();
|
|
5559
|
-
}
|
|
5560
|
-
function windowText(text, maxChars, maxWindows = 12) {
|
|
5561
|
-
const clean = cleanText(text);
|
|
5562
|
-
if (clean.length <= maxChars)
|
|
5563
|
-
return clean ? [clean] : [];
|
|
5564
|
-
const out = [];
|
|
5565
|
-
const overlap = 80;
|
|
5566
|
-
const step = Math.max(1, maxChars - overlap);
|
|
5567
|
-
for (let i = 0; i < clean.length && out.length < maxWindows; i += step) {
|
|
5568
|
-
out.push(clean.slice(i, i + maxChars));
|
|
5569
|
-
}
|
|
5570
|
-
return out;
|
|
5571
|
-
}
|
|
5572
|
-
function chunkSession(entry, content, maxChunks = 80, maxTokensPerChunk = 500) {
|
|
5573
|
-
const chunks = [];
|
|
5574
|
-
const maxChars = maxTokensPerChunk * 4;
|
|
5575
|
-
const baseData = {
|
|
5576
|
-
sessionId: entry.sessionId,
|
|
5577
|
-
projectPath: entry.projectPath,
|
|
5578
|
-
created: entry.created,
|
|
5579
|
-
modified: entry.modified,
|
|
5580
|
-
mtime: entry.fileMtime,
|
|
5581
|
-
firstPrompt: entry.firstPrompt || content.firstPrompt,
|
|
5582
|
-
sourceFile: content.sessionPath
|
|
5583
|
-
};
|
|
5584
|
-
for (let i = 0; i < Math.min(2, content.summaries.length); i++) {
|
|
5585
|
-
const summary = content.summaries[i];
|
|
5586
|
-
if (!summary?.trim())
|
|
5587
|
-
continue;
|
|
5588
|
-
const chunkText = cleanText(summary.slice(0, maxChars));
|
|
5589
|
-
if (chunkText.length > 8e3) {
|
|
5590
|
-
chunks.push({
|
|
5591
|
-
chunkId: `${entry.sessionId}_summary_${i}`,
|
|
5592
|
-
chunkType: "summary",
|
|
5593
|
-
text: chunkText.slice(0, 8e3),
|
|
5594
|
-
sourceLine: 0,
|
|
5595
|
-
...baseData
|
|
5596
|
-
});
|
|
5597
|
-
} else {
|
|
5598
|
-
chunks.push({
|
|
5599
|
-
chunkId: `${entry.sessionId}_summary_${i}`,
|
|
5600
|
-
chunkType: "summary",
|
|
5601
|
-
text: chunkText,
|
|
5602
|
-
sourceLine: 0,
|
|
5603
|
-
...baseData
|
|
5604
|
-
});
|
|
5605
|
-
}
|
|
5606
|
-
}
|
|
5607
|
-
if (chunks.length >= maxChunks) {
|
|
5608
|
-
return chunks.slice(0, maxChunks);
|
|
5609
|
-
}
|
|
5610
|
-
const firstPrompt = entry.firstPrompt || content.firstPrompt;
|
|
5611
|
-
if (firstPrompt?.trim()) {
|
|
5612
|
-
const firstLine = content.userMessages[0]?.lineNumber || 0;
|
|
5613
|
-
chunks.push({
|
|
5614
|
-
chunkId: `${entry.sessionId}_first_prompt`,
|
|
5615
|
-
chunkType: "first_prompt",
|
|
5616
|
-
text: cleanText(firstPrompt.slice(0, maxChars)),
|
|
5617
|
-
sourceLine: firstLine,
|
|
5618
|
-
...baseData
|
|
5619
|
-
});
|
|
5620
|
-
}
|
|
5621
|
-
if (chunks.length >= maxChunks) {
|
|
5622
|
-
return chunks.slice(0, maxChunks);
|
|
5623
|
-
}
|
|
5624
|
-
const userStart = firstPrompt ? 1 : 0;
|
|
5625
|
-
for (let i = userStart; i < content.userMessages.length; i++) {
|
|
5626
|
-
if (chunks.length >= maxChunks)
|
|
5627
|
-
break;
|
|
5628
|
-
const msg = content.userMessages[i];
|
|
5629
|
-
if (!msg.text.trim())
|
|
5630
|
-
continue;
|
|
5631
|
-
const windows = windowText(msg.text, maxChars);
|
|
5632
|
-
for (let w = 0; w < windows.length; w++) {
|
|
5633
|
-
if (chunks.length >= maxChunks)
|
|
5634
|
-
break;
|
|
5635
|
-
chunks.push({
|
|
5636
|
-
chunkId: `${entry.sessionId}_user_${i}_${w}`,
|
|
5637
|
-
chunkType: "user_context",
|
|
5638
|
-
text: windows[w],
|
|
5639
|
-
sourceLine: msg.lineNumber,
|
|
5640
|
-
...baseData
|
|
5641
|
-
});
|
|
5642
|
-
}
|
|
5643
|
-
}
|
|
5644
|
-
if (chunks.length >= maxChunks) {
|
|
5645
|
-
return chunks.slice(0, maxChunks);
|
|
5646
|
-
}
|
|
5647
|
-
for (let i = 0; i < content.assistantMessages.length; i++) {
|
|
5648
|
-
if (chunks.length >= maxChunks)
|
|
5649
|
-
break;
|
|
5650
|
-
const msg = content.assistantMessages[i];
|
|
5651
|
-
if (!msg.text.trim() || msg.text.length <= 50)
|
|
5652
|
-
continue;
|
|
5653
|
-
const windows = windowText(msg.text, maxChars);
|
|
5654
|
-
for (let w = 0; w < windows.length; w++) {
|
|
5655
|
-
if (chunks.length >= maxChunks)
|
|
5656
|
-
break;
|
|
5657
|
-
chunks.push({
|
|
5658
|
-
chunkId: `${entry.sessionId}_assistant_${i}_${w}`,
|
|
5659
|
-
chunkType: "assistant",
|
|
5660
|
-
text: windows[w],
|
|
5661
|
-
sourceLine: msg.lineNumber,
|
|
5662
|
-
...baseData
|
|
5663
|
-
});
|
|
5664
|
-
}
|
|
5665
|
-
}
|
|
5666
|
-
if (chunks.length >= maxChunks) {
|
|
5667
|
-
return chunks.slice(0, maxChunks);
|
|
5668
|
-
}
|
|
5669
|
-
if (content.toolResults.length > 0) {
|
|
5670
|
-
const webSearches = content.toolResults.filter((r) => r.contentType === "web_search");
|
|
5671
|
-
const otherResults = content.toolResults.filter((r) => r.contentType !== "web_search");
|
|
5672
|
-
const resultsToProcess = [...webSearches.slice(0, 5), ...otherResults.slice(0, 10)];
|
|
5673
|
-
const stripBodies = isToolOutputRedactionEnabled();
|
|
5674
|
-
for (let i = 0; i < resultsToProcess.length; i++) {
|
|
5675
|
-
if (chunks.length >= maxChunks)
|
|
5676
|
-
break;
|
|
5677
|
-
const result = resultsToProcess[i];
|
|
5678
|
-
if (result.text.trim()) {
|
|
5679
|
-
const body = stripBodies && result.contentType !== "web_search" ? redactToolResultBody(result.contentType, result.text) : result.text.slice(0, maxChars);
|
|
5680
|
-
chunks.push({
|
|
5681
|
-
chunkId: `${entry.sessionId}_${result.contentType}_${i}`,
|
|
5682
|
-
chunkType: result.contentType,
|
|
5683
|
-
text: cleanText(body),
|
|
5684
|
-
sourceLine: result.lineNumber,
|
|
5685
|
-
...baseData
|
|
5686
|
-
});
|
|
5687
|
-
}
|
|
5688
|
-
}
|
|
5689
|
-
}
|
|
5690
|
-
return chunks.slice(0, maxChunks);
|
|
5691
|
-
}
|
|
5692
|
-
var INJECTED_BANNERS;
|
|
5693
|
-
var init_chunker = __esm({
|
|
5694
|
-
"../engine/dist/parsers/chunker.js"() {
|
|
5695
|
-
"use strict";
|
|
5696
|
-
init_secret_redactor();
|
|
5697
|
-
INJECTED_BANNERS = [
|
|
5698
|
-
/MCP issues detected\. ?Run \/mcp list for status\.?/g,
|
|
5699
|
-
/Context low[^\n]*Run \/compact[^\n]*/g,
|
|
5700
|
-
/API Error:[^\n]{0,120}/g
|
|
5701
|
-
];
|
|
5702
|
-
}
|
|
5703
|
-
});
|
|
5704
|
-
|
|
5705
|
-
// ../engine/dist/core/home-approval.js
|
|
5706
|
-
var home_approval_exports = {};
|
|
5707
|
-
__export(home_approval_exports, {
|
|
5708
|
-
approveHome: () => approveHome,
|
|
5709
|
-
declineHome: () => declineHome,
|
|
5710
|
-
grandfatherLegacyHomes: () => grandfatherLegacyHomes,
|
|
5711
|
-
homeDecision: () => homeDecision,
|
|
5712
|
-
isHomeSynced: () => isHomeSynced,
|
|
5713
|
-
normalizeHomePath: () => normalizeHomePath,
|
|
5714
|
-
resetHomeDecision: () => resetHomeDecision
|
|
5715
|
-
});
|
|
5716
|
-
import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
|
|
5717
|
-
import { homedir as homedir2 } from "os";
|
|
5718
|
-
import { join as join3, dirname as dirname3 } from "path";
|
|
5719
|
-
function normalizeHomePath(p) {
|
|
5720
|
-
const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
|
|
5721
|
-
const trimmed = expanded.replace(/[/\\]+$/, "");
|
|
5722
|
-
try {
|
|
5723
|
-
return realpathSync(trimmed);
|
|
5724
|
-
} catch {
|
|
5725
|
-
return trimmed;
|
|
5726
|
-
}
|
|
5727
|
-
}
|
|
5728
|
-
function legacyAutoSyncedHomes() {
|
|
5729
|
-
const home = homedir2();
|
|
5730
|
-
const out = [];
|
|
5731
|
-
const add = (dir, marker) => {
|
|
5732
|
-
if (!existsSync3(join3(dir, marker)))
|
|
5733
|
-
return;
|
|
5734
|
-
const n = normalizeHomePath(dir);
|
|
5735
|
-
if (!out.includes(n))
|
|
5736
|
-
out.push(n);
|
|
5737
|
-
};
|
|
5738
|
-
const bases = [
|
|
5739
|
-
{ base: join3(home, ".claude"), marker: "projects" },
|
|
5740
|
-
{ base: join3(home, ".gemini"), marker: "tmp" },
|
|
5741
|
-
{ base: join3(home, ".codex"), marker: "sessions" },
|
|
5742
|
-
{ base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
|
|
5743
|
-
{ base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5744
|
-
{ base: join3(home, ".cursor"), marker: "chats" }
|
|
5745
|
-
];
|
|
5746
|
-
for (const { base: base3, marker } of bases) {
|
|
5747
|
-
add(base3, marker);
|
|
5748
|
-
const parent = dirname3(base3);
|
|
5749
|
-
const leaf = base3.slice(parent.length + 1);
|
|
5750
|
-
try {
|
|
5751
|
-
for (const e of readdirSync2(parent, { withFileTypes: true })) {
|
|
5752
|
-
if (!e.isDirectory())
|
|
5753
|
-
continue;
|
|
5754
|
-
if (e.name === leaf || !e.name.startsWith(leaf + "-"))
|
|
5755
|
-
continue;
|
|
5756
|
-
if (e.name === ".claude-code")
|
|
5757
|
-
continue;
|
|
5758
|
-
add(join3(parent, e.name), marker);
|
|
5759
|
-
}
|
|
5760
|
-
} catch {
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5763
|
-
return out;
|
|
5764
|
-
}
|
|
5765
|
-
function grandfatherLegacyHomes() {
|
|
5766
|
-
let s;
|
|
5767
|
-
try {
|
|
5768
|
-
s = loadSettings();
|
|
5769
|
-
} catch {
|
|
5770
|
-
return { seeded: [] };
|
|
5771
|
-
}
|
|
5772
|
-
if (s.sources.homesGrandfathered)
|
|
5773
|
-
return { seeded: [] };
|
|
5774
|
-
const legacy = legacyAutoSyncedHomes();
|
|
5775
|
-
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5776
|
-
const seeded = [];
|
|
5777
|
-
for (const p of legacy) {
|
|
5778
|
-
if (!approved.has(p)) {
|
|
5779
|
-
approved.add(p);
|
|
5780
|
-
seeded.push(p);
|
|
5781
|
-
}
|
|
5782
|
-
}
|
|
5783
|
-
s.sources.approvedHomes = [...approved];
|
|
5784
|
-
s.sources.homesGrandfathered = true;
|
|
5785
|
-
try {
|
|
5786
|
-
saveSettings(s);
|
|
5787
|
-
} catch {
|
|
5788
|
-
return { seeded: [] };
|
|
5789
|
-
}
|
|
5790
|
-
return { seeded };
|
|
5791
|
-
}
|
|
5792
|
-
function primaryHomes() {
|
|
5793
|
-
const home = homedir2();
|
|
5794
|
-
const env = process.env;
|
|
5795
|
-
let s;
|
|
5796
|
-
try {
|
|
5797
|
-
s = loadSettings().sources;
|
|
5798
|
-
} catch {
|
|
5799
|
-
s = void 0;
|
|
5800
|
-
}
|
|
5801
|
-
const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
|
|
5802
|
-
const candidates2 = [
|
|
5803
|
-
env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
|
|
5804
|
-
env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
|
|
5805
|
-
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5806
|
-
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5807
|
-
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5808
|
-
dirOf(opencodeDb)
|
|
5809
|
-
];
|
|
5810
|
-
return candidates2.map(normalizeHomePath);
|
|
5811
|
-
}
|
|
5812
|
-
function homeDecision(path) {
|
|
5813
|
-
const p = normalizeHomePath(path);
|
|
5814
|
-
if (primaryHomes().includes(p))
|
|
5815
|
-
return "primary";
|
|
5816
|
-
let s;
|
|
5817
|
-
try {
|
|
5818
|
-
s = loadSettings().sources;
|
|
5819
|
-
} catch {
|
|
5820
|
-
return "pending";
|
|
5821
|
-
}
|
|
5822
|
-
if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5823
|
-
return "declined";
|
|
5824
|
-
if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5825
|
-
return "approved";
|
|
5826
|
-
if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5827
|
-
return "approved";
|
|
5828
|
-
return "pending";
|
|
5829
|
-
}
|
|
5830
|
-
function isHomeSynced(path) {
|
|
5831
|
-
const d = homeDecision(path);
|
|
5832
|
-
return d === "primary" || d === "approved";
|
|
5833
|
-
}
|
|
5834
|
-
function setDecision(path, decision) {
|
|
5835
|
-
const p = normalizeHomePath(path);
|
|
5836
|
-
let s;
|
|
5837
|
-
try {
|
|
5838
|
-
s = loadSettings();
|
|
5839
|
-
} catch {
|
|
5840
|
-
return false;
|
|
5841
|
-
}
|
|
5842
|
-
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5843
|
-
const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
|
|
5844
|
-
approved.delete(p);
|
|
5845
|
-
declined.delete(p);
|
|
5846
|
-
(decision === "approved" ? approved : declined).add(p);
|
|
5847
|
-
s.sources.approvedHomes = [...approved];
|
|
5848
|
-
s.sources.declinedHomes = [...declined];
|
|
5849
|
-
try {
|
|
5850
|
-
saveSettings(s);
|
|
5851
|
-
} catch {
|
|
5852
|
-
return false;
|
|
5853
|
-
}
|
|
5854
|
-
return true;
|
|
5855
|
-
}
|
|
5856
|
-
function approveHome(path) {
|
|
5857
|
-
return setDecision(path, "approved");
|
|
5858
|
-
}
|
|
5859
|
-
function declineHome(path) {
|
|
5860
|
-
return setDecision(path, "declined");
|
|
5861
|
-
}
|
|
5862
|
-
function resetHomeDecision(path) {
|
|
5863
|
-
const p = normalizeHomePath(path);
|
|
5864
|
-
let s;
|
|
5865
|
-
try {
|
|
5866
|
-
s = loadSettings();
|
|
5867
|
-
} catch {
|
|
5868
|
-
return false;
|
|
5869
|
-
}
|
|
5870
|
-
s.sources.approvedHomes = (s.sources.approvedHomes ?? []).filter((x) => normalizeHomePath(x) !== p);
|
|
5871
|
-
s.sources.declinedHomes = (s.sources.declinedHomes ?? []).filter((x) => normalizeHomePath(x) !== p);
|
|
5872
|
-
try {
|
|
5873
|
-
saveSettings(s);
|
|
5874
|
-
} catch {
|
|
5875
|
-
return false;
|
|
5876
|
-
}
|
|
5877
|
-
return true;
|
|
5878
|
-
}
|
|
5879
|
-
var dirOf;
|
|
5880
|
-
var init_home_approval = __esm({
|
|
5881
|
-
"../engine/dist/core/home-approval.js"() {
|
|
5882
|
-
"use strict";
|
|
5883
|
-
init_settings();
|
|
5884
|
-
dirOf = (filePath) => dirname3(filePath);
|
|
5885
|
-
}
|
|
5886
|
-
});
|
|
5887
|
-
|
|
5888
|
-
// ../engine/dist/core/tool-paths.js
|
|
5889
|
-
import { existsSync as existsSync4, readdirSync as readdirSync3 } from "fs";
|
|
5890
|
-
import { homedir as homedir3 } from "os";
|
|
5891
|
-
import { join as join4, dirname as dirname4, basename } from "path";
|
|
5892
|
-
function claudeHomeDir() {
|
|
5893
|
-
return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
|
|
5894
|
-
}
|
|
5895
|
-
function claudeProjectDirs(opts = {}) {
|
|
5896
|
-
const home = homedir3();
|
|
5897
|
-
const dirs = [];
|
|
5898
|
-
const addHome = (d) => {
|
|
5899
|
-
const p = join4(d, "projects");
|
|
5900
|
-
if (existsSync4(p) && !dirs.includes(p))
|
|
5901
|
-
dirs.push(p);
|
|
5902
|
-
};
|
|
5903
|
-
addHome(claudeHomeDir());
|
|
5904
|
-
const homeOverridden = !!(process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome);
|
|
5905
|
-
if (!homeOverridden) {
|
|
5906
|
-
try {
|
|
5907
|
-
for (const entry of readdirSync3(home, { withFileTypes: true })) {
|
|
5908
|
-
if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
|
|
5909
|
-
addHome(join4(home, entry.name));
|
|
5910
|
-
}
|
|
5911
|
-
}
|
|
5912
|
-
} catch {
|
|
5913
|
-
}
|
|
5914
|
-
}
|
|
5915
|
-
if (process.env.CLAUDE_DIRS) {
|
|
5916
|
-
for (const d of process.env.CLAUDE_DIRS.split(",")) {
|
|
5917
|
-
const t = d.trim();
|
|
5918
|
-
if (t)
|
|
5919
|
-
addHome(t.startsWith("~/") ? join4(home, t.slice(2)) : t);
|
|
5920
|
-
}
|
|
5921
|
-
}
|
|
5922
|
-
if (dirs.length === 0)
|
|
5923
|
-
dirs.push(join4(claudeHomeDir(), "projects"));
|
|
5924
|
-
return applyDecisions(dirs, opts.includeExcluded);
|
|
5925
|
-
}
|
|
5926
|
-
function _setSourceExclusionFilter(fn) {
|
|
5927
|
-
_sourceExclusionFilter = fn;
|
|
5928
|
-
}
|
|
5929
|
-
function geminiHomeDir() {
|
|
5930
|
-
return process.env.CHAT_RECALL_GEMINI_HOME || sources().geminiHome || join4(homedir3(), ".gemini");
|
|
5931
|
-
}
|
|
5932
|
-
function opencodeDbPath() {
|
|
5933
|
-
return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
|
|
5934
|
-
}
|
|
5935
|
-
function codexHomeDir() {
|
|
5936
|
-
return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
|
|
5937
|
-
}
|
|
5938
|
-
function agyHomeDir() {
|
|
5939
|
-
return process.env.CHAT_RECALL_AGY_HOME || sources().agyHome || join4(homedir3(), ".gemini", "antigravity-cli");
|
|
5940
|
-
}
|
|
5941
|
-
function cursorHomeDir() {
|
|
5942
|
-
return process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome || join4(homedir3(), ".cursor");
|
|
5943
|
-
}
|
|
5944
|
-
function cursorIdeHomeDir() {
|
|
5945
|
-
return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
|
|
5344
|
+
function cursorIdeHomeDir() {
|
|
5345
|
+
return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
|
|
5946
5346
|
}
|
|
5947
5347
|
function siblingHomes(base3, overridden, suffix) {
|
|
5948
5348
|
const roots = [];
|
|
@@ -6287,7 +5687,7 @@ var init_model_pricing = __esm({
|
|
|
6287
5687
|
});
|
|
6288
5688
|
|
|
6289
5689
|
// ../engine/dist/parsers/session.js
|
|
6290
|
-
import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync2, statSync as
|
|
5690
|
+
import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync2, statSync as statSync3, openSync, readSync, closeSync } from "fs";
|
|
6291
5691
|
import { createInterface } from "readline";
|
|
6292
5692
|
import { join as join5, basename as basename2 } from "path";
|
|
6293
5693
|
function removeCodeBlocks(text) {
|
|
@@ -6334,44 +5734,6 @@ function isValuableToolResult(content, toolName = "") {
|
|
|
6334
5734
|
}
|
|
6335
5735
|
return true;
|
|
6336
5736
|
}
|
|
6337
|
-
function parseSessionsIndex(indexPath) {
|
|
6338
|
-
if (!existsSync5(indexPath)) {
|
|
6339
|
-
return [];
|
|
6340
|
-
}
|
|
6341
|
-
const data = JSON.parse(readFileSync2(indexPath, "utf-8"));
|
|
6342
|
-
const entries = data.entries || [];
|
|
6343
|
-
return entries.map((e) => ({
|
|
6344
|
-
sessionId: e.sessionId || "",
|
|
6345
|
-
fullPath: e.fullPath || "",
|
|
6346
|
-
fileMtime: e.fileMtime || 0,
|
|
6347
|
-
firstPrompt: e.firstPrompt || "",
|
|
6348
|
-
messageCount: e.messageCount || 0,
|
|
6349
|
-
created: e.created || "",
|
|
6350
|
-
modified: e.modified || "",
|
|
6351
|
-
gitBranch: e.gitBranch || "",
|
|
6352
|
-
projectPath: e.projectPath || "",
|
|
6353
|
-
isSidechain: e.isSidechain || false
|
|
6354
|
-
}));
|
|
6355
|
-
}
|
|
6356
|
-
function* iterAllSessionIndices(claudeDir) {
|
|
6357
|
-
const dir = claudeDir || claudeBackend.projectsDir();
|
|
6358
|
-
if (!existsSync5(dir)) {
|
|
6359
|
-
return;
|
|
6360
|
-
}
|
|
6361
|
-
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
6362
|
-
for (const entry of entries) {
|
|
6363
|
-
if (!entry.isDirectory())
|
|
6364
|
-
continue;
|
|
6365
|
-
const projectDir = join5(dir, entry.name);
|
|
6366
|
-
const indexPath = join5(projectDir, "sessions-index.json");
|
|
6367
|
-
if (existsSync5(indexPath)) {
|
|
6368
|
-
const sessions = parseSessionsIndex(indexPath);
|
|
6369
|
-
if (sessions.length > 0) {
|
|
6370
|
-
yield [projectDir, sessions];
|
|
6371
|
-
}
|
|
6372
|
-
}
|
|
6373
|
-
}
|
|
6374
|
-
}
|
|
6375
5737
|
function hasSubagentsDir(sessionPath) {
|
|
6376
5738
|
const dir = sessionPath.slice(0, -6);
|
|
6377
5739
|
return existsSync5(join5(dir, "subagents"));
|
|
@@ -6382,40 +5744,6 @@ function getSubagentPaths(sessionPath) {
|
|
|
6382
5744
|
return [];
|
|
6383
5745
|
return readdirSync5(subagentsDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join5(subagentsDir, f));
|
|
6384
5746
|
}
|
|
6385
|
-
function getSubagentProjectPath(sessionPath, folderDerivedPath) {
|
|
6386
|
-
const subagentPaths = getSubagentPaths(sessionPath);
|
|
6387
|
-
if (subagentPaths.length === 0)
|
|
6388
|
-
return folderDerivedPath;
|
|
6389
|
-
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6390
|
-
for (const filePath of subagentPaths) {
|
|
6391
|
-
try {
|
|
6392
|
-
const lines = readFileSync2(filePath, "utf-8").split("\n");
|
|
6393
|
-
for (const line of lines) {
|
|
6394
|
-
if (!line.trim())
|
|
6395
|
-
continue;
|
|
6396
|
-
try {
|
|
6397
|
-
const entry = JSON.parse(line);
|
|
6398
|
-
if (entry.type === "user" && typeof entry.cwd === "string" && entry.cwd) {
|
|
6399
|
-
cwdCounts.set(entry.cwd, (cwdCounts.get(entry.cwd) ?? 0) + 1);
|
|
6400
|
-
}
|
|
6401
|
-
} catch {
|
|
6402
|
-
}
|
|
6403
|
-
}
|
|
6404
|
-
} catch {
|
|
6405
|
-
}
|
|
6406
|
-
}
|
|
6407
|
-
if (cwdCounts.size === 0)
|
|
6408
|
-
return folderDerivedPath;
|
|
6409
|
-
let best = folderDerivedPath;
|
|
6410
|
-
let bestCount = 0;
|
|
6411
|
-
for (const [cwd, count] of cwdCounts) {
|
|
6412
|
-
if (count > bestCount) {
|
|
6413
|
-
best = cwd;
|
|
6414
|
-
bestCount = count;
|
|
6415
|
-
}
|
|
6416
|
-
}
|
|
6417
|
-
return best;
|
|
6418
|
-
}
|
|
6419
5747
|
function readCwdFromJsonl(sessionPath) {
|
|
6420
5748
|
try {
|
|
6421
5749
|
if (!existsSync5(sessionPath))
|
|
@@ -6444,22 +5772,6 @@ function readCwdFromJsonl(sessionPath) {
|
|
|
6444
5772
|
}
|
|
6445
5773
|
return "";
|
|
6446
5774
|
}
|
|
6447
|
-
function getSessionMtime(sessionPath) {
|
|
6448
|
-
const subagentPaths = getSubagentPaths(sessionPath);
|
|
6449
|
-
if (subagentPaths.length === 0) {
|
|
6450
|
-
return statSync4(sessionPath).mtimeMs;
|
|
6451
|
-
}
|
|
6452
|
-
let maxMtime = statSync4(sessionPath).mtimeMs;
|
|
6453
|
-
for (const p of subagentPaths) {
|
|
6454
|
-
try {
|
|
6455
|
-
const m = statSync4(p).mtimeMs;
|
|
6456
|
-
if (m > maxMtime)
|
|
6457
|
-
maxMtime = m;
|
|
6458
|
-
} catch {
|
|
6459
|
-
}
|
|
6460
|
-
}
|
|
6461
|
-
return maxMtime;
|
|
6462
|
-
}
|
|
6463
5775
|
async function parseSessionFile(sessionPath, maxMessages = 50) {
|
|
6464
5776
|
const sessionId = basename2(sessionPath, ".jsonl");
|
|
6465
5777
|
const content = {
|
|
@@ -6697,76 +6009,10 @@ ${resultText}` : resultText;
|
|
|
6697
6009
|
});
|
|
6698
6010
|
return content;
|
|
6699
6011
|
}
|
|
6700
|
-
function* getAllSessions(claudeDir) {
|
|
6701
|
-
const seenSessionIds = /* @__PURE__ */ new Set();
|
|
6702
|
-
const projectDirs = claudeDir ? [claudeDir] : claudeProjectDirs();
|
|
6703
|
-
for (const dir of projectDirs) {
|
|
6704
|
-
for (const [, entries] of iterAllSessionIndices(dir)) {
|
|
6705
|
-
for (const entry of entries) {
|
|
6706
|
-
if (!seenSessionIds.has(entry.sessionId) && existsSync5(entry.fullPath)) {
|
|
6707
|
-
seenSessionIds.add(entry.sessionId);
|
|
6708
|
-
yield [entry, entry.fullPath];
|
|
6709
|
-
}
|
|
6710
|
-
}
|
|
6711
|
-
}
|
|
6712
|
-
if (!existsSync5(dir))
|
|
6713
|
-
continue;
|
|
6714
|
-
const dirEntries = readdirSync5(dir, { withFileTypes: true });
|
|
6715
|
-
for (const projDirEntry of dirEntries) {
|
|
6716
|
-
if (!projDirEntry.isDirectory())
|
|
6717
|
-
continue;
|
|
6718
|
-
const projectPath = join5(dir, projDirEntry.name);
|
|
6719
|
-
const files = readdirSync5(projectPath);
|
|
6720
|
-
for (const file of files) {
|
|
6721
|
-
if (!file.endsWith(".jsonl"))
|
|
6722
|
-
continue;
|
|
6723
|
-
if (file.startsWith("agent-"))
|
|
6724
|
-
continue;
|
|
6725
|
-
if (file === "sessions-index.json")
|
|
6726
|
-
continue;
|
|
6727
|
-
const sessionId = basename2(file, ".jsonl");
|
|
6728
|
-
if (seenSessionIds.has(sessionId))
|
|
6729
|
-
continue;
|
|
6730
|
-
const fullPath = join5(projectPath, file);
|
|
6731
|
-
try {
|
|
6732
|
-
const stat3 = statSync4(fullPath);
|
|
6733
|
-
const derivedProjectPath = resolveProjectDirName(projDirEntry.name);
|
|
6734
|
-
const effectiveMtime = getSessionMtime(fullPath);
|
|
6735
|
-
let projectPath2 = "";
|
|
6736
|
-
if (hasSubagentsDir(fullPath)) {
|
|
6737
|
-
projectPath2 = getSubagentProjectPath(fullPath, derivedProjectPath);
|
|
6738
|
-
} else {
|
|
6739
|
-
const fromJsonl = readCwdFromJsonl(fullPath);
|
|
6740
|
-
if (fromJsonl)
|
|
6741
|
-
projectPath2 = fromJsonl;
|
|
6742
|
-
}
|
|
6743
|
-
if (!projectPath2)
|
|
6744
|
-
projectPath2 = derivedProjectPath;
|
|
6745
|
-
const entry = {
|
|
6746
|
-
sessionId,
|
|
6747
|
-
fullPath,
|
|
6748
|
-
fileMtime: effectiveMtime,
|
|
6749
|
-
firstPrompt: "",
|
|
6750
|
-
messageCount: 0,
|
|
6751
|
-
created: stat3.birthtime.toISOString(),
|
|
6752
|
-
modified: new Date(effectiveMtime).toISOString(),
|
|
6753
|
-
gitBranch: "",
|
|
6754
|
-
projectPath: projectPath2,
|
|
6755
|
-
isSidechain: false
|
|
6756
|
-
};
|
|
6757
|
-
seenSessionIds.add(sessionId);
|
|
6758
|
-
yield [entry, fullPath];
|
|
6759
|
-
} catch {
|
|
6760
|
-
continue;
|
|
6761
|
-
}
|
|
6762
|
-
}
|
|
6763
|
-
}
|
|
6764
|
-
}
|
|
6765
|
-
}
|
|
6766
6012
|
var init_session = __esm({
|
|
6767
6013
|
"../engine/dist/parsers/session.js"() {
|
|
6768
6014
|
"use strict";
|
|
6769
|
-
|
|
6015
|
+
init_banners();
|
|
6770
6016
|
init_claude2();
|
|
6771
6017
|
init_tool_paths();
|
|
6772
6018
|
init_project_dir_name();
|
|
@@ -7287,7 +6533,7 @@ __export(raw_exports, {
|
|
|
7287
6533
|
parseTranscriptFromContainer: () => parseTranscriptFromContainer
|
|
7288
6534
|
});
|
|
7289
6535
|
import { gzipSync, gunzipSync } from "zlib";
|
|
7290
|
-
import { createHash
|
|
6536
|
+
import { createHash } from "crypto";
|
|
7291
6537
|
import * as fsMod from "fs";
|
|
7292
6538
|
import * as pathMod from "path";
|
|
7293
6539
|
function buildRawContainer(exp) {
|
|
@@ -7299,7 +6545,7 @@ function buildRawContainer(exp) {
|
|
|
7299
6545
|
};
|
|
7300
6546
|
}
|
|
7301
6547
|
function containerSrcHash(c) {
|
|
7302
|
-
const h =
|
|
6548
|
+
const h = createHash("sha1");
|
|
7303
6549
|
h.update(c.tool);
|
|
7304
6550
|
for (const f of c.files) {
|
|
7305
6551
|
h.update("\0");
|
|
@@ -8846,17 +8092,17 @@ var init_opencode = __esm({
|
|
|
8846
8092
|
});
|
|
8847
8093
|
|
|
8848
8094
|
// ../engine/dist/core/backends/tail-read.js
|
|
8849
|
-
import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as
|
|
8850
|
-
function readTailFromOffset(path, offset) {
|
|
8095
|
+
import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync4 } from "node:fs";
|
|
8096
|
+
function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
8851
8097
|
let size = 0;
|
|
8852
8098
|
try {
|
|
8853
|
-
size =
|
|
8099
|
+
size = statSync4(path).size;
|
|
8854
8100
|
} catch {
|
|
8855
8101
|
return { text: "", newOffset: offset };
|
|
8856
8102
|
}
|
|
8857
8103
|
if (size <= offset)
|
|
8858
8104
|
return { text: "", newOffset: offset };
|
|
8859
|
-
const len = Math.min(size - offset,
|
|
8105
|
+
const len = Math.min(size - offset, Math.max(1, maxBytes));
|
|
8860
8106
|
const buf = Buffer.allocUnsafe(len);
|
|
8861
8107
|
let fd = null;
|
|
8862
8108
|
try {
|
|
@@ -8872,11 +8118,16 @@ function readTailFromOffset(path, offset) {
|
|
|
8872
8118
|
return { text: "", newOffset: offset };
|
|
8873
8119
|
const slice = got < len ? buf.subarray(0, got) : buf;
|
|
8874
8120
|
const lastNl = slice.lastIndexOf(10);
|
|
8875
|
-
if (lastNl
|
|
8876
|
-
|
|
8121
|
+
if (lastNl >= 0) {
|
|
8122
|
+
const text = slice.subarray(0, lastNl + 1).toString("utf-8");
|
|
8123
|
+
return { text, newOffset: offset + lastNl + 1 };
|
|
8877
8124
|
}
|
|
8878
|
-
|
|
8879
|
-
|
|
8125
|
+
if (offset + got >= size)
|
|
8126
|
+
return { text: "", newOffset: offset };
|
|
8127
|
+
const end = findNewline(fd, offset + got, size);
|
|
8128
|
+
if (end < 0)
|
|
8129
|
+
return { text: "", newOffset: offset };
|
|
8130
|
+
return { text: "", newOffset: end + 1, skippedBytes: end + 1 - offset };
|
|
8880
8131
|
} catch {
|
|
8881
8132
|
return { text: "", newOffset: offset };
|
|
8882
8133
|
} finally {
|
|
@@ -8888,14 +8139,31 @@ function readTailFromOffset(path, offset) {
|
|
|
8888
8139
|
}
|
|
8889
8140
|
}
|
|
8890
8141
|
}
|
|
8142
|
+
function findNewline(fd, from, size) {
|
|
8143
|
+
const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
|
|
8144
|
+
let pos = from;
|
|
8145
|
+
while (pos < size) {
|
|
8146
|
+
const n = readSync2(fd, block, 0, Math.min(SKIP_SCAN_BYTES, size - pos), pos);
|
|
8147
|
+
if (n <= 0)
|
|
8148
|
+
return -1;
|
|
8149
|
+
const i = block.subarray(0, n).indexOf(10);
|
|
8150
|
+
if (i >= 0)
|
|
8151
|
+
return pos + i;
|
|
8152
|
+
pos += n;
|
|
8153
|
+
}
|
|
8154
|
+
return -1;
|
|
8155
|
+
}
|
|
8156
|
+
var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
|
|
8891
8157
|
var init_tail_read = __esm({
|
|
8892
8158
|
"../engine/dist/core/backends/tail-read.js"() {
|
|
8893
8159
|
"use strict";
|
|
8160
|
+
TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
|
|
8161
|
+
SKIP_SCAN_BYTES = 1024 * 1024;
|
|
8894
8162
|
}
|
|
8895
8163
|
});
|
|
8896
8164
|
|
|
8897
8165
|
// ../engine/dist/core/backends/codex.js
|
|
8898
|
-
import { readdirSync as readdirSync6, readFileSync as readFileSync3, statSync as
|
|
8166
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync3, statSync as statSync5, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
|
|
8899
8167
|
import { basename as basename7, dirname as dirname9, join as join9 } from "path";
|
|
8900
8168
|
function parseShellWrites(cmd) {
|
|
8901
8169
|
const out = [];
|
|
@@ -9103,7 +8371,7 @@ var init_codex2 = __esm({
|
|
|
9103
8371
|
return null;
|
|
9104
8372
|
let mtime = 0;
|
|
9105
8373
|
try {
|
|
9106
|
-
mtime =
|
|
8374
|
+
mtime = statSync5(located.path).mtimeMs;
|
|
9107
8375
|
} catch {
|
|
9108
8376
|
}
|
|
9109
8377
|
return {
|
|
@@ -9123,7 +8391,7 @@ var init_codex2 = __esm({
|
|
|
9123
8391
|
for (const filePath of walkRollouts(root)) {
|
|
9124
8392
|
let stat3;
|
|
9125
8393
|
try {
|
|
9126
|
-
stat3 =
|
|
8394
|
+
stat3 = statSync5(filePath);
|
|
9127
8395
|
} catch {
|
|
9128
8396
|
continue;
|
|
9129
8397
|
}
|
|
@@ -9319,7 +8587,7 @@ var init_codex2 = __esm({
|
|
|
9319
8587
|
let mtime = 0;
|
|
9320
8588
|
try {
|
|
9321
8589
|
raw = readFileSync3(path, "utf-8");
|
|
9322
|
-
mtime =
|
|
8590
|
+
mtime = statSync5(path).mtimeMs;
|
|
9323
8591
|
} catch {
|
|
9324
8592
|
return [];
|
|
9325
8593
|
}
|
|
@@ -9419,7 +8687,7 @@ var init_codex2 = __esm({
|
|
|
9419
8687
|
const events = this.readEvents(this.toRawId(id));
|
|
9420
8688
|
let fileMtime = 0;
|
|
9421
8689
|
try {
|
|
9422
|
-
fileMtime =
|
|
8690
|
+
fileMtime = statSync5(located.path).mtimeMs;
|
|
9423
8691
|
} catch {
|
|
9424
8692
|
}
|
|
9425
8693
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -9483,7 +8751,7 @@ var init_codex2 = __esm({
|
|
|
9483
8751
|
const p = join9(dir, entry.name);
|
|
9484
8752
|
let st;
|
|
9485
8753
|
try {
|
|
9486
|
-
st =
|
|
8754
|
+
st = statSync5(p);
|
|
9487
8755
|
} catch {
|
|
9488
8756
|
continue;
|
|
9489
8757
|
}
|
|
@@ -9550,7 +8818,7 @@ var init_codex2 = __esm({
|
|
|
9550
8818
|
let mtime = 0;
|
|
9551
8819
|
const push = (path, name) => {
|
|
9552
8820
|
try {
|
|
9553
|
-
const st =
|
|
8821
|
+
const st = statSync5(path);
|
|
9554
8822
|
files.push({ name, bytes: readFileSync3(path) });
|
|
9555
8823
|
if (st.mtimeMs > mtime)
|
|
9556
8824
|
mtime = st.mtimeMs;
|
|
@@ -9592,16 +8860,16 @@ var init_codex2 = __esm({
|
|
|
9592
8860
|
if (!loc)
|
|
9593
8861
|
return 0;
|
|
9594
8862
|
try {
|
|
9595
|
-
return
|
|
8863
|
+
return statSync5(loc.path).size;
|
|
9596
8864
|
} catch {
|
|
9597
8865
|
return 0;
|
|
9598
8866
|
}
|
|
9599
8867
|
}
|
|
9600
|
-
async readFromOffset(prefixedId, offset) {
|
|
8868
|
+
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
9601
8869
|
const loc = this.findSession(prefixedId);
|
|
9602
8870
|
if (!loc)
|
|
9603
8871
|
return { text: "", newOffset: offset };
|
|
9604
|
-
return readTailFromOffset(loc.path, offset);
|
|
8872
|
+
return readTailFromOffset(loc.path, offset, maxBytes);
|
|
9605
8873
|
}
|
|
9606
8874
|
};
|
|
9607
8875
|
codexBackend = new CodexBackend();
|
|
@@ -9609,7 +8877,7 @@ var init_codex2 = __esm({
|
|
|
9609
8877
|
});
|
|
9610
8878
|
|
|
9611
8879
|
// ../engine/dist/core/backends/agy.js
|
|
9612
|
-
import { existsSync as existsSync8, readdirSync as readdirSync7, readFileSync as readFileSync4, statSync as
|
|
8880
|
+
import { existsSync as existsSync8, readdirSync as readdirSync7, readFileSync as readFileSync4, statSync as statSync6 } from "fs";
|
|
9613
8881
|
import { basename as basename8, dirname as dirname10, join as join10 } from "path";
|
|
9614
8882
|
function agyTranscriptPath(logsDir) {
|
|
9615
8883
|
const full = join10(logsDir, "transcript_full.jsonl");
|
|
@@ -9802,7 +9070,7 @@ var init_agy = __esm({
|
|
|
9802
9070
|
return null;
|
|
9803
9071
|
let mtime = 0;
|
|
9804
9072
|
try {
|
|
9805
|
-
mtime =
|
|
9073
|
+
mtime = statSync6(path).mtimeMs;
|
|
9806
9074
|
} catch {
|
|
9807
9075
|
}
|
|
9808
9076
|
const projectPath = resolveAgyProject(dirname10(path), this.homeDir());
|
|
@@ -9830,7 +9098,7 @@ var init_agy = __esm({
|
|
|
9830
9098
|
for (const rawId of sessionDirs) {
|
|
9831
9099
|
const sessionPath = join10(brainDir, rawId);
|
|
9832
9100
|
try {
|
|
9833
|
-
if (!
|
|
9101
|
+
if (!statSync6(sessionPath).isDirectory())
|
|
9834
9102
|
continue;
|
|
9835
9103
|
} catch {
|
|
9836
9104
|
continue;
|
|
@@ -9840,7 +9108,7 @@ var init_agy = __esm({
|
|
|
9840
9108
|
continue;
|
|
9841
9109
|
let stat3;
|
|
9842
9110
|
try {
|
|
9843
|
-
stat3 =
|
|
9111
|
+
stat3 = statSync6(filePath);
|
|
9844
9112
|
} catch {
|
|
9845
9113
|
continue;
|
|
9846
9114
|
}
|
|
@@ -10105,7 +9373,7 @@ var init_agy = __esm({
|
|
|
10105
9373
|
for (const rawId of sessionDirs) {
|
|
10106
9374
|
const sessionPath = join10(brainDir, rawId);
|
|
10107
9375
|
try {
|
|
10108
|
-
if (!
|
|
9376
|
+
if (!statSync6(sessionPath).isDirectory())
|
|
10109
9377
|
continue;
|
|
10110
9378
|
} catch {
|
|
10111
9379
|
continue;
|
|
@@ -10114,7 +9382,7 @@ var init_agy = __esm({
|
|
|
10114
9382
|
if (!filePath)
|
|
10115
9383
|
continue;
|
|
10116
9384
|
try {
|
|
10117
|
-
const st =
|
|
9385
|
+
const st = statSync6(filePath);
|
|
10118
9386
|
if (st.mtimeMs < opts.sinceMs)
|
|
10119
9387
|
continue;
|
|
10120
9388
|
candidates2.push({ rawId, mtime: st.mtimeMs });
|
|
@@ -10142,7 +9410,7 @@ var init_agy = __esm({
|
|
|
10142
9410
|
try {
|
|
10143
9411
|
return {
|
|
10144
9412
|
tool: "agy",
|
|
10145
|
-
mtime:
|
|
9413
|
+
mtime: statSync6(loc.path).mtimeMs,
|
|
10146
9414
|
files: [{ name: basename8(loc.path), bytes: readFileSync4(loc.path) }]
|
|
10147
9415
|
};
|
|
10148
9416
|
} catch {
|
|
@@ -10157,16 +9425,16 @@ var init_agy = __esm({
|
|
|
10157
9425
|
if (!loc)
|
|
10158
9426
|
return 0;
|
|
10159
9427
|
try {
|
|
10160
|
-
return
|
|
9428
|
+
return statSync6(loc.path).size;
|
|
10161
9429
|
} catch {
|
|
10162
9430
|
return 0;
|
|
10163
9431
|
}
|
|
10164
9432
|
}
|
|
10165
|
-
async readFromOffset(prefixedId, offset) {
|
|
9433
|
+
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
10166
9434
|
const loc = this.findSession(prefixedId);
|
|
10167
9435
|
if (!loc)
|
|
10168
9436
|
return { text: "", newOffset: offset };
|
|
10169
|
-
return readTailFromOffset(loc.path, offset);
|
|
9437
|
+
return readTailFromOffset(loc.path, offset, maxBytes);
|
|
10170
9438
|
}
|
|
10171
9439
|
};
|
|
10172
9440
|
agyBackend = new AgyBackend();
|
|
@@ -10174,7 +9442,7 @@ var init_agy = __esm({
|
|
|
10174
9442
|
});
|
|
10175
9443
|
|
|
10176
9444
|
// ../engine/dist/core/backends/cursor-store.js
|
|
10177
|
-
import { existsSync as existsSync9, readdirSync as readdirSync8, readFileSync as readFileSync5, statSync as
|
|
9445
|
+
import { existsSync as existsSync9, readdirSync as readdirSync8, readFileSync as readFileSync5, statSync as statSync7 } from "fs";
|
|
10178
9446
|
import { join as join11 } from "path";
|
|
10179
9447
|
function cursorProjectSlug(absPath) {
|
|
10180
9448
|
return absPath.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -10191,7 +9459,7 @@ function listChatsIn(chatsRoot) {
|
|
|
10191
9459
|
const hashPath3 = join11(chatsRoot, workspaceHash);
|
|
10192
9460
|
let chatIds;
|
|
10193
9461
|
try {
|
|
10194
|
-
if (!
|
|
9462
|
+
if (!statSync7(hashPath3).isDirectory())
|
|
10195
9463
|
continue;
|
|
10196
9464
|
chatIds = readdirSync8(hashPath3);
|
|
10197
9465
|
} catch {
|
|
@@ -10213,7 +9481,7 @@ function listChatsIn(chatsRoot) {
|
|
|
10213
9481
|
let created = meta.createdAtMs ?? 0;
|
|
10214
9482
|
if (!mtime || !created) {
|
|
10215
9483
|
try {
|
|
10216
|
-
const st =
|
|
9484
|
+
const st = statSync7(dbPath);
|
|
10217
9485
|
mtime = mtime || st.mtimeMs;
|
|
10218
9486
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
10219
9487
|
} catch {
|
|
@@ -10256,7 +9524,7 @@ function findChat(chatsRoots, chatId) {
|
|
|
10256
9524
|
let created = meta.createdAtMs ?? 0;
|
|
10257
9525
|
if (!mtime || !created) {
|
|
10258
9526
|
try {
|
|
10259
|
-
const st =
|
|
9527
|
+
const st = statSync7(dbPath);
|
|
10260
9528
|
mtime = mtime || st.mtimeMs;
|
|
10261
9529
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
10262
9530
|
} catch {
|
|
@@ -10559,7 +9827,7 @@ var init_cursor_store = __esm({
|
|
|
10559
9827
|
});
|
|
10560
9828
|
|
|
10561
9829
|
// ../engine/dist/core/backends/cursor-ide.js
|
|
10562
|
-
import { existsSync as existsSync10, readFileSync as readFileSync6, readdirSync as readdirSync9, statSync as
|
|
9830
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, readdirSync as readdirSync9, statSync as statSync8 } from "fs";
|
|
10563
9831
|
import { dirname as dirname11, join as join12 } from "path";
|
|
10564
9832
|
function jsonOf(value) {
|
|
10565
9833
|
if (value == null)
|
|
@@ -10629,7 +9897,7 @@ function workspaceIndex(workspaceRoots) {
|
|
|
10629
9897
|
for (const id of entries) {
|
|
10630
9898
|
const dir = join12(root, id);
|
|
10631
9899
|
try {
|
|
10632
|
-
if (!
|
|
9900
|
+
if (!statSync8(dir).isDirectory())
|
|
10633
9901
|
continue;
|
|
10634
9902
|
} catch {
|
|
10635
9903
|
continue;
|
|
@@ -10975,7 +10243,7 @@ var init_cursor_ide = __esm({
|
|
|
10975
10243
|
});
|
|
10976
10244
|
|
|
10977
10245
|
// ../engine/dist/core/backends/cursor.js
|
|
10978
|
-
import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as
|
|
10246
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as statSync9 } from "fs";
|
|
10979
10247
|
import { join as join13 } from "path";
|
|
10980
10248
|
var PREFIX4, CursorBackend, cursorBackend;
|
|
10981
10249
|
var init_cursor = __esm({
|
|
@@ -11028,7 +10296,7 @@ var init_cursor = __esm({
|
|
|
11028
10296
|
if (r.surface === "cli") {
|
|
11029
10297
|
let mtime2 = r.chat.updatedAtMs;
|
|
11030
10298
|
try {
|
|
11031
|
-
mtime2 =
|
|
10299
|
+
mtime2 = statSync9(r.chat.dbPath).mtimeMs || mtime2;
|
|
11032
10300
|
} catch {
|
|
11033
10301
|
}
|
|
11034
10302
|
return {
|
|
@@ -11041,7 +10309,7 @@ var init_cursor = __esm({
|
|
|
11041
10309
|
}
|
|
11042
10310
|
let mtime = r.composer.updatedAtMs;
|
|
11043
10311
|
try {
|
|
11044
|
-
mtime =
|
|
10312
|
+
mtime = statSync9(r.composer.globalDbPath).mtimeMs || mtime;
|
|
11045
10313
|
} catch {
|
|
11046
10314
|
}
|
|
11047
10315
|
return {
|
|
@@ -11287,7 +10555,7 @@ var init_cursor = __esm({
|
|
|
11287
10555
|
if (existsSync11(metaPath)) {
|
|
11288
10556
|
files.push({ name: "meta.json", bytes: readFileSync7(metaPath) });
|
|
11289
10557
|
}
|
|
11290
|
-
return { tool: "cursor", mtime:
|
|
10558
|
+
return { tool: "cursor", mtime: statSync9(r.chat.dbPath).mtimeMs, files };
|
|
11291
10559
|
}
|
|
11292
10560
|
const events = readComposerEvents(r.composer);
|
|
11293
10561
|
if (events.length === 0)
|
|
@@ -11295,7 +10563,7 @@ var init_cursor = __esm({
|
|
|
11295
10563
|
const body = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
11296
10564
|
return {
|
|
11297
10565
|
tool: "cursor",
|
|
11298
|
-
mtime:
|
|
10566
|
+
mtime: statSync9(r.composer.globalDbPath).mtimeMs,
|
|
11299
10567
|
files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(body, "utf8") }]
|
|
11300
10568
|
};
|
|
11301
10569
|
} catch {
|
|
@@ -11361,8 +10629,8 @@ var init_backends = __esm({
|
|
|
11361
10629
|
|
|
11362
10630
|
// ../engine/dist/transcript/shadow.js
|
|
11363
10631
|
import { gzipSync as gzipSync2, gunzipSync as gunzipSync2 } from "zlib";
|
|
11364
|
-
import { createHash as
|
|
11365
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as
|
|
10632
|
+
import { createHash as createHash2 } from "crypto";
|
|
10633
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync10, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
|
|
11366
10634
|
import { dirname as dirname12, join as join14 } from "path";
|
|
11367
10635
|
function shadowRoot() {
|
|
11368
10636
|
return join14(getDataDir(), "shadow");
|
|
@@ -11392,7 +10660,7 @@ function writeShadowContainer(tool, rawId, container) {
|
|
|
11392
10660
|
renameSync2(tmp, path);
|
|
11393
10661
|
}
|
|
11394
10662
|
function sha1(s) {
|
|
11395
|
-
return
|
|
10663
|
+
return createHash2("sha1").update(s).digest("hex");
|
|
11396
10664
|
}
|
|
11397
10665
|
function lineKey(line) {
|
|
11398
10666
|
try {
|
|
@@ -11570,7 +10838,7 @@ function liveFingerprint(backend, rawId) {
|
|
|
11570
10838
|
const loc = backend.findSession(rawId);
|
|
11571
10839
|
if (!loc?.path)
|
|
11572
10840
|
return "";
|
|
11573
|
-
const st =
|
|
10841
|
+
const st = statSync10(loc.path);
|
|
11574
10842
|
return `${st.mtimeMs}:${st.size}`;
|
|
11575
10843
|
} catch {
|
|
11576
10844
|
return "";
|
|
@@ -11586,9 +10854,32 @@ function seedShadow(sessionId, container) {
|
|
|
11586
10854
|
}
|
|
11587
10855
|
return fullest;
|
|
11588
10856
|
}
|
|
10857
|
+
function shadowUncompressedBytes(tool, rawId) {
|
|
10858
|
+
const path = shadowFileFor(tool, rawId);
|
|
10859
|
+
let fd = null;
|
|
10860
|
+
try {
|
|
10861
|
+
const size = statSync10(path).size;
|
|
10862
|
+
if (size < 18)
|
|
10863
|
+
return 0;
|
|
10864
|
+
fd = openSync4(path, "r");
|
|
10865
|
+
const tail = Buffer.alloc(4);
|
|
10866
|
+
if (readSync4(fd, tail, 0, 4, size - 4) !== 4)
|
|
10867
|
+
return 0;
|
|
10868
|
+
return tail.readUInt32LE(0);
|
|
10869
|
+
} catch {
|
|
10870
|
+
return 0;
|
|
10871
|
+
} finally {
|
|
10872
|
+
if (fd !== null) {
|
|
10873
|
+
try {
|
|
10874
|
+
closeSync4(fd);
|
|
10875
|
+
} catch {
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
11589
10880
|
function shadowSizeOnDisk(tool, rawId) {
|
|
11590
10881
|
try {
|
|
11591
|
-
return
|
|
10882
|
+
return statSync10(shadowFileFor(tool, rawId)).size;
|
|
11592
10883
|
} catch {
|
|
11593
10884
|
return 0;
|
|
11594
10885
|
}
|
|
@@ -11606,7 +10897,7 @@ var init_shadow = __esm({
|
|
|
11606
10897
|
});
|
|
11607
10898
|
|
|
11608
10899
|
// ../engine/dist/core/live-session-scan.js
|
|
11609
|
-
import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as
|
|
10900
|
+
import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
|
|
11610
10901
|
import { join as join15, basename as basename9 } from "path";
|
|
11611
10902
|
function detectTool(sessionId) {
|
|
11612
10903
|
return getBackendForId(sessionId)?.id ?? "claude";
|
|
@@ -11633,6 +10924,15 @@ function sessionFileIndex() {
|
|
|
11633
10924
|
return indexCache;
|
|
11634
10925
|
const byId = /* @__PURE__ */ new Map();
|
|
11635
10926
|
const seenRoots = /* @__PURE__ */ new Set();
|
|
10927
|
+
const projectPaths = /* @__PURE__ */ new Map();
|
|
10928
|
+
const projectPathOf = (dirName) => {
|
|
10929
|
+
let p = projectPaths.get(dirName);
|
|
10930
|
+
if (p === void 0) {
|
|
10931
|
+
p = resolveProjectDirName(dirName);
|
|
10932
|
+
projectPaths.set(dirName, p);
|
|
10933
|
+
}
|
|
10934
|
+
return p;
|
|
10935
|
+
};
|
|
11636
10936
|
for (const root of claudeProjectDirs()) {
|
|
11637
10937
|
if (seenRoots.has(root) || !existsSync13(root))
|
|
11638
10938
|
continue;
|
|
@@ -11660,7 +10960,7 @@ function sessionFileIndex() {
|
|
|
11660
10960
|
const located = {
|
|
11661
10961
|
path: join15(dir, name),
|
|
11662
10962
|
projectDir: entry.name,
|
|
11663
|
-
projectPath:
|
|
10963
|
+
projectPath: projectPathOf(entry.name)
|
|
11664
10964
|
};
|
|
11665
10965
|
const prior = byId.get(id);
|
|
11666
10966
|
if (prior)
|
|
@@ -11724,7 +11024,7 @@ function readSessionGroupText(group) {
|
|
|
11724
11024
|
const parts = [];
|
|
11725
11025
|
for (const p of group.paths) {
|
|
11726
11026
|
try {
|
|
11727
|
-
const st =
|
|
11027
|
+
const st = statSync11(p);
|
|
11728
11028
|
parts.push(`${p}:${st.mtimeMs}:${st.size}`);
|
|
11729
11029
|
} catch {
|
|
11730
11030
|
parts.push(`${p}:missing`);
|
|
@@ -11744,7 +11044,7 @@ function readSessionGroupText(group) {
|
|
|
11744
11044
|
continue;
|
|
11745
11045
|
}
|
|
11746
11046
|
try {
|
|
11747
|
-
mtime = Math.max(mtime,
|
|
11047
|
+
mtime = Math.max(mtime, statSync11(p).mtimeMs);
|
|
11748
11048
|
} catch {
|
|
11749
11049
|
}
|
|
11750
11050
|
text = text ? mergeLineText(text, raw).text : raw;
|
|
@@ -11850,7 +11150,7 @@ var init_live_session_scan = __esm({
|
|
|
11850
11150
|
});
|
|
11851
11151
|
|
|
11852
11152
|
// ../engine/dist/core/first-prompt.js
|
|
11853
|
-
import { createReadStream as createReadStream2, existsSync as existsSync14, openSync as
|
|
11153
|
+
import { createReadStream as createReadStream2, existsSync as existsSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
|
|
11854
11154
|
import { StringDecoder } from "string_decoder";
|
|
11855
11155
|
function stripBanners2(s) {
|
|
11856
11156
|
let out = s;
|
|
@@ -11904,7 +11204,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
11904
11204
|
const maxBytes = options.maxBytes ?? 4 * 1024 * 1024;
|
|
11905
11205
|
let fd;
|
|
11906
11206
|
try {
|
|
11907
|
-
fd =
|
|
11207
|
+
fd = openSync6(filePath, "r");
|
|
11908
11208
|
} catch {
|
|
11909
11209
|
return "";
|
|
11910
11210
|
}
|
|
@@ -11916,7 +11216,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
11916
11216
|
let pos = 0;
|
|
11917
11217
|
let lineNo = 0;
|
|
11918
11218
|
for (; ; ) {
|
|
11919
|
-
const n =
|
|
11219
|
+
const n = readSync6(fd, buf, 0, CHUNK, pos);
|
|
11920
11220
|
if (n <= 0)
|
|
11921
11221
|
break;
|
|
11922
11222
|
pos += n;
|
|
@@ -11942,7 +11242,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
11942
11242
|
return "";
|
|
11943
11243
|
} finally {
|
|
11944
11244
|
try {
|
|
11945
|
-
|
|
11245
|
+
closeSync6(fd);
|
|
11946
11246
|
} catch {
|
|
11947
11247
|
}
|
|
11948
11248
|
}
|
|
@@ -11961,7 +11261,7 @@ var init_first_prompt = __esm({
|
|
|
11961
11261
|
});
|
|
11962
11262
|
|
|
11963
11263
|
// ../engine/dist/core/backends/claude.js
|
|
11964
|
-
import { existsSync as existsSync15, readdirSync as readdirSync11, readFileSync as readFileSync10, statSync as
|
|
11264
|
+
import { existsSync as existsSync15, readdirSync as readdirSync11, readFileSync as readFileSync10, statSync as statSync12, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
|
|
11965
11265
|
import { join as join16, basename as basename10 } from "path";
|
|
11966
11266
|
function stripReminders(text) {
|
|
11967
11267
|
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
|
|
@@ -12058,13 +11358,13 @@ var init_claude2 = __esm({
|
|
|
12058
11358
|
return null;
|
|
12059
11359
|
let head;
|
|
12060
11360
|
try {
|
|
12061
|
-
const fd =
|
|
11361
|
+
const fd = openSync7(located.path, "r");
|
|
12062
11362
|
try {
|
|
12063
11363
|
const buf = Buffer.alloc(65536);
|
|
12064
|
-
const n =
|
|
11364
|
+
const n = readSync7(fd, buf, 0, buf.length, 0);
|
|
12065
11365
|
head = buf.toString("utf-8", 0, n);
|
|
12066
11366
|
} finally {
|
|
12067
|
-
|
|
11367
|
+
closeSync7(fd);
|
|
12068
11368
|
}
|
|
12069
11369
|
} catch {
|
|
12070
11370
|
return null;
|
|
@@ -12091,7 +11391,7 @@ var init_claude2 = __esm({
|
|
|
12091
11391
|
let mtime = 0;
|
|
12092
11392
|
for (const c of copies) {
|
|
12093
11393
|
try {
|
|
12094
|
-
mtime = Math.max(mtime,
|
|
11394
|
+
mtime = Math.max(mtime, statSync12(c.path).mtimeMs);
|
|
12095
11395
|
} catch {
|
|
12096
11396
|
}
|
|
12097
11397
|
}
|
|
@@ -12167,7 +11467,7 @@ var init_claude2 = __esm({
|
|
|
12167
11467
|
const fullPath = join16(projPath, f);
|
|
12168
11468
|
let stat3;
|
|
12169
11469
|
try {
|
|
12170
|
-
stat3 =
|
|
11470
|
+
stat3 = statSync12(fullPath);
|
|
12171
11471
|
} catch {
|
|
12172
11472
|
continue;
|
|
12173
11473
|
}
|
|
@@ -12388,7 +11688,7 @@ var init_claude2 = __esm({
|
|
|
12388
11688
|
const events = this.readEvents(rawId);
|
|
12389
11689
|
let fileMtime = 0;
|
|
12390
11690
|
try {
|
|
12391
|
-
fileMtime =
|
|
11691
|
+
fileMtime = statSync12(located.path).mtimeMs;
|
|
12392
11692
|
} catch {
|
|
12393
11693
|
}
|
|
12394
11694
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -12443,13 +11743,13 @@ var init_claude2 = __esm({
|
|
|
12443
11743
|
continue;
|
|
12444
11744
|
const fp = join16(projPath, f);
|
|
12445
11745
|
try {
|
|
12446
|
-
const st =
|
|
11746
|
+
const st = statSync12(fp);
|
|
12447
11747
|
let mtime = st.mtimeMs;
|
|
12448
11748
|
const subDir = join16(projPath, f.slice(0, -6), "subagents");
|
|
12449
11749
|
if (existsSync15(subDir)) {
|
|
12450
11750
|
for (const sub of readdirSync11(subDir)) {
|
|
12451
11751
|
try {
|
|
12452
|
-
const subSt =
|
|
11752
|
+
const subSt = statSync12(join16(subDir, sub));
|
|
12453
11753
|
if (subSt.mtimeMs > mtime)
|
|
12454
11754
|
mtime = subSt.mtimeMs;
|
|
12455
11755
|
} catch {
|
|
@@ -12514,7 +11814,7 @@ var init_claude2 = __esm({
|
|
|
12514
11814
|
let total = 0;
|
|
12515
11815
|
for (const located of findSessionFiles(this.toRawId(prefixedId))) {
|
|
12516
11816
|
try {
|
|
12517
|
-
total +=
|
|
11817
|
+
total += statSync12(located.path).size;
|
|
12518
11818
|
} catch {
|
|
12519
11819
|
}
|
|
12520
11820
|
}
|
|
@@ -12536,417 +11836,877 @@ var init_claude2 = __esm({
|
|
|
12536
11836
|
spansMultipleSources(prefixedId) {
|
|
12537
11837
|
return findSessionFiles(this.toRawId(prefixedId)).length > 1;
|
|
12538
11838
|
}
|
|
12539
|
-
async readFromOffset(prefixedId, offset) {
|
|
12540
|
-
const rawId = this.toRawId(prefixedId);
|
|
12541
|
-
const copies = findSessionFiles(rawId);
|
|
12542
|
-
if (copies.length === 0)
|
|
12543
|
-
return { text: "", newOffset: offset };
|
|
12544
|
-
if (copies.length > 1)
|
|
12545
|
-
return { text: "", newOffset: offset };
|
|
12546
|
-
return readTailFromOffset(copies[0].path, offset);
|
|
11839
|
+
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
11840
|
+
const rawId = this.toRawId(prefixedId);
|
|
11841
|
+
const copies = findSessionFiles(rawId);
|
|
11842
|
+
if (copies.length === 0)
|
|
11843
|
+
return { text: "", newOffset: offset };
|
|
11844
|
+
if (copies.length > 1)
|
|
11845
|
+
return { text: "", newOffset: offset };
|
|
11846
|
+
return readTailFromOffset(copies[0].path, offset, maxBytes);
|
|
11847
|
+
}
|
|
11848
|
+
};
|
|
11849
|
+
claudeBackend = new ClaudeBackend();
|
|
11850
|
+
}
|
|
11851
|
+
});
|
|
11852
|
+
|
|
11853
|
+
// ../engine/dist/core/project-resolver.js
|
|
11854
|
+
var project_resolver_exports = {};
|
|
11855
|
+
__export(project_resolver_exports, {
|
|
11856
|
+
_setProjectsConfigForTests: () => _setProjectsConfigForTests,
|
|
11857
|
+
getProjectsConfigPath: () => getProjectsConfigPath,
|
|
11858
|
+
globMatch: () => globMatch,
|
|
11859
|
+
loadProjectsConfig: () => loadProjectsConfig,
|
|
11860
|
+
parseGitRemote: () => parseGitRemote,
|
|
11861
|
+
resetProjectResolverCache: () => resetProjectResolverCache,
|
|
11862
|
+
resolveProjectId: () => resolveProjectId,
|
|
11863
|
+
resolveWorkspaceId: () => resolveWorkspaceId
|
|
11864
|
+
});
|
|
11865
|
+
import { execSync } from "child_process";
|
|
11866
|
+
import { createHash as createHash3 } from "crypto";
|
|
11867
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11, realpathSync as realpathSync2, readdirSync as readdirSync12, statSync as statSync13 } from "fs";
|
|
11868
|
+
import { basename as basename11, dirname as dirname13, isAbsolute, join as join17, resolve, sep } from "path";
|
|
11869
|
+
function getProjectsConfigPath() {
|
|
11870
|
+
return join17(getDataDir(), "projects.json");
|
|
11871
|
+
}
|
|
11872
|
+
function loadProjectsConfig() {
|
|
11873
|
+
if (cachedConfig && cachedConfig.mtime === Number.MAX_SAFE_INTEGER) {
|
|
11874
|
+
return cachedConfig.cfg;
|
|
11875
|
+
}
|
|
11876
|
+
const path = getProjectsConfigPath();
|
|
11877
|
+
if (!existsSync16(path)) {
|
|
11878
|
+
cachedConfig = { mtime: 0, cfg: {} };
|
|
11879
|
+
return cachedConfig.cfg;
|
|
11880
|
+
}
|
|
11881
|
+
const mtime = statSync13(path).mtimeMs;
|
|
11882
|
+
if (cachedConfig && cachedConfig.mtime === mtime)
|
|
11883
|
+
return cachedConfig.cfg;
|
|
11884
|
+
try {
|
|
11885
|
+
const raw = readFileSync11(path, "utf-8");
|
|
11886
|
+
const cfg = JSON.parse(raw);
|
|
11887
|
+
cachedConfig = { mtime, cfg };
|
|
11888
|
+
return cfg;
|
|
11889
|
+
} catch {
|
|
11890
|
+
cachedConfig = { mtime, cfg: {} };
|
|
11891
|
+
return cachedConfig.cfg;
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
function resetProjectResolverCache() {
|
|
11895
|
+
resolveCache.clear();
|
|
11896
|
+
gitCache.clear();
|
|
11897
|
+
workspaceCache.clear();
|
|
11898
|
+
wsIdCache.clear();
|
|
11899
|
+
cachedConfig = null;
|
|
11900
|
+
}
|
|
11901
|
+
function _setProjectsConfigForTests(cfg) {
|
|
11902
|
+
resetProjectResolverCache();
|
|
11903
|
+
cachedConfig = cfg === null ? null : { mtime: Number.MAX_SAFE_INTEGER, cfg };
|
|
11904
|
+
}
|
|
11905
|
+
function resolveProjectId(inputPath) {
|
|
11906
|
+
if (!inputPath) {
|
|
11907
|
+
return { id: "", displayName: "", source: "path" };
|
|
11908
|
+
}
|
|
11909
|
+
if (!isAbsolute(inputPath)) {
|
|
11910
|
+
return { id: `path:${inputPath}`, displayName: inputPath, source: "path" };
|
|
11911
|
+
}
|
|
11912
|
+
const realPath = safeRealpath(inputPath);
|
|
11913
|
+
const cached = resolveCache.get(realPath);
|
|
11914
|
+
if (cached)
|
|
11915
|
+
return cached;
|
|
11916
|
+
const result = doResolve(realPath);
|
|
11917
|
+
resolveCache.set(realPath, result);
|
|
11918
|
+
return result;
|
|
11919
|
+
}
|
|
11920
|
+
function resolveWorkspaceId(inputPath) {
|
|
11921
|
+
if (!inputPath || !isAbsolute(inputPath))
|
|
11922
|
+
return null;
|
|
11923
|
+
const realPath = safeRealpath(inputPath);
|
|
11924
|
+
const cached = wsIdCache.get(realPath);
|
|
11925
|
+
if (cached !== void 0)
|
|
11926
|
+
return cached;
|
|
11927
|
+
const result = doResolveWorkspace(realPath);
|
|
11928
|
+
wsIdCache.set(realPath, result);
|
|
11929
|
+
return result;
|
|
11930
|
+
}
|
|
11931
|
+
function doResolveWorkspace(realPath) {
|
|
11932
|
+
const cfg = loadProjectsConfig();
|
|
11933
|
+
const declaredWorkspaces = (cfg.projects || []).filter((p) => p.workspace).sort((a, b) => b.root.length - a.root.length);
|
|
11934
|
+
for (const proj of declaredWorkspaces) {
|
|
11935
|
+
if (isInside(realPath, safeRealpath(proj.root)))
|
|
11936
|
+
return proj.id;
|
|
11937
|
+
}
|
|
11938
|
+
const anchor = findGitToplevel(realPath) ?? realPath;
|
|
11939
|
+
const ws = resolveAutoWorkspace(anchor, cfg.autoWorkspaceMinRepos ?? DEFAULT_AUTO_WORKSPACE_MIN_REPOS);
|
|
11940
|
+
return ws ? ws.id : null;
|
|
11941
|
+
}
|
|
11942
|
+
function doResolve(realPath) {
|
|
11943
|
+
const cfg = loadProjectsConfig();
|
|
11944
|
+
if (matchesIgnore(realPath, cfg)) {
|
|
11945
|
+
return { id: `ignored:${realPath}`, displayName: basename11(realPath), source: "ignored" };
|
|
11946
|
+
}
|
|
11947
|
+
const declared = matchDeclaredProject(realPath, cfg.projects || []);
|
|
11948
|
+
if (declared)
|
|
11949
|
+
return declared;
|
|
11950
|
+
const bot = resolveBotWorktree(realPath);
|
|
11951
|
+
if (bot)
|
|
11952
|
+
return bot;
|
|
11953
|
+
const git2 = resolveGit(realPath);
|
|
11954
|
+
if (git2)
|
|
11955
|
+
return git2;
|
|
11956
|
+
const ws = resolveAutoWorkspace(realPath, cfg.autoWorkspaceMinRepos ?? DEFAULT_AUTO_WORKSPACE_MIN_REPOS);
|
|
11957
|
+
if (ws)
|
|
11958
|
+
return ws;
|
|
11959
|
+
return {
|
|
11960
|
+
id: `path:${realPath}`,
|
|
11961
|
+
displayName: basename11(realPath) || realPath,
|
|
11962
|
+
source: "path"
|
|
11963
|
+
};
|
|
11964
|
+
}
|
|
11965
|
+
function matchesIgnore(realPath, cfg) {
|
|
11966
|
+
const rules = cfg.ignore || [];
|
|
11967
|
+
for (const r of rules) {
|
|
11968
|
+
if (globMatch(r.match, realPath))
|
|
11969
|
+
return true;
|
|
11970
|
+
}
|
|
11971
|
+
return false;
|
|
11972
|
+
}
|
|
11973
|
+
function resolveBotWorktree(realPath) {
|
|
11974
|
+
const parts = realPath.split(sep);
|
|
11975
|
+
const idx = parts.findIndex((p) => BOT_WORKTREE_MARKERS.includes(p));
|
|
11976
|
+
if (idx === -1)
|
|
11977
|
+
return null;
|
|
11978
|
+
const botRoot = parts.slice(0, idx + 1).join(sep);
|
|
11979
|
+
return {
|
|
11980
|
+
id: `path:${botRoot}`,
|
|
11981
|
+
displayName: parts[idx],
|
|
11982
|
+
source: "path"
|
|
11983
|
+
};
|
|
11984
|
+
}
|
|
11985
|
+
function matchDeclaredProject(realPath, projects) {
|
|
11986
|
+
const candidates2 = [...projects].sort((a, b) => b.root.length - a.root.length);
|
|
11987
|
+
for (const proj of candidates2) {
|
|
11988
|
+
const root = safeRealpath(proj.root);
|
|
11989
|
+
if (!isInside(realPath, root))
|
|
11990
|
+
continue;
|
|
11991
|
+
if (!proj.workspace && proj.children?.length) {
|
|
11992
|
+
const child = matchChild(realPath, root, proj.children);
|
|
11993
|
+
if (child) {
|
|
11994
|
+
return {
|
|
11995
|
+
id: `${proj.id}/${child.id}`,
|
|
11996
|
+
displayName: child.name || child.id,
|
|
11997
|
+
source: "user",
|
|
11998
|
+
workspaceId: proj.id
|
|
11999
|
+
};
|
|
12000
|
+
}
|
|
12001
|
+
}
|
|
12002
|
+
return {
|
|
12003
|
+
id: proj.id,
|
|
12004
|
+
displayName: proj.name || proj.id,
|
|
12005
|
+
source: "user",
|
|
12006
|
+
workspaceId: proj.workspace ? void 0 : void 0
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
return null;
|
|
12010
|
+
}
|
|
12011
|
+
function matchChild(realPath, parentRoot, children) {
|
|
12012
|
+
const sorted = [...children].sort((a, b) => b.path.length - a.path.length);
|
|
12013
|
+
for (const c of sorted) {
|
|
12014
|
+
const childRoot = safeRealpath(join17(parentRoot, c.path));
|
|
12015
|
+
if (isInside(realPath, childRoot))
|
|
12016
|
+
return c;
|
|
12017
|
+
}
|
|
12018
|
+
return null;
|
|
12019
|
+
}
|
|
12020
|
+
function resolveGit(realPath) {
|
|
12021
|
+
const toplevel = findGitToplevel(realPath);
|
|
12022
|
+
if (!toplevel)
|
|
12023
|
+
return null;
|
|
12024
|
+
const probe = getGitRemote(toplevel);
|
|
12025
|
+
if (probe.kind === "remote") {
|
|
12026
|
+
const parsed = parseGitRemote(probe.url);
|
|
12027
|
+
if (parsed) {
|
|
12028
|
+
return {
|
|
12029
|
+
id: `git:${parsed.host}/${parsed.owner}/${parsed.repo}`,
|
|
12030
|
+
displayName: parsed.repo,
|
|
12031
|
+
source: "git-remote"
|
|
12032
|
+
};
|
|
12033
|
+
}
|
|
12034
|
+
}
|
|
12035
|
+
if (probe.kind === "failed") {
|
|
12036
|
+
console.error(`[project] could not read git remotes in ${toplevel} \u2014 filing it as a local-only repo for now, which is probably wrong: ${probe.reason}`);
|
|
12037
|
+
}
|
|
12038
|
+
const hash = createHash3("sha1").update(toplevel).digest("hex").slice(0, 12);
|
|
12039
|
+
return {
|
|
12040
|
+
id: `git-local:${hash}`,
|
|
12041
|
+
displayName: basename11(toplevel),
|
|
12042
|
+
source: "git-local"
|
|
12043
|
+
};
|
|
12044
|
+
}
|
|
12045
|
+
function findGitToplevel(realPath) {
|
|
12046
|
+
let cur = realPath;
|
|
12047
|
+
let last = "";
|
|
12048
|
+
while (cur && cur !== last) {
|
|
12049
|
+
if (existsSync16(join17(cur, ".git")))
|
|
12050
|
+
return cur;
|
|
12051
|
+
last = cur;
|
|
12052
|
+
cur = dirname13(cur);
|
|
12053
|
+
}
|
|
12054
|
+
return null;
|
|
12055
|
+
}
|
|
12056
|
+
function git(toplevel, args) {
|
|
12057
|
+
return execSync(`git ${args}`, {
|
|
12058
|
+
cwd: toplevel,
|
|
12059
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
12060
|
+
encoding: "utf-8",
|
|
12061
|
+
timeout: GIT_TIMEOUT_MS
|
|
12062
|
+
});
|
|
12063
|
+
}
|
|
12064
|
+
function probeGitRemote(toplevel) {
|
|
12065
|
+
let names;
|
|
12066
|
+
try {
|
|
12067
|
+
names = git(toplevel, "remote").split("\n").map((n) => n.trim()).filter(Boolean);
|
|
12068
|
+
} catch (err) {
|
|
12069
|
+
return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
12070
|
+
}
|
|
12071
|
+
if (names.length === 0)
|
|
12072
|
+
return { kind: "none" };
|
|
12073
|
+
const pick = names.includes("origin") ? "origin" : names[0];
|
|
12074
|
+
try {
|
|
12075
|
+
const url = git(toplevel, `remote get-url ${pick}`).trim();
|
|
12076
|
+
return url ? { kind: "remote", url } : { kind: "none" };
|
|
12077
|
+
} catch (err) {
|
|
12078
|
+
return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
12079
|
+
}
|
|
12080
|
+
}
|
|
12081
|
+
function getGitRemote(toplevel) {
|
|
12082
|
+
const cached = gitCache.get(toplevel);
|
|
12083
|
+
if (cached)
|
|
12084
|
+
return cached;
|
|
12085
|
+
const probe = probeGitRemote(toplevel);
|
|
12086
|
+
if (probe.kind !== "failed")
|
|
12087
|
+
gitCache.set(toplevel, probe);
|
|
12088
|
+
return probe;
|
|
12089
|
+
}
|
|
12090
|
+
function parseGitRemote(url) {
|
|
12091
|
+
const trimmed = url.trim().replace(/\.git$/, "");
|
|
12092
|
+
if (!trimmed)
|
|
12093
|
+
return null;
|
|
12094
|
+
const ssh = /^[^@]+@([^:]+):(.+?)\/([^/]+)$/.exec(trimmed);
|
|
12095
|
+
if (ssh)
|
|
12096
|
+
return { host: normaliseHost(ssh[1]), owner: ssh[2], repo: ssh[3] };
|
|
12097
|
+
const url1 = /^[a-z+]+:\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+?)\/([^/]+)$/.exec(trimmed);
|
|
12098
|
+
if (url1)
|
|
12099
|
+
return { host: normaliseHost(url1[1]), owner: url1[2], repo: url1[3] };
|
|
12100
|
+
return null;
|
|
12101
|
+
}
|
|
12102
|
+
function normaliseHost(host) {
|
|
12103
|
+
for (const known of ["github.com", "gitlab.com", "bitbucket.org", "codeberg.org", "gitea.com"]) {
|
|
12104
|
+
if (host === known)
|
|
12105
|
+
return known;
|
|
12106
|
+
if (host.startsWith(known + "-") || host.startsWith(known + "."))
|
|
12107
|
+
return known;
|
|
12108
|
+
}
|
|
12109
|
+
return host;
|
|
12110
|
+
}
|
|
12111
|
+
function resolveAutoWorkspace(realPath, minRepos) {
|
|
12112
|
+
let cur = realPath;
|
|
12113
|
+
let last = "";
|
|
12114
|
+
let depth = 0;
|
|
12115
|
+
while (cur && cur !== last && depth < AUTO_WORKSPACE_MAX_DEPTH) {
|
|
12116
|
+
if (!existsSync16(join17(cur, ".git"))) {
|
|
12117
|
+
const cached = workspaceCache.get(cur);
|
|
12118
|
+
let qualifies;
|
|
12119
|
+
if (cached === void 0) {
|
|
12120
|
+
qualifies = countGitChildren(cur) >= minRepos;
|
|
12121
|
+
workspaceCache.set(cur, qualifies ? cur : null);
|
|
12122
|
+
} else {
|
|
12123
|
+
qualifies = cached !== null;
|
|
12124
|
+
}
|
|
12125
|
+
if (qualifies) {
|
|
12126
|
+
const name = basename11(cur);
|
|
12127
|
+
return { id: `ws:${name}`, displayName: name, source: "auto-workspace" };
|
|
12547
12128
|
}
|
|
12548
|
-
}
|
|
12549
|
-
|
|
12129
|
+
}
|
|
12130
|
+
last = cur;
|
|
12131
|
+
cur = dirname13(cur);
|
|
12132
|
+
depth++;
|
|
12550
12133
|
}
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
// ../engine/dist/core/project-resolver.js
|
|
12554
|
-
var project_resolver_exports = {};
|
|
12555
|
-
__export(project_resolver_exports, {
|
|
12556
|
-
_setProjectsConfigForTests: () => _setProjectsConfigForTests,
|
|
12557
|
-
getProjectsConfigPath: () => getProjectsConfigPath,
|
|
12558
|
-
globMatch: () => globMatch,
|
|
12559
|
-
loadProjectsConfig: () => loadProjectsConfig,
|
|
12560
|
-
parseGitRemote: () => parseGitRemote,
|
|
12561
|
-
resetProjectResolverCache: () => resetProjectResolverCache,
|
|
12562
|
-
resolveProjectId: () => resolveProjectId,
|
|
12563
|
-
resolveWorkspaceId: () => resolveWorkspaceId
|
|
12564
|
-
});
|
|
12565
|
-
import { execSync } from "child_process";
|
|
12566
|
-
import { createHash as createHash4 } from "crypto";
|
|
12567
|
-
import { existsSync as existsSync16, readFileSync as readFileSync11, realpathSync as realpathSync2, readdirSync as readdirSync12, statSync as statSync14 } from "fs";
|
|
12568
|
-
import { basename as basename11, dirname as dirname13, isAbsolute, join as join17, resolve, sep } from "path";
|
|
12569
|
-
function getProjectsConfigPath() {
|
|
12570
|
-
return join17(getDataDir(), "projects.json");
|
|
12134
|
+
return null;
|
|
12571
12135
|
}
|
|
12572
|
-
function
|
|
12573
|
-
|
|
12574
|
-
|
|
12136
|
+
function countGitChildren(dir) {
|
|
12137
|
+
let count = 0;
|
|
12138
|
+
let entries;
|
|
12139
|
+
try {
|
|
12140
|
+
entries = readdirSync12(dir);
|
|
12141
|
+
} catch {
|
|
12142
|
+
return 0;
|
|
12575
12143
|
}
|
|
12576
|
-
const
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12144
|
+
for (const name of entries) {
|
|
12145
|
+
if (name.startsWith("."))
|
|
12146
|
+
continue;
|
|
12147
|
+
const child = join17(dir, name);
|
|
12148
|
+
try {
|
|
12149
|
+
const st = statSync13(child);
|
|
12150
|
+
if (!st.isDirectory())
|
|
12151
|
+
continue;
|
|
12152
|
+
if (existsSync16(join17(child, ".git")))
|
|
12153
|
+
count++;
|
|
12154
|
+
} catch {
|
|
12155
|
+
}
|
|
12580
12156
|
}
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12157
|
+
return count;
|
|
12158
|
+
}
|
|
12159
|
+
function safeRealpath(p) {
|
|
12584
12160
|
try {
|
|
12585
|
-
|
|
12586
|
-
const cfg = JSON.parse(raw);
|
|
12587
|
-
cachedConfig = { mtime, cfg };
|
|
12588
|
-
return cfg;
|
|
12161
|
+
return realpathSync2(p);
|
|
12589
12162
|
} catch {
|
|
12590
|
-
|
|
12591
|
-
return cachedConfig.cfg;
|
|
12163
|
+
return resolve(p);
|
|
12592
12164
|
}
|
|
12593
12165
|
}
|
|
12594
|
-
function
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12166
|
+
function isInside(child, parent) {
|
|
12167
|
+
if (!parent)
|
|
12168
|
+
return false;
|
|
12169
|
+
if (child === parent)
|
|
12170
|
+
return true;
|
|
12171
|
+
const c = child.endsWith(sep) ? child : child + sep;
|
|
12172
|
+
const p = parent.endsWith(sep) ? parent : parent + sep;
|
|
12173
|
+
return c.startsWith(p);
|
|
12600
12174
|
}
|
|
12601
|
-
function
|
|
12602
|
-
|
|
12603
|
-
|
|
12175
|
+
function globMatch(pattern, path) {
|
|
12176
|
+
const re = new RegExp("^" + globToRegex(toPosix(pattern)) + "$");
|
|
12177
|
+
return re.test(toPosix(path));
|
|
12604
12178
|
}
|
|
12605
|
-
function
|
|
12606
|
-
|
|
12607
|
-
|
|
12179
|
+
function toPosix(p) {
|
|
12180
|
+
return p.replace(/\\/g, "/");
|
|
12181
|
+
}
|
|
12182
|
+
function globToRegex(glob) {
|
|
12183
|
+
let out = "";
|
|
12184
|
+
let i = 0;
|
|
12185
|
+
while (i < glob.length) {
|
|
12186
|
+
const c = glob[i];
|
|
12187
|
+
if (c === "*" && glob[i + 1] === "*") {
|
|
12188
|
+
out += ".*";
|
|
12189
|
+
i += 2;
|
|
12190
|
+
if (glob[i] === "/")
|
|
12191
|
+
i++;
|
|
12192
|
+
} else if (c === "*") {
|
|
12193
|
+
out += "[^/]*";
|
|
12194
|
+
i++;
|
|
12195
|
+
} else if (c === "?") {
|
|
12196
|
+
out += "[^/]";
|
|
12197
|
+
i++;
|
|
12198
|
+
} else if (c === "{") {
|
|
12199
|
+
const end = glob.indexOf("}", i);
|
|
12200
|
+
if (end === -1) {
|
|
12201
|
+
out += "\\{";
|
|
12202
|
+
i++;
|
|
12203
|
+
continue;
|
|
12204
|
+
}
|
|
12205
|
+
const opts = glob.slice(i + 1, end).split(",").map(escapeRe).join("|");
|
|
12206
|
+
out += "(?:" + opts + ")";
|
|
12207
|
+
i = end + 1;
|
|
12208
|
+
} else {
|
|
12209
|
+
out += escapeRe(c);
|
|
12210
|
+
i++;
|
|
12211
|
+
}
|
|
12608
12212
|
}
|
|
12609
|
-
|
|
12610
|
-
|
|
12213
|
+
return out;
|
|
12214
|
+
}
|
|
12215
|
+
function escapeRe(s) {
|
|
12216
|
+
return s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
12217
|
+
}
|
|
12218
|
+
var DEFAULT_AUTO_WORKSPACE_MIN_REPOS, BOT_WORKTREE_MARKERS, resolveCache, gitCache, workspaceCache, wsIdCache, cachedConfig, GIT_TIMEOUT_MS, AUTO_WORKSPACE_MAX_DEPTH;
|
|
12219
|
+
var init_project_resolver = __esm({
|
|
12220
|
+
"../engine/dist/core/project-resolver.js"() {
|
|
12221
|
+
"use strict";
|
|
12222
|
+
init_paths();
|
|
12223
|
+
DEFAULT_AUTO_WORKSPACE_MIN_REPOS = 3;
|
|
12224
|
+
BOT_WORKTREE_MARKERS = [".claude-pr-bot"];
|
|
12225
|
+
resolveCache = /* @__PURE__ */ new Map();
|
|
12226
|
+
gitCache = /* @__PURE__ */ new Map();
|
|
12227
|
+
workspaceCache = /* @__PURE__ */ new Map();
|
|
12228
|
+
wsIdCache = /* @__PURE__ */ new Map();
|
|
12229
|
+
cachedConfig = null;
|
|
12230
|
+
GIT_TIMEOUT_MS = 1e4;
|
|
12231
|
+
AUTO_WORKSPACE_MAX_DEPTH = 2;
|
|
12611
12232
|
}
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
const
|
|
12617
|
-
|
|
12618
|
-
|
|
12233
|
+
});
|
|
12234
|
+
|
|
12235
|
+
// ../engine/dist/core/credentials-env.js
|
|
12236
|
+
function envCredentials(env = process.env) {
|
|
12237
|
+
const token = (env.CHAT_RECALL_TOKEN || "").trim();
|
|
12238
|
+
const server = (env.CHAT_RECALL_SERVER || "").trim();
|
|
12239
|
+
if (!token || !server)
|
|
12240
|
+
return null;
|
|
12241
|
+
if (!/^https?:\/\//i.test(server))
|
|
12242
|
+
return null;
|
|
12243
|
+
return { serverUrl: normalise(server), token };
|
|
12619
12244
|
}
|
|
12620
|
-
|
|
12621
|
-
|
|
12245
|
+
var normalise;
|
|
12246
|
+
var init_credentials_env = __esm({
|
|
12247
|
+
"../engine/dist/core/credentials-env.js"() {
|
|
12248
|
+
"use strict";
|
|
12249
|
+
normalise = (u) => u.trim().replace(/\/+$/, "");
|
|
12250
|
+
}
|
|
12251
|
+
});
|
|
12252
|
+
|
|
12253
|
+
// ../engine/dist/core/secret-redactor.js
|
|
12254
|
+
var secret_redactor_exports = {};
|
|
12255
|
+
__export(secret_redactor_exports, {
|
|
12256
|
+
DEFAULT_REDACTION_RULES: () => DEFAULT_REDACTION_RULES,
|
|
12257
|
+
_clearServerRulePack: () => _clearServerRulePack,
|
|
12258
|
+
_resetPrivacyCache: () => _resetPrivacyCache,
|
|
12259
|
+
_resetRedactorCache: () => _resetRedactorCache,
|
|
12260
|
+
applyChunkPrivacy: () => applyChunkPrivacy,
|
|
12261
|
+
findContextualSecrets: () => findContextualSecrets,
|
|
12262
|
+
installServerRulePack: () => installServerRulePack,
|
|
12263
|
+
isFilePathRedactionEnabled: () => isFilePathRedactionEnabled,
|
|
12264
|
+
isRedactionEnabled: () => isRedactionEnabled,
|
|
12265
|
+
isToolOutputRedactionEnabled: () => isToolOutputRedactionEnabled,
|
|
12266
|
+
redactFilePaths: () => redactFilePaths,
|
|
12267
|
+
redactSecrets: () => redactSecrets,
|
|
12268
|
+
redactToolResultBody: () => redactToolResultBody,
|
|
12269
|
+
scanTextForFindings: () => scanTextForFindings,
|
|
12270
|
+
serverRulePackVersion: () => serverRulePackVersion,
|
|
12271
|
+
validateRedactionRule: () => validateRedactionRule
|
|
12272
|
+
});
|
|
12273
|
+
import { statSync as statSync14 } from "fs";
|
|
12274
|
+
import { createHash as createHash4 } from "crypto";
|
|
12275
|
+
function secretOf(rule, m) {
|
|
12276
|
+
const raw = rule.secret != null ? m[rule.secret] : m[0];
|
|
12277
|
+
if (raw == null)
|
|
12622
12278
|
return null;
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
return cached;
|
|
12627
|
-
const result = doResolveWorkspace(realPath);
|
|
12628
|
-
wsIdCache.set(realPath, result);
|
|
12629
|
-
return result;
|
|
12279
|
+
if (rule.where && !rule.where(raw))
|
|
12280
|
+
return null;
|
|
12281
|
+
return raw;
|
|
12630
12282
|
}
|
|
12631
|
-
function
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
}
|
|
12638
|
-
const anchor = findGitToplevel(realPath) ?? realPath;
|
|
12639
|
-
const ws = resolveAutoWorkspace(anchor, cfg.autoWorkspaceMinRepos ?? DEFAULT_AUTO_WORKSPACE_MIN_REPOS);
|
|
12640
|
-
return ws ? ws.id : null;
|
|
12283
|
+
function looksLikeSecretToken(t) {
|
|
12284
|
+
if (t.length < 32)
|
|
12285
|
+
return false;
|
|
12286
|
+
if (/^[a-f0-9]+$/i.test(t))
|
|
12287
|
+
return true;
|
|
12288
|
+
return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
|
|
12641
12289
|
}
|
|
12642
|
-
function
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12290
|
+
function findContextualSecrets(text) {
|
|
12291
|
+
if (!text)
|
|
12292
|
+
return [];
|
|
12293
|
+
const ranges = [];
|
|
12294
|
+
let m;
|
|
12295
|
+
SECRET_CONTEXT_RE.lastIndex = 0;
|
|
12296
|
+
while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
|
|
12297
|
+
const winStart = m.index + m[0].length;
|
|
12298
|
+
const window = text.slice(winStart, winStart + 80);
|
|
12299
|
+
HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
|
|
12300
|
+
let tm;
|
|
12301
|
+
while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
|
|
12302
|
+
if (!looksLikeSecretToken(tm[0]))
|
|
12303
|
+
continue;
|
|
12304
|
+
if (/^[a-f0-9]+$/i.test(tm[0]) && /\b(?:commit|sha-?\d*|hash|digest|checksum|uuid|guid|etag|blob|ref)\b/i.test(window.slice(0, tm.index))) {
|
|
12305
|
+
continue;
|
|
12306
|
+
}
|
|
12307
|
+
ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
|
|
12308
|
+
break;
|
|
12309
|
+
}
|
|
12646
12310
|
}
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
const ws = resolveAutoWorkspace(realPath, cfg.autoWorkspaceMinRepos ?? DEFAULT_AUTO_WORKSPACE_MIN_REPOS);
|
|
12657
|
-
if (ws)
|
|
12658
|
-
return ws;
|
|
12659
|
-
return {
|
|
12660
|
-
id: `path:${realPath}`,
|
|
12661
|
-
displayName: basename11(realPath) || realPath,
|
|
12662
|
-
source: "path"
|
|
12663
|
-
};
|
|
12664
|
-
}
|
|
12665
|
-
function matchesIgnore(realPath, cfg) {
|
|
12666
|
-
const rules = cfg.ignore || [];
|
|
12667
|
-
for (const r of rules) {
|
|
12668
|
-
if (globMatch(r.match, realPath))
|
|
12669
|
-
return true;
|
|
12311
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
12312
|
+
const out = [];
|
|
12313
|
+
for (const r of ranges) {
|
|
12314
|
+
const last = out[out.length - 1];
|
|
12315
|
+
if (last && r.start < last.end) {
|
|
12316
|
+
last.end = Math.max(last.end, r.end);
|
|
12317
|
+
continue;
|
|
12318
|
+
}
|
|
12319
|
+
out.push({ ...r });
|
|
12670
12320
|
}
|
|
12671
|
-
return
|
|
12321
|
+
return out;
|
|
12672
12322
|
}
|
|
12673
|
-
function
|
|
12674
|
-
const
|
|
12675
|
-
|
|
12676
|
-
if (idx === -1)
|
|
12323
|
+
function isEnvRedactionRequested() {
|
|
12324
|
+
const v = process.env.CHAT_RECALL_REDACT_INDEX;
|
|
12325
|
+
if (v === void 0)
|
|
12677
12326
|
return null;
|
|
12678
|
-
|
|
12679
|
-
return {
|
|
12680
|
-
id: `path:${botRoot}`,
|
|
12681
|
-
displayName: parts[idx],
|
|
12682
|
-
source: "path"
|
|
12683
|
-
};
|
|
12327
|
+
return v === "1" || v === "true" || v === "yes";
|
|
12684
12328
|
}
|
|
12685
|
-
function
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12329
|
+
function settingsView() {
|
|
12330
|
+
let mtimeMs = -1;
|
|
12331
|
+
try {
|
|
12332
|
+
mtimeMs = statSync14(settingsFilePath()).mtimeMs;
|
|
12333
|
+
} catch {
|
|
12334
|
+
}
|
|
12335
|
+
if (cache && cache.mtimeMs === mtimeMs)
|
|
12336
|
+
return cache;
|
|
12337
|
+
let enabled = false;
|
|
12338
|
+
const extra = [];
|
|
12339
|
+
try {
|
|
12340
|
+
const s = loadSettings();
|
|
12341
|
+
enabled = !!s.privacy?.redactIndex;
|
|
12342
|
+
for (const r of s.privacy?.redactionRules ?? []) {
|
|
12343
|
+
try {
|
|
12344
|
+
extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
|
|
12345
|
+
} catch {
|
|
12700
12346
|
}
|
|
12701
12347
|
}
|
|
12702
|
-
|
|
12703
|
-
id: proj.id,
|
|
12704
|
-
displayName: proj.name || proj.id,
|
|
12705
|
-
source: "user",
|
|
12706
|
-
workspaceId: proj.workspace ? void 0 : void 0
|
|
12707
|
-
};
|
|
12348
|
+
} catch {
|
|
12708
12349
|
}
|
|
12709
|
-
|
|
12350
|
+
cache = { mtimeMs, enabled, rules: [...DEFAULT_REDACTION_RULES, ...extra] };
|
|
12351
|
+
return cache;
|
|
12710
12352
|
}
|
|
12711
|
-
function
|
|
12712
|
-
const
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12353
|
+
function compileServerRule(spec) {
|
|
12354
|
+
const name = spec.name || "(unnamed)";
|
|
12355
|
+
if (!spec.regex)
|
|
12356
|
+
return { name, reason: "empty pattern" };
|
|
12357
|
+
if (spec.regex.length > MAX_PATTERN_LEN)
|
|
12358
|
+
return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
|
|
12359
|
+
let re;
|
|
12360
|
+
try {
|
|
12361
|
+
const flags = "g" + (spec.flags || "").replace(/[^ims]/g, "");
|
|
12362
|
+
re = new RegExp(spec.regex, flags);
|
|
12363
|
+
} catch (e) {
|
|
12364
|
+
return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
|
|
12717
12365
|
}
|
|
12718
|
-
|
|
12366
|
+
if (re.test(""))
|
|
12367
|
+
return { name, reason: "matches the empty string (would redact everything)" };
|
|
12368
|
+
re.lastIndex = 0;
|
|
12369
|
+
if (re.test(CANARY))
|
|
12370
|
+
return { name, reason: "matches benign text \u2014 too broad" };
|
|
12371
|
+
re.lastIndex = 0;
|
|
12372
|
+
return { label: `${spec.source === "pack" ? "pack" : "tenant"}:${name}`, pattern: re };
|
|
12719
12373
|
}
|
|
12720
|
-
function
|
|
12721
|
-
const
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
source: "git-remote"
|
|
12732
|
-
};
|
|
12374
|
+
function validateRedactionRule(spec) {
|
|
12375
|
+
const out = compileServerRule(spec);
|
|
12376
|
+
return "reason" in out ? { ok: false, reason: out.reason } : { ok: true };
|
|
12377
|
+
}
|
|
12378
|
+
function installServerRulePack(pack) {
|
|
12379
|
+
const rejected = [];
|
|
12380
|
+
const rules = [];
|
|
12381
|
+
for (const spec of pack.rules || []) {
|
|
12382
|
+
if (rules.length >= MAX_PACK_RULES) {
|
|
12383
|
+
rejected.push({ name: spec.name || "(unnamed)", reason: `pack exceeds ${MAX_PACK_RULES} rules` });
|
|
12384
|
+
continue;
|
|
12733
12385
|
}
|
|
12386
|
+
const out = compileServerRule(spec);
|
|
12387
|
+
if ("reason" in out)
|
|
12388
|
+
rejected.push(out);
|
|
12389
|
+
else
|
|
12390
|
+
rules.push(out);
|
|
12734
12391
|
}
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
}
|
|
12738
|
-
const hash = createHash4("sha1").update(toplevel).digest("hex").slice(0, 12);
|
|
12739
|
-
return {
|
|
12740
|
-
id: `git-local:${hash}`,
|
|
12741
|
-
displayName: basename11(toplevel),
|
|
12742
|
-
source: "git-local"
|
|
12743
|
-
};
|
|
12392
|
+
const version2 = pack.version || "";
|
|
12393
|
+
serverPack = { version: version2, rules };
|
|
12394
|
+
return { version: version2, accepted: rules.length, rejected };
|
|
12744
12395
|
}
|
|
12745
|
-
function
|
|
12746
|
-
|
|
12747
|
-
let last = "";
|
|
12748
|
-
while (cur && cur !== last) {
|
|
12749
|
-
if (existsSync16(join17(cur, ".git")))
|
|
12750
|
-
return cur;
|
|
12751
|
-
last = cur;
|
|
12752
|
-
cur = dirname13(cur);
|
|
12753
|
-
}
|
|
12754
|
-
return null;
|
|
12396
|
+
function serverRulePackVersion() {
|
|
12397
|
+
return serverPack ? serverPack.version || "unversioned" : null;
|
|
12755
12398
|
}
|
|
12756
|
-
function
|
|
12757
|
-
|
|
12758
|
-
cwd: toplevel,
|
|
12759
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
12760
|
-
encoding: "utf-8",
|
|
12761
|
-
timeout: GIT_TIMEOUT_MS
|
|
12762
|
-
});
|
|
12399
|
+
function _clearServerRulePack() {
|
|
12400
|
+
serverPack = null;
|
|
12763
12401
|
}
|
|
12764
|
-
function
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12402
|
+
function activeRules() {
|
|
12403
|
+
const base3 = settingsView().rules;
|
|
12404
|
+
return serverPack && serverPack.rules.length > 0 ? [...base3, ...serverPack.rules] : base3;
|
|
12405
|
+
}
|
|
12406
|
+
function isRedactionEnabled() {
|
|
12407
|
+
const env = isEnvRedactionRequested();
|
|
12408
|
+
if (env !== null)
|
|
12409
|
+
return env;
|
|
12410
|
+
return settingsView().enabled;
|
|
12411
|
+
}
|
|
12412
|
+
function _resetRedactorCache() {
|
|
12413
|
+
cache = null;
|
|
12414
|
+
}
|
|
12415
|
+
function redactSecrets(text, opts = {}) {
|
|
12416
|
+
if (!opts.force && !isRedactionEnabled())
|
|
12417
|
+
return text;
|
|
12418
|
+
if (!text)
|
|
12419
|
+
return text;
|
|
12420
|
+
const rules = opts.rules || activeRules();
|
|
12421
|
+
let out = text;
|
|
12422
|
+
for (const r of rules) {
|
|
12423
|
+
out = out.replace(r.pattern, (...args) => {
|
|
12424
|
+
const whole = args[0];
|
|
12425
|
+
const m = args.slice(0, args.length);
|
|
12426
|
+
const secret = secretOf(r, m);
|
|
12427
|
+
if (secret == null)
|
|
12428
|
+
return whole;
|
|
12429
|
+
if (opts.count)
|
|
12430
|
+
opts.count.redactions++;
|
|
12431
|
+
return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
|
|
12432
|
+
});
|
|
12770
12433
|
}
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
} catch (err) {
|
|
12778
|
-
return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
12434
|
+
const ctx = findContextualSecrets(out);
|
|
12435
|
+
for (let i = ctx.length - 1; i >= 0; i--) {
|
|
12436
|
+
const { start, end } = ctx[i];
|
|
12437
|
+
if (opts.count)
|
|
12438
|
+
opts.count.redactions++;
|
|
12439
|
+
out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
|
|
12779
12440
|
}
|
|
12441
|
+
return out;
|
|
12780
12442
|
}
|
|
12781
|
-
function
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
gitCache.set(toplevel, probe);
|
|
12788
|
-
return probe;
|
|
12789
|
-
}
|
|
12790
|
-
function parseGitRemote(url) {
|
|
12791
|
-
const trimmed = url.trim().replace(/\.git$/, "");
|
|
12792
|
-
if (!trimmed)
|
|
12793
|
-
return null;
|
|
12794
|
-
const ssh = /^[^@]+@([^:]+):(.+?)\/([^/]+)$/.exec(trimmed);
|
|
12795
|
-
if (ssh)
|
|
12796
|
-
return { host: normaliseHost(ssh[1]), owner: ssh[2], repo: ssh[3] };
|
|
12797
|
-
const url1 = /^[a-z+]+:\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+?)\/([^/]+)$/.exec(trimmed);
|
|
12798
|
-
if (url1)
|
|
12799
|
-
return { host: normaliseHost(url1[1]), owner: url1[2], repo: url1[3] };
|
|
12800
|
-
return null;
|
|
12443
|
+
function maskSecret(s) {
|
|
12444
|
+
if (!s)
|
|
12445
|
+
return "";
|
|
12446
|
+
if (s.length <= 4)
|
|
12447
|
+
return "*".repeat(s.length);
|
|
12448
|
+
return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
|
|
12801
12449
|
}
|
|
12802
|
-
function
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12450
|
+
function globalRegexFor(pattern) {
|
|
12451
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
12452
|
+
const key2 = pattern.source + "\0" + flags;
|
|
12453
|
+
let re = globalRegexCache.get(key2);
|
|
12454
|
+
if (!re) {
|
|
12455
|
+
re = new RegExp(pattern.source, flags);
|
|
12456
|
+
globalRegexCache.set(key2, re);
|
|
12808
12457
|
}
|
|
12809
|
-
return
|
|
12458
|
+
return re;
|
|
12810
12459
|
}
|
|
12811
|
-
function
|
|
12812
|
-
|
|
12813
|
-
let
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
if (
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12460
|
+
function makeLineResolver(text) {
|
|
12461
|
+
const starts = [];
|
|
12462
|
+
for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1))
|
|
12463
|
+
starts.push(i);
|
|
12464
|
+
return (index) => {
|
|
12465
|
+
let lo = 0, hi = starts.length;
|
|
12466
|
+
while (lo < hi) {
|
|
12467
|
+
const mid = lo + hi >> 1;
|
|
12468
|
+
if (starts[mid] < index)
|
|
12469
|
+
lo = mid + 1;
|
|
12470
|
+
else
|
|
12471
|
+
hi = mid;
|
|
12472
|
+
}
|
|
12473
|
+
return lo + 1;
|
|
12474
|
+
};
|
|
12475
|
+
}
|
|
12476
|
+
function scanTextForFindings(text, opts = {}) {
|
|
12477
|
+
if (!text)
|
|
12478
|
+
return [];
|
|
12479
|
+
const rules = opts.rules || activeRules();
|
|
12480
|
+
const findings = [];
|
|
12481
|
+
const seenSpans = /* @__PURE__ */ new Set();
|
|
12482
|
+
const lineAt = makeLineResolver(text);
|
|
12483
|
+
for (const r of rules) {
|
|
12484
|
+
const re = globalRegexFor(r.pattern);
|
|
12485
|
+
re.lastIndex = 0;
|
|
12486
|
+
let m;
|
|
12487
|
+
while ((m = re.exec(text)) !== null) {
|
|
12488
|
+
const secret = secretOf(r, m);
|
|
12489
|
+
if (secret == null) {
|
|
12490
|
+
if (re.lastIndex === m.index)
|
|
12491
|
+
re.lastIndex++;
|
|
12492
|
+
continue;
|
|
12824
12493
|
}
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12494
|
+
const at = m.index + (m[0].length - secret.length);
|
|
12495
|
+
const span = `${at}:${secret.length}`;
|
|
12496
|
+
if (!seenSpans.has(span)) {
|
|
12497
|
+
seenSpans.add(span);
|
|
12498
|
+
findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
|
|
12828
12499
|
}
|
|
12500
|
+
if (re.lastIndex === m.index)
|
|
12501
|
+
re.lastIndex++;
|
|
12829
12502
|
}
|
|
12830
|
-
last = cur;
|
|
12831
|
-
cur = dirname13(cur);
|
|
12832
|
-
depth++;
|
|
12833
12503
|
}
|
|
12834
|
-
|
|
12504
|
+
for (const { start, end } of findContextualSecrets(text)) {
|
|
12505
|
+
const line = lineAt(start);
|
|
12506
|
+
findings.push({ rule: "secret-context", line, preview: maskSecret(text.slice(start, end)) });
|
|
12507
|
+
}
|
|
12508
|
+
return findings;
|
|
12835
12509
|
}
|
|
12836
|
-
function
|
|
12837
|
-
let
|
|
12838
|
-
let entries;
|
|
12510
|
+
function privacyView() {
|
|
12511
|
+
let mtimeMs = -1;
|
|
12839
12512
|
try {
|
|
12840
|
-
|
|
12513
|
+
mtimeMs = statSync14(settingsFilePath()).mtimeMs;
|
|
12841
12514
|
} catch {
|
|
12842
|
-
return 0;
|
|
12843
|
-
}
|
|
12844
|
-
for (const name of entries) {
|
|
12845
|
-
if (name.startsWith("."))
|
|
12846
|
-
continue;
|
|
12847
|
-
const child = join17(dir, name);
|
|
12848
|
-
try {
|
|
12849
|
-
const st = statSync14(child);
|
|
12850
|
-
if (!st.isDirectory())
|
|
12851
|
-
continue;
|
|
12852
|
-
if (existsSync16(join17(child, ".git")))
|
|
12853
|
-
count++;
|
|
12854
|
-
} catch {
|
|
12855
|
-
}
|
|
12856
12515
|
}
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12516
|
+
if (privacyCache && privacyCache.mtimeMs === mtimeMs)
|
|
12517
|
+
return privacyCache;
|
|
12518
|
+
let redactToolOutputs = false, redactFilePaths2 = false;
|
|
12860
12519
|
try {
|
|
12861
|
-
|
|
12520
|
+
const s = loadSettings();
|
|
12521
|
+
redactToolOutputs = !!s.privacy?.redactToolOutputs;
|
|
12522
|
+
redactFilePaths2 = !!s.privacy?.redactFilePaths;
|
|
12862
12523
|
} catch {
|
|
12863
|
-
return resolve(p);
|
|
12864
12524
|
}
|
|
12525
|
+
privacyCache = { mtimeMs, redactToolOutputs, redactFilePaths: redactFilePaths2 };
|
|
12526
|
+
return privacyCache;
|
|
12865
12527
|
}
|
|
12866
|
-
function
|
|
12867
|
-
|
|
12868
|
-
return false;
|
|
12869
|
-
if (child === parent)
|
|
12870
|
-
return true;
|
|
12871
|
-
const c = child.endsWith(sep) ? child : child + sep;
|
|
12872
|
-
const p = parent.endsWith(sep) ? parent : parent + sep;
|
|
12873
|
-
return c.startsWith(p);
|
|
12528
|
+
function _resetPrivacyCache() {
|
|
12529
|
+
privacyCache = null;
|
|
12874
12530
|
}
|
|
12875
|
-
function
|
|
12876
|
-
|
|
12877
|
-
return re.test(toPosix(path));
|
|
12531
|
+
function isToolOutputRedactionEnabled() {
|
|
12532
|
+
return privacyView().redactToolOutputs;
|
|
12878
12533
|
}
|
|
12879
|
-
function
|
|
12880
|
-
return
|
|
12534
|
+
function isFilePathRedactionEnabled() {
|
|
12535
|
+
return privacyView().redactFilePaths;
|
|
12881
12536
|
}
|
|
12882
|
-
function
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
i++;
|
|
12892
|
-
} else if (c === "*") {
|
|
12893
|
-
out += "[^/]*";
|
|
12894
|
-
i++;
|
|
12895
|
-
} else if (c === "?") {
|
|
12896
|
-
out += "[^/]";
|
|
12897
|
-
i++;
|
|
12898
|
-
} else if (c === "{") {
|
|
12899
|
-
const end = glob.indexOf("}", i);
|
|
12900
|
-
if (end === -1) {
|
|
12901
|
-
out += "\\{";
|
|
12902
|
-
i++;
|
|
12903
|
-
continue;
|
|
12904
|
-
}
|
|
12905
|
-
const opts = glob.slice(i + 1, end).split(",").map(escapeRe).join("|");
|
|
12906
|
-
out += "(?:" + opts + ")";
|
|
12907
|
-
i = end + 1;
|
|
12908
|
-
} else {
|
|
12909
|
-
out += escapeRe(c);
|
|
12910
|
-
i++;
|
|
12911
|
-
}
|
|
12912
|
-
}
|
|
12913
|
-
return out;
|
|
12537
|
+
function redactFilePaths(text) {
|
|
12538
|
+
if (!text)
|
|
12539
|
+
return text;
|
|
12540
|
+
return text.replace(PATH_REGEX, (m) => {
|
|
12541
|
+
if (m.startsWith("[REDACTED:"))
|
|
12542
|
+
return m;
|
|
12543
|
+
const h = createHash4("sha256").update(m).digest("hex").slice(0, 12);
|
|
12544
|
+
return `[path:${h}]`;
|
|
12545
|
+
});
|
|
12914
12546
|
}
|
|
12915
|
-
function
|
|
12916
|
-
|
|
12547
|
+
function applyChunkPrivacy(text) {
|
|
12548
|
+
let out = redactSecrets(text);
|
|
12549
|
+
if (privacyView().redactFilePaths)
|
|
12550
|
+
out = redactFilePaths(out);
|
|
12551
|
+
return out;
|
|
12917
12552
|
}
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
init_paths();
|
|
12923
|
-
DEFAULT_AUTO_WORKSPACE_MIN_REPOS = 3;
|
|
12924
|
-
BOT_WORKTREE_MARKERS = [".claude-pr-bot"];
|
|
12925
|
-
resolveCache = /* @__PURE__ */ new Map();
|
|
12926
|
-
gitCache = /* @__PURE__ */ new Map();
|
|
12927
|
-
workspaceCache = /* @__PURE__ */ new Map();
|
|
12928
|
-
wsIdCache = /* @__PURE__ */ new Map();
|
|
12929
|
-
cachedConfig = null;
|
|
12930
|
-
GIT_TIMEOUT_MS = 1e4;
|
|
12931
|
-
AUTO_WORKSPACE_MAX_DEPTH = 2;
|
|
12932
|
-
}
|
|
12933
|
-
});
|
|
12934
|
-
|
|
12935
|
-
// ../engine/dist/core/credentials-env.js
|
|
12936
|
-
function envCredentials(env = process.env) {
|
|
12937
|
-
const token = (env.CHAT_RECALL_TOKEN || "").trim();
|
|
12938
|
-
const server = (env.CHAT_RECALL_SERVER || "").trim();
|
|
12939
|
-
if (!token || !server)
|
|
12940
|
-
return null;
|
|
12941
|
-
if (!/^https?:\/\//i.test(server))
|
|
12942
|
-
return null;
|
|
12943
|
-
return { serverUrl: normalise(server), token };
|
|
12553
|
+
function redactToolResultBody(toolName, body) {
|
|
12554
|
+
if (!body)
|
|
12555
|
+
return body;
|
|
12556
|
+
return `[redacted: ${toolName} output, ${body.length} chars]`;
|
|
12944
12557
|
}
|
|
12945
|
-
var
|
|
12946
|
-
var
|
|
12947
|
-
"../engine/dist/core/
|
|
12558
|
+
var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache, privacyCache, PATH_REGEX;
|
|
12559
|
+
var init_secret_redactor = __esm({
|
|
12560
|
+
"../engine/dist/core/secret-redactor.js"() {
|
|
12948
12561
|
"use strict";
|
|
12949
|
-
|
|
12562
|
+
init_settings();
|
|
12563
|
+
DEFAULT_REDACTION_RULES = [
|
|
12564
|
+
// AWS access tokens. Prefix-anchored: AKIA, ASIA, ABIA (long-term,
|
|
12565
|
+
// STS, EC2). Always exactly 16 hex/alphanumeric after the prefix.
|
|
12566
|
+
// NOTE: this matches only the access-key-ID, NOT the secret key value —
|
|
12567
|
+
// the `env-secret` rule below covers AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN.
|
|
12568
|
+
{ label: "aws-access-token", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
|
|
12569
|
+
// AWS STS session tokens. These are long base64 blobs with NO `NAME=` context
|
|
12570
|
+
// when pasted bare into prose/code, so `env-secret` misses them — but they
|
|
12571
|
+
// reliably start with one of a handful of base64 prefixes (the encoded
|
|
12572
|
+
// `{"...` JSON header). Prefix-anchored → low false-positive. This is the
|
|
12573
|
+
// bare-value form that leaked (`search('IQoJEXAMPLEfakeSESSIONtokenNOTreal01234567890abcDEF==...')`).
|
|
12574
|
+
{ label: "aws-session-token", pattern: /\b(?:IQoJ|FwoG|FwoH|FQoG|FQoH|FwoD)[A-Za-z0-9/+]{40,}={0,2}/g },
|
|
12575
|
+
// Env/shell-assignment secrets — a VAR whose name ends in KEY/SECRET/TOKEN/
|
|
12576
|
+
// PASSWORD/CREDENTIAL, then `=`/`:`, then the value. Zero-width lookbehind so
|
|
12577
|
+
// only the VALUE is redacted (the var name stays for context). This is the
|
|
12578
|
+
// dominant real-world leak vector (`export` dumps, .env files, `curl -H`,
|
|
12579
|
+
// tool output) and is exactly what leaked AWS_SECRET_ACCESS_KEY and
|
|
12580
|
+
// AWS_SESSION_TOKEN in cleartext while the access-key-ID rule above caught
|
|
12581
|
+
// only the (least-sensitive) ID.
|
|
12582
|
+
// Suffix list deliberately broad: DB_PASS (our own helm chart's name!),
|
|
12583
|
+
// NPM_AUTH, SENTRY_DSN, SESSION/COOKIE secrets and BEARER vars all leaked
|
|
12584
|
+
// past the original KEY/SECRET/TOKEN/PASSWORD-only list. Values under 12
|
|
12585
|
+
// chars don't redact, so flag-ish vars (BYPASS=true) stay readable.
|
|
12586
|
+
// Context CONSUMED, secret in group 1 — see RedactionRule.secret. As a
|
|
12587
|
+
// lookbehind this was the second most expensive rule in the collector.
|
|
12588
|
+
{ label: "env-secret", secret: 1, pattern: /\b[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL|AUTH|DSN|SESSION|COOKIE|BEARER|SIGNATURE)S?["']?[ \t]{0,3}[:=][ \t]{0,3}["']?([A-Za-z0-9/+_.~=:@-]{12,})/g },
|
|
12589
|
+
// Credentials embedded in a connection URL: scheme://user:PASSWORD@host.
|
|
12590
|
+
// Redacts only the password segment (keeps scheme/user/host for context).
|
|
12591
|
+
//
|
|
12592
|
+
// Scheme-agnostic on purpose. The old rule allowlisted eight DB schemes, so
|
|
12593
|
+
// `https://user:pass@host`, `ssh://`, `ftp://`, `smtp://` and every other
|
|
12594
|
+
// userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
|
|
12595
|
+
// prefix broke the \b anchor for the one family it did cover. Any scheme
|
|
12596
|
+
// followed by userinfo is a credential, so match the shape, not a list.
|
|
12597
|
+
{ label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
|
|
12598
|
+
// Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
|
|
12599
|
+
// containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
|
|
12600
|
+
// requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
|
|
12601
|
+
// excludes longer blobs/tokens. It catches the value when it appears bare in
|
|
12602
|
+
// prose with NO `NAME=` and NO nearby context word (e.g. inside a markdown
|
|
12603
|
+
// table or quoted in analysis) — the residual leak path the env-secret and
|
|
12604
|
+
// contextual rules both miss. Placed AFTER env-secret/url-password so the
|
|
12605
|
+
// keyed/URL forms keep their more-specific labels.
|
|
12606
|
+
// The three stacked lookaheads each re-scanned the same 40 characters, which
|
|
12607
|
+
// made this the MOST expensive rule of all 106 the collector runs (13.1% of a
|
|
12608
|
+
// 29.5 MB scan). The mixed-case-plus-digit test is now one JS pass.
|
|
12609
|
+
{
|
|
12610
|
+
label: "aws-secret-key",
|
|
12611
|
+
secret: 1,
|
|
12612
|
+
pattern: /(?<![A-Za-z0-9/+])([A-Za-z0-9/+]{40})(?![A-Za-z0-9/+])/g,
|
|
12613
|
+
where: (v) => /[a-z]/.test(v) && /[A-Z]/.test(v) && /[0-9]/.test(v)
|
|
12614
|
+
},
|
|
12615
|
+
// GitHub personal access tokens.
|
|
12616
|
+
{ label: "github-pat", pattern: /\bghp_[A-Za-z0-9]{36}\b/g },
|
|
12617
|
+
// GitHub fine-grained PATs.
|
|
12618
|
+
{ label: "github-fine", pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g },
|
|
12619
|
+
// GitHub OAuth.
|
|
12620
|
+
{ label: "github-oauth", pattern: /\bgho_[A-Za-z0-9]{36}\b/g },
|
|
12621
|
+
// GitHub app + refresh.
|
|
12622
|
+
{ label: "github-app", pattern: /\b(?:ghs|ghu|ghr)_[A-Za-z0-9]{36}\b/g },
|
|
12623
|
+
// Slack bot/user/app tokens.
|
|
12624
|
+
{ label: "slack-token", pattern: /\bxox[abopr]-[A-Za-z0-9-]{10,}\b/g },
|
|
12625
|
+
// Slack incoming webhook URL.
|
|
12626
|
+
{ label: "slack-webhook", pattern: /\bhttps:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+\b/g },
|
|
12627
|
+
// Stripe live + test keys.
|
|
12628
|
+
{ label: "stripe-key", pattern: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g },
|
|
12629
|
+
// Anthropic.
|
|
12630
|
+
{ label: "anthropic-key", pattern: /\bsk-ant-[a-zA-Z0-9_-]{40,}\b/g },
|
|
12631
|
+
// OpenAI. Two shapes, deliberately kept apart. The classic key is
|
|
12632
|
+
// `sk-` + base62 with NO hyphens; the modern scoped keys carry an explicit
|
|
12633
|
+
// proj/svcacct/admin segment first. Allowing hyphens after a bare `sk-`
|
|
12634
|
+
// (which is what admitted sk-proj- in the first place) also matches ordinary
|
|
12635
|
+
// kebab-case text — `sk-fix-the-login-redirect-bug`, `.sk-button-primary`,
|
|
12636
|
+
// `sk-2024-01-01-backup` — and this corpus is full of branch names and CSS
|
|
12637
|
+
// classes. Requiring the segment keeps sk-proj- coverage without that.
|
|
12638
|
+
{ label: "openai-key", pattern: /\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b|\bsk-[A-Za-z0-9]{20,}\b/g },
|
|
12639
|
+
// Google API key (AIza…).
|
|
12640
|
+
{ label: "google-api-key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
12641
|
+
// Generic JWT — three base64url segments.
|
|
12642
|
+
{ label: "jwt", pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
|
|
12643
|
+
// Private key blocks (PEM). The label is a free word run rather than a fixed
|
|
12644
|
+
// list, so ENCRYPTED PRIVATE KEY and PGP PRIVATE KEY BLOCK are covered
|
|
12645
|
+
// without another enumeration, and \1 forces END to name the same kind as
|
|
12646
|
+
// BEGIN.
|
|
12647
|
+
//
|
|
12648
|
+
// The body is [^-], NOT [\s\S]*?. That is a performance boundary, not a
|
|
12649
|
+
// style choice. An unanchored prefix with an unbounded lazy gap re-scans to
|
|
12650
|
+
// end-of-input from every candidate start, so text carrying many BEGIN
|
|
12651
|
+
// markers and no matching END costs O(n^2): 500KB of that measured 810ms
|
|
12652
|
+
// here, and this runs synchronously on the server's event loop over
|
|
12653
|
+
// tenant-supplied text (services/secret-rescan.ts). A PEM body is base64
|
|
12654
|
+
// plus headers and contains no '-', so excluding it makes the scan stop at
|
|
12655
|
+
// the next delimiter instead of at EOF — same matches, linear cost.
|
|
12656
|
+
{ label: "private-key", pattern: /-----BEGIN ([A-Z0-9 ]{0,32})PRIVATE KEY(?: BLOCK)?-----[^-]{0,10000}?-----END \1PRIVATE KEY(?: BLOCK)?-----/g },
|
|
12657
|
+
// PuTTY keys are not PEM. Match the Private-Lines payload — the actual key
|
|
12658
|
+
// material — rather than spanning the whole file, which would need the same
|
|
12659
|
+
// unbounded gap the rule above just removed. Line-anchored and base64-only,
|
|
12660
|
+
// so it cannot run away.
|
|
12661
|
+
{ label: "putty-key", pattern: /^Private-Lines:[ \t]*\d{1,4}[\r\n]+(?:[A-Za-z0-9+/=]{8,120}[\r\n]+){1,400}/gm },
|
|
12662
|
+
// npm tokens (uuid-shaped — overlap with our UUID filter; intentional).
|
|
12663
|
+
{ label: "npm-token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
12664
|
+
// .npmrc auth lines: //registry.npmjs.org/:_authToken=VALUE (the key is not
|
|
12665
|
+
// ALL-CAPS, so the env-secret rule above never matched it). The leading `:`
|
|
12666
|
+
// is required — it is what makes this npmrc syntax rather than any shell var
|
|
12667
|
+
// ending in _AUTH, which env-secret already owns and labels better.
|
|
12668
|
+
// [^\s"'`]+ so the closing quote is not swallowed, and a ${VAR} placeholder
|
|
12669
|
+
// is left alone — redacting it destroys a config example and hides nothing.
|
|
12670
|
+
// The rule the perf profile caught the wedged daemon inside: 98.94% of samples.
|
|
12671
|
+
{ label: "npmrc-auth", secret: 1, pattern: /:_(?:authToken|password|auth)[ \t]*=[ \t]*(?!\$\{)([^\s"'`]{12,})/gi },
|
|
12672
|
+
// HTTP Authorization headers. env-secret requires an ALL-CAPS name, and
|
|
12673
|
+
// `Authorization` is mixed case, so both Bearer and Basic walked straight
|
|
12674
|
+
// through — including short opaque tokens the contextual pass also misses
|
|
12675
|
+
// (it needs 32+ chars AND mixed-case-plus-digit).
|
|
12676
|
+
// Same two guards as npmrc-auth: stop at a quote, and skip the obvious
|
|
12677
|
+
// placeholders that fill documentation (<YOUR_TOKEN>, ${TOKEN}, xxx…).
|
|
12678
|
+
{ label: "auth-header", secret: 1, pattern: /\bauthorization[ \t]*:[ \t]*(?:bearer|basic|token)[ \t]+(?![<$])([^\s"'`<]{8,})/gi },
|
|
12679
|
+
// Vendor tokens with unambiguous prefixes. Each is a fixed literal, so a
|
|
12680
|
+
// false positive is essentially impossible and the cost of listing is low.
|
|
12681
|
+
{ label: "gitlab-pat", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
12682
|
+
{ label: "slack-app-token", pattern: /\bxapp-\d-[A-Z0-9]{8,}-\d{10,}-[a-f0-9]{32,}\b/g },
|
|
12683
|
+
{ label: "sendgrid-key", pattern: /\bSG\.[A-Za-z0-9_-]{20,24}\.[A-Za-z0-9_-]{40,50}\b/g },
|
|
12684
|
+
// (?<![-\w]) so `cache-key-<md5>` is not read as a mailgun key.
|
|
12685
|
+
{ label: "mailgun-key", pattern: /(?<![-\w])key-[a-f0-9]{32}\b/g },
|
|
12686
|
+
{ label: "digitalocean-pat", pattern: /\bdop_v1_[a-f0-9]{64}\b/g },
|
|
12687
|
+
{ label: "huggingface-token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g },
|
|
12688
|
+
{ label: "shopify-token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g },
|
|
12689
|
+
{ label: "pypi-token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}\b/g },
|
|
12690
|
+
// No twilio-sid rule: AC…/SK… are Twilio SIDs, which are public account and
|
|
12691
|
+
// key identifiers, not the auth token. Redacting them hid nothing and
|
|
12692
|
+
// collided with 32-hex etags and MD5 sums.
|
|
12693
|
+
// No discord-bot-token rule either: the shape (word.word.word) matched
|
|
12694
|
+
// ordinary dotted symbols such as a.b.ApplicationContextProviderImpl.
|
|
12695
|
+
{ label: "telegram-bot-token", pattern: /\b\d{8,10}:AA[A-Za-z0-9_-]{32,}\b/g },
|
|
12696
|
+
{ label: "doppler-token", pattern: /\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9_-]{20,}\b/g },
|
|
12697
|
+
// Azure Storage SAS signature — the `sig=` parameter is the actual secret.
|
|
12698
|
+
{ label: "azure-sas", secret: 1, pattern: /[?&]sig=([A-Za-z0-9%/+=]{20,})/g }
|
|
12699
|
+
];
|
|
12700
|
+
SECRET_CONTEXT_RE = /\b(?:secret|password|passwd|credential|access[ _-]?key|private[ _-]?key|api[ _-]?key|auth(?:oriz\w*)?|bearer|token)\b/gi;
|
|
12701
|
+
HIGH_ENTROPY_TOKEN_RE = /[A-Za-z0-9/+_-]{32,}/g;
|
|
12702
|
+
cache = null;
|
|
12703
|
+
CANARY = "the quick brown fox jumps over the lazy dog 0123456789 commit 8ba1f109551bd432803012645ac136ddd475d0fb src/index.ts:42";
|
|
12704
|
+
MAX_PACK_RULES = 200;
|
|
12705
|
+
MAX_PATTERN_LEN = 512;
|
|
12706
|
+
serverPack = null;
|
|
12707
|
+
globalRegexCache = /* @__PURE__ */ new Map();
|
|
12708
|
+
privacyCache = null;
|
|
12709
|
+
PATH_REGEX = /(?<![A-Za-z0-9_])(?:\/[^\s'"`<>]+|[A-Za-z]:\\[^\s'"`<>]+)/g;
|
|
12950
12710
|
}
|
|
12951
12711
|
});
|
|
12952
12712
|
|
|
@@ -14036,10 +13796,10 @@ var init_types = __esm({
|
|
|
14036
13796
|
|
|
14037
13797
|
// ../engine/dist/transcript/opencode.js
|
|
14038
13798
|
async function parseOpenCodeSubagents(parentSessionId) {
|
|
14039
|
-
const { existsSync:
|
|
13799
|
+
const { existsSync: existsSync58 } = await import("fs");
|
|
14040
13800
|
const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
14041
13801
|
const dbPath = opencodeBackend2.dbPath();
|
|
14042
|
-
if (!
|
|
13802
|
+
if (!existsSync58(dbPath))
|
|
14043
13803
|
return [];
|
|
14044
13804
|
const realParent = parentSessionId.replace("opencode_", "");
|
|
14045
13805
|
const db = openSqliteReadonlyOrThrow(dbPath);
|
|
@@ -14080,10 +13840,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
|
|
|
14080
13840
|
}
|
|
14081
13841
|
}
|
|
14082
13842
|
async function parseOpenCodeTranscript(sessionId) {
|
|
14083
|
-
const { existsSync:
|
|
13843
|
+
const { existsSync: existsSync58 } = await import("fs");
|
|
14084
13844
|
const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
14085
13845
|
const dbPath = opencodeBackend2.dbPath();
|
|
14086
|
-
if (!
|
|
13846
|
+
if (!existsSync58(dbPath))
|
|
14087
13847
|
return [];
|
|
14088
13848
|
const realId = sessionId.replace("opencode_", "");
|
|
14089
13849
|
const db = openSqliteReadonlyOrThrow(dbPath);
|
|
@@ -14160,6 +13920,7 @@ __export(transcript_exports, {
|
|
|
14160
13920
|
shadowFileFor: () => shadowFileFor,
|
|
14161
13921
|
shadowRoot: () => shadowRoot,
|
|
14162
13922
|
shadowSizeOnDisk: () => shadowSizeOnDisk,
|
|
13923
|
+
shadowUncompressedBytes: () => shadowUncompressedBytes,
|
|
14163
13924
|
snapshotShadow: () => snapshotShadow,
|
|
14164
13925
|
trimTranscriptForSync: () => trimTranscriptForSync,
|
|
14165
13926
|
updateShadow: () => updateShadow,
|
|
@@ -14929,111 +14690,6 @@ var init_source_registry = __esm({
|
|
|
14929
14690
|
}
|
|
14930
14691
|
});
|
|
14931
14692
|
|
|
14932
|
-
// ../engine/dist/parsers/session-source.js
|
|
14933
|
-
var SessionSource;
|
|
14934
|
-
var init_session_source = __esm({
|
|
14935
|
-
"../engine/dist/parsers/session-source.js"() {
|
|
14936
|
-
"use strict";
|
|
14937
|
-
init_session();
|
|
14938
|
-
init_chunker();
|
|
14939
|
-
init_settings();
|
|
14940
|
-
SessionSource = class {
|
|
14941
|
-
sourceType = "session";
|
|
14942
|
-
claudeDir;
|
|
14943
|
-
constructor(claudeDir) {
|
|
14944
|
-
this.claudeDir = claudeDir;
|
|
14945
|
-
}
|
|
14946
|
-
async *discover() {
|
|
14947
|
-
if (!isSourceEnabled("claude", "sessions"))
|
|
14948
|
-
return;
|
|
14949
|
-
for (const [entry, sessionPath] of getAllSessions(this.claudeDir)) {
|
|
14950
|
-
yield this.entryToItem(entry, sessionPath);
|
|
14951
|
-
}
|
|
14952
|
-
}
|
|
14953
|
-
async parse(item) {
|
|
14954
|
-
const content = await parseSessionFile(item.filePath);
|
|
14955
|
-
const entry = this.itemToEntry(item);
|
|
14956
|
-
const sessionChunks = chunkSession(entry, content);
|
|
14957
|
-
item.extra = {
|
|
14958
|
-
...item.extra || {},
|
|
14959
|
-
...content.metadata
|
|
14960
|
-
};
|
|
14961
|
-
return sessionChunks.map((sc) => ({
|
|
14962
|
-
chunkId: sc.chunkId,
|
|
14963
|
-
itemId: sc.sessionId,
|
|
14964
|
-
sourceType: "session",
|
|
14965
|
-
// Empty title is preferable to "Session abc12345" placeholder text:
|
|
14966
|
-
// the latter pollutes search results and gets surfaced in the UI as
|
|
14967
|
-
// a misleading heading. Downstream renderers fall back to
|
|
14968
|
-
// project path / time when title is empty.
|
|
14969
|
-
title: sc.firstPrompt.slice(0, 100),
|
|
14970
|
-
text: sc.text,
|
|
14971
|
-
chunkType: sc.chunkType,
|
|
14972
|
-
projectPath: sc.projectPath,
|
|
14973
|
-
filePath: sc.sourceFile,
|
|
14974
|
-
mtime: sc.mtime
|
|
14975
|
-
}));
|
|
14976
|
-
}
|
|
14977
|
-
async extractLinks(item) {
|
|
14978
|
-
if (item.filePath) {
|
|
14979
|
-
try {
|
|
14980
|
-
const { detectForkPredecessor: detectForkPredecessor2 } = await Promise.resolve().then(() => (init_raw(), raw_exports));
|
|
14981
|
-
const predecessor = detectForkPredecessor2(item.filePath);
|
|
14982
|
-
if (predecessor) {
|
|
14983
|
-
return [{
|
|
14984
|
-
sourceType: "session",
|
|
14985
|
-
sourceId: item.id,
|
|
14986
|
-
targetType: "session",
|
|
14987
|
-
targetId: predecessor,
|
|
14988
|
-
linkType: "forked_from",
|
|
14989
|
-
confidence: 1
|
|
14990
|
-
}];
|
|
14991
|
-
}
|
|
14992
|
-
} catch {
|
|
14993
|
-
}
|
|
14994
|
-
}
|
|
14995
|
-
return [];
|
|
14996
|
-
}
|
|
14997
|
-
entryToItem(entry, sessionPath) {
|
|
14998
|
-
return {
|
|
14999
|
-
id: entry.sessionId,
|
|
15000
|
-
sourceType: "session",
|
|
15001
|
-
// Empty title when there's no first prompt — the UI then knows to
|
|
15002
|
-
// fall back to summary or project path instead of rendering the
|
|
15003
|
-
// useless "Session abc12345" placeholder as a heading.
|
|
15004
|
-
title: entry.firstPrompt ? entry.firstPrompt.replace(/\n/g, " ").trim().slice(0, 100) : "",
|
|
15005
|
-
projectPath: entry.projectPath,
|
|
15006
|
-
filePath: sessionPath,
|
|
15007
|
-
mtime: entry.fileMtime,
|
|
15008
|
-
contentPreview: entry.firstPrompt?.slice(0, 200),
|
|
15009
|
-
extra: {
|
|
15010
|
-
messageCount: entry.messageCount,
|
|
15011
|
-
gitBranch: entry.gitBranch,
|
|
15012
|
-
created: entry.created,
|
|
15013
|
-
modified: entry.modified,
|
|
15014
|
-
isSidechain: entry.isSidechain
|
|
15015
|
-
}
|
|
15016
|
-
};
|
|
15017
|
-
}
|
|
15018
|
-
itemToEntry(item) {
|
|
15019
|
-
const extra = item.extra || {};
|
|
15020
|
-
return {
|
|
15021
|
-
sessionId: item.id,
|
|
15022
|
-
fullPath: item.filePath,
|
|
15023
|
-
fileMtime: item.mtime,
|
|
15024
|
-
firstPrompt: item.contentPreview || "",
|
|
15025
|
-
messageCount: extra.messageCount || 0,
|
|
15026
|
-
created: extra.created || "",
|
|
15027
|
-
modified: extra.modified || "",
|
|
15028
|
-
gitBranch: extra.gitBranch || "",
|
|
15029
|
-
projectPath: item.projectPath,
|
|
15030
|
-
isSidechain: extra.isSidechain || false
|
|
15031
|
-
};
|
|
15032
|
-
}
|
|
15033
|
-
};
|
|
15034
|
-
}
|
|
15035
|
-
});
|
|
15036
|
-
|
|
15037
14693
|
// ../engine/dist/core/utils.js
|
|
15038
14694
|
import { existsSync as existsSync21, readdirSync as readdirSync15 } from "fs";
|
|
15039
14695
|
import { homedir as homedir6 } from "os";
|
|
@@ -18390,7 +18046,6 @@ var init_plugins_source = __esm({
|
|
|
18390
18046
|
|
|
18391
18047
|
// ../engine/dist/parsers/all-sources.js
|
|
18392
18048
|
function buildSourceRegistry(registry = new SourceRegistry()) {
|
|
18393
|
-
registry.register(new SessionSource());
|
|
18394
18049
|
registry.register(new PlanSource());
|
|
18395
18050
|
registry.register(new TaskSource());
|
|
18396
18051
|
registry.register(new ClaudeMdSource());
|
|
@@ -18414,7 +18069,6 @@ var init_all_sources = __esm({
|
|
|
18414
18069
|
"../engine/dist/parsers/all-sources.js"() {
|
|
18415
18070
|
"use strict";
|
|
18416
18071
|
init_source_registry();
|
|
18417
|
-
init_session_source();
|
|
18418
18072
|
init_plan_source();
|
|
18419
18073
|
init_task_source();
|
|
18420
18074
|
init_claude_md_source();
|
|
@@ -18736,7 +18390,7 @@ var init_sync_fields = __esm({
|
|
|
18736
18390
|
});
|
|
18737
18391
|
|
|
18738
18392
|
// ../engine/dist/core/index-lock.js
|
|
18739
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as
|
|
18393
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as statSync35, closeSync as closeSync8 } from "fs";
|
|
18740
18394
|
import { dirname as dirname16, join as join39 } from "path";
|
|
18741
18395
|
function acquireIndexLock(opts) {
|
|
18742
18396
|
const lockPath = lockFilePath();
|
|
@@ -18748,7 +18402,7 @@ function acquireIndexLock(opts) {
|
|
|
18748
18402
|
}
|
|
18749
18403
|
let fd;
|
|
18750
18404
|
try {
|
|
18751
|
-
fd =
|
|
18405
|
+
fd = openSync8(lockPath, "wx", 384);
|
|
18752
18406
|
} catch (err) {
|
|
18753
18407
|
if (err.code === "EEXIST")
|
|
18754
18408
|
return null;
|
|
@@ -18758,7 +18412,7 @@ function acquireIndexLock(opts) {
|
|
|
18758
18412
|
try {
|
|
18759
18413
|
writeFileSync6(fd, JSON.stringify(holder));
|
|
18760
18414
|
} finally {
|
|
18761
|
-
|
|
18415
|
+
closeSync8(fd);
|
|
18762
18416
|
}
|
|
18763
18417
|
let released = false;
|
|
18764
18418
|
return {
|
|
@@ -18858,7 +18512,7 @@ var init_http = __esm({
|
|
|
18858
18512
|
CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18859
18513
|
} catch {
|
|
18860
18514
|
try {
|
|
18861
|
-
if (true) CLI_VERSION = "0.
|
|
18515
|
+
if (true) CLI_VERSION = "0.7.1";
|
|
18862
18516
|
} catch {
|
|
18863
18517
|
}
|
|
18864
18518
|
}
|
|
@@ -19357,7 +19011,7 @@ var init_telemetry = __esm({
|
|
|
19357
19011
|
init_sync_client();
|
|
19358
19012
|
cliVersion2 = () => {
|
|
19359
19013
|
try {
|
|
19360
|
-
return true ? "0.
|
|
19014
|
+
return true ? "0.7.1" : "dev";
|
|
19361
19015
|
} catch {
|
|
19362
19016
|
return "dev";
|
|
19363
19017
|
}
|
|
@@ -19713,16 +19367,17 @@ __export(auto_update_exports, {
|
|
|
19713
19367
|
noteUpdateFailure: () => noteUpdateFailure,
|
|
19714
19368
|
planAutoUpdate: () => planAutoUpdate,
|
|
19715
19369
|
readUpdateState: () => readUpdateState,
|
|
19370
|
+
resolveNpm: () => resolveNpm,
|
|
19716
19371
|
runAutoUpdate: () => runAutoUpdate,
|
|
19717
19372
|
runningPrefix: () => runningPrefix,
|
|
19718
19373
|
sweepStaleStaging: () => sweepStaleStaging,
|
|
19719
19374
|
tarballUrl: () => tarballUrl
|
|
19720
19375
|
});
|
|
19721
|
-
import { execSync as execSync2 } from "node:child_process";
|
|
19722
|
-
import { readFileSync as readFileSync34 } from "node:fs";
|
|
19376
|
+
import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
|
|
19377
|
+
import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
|
|
19723
19378
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19724
19379
|
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36, mkdirSync as mkdirSync9 } from "node:fs";
|
|
19725
|
-
import { join as join41, dirname as dirname18, sep as sep2 } from "node:path";
|
|
19380
|
+
import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
|
|
19726
19381
|
import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
|
|
19727
19382
|
import { createHash as createHash8 } from "node:crypto";
|
|
19728
19383
|
function compareVersions(a, b) {
|
|
@@ -19837,25 +19492,46 @@ function runningPrefix(moduleUrl = import.meta.url) {
|
|
|
19837
19492
|
const beforeNodeModules = here.slice(0, at);
|
|
19838
19493
|
return beforeNodeModules.endsWith(`${sep2}lib`) ? beforeNodeModules.slice(0, -4) : beforeNodeModules;
|
|
19839
19494
|
}
|
|
19840
|
-
function npmInstall(
|
|
19495
|
+
function npmInstall(npm, args) {
|
|
19841
19496
|
try {
|
|
19842
|
-
|
|
19497
|
+
const opts = { stdio: ["ignore", "ignore", "pipe"], env: npm.env };
|
|
19498
|
+
if (npm.shell) execSync2([npm.file, ...npm.args, ...args].map((a) => `"${a}"`).join(" "), opts);
|
|
19499
|
+
else execFileSync3(npm.file, [...npm.args, ...args], opts);
|
|
19843
19500
|
} catch (e) {
|
|
19844
19501
|
const err = e;
|
|
19845
19502
|
const detail = String(err.stderr ?? "").trim().split("\n").filter((l) => l.trim() && !/^npm (notice|warn)\b/i.test(l)).slice(-4).join(" | ").slice(0, 400);
|
|
19846
19503
|
throw new Error(detail ? `${err.message ?? "install failed"} \u2014 ${detail}` : err.message ?? "install failed");
|
|
19847
19504
|
}
|
|
19848
19505
|
}
|
|
19506
|
+
function resolveNpm(loc) {
|
|
19507
|
+
const win = loc.platform === "win32";
|
|
19508
|
+
const p = win ? pathWin32 : pathPosix;
|
|
19509
|
+
const nodeDir = p.dirname(loc.execPath);
|
|
19510
|
+
const cliUnder = (root) => win ? p.join(root, "node_modules", "npm", "bin", "npm-cli.js") : p.join(root, "lib", "node_modules", "npm", "bin", "npm-cli.js");
|
|
19511
|
+
const candidates2 = [
|
|
19512
|
+
cliUnder(win ? nodeDir : p.join(nodeDir, "..")),
|
|
19513
|
+
...loc.prefix ? [cliUnder(loc.prefix)] : []
|
|
19514
|
+
];
|
|
19515
|
+
const pathKey = Object.keys(loc.env).find((k) => k.toUpperCase() === "PATH") ?? "PATH";
|
|
19516
|
+
const binDirs = [nodeDir, ...loc.prefix ? [win ? loc.prefix : p.join(loc.prefix, "bin")] : []];
|
|
19517
|
+
const delimiter3 = win ? ";" : ":";
|
|
19518
|
+
const current = loc.env[pathKey] ?? "";
|
|
19519
|
+
const env = { ...loc.env, [pathKey]: [...binDirs, ...current ? [current] : []].join(delimiter3) };
|
|
19520
|
+
const cli = candidates2.find((c) => loc.exists(c));
|
|
19521
|
+
if (cli) return { file: loc.execPath, args: [cli], env, shell: false };
|
|
19522
|
+
return { file: "npm", args: [], env, shell: true };
|
|
19523
|
+
}
|
|
19849
19524
|
function realInstall(tgz) {
|
|
19850
19525
|
const prefix = runningPrefix();
|
|
19526
|
+
const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync41 });
|
|
19851
19527
|
if (prefix) {
|
|
19852
|
-
npmInstall(
|
|
19528
|
+
npmInstall(npm, ["install", "-g", "--prefix", prefix, tgz]);
|
|
19853
19529
|
return;
|
|
19854
19530
|
}
|
|
19855
19531
|
try {
|
|
19856
|
-
npmInstall(
|
|
19532
|
+
npmInstall(npm, ["install", "-g", tgz]);
|
|
19857
19533
|
} catch {
|
|
19858
|
-
npmInstall(
|
|
19534
|
+
npmInstall(npm, ["install", "-g", "--prefix", join41(homedir15(), ".local"), tgz]);
|
|
19859
19535
|
}
|
|
19860
19536
|
}
|
|
19861
19537
|
function realRestart(platform2) {
|
|
@@ -19956,7 +19632,9 @@ var init_auto_update = __esm({
|
|
|
19956
19632
|
var sync_client_exports = {};
|
|
19957
19633
|
__export(sync_client_exports, {
|
|
19958
19634
|
BATCH_SCAN_MAX_BYTES: () => BATCH_SCAN_MAX_BYTES,
|
|
19635
|
+
DERIVED_MAX_BYTES: () => DERIVED_MAX_BYTES,
|
|
19959
19636
|
FULL_BUILD_MAX_BYTES: () => FULL_BUILD_MAX_BYTES,
|
|
19637
|
+
SYNC_CHUNK_BYTES: () => SYNC_CHUNK_BYTES,
|
|
19960
19638
|
_acceptedUploads: () => _acceptedUploads,
|
|
19961
19639
|
_fetchTenantSyncConfigForTests: () => _fetchTenantSyncConfigForTests,
|
|
19962
19640
|
_resetCapsCacheForTests: () => _resetCapsCacheForTests,
|
|
@@ -19975,8 +19653,10 @@ __export(sync_client_exports, {
|
|
|
19975
19653
|
reconcileFields: () => reconcileFields,
|
|
19976
19654
|
removeCredentials: () => removeCredentials,
|
|
19977
19655
|
saveCredentials: () => saveCredentials,
|
|
19656
|
+
scanTranscriptFindingsChunked: () => scanTranscriptFindingsChunked,
|
|
19978
19657
|
serverAcceptsGzipBody: () => serverAcceptsGzipBody,
|
|
19979
19658
|
serverIngestConcurrency: () => serverIngestConcurrency,
|
|
19659
|
+
sessionBuildBytes: () => sessionBuildBytes,
|
|
19980
19660
|
sessionFileBytes: () => sessionFileBytes,
|
|
19981
19661
|
setServerIngestConcurrency: () => setServerIngestConcurrency,
|
|
19982
19662
|
syncIncremental: () => syncIncremental,
|
|
@@ -20748,16 +20428,53 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20748
20428
|
clearTimeout(timer);
|
|
20749
20429
|
}
|
|
20750
20430
|
};
|
|
20431
|
+
const postChunkedHead = async (conv, fileSize) => {
|
|
20432
|
+
const headers = { "content-type": "application/json" };
|
|
20433
|
+
if (cred.token) headers.authorization = `Bearer ${cred.token}`;
|
|
20434
|
+
const ac = new AbortController();
|
|
20435
|
+
const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
|
|
20436
|
+
try {
|
|
20437
|
+
const res = await fetch(`${base3}/api/sync`, {
|
|
20438
|
+
method: "POST",
|
|
20439
|
+
headers,
|
|
20440
|
+
body: JSON.stringify({ conversations: [conv] }),
|
|
20441
|
+
signal: ac.signal
|
|
20442
|
+
});
|
|
20443
|
+
if (!res.ok) throw new Error(`chunked head sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
20444
|
+
const body = await res.json().catch(() => ({}));
|
|
20445
|
+
const id = conv.session_id;
|
|
20446
|
+
const guarded = (body.shrink_guarded ?? []).find((g) => g.session_id === id);
|
|
20447
|
+
const mtime = conv.mtime;
|
|
20448
|
+
try {
|
|
20449
|
+
if (!guarded) {
|
|
20450
|
+
markSynced(base3, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
|
|
20451
|
+
} else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
|
|
20452
|
+
markSynced(base3, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
|
|
20453
|
+
} else {
|
|
20454
|
+
markSynced(base3, [{ id, mtime }]);
|
|
20455
|
+
}
|
|
20456
|
+
} catch {
|
|
20457
|
+
}
|
|
20458
|
+
appendResults.uploaded++;
|
|
20459
|
+
} finally {
|
|
20460
|
+
clearTimeout(timer);
|
|
20461
|
+
}
|
|
20462
|
+
};
|
|
20751
20463
|
let appendBatch = [];
|
|
20464
|
+
let appendBatchBytes = 0;
|
|
20752
20465
|
const APPEND_BATCH_SIZE = 50;
|
|
20466
|
+
const APPEND_BATCH_MAX_BYTES = SYNC_CHUNK_BYTES;
|
|
20753
20467
|
const appendFlush = async () => {
|
|
20754
20468
|
if (appendBatch.length === 0) return;
|
|
20755
20469
|
const batch = appendBatch;
|
|
20756
20470
|
appendBatch = [];
|
|
20471
|
+
appendBatchBytes = 0;
|
|
20757
20472
|
await postAppend(batch);
|
|
20758
20473
|
};
|
|
20759
|
-
const appendAdd = async (conv) => {
|
|
20474
|
+
const appendAdd = async (conv, bytes = 0) => {
|
|
20475
|
+
if (appendBatch.length > 0 && appendBatchBytes + bytes > APPEND_BATCH_MAX_BYTES) await appendFlush();
|
|
20760
20476
|
appendBatch.push(conv);
|
|
20477
|
+
appendBatchBytes += bytes;
|
|
20761
20478
|
if (appendBatch.length >= APPEND_BATCH_SIZE) await appendFlush();
|
|
20762
20479
|
};
|
|
20763
20480
|
const itemsBatch = makeBatcher("items", 100);
|
|
@@ -20836,7 +20553,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20836
20553
|
let externalFindings = null;
|
|
20837
20554
|
if (scanSecrets) {
|
|
20838
20555
|
externalFindings = /* @__PURE__ */ new Map();
|
|
20839
|
-
const toScan = slice.filter((ref) => modeOf(ref) === "full" &&
|
|
20556
|
+
const toScan = slice.filter((ref) => modeOf(ref) === "full" && sessionBuildBytes(ref) <= FULL_BUILD_MAX_BYTES);
|
|
20840
20557
|
trace?.(`batch secret scan start: ${toScan.length} session(s), \u2264${(BATCH_SCAN_MAX_BYTES / 1048576).toFixed(0)}MB per slice`);
|
|
20841
20558
|
const batch = batchScanExternal(
|
|
20842
20559
|
toScan.map((ref) => ({
|
|
@@ -20880,11 +20597,17 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20880
20597
|
const ack = ledger.get(ref.prefixedId);
|
|
20881
20598
|
try {
|
|
20882
20599
|
const tail = await buildConversationTail(ref, ack.o ?? 0, { mapPath, tenantRules });
|
|
20883
|
-
|
|
20600
|
+
const chunked = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
|
|
20601
|
+
if (tail && tail.findings.length > 0 && !chunked) {
|
|
20884
20602
|
promotedForFindings = true;
|
|
20885
20603
|
} else if (tail) {
|
|
20886
20604
|
redactions += tail.redactions;
|
|
20887
|
-
await appendAdd(tail.conv);
|
|
20605
|
+
await appendAdd(tail.conv, tail.bytes);
|
|
20606
|
+
if (tail.findings.length > 0) {
|
|
20607
|
+
await appendFlush();
|
|
20608
|
+
const all = await scanTranscriptFindingsChunked(ref, tenantRules);
|
|
20609
|
+
if (all.length > 0) await findingsBatch.add(all);
|
|
20610
|
+
}
|
|
20888
20611
|
walked++;
|
|
20889
20612
|
if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
|
|
20890
20613
|
continue;
|
|
@@ -20932,12 +20655,13 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
20932
20655
|
}
|
|
20933
20656
|
redactions += built.redactions;
|
|
20934
20657
|
if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
|
|
20935
|
-
await
|
|
20658
|
+
if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
|
|
20659
|
+
else await convBatch.add(built.conv);
|
|
20936
20660
|
if (built.findings.length > 0) await findingsBatch.add(built.findings);
|
|
20937
20661
|
if (upload.sessionMeta) {
|
|
20938
20662
|
await accumulateKg(extractEntities(built.kgText, { projectPath: built.projectPath, sourceType: "session", sessionId: ref.prefixedId, validFrom: ref.mtime ? new Date(ref.mtime).toISOString().slice(0, 10) : void 0 }), ref.prefixedId);
|
|
20939
20663
|
}
|
|
20940
|
-
if (upload.sessionMeta && sessionFileBytes(ref) <=
|
|
20664
|
+
if (upload.sessionMeta && sessionFileBytes(ref) <= DERIVED_MAX_BYTES) {
|
|
20941
20665
|
try {
|
|
20942
20666
|
const count = { redactions: 0 };
|
|
20943
20667
|
const offloaded = await scanPool().runDerived({
|
|
@@ -21117,35 +20841,70 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21117
20841
|
scanMs: Math.round(scanMs)
|
|
21118
20842
|
};
|
|
21119
20843
|
}
|
|
20844
|
+
async function scanTranscriptFindingsChunked(ref, tenantRules) {
|
|
20845
|
+
const backend = getBackendForId(ref.prefixedId);
|
|
20846
|
+
const findings = [];
|
|
20847
|
+
if (!backend?.readFromOffset) return findings;
|
|
20848
|
+
let offset = 0;
|
|
20849
|
+
let lineBase = 0;
|
|
20850
|
+
for (; ; ) {
|
|
20851
|
+
const chunk = await backend.readFromOffset(ref.prefixedId, offset, SYNC_CHUNK_BYTES);
|
|
20852
|
+
if (chunk.newOffset <= offset) break;
|
|
20853
|
+
offset = chunk.newOffset;
|
|
20854
|
+
if (chunk.skippedBytes) {
|
|
20855
|
+
lineBase += 1;
|
|
20856
|
+
continue;
|
|
20857
|
+
}
|
|
20858
|
+
try {
|
|
20859
|
+
for (const f of scanTextForFindings(chunk.text)) {
|
|
20860
|
+
findings.push({ session_id: ref.prefixedId, detector: "builtin", rule: f.rule, line: lineBase + f.line, preview: f.preview });
|
|
20861
|
+
}
|
|
20862
|
+
if (tenantRules && tenantRules.length > 0) {
|
|
20863
|
+
for (const f of scanTenantRules(chunk.text, tenantRules)) {
|
|
20864
|
+
findings.push({ session_id: ref.prefixedId, detector: f.detector, rule: f.rule, line: lineBase + f.line, preview: f.preview });
|
|
20865
|
+
}
|
|
20866
|
+
}
|
|
20867
|
+
} catch {
|
|
20868
|
+
}
|
|
20869
|
+
for (let i = chunk.text.indexOf("\n"); i >= 0; i = chunk.text.indexOf("\n", i + 1)) lineBase++;
|
|
20870
|
+
}
|
|
20871
|
+
return dropFuzzyFindings(findings, (f) => ({ detector: f.detector, rule: f.rule }));
|
|
20872
|
+
}
|
|
21120
20873
|
function sessionFileBytes(ref) {
|
|
21121
20874
|
const backend = getBackendForId(ref.prefixedId);
|
|
21122
20875
|
if (!backend?.isAppendOnly?.()) return 0;
|
|
21123
20876
|
return backend.fileSize?.(ref.prefixedId) ?? 0;
|
|
21124
20877
|
}
|
|
20878
|
+
function sessionBuildBytes(ref) {
|
|
20879
|
+
const main = sessionFileBytes(ref);
|
|
20880
|
+
if (main === 0) return 0;
|
|
20881
|
+
return Math.max(main, shadowUncompressedBytes(ref.toolId, ref.rawId));
|
|
20882
|
+
}
|
|
21125
20883
|
async function buildConversationSync(ref, mtime, opts = {}) {
|
|
21126
20884
|
const mapPath = opts.mapPath ?? ((p) => p);
|
|
21127
20885
|
let includeRaw = opts.includeRaw !== false;
|
|
21128
20886
|
let includeMeta = opts.includeMeta !== false;
|
|
21129
20887
|
let srcHash;
|
|
21130
|
-
const
|
|
21131
|
-
|
|
21132
|
-
let
|
|
21133
|
-
let tailOffsetEnd = 0;
|
|
20888
|
+
const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
|
|
20889
|
+
let headOffsetEnd = 0;
|
|
20890
|
+
let oversizedFindings = null;
|
|
21134
20891
|
let transcript = null;
|
|
21135
20892
|
let fullestContainer = null;
|
|
21136
20893
|
if (oversized) {
|
|
21137
20894
|
const backend = getBackendForId(ref.prefixedId);
|
|
21138
20895
|
if (!backend?.readFromOffset) return null;
|
|
21139
20896
|
try {
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
|
|
20897
|
+
let head = await backend.readFromOffset(ref.prefixedId, 0, SYNC_CHUNK_BYTES);
|
|
20898
|
+
for (let hops = 0; head.skippedBytes && hops < 8; hops++) {
|
|
20899
|
+
head = await backend.readFromOffset(ref.prefixedId, head.newOffset, SYNC_CHUNK_BYTES);
|
|
20900
|
+
}
|
|
20901
|
+
headOffsetEnd = head.newOffset;
|
|
20902
|
+
const messages = parseTailMessages(ref.toolId, head.text);
|
|
21144
20903
|
if (messages.length > 0) transcript = { messages, subagents: [] };
|
|
21145
20904
|
} catch {
|
|
21146
20905
|
}
|
|
21147
20906
|
includeRaw = false;
|
|
21148
|
-
|
|
20907
|
+
oversizedFindings = await scanTranscriptFindingsChunked(ref, opts.tenantRules);
|
|
21149
20908
|
} else {
|
|
21150
20909
|
try {
|
|
21151
20910
|
const backend = getBackendForId(ref.prefixedId) ?? getBackend("claude");
|
|
@@ -21206,7 +20965,6 @@ async function buildConversationSync(ref, mtime, opts = {}) {
|
|
|
21206
20965
|
);
|
|
21207
20966
|
let projectPath = ref.projectPath;
|
|
21208
20967
|
const meta = { messageCount: textMessages.length };
|
|
21209
|
-
if (oversized) meta.truncated = true;
|
|
21210
20968
|
if (textMessages.filter((m) => m.role === "user").length <= 1) meta.oneShot = true;
|
|
21211
20969
|
try {
|
|
21212
20970
|
const cwd = ref.fullPath ? readCwdFromJsonl(ref.fullPath) : "";
|
|
@@ -21320,21 +21078,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
|
|
|
21320
21078
|
}
|
|
21321
21079
|
scanMs = performance.now() - t0;
|
|
21322
21080
|
}
|
|
21323
|
-
if (
|
|
21324
|
-
const t0 = performance.now();
|
|
21325
|
-
try {
|
|
21326
|
-
for (const f of scanTextForFindings(tailText)) {
|
|
21327
|
-
findings.push({ session_id: ref.prefixedId, detector: "builtin", rule: f.rule, line: f.line, preview: f.preview });
|
|
21328
|
-
}
|
|
21329
|
-
if (opts.tenantRules && opts.tenantRules.length > 0) {
|
|
21330
|
-
for (const f of scanTenantRules(tailText, opts.tenantRules)) {
|
|
21331
|
-
findings.push({ session_id: ref.prefixedId, detector: f.detector, rule: f.rule, line: f.line, preview: f.preview });
|
|
21332
|
-
}
|
|
21333
|
-
}
|
|
21334
|
-
} catch {
|
|
21335
|
-
}
|
|
21336
|
-
scanMs = performance.now() - t0;
|
|
21337
|
-
}
|
|
21081
|
+
if (oversizedFindings) findings.push(...oversizedFindings);
|
|
21338
21082
|
const keptFindings = dropFuzzyFindings(findings, (f) => ({ detector: f.detector, rule: f.rule }));
|
|
21339
21083
|
findings.length = 0;
|
|
21340
21084
|
findings.push(...keptFindings);
|
|
@@ -21380,7 +21124,10 @@ async function buildConversationSync(ref, mtime, opts = {}) {
|
|
|
21380
21124
|
// (convBatch onFlush uses this, not a recomputed size) so the next
|
|
21381
21125
|
// append's base_offset matches exactly — no spurious continuity misses on
|
|
21382
21126
|
// an actively-growing session. undefined for non-append-only backends.
|
|
21383
|
-
from_offset: oversized ?
|
|
21127
|
+
from_offset: oversized ? headOffsetEnd : getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId),
|
|
21128
|
+
// The head of a chunked session. The server reports back when its shrink
|
|
21129
|
+
// guard keeps a fuller stored copy, so the client appends from there.
|
|
21130
|
+
...oversized ? { chunked: true } : {},
|
|
21384
21131
|
meta,
|
|
21385
21132
|
mtime
|
|
21386
21133
|
},
|
|
@@ -21402,12 +21149,30 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
|
|
|
21402
21149
|
const backend = getBackendForId(ref.prefixedId);
|
|
21403
21150
|
if (!backend?.readFromOffset) return null;
|
|
21404
21151
|
const mapPath = opts.mapPath ?? ((p) => p);
|
|
21405
|
-
const { text: tailText, newOffset } = await backend.readFromOffset(ref.prefixedId, fromOffset);
|
|
21406
|
-
if (
|
|
21407
|
-
const messages = parseTailMessages(ref.toolId, tailText);
|
|
21408
|
-
if (messages.length === 0) return null;
|
|
21152
|
+
const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
|
|
21153
|
+
if (newOffset <= fromOffset) return null;
|
|
21154
|
+
const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
|
|
21409
21155
|
const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
|
|
21410
|
-
|
|
21156
|
+
const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
|
|
21157
|
+
if (!hasContent) {
|
|
21158
|
+
return {
|
|
21159
|
+
conv: {
|
|
21160
|
+
session_id: ref.prefixedId,
|
|
21161
|
+
tool: ref.toolId,
|
|
21162
|
+
project_path: mapPath(ref.projectPath),
|
|
21163
|
+
append: true,
|
|
21164
|
+
base_offset: fromOffset,
|
|
21165
|
+
from_offset: newOffset,
|
|
21166
|
+
redacted_text: "",
|
|
21167
|
+
envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
|
|
21168
|
+
mtime: Math.floor(ref.mtime) || 0
|
|
21169
|
+
},
|
|
21170
|
+
redactions: 0,
|
|
21171
|
+
newOffset,
|
|
21172
|
+
findings: [],
|
|
21173
|
+
bytes: 0
|
|
21174
|
+
};
|
|
21175
|
+
}
|
|
21411
21176
|
const firstUser = textMessages.find((m) => m.role === "user");
|
|
21412
21177
|
if (isInternalToolPrompt(firstUser?.content)) return null;
|
|
21413
21178
|
const count = { redactions: 0 };
|
|
@@ -21450,7 +21215,8 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
|
|
|
21450
21215
|
},
|
|
21451
21216
|
redactions: count.redactions,
|
|
21452
21217
|
newOffset,
|
|
21453
|
-
findings
|
|
21218
|
+
findings,
|
|
21219
|
+
bytes: Buffer.byteLength(tailText)
|
|
21454
21220
|
};
|
|
21455
21221
|
}
|
|
21456
21222
|
function isSyncSkip(r) {
|
|
@@ -21480,7 +21246,7 @@ async function syncIncremental(opts = {}) {
|
|
|
21480
21246
|
lock.release();
|
|
21481
21247
|
}
|
|
21482
21248
|
}
|
|
21483
|
-
var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES,
|
|
21249
|
+
var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, DERIVED_MAX_BYTES, SYNC_CHUNK_BYTES, BATCH_SCAN_MAX_BYTES;
|
|
21484
21250
|
var init_sync_client = __esm({
|
|
21485
21251
|
"src/sync-client.ts"() {
|
|
21486
21252
|
"use strict";
|
|
@@ -21525,7 +21291,7 @@ var init_sync_client = __esm({
|
|
|
21525
21291
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21526
21292
|
} catch {
|
|
21527
21293
|
try {
|
|
21528
|
-
if (true) ownVersion = "0.
|
|
21294
|
+
if (true) ownVersion = "0.7.1";
|
|
21529
21295
|
} catch {
|
|
21530
21296
|
}
|
|
21531
21297
|
}
|
|
@@ -21552,8 +21318,9 @@ var init_sync_client = __esm({
|
|
|
21552
21318
|
capsOk = /* @__PURE__ */ new Map();
|
|
21553
21319
|
advertisedIngestConc = /* @__PURE__ */ new Map();
|
|
21554
21320
|
advertisedGzipBody = /* @__PURE__ */ new Set();
|
|
21555
|
-
FULL_BUILD_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_FULL_BUILD_MAX_MB || "
|
|
21556
|
-
|
|
21321
|
+
FULL_BUILD_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_FULL_BUILD_MAX_MB || "24", 10)) * 1024 * 1024;
|
|
21322
|
+
DERIVED_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_DERIVED_MAX_MB || "64", 10)) * 1024 * 1024;
|
|
21323
|
+
SYNC_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
21557
21324
|
BATCH_SCAN_MAX_BYTES = Math.max(1, parseInt(process.env.CHAT_RECALL_BATCHSCAN_MAX_MB || "64", 10)) * 1024 * 1024;
|
|
21558
21325
|
}
|
|
21559
21326
|
});
|
|
@@ -21674,7 +21441,7 @@ var init_update_notice = __esm({
|
|
|
21674
21441
|
});
|
|
21675
21442
|
|
|
21676
21443
|
// ../engine/dist/core/which.js
|
|
21677
|
-
import { existsSync as
|
|
21444
|
+
import { existsSync as existsSync42, statSync as statSync38, accessSync, constants } from "node:fs";
|
|
21678
21445
|
import { join as join44, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
|
|
21679
21446
|
function executable(p) {
|
|
21680
21447
|
try {
|
|
@@ -21715,7 +21482,7 @@ function resolveOnPath(name) {
|
|
|
21715
21482
|
continue;
|
|
21716
21483
|
for (const c of candidates(name)) {
|
|
21717
21484
|
const full = join44(dir, c);
|
|
21718
|
-
if (
|
|
21485
|
+
if (existsSync42(full) && executable(full))
|
|
21719
21486
|
return full;
|
|
21720
21487
|
}
|
|
21721
21488
|
}
|
|
@@ -21778,7 +21545,7 @@ __export(mcp_clients_exports, {
|
|
|
21778
21545
|
inspectMcpClients: () => inspectMcpClients,
|
|
21779
21546
|
registerMcpEverywhere: () => registerMcpEverywhere
|
|
21780
21547
|
});
|
|
21781
|
-
import { existsSync as
|
|
21548
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync12, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "fs";
|
|
21782
21549
|
import { homedir as homedir16 } from "os";
|
|
21783
21550
|
import { dirname as dirname21, join as join46 } from "path";
|
|
21784
21551
|
function envHome(varName, fallback) {
|
|
@@ -21797,7 +21564,7 @@ function readJsonObject(path) {
|
|
|
21797
21564
|
}
|
|
21798
21565
|
function registerMcpServersJson(target, path, spec) {
|
|
21799
21566
|
let cfg = {};
|
|
21800
|
-
if (
|
|
21567
|
+
if (existsSync44(path)) {
|
|
21801
21568
|
try {
|
|
21802
21569
|
cfg = readJsonObject(path);
|
|
21803
21570
|
} catch (err) {
|
|
@@ -21826,7 +21593,7 @@ function registerOpencodeJson(path, spec) {
|
|
|
21826
21593
|
const id = "opencode";
|
|
21827
21594
|
const label = "OpenCode";
|
|
21828
21595
|
let cfg = {};
|
|
21829
|
-
if (
|
|
21596
|
+
if (existsSync44(path)) {
|
|
21830
21597
|
try {
|
|
21831
21598
|
cfg = readJsonObject(path);
|
|
21832
21599
|
} catch (err) {
|
|
@@ -21865,7 +21632,7 @@ function registerCodexToml(path, spec) {
|
|
|
21865
21632
|
const id = "codex";
|
|
21866
21633
|
const label = "Codex";
|
|
21867
21634
|
const block = codexBlock(spec);
|
|
21868
|
-
const prev =
|
|
21635
|
+
const prev = existsSync44(path) ? readFileSync37(path, "utf-8") : "";
|
|
21869
21636
|
const lines = prev.length ? prev.split("\n") : [];
|
|
21870
21637
|
const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
|
|
21871
21638
|
if (start === -1) {
|
|
@@ -21906,7 +21673,7 @@ function inspectMcpClients(opts = {}) {
|
|
|
21906
21673
|
return MCP_CLIENTS.map((c) => {
|
|
21907
21674
|
const path = c.configPath(home);
|
|
21908
21675
|
let registered = false;
|
|
21909
|
-
if (
|
|
21676
|
+
if (existsSync44(path)) {
|
|
21910
21677
|
try {
|
|
21911
21678
|
const raw = readFileSync37(path, "utf-8");
|
|
21912
21679
|
registered = c.id === "codex" ? raw.includes(CODEX_HEADER) : !!(JSON.parse(raw).mcpServers?.[MCP_SERVER_NAME] ?? JSON.parse(raw).mcp?.[MCP_SERVER_NAME]);
|
|
@@ -21943,7 +21710,7 @@ var init_mcp_clients = __esm({
|
|
|
21943
21710
|
configPath: (home) => join46(envHome("CHAT_RECALL_CODEX_HOME", join46(home, ".codex")), "config.toml"),
|
|
21944
21711
|
isPresent(home) {
|
|
21945
21712
|
const p = this.configPath(home);
|
|
21946
|
-
return
|
|
21713
|
+
return existsSync44(p) || existsSync44(dirname21(p));
|
|
21947
21714
|
},
|
|
21948
21715
|
register(spec, home) {
|
|
21949
21716
|
return registerCodexToml(this.configPath(home), spec);
|
|
@@ -21956,7 +21723,7 @@ var init_mcp_clients = __esm({
|
|
|
21956
21723
|
configPath: (home) => join46(envHome("XDG_CONFIG_HOME", join46(home, ".config")), "opencode", "opencode.json"),
|
|
21957
21724
|
isPresent(home) {
|
|
21958
21725
|
const p = this.configPath(home);
|
|
21959
|
-
return
|
|
21726
|
+
return existsSync44(p) || existsSync44(dirname21(p));
|
|
21960
21727
|
},
|
|
21961
21728
|
register(spec, home) {
|
|
21962
21729
|
return registerOpencodeJson(this.configPath(home), spec);
|
|
@@ -21970,7 +21737,7 @@ var init_mcp_clients = __esm({
|
|
|
21970
21737
|
bin: "agy",
|
|
21971
21738
|
configPath: (home) => join46(home, ".gemini", "config", "mcp_config.json"),
|
|
21972
21739
|
isPresent(home) {
|
|
21973
|
-
return
|
|
21740
|
+
return existsSync44(join46(home, ".gemini", "antigravity-cli")) || existsSync44(this.configPath(home));
|
|
21974
21741
|
},
|
|
21975
21742
|
register(spec, home) {
|
|
21976
21743
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -21983,7 +21750,7 @@ var init_mcp_clients = __esm({
|
|
|
21983
21750
|
configPath: (home) => join46(home, ".cursor", "mcp.json"),
|
|
21984
21751
|
isPresent(home) {
|
|
21985
21752
|
const p = this.configPath(home);
|
|
21986
|
-
return
|
|
21753
|
+
return existsSync44(p) || existsSync44(dirname21(p));
|
|
21987
21754
|
},
|
|
21988
21755
|
register(spec, home) {
|
|
21989
21756
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -22007,7 +21774,7 @@ __export(companions_exports, {
|
|
|
22007
21774
|
uninstallCodeindex: () => uninstallCodeindex,
|
|
22008
21775
|
unregisterCodeindexMcp: () => unregisterCodeindexMcp
|
|
22009
21776
|
});
|
|
22010
|
-
import { existsSync as
|
|
21777
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync39, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "node:fs";
|
|
22011
21778
|
import { homedir as homedir17 } from "node:os";
|
|
22012
21779
|
import { join as join47 } from "node:path";
|
|
22013
21780
|
import { execSync as execSync3 } from "node:child_process";
|
|
@@ -22130,7 +21897,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22130
21897
|
renameSync5(tmpPath, CODEINDEX_BIN_PATH);
|
|
22131
21898
|
} catch (err) {
|
|
22132
21899
|
try {
|
|
22133
|
-
if (
|
|
21900
|
+
if (existsSync45(tmpPath))
|
|
22134
21901
|
unlinkSync3(tmpPath);
|
|
22135
21902
|
} catch {
|
|
22136
21903
|
}
|
|
@@ -22139,7 +21906,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22139
21906
|
return checkCodeindexStatus();
|
|
22140
21907
|
}
|
|
22141
21908
|
function uninstallCodeindex() {
|
|
22142
|
-
if (
|
|
21909
|
+
if (existsSync45(CODEINDEX_BIN_PATH)) {
|
|
22143
21910
|
unlinkSync3(CODEINDEX_BIN_PATH);
|
|
22144
21911
|
return { removed: true, path: CODEINDEX_BIN_PATH };
|
|
22145
21912
|
}
|
|
@@ -22147,7 +21914,7 @@ function uninstallCodeindex() {
|
|
|
22147
21914
|
}
|
|
22148
21915
|
function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
22149
21916
|
let cfg = {};
|
|
22150
|
-
if (
|
|
21917
|
+
if (existsSync45(mcpJsonPath)) {
|
|
22151
21918
|
try {
|
|
22152
21919
|
cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
|
|
22153
21920
|
} catch {
|
|
@@ -22162,7 +21929,7 @@ function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
|
22162
21929
|
return { added: true };
|
|
22163
21930
|
}
|
|
22164
21931
|
function unregisterCodeindexMcp(mcpJsonPath) {
|
|
22165
|
-
if (!
|
|
21932
|
+
if (!existsSync45(mcpJsonPath))
|
|
22166
21933
|
return { removed: false };
|
|
22167
21934
|
let cfg;
|
|
22168
21935
|
try {
|
|
@@ -22208,7 +21975,7 @@ __export(install_skills_exports, {
|
|
|
22208
21975
|
skillsSourceDir: () => skillsSourceDir,
|
|
22209
21976
|
uninstallSkills: () => uninstallSkills
|
|
22210
21977
|
});
|
|
22211
|
-
import { readdirSync as readdirSync33, existsSync as
|
|
21978
|
+
import { readdirSync as readdirSync33, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as statSync40, realpathSync as realpathSync4 } from "fs";
|
|
22212
21979
|
import { createHash as createHash11 } from "crypto";
|
|
22213
21980
|
import { join as join48, basename as basename21 } from "path";
|
|
22214
21981
|
function skillsSourceDir() {
|
|
@@ -22247,8 +22014,8 @@ function skillStamp() {
|
|
|
22247
22014
|
}
|
|
22248
22015
|
function bundledSkillNames() {
|
|
22249
22016
|
const src = skillsSourceDir();
|
|
22250
|
-
if (!
|
|
22251
|
-
return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() &&
|
|
22017
|
+
if (!existsSync46(src)) return [];
|
|
22018
|
+
return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join48(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
|
|
22252
22019
|
}
|
|
22253
22020
|
function realPathOrSelf(p) {
|
|
22254
22021
|
try {
|
|
@@ -22272,7 +22039,7 @@ function claudeSkillTargets() {
|
|
|
22272
22039
|
dir,
|
|
22273
22040
|
// Judge each profile on ITS OWN transcripts, not the primary's: a home
|
|
22274
22041
|
// with no projects/ is a directory someone made, not a Claude install.
|
|
22275
|
-
available:
|
|
22042
|
+
available: existsSync46(join48(home, "projects"))
|
|
22276
22043
|
});
|
|
22277
22044
|
}
|
|
22278
22045
|
if (targets.length === 0) {
|
|
@@ -22296,7 +22063,7 @@ function skillTargets() {
|
|
|
22296
22063
|
];
|
|
22297
22064
|
}
|
|
22298
22065
|
function isManaged(dir) {
|
|
22299
|
-
return
|
|
22066
|
+
return existsSync46(join48(dir, MARKER));
|
|
22300
22067
|
}
|
|
22301
22068
|
function installSkills(opts = {}) {
|
|
22302
22069
|
const onlyAvailable = opts.onlyAvailable ?? true;
|
|
@@ -22314,7 +22081,7 @@ function installSkills(opts = {}) {
|
|
|
22314
22081
|
}
|
|
22315
22082
|
for (const name of names) {
|
|
22316
22083
|
const dest = join48(t.dir, name);
|
|
22317
|
-
if (
|
|
22084
|
+
if (existsSync46(dest) && !isManaged(dest)) {
|
|
22318
22085
|
skippedUserOwned.push(name);
|
|
22319
22086
|
continue;
|
|
22320
22087
|
}
|
|
@@ -22334,7 +22101,7 @@ function uninstallSkills() {
|
|
|
22334
22101
|
const perTarget = [];
|
|
22335
22102
|
for (const t of skillTargets()) {
|
|
22336
22103
|
const removed = [];
|
|
22337
|
-
if (!
|
|
22104
|
+
if (!existsSync46(t.dir)) {
|
|
22338
22105
|
perTarget.push({ id: t.id, label: t.label, removed });
|
|
22339
22106
|
continue;
|
|
22340
22107
|
}
|
|
@@ -22358,7 +22125,7 @@ function skillsNeedRefresh() {
|
|
|
22358
22125
|
if (!t.available) continue;
|
|
22359
22126
|
for (const name of names) {
|
|
22360
22127
|
const marker = join48(t.dir, name, MARKER);
|
|
22361
|
-
if (!
|
|
22128
|
+
if (!existsSync46(marker)) return true;
|
|
22362
22129
|
try {
|
|
22363
22130
|
if (readFileSync39(marker, "utf-8").trim() !== cur) return true;
|
|
22364
22131
|
} catch {
|
|
@@ -22376,7 +22143,7 @@ function skillStatus() {
|
|
|
22376
22143
|
let ver = null;
|
|
22377
22144
|
for (const name of names) {
|
|
22378
22145
|
const marker = join48(t.dir, name, MARKER);
|
|
22379
|
-
if (
|
|
22146
|
+
if (existsSync46(marker)) {
|
|
22380
22147
|
installed++;
|
|
22381
22148
|
try {
|
|
22382
22149
|
ver = readFileSync39(marker, "utf-8").trim();
|
|
@@ -22564,7 +22331,7 @@ __export(toolkit_sync_exports, {
|
|
|
22564
22331
|
supportedTargetsFor: () => supportedTargetsFor,
|
|
22565
22332
|
writeMcpEntry: () => writeMcpEntry
|
|
22566
22333
|
});
|
|
22567
|
-
import { existsSync as
|
|
22334
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync41, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
|
|
22568
22335
|
import { dirname as dirname22, join as join50, basename as basename23 } from "path";
|
|
22569
22336
|
import { homedir as homedir19 } from "os";
|
|
22570
22337
|
function supportedTargetsFor(type) {
|
|
@@ -22619,11 +22386,11 @@ function skillsDirFor(tool) {
|
|
|
22619
22386
|
}
|
|
22620
22387
|
function copySkill(row, toTool) {
|
|
22621
22388
|
const skillDir = row.extra.skillDir || dirname22(row.filePath);
|
|
22622
|
-
if (!
|
|
22389
|
+
if (!existsSync47(skillDir))
|
|
22623
22390
|
return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
|
|
22624
22391
|
const name = row.extra.skillName || basename23(skillDir);
|
|
22625
22392
|
const targetDir = join50(skillsDirFor(toTool), name);
|
|
22626
|
-
if (
|
|
22393
|
+
if (existsSync47(targetDir))
|
|
22627
22394
|
return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
|
|
22628
22395
|
try {
|
|
22629
22396
|
mkdirSync15(dirname22(targetDir), { recursive: true });
|
|
@@ -22655,7 +22422,7 @@ function readMcpEntry(tool, name) {
|
|
|
22655
22422
|
return entry;
|
|
22656
22423
|
}
|
|
22657
22424
|
for (const t of tries) {
|
|
22658
|
-
if (!
|
|
22425
|
+
if (!existsSync47(t.path))
|
|
22659
22426
|
continue;
|
|
22660
22427
|
try {
|
|
22661
22428
|
const cfg = JSON.parse(readFileSync41(t.path, "utf-8"));
|
|
@@ -22668,7 +22435,7 @@ function readMcpEntry(tool, name) {
|
|
|
22668
22435
|
return null;
|
|
22669
22436
|
}
|
|
22670
22437
|
function readCodexMcpEntry(path, target) {
|
|
22671
|
-
if (!
|
|
22438
|
+
if (!existsSync47(path))
|
|
22672
22439
|
return null;
|
|
22673
22440
|
let content;
|
|
22674
22441
|
try {
|
|
@@ -22722,7 +22489,7 @@ function readCodexMcpEntry(path, target) {
|
|
|
22722
22489
|
}
|
|
22723
22490
|
function writeCodexMcpEntry(path, name, entry) {
|
|
22724
22491
|
let content = "";
|
|
22725
|
-
if (
|
|
22492
|
+
if (existsSync47(path))
|
|
22726
22493
|
content = readFileSync41(path, "utf-8");
|
|
22727
22494
|
const header = `[mcp_servers.${name}]`;
|
|
22728
22495
|
if (content.includes(header))
|
|
@@ -22780,7 +22547,7 @@ function writeMcpEntry(toTool, name, entry) {
|
|
|
22780
22547
|
key2 = "mcp";
|
|
22781
22548
|
}
|
|
22782
22549
|
let cfg = {};
|
|
22783
|
-
if (
|
|
22550
|
+
if (existsSync47(path)) {
|
|
22784
22551
|
try {
|
|
22785
22552
|
cfg = JSON.parse(readFileSync41(path, "utf-8"));
|
|
22786
22553
|
} catch {
|
|
@@ -22844,7 +22611,7 @@ function copyCommandOrAgent(type, row, toTool) {
|
|
|
22844
22611
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22845
22612
|
}
|
|
22846
22613
|
const out = emit(type, art, toTool);
|
|
22847
|
-
if (
|
|
22614
|
+
if (existsSync47(out.path))
|
|
22848
22615
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22849
22616
|
try {
|
|
22850
22617
|
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
@@ -22864,7 +22631,7 @@ function copyInstructions(row, toTool) {
|
|
|
22864
22631
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22865
22632
|
}
|
|
22866
22633
|
const out = emit("instructions", art, toTool, row.projectPath || void 0);
|
|
22867
|
-
if (
|
|
22634
|
+
if (existsSync47(out.path))
|
|
22868
22635
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22869
22636
|
try {
|
|
22870
22637
|
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
@@ -23014,7 +22781,7 @@ __export(toolkit_pull_exports, {
|
|
|
23014
22781
|
planPull: () => planPull,
|
|
23015
22782
|
portableCommand: () => portableCommand
|
|
23016
22783
|
});
|
|
23017
|
-
import { existsSync as
|
|
22784
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync41, writeFileSync as writeFileSync16 } from "node:fs";
|
|
23018
22785
|
import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
|
|
23019
22786
|
function isExecutableFile(p) {
|
|
23020
22787
|
try {
|
|
@@ -23167,7 +22934,7 @@ function executePull(rows, opts = {}) {
|
|
|
23167
22934
|
for (const tool of tools) {
|
|
23168
22935
|
const dir = join51(skillsDirFor(tool), name);
|
|
23169
22936
|
const file = join51(dir, "SKILL.md");
|
|
23170
|
-
if (
|
|
22937
|
+
if (existsSync48(file)) {
|
|
23171
22938
|
outcomes.push({ type: "skill", name, tool, status: "present" });
|
|
23172
22939
|
continue;
|
|
23173
22940
|
}
|
|
@@ -23212,7 +22979,7 @@ function executePull(rows, opts = {}) {
|
|
|
23212
22979
|
outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
|
|
23213
22980
|
continue;
|
|
23214
22981
|
}
|
|
23215
|
-
if (
|
|
22982
|
+
if (existsSync48(out.path)) {
|
|
23216
22983
|
outcomes.push({ type, name, tool, status: "present" });
|
|
23217
22984
|
continue;
|
|
23218
22985
|
}
|
|
@@ -23370,7 +23137,7 @@ __export(board_hooks_exports, {
|
|
|
23370
23137
|
removeBoardHooks: () => removeBoardHooks,
|
|
23371
23138
|
resolveCliEntry: () => resolveCliEntry
|
|
23372
23139
|
});
|
|
23373
|
-
import { existsSync as
|
|
23140
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync42, writeFileSync as writeFileSync17 } from "node:fs";
|
|
23374
23141
|
import { dirname as dirname24, join as join52 } from "node:path";
|
|
23375
23142
|
function boardHookCommand(execPath, cliEntry, mode) {
|
|
23376
23143
|
return `"${execPath}" "${cliEntry}" task-hook --${mode}`;
|
|
@@ -23410,7 +23177,7 @@ function removeBoardHooks(config2) {
|
|
|
23410
23177
|
return removed;
|
|
23411
23178
|
}
|
|
23412
23179
|
function readConfig(file) {
|
|
23413
|
-
if (!
|
|
23180
|
+
if (!existsSync49(file)) return { hooks: {} };
|
|
23414
23181
|
try {
|
|
23415
23182
|
const parsed = JSON.parse(readFileSync42(file, "utf-8"));
|
|
23416
23183
|
if (!parsed.hooks || typeof parsed.hooks !== "object") parsed.hooks = {};
|
|
@@ -23422,7 +23189,7 @@ function readConfig(file) {
|
|
|
23422
23189
|
function registerBoardHooks(files, execPath, cliEntry) {
|
|
23423
23190
|
const written = [];
|
|
23424
23191
|
for (const file of files) {
|
|
23425
|
-
if (
|
|
23192
|
+
if (existsSync49(file)) {
|
|
23426
23193
|
try {
|
|
23427
23194
|
JSON.parse(readFileSync42(file, "utf-8"));
|
|
23428
23195
|
} catch {
|
|
@@ -23442,7 +23209,7 @@ function registerBoardHooks(files, execPath, cliEntry) {
|
|
|
23442
23209
|
}
|
|
23443
23210
|
function resolveCliEntry(distDir) {
|
|
23444
23211
|
const bundled = join52(distDir, "cli.js");
|
|
23445
|
-
return
|
|
23212
|
+
return existsSync49(bundled) ? bundled : join52(distDir, "cli.ts");
|
|
23446
23213
|
}
|
|
23447
23214
|
var BOARD_HOOKS;
|
|
23448
23215
|
var init_board_hooks = __esm({
|
|
@@ -23545,6 +23312,9 @@ async function repairSession(id, opts = {}) {
|
|
|
23545
23312
|
note: "archive count is a lower bound; the rebuild also unions the on-disk tail"
|
|
23546
23313
|
};
|
|
23547
23314
|
}
|
|
23315
|
+
if (!opts.force && endpoints.every((ep) => (currentOnServer.get(ep.url) ?? 0) >= fullest.messages)) {
|
|
23316
|
+
return { sessionId: id, status: "already-full", fullestMessages: fullest.messages, fullestSource: fullest.source, pushed: [] };
|
|
23317
|
+
}
|
|
23548
23318
|
seedShadow(rawId, fullest.container);
|
|
23549
23319
|
const located = backend.findSession(rawId);
|
|
23550
23320
|
const projectPath = located?.projectPath || cwdFromContainer(fullest.container);
|
|
@@ -23587,7 +23357,7 @@ async function repairSession(id, opts = {}) {
|
|
|
23587
23357
|
try {
|
|
23588
23358
|
const res = await fetchWithTimeout(`${ep.url}/api/sync`, { method: "POST", headers, body: JSON.stringify({ conversations: [built.conv] }) }, 6e4);
|
|
23589
23359
|
if (!res.ok) {
|
|
23590
|
-
pushed.push({ server: ep.url, before, after: before });
|
|
23360
|
+
pushed.push({ server: ep.url, before, after: before, error: `HTTP ${res.status}` });
|
|
23591
23361
|
log6(`push to ${ep.url} FAILED: HTTP ${res.status}`);
|
|
23592
23362
|
continue;
|
|
23593
23363
|
}
|
|
@@ -23596,10 +23366,21 @@ async function repairSession(id, opts = {}) {
|
|
|
23596
23366
|
pushed.push({ server: ep.url, before, after: afterN });
|
|
23597
23367
|
log6(`pushed to ${ep.url}: ${before} \u2192 ${afterN} msg(s)`);
|
|
23598
23368
|
} catch (e) {
|
|
23599
|
-
|
|
23600
|
-
|
|
23369
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
23370
|
+
pushed.push({ server: ep.url, before, after: before, error });
|
|
23371
|
+
log6(`push to ${ep.url} errored: ${error}`);
|
|
23601
23372
|
}
|
|
23602
23373
|
}
|
|
23374
|
+
if (pushed.every((p) => p.error)) {
|
|
23375
|
+
return {
|
|
23376
|
+
sessionId: id,
|
|
23377
|
+
status: "error",
|
|
23378
|
+
fullestMessages: builtCount,
|
|
23379
|
+
fullestSource: fullest.source,
|
|
23380
|
+
pushed,
|
|
23381
|
+
note: `every push failed: ${pushed.map((p) => `${p.server}: ${p.error}`).join("; ")}`
|
|
23382
|
+
};
|
|
23383
|
+
}
|
|
23603
23384
|
return { sessionId: id, status: "repaired", fullestMessages: builtCount, fullestSource: fullest.source, pushed };
|
|
23604
23385
|
}
|
|
23605
23386
|
async function repairSessions(ids, opts = {}) {
|
|
@@ -24094,11 +23875,11 @@ var verify_repair_exports = {};
|
|
|
24094
23875
|
__export(verify_repair_exports, {
|
|
24095
23876
|
forceFullResync: () => forceFullResync
|
|
24096
23877
|
});
|
|
24097
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
23878
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync50 } from "fs";
|
|
24098
23879
|
function forceFullResync(server, sessionIds) {
|
|
24099
23880
|
if (sessionIds.length === 0) return 0;
|
|
24100
23881
|
const path = ledgerFilePath();
|
|
24101
|
-
if (
|
|
23882
|
+
if (existsSync50(path)) {
|
|
24102
23883
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
24103
23884
|
try {
|
|
24104
23885
|
copyFileSync3(path, `${path}.bak-${stamp}`);
|
|
@@ -25252,7 +25033,7 @@ __export(project_tasks_exports, {
|
|
|
25252
25033
|
pushProjectTaskStatuses: () => pushProjectTaskStatuses,
|
|
25253
25034
|
renderTeamTasksMd: () => renderTeamTasksMd
|
|
25254
25035
|
});
|
|
25255
|
-
import { existsSync as
|
|
25036
|
+
import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync42 } from "node:fs";
|
|
25256
25037
|
import { join as join55, dirname as dirname25 } from "node:path";
|
|
25257
25038
|
import { homedir as homedir21 } from "node:os";
|
|
25258
25039
|
function isTeamTaskStatus(v) {
|
|
@@ -25338,7 +25119,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
25338
25119
|
}
|
|
25339
25120
|
for (const project of projects) {
|
|
25340
25121
|
const file = join55(project, fk.filename);
|
|
25341
|
-
if (!
|
|
25122
|
+
if (!existsSync51(file)) continue;
|
|
25342
25123
|
let mtime;
|
|
25343
25124
|
try {
|
|
25344
25125
|
mtime = statSync42(file).mtimeMs;
|
|
@@ -25420,7 +25201,7 @@ __export(intent_drain_exports, {
|
|
|
25420
25201
|
drainSyncIntents: () => drainSyncIntents,
|
|
25421
25202
|
instructionTargets: () => instructionTargets
|
|
25422
25203
|
});
|
|
25423
|
-
import { existsSync as
|
|
25204
|
+
import { existsSync as existsSync52, readFileSync as readFileSync45, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
|
|
25424
25205
|
import { join as join56 } from "node:path";
|
|
25425
25206
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
25426
25207
|
import { dirname as dirname26 } from "node:path";
|
|
@@ -25435,7 +25216,7 @@ function instructionTargets(rootPath, isGlobal) {
|
|
|
25435
25216
|
const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
|
|
25436
25217
|
if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
|
|
25437
25218
|
if (!rootPath) return [];
|
|
25438
|
-
const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) =>
|
|
25219
|
+
const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) => existsSync52(p));
|
|
25439
25220
|
if (present.length) return present;
|
|
25440
25221
|
return [...new Set(tools.map((t) => join56(rootPath, instructionsFilename(t.id))))];
|
|
25441
25222
|
}
|
|
@@ -25463,7 +25244,7 @@ function applyCodeRecommendation(intent) {
|
|
|
25463
25244
|
mkdirSync19(dir, { recursive: true });
|
|
25464
25245
|
} catch {
|
|
25465
25246
|
}
|
|
25466
|
-
const existing =
|
|
25247
|
+
const existing = existsSync52(file) ? readFileSync45(file, "utf8") : "";
|
|
25467
25248
|
if (existing.includes(text)) {
|
|
25468
25249
|
skipped.push(file);
|
|
25469
25250
|
continue;
|
|
@@ -25641,7 +25422,7 @@ var init_intent_drain = __esm({
|
|
|
25641
25422
|
ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25642
25423
|
} catch {
|
|
25643
25424
|
try {
|
|
25644
|
-
if (true) ownVersion2 = "0.
|
|
25425
|
+
if (true) ownVersion2 = "0.7.1";
|
|
25645
25426
|
} catch {
|
|
25646
25427
|
}
|
|
25647
25428
|
}
|
|
@@ -25842,7 +25623,7 @@ var team_merge_exports = {};
|
|
|
25842
25623
|
__export(team_merge_exports, {
|
|
25843
25624
|
mergePullResult: () => mergePullResult
|
|
25844
25625
|
});
|
|
25845
|
-
import { existsSync as
|
|
25626
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync43 } from "fs";
|
|
25846
25627
|
import { dirname as dirname27, join as join57 } from "path";
|
|
25847
25628
|
import { createHash as createHash14 } from "crypto";
|
|
25848
25629
|
function installPathFor(art, tool) {
|
|
@@ -25959,7 +25740,7 @@ async function mergePullResult(opts) {
|
|
|
25959
25740
|
if (!path)
|
|
25960
25741
|
continue;
|
|
25961
25742
|
try {
|
|
25962
|
-
if (
|
|
25743
|
+
if (existsSync53(path))
|
|
25963
25744
|
rmSync8(path, { force: true });
|
|
25964
25745
|
await teamInstallsForget(id, row.tool);
|
|
25965
25746
|
report.removed.push({ artifactId: id, path });
|
|
@@ -25977,7 +25758,7 @@ async function mergePullResult(opts) {
|
|
|
25977
25758
|
}
|
|
25978
25759
|
function readFileIfExists(path) {
|
|
25979
25760
|
try {
|
|
25980
|
-
if (!
|
|
25761
|
+
if (!existsSync53(path))
|
|
25981
25762
|
return null;
|
|
25982
25763
|
const s = statSync43(path);
|
|
25983
25764
|
if (!s.isFile())
|
|
@@ -26063,7 +25844,7 @@ var init_vault_crypto = __esm({
|
|
|
26063
25844
|
});
|
|
26064
25845
|
|
|
26065
25846
|
// ../engine/dist/core/vault-source.js
|
|
26066
|
-
import { existsSync as
|
|
25847
|
+
import { existsSync as existsSync54, readdirSync as readdirSync34, statSync as statSync44 } from "fs";
|
|
26067
25848
|
import { extname, join as join58 } from "path";
|
|
26068
25849
|
function* walkVaultSources() {
|
|
26069
25850
|
const t = loadSettings().team.vault;
|
|
@@ -26089,7 +25870,7 @@ function safeStat(path) {
|
|
|
26089
25870
|
}
|
|
26090
25871
|
function* walkClaude() {
|
|
26091
25872
|
const root = claudeBackend.projectsDir();
|
|
26092
|
-
if (!
|
|
25873
|
+
if (!existsSync54(root))
|
|
26093
25874
|
return;
|
|
26094
25875
|
let projectDirs = [];
|
|
26095
25876
|
try {
|
|
@@ -26142,7 +25923,7 @@ function* walkCursor() {
|
|
|
26142
25923
|
}
|
|
26143
25924
|
function* walkCodex() {
|
|
26144
25925
|
const sessionsRoot = join58(codexBackend.homeDir(), "sessions");
|
|
26145
|
-
if (!
|
|
25926
|
+
if (!existsSync54(sessionsRoot))
|
|
26146
25927
|
return;
|
|
26147
25928
|
const stack = [sessionsRoot];
|
|
26148
25929
|
while (stack.length) {
|
|
@@ -26171,7 +25952,7 @@ function* walkCodex() {
|
|
|
26171
25952
|
}
|
|
26172
25953
|
function* walkAgy() {
|
|
26173
25954
|
const brainDir = join58(agyBackend.homeDir(), "brain");
|
|
26174
|
-
if (!
|
|
25955
|
+
if (!existsSync54(brainDir))
|
|
26175
25956
|
return;
|
|
26176
25957
|
let sessionDirs = [];
|
|
26177
25958
|
try {
|
|
@@ -26188,10 +25969,10 @@ function* walkAgy() {
|
|
|
26188
25969
|
continue;
|
|
26189
25970
|
}
|
|
26190
25971
|
let filePath = join58(sessionPath, ".system_generated", "logs", "transcript.jsonl");
|
|
26191
|
-
if (!
|
|
25972
|
+
if (!existsSync54(filePath)) {
|
|
26192
25973
|
filePath = join58(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
|
|
26193
25974
|
}
|
|
26194
|
-
if (!
|
|
25975
|
+
if (!existsSync54(filePath))
|
|
26195
25976
|
continue;
|
|
26196
25977
|
const s = safeStat(filePath);
|
|
26197
25978
|
if (!s)
|
|
@@ -26227,7 +26008,7 @@ __export(vault_client_exports, {
|
|
|
26227
26008
|
vaultStatus: () => vaultStatus,
|
|
26228
26009
|
vaultSync: () => vaultSync
|
|
26229
26010
|
});
|
|
26230
|
-
import { mkdirSync as mkdirSync21, readFileSync as readFileSync47, writeFileSync as writeFileSync21, existsSync as
|
|
26011
|
+
import { mkdirSync as mkdirSync21, readFileSync as readFileSync47, writeFileSync as writeFileSync21, existsSync as existsSync55 } from "fs";
|
|
26231
26012
|
import { dirname as dirname28, join as join59 } from "path";
|
|
26232
26013
|
function context2() {
|
|
26233
26014
|
const s = loadSettings().team;
|
|
@@ -26440,7 +26221,7 @@ async function vaultRestore(passphrase, opts = {}) {
|
|
|
26440
26221
|
}
|
|
26441
26222
|
try {
|
|
26442
26223
|
const path = importedPathFor(blob.tool, blob.sessionId);
|
|
26443
|
-
if (
|
|
26224
|
+
if (existsSync55(path)) {
|
|
26444
26225
|
const localStat = await import("fs").then((m) => m.statSync(path));
|
|
26445
26226
|
if (Math.abs(localStat.mtimeMs - blob.mtimeSourceMs) < 1) {
|
|
26446
26227
|
report.skipped.push({ sessionId: blob.sessionId, tool: blob.tool, reason: "unchanged" });
|
|
@@ -26523,7 +26304,7 @@ __export(keyring_exports, {
|
|
|
26523
26304
|
psQuote: () => psQuote,
|
|
26524
26305
|
shQuote: () => shQuote
|
|
26525
26306
|
});
|
|
26526
|
-
import { execFileSync as
|
|
26307
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
26527
26308
|
function probeKeyring(r = realRunner) {
|
|
26528
26309
|
if (r.platform === "darwin") {
|
|
26529
26310
|
try {
|
|
@@ -26635,7 +26416,7 @@ var init_keyring = __esm({
|
|
|
26635
26416
|
ACCOUNT = "vault-passphrase";
|
|
26636
26417
|
realRunner = {
|
|
26637
26418
|
run(file, args, input) {
|
|
26638
|
-
return
|
|
26419
|
+
return execFileSync4(file, args, {
|
|
26639
26420
|
input,
|
|
26640
26421
|
encoding: "utf-8",
|
|
26641
26422
|
stdio: input === void 0 ? ["ignore", "pipe", "pipe"] : ["pipe", "pipe", "pipe"]
|
|
@@ -27070,7 +26851,7 @@ __export(service_installer_exports, {
|
|
|
27070
26851
|
uninstallService: () => uninstallService
|
|
27071
26852
|
});
|
|
27072
26853
|
import { execSync as execSync5 } from "child_process";
|
|
27073
|
-
import { existsSync as
|
|
26854
|
+
import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync48, rmSync as rmSync9, writeFileSync as writeFileSync22 } from "fs";
|
|
27074
26855
|
import { homedir as homedir22 } from "os";
|
|
27075
26856
|
import { join as join60 } from "path";
|
|
27076
26857
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
@@ -27097,7 +26878,7 @@ function isServiceRunning() {
|
|
|
27097
26878
|
return /com\.chat-recall\.watch/.test(tryCmd("launchctl list 2>/dev/null"));
|
|
27098
26879
|
}
|
|
27099
26880
|
if (process.platform === "linux") {
|
|
27100
|
-
return
|
|
26881
|
+
return /^active$/m.test(tryCmd("systemctl --user is-active chat-recall-watch.service 2>/dev/null"));
|
|
27101
26882
|
}
|
|
27102
26883
|
return false;
|
|
27103
26884
|
}
|
|
@@ -27106,7 +26887,7 @@ function refreshServiceDefinition() {
|
|
|
27106
26887
|
try {
|
|
27107
26888
|
const { watchJs, node, logFile } = resolveDaemonPaths();
|
|
27108
26889
|
const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
|
|
27109
|
-
if (!
|
|
26890
|
+
if (!existsSync56(unitPath)) return false;
|
|
27110
26891
|
const current = readFileSync48(unitPath, "utf8");
|
|
27111
26892
|
if (!current.includes(watchJs)) return false;
|
|
27112
26893
|
const wanted = renderSystemdUnit(watchJs, node, logFile);
|
|
@@ -27173,7 +26954,12 @@ function renderSystemdUnit(watchJs, node, logFile) {
|
|
|
27173
26954
|
// sawtooth. 1536MB keeps GC honest (768 OOMed on the largest subagent-fanout session) with plenty of headroom for the
|
|
27174
26955
|
// largest transcripts.
|
|
27175
26956
|
`ExecStart=${node} --max-old-space-size=1536 ${watchJs}`,
|
|
27176
|
-
|
|
26957
|
+
// ALWAYS: the collector never ends its own work, so any exit systemd did
|
|
26958
|
+
// not ask for is a fault. Under on-failure a clean exit left it dead: on
|
|
26959
|
+
// 2026-09-16 the unit went "inactive (dead) … status=0/SUCCESS" and stayed
|
|
26960
|
+
// down for 8 days, so nothing synced and the auto-update tick, which runs
|
|
26961
|
+
// inside the daemon, never ran. `systemctl stop` still stops it.
|
|
26962
|
+
"Restart=always",
|
|
27177
26963
|
"RestartSec=10",
|
|
27178
26964
|
"Nice=10",
|
|
27179
26965
|
// NEVER STOP RETRYING. systemd's default start limiter gives up after a
|
|
@@ -27269,7 +27055,7 @@ function uninstallService() {
|
|
|
27269
27055
|
} catch {
|
|
27270
27056
|
}
|
|
27271
27057
|
const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
|
|
27272
|
-
if (
|
|
27058
|
+
if (existsSync56(unitPath)) {
|
|
27273
27059
|
rmSync9(unitPath, { force: true });
|
|
27274
27060
|
removed = true;
|
|
27275
27061
|
}
|
|
@@ -27286,7 +27072,7 @@ function uninstallService() {
|
|
|
27286
27072
|
} catch {
|
|
27287
27073
|
}
|
|
27288
27074
|
let removed = false;
|
|
27289
|
-
if (
|
|
27075
|
+
if (existsSync56(plistPath)) {
|
|
27290
27076
|
rmSync9(plistPath, { force: true });
|
|
27291
27077
|
removed = true;
|
|
27292
27078
|
}
|
|
@@ -27699,7 +27485,7 @@ init_project_resolver();
|
|
|
27699
27485
|
import { config } from "dotenv";
|
|
27700
27486
|
import { Command } from "commander";
|
|
27701
27487
|
import chalk2 from "chalk";
|
|
27702
|
-
import { existsSync as
|
|
27488
|
+
import { existsSync as existsSync57, readFileSync as readFileSync49, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
|
|
27703
27489
|
import { homedir as homedir23 } from "os";
|
|
27704
27490
|
import { join as join61, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
|
|
27705
27491
|
|
|
@@ -27808,7 +27594,7 @@ function renderEnvNeeds(needs, installedCount) {
|
|
|
27808
27594
|
|
|
27809
27595
|
// src/star-nudge.ts
|
|
27810
27596
|
init_paths();
|
|
27811
|
-
import { existsSync as
|
|
27597
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
27812
27598
|
import { dirname as dirname20, join as join45 } from "node:path";
|
|
27813
27599
|
var REPO = "https://github.com/munhq/chat-recall";
|
|
27814
27600
|
function markerPath() {
|
|
@@ -27824,7 +27610,7 @@ function shouldAskForStar(resultCount) {
|
|
|
27824
27610
|
if (suppressed()) return false;
|
|
27825
27611
|
if (!process.stderr.isTTY) return false;
|
|
27826
27612
|
try {
|
|
27827
|
-
return !
|
|
27613
|
+
return !existsSync43(markerPath());
|
|
27828
27614
|
} catch {
|
|
27829
27615
|
return false;
|
|
27830
27616
|
}
|
|
@@ -27865,7 +27651,7 @@ function claudeHookConfigFiles() {
|
|
|
27865
27651
|
const out = [];
|
|
27866
27652
|
const seen = /* @__PURE__ */ new Set();
|
|
27867
27653
|
for (const home of claudeHomeDirs()) {
|
|
27868
|
-
if (!
|
|
27654
|
+
if (!existsSync57(join61(home, "projects"))) continue;
|
|
27869
27655
|
const file = join61(home, "hooks.json");
|
|
27870
27656
|
let key2;
|
|
27871
27657
|
try {
|
|
@@ -28013,6 +27799,7 @@ var DEFAULT_ALLOW = [
|
|
|
28013
27799
|
"recall_user_prompts",
|
|
28014
27800
|
"recall_decision_record",
|
|
28015
27801
|
"recall_decisions",
|
|
27802
|
+
"recall_decision_scan",
|
|
28016
27803
|
"recall_analytics_summary",
|
|
28017
27804
|
"recall_outcome_summary",
|
|
28018
27805
|
"recall_regenerate_summary",
|
|
@@ -28650,7 +28437,7 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
|
|
|
28650
28437
|
try {
|
|
28651
28438
|
const identityFile = getIdentityFilePath();
|
|
28652
28439
|
let identity = "AI coding assistant";
|
|
28653
|
-
if (
|
|
28440
|
+
if (existsSync57(identityFile)) {
|
|
28654
28441
|
identity = readFileSync49(identityFile, "utf-8").trim();
|
|
28655
28442
|
}
|
|
28656
28443
|
const lines = [
|
|
@@ -28751,7 +28538,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
28751
28538
|
}
|
|
28752
28539
|
});
|
|
28753
28540
|
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) => {
|
|
28754
|
-
const { existsSync:
|
|
28541
|
+
const { existsSync: existsSync58, readFileSync: readFileSync50, statSync: statSync45 } = await import("fs");
|
|
28755
28542
|
const { execSync: execSync6 } = await import("child_process");
|
|
28756
28543
|
const rows = [];
|
|
28757
28544
|
const note = (ok, label, detail) => rows.push({ ok, label, detail });
|
|
@@ -28815,7 +28602,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28815
28602
|
}
|
|
28816
28603
|
}
|
|
28817
28604
|
const credFile = join61(getDataDir(), "credentials.json");
|
|
28818
|
-
if (!
|
|
28605
|
+
if (!existsSync58(credFile)) {
|
|
28819
28606
|
note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
|
|
28820
28607
|
} else {
|
|
28821
28608
|
try {
|
|
@@ -28830,13 +28617,13 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28830
28617
|
for (const hooksJson of claudeHookConfigFiles()) {
|
|
28831
28618
|
const profile = basename27(dirname29(hooksJson));
|
|
28832
28619
|
const label = `Claude hooks (${profile})`;
|
|
28833
|
-
if (!
|
|
28620
|
+
if (!existsSync58(hooksJson)) {
|
|
28834
28621
|
note(false, label, `no ${hooksJson} \u2014 run \`chat-recall install-hooks\``);
|
|
28835
28622
|
continue;
|
|
28836
28623
|
}
|
|
28837
28624
|
try {
|
|
28838
28625
|
const readHooks = (file) => {
|
|
28839
|
-
if (!
|
|
28626
|
+
if (!existsSync58(file)) return {};
|
|
28840
28627
|
try {
|
|
28841
28628
|
return JSON.parse(readFileSync50(file, "utf-8")).hooks || {};
|
|
28842
28629
|
} catch {
|
|
@@ -28879,9 +28666,9 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28879
28666
|
try {
|
|
28880
28667
|
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
28881
28668
|
for (const target2 of reg.guardTargets()) {
|
|
28882
|
-
if (!
|
|
28669
|
+
if (!existsSync58(target2.homeDir)) continue;
|
|
28883
28670
|
const label = `Decision guard \u2014 ${target2.label}`;
|
|
28884
|
-
if (!
|
|
28671
|
+
if (!existsSync58(target2.configPath)) {
|
|
28885
28672
|
note(false, label, `not registered \u2014 run \`chat-recall install-hooks\``);
|
|
28886
28673
|
continue;
|
|
28887
28674
|
}
|
|
@@ -28906,7 +28693,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28906
28693
|
const want = bundledSkillNames2().length;
|
|
28907
28694
|
for (const t of skillTargets2()) {
|
|
28908
28695
|
if (!t.available) continue;
|
|
28909
|
-
const have = want === 0 ? 0 : bundledSkillNames2().filter((n) =>
|
|
28696
|
+
const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync58(join61(t.dir, n, ".chat-recall-managed"))).length;
|
|
28910
28697
|
note(
|
|
28911
28698
|
have === want,
|
|
28912
28699
|
`Skills \u2014 ${t.label}`,
|
|
@@ -28976,7 +28763,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28976
28763
|
} else if (process.platform === "darwin") {
|
|
28977
28764
|
watchRunning = /com\.chat-recall\.watch/.test(tryCmd2("launchctl list 2>/dev/null"));
|
|
28978
28765
|
} else {
|
|
28979
|
-
watchRunning =
|
|
28766
|
+
watchRunning = /^active$/m.test(tryCmd2("systemctl --user is-active chat-recall-watch.service 2>/dev/null"));
|
|
28980
28767
|
}
|
|
28981
28768
|
note(watchRunning, "Watch daemon", watchRunning ? "running" : "not running (run `chat-recall watch --install-service` to keep collection running)");
|
|
28982
28769
|
const dataDir = getDataDir();
|
|
@@ -28988,7 +28775,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28988
28775
|
join61(dataDir, "index", "knowledge_graph.db"),
|
|
28989
28776
|
join61(dataDir, "index", "metadata.db")
|
|
28990
28777
|
];
|
|
28991
|
-
const legacyPresent = legacyPaths.filter((p) =>
|
|
28778
|
+
const legacyPresent = legacyPaths.filter((p) => existsSync58(p));
|
|
28992
28779
|
if (opts.purgeLocal) {
|
|
28993
28780
|
if (!target) {
|
|
28994
28781
|
note(false, "Purge legacy index", "refused \u2014 log in and `chat-recall sync` first, so your history is on the server before removing local copies");
|
|
@@ -29632,10 +29419,10 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
|
|
|
29632
29419
|
try {
|
|
29633
29420
|
const { resolveProjectId: resolveProjectId2 } = await Promise.resolve().then(() => (init_project_resolver(), project_resolver_exports));
|
|
29634
29421
|
const { renderTeamTasksMd: renderTeamTasksMd2, parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
29635
|
-
const { writeFileSync: writeFileSync24, readFileSync: readFileSync50, existsSync:
|
|
29422
|
+
const { writeFileSync: writeFileSync24, readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
|
|
29636
29423
|
const { join: join62 } = await import("node:path");
|
|
29637
29424
|
const file = join62(cwd, "TEAM_TASKS.md");
|
|
29638
|
-
if (!opts.force &&
|
|
29425
|
+
if (!opts.force && existsSync58(file) && parseTeamTasksFile2(readFileSync50(file, "utf8")).length > 0) {
|
|
29639
29426
|
console.error(chalk2.red("TEAM_TASKS.md has unpushed edits."), "Run", chalk2.bold("chat-recall tasks push"), "first, or", chalk2.bold("tasks pull --force"), "to discard.");
|
|
29640
29427
|
process.exit(1);
|
|
29641
29428
|
}
|
|
@@ -29654,10 +29441,10 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
|
|
|
29654
29441
|
const cwd = dir || process.cwd();
|
|
29655
29442
|
try {
|
|
29656
29443
|
const { parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
29657
|
-
const { readFileSync: readFileSync50, existsSync:
|
|
29444
|
+
const { readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
|
|
29658
29445
|
const { join: join62 } = await import("node:path");
|
|
29659
29446
|
const file = join62(cwd, "TEAM_TASKS.md");
|
|
29660
|
-
if (!
|
|
29447
|
+
if (!existsSync58(file)) {
|
|
29661
29448
|
console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
|
|
29662
29449
|
process.exit(1);
|
|
29663
29450
|
}
|
|
@@ -30099,10 +29886,10 @@ program.command("task-hook").description("Move a board card when a prompt or a c
|
|
|
30099
29886
|
return;
|
|
30100
29887
|
}
|
|
30101
29888
|
if (!opts.close) return;
|
|
30102
|
-
const { execFileSync:
|
|
29889
|
+
const { execFileSync: execFileSync5 } = await import("node:child_process");
|
|
30103
29890
|
const git2 = (args) => {
|
|
30104
29891
|
try {
|
|
30105
|
-
return
|
|
29892
|
+
return execFileSync5("git", args, {
|
|
30106
29893
|
cwd,
|
|
30107
29894
|
encoding: "utf8",
|
|
30108
29895
|
timeout: 5e3,
|
|
@@ -30144,7 +29931,7 @@ program.command("task-hook").description("Move a board card when a prompt or a c
|
|
|
30144
29931
|
}
|
|
30145
29932
|
});
|
|
30146
29933
|
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) => {
|
|
30147
|
-
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync:
|
|
29934
|
+
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync45, unlinkSync: unlinkSync4 } = await import("fs");
|
|
30148
29935
|
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
30149
29936
|
const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
|
|
30150
29937
|
const here = fileURLToPath6(new URL(".", import.meta.url));
|
|
@@ -30157,7 +29944,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30157
29944
|
// freshly added script is only at the root until that build runs.
|
|
30158
29945
|
join61(here, "..", "..", "..", "hooks", name)
|
|
30159
29946
|
];
|
|
30160
|
-
return candidates2.find((p) =>
|
|
29947
|
+
return candidates2.find((p) => existsSync58(p));
|
|
30161
29948
|
};
|
|
30162
29949
|
const sourceSaveHook = findHook("chat_recall_save_hook.sh");
|
|
30163
29950
|
const sourceGuardHook = findHook("chat_recall_guard_hook.sh");
|
|
@@ -30176,7 +29963,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30176
29963
|
const installedWakeupHook = join61(hooksDir, "chat_recall_wakeup_hook.sh");
|
|
30177
29964
|
const wakeupWiredInSettings = (home) => {
|
|
30178
29965
|
const settings2 = join61(home, "settings.json");
|
|
30179
|
-
if (!
|
|
29966
|
+
if (!existsSync58(settings2)) return false;
|
|
30180
29967
|
try {
|
|
30181
29968
|
const cfg = JSON.parse(readFileSync50(settings2, "utf-8"));
|
|
30182
29969
|
const arr = Array.isArray(cfg.hooks?.SessionStart) ? cfg.hooks.SessionStart : [];
|
|
@@ -30187,7 +29974,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30187
29974
|
};
|
|
30188
29975
|
const hookConfigFiles = claudeHookConfigFiles();
|
|
30189
29976
|
const readHookConfig = (file) => {
|
|
30190
|
-
if (!
|
|
29977
|
+
if (!existsSync58(file)) return { hooks: {} };
|
|
30191
29978
|
try {
|
|
30192
29979
|
const c = JSON.parse(readFileSync50(file, "utf-8"));
|
|
30193
29980
|
if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
|
|
@@ -30204,7 +29991,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30204
29991
|
};
|
|
30205
29992
|
const HOOK_EVENTS = ["Stop", "PreCompact", "UserPromptSubmit", "SessionEnd", "SessionStart", "PreToolUse"];
|
|
30206
29993
|
const readJson4 = (file) => {
|
|
30207
|
-
if (!
|
|
29994
|
+
if (!existsSync58(file)) return {};
|
|
30208
29995
|
try {
|
|
30209
29996
|
return JSON.parse(readFileSync50(file, "utf-8"));
|
|
30210
29997
|
} catch {
|
|
@@ -30215,7 +30002,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30215
30002
|
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
30216
30003
|
const notes = [];
|
|
30217
30004
|
for (const target of reg.guardTargets()) {
|
|
30218
|
-
if (!
|
|
30005
|
+
if (!existsSync58(target.homeDir)) continue;
|
|
30219
30006
|
const wrapper = join61(hooksDir, reg.guardWrapperName(target.tool));
|
|
30220
30007
|
writeFileSync24(wrapper, reg.guardWrapperSource(target.tool, hookPath));
|
|
30221
30008
|
chmodSync5(wrapper, 493);
|
|
@@ -30236,7 +30023,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30236
30023
|
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
30237
30024
|
let removed = 0;
|
|
30238
30025
|
for (const target of reg.guardTargets()) {
|
|
30239
|
-
if (!
|
|
30026
|
+
if (!existsSync58(target.configPath)) continue;
|
|
30240
30027
|
if (target.tool === "opencode") {
|
|
30241
30028
|
const body = readFileSync50(target.configPath, "utf-8");
|
|
30242
30029
|
if (!body.includes("chat-recall install-hooks")) continue;
|
|
@@ -30264,7 +30051,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
30264
30051
|
if (filtered.length) config2.hooks[event] = filtered;
|
|
30265
30052
|
else delete config2.hooks[event];
|
|
30266
30053
|
}
|
|
30267
|
-
if (removed === 0 && !
|
|
30054
|
+
if (removed === 0 && !existsSync58(hooksJson)) continue;
|
|
30268
30055
|
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
30269
30056
|
total += removed;
|
|
30270
30057
|
console.log(chalk2.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
|
|
@@ -31137,8 +30924,8 @@ npm has ${version2}; the server you sync to still serves ${check.serverVersion}.
|
|
|
31137
30924
|
console.log(chalk2.dim(`
|
|
31138
30925
|
Installing ${version2} from npm\u2026`));
|
|
31139
30926
|
try {
|
|
31140
|
-
const { execFileSync:
|
|
31141
|
-
|
|
30927
|
+
const { execFileSync: execFileSync5 } = await import("node:child_process");
|
|
30928
|
+
execFileSync5("npm", ["install", "-g", `chat-recall@${version2}`], { stdio: "inherit" });
|
|
31142
30929
|
const { installedVersion: installedVersion2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
31143
30930
|
const landed = installedVersion2();
|
|
31144
30931
|
if (landed && compareVersions2(landed, version2) >= 0) {
|