token-goat 2.9.10 → 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.
Files changed (27) hide show
  1. package/README.md +54 -11
  2. package/SECURITY.md +17 -19
  3. package/THIRD_PARTY_NOTICES.md +667 -0
  4. package/dist/token-goat-chunk-2ZUPQYLO.mjs +3728 -0
  5. package/dist/{token-goat-chunk-DCYHAUW3.mjs → token-goat-chunk-3ZALKJ23.mjs} +69 -37
  6. package/dist/{token-goat-chunk-JS44SRAX.mjs → token-goat-chunk-4NZUXC4F.mjs} +3 -2
  7. package/dist/{token-goat-chunk-VCEK77ED.mjs → token-goat-chunk-AAEYU2U2.mjs} +5307 -6350
  8. package/dist/{token-goat-chunk-ATVQ6IIT.mjs → token-goat-chunk-FDURVZQD.mjs} +10 -2
  9. package/dist/{token-goat-chunk-DSHYDU53.mjs → token-goat-chunk-HDL77BN3.mjs} +11 -7
  10. package/dist/{token-goat-chunk-LYWIRYCF.mjs → token-goat-chunk-LHLQFGWQ.mjs} +1 -1
  11. package/dist/{token-goat-chunk-YFXFUG3L.mjs → token-goat-chunk-LKOYKSID.mjs} +1275 -655
  12. package/dist/{token-goat-chunk-ZQODJQTP.mjs → token-goat-chunk-MKITQ5RY.mjs} +354 -180
  13. package/dist/{token-goat-chunk-7ALFFPJW.mjs → token-goat-chunk-MRZ555B3.mjs} +2699 -2175
  14. package/dist/{token-goat-chunk-2TJBY5RD.mjs → token-goat-chunk-PJPFOOGM.mjs} +6 -5
  15. package/dist/{token-goat-chunk-FQD3OB5W.mjs → token-goat-chunk-QCQUIPIP.mjs} +6068 -1687
  16. package/dist/{token-goat-chunk-PA5UXNOT.mjs → token-goat-chunk-SY7WTZMW.mjs} +5053 -7735
  17. package/dist/{token-goat-chunk-PXWBHSFB.mjs → token-goat-chunk-T6M7DAW3.mjs} +31 -4
  18. package/dist/{token-goat-chunk-SLGOIXKF.mjs → token-goat-chunk-V53Z47ZH.mjs} +85 -328
  19. package/dist/token-goat-chunk-Y5VKNLVD.mjs +5942 -0
  20. package/dist/token-goat-chunk-YCKCFYTM.mjs +3564 -0
  21. package/dist/token-goat-hook.mjs +6 -5
  22. package/dist/token-goat.core.mjs +8 -6
  23. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  24. package/docs/cli.md +4 -1
  25. package/docs/install.md +131 -11
  26. package/docs/security.md +2 -0
  27. package/package.json +8 -3
@@ -25,7 +25,8 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-chunk-YFXFUG3L.mjs";
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-PA5UXNOT.mjs";
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-VCEK77ED.mjs";
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 realPathOrSelf(p) {
129
+ function realPathForContainment(p) {
125
130
  try {
126
131
  return fs.realpathSync.native(p);
127
- } catch {
128
- return p;
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.toLowerCase() : 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
- function checkWithinProjectRoot(target, resolvedRoot) {
150
- const root = forCompare(normalizePath(realPathOrSelf(resolvedRoot)));
151
- const abs = path.resolve(resolvedRoot, normalizePath(target));
152
- let identity;
155
+ var NO_CHECK_PINS = [];
156
+ function identityOf(p) {
153
157
  try {
154
- identity = fileIdentity(fs.statSync(abs, { bigint: true }));
158
+ return fileIdentity(fs.statSync(p, { bigint: true }));
155
159
  } catch {
156
- identity = null;
160
+ return null;
157
161
  }
158
- const realNative = realPathOrSelf(abs);
159
- const real = forCompare(normalizePath(realNative));
160
- return { inside: real === root || real.startsWith(root.endsWith("/") ? root : root + "/"), abs, real: realNative, identity };
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 pinIdentity = check.identity ?? ABSENT_PIN;
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-LYWIRYCF.mjs"), import("./token-goat-chunk-PXWBHSFB.mjs")]);
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: JSON.stringify(status, null, 2), code: 0 });
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: JSON.stringify(compressionPayload(compressText(args.text)), null, 2), code: 0 })
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: JSON.stringify(createHandoff(args.name, args.text, resolveToolRoot(args.projectRoot)), null, 2),
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: JSON.stringify(compressionPayload(result), null, 2), code: 0 });
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-ATVQ6IIT.mjs";
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-VCEK77ED.mjs";
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";