token-goat 2.6.34 → 2.6.36
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 +4 -2
- package/SECURITY.md +14 -11
- package/dist/{token-goat-chunk-L7QLEVO7.mjs → token-goat-chunk-65BKISIS.mjs} +16 -14
- package/dist/{token-goat-chunk-I6EQH27G.mjs → token-goat-chunk-CNDOJ3ZP.mjs} +2 -2
- package/dist/{token-goat-chunk-FRMEOIRU.mjs → token-goat-chunk-FRTBMRP7.mjs} +208 -13
- package/dist/{token-goat-chunk-IYYQOPY4.mjs → token-goat-chunk-IYTVE6KN.mjs} +29 -10
- package/dist/{token-goat-chunk-GXBPEQR2.mjs → token-goat-chunk-KYFJC37X.mjs} +21 -14
- package/dist/{token-goat-chunk-CKRATWCJ.mjs → token-goat-chunk-LN6OUHTV.mjs} +5 -5
- package/dist/{token-goat-chunk-WA2ER6TZ.mjs → token-goat-chunk-MGOUYAA2.mjs} +1 -1
- package/dist/{token-goat-chunk-CN7GUPX6.mjs → token-goat-chunk-UFOVM7ZN.mjs} +461 -97
- package/dist/{token-goat-chunk-7FWBSPCT.mjs → token-goat-chunk-V465YKOR.mjs} +198 -21
- package/dist/{token-goat-chunk-OA3GFKP2.mjs → token-goat-chunk-VYMGEVZS.mjs} +16 -4
- package/dist/{token-goat-hook-chunk-2ISZ4AZR.mjs → token-goat-hook-chunk-3ZDBWJDF.mjs} +1 -1
- package/dist/{token-goat-hook-chunk-6C5RN3CM.mjs → token-goat-hook-chunk-5UH54CW6.mjs} +16 -4
- package/dist/{token-goat-hook-chunk-OQML5EMA.mjs → token-goat-hook-chunk-6ODM3MP7.mjs} +16 -14
- package/dist/{token-goat-hook-chunk-6QTWMSRO.mjs → token-goat-hook-chunk-A77A26A7.mjs} +461 -97
- package/dist/{token-goat-hook-chunk-BU5OFDGQ.mjs → token-goat-hook-chunk-BDR6C6IE.mjs} +208 -13
- package/dist/{token-goat-hook-chunk-I5VM5PO5.mjs → token-goat-hook-chunk-C6GIABOX.mjs} +21 -14
- package/dist/{token-goat-hook-chunk-QTZ6YUMQ.mjs → token-goat-hook-chunk-E257IGSN.mjs} +2 -2
- package/dist/{token-goat-hook-chunk-KGLMNOGD.mjs → token-goat-hook-chunk-MW5HPEGD.mjs} +29 -10
- package/dist/{token-goat-hook-chunk-ATEGFQAU.mjs → token-goat-hook-chunk-QSCYNJ2B.mjs} +5 -5
- package/dist/{token-goat-hook-chunk-CNLMPHXT.mjs → token-goat-hook-chunk-Y2WHX2P3.mjs} +198 -21
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +13 -11
- package/scripts/install-git-hooks.mjs +55 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
function resolveVersion() {
|
|
10
10
|
if (true) {
|
|
11
|
-
return "2.6.
|
|
11
|
+
return "2.6.36";
|
|
12
12
|
}
|
|
13
13
|
const require2 = createRequire(import.meta.url);
|
|
14
14
|
const pkg = require2("../package.json");
|
|
@@ -770,6 +770,9 @@ function countContentLines(content) {
|
|
|
770
770
|
const n = content.split("\n").length;
|
|
771
771
|
return content.endsWith("\n") ? n - 1 : n;
|
|
772
772
|
}
|
|
773
|
+
function pushAll(target, items) {
|
|
774
|
+
for (const item of items) target.push(item);
|
|
775
|
+
}
|
|
773
776
|
function escapeRegExp(s) {
|
|
774
777
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
775
778
|
}
|
|
@@ -1196,6 +1199,9 @@ function getDisplayRoot(explicitRoot) {
|
|
|
1196
1199
|
_displayRootCache = { cwd, root };
|
|
1197
1200
|
return root;
|
|
1198
1201
|
}
|
|
1202
|
+
registerReset(() => {
|
|
1203
|
+
_displayRootCache = null;
|
|
1204
|
+
});
|
|
1199
1205
|
function isUnderSystemTemp(filePath) {
|
|
1200
1206
|
let sysTemp;
|
|
1201
1207
|
try {
|
|
@@ -2446,7 +2452,6 @@ var ENV_KEYS2 = [
|
|
|
2446
2452
|
"TOKEN_GOAT_OVERFLOW_MAX_TOKENS",
|
|
2447
2453
|
"TOKEN_GOAT_HINT_JSON_SIDECAR",
|
|
2448
2454
|
"TOKEN_GOAT_LARGE_READ_BYTES",
|
|
2449
|
-
"TOKEN_GOAT_CURATOR",
|
|
2450
2455
|
"TOKEN_GOAT_HINT_BUDGET",
|
|
2451
2456
|
"TOKEN_GOAT_HOOK_WATCHDOG_MS",
|
|
2452
2457
|
"TOKEN_GOAT_WEB_COMPRESS",
|
|
@@ -2507,8 +2512,19 @@ var _lastProjectConfigParseError = null;
|
|
|
2507
2512
|
function getLastProjectConfigParseError() {
|
|
2508
2513
|
return _lastProjectConfigParseError;
|
|
2509
2514
|
}
|
|
2510
|
-
var PROJECT_LOCKED_SECTIONS = [
|
|
2511
|
-
|
|
2515
|
+
var PROJECT_LOCKED_SECTIONS = [
|
|
2516
|
+
"injection",
|
|
2517
|
+
"webfetch",
|
|
2518
|
+
"gdrive",
|
|
2519
|
+
"mcp",
|
|
2520
|
+
"network",
|
|
2521
|
+
"screenshot"
|
|
2522
|
+
];
|
|
2523
|
+
var PROJECT_LOCKED_KEYS = [
|
|
2524
|
+
"image_shrink.max_image_pixels",
|
|
2525
|
+
"indexing.cross_project_symbols",
|
|
2526
|
+
"worker.blocked_roots"
|
|
2527
|
+
];
|
|
2512
2528
|
var _lastProjectConfigLockedKeys = [];
|
|
2513
2529
|
function stripLockedProjectKeys(projectRaw) {
|
|
2514
2530
|
const cleaned = {};
|
|
@@ -2639,10 +2655,12 @@ function resolveConfigKeyLayer(key, effectiveValue, cfg, projectInfo) {
|
|
|
2639
2655
|
if (rawValueEquals(rawValue, effectiveValue)) return { layer: "project", path: projectInfo.path };
|
|
2640
2656
|
return { layer: "project-invalid", path: projectInfo.path, rawValue, effectiveValue, reason: rejectionReason(key, rawValue, effectiveValue, cfg) };
|
|
2641
2657
|
}
|
|
2658
|
+
function readConfigSource(p) {
|
|
2659
|
+
return decodeSource(fs5.readFileSync(p));
|
|
2660
|
+
}
|
|
2642
2661
|
function readConfigToml(p) {
|
|
2643
2662
|
try {
|
|
2644
|
-
|
|
2645
|
-
return { raw: parse(text), parseError: null };
|
|
2663
|
+
return { raw: parse(readConfigSource(p)), parseError: null };
|
|
2646
2664
|
} catch (e) {
|
|
2647
2665
|
const code = e.code;
|
|
2648
2666
|
if (code === "ENOENT") return { raw: {}, parseError: null };
|
|
@@ -2651,7 +2669,7 @@ function readConfigToml(p) {
|
|
|
2651
2669
|
}
|
|
2652
2670
|
function readConfigText(p) {
|
|
2653
2671
|
try {
|
|
2654
|
-
return { text:
|
|
2672
|
+
return { text: readConfigSource(p), readError: null };
|
|
2655
2673
|
} catch (e) {
|
|
2656
2674
|
const code = e.code;
|
|
2657
2675
|
if (code === "ENOENT") return { text: null, readError: null };
|
|
@@ -2680,6 +2698,9 @@ function resolveConfigProjectRoot() {
|
|
|
2680
2698
|
_projectRootCache = { cwd, root };
|
|
2681
2699
|
return root;
|
|
2682
2700
|
}
|
|
2701
|
+
registerReset(() => {
|
|
2702
|
+
_projectRootCache = null;
|
|
2703
|
+
});
|
|
2683
2704
|
function isAutoTriggerMultiplierExplicit() {
|
|
2684
2705
|
const setsMultiplier = (text) => {
|
|
2685
2706
|
const raw = parse(text);
|
|
@@ -2690,11 +2711,11 @@ function isAutoTriggerMultiplierExplicit() {
|
|
|
2690
2711
|
return ca_raw["auto_trigger_multiplier"] !== void 0;
|
|
2691
2712
|
};
|
|
2692
2713
|
try {
|
|
2693
|
-
if (setsMultiplier(
|
|
2714
|
+
if (setsMultiplier(readConfigSource(configPath()))) return true;
|
|
2694
2715
|
} catch {
|
|
2695
2716
|
}
|
|
2696
2717
|
try {
|
|
2697
|
-
return setsMultiplier(
|
|
2718
|
+
return setsMultiplier(readConfigSource(projectConfigPath(resolveConfigProjectRoot())));
|
|
2698
2719
|
} catch {
|
|
2699
2720
|
return false;
|
|
2700
2721
|
}
|
|
@@ -4171,7 +4192,9 @@ function propagateEndLinesToSymbols(symbols, sections) {
|
|
|
4171
4192
|
return sym;
|
|
4172
4193
|
});
|
|
4173
4194
|
}
|
|
4174
|
-
function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex, lineCommentPrefix) {
|
|
4195
|
+
function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex, lineCommentPrefix, opts) {
|
|
4196
|
+
const block = opts?.blockComment;
|
|
4197
|
+
const backtick = opts?.backtickQuote === true;
|
|
4175
4198
|
let depth = 0;
|
|
4176
4199
|
let quote = null;
|
|
4177
4200
|
for (let i = openBraceIndex; i < content.length; i++) {
|
|
@@ -4184,11 +4207,16 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
|
|
|
4184
4207
|
if (ch === quote) quote = null;
|
|
4185
4208
|
continue;
|
|
4186
4209
|
}
|
|
4210
|
+
if (block !== void 0 && content.startsWith(block[0], i)) {
|
|
4211
|
+
const close = content.indexOf(block[1], i + block[0].length);
|
|
4212
|
+
i = close === -1 ? content.length : close + block[1].length - 1;
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4187
4215
|
if (lineCommentPrefix !== void 0 && content.startsWith(lineCommentPrefix, i)) {
|
|
4188
4216
|
while (i < content.length && content[i] !== "\n") i++;
|
|
4189
4217
|
continue;
|
|
4190
4218
|
}
|
|
4191
|
-
if (ch === '"' || ch === "'") {
|
|
4219
|
+
if (ch === '"' || ch === "'" || backtick && ch === "`") {
|
|
4192
4220
|
quote = ch;
|
|
4193
4221
|
continue;
|
|
4194
4222
|
}
|
|
@@ -4200,7 +4228,106 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
|
|
|
4200
4228
|
}
|
|
4201
4229
|
}
|
|
4202
4230
|
}
|
|
4203
|
-
return totalLines;
|
|
4231
|
+
return opts?.noMatchValue ?? totalLines;
|
|
4232
|
+
}
|
|
4233
|
+
function assignBraceBlockSpans(symbols, content, lineCommentPrefix) {
|
|
4234
|
+
if (symbols.length === 0) return [...symbols];
|
|
4235
|
+
const lines = content.split("\n");
|
|
4236
|
+
const totalLines = lines.length;
|
|
4237
|
+
const lineIndex = buildLineIndex(content);
|
|
4238
|
+
const starts = [...new Set(symbols.map((s) => s.lineStart))].sort((a, b) => a - b);
|
|
4239
|
+
const blockComment = lineCommentPrefix === "//" ? ["/*", "*/"] : lineCommentPrefix === "#" ? ["<#", "#>"] : void 0;
|
|
4240
|
+
return symbols.map((sym) => {
|
|
4241
|
+
if (sym.lineEnd !== sym.lineStart) return sym;
|
|
4242
|
+
const nextStart = starts.find((s) => s > sym.lineStart);
|
|
4243
|
+
const lastSearchLine = Math.min(nextStart !== void 0 ? nextStart - 1 : totalLines, sym.lineStart + BRACE_SEARCH_MAX_LINES);
|
|
4244
|
+
const openIndex = findBlockOpenBrace(content, lineIndex, sym.lineStart, lastSearchLine, lineCommentPrefix, blockComment);
|
|
4245
|
+
if (openIndex === null) return sym;
|
|
4246
|
+
const endLine = findMatchingBraceEndLine(
|
|
4247
|
+
content,
|
|
4248
|
+
openIndex,
|
|
4249
|
+
totalLines,
|
|
4250
|
+
lineIndex,
|
|
4251
|
+
lineCommentPrefix,
|
|
4252
|
+
blockComment === void 0 ? { noMatchValue: -1 } : { blockComment, noMatchValue: -1 }
|
|
4253
|
+
);
|
|
4254
|
+
if (endLine <= sym.lineStart) return sym;
|
|
4255
|
+
return { ...sym, lineEnd: endLine, body: lines.slice(sym.lineStart - 1, endLine).join("\n") };
|
|
4256
|
+
});
|
|
4257
|
+
}
|
|
4258
|
+
var BRACE_SEARCH_MAX_LINES = 10;
|
|
4259
|
+
function findBlockOpenBrace(content, lineIndex, startLine, lastSearchLine, lineCommentPrefix, blockComment) {
|
|
4260
|
+
const from = lineIndex[startLine - 1];
|
|
4261
|
+
if (from === void 0) return null;
|
|
4262
|
+
const to = lineIndex[lastSearchLine] ?? content.length;
|
|
4263
|
+
let quote = null;
|
|
4264
|
+
let parenDepth = 0;
|
|
4265
|
+
let linesSeen = 0;
|
|
4266
|
+
let atLineStart = false;
|
|
4267
|
+
for (let i = from; i < to; i++) {
|
|
4268
|
+
const ch = content[i];
|
|
4269
|
+
if (ch === void 0) break;
|
|
4270
|
+
if (quote !== null) {
|
|
4271
|
+
if (ch === "\\") {
|
|
4272
|
+
i++;
|
|
4273
|
+
continue;
|
|
4274
|
+
}
|
|
4275
|
+
if (ch === quote) quote = null;
|
|
4276
|
+
continue;
|
|
4277
|
+
}
|
|
4278
|
+
if (ch === "\n") {
|
|
4279
|
+
linesSeen++;
|
|
4280
|
+
atLineStart = true;
|
|
4281
|
+
continue;
|
|
4282
|
+
}
|
|
4283
|
+
if (blockComment !== void 0 && content.startsWith(blockComment[0], i)) {
|
|
4284
|
+
const close = content.indexOf(blockComment[1], i + blockComment[0].length);
|
|
4285
|
+
if (close === -1) return null;
|
|
4286
|
+
i = close + blockComment[1].length - 1;
|
|
4287
|
+
continue;
|
|
4288
|
+
}
|
|
4289
|
+
if (lineCommentPrefix !== void 0 && content.startsWith(lineCommentPrefix, i)) {
|
|
4290
|
+
while (i + 1 < to && content[i + 1] !== "\n") i++;
|
|
4291
|
+
continue;
|
|
4292
|
+
}
|
|
4293
|
+
if (atLineStart && !/\s/.test(ch)) {
|
|
4294
|
+
atLineStart = false;
|
|
4295
|
+
if (parenDepth === 0 && linesSeen >= 1 && startsWithBlockKeyword(content, i, to)) return null;
|
|
4296
|
+
}
|
|
4297
|
+
if (ch === '"' || ch === "'") {
|
|
4298
|
+
quote = ch;
|
|
4299
|
+
continue;
|
|
4300
|
+
}
|
|
4301
|
+
if (ch === "(" || ch === "[") parenDepth++;
|
|
4302
|
+
else if (ch === ")" || ch === "]") {
|
|
4303
|
+
if (parenDepth > 0) parenDepth--;
|
|
4304
|
+
} else if (ch === ";") return null;
|
|
4305
|
+
else if (ch === "{") return i;
|
|
4306
|
+
}
|
|
4307
|
+
return null;
|
|
4308
|
+
}
|
|
4309
|
+
var BLOCK_OPENING_KEYWORDS = /* @__PURE__ */ new Set([
|
|
4310
|
+
"if",
|
|
4311
|
+
"for",
|
|
4312
|
+
"while",
|
|
4313
|
+
"do",
|
|
4314
|
+
"switch",
|
|
4315
|
+
"when",
|
|
4316
|
+
"match",
|
|
4317
|
+
"try",
|
|
4318
|
+
"foreach",
|
|
4319
|
+
"loop",
|
|
4320
|
+
"guard",
|
|
4321
|
+
"repeat",
|
|
4322
|
+
"unless",
|
|
4323
|
+
"until"
|
|
4324
|
+
]);
|
|
4325
|
+
function startsWithBlockKeyword(content, i, to) {
|
|
4326
|
+
const first = content[i];
|
|
4327
|
+
if (first === void 0 || !/[A-Za-z_]/.test(first)) return false;
|
|
4328
|
+
let j = i + 1;
|
|
4329
|
+
while (j < to && /[A-Za-z0-9_]/.test(content[j])) j++;
|
|
4330
|
+
return BLOCK_OPENING_KEYWORDS.has(content.slice(i, j));
|
|
4204
4331
|
}
|
|
4205
4332
|
|
|
4206
4333
|
// src/languages/ini_idx.ts
|
|
@@ -4612,7 +4739,7 @@ function purgeDotenvEmbeddings(conn) {
|
|
|
4612
4739
|
var MIGRATIONS = {
|
|
4613
4740
|
// v1 -> v2: adds files.embed_sha, tracked separately from files.sha so embedding freshness can be gated independently of parse freshness (see makeIndexer in worker.ts). A pre-existing v1 database's `files` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has the column from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, so a genuine ALTER TABLE failure is never silently lost.
|
|
4614
4741
|
1: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN embed_sha TEXT"),
|
|
4615
|
-
// v2 -> v3: adds files.retry_count, a durable per-path counter for consecutive transient-read-failure requeues (see MAX_TRANSIENT_RETRIES / requeueDirtyPath /
|
|
4742
|
+
// v2 -> v3: adds files.retry_count, a durable per-path counter for consecutive transient-read-failure requeues (see MAX_TRANSIENT_RETRIES / requeueDirtyPath / clearRetryCount in worker.ts). Previously this counter lived only in an in-memory Map inside worker.ts, which meant the retry-count reset -- run at the time in the short-lived hook CLI process -- could never actually reach the long-lived detached daemon process's own copy of that Map: they are different Node processes with no shared memory, so the reset was a silent no-op in the real deployed topology. Persisting the counter in `files` makes it visible to both processes via the one thing they do share: the index DB. Same swallow-duplicate-column pattern as v1 -> v2 above.
|
|
4616
4743
|
2: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0"),
|
|
4617
4744
|
// v8 -> v9: adds symbols.parent (see SCHEMA_VERSION comment above for why). A pre-existing v8 database's `symbols` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has the column from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, same pattern as v1 -> v2 / v2 -> v3 above.
|
|
4618
4745
|
8: (conn) => alterTableIdempotent(conn, "ALTER TABLE symbols ADD COLUMN parent TEXT NOT NULL DEFAULT ''"),
|
|
@@ -4632,13 +4759,32 @@ function runMigrations(conn, fromVersion, toVersion) {
|
|
|
4632
4759
|
MIGRATIONS[v]?.(conn);
|
|
4633
4760
|
}
|
|
4634
4761
|
}
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4762
|
+
var WAL_SWITCH_DEADLINE_MS = 15e3;
|
|
4763
|
+
function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS) {
|
|
4764
|
+
const deadline = Date.now() + budgetMs;
|
|
4765
|
+
let lastError;
|
|
4766
|
+
for (; ; ) {
|
|
4767
|
+
try {
|
|
4768
|
+
const mode = conn.pragma("journal_mode = WAL", { simple: true });
|
|
4769
|
+
if (String(mode).toLowerCase() === "wal") return;
|
|
4770
|
+
lastError = new Error(`got: ${String(mode)}`);
|
|
4771
|
+
} catch (e) {
|
|
4772
|
+
lastError = e;
|
|
4773
|
+
}
|
|
4774
|
+
try {
|
|
4775
|
+
if (String(conn.pragma("journal_mode", { simple: true })).toLowerCase() === "wal") return;
|
|
4776
|
+
} catch {
|
|
4777
|
+
}
|
|
4778
|
+
if (Date.now() >= deadline) {
|
|
4779
|
+
throw new Error(`db: failed to enable WAL mode (${lastError instanceof Error ? lastError.message : String(lastError)})`);
|
|
4780
|
+
}
|
|
4781
|
+
sleepSync(25);
|
|
4639
4782
|
}
|
|
4640
|
-
|
|
4783
|
+
}
|
|
4784
|
+
function initConnection(conn) {
|
|
4641
4785
|
conn.pragma("busy_timeout = 15000");
|
|
4786
|
+
enableWalWithRetry(conn);
|
|
4787
|
+
conn.pragma("synchronous = NORMAL");
|
|
4642
4788
|
conn.function(
|
|
4643
4789
|
"TG_LOWER",
|
|
4644
4790
|
{ deterministic: true },
|
|
@@ -5983,13 +6129,26 @@ var SECRET_PATTERNS = [
|
|
|
5983
6129
|
// directly against the header name and the scheme directly against the space, so a body like
|
|
5984
6130
|
// {"Authorization": "Bearer <token>"} -- the shape any logged fetch or MCP result arrives in
|
|
5985
6131
|
// -- matched nothing and the token was cached verbatim.
|
|
5986
|
-
|
|
6132
|
+
// `token` is the second scheme spelling in wide use -- it is what curl and gh examples pass for
|
|
6133
|
+
// GitHub and many other APIs -- and an opaque value behind it carries exactly the same authority
|
|
6134
|
+
// as one behind `Bearer`. The trailing gap is `{1,8}` rather than a single space for the same
|
|
6135
|
+
// reason every other gap in this lookbehind already is: a hand-aligned or reformatted header
|
|
6136
|
+
// ("Authorization: Bearer <token>") is ordinary, and demanding exactly one space there made
|
|
6137
|
+
// this the one position in the pattern that a second space defeated.
|
|
6138
|
+
["auth_bearer_token", /(?<=Authorization["']?[ \t]{0,8}:[ \t]{0,8}["']?[ \t]{0,8}(?:Bearer|token)[ \t]{1,8})[A-Za-z0-9\-._~+/]{10,}=*/gi],
|
|
5987
6139
|
["auth_basic_token", /(?<=Authorization["']?[ \t]{0,8}:[ \t]{0,8}["']?[ \t]{0,8}Basic[ \t])[A-Za-z0-9+/]{6,}=*/gi],
|
|
5988
6140
|
// JWTs have no distinctive prefix of their own, but the base64url encoding of the smallest
|
|
5989
6141
|
// realistic header ('{"alg":' or similar) always starts with "eyJ", so that's the practical
|
|
5990
6142
|
// anchor here -- each of the three dot-separated segments requires a minimum length to avoid
|
|
5991
6143
|
// matching a short, coincidentally dotted token.
|
|
5992
|
-
|
|
6144
|
+
//
|
|
6145
|
+
// The trailing `={0,2}` on each segment is what makes a padded token match. Base64url as the JWT
|
|
6146
|
+
// spec defines it drops the `=` padding, but producers that reach for a plain base64 encoder
|
|
6147
|
+
// emit it anyway, and a `=` in the header or payload segment used to defeat the match outright:
|
|
6148
|
+
// not a partial redaction, but none at all, so the entire token was printed. `=` cannot appear
|
|
6149
|
+
// anywhere except the end of a segment, since it is not one of the characters the segment body
|
|
6150
|
+
// allows, so accepting it here cannot widen the match onto anything else.
|
|
6151
|
+
["jwt", /eyJ[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}/g],
|
|
5993
6152
|
["npm_token", /npm_[A-Za-z0-9]{36}/g],
|
|
5994
6153
|
// rk_live_ (restricted keys) share the sk_live_/sk_test_ secret-key shape and risk level, so
|
|
5995
6154
|
// one pattern covers all three rather than adding a near-duplicate entry.
|
|
@@ -6037,7 +6196,22 @@ var SECRET_PATTERNS = [
|
|
|
6037
6196
|
// SECRET_KEY=, and DB_PASSWORD_HASH= all passed through in full. That class matches
|
|
6038
6197
|
// identifier characters only, so prose that merely mentions a keyword still never reaches
|
|
6039
6198
|
// a separator and stays unredacted. api[_-]?key covers the apikey and api-key spellings too.
|
|
6040
|
-
|
|
6199
|
+
// `& ; # , :` play two incompatible roles. They separate one field from the next (a query
|
|
6200
|
+
// string, a cookie header, an inline env list), and they are also perfectly ordinary password
|
|
6201
|
+
// characters. Rejecting them outright got the first role right and the second badly wrong: the
|
|
6202
|
+
// match stopped at the first one and left everything after it in plain text, so
|
|
6203
|
+
// `password=corr&horse&battery` redacted four characters and printed the rest, and
|
|
6204
|
+
// `DB_PASSWORD=Aa1:xyz` matched nothing at all because the run before the `:` was under the
|
|
6205
|
+
// four-character floor. A tail left sitting in the open is the outcome this module's header
|
|
6206
|
+
// calls worse than no redaction, because it reads as handled.
|
|
6207
|
+
//
|
|
6208
|
+
// So the separator role is decided by what follows rather than assumed: one of these characters
|
|
6209
|
+
// ends the value only when the next thing along is another `name=` / `name:` pair, which is what
|
|
6210
|
+
// an actual field separator is always followed by. `,OTHER=public` and `; other=1` still end it;
|
|
6211
|
+
// the `&` in the middle of a passphrase does not. Whitespace, quotes and brackets are unchanged
|
|
6212
|
+
// -- they end a value unconditionally, which is also what keeps this pattern from re-matching
|
|
6213
|
+
// the `[REDACTED:...]` placeholder it just wrote.
|
|
6214
|
+
["generic_secret_assignment", /(?<=(?:password|passwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token)[a-z0-9_-]{0,64}["']?[ \t]{0,8}[:=][ \t]{0,8}["']?)(?:\\[^\n]|[^\s\\&;#,:'"[\]{}]|[&;#,:](?![ \t]*[A-Za-z_][A-Za-z0-9_.-]*[ \t]*[:=])){4,}/gi]
|
|
6041
6215
|
];
|
|
6042
6216
|
function redactSecrets(text) {
|
|
6043
6217
|
let count = 0;
|
|
@@ -6169,6 +6343,7 @@ export {
|
|
|
6169
6343
|
countNoun,
|
|
6170
6344
|
excludeTestsHiddenNote,
|
|
6171
6345
|
countContentLines,
|
|
6346
|
+
pushAll,
|
|
6172
6347
|
escapeRegExp,
|
|
6173
6348
|
windowsCmdQuoteArg,
|
|
6174
6349
|
redactUrlQuery,
|
|
@@ -6206,6 +6381,7 @@ export {
|
|
|
6206
6381
|
lastProjectConfigLockedKeys,
|
|
6207
6382
|
getProjectConfigInfo,
|
|
6208
6383
|
resolveConfigKeyLayer,
|
|
6384
|
+
readConfigSource,
|
|
6209
6385
|
isAutoTriggerMultiplierExplicit,
|
|
6210
6386
|
loadConfig,
|
|
6211
6387
|
invalidateConfigCache,
|
|
@@ -6234,6 +6410,7 @@ export {
|
|
|
6234
6410
|
assignFlatEndLines,
|
|
6235
6411
|
propagateEndLinesToSymbols,
|
|
6236
6412
|
findMatchingBraceEndLine,
|
|
6413
|
+
assignBraceBlockSpans,
|
|
6237
6414
|
extractIni,
|
|
6238
6415
|
_lineClosesQuote,
|
|
6239
6416
|
_detectOpenQuote,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
loadBlob,
|
|
7
7
|
sanitizeFtsQuery,
|
|
8
8
|
storeBlob
|
|
9
|
-
} from "./token-goat-chunk-
|
|
9
|
+
} from "./token-goat-chunk-UFOVM7ZN.mjs";
|
|
10
10
|
import {
|
|
11
11
|
SYMBOL_BODY_CHAR_CAP,
|
|
12
12
|
extractErrorMessage,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
redactSecrets,
|
|
17
17
|
runGit,
|
|
18
18
|
shortFingerprint
|
|
19
|
-
} from "./token-goat-chunk-
|
|
19
|
+
} from "./token-goat-chunk-V465YKOR.mjs";
|
|
20
20
|
import {
|
|
21
21
|
registerReset
|
|
22
22
|
} from "./token-goat-chunk-AO2QD2AG.mjs";
|
|
@@ -790,6 +790,7 @@ function formatCommandManifest(manifest) {
|
|
|
790
790
|
|
|
791
791
|
// src/stdin_json.ts
|
|
792
792
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
793
|
+
var MAX_STDIN_WALL_MS = 6e4;
|
|
793
794
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
794
795
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
795
796
|
return new Promise((resolve2, reject) => {
|
|
@@ -799,16 +800,27 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
799
800
|
const finish = (fn) => {
|
|
800
801
|
if (settled) return;
|
|
801
802
|
settled = true;
|
|
802
|
-
clearTimeout(
|
|
803
|
+
clearTimeout(idleTimer);
|
|
804
|
+
clearTimeout(wallTimer);
|
|
803
805
|
process.stdin.removeListener("data", onData);
|
|
804
806
|
process.stdin.removeListener("end", onEnd);
|
|
805
807
|
process.stdin.removeListener("error", onError);
|
|
806
808
|
fn();
|
|
807
809
|
};
|
|
808
|
-
|
|
810
|
+
let idleTimer = setTimeout(() => {
|
|
809
811
|
finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
|
|
810
812
|
}, timeoutMs);
|
|
813
|
+
const wallTimer = setTimeout(() => {
|
|
814
|
+
finish(() => {
|
|
815
|
+
process.stdin.destroy();
|
|
816
|
+
reject(new Error(`readStdinJson: stdin took longer than ${MAX_STDIN_WALL_MS} ms`));
|
|
817
|
+
});
|
|
818
|
+
}, MAX_STDIN_WALL_MS);
|
|
811
819
|
const onData = (chunk) => {
|
|
820
|
+
clearTimeout(idleTimer);
|
|
821
|
+
idleTimer = setTimeout(() => {
|
|
822
|
+
finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
|
|
823
|
+
}, timeoutMs);
|
|
812
824
|
totalBytes += chunk.length;
|
|
813
825
|
if (totalBytes > maxBytes) {
|
|
814
826
|
finish(() => {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
loadBlob,
|
|
7
7
|
sanitizeFtsQuery,
|
|
8
8
|
storeBlob
|
|
9
|
-
} from "./token-goat-hook-chunk-
|
|
9
|
+
} from "./token-goat-hook-chunk-A77A26A7.mjs";
|
|
10
10
|
import {
|
|
11
11
|
SYMBOL_BODY_CHAR_CAP,
|
|
12
12
|
extractErrorMessage,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
redactSecrets,
|
|
17
17
|
runGit,
|
|
18
18
|
shortFingerprint
|
|
19
|
-
} from "./token-goat-hook-chunk-
|
|
19
|
+
} from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
|
|
20
20
|
import {
|
|
21
21
|
registerReset
|
|
22
22
|
} from "./token-goat-hook-chunk-BUOCULAM.mjs";
|
|
@@ -183,6 +183,7 @@ var HOOK_EVENTS = [
|
|
|
183
183
|
|
|
184
184
|
// src/stdin_json.ts
|
|
185
185
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
186
|
+
var MAX_STDIN_WALL_MS = 6e4;
|
|
186
187
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
187
188
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
188
189
|
return new Promise((resolve2, reject) => {
|
|
@@ -192,16 +193,27 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
192
193
|
const finish = (fn) => {
|
|
193
194
|
if (settled) return;
|
|
194
195
|
settled = true;
|
|
195
|
-
clearTimeout(
|
|
196
|
+
clearTimeout(idleTimer);
|
|
197
|
+
clearTimeout(wallTimer);
|
|
196
198
|
process.stdin.removeListener("data", onData);
|
|
197
199
|
process.stdin.removeListener("end", onEnd);
|
|
198
200
|
process.stdin.removeListener("error", onError);
|
|
199
201
|
fn();
|
|
200
202
|
};
|
|
201
|
-
|
|
203
|
+
let idleTimer = setTimeout(() => {
|
|
202
204
|
finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
|
|
203
205
|
}, timeoutMs);
|
|
206
|
+
const wallTimer = setTimeout(() => {
|
|
207
|
+
finish(() => {
|
|
208
|
+
process.stdin.destroy();
|
|
209
|
+
reject(new Error(`readStdinJson: stdin took longer than ${MAX_STDIN_WALL_MS} ms`));
|
|
210
|
+
});
|
|
211
|
+
}, MAX_STDIN_WALL_MS);
|
|
204
212
|
const onData = (chunk) => {
|
|
213
|
+
clearTimeout(idleTimer);
|
|
214
|
+
idleTimer = setTimeout(() => {
|
|
215
|
+
finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
|
|
216
|
+
}, timeoutMs);
|
|
205
217
|
totalBytes += chunk.length;
|
|
206
218
|
if (totalBytes > maxBytes) {
|
|
207
219
|
finish(() => {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runSkeleton,
|
|
26
26
|
runSymbol,
|
|
27
27
|
withPinnedReads
|
|
28
|
-
} from "./token-goat-hook-chunk-
|
|
28
|
+
} from "./token-goat-hook-chunk-MW5HPEGD.mjs";
|
|
29
29
|
import {
|
|
30
30
|
buildProjectMap,
|
|
31
31
|
embeddingsDepsAvailable,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getProjectIndexCounts,
|
|
35
35
|
isWorkerRunning,
|
|
36
36
|
mapLookupBytesSaved
|
|
37
|
-
} from "./token-goat-hook-chunk-
|
|
37
|
+
} from "./token-goat-hook-chunk-A77A26A7.mjs";
|
|
38
38
|
import {
|
|
39
39
|
VERSION,
|
|
40
40
|
dataDir,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
normalizePath,
|
|
46
46
|
recordStat,
|
|
47
47
|
resolveProjectRoot
|
|
48
|
-
} from "./token-goat-hook-chunk-
|
|
48
|
+
} from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
|
|
49
49
|
import "./token-goat-hook-chunk-BUOCULAM.mjs";
|
|
50
50
|
import "./token-goat-hook-chunk-RFRLWOQH.mjs";
|
|
51
51
|
|
|
@@ -126,7 +126,19 @@ function forCompare(p) {
|
|
|
126
126
|
return process.platform === "win32" ? p.toLowerCase() : p;
|
|
127
127
|
}
|
|
128
128
|
function resolveToolRoot(projectRoot) {
|
|
129
|
-
|
|
129
|
+
const resolved = resolveProjectRoot(projectRoot !== void 0 ? { project: projectRoot } : {});
|
|
130
|
+
assertRootAllowed(resolved);
|
|
131
|
+
return resolved;
|
|
132
|
+
}
|
|
133
|
+
var RootNotAllowedError = class extends Error {
|
|
134
|
+
};
|
|
135
|
+
function assertRootAllowed(resolvedRoot) {
|
|
136
|
+
const allowedRoots = loadConfig().mcp.allowed_roots;
|
|
137
|
+
if (allowedRoots.length === 0) return;
|
|
138
|
+
if (allowedRoots.some((allowed) => checkWithinProjectRoot(resolvedRoot, allowed).inside)) return;
|
|
139
|
+
throw new RootNotAllowedError(
|
|
140
|
+
`refused: "${resolvedRoot}" is not inside any root listed in mcp.allowed_roots. A caller-supplied projectRoot is untrusted input, so this deployment pins which roots may be named; add the root to mcp.allowed_roots (or TOKEN_GOAT_MCP_ALLOWED_ROOTS) to permit it.`
|
|
141
|
+
);
|
|
130
142
|
}
|
|
131
143
|
function checkWithinProjectRoot(target, resolvedRoot) {
|
|
132
144
|
const root = forCompare(normalizePath(realPathOrSelf(resolvedRoot)));
|
|
@@ -150,16 +162,6 @@ function specFilePart(spec) {
|
|
|
150
162
|
var NO_PINS = /* @__PURE__ */ new Map();
|
|
151
163
|
function confineTargets(targets, resolvedRoot, splitCommas = true) {
|
|
152
164
|
if (!loadConfig(resolvedRoot).mcp.confine_reads_to_project_root) return { ok: true, targets, pins: NO_PINS };
|
|
153
|
-
const allowedRoots = loadConfig().mcp.allowed_roots;
|
|
154
|
-
if (allowedRoots.length > 0 && !allowedRoots.some((allowed) => checkWithinProjectRoot(resolvedRoot, allowed).inside)) {
|
|
155
|
-
return {
|
|
156
|
-
ok: false,
|
|
157
|
-
refusal: toCallToolResult({
|
|
158
|
-
text: `refused: "${resolvedRoot}" is not inside any root listed in mcp.allowed_roots. A caller-supplied projectRoot is untrusted input, so this deployment pins which roots may be named; add the root to mcp.allowed_roots (or TOKEN_GOAT_MCP_ALLOWED_ROOTS) to permit it.`,
|
|
159
|
-
code: 1
|
|
160
|
-
})
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
165
|
const checked = [];
|
|
164
166
|
const pins = /* @__PURE__ */ new Map();
|
|
165
167
|
for (const raw of targets) {
|