token-goat 2.9.9 → 2.9.11
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 +54 -11
- package/SECURITY.md +17 -19
- package/THIRD_PARTY_NOTICES.md +667 -0
- package/dist/token-goat-chunk-2ZUPQYLO.mjs +3728 -0
- package/dist/{token-goat-chunk-LQ3SGWIW.mjs → token-goat-chunk-3ZALKJ23.mjs} +69 -37
- package/dist/{token-goat-chunk-WBPVYAEO.mjs → token-goat-chunk-4NZUXC4F.mjs} +3 -2
- package/dist/{token-goat-chunk-KIEOFWLL.mjs → token-goat-chunk-AAEYU2U2.mjs} +5307 -6350
- package/dist/{token-goat-chunk-OBDTBOQA.mjs → token-goat-chunk-FDURVZQD.mjs} +10 -2
- package/dist/{token-goat-chunk-DRH4CVGF.mjs → token-goat-chunk-HDL77BN3.mjs} +11 -7
- package/dist/{token-goat-chunk-LYWIRYCF.mjs → token-goat-chunk-LHLQFGWQ.mjs} +1 -1
- package/dist/{token-goat-chunk-L6Q2XM5X.mjs → token-goat-chunk-LKOYKSID.mjs} +1275 -655
- package/dist/{token-goat-chunk-P5SQX2VK.mjs → token-goat-chunk-MKITQ5RY.mjs} +354 -180
- package/dist/{token-goat-chunk-PNMGVJ4C.mjs → token-goat-chunk-MRZ555B3.mjs} +2699 -2175
- package/dist/{token-goat-chunk-K5GKX6ND.mjs → token-goat-chunk-PJPFOOGM.mjs} +6 -5
- package/dist/{token-goat-chunk-FQD3OB5W.mjs → token-goat-chunk-QCQUIPIP.mjs} +6068 -1687
- package/dist/{token-goat-chunk-BYDDWQ2T.mjs → token-goat-chunk-SY7WTZMW.mjs} +5043 -7721
- package/dist/{token-goat-chunk-PXWBHSFB.mjs → token-goat-chunk-T6M7DAW3.mjs} +31 -4
- package/dist/{token-goat-chunk-QCHD2ENV.mjs → token-goat-chunk-V53Z47ZH.mjs} +85 -328
- package/dist/token-goat-chunk-Y5VKNLVD.mjs +5942 -0
- package/dist/token-goat-chunk-YCKCFYTM.mjs +3564 -0
- package/dist/token-goat-hook.mjs +6 -5
- package/dist/token-goat.core.mjs +8 -6
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +4 -1
- package/docs/install.md +131 -11
- package/docs/security.md +3 -1
- package/package.json +8 -3
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
runSkeleton,
|
|
26
26
|
runSymbol,
|
|
27
27
|
withPinnedReads
|
|
28
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-LKOYKSID.mjs";
|
|
29
|
+
import "./token-goat-chunk-2ZUPQYLO.mjs";
|
|
29
30
|
import {
|
|
30
31
|
buildProjectMap,
|
|
31
32
|
embeddingsDepsAvailable,
|
|
@@ -34,22 +35,26 @@ import {
|
|
|
34
35
|
getProjectIndexCounts,
|
|
35
36
|
isWorkerRunning,
|
|
36
37
|
mapLookupBytesSaved
|
|
37
|
-
} from "./token-goat-chunk-
|
|
38
|
+
} from "./token-goat-chunk-SY7WTZMW.mjs";
|
|
38
39
|
import {
|
|
39
|
-
ENV_KEYS,
|
|
40
|
-
VERSION,
|
|
41
|
-
dataDir,
|
|
42
40
|
envStrList,
|
|
43
|
-
extractErrorMessage,
|
|
44
41
|
getDb,
|
|
45
|
-
globalDbPath,
|
|
46
42
|
loadConfig,
|
|
47
|
-
normalizePath,
|
|
48
43
|
recordStat,
|
|
49
44
|
resolveProjectRoot,
|
|
50
45
|
savedTokensFromBytes
|
|
51
|
-
} from "./token-goat-chunk-
|
|
46
|
+
} from "./token-goat-chunk-AAEYU2U2.mjs";
|
|
52
47
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
48
|
+
import {
|
|
49
|
+
ENV_KEYS,
|
|
50
|
+
VERSION,
|
|
51
|
+
dataDir,
|
|
52
|
+
displaySafeJson,
|
|
53
|
+
extractErrorMessage,
|
|
54
|
+
foldCaseForContainment,
|
|
55
|
+
globalDbPath,
|
|
56
|
+
normalizePath
|
|
57
|
+
} from "./token-goat-chunk-YCKCFYTM.mjs";
|
|
53
58
|
import {
|
|
54
59
|
init_define_import_meta_env
|
|
55
60
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -121,15 +126,16 @@ function toCallToolResultFromExitCode(fn) {
|
|
|
121
126
|
const { code, text } = captureOutput(fn);
|
|
122
127
|
return toCallToolResult({ text, code });
|
|
123
128
|
}
|
|
124
|
-
function
|
|
129
|
+
function realPathForContainment(p) {
|
|
125
130
|
try {
|
|
126
131
|
return fs.realpathSync.native(p);
|
|
127
|
-
} catch {
|
|
128
|
-
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const code = err.code;
|
|
134
|
+
return code === "ENOENT" || code === "ENOTDIR" ? p : null;
|
|
129
135
|
}
|
|
130
136
|
}
|
|
131
137
|
function forCompare(p) {
|
|
132
|
-
return process.platform === "win32" ? p
|
|
138
|
+
return process.platform === "win32" ? foldCaseForContainment(p) : p;
|
|
133
139
|
}
|
|
134
140
|
function resolveToolRoot(projectRoot) {
|
|
135
141
|
const resolved = resolveProjectRoot(projectRoot !== void 0 ? { project: projectRoot } : {});
|
|
@@ -146,18 +152,40 @@ function assertRootAllowed(resolvedRoot) {
|
|
|
146
152
|
`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.`
|
|
147
153
|
);
|
|
148
154
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const abs = path.resolve(resolvedRoot, normalizePath(target));
|
|
152
|
-
let identity;
|
|
155
|
+
var NO_CHECK_PINS = [];
|
|
156
|
+
function identityOf(p) {
|
|
153
157
|
try {
|
|
154
|
-
|
|
158
|
+
return fileIdentity(fs.statSync(p, { bigint: true }));
|
|
155
159
|
} catch {
|
|
156
|
-
|
|
160
|
+
return null;
|
|
157
161
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
}
|
|
163
|
+
function pinsFor(abs, real, identity) {
|
|
164
|
+
const pinIdentity = identity ?? ABSENT_PIN;
|
|
165
|
+
return [
|
|
166
|
+
[pinKey(abs), pinIdentity],
|
|
167
|
+
[pinKey(real), pinIdentity]
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
function checkWithinProjectRoot(target, resolvedRoot) {
|
|
171
|
+
const rootReal = realPathForContainment(resolvedRoot);
|
|
172
|
+
const abs = path.resolve(resolvedRoot, normalizePath(target));
|
|
173
|
+
const absRaw = path.resolve(resolvedRoot, target);
|
|
174
|
+
const identity = identityOf(absRaw);
|
|
175
|
+
const realNative = realPathForContainment(abs);
|
|
176
|
+
if (rootReal === null) return { inside: false, reason: "unresolvable-root", pins: NO_CHECK_PINS };
|
|
177
|
+
if (realNative === null) return { inside: false, reason: "unresolvable-target", pins: NO_CHECK_PINS };
|
|
178
|
+
const root = forCompare(normalizePath(rootReal));
|
|
179
|
+
const under = (real) => {
|
|
180
|
+
const r = forCompare(normalizePath(real));
|
|
181
|
+
return r === root || r.startsWith(root.endsWith("/") ? root : root + "/");
|
|
182
|
+
};
|
|
183
|
+
if (!under(realNative)) return { inside: false, reason: "outside", pins: NO_CHECK_PINS };
|
|
184
|
+
if (absRaw === abs) return { inside: true, reason: "inside", pins: pinsFor(abs, realNative, identity) };
|
|
185
|
+
const realRaw = realPathForContainment(absRaw);
|
|
186
|
+
if (realRaw === null) return { inside: false, reason: "unresolvable-target", pins: NO_CHECK_PINS };
|
|
187
|
+
if (!under(realRaw)) return { inside: false, reason: "outside", pins: NO_CHECK_PINS };
|
|
188
|
+
return { inside: true, reason: "inside", pins: [...pinsFor(abs, realNative, identity), ...pinsFor(absRaw, realRaw, identity)] };
|
|
161
189
|
}
|
|
162
190
|
function specFilePart(spec) {
|
|
163
191
|
const range = parseLineRange(spec);
|
|
@@ -166,6 +194,17 @@ function specFilePart(spec) {
|
|
|
166
194
|
return colonIdx === -1 ? spec : spec.slice(0, colonIdx);
|
|
167
195
|
}
|
|
168
196
|
var NO_PINS = /* @__PURE__ */ new Map();
|
|
197
|
+
function refusalText(file, resolvedRoot, reason) {
|
|
198
|
+
const escapeHatch = "Set mcp.confine_reads_to_project_root = false (or TOKEN_GOAT_MCP_CONFINE_READS=0) to allow cross-root reads.";
|
|
199
|
+
if (reason === "unresolvable-root") {
|
|
200
|
+
return `refused: the project root "${resolvedRoot}" could not be resolved, so no path can be confirmed to sit inside it. This is a broken workspace root -- an unmounted share, a deleted directory, or a permission change on a parent -- not a request to read outside the project.`;
|
|
201
|
+
}
|
|
202
|
+
if (reason === "unresolvable-target") {
|
|
203
|
+
return `refused: "${file}" could not be resolved to a real location, so it cannot be confirmed to sit inside the project root. A symlink loop, a permission error on a parent directory, or a path past the operating system's length limit all produce this. The check fails closed rather than falling back to comparing the text of the path.`;
|
|
204
|
+
}
|
|
205
|
+
const rootScope = loadConfig().mcp.allowed_roots.length === 0 ? "Each call is confined to the projectRoot it names, and that root comes from the caller: set mcp.allowed_roots (or TOKEN_GOAT_MCP_ALLOWED_ROOTS) to pin which roots may be named at all." : "Each call is confined to the projectRoot it names, which must itself sit inside mcp.allowed_roots.";
|
|
206
|
+
return `refused: "${file}" is outside the project root "${resolvedRoot}". ${rootScope} ${escapeHatch}`;
|
|
207
|
+
}
|
|
169
208
|
function confineTargets(targets, resolvedRoot, splitCommas = true) {
|
|
170
209
|
if (!loadConfig(resolvedRoot).mcp.confine_reads_to_project_root) return { ok: true, targets, pins: NO_PINS };
|
|
171
210
|
const checked = [];
|
|
@@ -177,17 +216,9 @@ function confineTargets(targets, resolvedRoot, splitCommas = true) {
|
|
|
177
216
|
if (file === "") continue;
|
|
178
217
|
const check = checkWithinProjectRoot(file, resolvedRoot);
|
|
179
218
|
if (!check.inside) {
|
|
180
|
-
return {
|
|
181
|
-
ok: false,
|
|
182
|
-
refusal: toCallToolResult({
|
|
183
|
-
text: `refused: "${file}" is outside the project root. The MCP tools are confined to the workspace. Set mcp.confine_reads_to_project_root = false (or TOKEN_GOAT_MCP_CONFINE_READS=0) to allow cross-root reads.`,
|
|
184
|
-
code: 1
|
|
185
|
-
})
|
|
186
|
-
};
|
|
219
|
+
return { ok: false, refusal: toCallToolResult({ text: refusalText(file, resolvedRoot, check.reason), code: 1 }) };
|
|
187
220
|
}
|
|
188
|
-
const
|
|
189
|
-
pins.set(pinKey(check.abs), pinIdentity);
|
|
190
|
-
pins.set(pinKey(check.real), pinIdentity);
|
|
221
|
+
for (const [key, identity] of check.pins) pins.set(key, identity);
|
|
191
222
|
}
|
|
192
223
|
checked.push(splitCommas ? parts.join(",") : parts[0]);
|
|
193
224
|
}
|
|
@@ -211,7 +242,7 @@ function mcpToolAllowlist() {
|
|
|
211
242
|
return names.length === 0 ? null : new Set(names);
|
|
212
243
|
}
|
|
213
244
|
async function createMcpServer() {
|
|
214
|
-
const [{ McpServer }, { z }] = await Promise.all([import("./token-goat-chunk-
|
|
245
|
+
const [{ McpServer }, { z }] = await Promise.all([import("./token-goat-chunk-LHLQFGWQ.mjs"), import("./token-goat-chunk-T6M7DAW3.mjs")]);
|
|
215
246
|
const server = new McpServer({ name: "token-goat", version: VERSION });
|
|
216
247
|
const allowlist = mcpToolAllowlist();
|
|
217
248
|
if (allowlist !== null) {
|
|
@@ -473,7 +504,7 @@ async function createMcpServer() {
|
|
|
473
504
|
embeddingsEnabled,
|
|
474
505
|
embeddingsAvailable
|
|
475
506
|
};
|
|
476
|
-
return toCallToolResult({ text:
|
|
507
|
+
return toCallToolResult({ text: displaySafeJson(status), code: 0 });
|
|
477
508
|
}
|
|
478
509
|
);
|
|
479
510
|
server.registerTool(
|
|
@@ -678,7 +709,7 @@ async function createMcpServer() {
|
|
|
678
709
|
text: z.string().max(CONTENT_MAX_INPUT_CHARS).describe("text to compress")
|
|
679
710
|
}
|
|
680
711
|
},
|
|
681
|
-
(args) => toCallToolResult({ text:
|
|
712
|
+
(args) => toCallToolResult({ text: displaySafeJson(compressionPayload(compressText(args.text))), code: 0 })
|
|
682
713
|
);
|
|
683
714
|
server.registerTool(
|
|
684
715
|
"retrieve_text",
|
|
@@ -704,7 +735,7 @@ async function createMcpServer() {
|
|
|
704
735
|
}
|
|
705
736
|
},
|
|
706
737
|
(args) => toCallToolResult({
|
|
707
|
-
text:
|
|
738
|
+
text: displaySafeJson(createHandoff(args.name, args.text, resolveToolRoot(args.projectRoot))),
|
|
708
739
|
code: 0
|
|
709
740
|
})
|
|
710
741
|
);
|
|
@@ -723,12 +754,13 @@ async function createMcpServer() {
|
|
|
723
754
|
projectRoot: resolveToolRoot(args.projectRoot),
|
|
724
755
|
...args.full === true ? { full: true } : {}
|
|
725
756
|
});
|
|
726
|
-
return result === null ? toCallToolResult({ text: `no local handoff named "${args.name}" in this project`, code: 1 }) : typeof result === "string" ? toRawCallToolResult({ text: result, code: 0 }) : toCallToolResult({ text:
|
|
757
|
+
return result === null ? toCallToolResult({ text: `no local handoff named "${args.name}" in this project`, code: 1 }) : typeof result === "string" ? toRawCallToolResult({ text: result, code: 0 }) : toCallToolResult({ text: displaySafeJson(compressionPayload(result)), code: 0 });
|
|
727
758
|
}
|
|
728
759
|
);
|
|
729
760
|
return server;
|
|
730
761
|
}
|
|
731
762
|
export {
|
|
763
|
+
checkWithinProjectRoot,
|
|
732
764
|
createMcpServer,
|
|
733
765
|
mcpToolAllowlist
|
|
734
766
|
};
|
|
@@ -3,7 +3,7 @@ const require = __cjsRequire(import.meta.url);
|
|
|
3
3
|
import {
|
|
4
4
|
deliveredOutputBytes,
|
|
5
5
|
wrappedShell
|
|
6
|
-
} from "./token-goat-chunk-
|
|
6
|
+
} from "./token-goat-chunk-FDURVZQD.mjs";
|
|
7
7
|
import {
|
|
8
8
|
ToolFilter,
|
|
9
9
|
capTokens,
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
recordStat,
|
|
16
16
|
selectFilter,
|
|
17
17
|
shlexSplit
|
|
18
|
-
} from "./token-goat-chunk-
|
|
18
|
+
} from "./token-goat-chunk-AAEYU2U2.mjs";
|
|
19
19
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
20
|
+
import "./token-goat-chunk-YCKCFYTM.mjs";
|
|
20
21
|
import {
|
|
21
22
|
init_define_import_meta_env
|
|
22
23
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|