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");
|
|
@@ -742,6 +742,9 @@ function countContentLines(content) {
|
|
|
742
742
|
const n = content.split("\n").length;
|
|
743
743
|
return content.endsWith("\n") ? n - 1 : n;
|
|
744
744
|
}
|
|
745
|
+
function pushAll(target, items) {
|
|
746
|
+
for (const item of items) target.push(item);
|
|
747
|
+
}
|
|
745
748
|
function escapeRegExp(s) {
|
|
746
749
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
747
750
|
}
|
|
@@ -1183,6 +1186,9 @@ function getDisplayRoot(explicitRoot) {
|
|
|
1183
1186
|
_displayRootCache = { cwd, root };
|
|
1184
1187
|
return root;
|
|
1185
1188
|
}
|
|
1189
|
+
registerReset(() => {
|
|
1190
|
+
_displayRootCache = null;
|
|
1191
|
+
});
|
|
1186
1192
|
function isUnderSystemTemp(filePath) {
|
|
1187
1193
|
let sysTemp;
|
|
1188
1194
|
try {
|
|
@@ -2433,7 +2439,6 @@ var ENV_KEYS2 = [
|
|
|
2433
2439
|
"TOKEN_GOAT_OVERFLOW_MAX_TOKENS",
|
|
2434
2440
|
"TOKEN_GOAT_HINT_JSON_SIDECAR",
|
|
2435
2441
|
"TOKEN_GOAT_LARGE_READ_BYTES",
|
|
2436
|
-
"TOKEN_GOAT_CURATOR",
|
|
2437
2442
|
"TOKEN_GOAT_HINT_BUDGET",
|
|
2438
2443
|
"TOKEN_GOAT_HOOK_WATCHDOG_MS",
|
|
2439
2444
|
"TOKEN_GOAT_WEB_COMPRESS",
|
|
@@ -2494,8 +2499,19 @@ var _lastProjectConfigParseError = null;
|
|
|
2494
2499
|
function getLastProjectConfigParseError() {
|
|
2495
2500
|
return _lastProjectConfigParseError;
|
|
2496
2501
|
}
|
|
2497
|
-
var PROJECT_LOCKED_SECTIONS = [
|
|
2498
|
-
|
|
2502
|
+
var PROJECT_LOCKED_SECTIONS = [
|
|
2503
|
+
"injection",
|
|
2504
|
+
"webfetch",
|
|
2505
|
+
"gdrive",
|
|
2506
|
+
"mcp",
|
|
2507
|
+
"network",
|
|
2508
|
+
"screenshot"
|
|
2509
|
+
];
|
|
2510
|
+
var PROJECT_LOCKED_KEYS = [
|
|
2511
|
+
"image_shrink.max_image_pixels",
|
|
2512
|
+
"indexing.cross_project_symbols",
|
|
2513
|
+
"worker.blocked_roots"
|
|
2514
|
+
];
|
|
2499
2515
|
var _lastProjectConfigLockedKeys = [];
|
|
2500
2516
|
function stripLockedProjectKeys(projectRaw) {
|
|
2501
2517
|
const cleaned = {};
|
|
@@ -2626,10 +2642,12 @@ function resolveConfigKeyLayer(key, effectiveValue, cfg, projectInfo) {
|
|
|
2626
2642
|
if (rawValueEquals(rawValue, effectiveValue)) return { layer: "project", path: projectInfo.path };
|
|
2627
2643
|
return { layer: "project-invalid", path: projectInfo.path, rawValue, effectiveValue, reason: rejectionReason(key, rawValue, effectiveValue, cfg) };
|
|
2628
2644
|
}
|
|
2645
|
+
function readConfigSource(p) {
|
|
2646
|
+
return decodeSource(fs5.readFileSync(p));
|
|
2647
|
+
}
|
|
2629
2648
|
function readConfigToml(p) {
|
|
2630
2649
|
try {
|
|
2631
|
-
|
|
2632
|
-
return { raw: parse(text), parseError: null };
|
|
2650
|
+
return { raw: parse(readConfigSource(p)), parseError: null };
|
|
2633
2651
|
} catch (e) {
|
|
2634
2652
|
const code = e.code;
|
|
2635
2653
|
if (code === "ENOENT") return { raw: {}, parseError: null };
|
|
@@ -2638,7 +2656,7 @@ function readConfigToml(p) {
|
|
|
2638
2656
|
}
|
|
2639
2657
|
function readConfigText(p) {
|
|
2640
2658
|
try {
|
|
2641
|
-
return { text:
|
|
2659
|
+
return { text: readConfigSource(p), readError: null };
|
|
2642
2660
|
} catch (e) {
|
|
2643
2661
|
const code = e.code;
|
|
2644
2662
|
if (code === "ENOENT") return { text: null, readError: null };
|
|
@@ -2667,6 +2685,9 @@ function resolveConfigProjectRoot() {
|
|
|
2667
2685
|
_projectRootCache = { cwd, root };
|
|
2668
2686
|
return root;
|
|
2669
2687
|
}
|
|
2688
|
+
registerReset(() => {
|
|
2689
|
+
_projectRootCache = null;
|
|
2690
|
+
});
|
|
2670
2691
|
function isAutoTriggerMultiplierExplicit() {
|
|
2671
2692
|
const setsMultiplier = (text) => {
|
|
2672
2693
|
const raw = parse(text);
|
|
@@ -2677,11 +2698,11 @@ function isAutoTriggerMultiplierExplicit() {
|
|
|
2677
2698
|
return ca_raw["auto_trigger_multiplier"] !== void 0;
|
|
2678
2699
|
};
|
|
2679
2700
|
try {
|
|
2680
|
-
if (setsMultiplier(
|
|
2701
|
+
if (setsMultiplier(readConfigSource(configPath()))) return true;
|
|
2681
2702
|
} catch {
|
|
2682
2703
|
}
|
|
2683
2704
|
try {
|
|
2684
|
-
return setsMultiplier(
|
|
2705
|
+
return setsMultiplier(readConfigSource(projectConfigPath(resolveConfigProjectRoot())));
|
|
2685
2706
|
} catch {
|
|
2686
2707
|
return false;
|
|
2687
2708
|
}
|
|
@@ -4158,7 +4179,9 @@ function propagateEndLinesToSymbols(symbols, sections) {
|
|
|
4158
4179
|
return sym;
|
|
4159
4180
|
});
|
|
4160
4181
|
}
|
|
4161
|
-
function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex, lineCommentPrefix) {
|
|
4182
|
+
function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex, lineCommentPrefix, opts) {
|
|
4183
|
+
const block = opts?.blockComment;
|
|
4184
|
+
const backtick = opts?.backtickQuote === true;
|
|
4162
4185
|
let depth = 0;
|
|
4163
4186
|
let quote = null;
|
|
4164
4187
|
for (let i = openBraceIndex; i < content.length; i++) {
|
|
@@ -4171,11 +4194,16 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
|
|
|
4171
4194
|
if (ch === quote) quote = null;
|
|
4172
4195
|
continue;
|
|
4173
4196
|
}
|
|
4197
|
+
if (block !== void 0 && content.startsWith(block[0], i)) {
|
|
4198
|
+
const close = content.indexOf(block[1], i + block[0].length);
|
|
4199
|
+
i = close === -1 ? content.length : close + block[1].length - 1;
|
|
4200
|
+
continue;
|
|
4201
|
+
}
|
|
4174
4202
|
if (lineCommentPrefix !== void 0 && content.startsWith(lineCommentPrefix, i)) {
|
|
4175
4203
|
while (i < content.length && content[i] !== "\n") i++;
|
|
4176
4204
|
continue;
|
|
4177
4205
|
}
|
|
4178
|
-
if (ch === '"' || ch === "'") {
|
|
4206
|
+
if (ch === '"' || ch === "'" || backtick && ch === "`") {
|
|
4179
4207
|
quote = ch;
|
|
4180
4208
|
continue;
|
|
4181
4209
|
}
|
|
@@ -4187,7 +4215,106 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
|
|
|
4187
4215
|
}
|
|
4188
4216
|
}
|
|
4189
4217
|
}
|
|
4190
|
-
return totalLines;
|
|
4218
|
+
return opts?.noMatchValue ?? totalLines;
|
|
4219
|
+
}
|
|
4220
|
+
function assignBraceBlockSpans(symbols, content, lineCommentPrefix) {
|
|
4221
|
+
if (symbols.length === 0) return [...symbols];
|
|
4222
|
+
const lines = content.split("\n");
|
|
4223
|
+
const totalLines = lines.length;
|
|
4224
|
+
const lineIndex = buildLineIndex(content);
|
|
4225
|
+
const starts = [...new Set(symbols.map((s) => s.lineStart))].sort((a, b) => a - b);
|
|
4226
|
+
const blockComment = lineCommentPrefix === "//" ? ["/*", "*/"] : lineCommentPrefix === "#" ? ["<#", "#>"] : void 0;
|
|
4227
|
+
return symbols.map((sym) => {
|
|
4228
|
+
if (sym.lineEnd !== sym.lineStart) return sym;
|
|
4229
|
+
const nextStart = starts.find((s) => s > sym.lineStart);
|
|
4230
|
+
const lastSearchLine = Math.min(nextStart !== void 0 ? nextStart - 1 : totalLines, sym.lineStart + BRACE_SEARCH_MAX_LINES);
|
|
4231
|
+
const openIndex = findBlockOpenBrace(content, lineIndex, sym.lineStart, lastSearchLine, lineCommentPrefix, blockComment);
|
|
4232
|
+
if (openIndex === null) return sym;
|
|
4233
|
+
const endLine = findMatchingBraceEndLine(
|
|
4234
|
+
content,
|
|
4235
|
+
openIndex,
|
|
4236
|
+
totalLines,
|
|
4237
|
+
lineIndex,
|
|
4238
|
+
lineCommentPrefix,
|
|
4239
|
+
blockComment === void 0 ? { noMatchValue: -1 } : { blockComment, noMatchValue: -1 }
|
|
4240
|
+
);
|
|
4241
|
+
if (endLine <= sym.lineStart) return sym;
|
|
4242
|
+
return { ...sym, lineEnd: endLine, body: lines.slice(sym.lineStart - 1, endLine).join("\n") };
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
var BRACE_SEARCH_MAX_LINES = 10;
|
|
4246
|
+
function findBlockOpenBrace(content, lineIndex, startLine, lastSearchLine, lineCommentPrefix, blockComment) {
|
|
4247
|
+
const from = lineIndex[startLine - 1];
|
|
4248
|
+
if (from === void 0) return null;
|
|
4249
|
+
const to = lineIndex[lastSearchLine] ?? content.length;
|
|
4250
|
+
let quote = null;
|
|
4251
|
+
let parenDepth = 0;
|
|
4252
|
+
let linesSeen = 0;
|
|
4253
|
+
let atLineStart = false;
|
|
4254
|
+
for (let i = from; i < to; i++) {
|
|
4255
|
+
const ch = content[i];
|
|
4256
|
+
if (ch === void 0) break;
|
|
4257
|
+
if (quote !== null) {
|
|
4258
|
+
if (ch === "\\") {
|
|
4259
|
+
i++;
|
|
4260
|
+
continue;
|
|
4261
|
+
}
|
|
4262
|
+
if (ch === quote) quote = null;
|
|
4263
|
+
continue;
|
|
4264
|
+
}
|
|
4265
|
+
if (ch === "\n") {
|
|
4266
|
+
linesSeen++;
|
|
4267
|
+
atLineStart = true;
|
|
4268
|
+
continue;
|
|
4269
|
+
}
|
|
4270
|
+
if (blockComment !== void 0 && content.startsWith(blockComment[0], i)) {
|
|
4271
|
+
const close = content.indexOf(blockComment[1], i + blockComment[0].length);
|
|
4272
|
+
if (close === -1) return null;
|
|
4273
|
+
i = close + blockComment[1].length - 1;
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
if (lineCommentPrefix !== void 0 && content.startsWith(lineCommentPrefix, i)) {
|
|
4277
|
+
while (i + 1 < to && content[i + 1] !== "\n") i++;
|
|
4278
|
+
continue;
|
|
4279
|
+
}
|
|
4280
|
+
if (atLineStart && !/\s/.test(ch)) {
|
|
4281
|
+
atLineStart = false;
|
|
4282
|
+
if (parenDepth === 0 && linesSeen >= 1 && startsWithBlockKeyword(content, i, to)) return null;
|
|
4283
|
+
}
|
|
4284
|
+
if (ch === '"' || ch === "'") {
|
|
4285
|
+
quote = ch;
|
|
4286
|
+
continue;
|
|
4287
|
+
}
|
|
4288
|
+
if (ch === "(" || ch === "[") parenDepth++;
|
|
4289
|
+
else if (ch === ")" || ch === "]") {
|
|
4290
|
+
if (parenDepth > 0) parenDepth--;
|
|
4291
|
+
} else if (ch === ";") return null;
|
|
4292
|
+
else if (ch === "{") return i;
|
|
4293
|
+
}
|
|
4294
|
+
return null;
|
|
4295
|
+
}
|
|
4296
|
+
var BLOCK_OPENING_KEYWORDS = /* @__PURE__ */ new Set([
|
|
4297
|
+
"if",
|
|
4298
|
+
"for",
|
|
4299
|
+
"while",
|
|
4300
|
+
"do",
|
|
4301
|
+
"switch",
|
|
4302
|
+
"when",
|
|
4303
|
+
"match",
|
|
4304
|
+
"try",
|
|
4305
|
+
"foreach",
|
|
4306
|
+
"loop",
|
|
4307
|
+
"guard",
|
|
4308
|
+
"repeat",
|
|
4309
|
+
"unless",
|
|
4310
|
+
"until"
|
|
4311
|
+
]);
|
|
4312
|
+
function startsWithBlockKeyword(content, i, to) {
|
|
4313
|
+
const first = content[i];
|
|
4314
|
+
if (first === void 0 || !/[A-Za-z_]/.test(first)) return false;
|
|
4315
|
+
let j = i + 1;
|
|
4316
|
+
while (j < to && /[A-Za-z0-9_]/.test(content[j])) j++;
|
|
4317
|
+
return BLOCK_OPENING_KEYWORDS.has(content.slice(i, j));
|
|
4191
4318
|
}
|
|
4192
4319
|
|
|
4193
4320
|
// src/languages/ini_idx.ts
|
|
@@ -4599,7 +4726,7 @@ function purgeDotenvEmbeddings(conn) {
|
|
|
4599
4726
|
var MIGRATIONS = {
|
|
4600
4727
|
// 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.
|
|
4601
4728
|
1: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN embed_sha TEXT"),
|
|
4602
|
-
// v2 -> v3: adds files.retry_count, a durable per-path counter for consecutive transient-read-failure requeues (see MAX_TRANSIENT_RETRIES / requeueDirtyPath /
|
|
4729
|
+
// 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.
|
|
4603
4730
|
2: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0"),
|
|
4604
4731
|
// 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.
|
|
4605
4732
|
8: (conn) => alterTableIdempotent(conn, "ALTER TABLE symbols ADD COLUMN parent TEXT NOT NULL DEFAULT ''"),
|
|
@@ -4619,13 +4746,32 @@ function runMigrations(conn, fromVersion, toVersion) {
|
|
|
4619
4746
|
MIGRATIONS[v]?.(conn);
|
|
4620
4747
|
}
|
|
4621
4748
|
}
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4749
|
+
var WAL_SWITCH_DEADLINE_MS = 15e3;
|
|
4750
|
+
function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS) {
|
|
4751
|
+
const deadline = Date.now() + budgetMs;
|
|
4752
|
+
let lastError;
|
|
4753
|
+
for (; ; ) {
|
|
4754
|
+
try {
|
|
4755
|
+
const mode = conn.pragma("journal_mode = WAL", { simple: true });
|
|
4756
|
+
if (String(mode).toLowerCase() === "wal") return;
|
|
4757
|
+
lastError = new Error(`got: ${String(mode)}`);
|
|
4758
|
+
} catch (e) {
|
|
4759
|
+
lastError = e;
|
|
4760
|
+
}
|
|
4761
|
+
try {
|
|
4762
|
+
if (String(conn.pragma("journal_mode", { simple: true })).toLowerCase() === "wal") return;
|
|
4763
|
+
} catch {
|
|
4764
|
+
}
|
|
4765
|
+
if (Date.now() >= deadline) {
|
|
4766
|
+
throw new Error(`db: failed to enable WAL mode (${lastError instanceof Error ? lastError.message : String(lastError)})`);
|
|
4767
|
+
}
|
|
4768
|
+
sleepSync(25);
|
|
4626
4769
|
}
|
|
4627
|
-
|
|
4770
|
+
}
|
|
4771
|
+
function initConnection(conn) {
|
|
4628
4772
|
conn.pragma("busy_timeout = 15000");
|
|
4773
|
+
enableWalWithRetry(conn);
|
|
4774
|
+
conn.pragma("synchronous = NORMAL");
|
|
4629
4775
|
conn.function(
|
|
4630
4776
|
"TG_LOWER",
|
|
4631
4777
|
{ deterministic: true },
|
|
@@ -5970,13 +6116,26 @@ var SECRET_PATTERNS = [
|
|
|
5970
6116
|
// directly against the header name and the scheme directly against the space, so a body like
|
|
5971
6117
|
// {"Authorization": "Bearer <token>"} -- the shape any logged fetch or MCP result arrives in
|
|
5972
6118
|
// -- matched nothing and the token was cached verbatim.
|
|
5973
|
-
|
|
6119
|
+
// `token` is the second scheme spelling in wide use -- it is what curl and gh examples pass for
|
|
6120
|
+
// GitHub and many other APIs -- and an opaque value behind it carries exactly the same authority
|
|
6121
|
+
// as one behind `Bearer`. The trailing gap is `{1,8}` rather than a single space for the same
|
|
6122
|
+
// reason every other gap in this lookbehind already is: a hand-aligned or reformatted header
|
|
6123
|
+
// ("Authorization: Bearer <token>") is ordinary, and demanding exactly one space there made
|
|
6124
|
+
// this the one position in the pattern that a second space defeated.
|
|
6125
|
+
["auth_bearer_token", /(?<=Authorization["']?[ \t]{0,8}:[ \t]{0,8}["']?[ \t]{0,8}(?:Bearer|token)[ \t]{1,8})[A-Za-z0-9\-._~+/]{10,}=*/gi],
|
|
5974
6126
|
["auth_basic_token", /(?<=Authorization["']?[ \t]{0,8}:[ \t]{0,8}["']?[ \t]{0,8}Basic[ \t])[A-Za-z0-9+/]{6,}=*/gi],
|
|
5975
6127
|
// JWTs have no distinctive prefix of their own, but the base64url encoding of the smallest
|
|
5976
6128
|
// realistic header ('{"alg":' or similar) always starts with "eyJ", so that's the practical
|
|
5977
6129
|
// anchor here -- each of the three dot-separated segments requires a minimum length to avoid
|
|
5978
6130
|
// matching a short, coincidentally dotted token.
|
|
5979
|
-
|
|
6131
|
+
//
|
|
6132
|
+
// The trailing `={0,2}` on each segment is what makes a padded token match. Base64url as the JWT
|
|
6133
|
+
// spec defines it drops the `=` padding, but producers that reach for a plain base64 encoder
|
|
6134
|
+
// emit it anyway, and a `=` in the header or payload segment used to defeat the match outright:
|
|
6135
|
+
// not a partial redaction, but none at all, so the entire token was printed. `=` cannot appear
|
|
6136
|
+
// anywhere except the end of a segment, since it is not one of the characters the segment body
|
|
6137
|
+
// allows, so accepting it here cannot widen the match onto anything else.
|
|
6138
|
+
["jwt", /eyJ[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}/g],
|
|
5980
6139
|
["npm_token", /npm_[A-Za-z0-9]{36}/g],
|
|
5981
6140
|
// rk_live_ (restricted keys) share the sk_live_/sk_test_ secret-key shape and risk level, so
|
|
5982
6141
|
// one pattern covers all three rather than adding a near-duplicate entry.
|
|
@@ -6024,7 +6183,22 @@ var SECRET_PATTERNS = [
|
|
|
6024
6183
|
// SECRET_KEY=, and DB_PASSWORD_HASH= all passed through in full. That class matches
|
|
6025
6184
|
// identifier characters only, so prose that merely mentions a keyword still never reaches
|
|
6026
6185
|
// a separator and stays unredacted. api[_-]?key covers the apikey and api-key spellings too.
|
|
6027
|
-
|
|
6186
|
+
// `& ; # , :` play two incompatible roles. They separate one field from the next (a query
|
|
6187
|
+
// string, a cookie header, an inline env list), and they are also perfectly ordinary password
|
|
6188
|
+
// characters. Rejecting them outright got the first role right and the second badly wrong: the
|
|
6189
|
+
// match stopped at the first one and left everything after it in plain text, so
|
|
6190
|
+
// `password=corr&horse&battery` redacted four characters and printed the rest, and
|
|
6191
|
+
// `DB_PASSWORD=Aa1:xyz` matched nothing at all because the run before the `:` was under the
|
|
6192
|
+
// four-character floor. A tail left sitting in the open is the outcome this module's header
|
|
6193
|
+
// calls worse than no redaction, because it reads as handled.
|
|
6194
|
+
//
|
|
6195
|
+
// So the separator role is decided by what follows rather than assumed: one of these characters
|
|
6196
|
+
// ends the value only when the next thing along is another `name=` / `name:` pair, which is what
|
|
6197
|
+
// an actual field separator is always followed by. `,OTHER=public` and `; other=1` still end it;
|
|
6198
|
+
// the `&` in the middle of a passphrase does not. Whitespace, quotes and brackets are unchanged
|
|
6199
|
+
// -- they end a value unconditionally, which is also what keeps this pattern from re-matching
|
|
6200
|
+
// the `[REDACTED:...]` placeholder it just wrote.
|
|
6201
|
+
["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]
|
|
6028
6202
|
];
|
|
6029
6203
|
function redactSecrets(text) {
|
|
6030
6204
|
let count = 0;
|
|
@@ -6152,6 +6326,7 @@ export {
|
|
|
6152
6326
|
countNoun,
|
|
6153
6327
|
excludeTestsHiddenNote,
|
|
6154
6328
|
countContentLines,
|
|
6329
|
+
pushAll,
|
|
6155
6330
|
escapeRegExp,
|
|
6156
6331
|
windowsCmdQuoteArg,
|
|
6157
6332
|
redactUrlQuery,
|
|
@@ -6192,6 +6367,7 @@ export {
|
|
|
6192
6367
|
lastProjectConfigLockedKeys,
|
|
6193
6368
|
getProjectConfigInfo,
|
|
6194
6369
|
resolveConfigKeyLayer,
|
|
6370
|
+
readConfigSource,
|
|
6195
6371
|
isAutoTriggerMultiplierExplicit,
|
|
6196
6372
|
loadConfig,
|
|
6197
6373
|
invalidateConfigCache,
|
|
@@ -6221,6 +6397,7 @@ export {
|
|
|
6221
6397
|
assignFlatEndLines,
|
|
6222
6398
|
propagateEndLinesToSymbols,
|
|
6223
6399
|
findMatchingBraceEndLine,
|
|
6400
|
+
assignBraceBlockSpans,
|
|
6224
6401
|
extractIni,
|
|
6225
6402
|
_lineClosesQuote,
|
|
6226
6403
|
_detectOpenQuote,
|
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
relayInProcess
|
|
5
|
-
} from "./token-goat-hook-chunk-
|
|
6
|
-
import "./token-goat-hook-chunk-
|
|
7
|
-
import "./token-goat-hook-chunk-
|
|
8
|
-
import "./token-goat-hook-chunk-
|
|
9
|
-
import "./token-goat-hook-chunk-
|
|
5
|
+
} from "./token-goat-hook-chunk-BDR6C6IE.mjs";
|
|
6
|
+
import "./token-goat-hook-chunk-3ZDBWJDF.mjs";
|
|
7
|
+
import "./token-goat-hook-chunk-5UH54CW6.mjs";
|
|
8
|
+
import "./token-goat-hook-chunk-A77A26A7.mjs";
|
|
9
|
+
import "./token-goat-hook-chunk-Y2WHX2P3.mjs";
|
|
10
10
|
import "./token-goat-hook-chunk-BUOCULAM.mjs";
|
|
11
11
|
import "./token-goat-hook-chunk-RFRLWOQH.mjs";
|
|
12
12
|
export {
|
package/dist/token-goat.core.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
run
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-KYFJC37X.mjs";
|
|
6
|
+
import "./token-goat-chunk-IYTVE6KN.mjs";
|
|
7
|
+
import "./token-goat-chunk-VYMGEVZS.mjs";
|
|
8
|
+
import "./token-goat-chunk-UFOVM7ZN.mjs";
|
|
9
9
|
import {
|
|
10
10
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-V465YKOR.mjs";
|
|
12
12
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
13
13
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
14
14
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-goat",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.36",
|
|
4
4
|
"description": "Surgical token-reduction companion for Claude Code and other AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/token-goat.mjs",
|
|
7
7
|
"bin": {
|
|
8
|
-
"token-goat": "./dist/token-goat.mjs"
|
|
8
|
+
"token-goat": "./dist/token-goat.mjs",
|
|
9
|
+
"tg": "./dist/token-goat.mjs"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -50,7 +51,8 @@
|
|
|
50
51
|
"dist/",
|
|
51
52
|
"README.md",
|
|
52
53
|
"SECURITY.md",
|
|
53
|
-
"LICENSE"
|
|
54
|
+
"LICENSE",
|
|
55
|
+
"scripts/install-git-hooks.mjs"
|
|
54
56
|
],
|
|
55
57
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
56
58
|
"engines": {
|
|
@@ -58,30 +60,30 @@
|
|
|
58
60
|
},
|
|
59
61
|
"dependencies": {
|
|
60
62
|
"better-sqlite3": "^12.11.1",
|
|
61
|
-
"
|
|
62
|
-
"csv-parse": "^7.0.2",
|
|
63
|
-
"js-yaml": "^5.2.1",
|
|
64
|
-
"jsonc-parser": "^3.3.1",
|
|
65
|
-
"smol-toml": "^1.8.0",
|
|
66
|
-
"zod": "^4.4.3"
|
|
63
|
+
"jsonc-parser": "^3.3.1"
|
|
67
64
|
},
|
|
68
65
|
"devDependencies": {
|
|
69
66
|
"@eslint/js": "^10.0.1",
|
|
70
67
|
"@types/better-sqlite3": "^9.6.0",
|
|
71
68
|
"@types/node": "^26.2.0",
|
|
69
|
+
"commander": "^15.0.0",
|
|
70
|
+
"csv-parse": "^7.0.2",
|
|
72
71
|
"esbuild": "^0.28.2",
|
|
73
72
|
"eslint": "^10.8.1",
|
|
73
|
+
"exceljs": "^4.4.0",
|
|
74
74
|
"html-to-text": "^10.0.1",
|
|
75
|
+
"js-yaml": "^5.2.1",
|
|
75
76
|
"lefthook": "^2.1.10",
|
|
77
|
+
"smol-toml": "^1.8.0",
|
|
76
78
|
"tsx": "^4.23.12",
|
|
77
79
|
"typescript": "^6.0.3",
|
|
78
80
|
"typescript-eslint": "^8.67.0",
|
|
79
|
-
"vitest": "^4.1.11"
|
|
81
|
+
"vitest": "^4.1.11",
|
|
82
|
+
"zod": "^4.4.3"
|
|
80
83
|
},
|
|
81
84
|
"optionalDependencies": {
|
|
82
85
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
83
86
|
"@xenova/transformers": "^2.17.2",
|
|
84
|
-
"exceljs": "^4.4.0",
|
|
85
87
|
"fast-xml-parser": "^5.9.3",
|
|
86
88
|
"fflate": "^0.8.3",
|
|
87
89
|
"pdfjs-dist": "^6.1.200",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `prepare` hook: wire up lefthook's git hooks for a development checkout.
|
|
3
|
+
*
|
|
4
|
+
* Runs `lefthook install` when lefthook is actually present, and skips quietly when it isn't.
|
|
5
|
+
*
|
|
6
|
+
* The skip matters because npm runs `prepare` for more than the dev-checkout case it was added
|
|
7
|
+
* for. `npm install -g .` runs it in a context without this project's node_modules/.bin on PATH,
|
|
8
|
+
* so a bare `lefthook install` dies with "'lefthook' is not recognized" and fails the global
|
|
9
|
+
* install that CLAUDE.md prescribes as the dogfood step for every CLI/hook change. Installing
|
|
10
|
+
* token-goat as a dependency has the same shape: no devDependencies, and no git repo to hook.
|
|
11
|
+
*
|
|
12
|
+
* Skipping is deliberately conditioned on lefthook being *absent*, not on the command failing.
|
|
13
|
+
* A developer whose `lefthook install` genuinely breaks still gets a hard error, because these
|
|
14
|
+
* hooks were decorative once already -- configured in lefthook.yml but never installed, so every
|
|
15
|
+
* guard in it silently did nothing -- and swallowing failures here is how that recurs.
|
|
16
|
+
*/
|
|
17
|
+
import { spawnSync } from 'node:child_process'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
import path from 'node:path'
|
|
20
|
+
import fs from 'node:fs'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Absolute path to the lefthook CLI shim, or null when lefthook isn't installed here.
|
|
24
|
+
*
|
|
25
|
+
* Looks for npm's own `.bin` shim rather than resolving the package, because the shim is what
|
|
26
|
+
* npm creates for a devDependency and it is the exact thing missing in the contexts we skip.
|
|
27
|
+
* `.cmd` first: on Windows the extensionless file is a shell script Node can't spawn directly.
|
|
28
|
+
*/
|
|
29
|
+
function findLefthookBin(startDir) {
|
|
30
|
+
const binDir = path.join(startDir, 'node_modules', '.bin')
|
|
31
|
+
for (const name of ['lefthook.cmd', 'lefthook']) {
|
|
32
|
+
const candidate = path.join(binDir, name)
|
|
33
|
+
if (fs.existsSync(candidate)) return candidate
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
39
|
+
const bin = findLefthookBin(projectRoot)
|
|
40
|
+
|
|
41
|
+
if (bin === null) {
|
|
42
|
+
// Not a dev checkout with lefthook installed: a global install, a published-package install, or
|
|
43
|
+
// a pack/prepare temp dir. Nothing to wire up and nothing wrong -- exit 0 so the install works.
|
|
44
|
+
process.exit(0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// npm's Windows shim is a .cmd batch file, which Node refuses to spawn without a shell. Quote the
|
|
48
|
+
// path in that case, since `shell: true` re-parses the command string and the path can contain
|
|
49
|
+
// spaces (a checkout under "C:\Program Files", a user profile with a space in the name).
|
|
50
|
+
const needsShell = /\.(cmd|bat)$/i.test(bin)
|
|
51
|
+
const result = spawnSync(needsShell ? `"${bin}"` : bin, ['install'], {
|
|
52
|
+
stdio: 'inherit',
|
|
53
|
+
shell: needsShell,
|
|
54
|
+
})
|
|
55
|
+
process.exit(result.status ?? 1)
|