chat-recall 0.6.9 → 0.7.0
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/cli.js +785 -1185
- 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 +1 -1
- package/dist/mcp.js +2392 -2317
- package/dist/scan-worker.js +4 -5
- package/dist/watch.js +598 -1026
- package/package.json +1 -1
- package/skills/chat-recall/SKILL.md +2 -1
- package/skills/chat-recall-memory/SKILL.md +15 -0
package/dist/watch.js
CHANGED
|
@@ -115,6 +115,25 @@ var init_tool_backend = __esm({
|
|
|
115
115
|
}
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
// ../engine/dist/parsers/banners.js
|
|
119
|
+
function stripInjectedBanners(text) {
|
|
120
|
+
let result = text;
|
|
121
|
+
for (const re of INJECTED_BANNERS)
|
|
122
|
+
result = result.replace(re, " ");
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
var INJECTED_BANNERS;
|
|
126
|
+
var init_banners = __esm({
|
|
127
|
+
"../engine/dist/parsers/banners.js"() {
|
|
128
|
+
"use strict";
|
|
129
|
+
INJECTED_BANNERS = [
|
|
130
|
+
/MCP issues detected\. ?Run \/mcp list for status\.?/g,
|
|
131
|
+
/Context low[^\n]*Run \/compact[^\n]*/g,
|
|
132
|
+
/API Error:[^\n]{0,120}/g
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
118
137
|
// ../engine/dist/core/project-path-match.js
|
|
119
138
|
function canonicalProjectPath(p) {
|
|
120
139
|
return (p || "").trim().replace(/\\/g, "/").replace(/[-_]/g, "/").replace(/\/{2,}/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -5031,715 +5050,149 @@ var init_settings = __esm({
|
|
|
5031
5050
|
}
|
|
5032
5051
|
});
|
|
5033
5052
|
|
|
5034
|
-
// ../engine/dist/core/
|
|
5035
|
-
import {
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
}
|
|
5044
|
-
|
|
5045
|
-
if (t.length < 32)
|
|
5046
|
-
return false;
|
|
5047
|
-
if (/^[a-f0-9]+$/i.test(t))
|
|
5048
|
-
return true;
|
|
5049
|
-
return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
|
|
5050
|
-
}
|
|
5051
|
-
function findContextualSecrets(text) {
|
|
5052
|
-
if (!text)
|
|
5053
|
-
return [];
|
|
5054
|
-
const ranges = [];
|
|
5055
|
-
let m;
|
|
5056
|
-
SECRET_CONTEXT_RE.lastIndex = 0;
|
|
5057
|
-
while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
|
|
5058
|
-
const winStart = m.index + m[0].length;
|
|
5059
|
-
const window = text.slice(winStart, winStart + 80);
|
|
5060
|
-
HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
|
|
5061
|
-
let tm;
|
|
5062
|
-
while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
|
|
5063
|
-
if (!looksLikeSecretToken(tm[0]))
|
|
5064
|
-
continue;
|
|
5065
|
-
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))) {
|
|
5066
|
-
continue;
|
|
5067
|
-
}
|
|
5068
|
-
ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
|
|
5069
|
-
break;
|
|
5070
|
-
}
|
|
5053
|
+
// ../engine/dist/core/home-approval.js
|
|
5054
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
|
|
5055
|
+
import { homedir as homedir2 } from "os";
|
|
5056
|
+
import { join as join3, dirname as dirname3 } from "path";
|
|
5057
|
+
function normalizeHomePath(p) {
|
|
5058
|
+
const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
|
|
5059
|
+
const trimmed = expanded.replace(/[/\\]+$/, "");
|
|
5060
|
+
try {
|
|
5061
|
+
return realpathSync(trimmed);
|
|
5062
|
+
} catch {
|
|
5063
|
+
return trimmed;
|
|
5071
5064
|
}
|
|
5072
|
-
|
|
5065
|
+
}
|
|
5066
|
+
function legacyAutoSyncedHomes() {
|
|
5067
|
+
const home = homedir2();
|
|
5073
5068
|
const out = [];
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5069
|
+
const add = (dir, marker) => {
|
|
5070
|
+
if (!existsSync3(join3(dir, marker)))
|
|
5071
|
+
return;
|
|
5072
|
+
const n = normalizeHomePath(dir);
|
|
5073
|
+
if (!out.includes(n))
|
|
5074
|
+
out.push(n);
|
|
5075
|
+
};
|
|
5076
|
+
const bases = [
|
|
5077
|
+
{ base: join3(home, ".claude"), marker: "projects" },
|
|
5078
|
+
{ base: join3(home, ".gemini"), marker: "tmp" },
|
|
5079
|
+
{ base: join3(home, ".codex"), marker: "sessions" },
|
|
5080
|
+
{ base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
|
|
5081
|
+
{ base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5082
|
+
{ base: join3(home, ".cursor"), marker: "chats" }
|
|
5083
|
+
];
|
|
5084
|
+
for (const { base: base2, marker } of bases) {
|
|
5085
|
+
add(base2, marker);
|
|
5086
|
+
const parent = dirname3(base2);
|
|
5087
|
+
const leaf = base2.slice(parent.length + 1);
|
|
5088
|
+
try {
|
|
5089
|
+
for (const e of readdirSync2(parent, { withFileTypes: true })) {
|
|
5090
|
+
if (!e.isDirectory())
|
|
5091
|
+
continue;
|
|
5092
|
+
if (e.name === leaf || !e.name.startsWith(leaf + "-"))
|
|
5093
|
+
continue;
|
|
5094
|
+
if (e.name === ".claude-code")
|
|
5095
|
+
continue;
|
|
5096
|
+
add(join3(parent, e.name), marker);
|
|
5097
|
+
}
|
|
5098
|
+
} catch {
|
|
5079
5099
|
}
|
|
5080
|
-
out.push({ ...r });
|
|
5081
5100
|
}
|
|
5082
5101
|
return out;
|
|
5083
5102
|
}
|
|
5084
|
-
function
|
|
5085
|
-
|
|
5086
|
-
if (v === void 0)
|
|
5087
|
-
return null;
|
|
5088
|
-
return v === "1" || v === "true" || v === "yes";
|
|
5089
|
-
}
|
|
5090
|
-
function settingsView() {
|
|
5091
|
-
let mtimeMs = -1;
|
|
5103
|
+
function grandfatherLegacyHomes() {
|
|
5104
|
+
let s;
|
|
5092
5105
|
try {
|
|
5093
|
-
|
|
5106
|
+
s = loadSettings();
|
|
5094
5107
|
} catch {
|
|
5108
|
+
return { seeded: [] };
|
|
5095
5109
|
}
|
|
5096
|
-
if (
|
|
5097
|
-
return
|
|
5098
|
-
|
|
5099
|
-
const
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
|
|
5106
|
-
} catch {
|
|
5107
|
-
}
|
|
5110
|
+
if (s.sources.homesGrandfathered)
|
|
5111
|
+
return { seeded: [] };
|
|
5112
|
+
const legacy = legacyAutoSyncedHomes();
|
|
5113
|
+
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5114
|
+
const seeded = [];
|
|
5115
|
+
for (const p of legacy) {
|
|
5116
|
+
if (!approved.has(p)) {
|
|
5117
|
+
approved.add(p);
|
|
5118
|
+
seeded.push(p);
|
|
5108
5119
|
}
|
|
5120
|
+
}
|
|
5121
|
+
s.sources.approvedHomes = [...approved];
|
|
5122
|
+
s.sources.homesGrandfathered = true;
|
|
5123
|
+
try {
|
|
5124
|
+
saveSettings(s);
|
|
5109
5125
|
} catch {
|
|
5126
|
+
return { seeded: [] };
|
|
5110
5127
|
}
|
|
5111
|
-
|
|
5112
|
-
return cache;
|
|
5128
|
+
return { seeded };
|
|
5113
5129
|
}
|
|
5114
|
-
function
|
|
5115
|
-
const
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
if (spec.regex.length > MAX_PATTERN_LEN)
|
|
5119
|
-
return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
|
|
5120
|
-
let re;
|
|
5130
|
+
function primaryHomes() {
|
|
5131
|
+
const home = homedir2();
|
|
5132
|
+
const env = process.env;
|
|
5133
|
+
let s;
|
|
5121
5134
|
try {
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
|
|
5135
|
+
s = loadSettings().sources;
|
|
5136
|
+
} catch {
|
|
5137
|
+
s = void 0;
|
|
5126
5138
|
}
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5139
|
+
const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
|
|
5140
|
+
const candidates2 = [
|
|
5141
|
+
env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
|
|
5142
|
+
env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
|
|
5143
|
+
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5144
|
+
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5145
|
+
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5146
|
+
dirOf(opencodeDb)
|
|
5147
|
+
];
|
|
5148
|
+
return candidates2.map(normalizeHomePath);
|
|
5134
5149
|
}
|
|
5135
|
-
function
|
|
5136
|
-
const
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
if ("reason" in out)
|
|
5145
|
-
rejected.push(out);
|
|
5146
|
-
else
|
|
5147
|
-
rules.push(out);
|
|
5150
|
+
function homeDecision(path) {
|
|
5151
|
+
const p = normalizeHomePath(path);
|
|
5152
|
+
if (primaryHomes().includes(p))
|
|
5153
|
+
return "primary";
|
|
5154
|
+
let s;
|
|
5155
|
+
try {
|
|
5156
|
+
s = loadSettings().sources;
|
|
5157
|
+
} catch {
|
|
5158
|
+
return "pending";
|
|
5148
5159
|
}
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5160
|
+
if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5161
|
+
return "declined";
|
|
5162
|
+
if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5163
|
+
return "approved";
|
|
5164
|
+
if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5165
|
+
return "approved";
|
|
5166
|
+
return "pending";
|
|
5152
5167
|
}
|
|
5153
|
-
function
|
|
5154
|
-
|
|
5168
|
+
function isHomeSynced(path) {
|
|
5169
|
+
const d = homeDecision(path);
|
|
5170
|
+
return d === "primary" || d === "approved";
|
|
5155
5171
|
}
|
|
5156
|
-
function
|
|
5157
|
-
const
|
|
5158
|
-
|
|
5172
|
+
function setDecision(path, decision) {
|
|
5173
|
+
const p = normalizeHomePath(path);
|
|
5174
|
+
let s;
|
|
5175
|
+
try {
|
|
5176
|
+
s = loadSettings();
|
|
5177
|
+
} catch {
|
|
5178
|
+
return false;
|
|
5179
|
+
}
|
|
5180
|
+
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5181
|
+
const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
|
|
5182
|
+
approved.delete(p);
|
|
5183
|
+
declined.delete(p);
|
|
5184
|
+
(decision === "approved" ? approved : declined).add(p);
|
|
5185
|
+
s.sources.approvedHomes = [...approved];
|
|
5186
|
+
s.sources.declinedHomes = [...declined];
|
|
5187
|
+
try {
|
|
5188
|
+
saveSettings(s);
|
|
5189
|
+
} catch {
|
|
5190
|
+
return false;
|
|
5191
|
+
}
|
|
5192
|
+
return true;
|
|
5159
5193
|
}
|
|
5160
|
-
function
|
|
5161
|
-
|
|
5162
|
-
if (env !== null)
|
|
5163
|
-
return env;
|
|
5164
|
-
return settingsView().enabled;
|
|
5165
|
-
}
|
|
5166
|
-
function redactSecrets(text, opts = {}) {
|
|
5167
|
-
if (!opts.force && !isRedactionEnabled())
|
|
5168
|
-
return text;
|
|
5169
|
-
if (!text)
|
|
5170
|
-
return text;
|
|
5171
|
-
const rules = opts.rules || activeRules();
|
|
5172
|
-
let out = text;
|
|
5173
|
-
for (const r of rules) {
|
|
5174
|
-
out = out.replace(r.pattern, (...args) => {
|
|
5175
|
-
const whole = args[0];
|
|
5176
|
-
const m = args.slice(0, args.length);
|
|
5177
|
-
const secret = secretOf(r, m);
|
|
5178
|
-
if (secret == null)
|
|
5179
|
-
return whole;
|
|
5180
|
-
if (opts.count)
|
|
5181
|
-
opts.count.redactions++;
|
|
5182
|
-
return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
|
|
5183
|
-
});
|
|
5184
|
-
}
|
|
5185
|
-
const ctx = findContextualSecrets(out);
|
|
5186
|
-
for (let i = ctx.length - 1; i >= 0; i--) {
|
|
5187
|
-
const { start, end } = ctx[i];
|
|
5188
|
-
if (opts.count)
|
|
5189
|
-
opts.count.redactions++;
|
|
5190
|
-
out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
|
|
5191
|
-
}
|
|
5192
|
-
return out;
|
|
5193
|
-
}
|
|
5194
|
-
function maskSecret(s) {
|
|
5195
|
-
if (!s)
|
|
5196
|
-
return "";
|
|
5197
|
-
if (s.length <= 4)
|
|
5198
|
-
return "*".repeat(s.length);
|
|
5199
|
-
return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
|
|
5200
|
-
}
|
|
5201
|
-
function globalRegexFor(pattern) {
|
|
5202
|
-
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
5203
|
-
const key2 = pattern.source + "\0" + flags;
|
|
5204
|
-
let re = globalRegexCache.get(key2);
|
|
5205
|
-
if (!re) {
|
|
5206
|
-
re = new RegExp(pattern.source, flags);
|
|
5207
|
-
globalRegexCache.set(key2, re);
|
|
5208
|
-
}
|
|
5209
|
-
return re;
|
|
5210
|
-
}
|
|
5211
|
-
function makeLineResolver(text) {
|
|
5212
|
-
const starts = [];
|
|
5213
|
-
for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1))
|
|
5214
|
-
starts.push(i);
|
|
5215
|
-
return (index) => {
|
|
5216
|
-
let lo = 0, hi = starts.length;
|
|
5217
|
-
while (lo < hi) {
|
|
5218
|
-
const mid = lo + hi >> 1;
|
|
5219
|
-
if (starts[mid] < index)
|
|
5220
|
-
lo = mid + 1;
|
|
5221
|
-
else
|
|
5222
|
-
hi = mid;
|
|
5223
|
-
}
|
|
5224
|
-
return lo + 1;
|
|
5225
|
-
};
|
|
5226
|
-
}
|
|
5227
|
-
function scanTextForFindings(text, opts = {}) {
|
|
5228
|
-
if (!text)
|
|
5229
|
-
return [];
|
|
5230
|
-
const rules = opts.rules || activeRules();
|
|
5231
|
-
const findings = [];
|
|
5232
|
-
const seenSpans = /* @__PURE__ */ new Set();
|
|
5233
|
-
const lineAt = makeLineResolver(text);
|
|
5234
|
-
for (const r of rules) {
|
|
5235
|
-
const re = globalRegexFor(r.pattern);
|
|
5236
|
-
re.lastIndex = 0;
|
|
5237
|
-
let m;
|
|
5238
|
-
while ((m = re.exec(text)) !== null) {
|
|
5239
|
-
const secret = secretOf(r, m);
|
|
5240
|
-
if (secret == null) {
|
|
5241
|
-
if (re.lastIndex === m.index)
|
|
5242
|
-
re.lastIndex++;
|
|
5243
|
-
continue;
|
|
5244
|
-
}
|
|
5245
|
-
const at = m.index + (m[0].length - secret.length);
|
|
5246
|
-
const span = `${at}:${secret.length}`;
|
|
5247
|
-
if (!seenSpans.has(span)) {
|
|
5248
|
-
seenSpans.add(span);
|
|
5249
|
-
findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
|
|
5250
|
-
}
|
|
5251
|
-
if (re.lastIndex === m.index)
|
|
5252
|
-
re.lastIndex++;
|
|
5253
|
-
}
|
|
5254
|
-
}
|
|
5255
|
-
for (const { start, end } of findContextualSecrets(text)) {
|
|
5256
|
-
const line = lineAt(start);
|
|
5257
|
-
findings.push({ rule: "secret-context", line, preview: maskSecret(text.slice(start, end)) });
|
|
5258
|
-
}
|
|
5259
|
-
return findings;
|
|
5260
|
-
}
|
|
5261
|
-
function privacyView() {
|
|
5262
|
-
let mtimeMs = -1;
|
|
5263
|
-
try {
|
|
5264
|
-
mtimeMs = statSync3(settingsFilePath()).mtimeMs;
|
|
5265
|
-
} catch {
|
|
5266
|
-
}
|
|
5267
|
-
if (privacyCache && privacyCache.mtimeMs === mtimeMs)
|
|
5268
|
-
return privacyCache;
|
|
5269
|
-
let redactToolOutputs = false, redactFilePaths = false;
|
|
5270
|
-
try {
|
|
5271
|
-
const s = loadSettings();
|
|
5272
|
-
redactToolOutputs = !!s.privacy?.redactToolOutputs;
|
|
5273
|
-
redactFilePaths = !!s.privacy?.redactFilePaths;
|
|
5274
|
-
} catch {
|
|
5275
|
-
}
|
|
5276
|
-
privacyCache = { mtimeMs, redactToolOutputs, redactFilePaths };
|
|
5277
|
-
return privacyCache;
|
|
5278
|
-
}
|
|
5279
|
-
function isToolOutputRedactionEnabled() {
|
|
5280
|
-
return privacyView().redactToolOutputs;
|
|
5281
|
-
}
|
|
5282
|
-
function redactToolResultBody(toolName, body) {
|
|
5283
|
-
if (!body)
|
|
5284
|
-
return body;
|
|
5285
|
-
return `[redacted: ${toolName} output, ${body.length} chars]`;
|
|
5286
|
-
}
|
|
5287
|
-
var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache, privacyCache;
|
|
5288
|
-
var init_secret_redactor = __esm({
|
|
5289
|
-
"../engine/dist/core/secret-redactor.js"() {
|
|
5290
|
-
"use strict";
|
|
5291
|
-
init_settings();
|
|
5292
|
-
DEFAULT_REDACTION_RULES = [
|
|
5293
|
-
// AWS access tokens. Prefix-anchored: AKIA, ASIA, ABIA (long-term,
|
|
5294
|
-
// STS, EC2). Always exactly 16 hex/alphanumeric after the prefix.
|
|
5295
|
-
// NOTE: this matches only the access-key-ID, NOT the secret key value —
|
|
5296
|
-
// the `env-secret` rule below covers AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN.
|
|
5297
|
-
{ label: "aws-access-token", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
|
|
5298
|
-
// AWS STS session tokens. These are long base64 blobs with NO `NAME=` context
|
|
5299
|
-
// when pasted bare into prose/code, so `env-secret` misses them — but they
|
|
5300
|
-
// reliably start with one of a handful of base64 prefixes (the encoded
|
|
5301
|
-
// `{"...` JSON header). Prefix-anchored → low false-positive. This is the
|
|
5302
|
-
// bare-value form that leaked (`search('IQoJEXAMPLEfakeSESSIONtokenNOTreal01234567890abcDEF==...')`).
|
|
5303
|
-
{ label: "aws-session-token", pattern: /\b(?:IQoJ|FwoG|FwoH|FQoG|FQoH|FwoD)[A-Za-z0-9/+]{40,}={0,2}/g },
|
|
5304
|
-
// Env/shell-assignment secrets — a VAR whose name ends in KEY/SECRET/TOKEN/
|
|
5305
|
-
// PASSWORD/CREDENTIAL, then `=`/`:`, then the value. Zero-width lookbehind so
|
|
5306
|
-
// only the VALUE is redacted (the var name stays for context). This is the
|
|
5307
|
-
// dominant real-world leak vector (`export` dumps, .env files, `curl -H`,
|
|
5308
|
-
// tool output) and is exactly what leaked AWS_SECRET_ACCESS_KEY and
|
|
5309
|
-
// AWS_SESSION_TOKEN in cleartext while the access-key-ID rule above caught
|
|
5310
|
-
// only the (least-sensitive) ID.
|
|
5311
|
-
// Suffix list deliberately broad: DB_PASS (our own helm chart's name!),
|
|
5312
|
-
// NPM_AUTH, SENTRY_DSN, SESSION/COOKIE secrets and BEARER vars all leaked
|
|
5313
|
-
// past the original KEY/SECRET/TOKEN/PASSWORD-only list. Values under 12
|
|
5314
|
-
// chars don't redact, so flag-ish vars (BYPASS=true) stay readable.
|
|
5315
|
-
// Context CONSUMED, secret in group 1 — see RedactionRule.secret. As a
|
|
5316
|
-
// lookbehind this was the second most expensive rule in the collector.
|
|
5317
|
-
{ 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 },
|
|
5318
|
-
// Credentials embedded in a connection URL: scheme://user:PASSWORD@host.
|
|
5319
|
-
// Redacts only the password segment (keeps scheme/user/host for context).
|
|
5320
|
-
//
|
|
5321
|
-
// Scheme-agnostic on purpose. The old rule allowlisted eight DB schemes, so
|
|
5322
|
-
// `https://user:pass@host`, `ssh://`, `ftp://`, `smtp://` and every other
|
|
5323
|
-
// userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
|
|
5324
|
-
// prefix broke the \b anchor for the one family it did cover. Any scheme
|
|
5325
|
-
// followed by userinfo is a credential, so match the shape, not a list.
|
|
5326
|
-
{ label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
|
|
5327
|
-
// Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
|
|
5328
|
-
// containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
|
|
5329
|
-
// requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
|
|
5330
|
-
// excludes longer blobs/tokens. It catches the value when it appears bare in
|
|
5331
|
-
// prose with NO `NAME=` and NO nearby context word (e.g. inside a markdown
|
|
5332
|
-
// table or quoted in analysis) — the residual leak path the env-secret and
|
|
5333
|
-
// contextual rules both miss. Placed AFTER env-secret/url-password so the
|
|
5334
|
-
// keyed/URL forms keep their more-specific labels.
|
|
5335
|
-
// The three stacked lookaheads each re-scanned the same 40 characters, which
|
|
5336
|
-
// made this the MOST expensive rule of all 106 the collector runs (13.1% of a
|
|
5337
|
-
// 29.5 MB scan). The mixed-case-plus-digit test is now one JS pass.
|
|
5338
|
-
{
|
|
5339
|
-
label: "aws-secret-key",
|
|
5340
|
-
secret: 1,
|
|
5341
|
-
pattern: /(?<![A-Za-z0-9/+])([A-Za-z0-9/+]{40})(?![A-Za-z0-9/+])/g,
|
|
5342
|
-
where: (v) => /[a-z]/.test(v) && /[A-Z]/.test(v) && /[0-9]/.test(v)
|
|
5343
|
-
},
|
|
5344
|
-
// GitHub personal access tokens.
|
|
5345
|
-
{ label: "github-pat", pattern: /\bghp_[A-Za-z0-9]{36}\b/g },
|
|
5346
|
-
// GitHub fine-grained PATs.
|
|
5347
|
-
{ label: "github-fine", pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g },
|
|
5348
|
-
// GitHub OAuth.
|
|
5349
|
-
{ label: "github-oauth", pattern: /\bgho_[A-Za-z0-9]{36}\b/g },
|
|
5350
|
-
// GitHub app + refresh.
|
|
5351
|
-
{ label: "github-app", pattern: /\b(?:ghs|ghu|ghr)_[A-Za-z0-9]{36}\b/g },
|
|
5352
|
-
// Slack bot/user/app tokens.
|
|
5353
|
-
{ label: "slack-token", pattern: /\bxox[abopr]-[A-Za-z0-9-]{10,}\b/g },
|
|
5354
|
-
// Slack incoming webhook URL.
|
|
5355
|
-
{ label: "slack-webhook", pattern: /\bhttps:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+\b/g },
|
|
5356
|
-
// Stripe live + test keys.
|
|
5357
|
-
{ label: "stripe-key", pattern: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g },
|
|
5358
|
-
// Anthropic.
|
|
5359
|
-
{ label: "anthropic-key", pattern: /\bsk-ant-[a-zA-Z0-9_-]{40,}\b/g },
|
|
5360
|
-
// OpenAI. Two shapes, deliberately kept apart. The classic key is
|
|
5361
|
-
// `sk-` + base62 with NO hyphens; the modern scoped keys carry an explicit
|
|
5362
|
-
// proj/svcacct/admin segment first. Allowing hyphens after a bare `sk-`
|
|
5363
|
-
// (which is what admitted sk-proj- in the first place) also matches ordinary
|
|
5364
|
-
// kebab-case text — `sk-fix-the-login-redirect-bug`, `.sk-button-primary`,
|
|
5365
|
-
// `sk-2024-01-01-backup` — and this corpus is full of branch names and CSS
|
|
5366
|
-
// classes. Requiring the segment keeps sk-proj- coverage without that.
|
|
5367
|
-
{ label: "openai-key", pattern: /\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b|\bsk-[A-Za-z0-9]{20,}\b/g },
|
|
5368
|
-
// Google API key (AIza…).
|
|
5369
|
-
{ label: "google-api-key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
5370
|
-
// Generic JWT — three base64url segments.
|
|
5371
|
-
{ label: "jwt", pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
|
|
5372
|
-
// Private key blocks (PEM). The label is a free word run rather than a fixed
|
|
5373
|
-
// list, so ENCRYPTED PRIVATE KEY and PGP PRIVATE KEY BLOCK are covered
|
|
5374
|
-
// without another enumeration, and \1 forces END to name the same kind as
|
|
5375
|
-
// BEGIN.
|
|
5376
|
-
//
|
|
5377
|
-
// The body is [^-], NOT [\s\S]*?. That is a performance boundary, not a
|
|
5378
|
-
// style choice. An unanchored prefix with an unbounded lazy gap re-scans to
|
|
5379
|
-
// end-of-input from every candidate start, so text carrying many BEGIN
|
|
5380
|
-
// markers and no matching END costs O(n^2): 500KB of that measured 810ms
|
|
5381
|
-
// here, and this runs synchronously on the server's event loop over
|
|
5382
|
-
// tenant-supplied text (services/secret-rescan.ts). A PEM body is base64
|
|
5383
|
-
// plus headers and contains no '-', so excluding it makes the scan stop at
|
|
5384
|
-
// the next delimiter instead of at EOF — same matches, linear cost.
|
|
5385
|
-
{ label: "private-key", pattern: /-----BEGIN ([A-Z0-9 ]{0,32})PRIVATE KEY(?: BLOCK)?-----[^-]{0,10000}?-----END \1PRIVATE KEY(?: BLOCK)?-----/g },
|
|
5386
|
-
// PuTTY keys are not PEM. Match the Private-Lines payload — the actual key
|
|
5387
|
-
// material — rather than spanning the whole file, which would need the same
|
|
5388
|
-
// unbounded gap the rule above just removed. Line-anchored and base64-only,
|
|
5389
|
-
// so it cannot run away.
|
|
5390
|
-
{ label: "putty-key", pattern: /^Private-Lines:[ \t]*\d{1,4}[\r\n]+(?:[A-Za-z0-9+/=]{8,120}[\r\n]+){1,400}/gm },
|
|
5391
|
-
// npm tokens (uuid-shaped — overlap with our UUID filter; intentional).
|
|
5392
|
-
{ label: "npm-token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
5393
|
-
// .npmrc auth lines: //registry.npmjs.org/:_authToken=VALUE (the key is not
|
|
5394
|
-
// ALL-CAPS, so the env-secret rule above never matched it). The leading `:`
|
|
5395
|
-
// is required — it is what makes this npmrc syntax rather than any shell var
|
|
5396
|
-
// ending in _AUTH, which env-secret already owns and labels better.
|
|
5397
|
-
// [^\s"'`]+ so the closing quote is not swallowed, and a ${VAR} placeholder
|
|
5398
|
-
// is left alone — redacting it destroys a config example and hides nothing.
|
|
5399
|
-
// The rule the perf profile caught the wedged daemon inside: 98.94% of samples.
|
|
5400
|
-
{ label: "npmrc-auth", secret: 1, pattern: /:_(?:authToken|password|auth)[ \t]*=[ \t]*(?!\$\{)([^\s"'`]{12,})/gi },
|
|
5401
|
-
// HTTP Authorization headers. env-secret requires an ALL-CAPS name, and
|
|
5402
|
-
// `Authorization` is mixed case, so both Bearer and Basic walked straight
|
|
5403
|
-
// through — including short opaque tokens the contextual pass also misses
|
|
5404
|
-
// (it needs 32+ chars AND mixed-case-plus-digit).
|
|
5405
|
-
// Same two guards as npmrc-auth: stop at a quote, and skip the obvious
|
|
5406
|
-
// placeholders that fill documentation (<YOUR_TOKEN>, ${TOKEN}, xxx…).
|
|
5407
|
-
{ label: "auth-header", secret: 1, pattern: /\bauthorization[ \t]*:[ \t]*(?:bearer|basic|token)[ \t]+(?![<$])([^\s"'`<]{8,})/gi },
|
|
5408
|
-
// Vendor tokens with unambiguous prefixes. Each is a fixed literal, so a
|
|
5409
|
-
// false positive is essentially impossible and the cost of listing is low.
|
|
5410
|
-
{ label: "gitlab-pat", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
5411
|
-
{ label: "slack-app-token", pattern: /\bxapp-\d-[A-Z0-9]{8,}-\d{10,}-[a-f0-9]{32,}\b/g },
|
|
5412
|
-
{ label: "sendgrid-key", pattern: /\bSG\.[A-Za-z0-9_-]{20,24}\.[A-Za-z0-9_-]{40,50}\b/g },
|
|
5413
|
-
// (?<![-\w]) so `cache-key-<md5>` is not read as a mailgun key.
|
|
5414
|
-
{ label: "mailgun-key", pattern: /(?<![-\w])key-[a-f0-9]{32}\b/g },
|
|
5415
|
-
{ label: "digitalocean-pat", pattern: /\bdop_v1_[a-f0-9]{64}\b/g },
|
|
5416
|
-
{ label: "huggingface-token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g },
|
|
5417
|
-
{ label: "shopify-token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g },
|
|
5418
|
-
{ label: "pypi-token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}\b/g },
|
|
5419
|
-
// No twilio-sid rule: AC…/SK… are Twilio SIDs, which are public account and
|
|
5420
|
-
// key identifiers, not the auth token. Redacting them hid nothing and
|
|
5421
|
-
// collided with 32-hex etags and MD5 sums.
|
|
5422
|
-
// No discord-bot-token rule either: the shape (word.word.word) matched
|
|
5423
|
-
// ordinary dotted symbols such as a.b.ApplicationContextProviderImpl.
|
|
5424
|
-
{ label: "telegram-bot-token", pattern: /\b\d{8,10}:AA[A-Za-z0-9_-]{32,}\b/g },
|
|
5425
|
-
{ label: "doppler-token", pattern: /\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9_-]{20,}\b/g },
|
|
5426
|
-
// Azure Storage SAS signature — the `sig=` parameter is the actual secret.
|
|
5427
|
-
{ label: "azure-sas", secret: 1, pattern: /[?&]sig=([A-Za-z0-9%/+=]{20,})/g }
|
|
5428
|
-
];
|
|
5429
|
-
SECRET_CONTEXT_RE = /\b(?:secret|password|passwd|credential|access[ _-]?key|private[ _-]?key|api[ _-]?key|auth(?:oriz\w*)?|bearer|token)\b/gi;
|
|
5430
|
-
HIGH_ENTROPY_TOKEN_RE = /[A-Za-z0-9/+_-]{32,}/g;
|
|
5431
|
-
cache = null;
|
|
5432
|
-
CANARY = "the quick brown fox jumps over the lazy dog 0123456789 commit 8ba1f109551bd432803012645ac136ddd475d0fb src/index.ts:42";
|
|
5433
|
-
MAX_PACK_RULES = 200;
|
|
5434
|
-
MAX_PATTERN_LEN = 512;
|
|
5435
|
-
serverPack = null;
|
|
5436
|
-
globalRegexCache = /* @__PURE__ */ new Map();
|
|
5437
|
-
privacyCache = null;
|
|
5438
|
-
}
|
|
5439
|
-
});
|
|
5440
|
-
|
|
5441
|
-
// ../engine/dist/parsers/chunker.js
|
|
5442
|
-
function stripInjectedBanners(text) {
|
|
5443
|
-
let result = text;
|
|
5444
|
-
for (const re of INJECTED_BANNERS)
|
|
5445
|
-
result = result.replace(re, " ");
|
|
5446
|
-
return result;
|
|
5447
|
-
}
|
|
5448
|
-
function cleanText(text) {
|
|
5449
|
-
let result = text.replace(/<system-reminder>.*?<\/system-reminder>/gs, "");
|
|
5450
|
-
result = stripInjectedBanners(result);
|
|
5451
|
-
result = result.replace(/\x1b\[[0-9;]*m/g, "");
|
|
5452
|
-
result = result.replace(/\s+/g, " ");
|
|
5453
|
-
return result.trim();
|
|
5454
|
-
}
|
|
5455
|
-
function windowText(text, maxChars, maxWindows = 12) {
|
|
5456
|
-
const clean = cleanText(text);
|
|
5457
|
-
if (clean.length <= maxChars)
|
|
5458
|
-
return clean ? [clean] : [];
|
|
5459
|
-
const out = [];
|
|
5460
|
-
const overlap = 80;
|
|
5461
|
-
const step = Math.max(1, maxChars - overlap);
|
|
5462
|
-
for (let i = 0; i < clean.length && out.length < maxWindows; i += step) {
|
|
5463
|
-
out.push(clean.slice(i, i + maxChars));
|
|
5464
|
-
}
|
|
5465
|
-
return out;
|
|
5466
|
-
}
|
|
5467
|
-
function chunkSession(entry, content, maxChunks = 80, maxTokensPerChunk = 500) {
|
|
5468
|
-
const chunks = [];
|
|
5469
|
-
const maxChars = maxTokensPerChunk * 4;
|
|
5470
|
-
const baseData = {
|
|
5471
|
-
sessionId: entry.sessionId,
|
|
5472
|
-
projectPath: entry.projectPath,
|
|
5473
|
-
created: entry.created,
|
|
5474
|
-
modified: entry.modified,
|
|
5475
|
-
mtime: entry.fileMtime,
|
|
5476
|
-
firstPrompt: entry.firstPrompt || content.firstPrompt,
|
|
5477
|
-
sourceFile: content.sessionPath
|
|
5478
|
-
};
|
|
5479
|
-
for (let i = 0; i < Math.min(2, content.summaries.length); i++) {
|
|
5480
|
-
const summary = content.summaries[i];
|
|
5481
|
-
if (!summary?.trim())
|
|
5482
|
-
continue;
|
|
5483
|
-
const chunkText = cleanText(summary.slice(0, maxChars));
|
|
5484
|
-
if (chunkText.length > 8e3) {
|
|
5485
|
-
chunks.push({
|
|
5486
|
-
chunkId: `${entry.sessionId}_summary_${i}`,
|
|
5487
|
-
chunkType: "summary",
|
|
5488
|
-
text: chunkText.slice(0, 8e3),
|
|
5489
|
-
sourceLine: 0,
|
|
5490
|
-
...baseData
|
|
5491
|
-
});
|
|
5492
|
-
} else {
|
|
5493
|
-
chunks.push({
|
|
5494
|
-
chunkId: `${entry.sessionId}_summary_${i}`,
|
|
5495
|
-
chunkType: "summary",
|
|
5496
|
-
text: chunkText,
|
|
5497
|
-
sourceLine: 0,
|
|
5498
|
-
...baseData
|
|
5499
|
-
});
|
|
5500
|
-
}
|
|
5501
|
-
}
|
|
5502
|
-
if (chunks.length >= maxChunks) {
|
|
5503
|
-
return chunks.slice(0, maxChunks);
|
|
5504
|
-
}
|
|
5505
|
-
const firstPrompt = entry.firstPrompt || content.firstPrompt;
|
|
5506
|
-
if (firstPrompt?.trim()) {
|
|
5507
|
-
const firstLine = content.userMessages[0]?.lineNumber || 0;
|
|
5508
|
-
chunks.push({
|
|
5509
|
-
chunkId: `${entry.sessionId}_first_prompt`,
|
|
5510
|
-
chunkType: "first_prompt",
|
|
5511
|
-
text: cleanText(firstPrompt.slice(0, maxChars)),
|
|
5512
|
-
sourceLine: firstLine,
|
|
5513
|
-
...baseData
|
|
5514
|
-
});
|
|
5515
|
-
}
|
|
5516
|
-
if (chunks.length >= maxChunks) {
|
|
5517
|
-
return chunks.slice(0, maxChunks);
|
|
5518
|
-
}
|
|
5519
|
-
const userStart = firstPrompt ? 1 : 0;
|
|
5520
|
-
for (let i = userStart; i < content.userMessages.length; i++) {
|
|
5521
|
-
if (chunks.length >= maxChunks)
|
|
5522
|
-
break;
|
|
5523
|
-
const msg = content.userMessages[i];
|
|
5524
|
-
if (!msg.text.trim())
|
|
5525
|
-
continue;
|
|
5526
|
-
const windows = windowText(msg.text, maxChars);
|
|
5527
|
-
for (let w = 0; w < windows.length; w++) {
|
|
5528
|
-
if (chunks.length >= maxChunks)
|
|
5529
|
-
break;
|
|
5530
|
-
chunks.push({
|
|
5531
|
-
chunkId: `${entry.sessionId}_user_${i}_${w}`,
|
|
5532
|
-
chunkType: "user_context",
|
|
5533
|
-
text: windows[w],
|
|
5534
|
-
sourceLine: msg.lineNumber,
|
|
5535
|
-
...baseData
|
|
5536
|
-
});
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
if (chunks.length >= maxChunks) {
|
|
5540
|
-
return chunks.slice(0, maxChunks);
|
|
5541
|
-
}
|
|
5542
|
-
for (let i = 0; i < content.assistantMessages.length; i++) {
|
|
5543
|
-
if (chunks.length >= maxChunks)
|
|
5544
|
-
break;
|
|
5545
|
-
const msg = content.assistantMessages[i];
|
|
5546
|
-
if (!msg.text.trim() || msg.text.length <= 50)
|
|
5547
|
-
continue;
|
|
5548
|
-
const windows = windowText(msg.text, maxChars);
|
|
5549
|
-
for (let w = 0; w < windows.length; w++) {
|
|
5550
|
-
if (chunks.length >= maxChunks)
|
|
5551
|
-
break;
|
|
5552
|
-
chunks.push({
|
|
5553
|
-
chunkId: `${entry.sessionId}_assistant_${i}_${w}`,
|
|
5554
|
-
chunkType: "assistant",
|
|
5555
|
-
text: windows[w],
|
|
5556
|
-
sourceLine: msg.lineNumber,
|
|
5557
|
-
...baseData
|
|
5558
|
-
});
|
|
5559
|
-
}
|
|
5560
|
-
}
|
|
5561
|
-
if (chunks.length >= maxChunks) {
|
|
5562
|
-
return chunks.slice(0, maxChunks);
|
|
5563
|
-
}
|
|
5564
|
-
if (content.toolResults.length > 0) {
|
|
5565
|
-
const webSearches = content.toolResults.filter((r) => r.contentType === "web_search");
|
|
5566
|
-
const otherResults = content.toolResults.filter((r) => r.contentType !== "web_search");
|
|
5567
|
-
const resultsToProcess = [...webSearches.slice(0, 5), ...otherResults.slice(0, 10)];
|
|
5568
|
-
const stripBodies = isToolOutputRedactionEnabled();
|
|
5569
|
-
for (let i = 0; i < resultsToProcess.length; i++) {
|
|
5570
|
-
if (chunks.length >= maxChunks)
|
|
5571
|
-
break;
|
|
5572
|
-
const result = resultsToProcess[i];
|
|
5573
|
-
if (result.text.trim()) {
|
|
5574
|
-
const body = stripBodies && result.contentType !== "web_search" ? redactToolResultBody(result.contentType, result.text) : result.text.slice(0, maxChars);
|
|
5575
|
-
chunks.push({
|
|
5576
|
-
chunkId: `${entry.sessionId}_${result.contentType}_${i}`,
|
|
5577
|
-
chunkType: result.contentType,
|
|
5578
|
-
text: cleanText(body),
|
|
5579
|
-
sourceLine: result.lineNumber,
|
|
5580
|
-
...baseData
|
|
5581
|
-
});
|
|
5582
|
-
}
|
|
5583
|
-
}
|
|
5584
|
-
}
|
|
5585
|
-
return chunks.slice(0, maxChunks);
|
|
5586
|
-
}
|
|
5587
|
-
var INJECTED_BANNERS;
|
|
5588
|
-
var init_chunker = __esm({
|
|
5589
|
-
"../engine/dist/parsers/chunker.js"() {
|
|
5590
|
-
"use strict";
|
|
5591
|
-
init_secret_redactor();
|
|
5592
|
-
INJECTED_BANNERS = [
|
|
5593
|
-
/MCP issues detected\. ?Run \/mcp list for status\.?/g,
|
|
5594
|
-
/Context low[^\n]*Run \/compact[^\n]*/g,
|
|
5595
|
-
/API Error:[^\n]{0,120}/g
|
|
5596
|
-
];
|
|
5597
|
-
}
|
|
5598
|
-
});
|
|
5599
|
-
|
|
5600
|
-
// ../engine/dist/core/home-approval.js
|
|
5601
|
-
import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
|
|
5602
|
-
import { homedir as homedir2 } from "os";
|
|
5603
|
-
import { join as join3, dirname as dirname3 } from "path";
|
|
5604
|
-
function normalizeHomePath(p) {
|
|
5605
|
-
const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
|
|
5606
|
-
const trimmed = expanded.replace(/[/\\]+$/, "");
|
|
5607
|
-
try {
|
|
5608
|
-
return realpathSync(trimmed);
|
|
5609
|
-
} catch {
|
|
5610
|
-
return trimmed;
|
|
5611
|
-
}
|
|
5612
|
-
}
|
|
5613
|
-
function legacyAutoSyncedHomes() {
|
|
5614
|
-
const home = homedir2();
|
|
5615
|
-
const out = [];
|
|
5616
|
-
const add = (dir, marker) => {
|
|
5617
|
-
if (!existsSync3(join3(dir, marker)))
|
|
5618
|
-
return;
|
|
5619
|
-
const n = normalizeHomePath(dir);
|
|
5620
|
-
if (!out.includes(n))
|
|
5621
|
-
out.push(n);
|
|
5622
|
-
};
|
|
5623
|
-
const bases = [
|
|
5624
|
-
{ base: join3(home, ".claude"), marker: "projects" },
|
|
5625
|
-
{ base: join3(home, ".gemini"), marker: "tmp" },
|
|
5626
|
-
{ base: join3(home, ".codex"), marker: "sessions" },
|
|
5627
|
-
{ base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
|
|
5628
|
-
{ base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
|
|
5629
|
-
{ base: join3(home, ".cursor"), marker: "chats" }
|
|
5630
|
-
];
|
|
5631
|
-
for (const { base: base2, marker } of bases) {
|
|
5632
|
-
add(base2, marker);
|
|
5633
|
-
const parent = dirname3(base2);
|
|
5634
|
-
const leaf = base2.slice(parent.length + 1);
|
|
5635
|
-
try {
|
|
5636
|
-
for (const e of readdirSync2(parent, { withFileTypes: true })) {
|
|
5637
|
-
if (!e.isDirectory())
|
|
5638
|
-
continue;
|
|
5639
|
-
if (e.name === leaf || !e.name.startsWith(leaf + "-"))
|
|
5640
|
-
continue;
|
|
5641
|
-
if (e.name === ".claude-code")
|
|
5642
|
-
continue;
|
|
5643
|
-
add(join3(parent, e.name), marker);
|
|
5644
|
-
}
|
|
5645
|
-
} catch {
|
|
5646
|
-
}
|
|
5647
|
-
}
|
|
5648
|
-
return out;
|
|
5649
|
-
}
|
|
5650
|
-
function grandfatherLegacyHomes() {
|
|
5651
|
-
let s;
|
|
5652
|
-
try {
|
|
5653
|
-
s = loadSettings();
|
|
5654
|
-
} catch {
|
|
5655
|
-
return { seeded: [] };
|
|
5656
|
-
}
|
|
5657
|
-
if (s.sources.homesGrandfathered)
|
|
5658
|
-
return { seeded: [] };
|
|
5659
|
-
const legacy = legacyAutoSyncedHomes();
|
|
5660
|
-
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5661
|
-
const seeded = [];
|
|
5662
|
-
for (const p of legacy) {
|
|
5663
|
-
if (!approved.has(p)) {
|
|
5664
|
-
approved.add(p);
|
|
5665
|
-
seeded.push(p);
|
|
5666
|
-
}
|
|
5667
|
-
}
|
|
5668
|
-
s.sources.approvedHomes = [...approved];
|
|
5669
|
-
s.sources.homesGrandfathered = true;
|
|
5670
|
-
try {
|
|
5671
|
-
saveSettings(s);
|
|
5672
|
-
} catch {
|
|
5673
|
-
return { seeded: [] };
|
|
5674
|
-
}
|
|
5675
|
-
return { seeded };
|
|
5676
|
-
}
|
|
5677
|
-
function primaryHomes() {
|
|
5678
|
-
const home = homedir2();
|
|
5679
|
-
const env = process.env;
|
|
5680
|
-
let s;
|
|
5681
|
-
try {
|
|
5682
|
-
s = loadSettings().sources;
|
|
5683
|
-
} catch {
|
|
5684
|
-
s = void 0;
|
|
5685
|
-
}
|
|
5686
|
-
const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
|
|
5687
|
-
const candidates2 = [
|
|
5688
|
-
env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
|
|
5689
|
-
env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
|
|
5690
|
-
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5691
|
-
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5692
|
-
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5693
|
-
dirOf(opencodeDb)
|
|
5694
|
-
];
|
|
5695
|
-
return candidates2.map(normalizeHomePath);
|
|
5696
|
-
}
|
|
5697
|
-
function homeDecision(path) {
|
|
5698
|
-
const p = normalizeHomePath(path);
|
|
5699
|
-
if (primaryHomes().includes(p))
|
|
5700
|
-
return "primary";
|
|
5701
|
-
let s;
|
|
5702
|
-
try {
|
|
5703
|
-
s = loadSettings().sources;
|
|
5704
|
-
} catch {
|
|
5705
|
-
return "pending";
|
|
5706
|
-
}
|
|
5707
|
-
if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5708
|
-
return "declined";
|
|
5709
|
-
if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5710
|
-
return "approved";
|
|
5711
|
-
if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
|
|
5712
|
-
return "approved";
|
|
5713
|
-
return "pending";
|
|
5714
|
-
}
|
|
5715
|
-
function isHomeSynced(path) {
|
|
5716
|
-
const d = homeDecision(path);
|
|
5717
|
-
return d === "primary" || d === "approved";
|
|
5718
|
-
}
|
|
5719
|
-
function setDecision(path, decision) {
|
|
5720
|
-
const p = normalizeHomePath(path);
|
|
5721
|
-
let s;
|
|
5722
|
-
try {
|
|
5723
|
-
s = loadSettings();
|
|
5724
|
-
} catch {
|
|
5725
|
-
return false;
|
|
5726
|
-
}
|
|
5727
|
-
const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
|
|
5728
|
-
const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
|
|
5729
|
-
approved.delete(p);
|
|
5730
|
-
declined.delete(p);
|
|
5731
|
-
(decision === "approved" ? approved : declined).add(p);
|
|
5732
|
-
s.sources.approvedHomes = [...approved];
|
|
5733
|
-
s.sources.declinedHomes = [...declined];
|
|
5734
|
-
try {
|
|
5735
|
-
saveSettings(s);
|
|
5736
|
-
} catch {
|
|
5737
|
-
return false;
|
|
5738
|
-
}
|
|
5739
|
-
return true;
|
|
5740
|
-
}
|
|
5741
|
-
function approveHome(path) {
|
|
5742
|
-
return setDecision(path, "approved");
|
|
5194
|
+
function approveHome(path) {
|
|
5195
|
+
return setDecision(path, "approved");
|
|
5743
5196
|
}
|
|
5744
5197
|
function declineHome(path) {
|
|
5745
5198
|
return setDecision(path, "declined");
|
|
@@ -6141,7 +5594,7 @@ var init_model_pricing = __esm({
|
|
|
6141
5594
|
});
|
|
6142
5595
|
|
|
6143
5596
|
// ../engine/dist/parsers/session.js
|
|
6144
|
-
import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync2, statSync as
|
|
5597
|
+
import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync2, statSync as statSync3, openSync, readSync, closeSync } from "fs";
|
|
6145
5598
|
import { createInterface } from "readline";
|
|
6146
5599
|
import { join as join5, basename as basename2 } from "path";
|
|
6147
5600
|
function removeCodeBlocks(text) {
|
|
@@ -6183,48 +5636,10 @@ function isValuableToolResult(content, toolName = "") {
|
|
|
6183
5636
|
if (["WebSearch", "WebFetch"].includes(toolName) || content.toLowerCase().includes("http")) {
|
|
6184
5637
|
return true;
|
|
6185
5638
|
}
|
|
6186
|
-
if (content.length > 5e3) {
|
|
6187
|
-
return false;
|
|
6188
|
-
}
|
|
6189
|
-
return true;
|
|
6190
|
-
}
|
|
6191
|
-
function parseSessionsIndex(indexPath) {
|
|
6192
|
-
if (!existsSync5(indexPath)) {
|
|
6193
|
-
return [];
|
|
6194
|
-
}
|
|
6195
|
-
const data = JSON.parse(readFileSync2(indexPath, "utf-8"));
|
|
6196
|
-
const entries = data.entries || [];
|
|
6197
|
-
return entries.map((e) => ({
|
|
6198
|
-
sessionId: e.sessionId || "",
|
|
6199
|
-
fullPath: e.fullPath || "",
|
|
6200
|
-
fileMtime: e.fileMtime || 0,
|
|
6201
|
-
firstPrompt: e.firstPrompt || "",
|
|
6202
|
-
messageCount: e.messageCount || 0,
|
|
6203
|
-
created: e.created || "",
|
|
6204
|
-
modified: e.modified || "",
|
|
6205
|
-
gitBranch: e.gitBranch || "",
|
|
6206
|
-
projectPath: e.projectPath || "",
|
|
6207
|
-
isSidechain: e.isSidechain || false
|
|
6208
|
-
}));
|
|
6209
|
-
}
|
|
6210
|
-
function* iterAllSessionIndices(claudeDir) {
|
|
6211
|
-
const dir = claudeDir || claudeBackend.projectsDir();
|
|
6212
|
-
if (!existsSync5(dir)) {
|
|
6213
|
-
return;
|
|
6214
|
-
}
|
|
6215
|
-
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
6216
|
-
for (const entry of entries) {
|
|
6217
|
-
if (!entry.isDirectory())
|
|
6218
|
-
continue;
|
|
6219
|
-
const projectDir = join5(dir, entry.name);
|
|
6220
|
-
const indexPath = join5(projectDir, "sessions-index.json");
|
|
6221
|
-
if (existsSync5(indexPath)) {
|
|
6222
|
-
const sessions = parseSessionsIndex(indexPath);
|
|
6223
|
-
if (sessions.length > 0) {
|
|
6224
|
-
yield [projectDir, sessions];
|
|
6225
|
-
}
|
|
6226
|
-
}
|
|
5639
|
+
if (content.length > 5e3) {
|
|
5640
|
+
return false;
|
|
6227
5641
|
}
|
|
5642
|
+
return true;
|
|
6228
5643
|
}
|
|
6229
5644
|
function hasSubagentsDir(sessionPath) {
|
|
6230
5645
|
const dir = sessionPath.slice(0, -6);
|
|
@@ -6236,40 +5651,6 @@ function getSubagentPaths(sessionPath) {
|
|
|
6236
5651
|
return [];
|
|
6237
5652
|
return readdirSync5(subagentsDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join5(subagentsDir, f));
|
|
6238
5653
|
}
|
|
6239
|
-
function getSubagentProjectPath(sessionPath, folderDerivedPath) {
|
|
6240
|
-
const subagentPaths = getSubagentPaths(sessionPath);
|
|
6241
|
-
if (subagentPaths.length === 0)
|
|
6242
|
-
return folderDerivedPath;
|
|
6243
|
-
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6244
|
-
for (const filePath of subagentPaths) {
|
|
6245
|
-
try {
|
|
6246
|
-
const lines = readFileSync2(filePath, "utf-8").split("\n");
|
|
6247
|
-
for (const line of lines) {
|
|
6248
|
-
if (!line.trim())
|
|
6249
|
-
continue;
|
|
6250
|
-
try {
|
|
6251
|
-
const entry = JSON.parse(line);
|
|
6252
|
-
if (entry.type === "user" && typeof entry.cwd === "string" && entry.cwd) {
|
|
6253
|
-
cwdCounts.set(entry.cwd, (cwdCounts.get(entry.cwd) ?? 0) + 1);
|
|
6254
|
-
}
|
|
6255
|
-
} catch {
|
|
6256
|
-
}
|
|
6257
|
-
}
|
|
6258
|
-
} catch {
|
|
6259
|
-
}
|
|
6260
|
-
}
|
|
6261
|
-
if (cwdCounts.size === 0)
|
|
6262
|
-
return folderDerivedPath;
|
|
6263
|
-
let best = folderDerivedPath;
|
|
6264
|
-
let bestCount = 0;
|
|
6265
|
-
for (const [cwd, count] of cwdCounts) {
|
|
6266
|
-
if (count > bestCount) {
|
|
6267
|
-
best = cwd;
|
|
6268
|
-
bestCount = count;
|
|
6269
|
-
}
|
|
6270
|
-
}
|
|
6271
|
-
return best;
|
|
6272
|
-
}
|
|
6273
5654
|
function readCwdFromJsonl(sessionPath) {
|
|
6274
5655
|
try {
|
|
6275
5656
|
if (!existsSync5(sessionPath))
|
|
@@ -6298,22 +5679,6 @@ function readCwdFromJsonl(sessionPath) {
|
|
|
6298
5679
|
}
|
|
6299
5680
|
return "";
|
|
6300
5681
|
}
|
|
6301
|
-
function getSessionMtime(sessionPath) {
|
|
6302
|
-
const subagentPaths = getSubagentPaths(sessionPath);
|
|
6303
|
-
if (subagentPaths.length === 0) {
|
|
6304
|
-
return statSync4(sessionPath).mtimeMs;
|
|
6305
|
-
}
|
|
6306
|
-
let maxMtime = statSync4(sessionPath).mtimeMs;
|
|
6307
|
-
for (const p of subagentPaths) {
|
|
6308
|
-
try {
|
|
6309
|
-
const m = statSync4(p).mtimeMs;
|
|
6310
|
-
if (m > maxMtime)
|
|
6311
|
-
maxMtime = m;
|
|
6312
|
-
} catch {
|
|
6313
|
-
}
|
|
6314
|
-
}
|
|
6315
|
-
return maxMtime;
|
|
6316
|
-
}
|
|
6317
5682
|
async function parseSessionFile(sessionPath, maxMessages = 50) {
|
|
6318
5683
|
const sessionId = basename2(sessionPath, ".jsonl");
|
|
6319
5684
|
const content = {
|
|
@@ -6551,76 +5916,10 @@ ${resultText}` : resultText;
|
|
|
6551
5916
|
});
|
|
6552
5917
|
return content;
|
|
6553
5918
|
}
|
|
6554
|
-
function* getAllSessions(claudeDir) {
|
|
6555
|
-
const seenSessionIds = /* @__PURE__ */ new Set();
|
|
6556
|
-
const projectDirs = claudeDir ? [claudeDir] : claudeProjectDirs();
|
|
6557
|
-
for (const dir of projectDirs) {
|
|
6558
|
-
for (const [, entries] of iterAllSessionIndices(dir)) {
|
|
6559
|
-
for (const entry of entries) {
|
|
6560
|
-
if (!seenSessionIds.has(entry.sessionId) && existsSync5(entry.fullPath)) {
|
|
6561
|
-
seenSessionIds.add(entry.sessionId);
|
|
6562
|
-
yield [entry, entry.fullPath];
|
|
6563
|
-
}
|
|
6564
|
-
}
|
|
6565
|
-
}
|
|
6566
|
-
if (!existsSync5(dir))
|
|
6567
|
-
continue;
|
|
6568
|
-
const dirEntries = readdirSync5(dir, { withFileTypes: true });
|
|
6569
|
-
for (const projDirEntry of dirEntries) {
|
|
6570
|
-
if (!projDirEntry.isDirectory())
|
|
6571
|
-
continue;
|
|
6572
|
-
const projectPath = join5(dir, projDirEntry.name);
|
|
6573
|
-
const files = readdirSync5(projectPath);
|
|
6574
|
-
for (const file of files) {
|
|
6575
|
-
if (!file.endsWith(".jsonl"))
|
|
6576
|
-
continue;
|
|
6577
|
-
if (file.startsWith("agent-"))
|
|
6578
|
-
continue;
|
|
6579
|
-
if (file === "sessions-index.json")
|
|
6580
|
-
continue;
|
|
6581
|
-
const sessionId = basename2(file, ".jsonl");
|
|
6582
|
-
if (seenSessionIds.has(sessionId))
|
|
6583
|
-
continue;
|
|
6584
|
-
const fullPath = join5(projectPath, file);
|
|
6585
|
-
try {
|
|
6586
|
-
const stat3 = statSync4(fullPath);
|
|
6587
|
-
const derivedProjectPath = resolveProjectDirName(projDirEntry.name);
|
|
6588
|
-
const effectiveMtime = getSessionMtime(fullPath);
|
|
6589
|
-
let projectPath2 = "";
|
|
6590
|
-
if (hasSubagentsDir(fullPath)) {
|
|
6591
|
-
projectPath2 = getSubagentProjectPath(fullPath, derivedProjectPath);
|
|
6592
|
-
} else {
|
|
6593
|
-
const fromJsonl = readCwdFromJsonl(fullPath);
|
|
6594
|
-
if (fromJsonl)
|
|
6595
|
-
projectPath2 = fromJsonl;
|
|
6596
|
-
}
|
|
6597
|
-
if (!projectPath2)
|
|
6598
|
-
projectPath2 = derivedProjectPath;
|
|
6599
|
-
const entry = {
|
|
6600
|
-
sessionId,
|
|
6601
|
-
fullPath,
|
|
6602
|
-
fileMtime: effectiveMtime,
|
|
6603
|
-
firstPrompt: "",
|
|
6604
|
-
messageCount: 0,
|
|
6605
|
-
created: stat3.birthtime.toISOString(),
|
|
6606
|
-
modified: new Date(effectiveMtime).toISOString(),
|
|
6607
|
-
gitBranch: "",
|
|
6608
|
-
projectPath: projectPath2,
|
|
6609
|
-
isSidechain: false
|
|
6610
|
-
};
|
|
6611
|
-
seenSessionIds.add(sessionId);
|
|
6612
|
-
yield [entry, fullPath];
|
|
6613
|
-
} catch {
|
|
6614
|
-
continue;
|
|
6615
|
-
}
|
|
6616
|
-
}
|
|
6617
|
-
}
|
|
6618
|
-
}
|
|
6619
|
-
}
|
|
6620
5919
|
var init_session = __esm({
|
|
6621
5920
|
"../engine/dist/parsers/session.js"() {
|
|
6622
5921
|
"use strict";
|
|
6623
|
-
|
|
5922
|
+
init_banners();
|
|
6624
5923
|
init_claude2();
|
|
6625
5924
|
init_tool_paths();
|
|
6626
5925
|
init_project_dir_name();
|
|
@@ -7286,7 +6585,7 @@ var init_raw = __esm({
|
|
|
7286
6585
|
// ../engine/dist/transcript/shadow.js
|
|
7287
6586
|
import { gzipSync as gzipSync2, gunzipSync as gunzipSync2 } from "zlib";
|
|
7288
6587
|
import { createHash as createHash2 } from "crypto";
|
|
7289
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as
|
|
6588
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync4 } from "fs";
|
|
7290
6589
|
import { dirname as dirname7, join as join8 } from "path";
|
|
7291
6590
|
function shadowRoot() {
|
|
7292
6591
|
return join8(getDataDir(), "shadow");
|
|
@@ -7494,7 +6793,7 @@ function liveFingerprint(backend, rawId) {
|
|
|
7494
6793
|
const loc = backend.findSession(rawId);
|
|
7495
6794
|
if (!loc?.path)
|
|
7496
6795
|
return "";
|
|
7497
|
-
const st =
|
|
6796
|
+
const st = statSync4(loc.path);
|
|
7498
6797
|
return `${st.mtimeMs}:${st.size}`;
|
|
7499
6798
|
} catch {
|
|
7500
6799
|
return "";
|
|
@@ -7512,7 +6811,7 @@ function seedShadow(sessionId, container) {
|
|
|
7512
6811
|
}
|
|
7513
6812
|
function shadowSizeOnDisk(tool, rawId) {
|
|
7514
6813
|
try {
|
|
7515
|
-
return
|
|
6814
|
+
return statSync4(shadowFileFor(tool, rawId)).size;
|
|
7516
6815
|
} catch {
|
|
7517
6816
|
return 0;
|
|
7518
6817
|
}
|
|
@@ -7530,7 +6829,7 @@ var init_shadow = __esm({
|
|
|
7530
6829
|
});
|
|
7531
6830
|
|
|
7532
6831
|
// ../engine/dist/core/live-session-scan.js
|
|
7533
|
-
import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as
|
|
6832
|
+
import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
|
|
7534
6833
|
import { join as join9, basename as basename5 } from "path";
|
|
7535
6834
|
function detectTool(sessionId) {
|
|
7536
6835
|
return getBackendForId(sessionId)?.id ?? "claude";
|
|
@@ -7648,7 +6947,7 @@ function readSessionGroupText(group) {
|
|
|
7648
6947
|
const parts = [];
|
|
7649
6948
|
for (const p of group.paths) {
|
|
7650
6949
|
try {
|
|
7651
|
-
const st =
|
|
6950
|
+
const st = statSync5(p);
|
|
7652
6951
|
parts.push(`${p}:${st.mtimeMs}:${st.size}`);
|
|
7653
6952
|
} catch {
|
|
7654
6953
|
parts.push(`${p}:missing`);
|
|
@@ -7668,7 +6967,7 @@ function readSessionGroupText(group) {
|
|
|
7668
6967
|
continue;
|
|
7669
6968
|
}
|
|
7670
6969
|
try {
|
|
7671
|
-
mtime = Math.max(mtime,
|
|
6970
|
+
mtime = Math.max(mtime, statSync5(p).mtimeMs);
|
|
7672
6971
|
} catch {
|
|
7673
6972
|
}
|
|
7674
6973
|
text = text ? mergeLineText(text, raw).text : raw;
|
|
@@ -8377,11 +7676,11 @@ var init_first_prompt = __esm({
|
|
|
8377
7676
|
});
|
|
8378
7677
|
|
|
8379
7678
|
// ../engine/dist/core/backends/tail-read.js
|
|
8380
|
-
import { openSync as openSync4, readSync as readSync4, closeSync as closeSync4, statSync as
|
|
7679
|
+
import { openSync as openSync4, readSync as readSync4, closeSync as closeSync4, statSync as statSync6 } from "node:fs";
|
|
8381
7680
|
function readTailFromOffset(path, offset) {
|
|
8382
7681
|
let size = 0;
|
|
8383
7682
|
try {
|
|
8384
|
-
size =
|
|
7683
|
+
size = statSync6(path).size;
|
|
8385
7684
|
} catch {
|
|
8386
7685
|
return { text: "", newOffset: offset };
|
|
8387
7686
|
}
|
|
@@ -8698,7 +7997,7 @@ var init_generic_engine = __esm({
|
|
|
8698
7997
|
});
|
|
8699
7998
|
|
|
8700
7999
|
// ../engine/dist/core/backends/claude.js
|
|
8701
|
-
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as
|
|
8000
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as statSync7, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
|
|
8702
8001
|
import { join as join10, basename as basename8 } from "path";
|
|
8703
8002
|
function stripReminders(text) {
|
|
8704
8003
|
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
|
|
@@ -8828,7 +8127,7 @@ var init_claude2 = __esm({
|
|
|
8828
8127
|
let mtime = 0;
|
|
8829
8128
|
for (const c of copies) {
|
|
8830
8129
|
try {
|
|
8831
|
-
mtime = Math.max(mtime,
|
|
8130
|
+
mtime = Math.max(mtime, statSync7(c.path).mtimeMs);
|
|
8832
8131
|
} catch {
|
|
8833
8132
|
}
|
|
8834
8133
|
}
|
|
@@ -8904,7 +8203,7 @@ var init_claude2 = __esm({
|
|
|
8904
8203
|
const fullPath = join10(projPath, f);
|
|
8905
8204
|
let stat3;
|
|
8906
8205
|
try {
|
|
8907
|
-
stat3 =
|
|
8206
|
+
stat3 = statSync7(fullPath);
|
|
8908
8207
|
} catch {
|
|
8909
8208
|
continue;
|
|
8910
8209
|
}
|
|
@@ -9125,7 +8424,7 @@ var init_claude2 = __esm({
|
|
|
9125
8424
|
const events = this.readEvents(rawId);
|
|
9126
8425
|
let fileMtime = 0;
|
|
9127
8426
|
try {
|
|
9128
|
-
fileMtime =
|
|
8427
|
+
fileMtime = statSync7(located.path).mtimeMs;
|
|
9129
8428
|
} catch {
|
|
9130
8429
|
}
|
|
9131
8430
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -9180,13 +8479,13 @@ var init_claude2 = __esm({
|
|
|
9180
8479
|
continue;
|
|
9181
8480
|
const fp = join10(projPath, f);
|
|
9182
8481
|
try {
|
|
9183
|
-
const st =
|
|
8482
|
+
const st = statSync7(fp);
|
|
9184
8483
|
let mtime = st.mtimeMs;
|
|
9185
8484
|
const subDir = join10(projPath, f.slice(0, -6), "subagents");
|
|
9186
8485
|
if (existsSync10(subDir)) {
|
|
9187
8486
|
for (const sub of readdirSync7(subDir)) {
|
|
9188
8487
|
try {
|
|
9189
|
-
const subSt =
|
|
8488
|
+
const subSt = statSync7(join10(subDir, sub));
|
|
9190
8489
|
if (subSt.mtimeMs > mtime)
|
|
9191
8490
|
mtime = subSt.mtimeMs;
|
|
9192
8491
|
} catch {
|
|
@@ -9251,7 +8550,7 @@ var init_claude2 = __esm({
|
|
|
9251
8550
|
let total = 0;
|
|
9252
8551
|
for (const located of findSessionFiles(this.toRawId(prefixedId))) {
|
|
9253
8552
|
try {
|
|
9254
|
-
total +=
|
|
8553
|
+
total += statSync7(located.path).size;
|
|
9255
8554
|
} catch {
|
|
9256
8555
|
}
|
|
9257
8556
|
}
|
|
@@ -9864,7 +9163,7 @@ var init_opencode = __esm({
|
|
|
9864
9163
|
});
|
|
9865
9164
|
|
|
9866
9165
|
// ../engine/dist/core/backends/codex.js
|
|
9867
|
-
import { readdirSync as readdirSync8, readFileSync as readFileSync6, statSync as
|
|
9166
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync6, statSync as statSync8, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
|
|
9868
9167
|
import { basename as basename9, dirname as dirname10, join as join12 } from "path";
|
|
9869
9168
|
function parseShellWrites(cmd) {
|
|
9870
9169
|
const out = [];
|
|
@@ -10072,7 +9371,7 @@ var init_codex2 = __esm({
|
|
|
10072
9371
|
return null;
|
|
10073
9372
|
let mtime = 0;
|
|
10074
9373
|
try {
|
|
10075
|
-
mtime =
|
|
9374
|
+
mtime = statSync8(located.path).mtimeMs;
|
|
10076
9375
|
} catch {
|
|
10077
9376
|
}
|
|
10078
9377
|
return {
|
|
@@ -10092,7 +9391,7 @@ var init_codex2 = __esm({
|
|
|
10092
9391
|
for (const filePath of walkRollouts(root)) {
|
|
10093
9392
|
let stat3;
|
|
10094
9393
|
try {
|
|
10095
|
-
stat3 =
|
|
9394
|
+
stat3 = statSync8(filePath);
|
|
10096
9395
|
} catch {
|
|
10097
9396
|
continue;
|
|
10098
9397
|
}
|
|
@@ -10288,7 +9587,7 @@ var init_codex2 = __esm({
|
|
|
10288
9587
|
let mtime = 0;
|
|
10289
9588
|
try {
|
|
10290
9589
|
raw = readFileSync6(path, "utf-8");
|
|
10291
|
-
mtime =
|
|
9590
|
+
mtime = statSync8(path).mtimeMs;
|
|
10292
9591
|
} catch {
|
|
10293
9592
|
return [];
|
|
10294
9593
|
}
|
|
@@ -10388,7 +9687,7 @@ var init_codex2 = __esm({
|
|
|
10388
9687
|
const events = this.readEvents(this.toRawId(id));
|
|
10389
9688
|
let fileMtime = 0;
|
|
10390
9689
|
try {
|
|
10391
|
-
fileMtime =
|
|
9690
|
+
fileMtime = statSync8(located.path).mtimeMs;
|
|
10392
9691
|
} catch {
|
|
10393
9692
|
}
|
|
10394
9693
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -10452,7 +9751,7 @@ var init_codex2 = __esm({
|
|
|
10452
9751
|
const p = join12(dir, entry.name);
|
|
10453
9752
|
let st;
|
|
10454
9753
|
try {
|
|
10455
|
-
st =
|
|
9754
|
+
st = statSync8(p);
|
|
10456
9755
|
} catch {
|
|
10457
9756
|
continue;
|
|
10458
9757
|
}
|
|
@@ -10519,7 +9818,7 @@ var init_codex2 = __esm({
|
|
|
10519
9818
|
let mtime = 0;
|
|
10520
9819
|
const push = (path, name) => {
|
|
10521
9820
|
try {
|
|
10522
|
-
const st =
|
|
9821
|
+
const st = statSync8(path);
|
|
10523
9822
|
files.push({ name, bytes: readFileSync6(path) });
|
|
10524
9823
|
if (st.mtimeMs > mtime)
|
|
10525
9824
|
mtime = st.mtimeMs;
|
|
@@ -10561,7 +9860,7 @@ var init_codex2 = __esm({
|
|
|
10561
9860
|
if (!loc)
|
|
10562
9861
|
return 0;
|
|
10563
9862
|
try {
|
|
10564
|
-
return
|
|
9863
|
+
return statSync8(loc.path).size;
|
|
10565
9864
|
} catch {
|
|
10566
9865
|
return 0;
|
|
10567
9866
|
}
|
|
@@ -10578,7 +9877,7 @@ var init_codex2 = __esm({
|
|
|
10578
9877
|
});
|
|
10579
9878
|
|
|
10580
9879
|
// ../engine/dist/core/backends/agy.js
|
|
10581
|
-
import { existsSync as existsSync12, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as
|
|
9880
|
+
import { existsSync as existsSync12, readdirSync as readdirSync9, readFileSync as readFileSync7, statSync as statSync9 } from "fs";
|
|
10582
9881
|
import { basename as basename10, dirname as dirname11, join as join13 } from "path";
|
|
10583
9882
|
function agyTranscriptPath(logsDir) {
|
|
10584
9883
|
const full = join13(logsDir, "transcript_full.jsonl");
|
|
@@ -10771,7 +10070,7 @@ var init_agy = __esm({
|
|
|
10771
10070
|
return null;
|
|
10772
10071
|
let mtime = 0;
|
|
10773
10072
|
try {
|
|
10774
|
-
mtime =
|
|
10073
|
+
mtime = statSync9(path).mtimeMs;
|
|
10775
10074
|
} catch {
|
|
10776
10075
|
}
|
|
10777
10076
|
const projectPath = resolveAgyProject(dirname11(path), this.homeDir());
|
|
@@ -10799,7 +10098,7 @@ var init_agy = __esm({
|
|
|
10799
10098
|
for (const rawId of sessionDirs) {
|
|
10800
10099
|
const sessionPath = join13(brainDir, rawId);
|
|
10801
10100
|
try {
|
|
10802
|
-
if (!
|
|
10101
|
+
if (!statSync9(sessionPath).isDirectory())
|
|
10803
10102
|
continue;
|
|
10804
10103
|
} catch {
|
|
10805
10104
|
continue;
|
|
@@ -10809,7 +10108,7 @@ var init_agy = __esm({
|
|
|
10809
10108
|
continue;
|
|
10810
10109
|
let stat3;
|
|
10811
10110
|
try {
|
|
10812
|
-
stat3 =
|
|
10111
|
+
stat3 = statSync9(filePath);
|
|
10813
10112
|
} catch {
|
|
10814
10113
|
continue;
|
|
10815
10114
|
}
|
|
@@ -11074,7 +10373,7 @@ var init_agy = __esm({
|
|
|
11074
10373
|
for (const rawId of sessionDirs) {
|
|
11075
10374
|
const sessionPath = join13(brainDir, rawId);
|
|
11076
10375
|
try {
|
|
11077
|
-
if (!
|
|
10376
|
+
if (!statSync9(sessionPath).isDirectory())
|
|
11078
10377
|
continue;
|
|
11079
10378
|
} catch {
|
|
11080
10379
|
continue;
|
|
@@ -11083,7 +10382,7 @@ var init_agy = __esm({
|
|
|
11083
10382
|
if (!filePath)
|
|
11084
10383
|
continue;
|
|
11085
10384
|
try {
|
|
11086
|
-
const st =
|
|
10385
|
+
const st = statSync9(filePath);
|
|
11087
10386
|
if (st.mtimeMs < opts.sinceMs)
|
|
11088
10387
|
continue;
|
|
11089
10388
|
candidates2.push({ rawId, mtime: st.mtimeMs });
|
|
@@ -11111,7 +10410,7 @@ var init_agy = __esm({
|
|
|
11111
10410
|
try {
|
|
11112
10411
|
return {
|
|
11113
10412
|
tool: "agy",
|
|
11114
|
-
mtime:
|
|
10413
|
+
mtime: statSync9(loc.path).mtimeMs,
|
|
11115
10414
|
files: [{ name: basename10(loc.path), bytes: readFileSync7(loc.path) }]
|
|
11116
10415
|
};
|
|
11117
10416
|
} catch {
|
|
@@ -11126,7 +10425,7 @@ var init_agy = __esm({
|
|
|
11126
10425
|
if (!loc)
|
|
11127
10426
|
return 0;
|
|
11128
10427
|
try {
|
|
11129
|
-
return
|
|
10428
|
+
return statSync9(loc.path).size;
|
|
11130
10429
|
} catch {
|
|
11131
10430
|
return 0;
|
|
11132
10431
|
}
|
|
@@ -11143,7 +10442,7 @@ var init_agy = __esm({
|
|
|
11143
10442
|
});
|
|
11144
10443
|
|
|
11145
10444
|
// ../engine/dist/core/backends/cursor-store.js
|
|
11146
|
-
import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync8, statSync as
|
|
10445
|
+
import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync8, statSync as statSync10 } from "fs";
|
|
11147
10446
|
import { join as join14 } from "path";
|
|
11148
10447
|
function cursorProjectSlug(absPath) {
|
|
11149
10448
|
return absPath.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -11160,7 +10459,7 @@ function listChatsIn(chatsRoot) {
|
|
|
11160
10459
|
const hashPath3 = join14(chatsRoot, workspaceHash);
|
|
11161
10460
|
let chatIds;
|
|
11162
10461
|
try {
|
|
11163
|
-
if (!
|
|
10462
|
+
if (!statSync10(hashPath3).isDirectory())
|
|
11164
10463
|
continue;
|
|
11165
10464
|
chatIds = readdirSync10(hashPath3);
|
|
11166
10465
|
} catch {
|
|
@@ -11182,7 +10481,7 @@ function listChatsIn(chatsRoot) {
|
|
|
11182
10481
|
let created = meta.createdAtMs ?? 0;
|
|
11183
10482
|
if (!mtime || !created) {
|
|
11184
10483
|
try {
|
|
11185
|
-
const st =
|
|
10484
|
+
const st = statSync10(dbPath);
|
|
11186
10485
|
mtime = mtime || st.mtimeMs;
|
|
11187
10486
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
11188
10487
|
} catch {
|
|
@@ -11225,7 +10524,7 @@ function findChat(chatsRoots, chatId) {
|
|
|
11225
10524
|
let created = meta.createdAtMs ?? 0;
|
|
11226
10525
|
if (!mtime || !created) {
|
|
11227
10526
|
try {
|
|
11228
|
-
const st =
|
|
10527
|
+
const st = statSync10(dbPath);
|
|
11229
10528
|
mtime = mtime || st.mtimeMs;
|
|
11230
10529
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
11231
10530
|
} catch {
|
|
@@ -11528,7 +10827,7 @@ var init_cursor_store = __esm({
|
|
|
11528
10827
|
});
|
|
11529
10828
|
|
|
11530
10829
|
// ../engine/dist/core/backends/cursor-ide.js
|
|
11531
|
-
import { existsSync as existsSync14, readFileSync as readFileSync9, readdirSync as readdirSync11, statSync as
|
|
10830
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9, readdirSync as readdirSync11, statSync as statSync11 } from "fs";
|
|
11532
10831
|
import { dirname as dirname12, join as join15 } from "path";
|
|
11533
10832
|
function jsonOf(value) {
|
|
11534
10833
|
if (value == null)
|
|
@@ -11598,7 +10897,7 @@ function workspaceIndex(workspaceRoots) {
|
|
|
11598
10897
|
for (const id of entries) {
|
|
11599
10898
|
const dir = join15(root, id);
|
|
11600
10899
|
try {
|
|
11601
|
-
if (!
|
|
10900
|
+
if (!statSync11(dir).isDirectory())
|
|
11602
10901
|
continue;
|
|
11603
10902
|
} catch {
|
|
11604
10903
|
continue;
|
|
@@ -11944,7 +11243,7 @@ var init_cursor_ide = __esm({
|
|
|
11944
11243
|
});
|
|
11945
11244
|
|
|
11946
11245
|
// ../engine/dist/core/backends/cursor.js
|
|
11947
|
-
import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as
|
|
11246
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync12 } from "fs";
|
|
11948
11247
|
import { join as join16 } from "path";
|
|
11949
11248
|
var PREFIX4, CursorBackend, cursorBackend;
|
|
11950
11249
|
var init_cursor = __esm({
|
|
@@ -11997,7 +11296,7 @@ var init_cursor = __esm({
|
|
|
11997
11296
|
if (r.surface === "cli") {
|
|
11998
11297
|
let mtime2 = r.chat.updatedAtMs;
|
|
11999
11298
|
try {
|
|
12000
|
-
mtime2 =
|
|
11299
|
+
mtime2 = statSync12(r.chat.dbPath).mtimeMs || mtime2;
|
|
12001
11300
|
} catch {
|
|
12002
11301
|
}
|
|
12003
11302
|
return {
|
|
@@ -12010,7 +11309,7 @@ var init_cursor = __esm({
|
|
|
12010
11309
|
}
|
|
12011
11310
|
let mtime = r.composer.updatedAtMs;
|
|
12012
11311
|
try {
|
|
12013
|
-
mtime =
|
|
11312
|
+
mtime = statSync12(r.composer.globalDbPath).mtimeMs || mtime;
|
|
12014
11313
|
} catch {
|
|
12015
11314
|
}
|
|
12016
11315
|
return {
|
|
@@ -12256,7 +11555,7 @@ var init_cursor = __esm({
|
|
|
12256
11555
|
if (existsSync15(metaPath)) {
|
|
12257
11556
|
files.push({ name: "meta.json", bytes: readFileSync10(metaPath) });
|
|
12258
11557
|
}
|
|
12259
|
-
return { tool: "cursor", mtime:
|
|
11558
|
+
return { tool: "cursor", mtime: statSync12(r.chat.dbPath).mtimeMs, files };
|
|
12260
11559
|
}
|
|
12261
11560
|
const events = readComposerEvents(r.composer);
|
|
12262
11561
|
if (events.length === 0)
|
|
@@ -12264,7 +11563,7 @@ var init_cursor = __esm({
|
|
|
12264
11563
|
const body = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
12265
11564
|
return {
|
|
12266
11565
|
tool: "cursor",
|
|
12267
|
-
mtime:
|
|
11566
|
+
mtime: statSync12(r.composer.globalDbPath).mtimeMs,
|
|
12268
11567
|
files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(body, "utf8") }]
|
|
12269
11568
|
};
|
|
12270
11569
|
} catch {
|
|
@@ -12386,33 +11685,413 @@ function reportWalkProgress(p) {
|
|
|
12386
11685
|
lastProgressWrite = now;
|
|
12387
11686
|
updateCollectorHealth({ progress: p });
|
|
12388
11687
|
}
|
|
12389
|
-
var healthPath, PROGRESS_WRITE_INTERVAL_MS, lastProgressWrite, STALE_AFTER_MS;
|
|
12390
|
-
var init_collector_health = __esm({
|
|
12391
|
-
"../engine/dist/core/collector-health.js"() {
|
|
12392
|
-
"use strict";
|
|
12393
|
-
init_paths();
|
|
12394
|
-
healthPath = () => join17(getDataDir(), "collector-health.json");
|
|
12395
|
-
PROGRESS_WRITE_INTERVAL_MS = 2e3;
|
|
12396
|
-
lastProgressWrite = 0;
|
|
12397
|
-
STALE_AFTER_MS = 30 * 6e4;
|
|
11688
|
+
var healthPath, PROGRESS_WRITE_INTERVAL_MS, lastProgressWrite, STALE_AFTER_MS;
|
|
11689
|
+
var init_collector_health = __esm({
|
|
11690
|
+
"../engine/dist/core/collector-health.js"() {
|
|
11691
|
+
"use strict";
|
|
11692
|
+
init_paths();
|
|
11693
|
+
healthPath = () => join17(getDataDir(), "collector-health.json");
|
|
11694
|
+
PROGRESS_WRITE_INTERVAL_MS = 2e3;
|
|
11695
|
+
lastProgressWrite = 0;
|
|
11696
|
+
STALE_AFTER_MS = 30 * 6e4;
|
|
11697
|
+
}
|
|
11698
|
+
});
|
|
11699
|
+
|
|
11700
|
+
// ../engine/dist/core/credentials-env.js
|
|
11701
|
+
function envCredentials(env = process.env) {
|
|
11702
|
+
const token = (env.CHAT_RECALL_TOKEN || "").trim();
|
|
11703
|
+
const server = (env.CHAT_RECALL_SERVER || "").trim();
|
|
11704
|
+
if (!token || !server)
|
|
11705
|
+
return null;
|
|
11706
|
+
if (!/^https?:\/\//i.test(server))
|
|
11707
|
+
return null;
|
|
11708
|
+
return { serverUrl: normalise(server), token };
|
|
11709
|
+
}
|
|
11710
|
+
var normalise;
|
|
11711
|
+
var init_credentials_env = __esm({
|
|
11712
|
+
"../engine/dist/core/credentials-env.js"() {
|
|
11713
|
+
"use strict";
|
|
11714
|
+
normalise = (u) => u.trim().replace(/\/+$/, "");
|
|
11715
|
+
}
|
|
11716
|
+
});
|
|
11717
|
+
|
|
11718
|
+
// ../engine/dist/core/secret-redactor.js
|
|
11719
|
+
import { statSync as statSync13 } from "fs";
|
|
11720
|
+
function secretOf(rule, m) {
|
|
11721
|
+
const raw = rule.secret != null ? m[rule.secret] : m[0];
|
|
11722
|
+
if (raw == null)
|
|
11723
|
+
return null;
|
|
11724
|
+
if (rule.where && !rule.where(raw))
|
|
11725
|
+
return null;
|
|
11726
|
+
return raw;
|
|
11727
|
+
}
|
|
11728
|
+
function looksLikeSecretToken(t) {
|
|
11729
|
+
if (t.length < 32)
|
|
11730
|
+
return false;
|
|
11731
|
+
if (/^[a-f0-9]+$/i.test(t))
|
|
11732
|
+
return true;
|
|
11733
|
+
return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
|
|
11734
|
+
}
|
|
11735
|
+
function findContextualSecrets(text) {
|
|
11736
|
+
if (!text)
|
|
11737
|
+
return [];
|
|
11738
|
+
const ranges = [];
|
|
11739
|
+
let m;
|
|
11740
|
+
SECRET_CONTEXT_RE.lastIndex = 0;
|
|
11741
|
+
while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
|
|
11742
|
+
const winStart = m.index + m[0].length;
|
|
11743
|
+
const window = text.slice(winStart, winStart + 80);
|
|
11744
|
+
HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
|
|
11745
|
+
let tm;
|
|
11746
|
+
while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
|
|
11747
|
+
if (!looksLikeSecretToken(tm[0]))
|
|
11748
|
+
continue;
|
|
11749
|
+
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))) {
|
|
11750
|
+
continue;
|
|
11751
|
+
}
|
|
11752
|
+
ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
|
|
11753
|
+
break;
|
|
11754
|
+
}
|
|
11755
|
+
}
|
|
11756
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
11757
|
+
const out = [];
|
|
11758
|
+
for (const r of ranges) {
|
|
11759
|
+
const last = out[out.length - 1];
|
|
11760
|
+
if (last && r.start < last.end) {
|
|
11761
|
+
last.end = Math.max(last.end, r.end);
|
|
11762
|
+
continue;
|
|
11763
|
+
}
|
|
11764
|
+
out.push({ ...r });
|
|
11765
|
+
}
|
|
11766
|
+
return out;
|
|
11767
|
+
}
|
|
11768
|
+
function isEnvRedactionRequested() {
|
|
11769
|
+
const v = process.env.CHAT_RECALL_REDACT_INDEX;
|
|
11770
|
+
if (v === void 0)
|
|
11771
|
+
return null;
|
|
11772
|
+
return v === "1" || v === "true" || v === "yes";
|
|
11773
|
+
}
|
|
11774
|
+
function settingsView() {
|
|
11775
|
+
let mtimeMs = -1;
|
|
11776
|
+
try {
|
|
11777
|
+
mtimeMs = statSync13(settingsFilePath()).mtimeMs;
|
|
11778
|
+
} catch {
|
|
11779
|
+
}
|
|
11780
|
+
if (cache && cache.mtimeMs === mtimeMs)
|
|
11781
|
+
return cache;
|
|
11782
|
+
let enabled = false;
|
|
11783
|
+
const extra = [];
|
|
11784
|
+
try {
|
|
11785
|
+
const s = loadSettings();
|
|
11786
|
+
enabled = !!s.privacy?.redactIndex;
|
|
11787
|
+
for (const r of s.privacy?.redactionRules ?? []) {
|
|
11788
|
+
try {
|
|
11789
|
+
extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
|
|
11790
|
+
} catch {
|
|
11791
|
+
}
|
|
11792
|
+
}
|
|
11793
|
+
} catch {
|
|
11794
|
+
}
|
|
11795
|
+
cache = { mtimeMs, enabled, rules: [...DEFAULT_REDACTION_RULES, ...extra] };
|
|
11796
|
+
return cache;
|
|
11797
|
+
}
|
|
11798
|
+
function compileServerRule(spec) {
|
|
11799
|
+
const name = spec.name || "(unnamed)";
|
|
11800
|
+
if (!spec.regex)
|
|
11801
|
+
return { name, reason: "empty pattern" };
|
|
11802
|
+
if (spec.regex.length > MAX_PATTERN_LEN)
|
|
11803
|
+
return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
|
|
11804
|
+
let re;
|
|
11805
|
+
try {
|
|
11806
|
+
const flags = "g" + (spec.flags || "").replace(/[^ims]/g, "");
|
|
11807
|
+
re = new RegExp(spec.regex, flags);
|
|
11808
|
+
} catch (e) {
|
|
11809
|
+
return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
|
|
11810
|
+
}
|
|
11811
|
+
if (re.test(""))
|
|
11812
|
+
return { name, reason: "matches the empty string (would redact everything)" };
|
|
11813
|
+
re.lastIndex = 0;
|
|
11814
|
+
if (re.test(CANARY))
|
|
11815
|
+
return { name, reason: "matches benign text \u2014 too broad" };
|
|
11816
|
+
re.lastIndex = 0;
|
|
11817
|
+
return { label: `${spec.source === "pack" ? "pack" : "tenant"}:${name}`, pattern: re };
|
|
11818
|
+
}
|
|
11819
|
+
function installServerRulePack(pack) {
|
|
11820
|
+
const rejected = [];
|
|
11821
|
+
const rules = [];
|
|
11822
|
+
for (const spec of pack.rules || []) {
|
|
11823
|
+
if (rules.length >= MAX_PACK_RULES) {
|
|
11824
|
+
rejected.push({ name: spec.name || "(unnamed)", reason: `pack exceeds ${MAX_PACK_RULES} rules` });
|
|
11825
|
+
continue;
|
|
11826
|
+
}
|
|
11827
|
+
const out = compileServerRule(spec);
|
|
11828
|
+
if ("reason" in out)
|
|
11829
|
+
rejected.push(out);
|
|
11830
|
+
else
|
|
11831
|
+
rules.push(out);
|
|
11832
|
+
}
|
|
11833
|
+
const version = pack.version || "";
|
|
11834
|
+
serverPack = { version, rules };
|
|
11835
|
+
return { version, accepted: rules.length, rejected };
|
|
11836
|
+
}
|
|
11837
|
+
function serverRulePackVersion() {
|
|
11838
|
+
return serverPack ? serverPack.version || "unversioned" : null;
|
|
11839
|
+
}
|
|
11840
|
+
function activeRules() {
|
|
11841
|
+
const base2 = settingsView().rules;
|
|
11842
|
+
return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
|
|
11843
|
+
}
|
|
11844
|
+
function isRedactionEnabled() {
|
|
11845
|
+
const env = isEnvRedactionRequested();
|
|
11846
|
+
if (env !== null)
|
|
11847
|
+
return env;
|
|
11848
|
+
return settingsView().enabled;
|
|
11849
|
+
}
|
|
11850
|
+
function redactSecrets(text, opts = {}) {
|
|
11851
|
+
if (!opts.force && !isRedactionEnabled())
|
|
11852
|
+
return text;
|
|
11853
|
+
if (!text)
|
|
11854
|
+
return text;
|
|
11855
|
+
const rules = opts.rules || activeRules();
|
|
11856
|
+
let out = text;
|
|
11857
|
+
for (const r of rules) {
|
|
11858
|
+
out = out.replace(r.pattern, (...args) => {
|
|
11859
|
+
const whole = args[0];
|
|
11860
|
+
const m = args.slice(0, args.length);
|
|
11861
|
+
const secret = secretOf(r, m);
|
|
11862
|
+
if (secret == null)
|
|
11863
|
+
return whole;
|
|
11864
|
+
if (opts.count)
|
|
11865
|
+
opts.count.redactions++;
|
|
11866
|
+
return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
|
|
11867
|
+
});
|
|
11868
|
+
}
|
|
11869
|
+
const ctx = findContextualSecrets(out);
|
|
11870
|
+
for (let i = ctx.length - 1; i >= 0; i--) {
|
|
11871
|
+
const { start, end } = ctx[i];
|
|
11872
|
+
if (opts.count)
|
|
11873
|
+
opts.count.redactions++;
|
|
11874
|
+
out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
|
|
11875
|
+
}
|
|
11876
|
+
return out;
|
|
11877
|
+
}
|
|
11878
|
+
function maskSecret(s) {
|
|
11879
|
+
if (!s)
|
|
11880
|
+
return "";
|
|
11881
|
+
if (s.length <= 4)
|
|
11882
|
+
return "*".repeat(s.length);
|
|
11883
|
+
return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
|
|
11884
|
+
}
|
|
11885
|
+
function globalRegexFor(pattern) {
|
|
11886
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
11887
|
+
const key2 = pattern.source + "\0" + flags;
|
|
11888
|
+
let re = globalRegexCache.get(key2);
|
|
11889
|
+
if (!re) {
|
|
11890
|
+
re = new RegExp(pattern.source, flags);
|
|
11891
|
+
globalRegexCache.set(key2, re);
|
|
11892
|
+
}
|
|
11893
|
+
return re;
|
|
11894
|
+
}
|
|
11895
|
+
function makeLineResolver(text) {
|
|
11896
|
+
const starts = [];
|
|
11897
|
+
for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1))
|
|
11898
|
+
starts.push(i);
|
|
11899
|
+
return (index) => {
|
|
11900
|
+
let lo = 0, hi = starts.length;
|
|
11901
|
+
while (lo < hi) {
|
|
11902
|
+
const mid = lo + hi >> 1;
|
|
11903
|
+
if (starts[mid] < index)
|
|
11904
|
+
lo = mid + 1;
|
|
11905
|
+
else
|
|
11906
|
+
hi = mid;
|
|
11907
|
+
}
|
|
11908
|
+
return lo + 1;
|
|
11909
|
+
};
|
|
11910
|
+
}
|
|
11911
|
+
function scanTextForFindings(text, opts = {}) {
|
|
11912
|
+
if (!text)
|
|
11913
|
+
return [];
|
|
11914
|
+
const rules = opts.rules || activeRules();
|
|
11915
|
+
const findings = [];
|
|
11916
|
+
const seenSpans = /* @__PURE__ */ new Set();
|
|
11917
|
+
const lineAt = makeLineResolver(text);
|
|
11918
|
+
for (const r of rules) {
|
|
11919
|
+
const re = globalRegexFor(r.pattern);
|
|
11920
|
+
re.lastIndex = 0;
|
|
11921
|
+
let m;
|
|
11922
|
+
while ((m = re.exec(text)) !== null) {
|
|
11923
|
+
const secret = secretOf(r, m);
|
|
11924
|
+
if (secret == null) {
|
|
11925
|
+
if (re.lastIndex === m.index)
|
|
11926
|
+
re.lastIndex++;
|
|
11927
|
+
continue;
|
|
11928
|
+
}
|
|
11929
|
+
const at = m.index + (m[0].length - secret.length);
|
|
11930
|
+
const span = `${at}:${secret.length}`;
|
|
11931
|
+
if (!seenSpans.has(span)) {
|
|
11932
|
+
seenSpans.add(span);
|
|
11933
|
+
findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
|
|
11934
|
+
}
|
|
11935
|
+
if (re.lastIndex === m.index)
|
|
11936
|
+
re.lastIndex++;
|
|
11937
|
+
}
|
|
12398
11938
|
}
|
|
12399
|
-
})
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
const server = (env.CHAT_RECALL_SERVER || "").trim();
|
|
12405
|
-
if (!token || !server)
|
|
12406
|
-
return null;
|
|
12407
|
-
if (!/^https?:\/\//i.test(server))
|
|
12408
|
-
return null;
|
|
12409
|
-
return { serverUrl: normalise(server), token };
|
|
11939
|
+
for (const { start, end } of findContextualSecrets(text)) {
|
|
11940
|
+
const line = lineAt(start);
|
|
11941
|
+
findings.push({ rule: "secret-context", line, preview: maskSecret(text.slice(start, end)) });
|
|
11942
|
+
}
|
|
11943
|
+
return findings;
|
|
12410
11944
|
}
|
|
12411
|
-
var
|
|
12412
|
-
var
|
|
12413
|
-
"../engine/dist/core/
|
|
11945
|
+
var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache;
|
|
11946
|
+
var init_secret_redactor = __esm({
|
|
11947
|
+
"../engine/dist/core/secret-redactor.js"() {
|
|
12414
11948
|
"use strict";
|
|
12415
|
-
|
|
11949
|
+
init_settings();
|
|
11950
|
+
DEFAULT_REDACTION_RULES = [
|
|
11951
|
+
// AWS access tokens. Prefix-anchored: AKIA, ASIA, ABIA (long-term,
|
|
11952
|
+
// STS, EC2). Always exactly 16 hex/alphanumeric after the prefix.
|
|
11953
|
+
// NOTE: this matches only the access-key-ID, NOT the secret key value —
|
|
11954
|
+
// the `env-secret` rule below covers AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN.
|
|
11955
|
+
{ label: "aws-access-token", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
|
|
11956
|
+
// AWS STS session tokens. These are long base64 blobs with NO `NAME=` context
|
|
11957
|
+
// when pasted bare into prose/code, so `env-secret` misses them — but they
|
|
11958
|
+
// reliably start with one of a handful of base64 prefixes (the encoded
|
|
11959
|
+
// `{"...` JSON header). Prefix-anchored → low false-positive. This is the
|
|
11960
|
+
// bare-value form that leaked (`search('IQoJEXAMPLEfakeSESSIONtokenNOTreal01234567890abcDEF==...')`).
|
|
11961
|
+
{ label: "aws-session-token", pattern: /\b(?:IQoJ|FwoG|FwoH|FQoG|FQoH|FwoD)[A-Za-z0-9/+]{40,}={0,2}/g },
|
|
11962
|
+
// Env/shell-assignment secrets — a VAR whose name ends in KEY/SECRET/TOKEN/
|
|
11963
|
+
// PASSWORD/CREDENTIAL, then `=`/`:`, then the value. Zero-width lookbehind so
|
|
11964
|
+
// only the VALUE is redacted (the var name stays for context). This is the
|
|
11965
|
+
// dominant real-world leak vector (`export` dumps, .env files, `curl -H`,
|
|
11966
|
+
// tool output) and is exactly what leaked AWS_SECRET_ACCESS_KEY and
|
|
11967
|
+
// AWS_SESSION_TOKEN in cleartext while the access-key-ID rule above caught
|
|
11968
|
+
// only the (least-sensitive) ID.
|
|
11969
|
+
// Suffix list deliberately broad: DB_PASS (our own helm chart's name!),
|
|
11970
|
+
// NPM_AUTH, SENTRY_DSN, SESSION/COOKIE secrets and BEARER vars all leaked
|
|
11971
|
+
// past the original KEY/SECRET/TOKEN/PASSWORD-only list. Values under 12
|
|
11972
|
+
// chars don't redact, so flag-ish vars (BYPASS=true) stay readable.
|
|
11973
|
+
// Context CONSUMED, secret in group 1 — see RedactionRule.secret. As a
|
|
11974
|
+
// lookbehind this was the second most expensive rule in the collector.
|
|
11975
|
+
{ 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 },
|
|
11976
|
+
// Credentials embedded in a connection URL: scheme://user:PASSWORD@host.
|
|
11977
|
+
// Redacts only the password segment (keeps scheme/user/host for context).
|
|
11978
|
+
//
|
|
11979
|
+
// Scheme-agnostic on purpose. The old rule allowlisted eight DB schemes, so
|
|
11980
|
+
// `https://user:pass@host`, `ssh://`, `ftp://`, `smtp://` and every other
|
|
11981
|
+
// userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
|
|
11982
|
+
// prefix broke the \b anchor for the one family it did cover. Any scheme
|
|
11983
|
+
// followed by userinfo is a credential, so match the shape, not a list.
|
|
11984
|
+
{ label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
|
|
11985
|
+
// Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
|
|
11986
|
+
// containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
|
|
11987
|
+
// requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
|
|
11988
|
+
// excludes longer blobs/tokens. It catches the value when it appears bare in
|
|
11989
|
+
// prose with NO `NAME=` and NO nearby context word (e.g. inside a markdown
|
|
11990
|
+
// table or quoted in analysis) — the residual leak path the env-secret and
|
|
11991
|
+
// contextual rules both miss. Placed AFTER env-secret/url-password so the
|
|
11992
|
+
// keyed/URL forms keep their more-specific labels.
|
|
11993
|
+
// The three stacked lookaheads each re-scanned the same 40 characters, which
|
|
11994
|
+
// made this the MOST expensive rule of all 106 the collector runs (13.1% of a
|
|
11995
|
+
// 29.5 MB scan). The mixed-case-plus-digit test is now one JS pass.
|
|
11996
|
+
{
|
|
11997
|
+
label: "aws-secret-key",
|
|
11998
|
+
secret: 1,
|
|
11999
|
+
pattern: /(?<![A-Za-z0-9/+])([A-Za-z0-9/+]{40})(?![A-Za-z0-9/+])/g,
|
|
12000
|
+
where: (v) => /[a-z]/.test(v) && /[A-Z]/.test(v) && /[0-9]/.test(v)
|
|
12001
|
+
},
|
|
12002
|
+
// GitHub personal access tokens.
|
|
12003
|
+
{ label: "github-pat", pattern: /\bghp_[A-Za-z0-9]{36}\b/g },
|
|
12004
|
+
// GitHub fine-grained PATs.
|
|
12005
|
+
{ label: "github-fine", pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g },
|
|
12006
|
+
// GitHub OAuth.
|
|
12007
|
+
{ label: "github-oauth", pattern: /\bgho_[A-Za-z0-9]{36}\b/g },
|
|
12008
|
+
// GitHub app + refresh.
|
|
12009
|
+
{ label: "github-app", pattern: /\b(?:ghs|ghu|ghr)_[A-Za-z0-9]{36}\b/g },
|
|
12010
|
+
// Slack bot/user/app tokens.
|
|
12011
|
+
{ label: "slack-token", pattern: /\bxox[abopr]-[A-Za-z0-9-]{10,}\b/g },
|
|
12012
|
+
// Slack incoming webhook URL.
|
|
12013
|
+
{ label: "slack-webhook", pattern: /\bhttps:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+\b/g },
|
|
12014
|
+
// Stripe live + test keys.
|
|
12015
|
+
{ label: "stripe-key", pattern: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g },
|
|
12016
|
+
// Anthropic.
|
|
12017
|
+
{ label: "anthropic-key", pattern: /\bsk-ant-[a-zA-Z0-9_-]{40,}\b/g },
|
|
12018
|
+
// OpenAI. Two shapes, deliberately kept apart. The classic key is
|
|
12019
|
+
// `sk-` + base62 with NO hyphens; the modern scoped keys carry an explicit
|
|
12020
|
+
// proj/svcacct/admin segment first. Allowing hyphens after a bare `sk-`
|
|
12021
|
+
// (which is what admitted sk-proj- in the first place) also matches ordinary
|
|
12022
|
+
// kebab-case text — `sk-fix-the-login-redirect-bug`, `.sk-button-primary`,
|
|
12023
|
+
// `sk-2024-01-01-backup` — and this corpus is full of branch names and CSS
|
|
12024
|
+
// classes. Requiring the segment keeps sk-proj- coverage without that.
|
|
12025
|
+
{ label: "openai-key", pattern: /\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b|\bsk-[A-Za-z0-9]{20,}\b/g },
|
|
12026
|
+
// Google API key (AIza…).
|
|
12027
|
+
{ label: "google-api-key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
12028
|
+
// Generic JWT — three base64url segments.
|
|
12029
|
+
{ label: "jwt", pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
|
|
12030
|
+
// Private key blocks (PEM). The label is a free word run rather than a fixed
|
|
12031
|
+
// list, so ENCRYPTED PRIVATE KEY and PGP PRIVATE KEY BLOCK are covered
|
|
12032
|
+
// without another enumeration, and \1 forces END to name the same kind as
|
|
12033
|
+
// BEGIN.
|
|
12034
|
+
//
|
|
12035
|
+
// The body is [^-], NOT [\s\S]*?. That is a performance boundary, not a
|
|
12036
|
+
// style choice. An unanchored prefix with an unbounded lazy gap re-scans to
|
|
12037
|
+
// end-of-input from every candidate start, so text carrying many BEGIN
|
|
12038
|
+
// markers and no matching END costs O(n^2): 500KB of that measured 810ms
|
|
12039
|
+
// here, and this runs synchronously on the server's event loop over
|
|
12040
|
+
// tenant-supplied text (services/secret-rescan.ts). A PEM body is base64
|
|
12041
|
+
// plus headers and contains no '-', so excluding it makes the scan stop at
|
|
12042
|
+
// the next delimiter instead of at EOF — same matches, linear cost.
|
|
12043
|
+
{ label: "private-key", pattern: /-----BEGIN ([A-Z0-9 ]{0,32})PRIVATE KEY(?: BLOCK)?-----[^-]{0,10000}?-----END \1PRIVATE KEY(?: BLOCK)?-----/g },
|
|
12044
|
+
// PuTTY keys are not PEM. Match the Private-Lines payload — the actual key
|
|
12045
|
+
// material — rather than spanning the whole file, which would need the same
|
|
12046
|
+
// unbounded gap the rule above just removed. Line-anchored and base64-only,
|
|
12047
|
+
// so it cannot run away.
|
|
12048
|
+
{ label: "putty-key", pattern: /^Private-Lines:[ \t]*\d{1,4}[\r\n]+(?:[A-Za-z0-9+/=]{8,120}[\r\n]+){1,400}/gm },
|
|
12049
|
+
// npm tokens (uuid-shaped — overlap with our UUID filter; intentional).
|
|
12050
|
+
{ label: "npm-token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
12051
|
+
// .npmrc auth lines: //registry.npmjs.org/:_authToken=VALUE (the key is not
|
|
12052
|
+
// ALL-CAPS, so the env-secret rule above never matched it). The leading `:`
|
|
12053
|
+
// is required — it is what makes this npmrc syntax rather than any shell var
|
|
12054
|
+
// ending in _AUTH, which env-secret already owns and labels better.
|
|
12055
|
+
// [^\s"'`]+ so the closing quote is not swallowed, and a ${VAR} placeholder
|
|
12056
|
+
// is left alone — redacting it destroys a config example and hides nothing.
|
|
12057
|
+
// The rule the perf profile caught the wedged daemon inside: 98.94% of samples.
|
|
12058
|
+
{ label: "npmrc-auth", secret: 1, pattern: /:_(?:authToken|password|auth)[ \t]*=[ \t]*(?!\$\{)([^\s"'`]{12,})/gi },
|
|
12059
|
+
// HTTP Authorization headers. env-secret requires an ALL-CAPS name, and
|
|
12060
|
+
// `Authorization` is mixed case, so both Bearer and Basic walked straight
|
|
12061
|
+
// through — including short opaque tokens the contextual pass also misses
|
|
12062
|
+
// (it needs 32+ chars AND mixed-case-plus-digit).
|
|
12063
|
+
// Same two guards as npmrc-auth: stop at a quote, and skip the obvious
|
|
12064
|
+
// placeholders that fill documentation (<YOUR_TOKEN>, ${TOKEN}, xxx…).
|
|
12065
|
+
{ label: "auth-header", secret: 1, pattern: /\bauthorization[ \t]*:[ \t]*(?:bearer|basic|token)[ \t]+(?![<$])([^\s"'`<]{8,})/gi },
|
|
12066
|
+
// Vendor tokens with unambiguous prefixes. Each is a fixed literal, so a
|
|
12067
|
+
// false positive is essentially impossible and the cost of listing is low.
|
|
12068
|
+
{ label: "gitlab-pat", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
12069
|
+
{ label: "slack-app-token", pattern: /\bxapp-\d-[A-Z0-9]{8,}-\d{10,}-[a-f0-9]{32,}\b/g },
|
|
12070
|
+
{ label: "sendgrid-key", pattern: /\bSG\.[A-Za-z0-9_-]{20,24}\.[A-Za-z0-9_-]{40,50}\b/g },
|
|
12071
|
+
// (?<![-\w]) so `cache-key-<md5>` is not read as a mailgun key.
|
|
12072
|
+
{ label: "mailgun-key", pattern: /(?<![-\w])key-[a-f0-9]{32}\b/g },
|
|
12073
|
+
{ label: "digitalocean-pat", pattern: /\bdop_v1_[a-f0-9]{64}\b/g },
|
|
12074
|
+
{ label: "huggingface-token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g },
|
|
12075
|
+
{ label: "shopify-token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g },
|
|
12076
|
+
{ label: "pypi-token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}\b/g },
|
|
12077
|
+
// No twilio-sid rule: AC…/SK… are Twilio SIDs, which are public account and
|
|
12078
|
+
// key identifiers, not the auth token. Redacting them hid nothing and
|
|
12079
|
+
// collided with 32-hex etags and MD5 sums.
|
|
12080
|
+
// No discord-bot-token rule either: the shape (word.word.word) matched
|
|
12081
|
+
// ordinary dotted symbols such as a.b.ApplicationContextProviderImpl.
|
|
12082
|
+
{ label: "telegram-bot-token", pattern: /\b\d{8,10}:AA[A-Za-z0-9_-]{32,}\b/g },
|
|
12083
|
+
{ label: "doppler-token", pattern: /\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9_-]{20,}\b/g },
|
|
12084
|
+
// Azure Storage SAS signature — the `sig=` parameter is the actual secret.
|
|
12085
|
+
{ label: "azure-sas", secret: 1, pattern: /[?&]sig=([A-Za-z0-9%/+=]{20,})/g }
|
|
12086
|
+
];
|
|
12087
|
+
SECRET_CONTEXT_RE = /\b(?:secret|password|passwd|credential|access[ _-]?key|private[ _-]?key|api[ _-]?key|auth(?:oriz\w*)?|bearer|token)\b/gi;
|
|
12088
|
+
HIGH_ENTROPY_TOKEN_RE = /[A-Za-z0-9/+_-]{32,}/g;
|
|
12089
|
+
cache = null;
|
|
12090
|
+
CANARY = "the quick brown fox jumps over the lazy dog 0123456789 commit 8ba1f109551bd432803012645ac136ddd475d0fb src/index.ts:42";
|
|
12091
|
+
MAX_PACK_RULES = 200;
|
|
12092
|
+
MAX_PATTERN_LEN = 512;
|
|
12093
|
+
serverPack = null;
|
|
12094
|
+
globalRegexCache = /* @__PURE__ */ new Map();
|
|
12416
12095
|
}
|
|
12417
12096
|
});
|
|
12418
12097
|
|
|
@@ -14446,111 +14125,6 @@ var init_source_registry = __esm({
|
|
|
14446
14125
|
}
|
|
14447
14126
|
});
|
|
14448
14127
|
|
|
14449
|
-
// ../engine/dist/parsers/session-source.js
|
|
14450
|
-
var SessionSource;
|
|
14451
|
-
var init_session_source = __esm({
|
|
14452
|
-
"../engine/dist/parsers/session-source.js"() {
|
|
14453
|
-
"use strict";
|
|
14454
|
-
init_session();
|
|
14455
|
-
init_chunker();
|
|
14456
|
-
init_settings();
|
|
14457
|
-
SessionSource = class {
|
|
14458
|
-
sourceType = "session";
|
|
14459
|
-
claudeDir;
|
|
14460
|
-
constructor(claudeDir) {
|
|
14461
|
-
this.claudeDir = claudeDir;
|
|
14462
|
-
}
|
|
14463
|
-
async *discover() {
|
|
14464
|
-
if (!isSourceEnabled("claude", "sessions"))
|
|
14465
|
-
return;
|
|
14466
|
-
for (const [entry, sessionPath] of getAllSessions(this.claudeDir)) {
|
|
14467
|
-
yield this.entryToItem(entry, sessionPath);
|
|
14468
|
-
}
|
|
14469
|
-
}
|
|
14470
|
-
async parse(item) {
|
|
14471
|
-
const content = await parseSessionFile(item.filePath);
|
|
14472
|
-
const entry = this.itemToEntry(item);
|
|
14473
|
-
const sessionChunks = chunkSession(entry, content);
|
|
14474
|
-
item.extra = {
|
|
14475
|
-
...item.extra || {},
|
|
14476
|
-
...content.metadata
|
|
14477
|
-
};
|
|
14478
|
-
return sessionChunks.map((sc) => ({
|
|
14479
|
-
chunkId: sc.chunkId,
|
|
14480
|
-
itemId: sc.sessionId,
|
|
14481
|
-
sourceType: "session",
|
|
14482
|
-
// Empty title is preferable to "Session abc12345" placeholder text:
|
|
14483
|
-
// the latter pollutes search results and gets surfaced in the UI as
|
|
14484
|
-
// a misleading heading. Downstream renderers fall back to
|
|
14485
|
-
// project path / time when title is empty.
|
|
14486
|
-
title: sc.firstPrompt.slice(0, 100),
|
|
14487
|
-
text: sc.text,
|
|
14488
|
-
chunkType: sc.chunkType,
|
|
14489
|
-
projectPath: sc.projectPath,
|
|
14490
|
-
filePath: sc.sourceFile,
|
|
14491
|
-
mtime: sc.mtime
|
|
14492
|
-
}));
|
|
14493
|
-
}
|
|
14494
|
-
async extractLinks(item) {
|
|
14495
|
-
if (item.filePath) {
|
|
14496
|
-
try {
|
|
14497
|
-
const { detectForkPredecessor: detectForkPredecessor2 } = await Promise.resolve().then(() => (init_raw(), raw_exports));
|
|
14498
|
-
const predecessor = detectForkPredecessor2(item.filePath);
|
|
14499
|
-
if (predecessor) {
|
|
14500
|
-
return [{
|
|
14501
|
-
sourceType: "session",
|
|
14502
|
-
sourceId: item.id,
|
|
14503
|
-
targetType: "session",
|
|
14504
|
-
targetId: predecessor,
|
|
14505
|
-
linkType: "forked_from",
|
|
14506
|
-
confidence: 1
|
|
14507
|
-
}];
|
|
14508
|
-
}
|
|
14509
|
-
} catch {
|
|
14510
|
-
}
|
|
14511
|
-
}
|
|
14512
|
-
return [];
|
|
14513
|
-
}
|
|
14514
|
-
entryToItem(entry, sessionPath) {
|
|
14515
|
-
return {
|
|
14516
|
-
id: entry.sessionId,
|
|
14517
|
-
sourceType: "session",
|
|
14518
|
-
// Empty title when there's no first prompt — the UI then knows to
|
|
14519
|
-
// fall back to summary or project path instead of rendering the
|
|
14520
|
-
// useless "Session abc12345" placeholder as a heading.
|
|
14521
|
-
title: entry.firstPrompt ? entry.firstPrompt.replace(/\n/g, " ").trim().slice(0, 100) : "",
|
|
14522
|
-
projectPath: entry.projectPath,
|
|
14523
|
-
filePath: sessionPath,
|
|
14524
|
-
mtime: entry.fileMtime,
|
|
14525
|
-
contentPreview: entry.firstPrompt?.slice(0, 200),
|
|
14526
|
-
extra: {
|
|
14527
|
-
messageCount: entry.messageCount,
|
|
14528
|
-
gitBranch: entry.gitBranch,
|
|
14529
|
-
created: entry.created,
|
|
14530
|
-
modified: entry.modified,
|
|
14531
|
-
isSidechain: entry.isSidechain
|
|
14532
|
-
}
|
|
14533
|
-
};
|
|
14534
|
-
}
|
|
14535
|
-
itemToEntry(item) {
|
|
14536
|
-
const extra = item.extra || {};
|
|
14537
|
-
return {
|
|
14538
|
-
sessionId: item.id,
|
|
14539
|
-
fullPath: item.filePath,
|
|
14540
|
-
fileMtime: item.mtime,
|
|
14541
|
-
firstPrompt: item.contentPreview || "",
|
|
14542
|
-
messageCount: extra.messageCount || 0,
|
|
14543
|
-
created: extra.created || "",
|
|
14544
|
-
modified: extra.modified || "",
|
|
14545
|
-
gitBranch: extra.gitBranch || "",
|
|
14546
|
-
projectPath: item.projectPath,
|
|
14547
|
-
isSidechain: extra.isSidechain || false
|
|
14548
|
-
};
|
|
14549
|
-
}
|
|
14550
|
-
};
|
|
14551
|
-
}
|
|
14552
|
-
});
|
|
14553
|
-
|
|
14554
14128
|
// ../engine/dist/core/utils.js
|
|
14555
14129
|
import { existsSync as existsSync21, readdirSync as readdirSync15 } from "fs";
|
|
14556
14130
|
import { homedir as homedir6 } from "os";
|
|
@@ -17907,7 +17481,6 @@ var init_plugins_source = __esm({
|
|
|
17907
17481
|
|
|
17908
17482
|
// ../engine/dist/parsers/all-sources.js
|
|
17909
17483
|
function buildSourceRegistry(registry = new SourceRegistry()) {
|
|
17910
|
-
registry.register(new SessionSource());
|
|
17911
17484
|
registry.register(new PlanSource());
|
|
17912
17485
|
registry.register(new TaskSource());
|
|
17913
17486
|
registry.register(new ClaudeMdSource());
|
|
@@ -17931,7 +17504,6 @@ var init_all_sources = __esm({
|
|
|
17931
17504
|
"../engine/dist/parsers/all-sources.js"() {
|
|
17932
17505
|
"use strict";
|
|
17933
17506
|
init_source_registry();
|
|
17934
|
-
init_session_source();
|
|
17935
17507
|
init_plan_source();
|
|
17936
17508
|
init_task_source();
|
|
17937
17509
|
init_claude_md_source();
|
|
@@ -18416,7 +17988,7 @@ var init_http = __esm({
|
|
|
18416
17988
|
CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18417
17989
|
} catch {
|
|
18418
17990
|
try {
|
|
18419
|
-
if (true) CLI_VERSION = "0.
|
|
17991
|
+
if (true) CLI_VERSION = "0.7.0";
|
|
18420
17992
|
} catch {
|
|
18421
17993
|
}
|
|
18422
17994
|
}
|
|
@@ -18911,7 +18483,7 @@ var init_telemetry = __esm({
|
|
|
18911
18483
|
init_sync_client();
|
|
18912
18484
|
cliVersion = () => {
|
|
18913
18485
|
try {
|
|
18914
|
-
return true ? "0.
|
|
18486
|
+
return true ? "0.7.0" : "dev";
|
|
18915
18487
|
} catch {
|
|
18916
18488
|
return "dev";
|
|
18917
18489
|
}
|
|
@@ -20858,7 +20430,7 @@ var init_sync_client = __esm({
|
|
|
20858
20430
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
20859
20431
|
} catch {
|
|
20860
20432
|
try {
|
|
20861
|
-
if (true) ownVersion = "0.
|
|
20433
|
+
if (true) ownVersion = "0.7.0";
|
|
20862
20434
|
} catch {
|
|
20863
20435
|
}
|
|
20864
20436
|
}
|
|
@@ -23944,7 +23516,7 @@ try {
|
|
|
23944
23516
|
ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
23945
23517
|
} catch {
|
|
23946
23518
|
try {
|
|
23947
|
-
if (true) ownVersion2 = "0.
|
|
23519
|
+
if (true) ownVersion2 = "0.7.0";
|
|
23948
23520
|
} catch {
|
|
23949
23521
|
}
|
|
23950
23522
|
}
|
|
@@ -24292,7 +23864,7 @@ async function runCollectorMigration(opts) {
|
|
|
24292
23864
|
init_sync_ledger();
|
|
24293
23865
|
init_collector_health();
|
|
24294
23866
|
import { existsSync as existsSync49, readFileSync as readFileSync45, statSync as statSync44 } from "fs";
|
|
24295
|
-
var CLI_VERSION3 = true ? "0.
|
|
23867
|
+
var CLI_VERSION3 = true ? "0.7.0" : "0.0.0";
|
|
24296
23868
|
var DEBOUNCE_MS = 5e3;
|
|
24297
23869
|
var CLAUDE_DIR = claudeBackend.projectsDir();
|
|
24298
23870
|
var PLANS_DIR = claudeBackend.plansDir();
|