token-goat 2.9.11 → 2.9.13

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 (25) hide show
  1. package/README.md +7 -1
  2. package/dist/{token-goat-chunk-FDURVZQD.mjs → token-goat-chunk-2WC4ZUXN.mjs} +1 -1
  3. package/dist/{token-goat-chunk-MRZ555B3.mjs → token-goat-chunk-3ESRORNM.mjs} +188 -97
  4. package/dist/token-goat-chunk-4NXUKV7D.mjs +56 -0
  5. package/dist/{token-goat-chunk-Y5VKNLVD.mjs → token-goat-chunk-6B44WLIF.mjs} +5 -5
  6. package/dist/{token-goat-chunk-2ZUPQYLO.mjs → token-goat-chunk-B3CTCQTH.mjs} +1 -1
  7. package/dist/{token-goat-chunk-V53Z47ZH.mjs → token-goat-chunk-FQCNJV4V.mjs} +20 -6
  8. package/dist/{token-goat-chunk-3ZALKJ23.mjs → token-goat-chunk-FZU7GMUS.mjs} +49 -24
  9. package/dist/{token-goat-chunk-AAEYU2U2.mjs → token-goat-chunk-JOXLE672.mjs} +344 -5699
  10. package/dist/{token-goat-chunk-HDL77BN3.mjs → token-goat-chunk-JVNPCQB7.mjs} +10 -7
  11. package/dist/{token-goat-chunk-LHLQFGWQ.mjs → token-goat-chunk-OMNQUUIT.mjs} +9 -27
  12. package/dist/{token-goat-chunk-PJPFOOGM.mjs → token-goat-chunk-P2PU4CR5.mjs} +8 -6
  13. package/dist/{token-goat-chunk-YCKCFYTM.mjs → token-goat-chunk-QKXBGBQR.mjs} +110 -21
  14. package/dist/{token-goat-chunk-MKITQ5RY.mjs → token-goat-chunk-QWSUZWFP.mjs} +44 -33
  15. package/dist/{token-goat-chunk-4NZUXC4F.mjs → token-goat-chunk-RDITECDL.mjs} +7 -5
  16. package/dist/{token-goat-chunk-LKOYKSID.mjs → token-goat-chunk-U7X6LQD2.mjs} +271 -69
  17. package/dist/token-goat-chunk-UM47DRD3.mjs +242 -0
  18. package/dist/token-goat-chunk-UMXJN7DI.mjs +5521 -0
  19. package/dist/token-goat-chunk-YOA4N6WA.mjs +308 -0
  20. package/dist/{token-goat-chunk-SY7WTZMW.mjs → token-goat-chunk-ZZI3IDQZ.mjs} +1016 -995
  21. package/dist/token-goat-hook.mjs +8 -6
  22. package/dist/token-goat.core.mjs +10 -7
  23. package/docs/cli.md +11 -8
  24. package/docs/security.md +1 -1
  25. package/package.json +1 -1
@@ -0,0 +1,242 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ loadConfig,
5
+ recordStat,
6
+ redactSecrets
7
+ } from "./token-goat-chunk-UMXJN7DI.mjs";
8
+ import {
9
+ atomicWriteText,
10
+ ensureDirSync,
11
+ sanitizeIdForFilename,
12
+ tokenGoatHome
13
+ } from "./token-goat-chunk-QKXBGBQR.mjs";
14
+ import {
15
+ init_define_import_meta_env
16
+ } from "./token-goat-chunk-A37V4PBF.mjs";
17
+
18
+ // src/disk_cache.ts
19
+ init_define_import_meta_env();
20
+ import * as fs from "node:fs";
21
+ import * as path from "node:path";
22
+ var DEFAULT_MAX_COUNT = 200;
23
+ var DEFAULT_MAX_AGE_MS = 24 * 3600 * 1e3;
24
+ function sanitizeId(id) {
25
+ return sanitizeIdForFilename(id, 64);
26
+ }
27
+ function blobDir(subdir) {
28
+ return path.join(tokenGoatHome(), subdir);
29
+ }
30
+ function blobPath(subdir, id) {
31
+ const safe = sanitizeId(id);
32
+ if (!safe) return null;
33
+ const dir = blobDir(subdir);
34
+ const candidate = path.join(dir, `${safe}.json`);
35
+ try {
36
+ const rel = path.relative(dir, candidate);
37
+ if (rel.startsWith("..")) return null;
38
+ } catch {
39
+ return null;
40
+ }
41
+ return candidate;
42
+ }
43
+ function isBlobStale(subdir, id) {
44
+ const p = blobPath(subdir, id);
45
+ if (p === null) return false;
46
+ try {
47
+ const stat = fs.statSync(p);
48
+ return Date.now() - stat.mtimeMs > DEFAULT_MAX_AGE_MS;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+ function subdirCacheDefaults(subdir) {
54
+ try {
55
+ if (subdir === "bash_outputs") {
56
+ const bc = loadConfig().bash_compress;
57
+ return { maxCount: bc.cache_max_file_count, maxBytes: bc.cache_max_bytes, maxBytesPerItem: bc.cache_max_bytes_per_output };
58
+ }
59
+ if (subdir === "web_outputs") {
60
+ const wf = loadConfig().webfetch;
61
+ return { maxCount: wf.max_file_count, maxBytes: wf.max_bytes, maxBytesPerItem: Number.POSITIVE_INFINITY };
62
+ }
63
+ } catch {
64
+ }
65
+ return { maxCount: DEFAULT_MAX_COUNT, maxBytes: Number.POSITIVE_INFINITY, maxBytesPerItem: Number.POSITIVE_INFINITY };
66
+ }
67
+ function storeBlob(subdir, id, value, opts = {}) {
68
+ const p = blobPath(subdir, id);
69
+ if (!p) return false;
70
+ const defaults = subdirCacheDefaults(subdir);
71
+ const maxBytesPerItem = opts.maxBytesPerItem ?? defaults.maxBytesPerItem;
72
+ let rawJson;
73
+ try {
74
+ rawJson = JSON.stringify(value);
75
+ } catch {
76
+ return false;
77
+ }
78
+ let json;
79
+ try {
80
+ const result = redactSecrets(rawJson);
81
+ json = result.text;
82
+ if (result.count > 0) recordStat("secret_redacted", 0, result.count, void 0, subdir);
83
+ } catch {
84
+ return false;
85
+ }
86
+ if (Number.isFinite(maxBytesPerItem) && Buffer.byteLength(json, "utf-8") > maxBytesPerItem) return false;
87
+ try {
88
+ const dir = path.dirname(p);
89
+ if (!fs.existsSync(dir)) ensureDirSync(dir);
90
+ atomicWriteText(p, json);
91
+ } catch {
92
+ return false;
93
+ }
94
+ pruneBlobs(
95
+ subdir,
96
+ opts.maxCount ?? defaults.maxCount,
97
+ opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS,
98
+ opts.maxBytes ?? defaults.maxBytes,
99
+ p
100
+ );
101
+ return true;
102
+ }
103
+ function loadBlob(subdir, id) {
104
+ const p = blobPath(subdir, id);
105
+ if (!p) return null;
106
+ try {
107
+ if (!fs.existsSync(p)) return null;
108
+ return JSON.parse(fs.readFileSync(p, "utf8"));
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+ function listBlobs(subdir) {
114
+ const dir = blobDir(subdir);
115
+ const out = [];
116
+ try {
117
+ if (!fs.existsSync(dir)) return out;
118
+ for (const file of fs.readdirSync(dir)) {
119
+ if (!file.endsWith(".json")) continue;
120
+ const id = file.slice(0, -5);
121
+ let mtime = 0;
122
+ try {
123
+ mtime = fs.statSync(path.join(dir, file)).mtimeMs;
124
+ } catch {
125
+ }
126
+ const value = loadBlob(subdir, id);
127
+ if (value !== null) out.push({ id, mtime, value });
128
+ }
129
+ } catch {
130
+ return out;
131
+ }
132
+ return out;
133
+ }
134
+ function pruneBlobs(subdir, maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS, maxBytes = Number.POSITIVE_INFINITY, protectedPath) {
135
+ return pruneBlobDir(blobDir(subdir), maxCount, maxAgeMs, maxBytes, protectedPath);
136
+ }
137
+ function pruneBlobDir(dir, maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS, maxBytes = Number.POSITIVE_INFINITY, protectedPath) {
138
+ let removed = 0;
139
+ try {
140
+ if (!fs.existsSync(dir)) return 0;
141
+ const cutoff = Date.now() - maxAgeMs;
142
+ let kept = [];
143
+ let protectedEntry;
144
+ for (const file of fs.readdirSync(dir)) {
145
+ const full = path.join(dir, file);
146
+ let stat;
147
+ try {
148
+ stat = fs.statSync(full);
149
+ } catch {
150
+ continue;
151
+ }
152
+ if (!stat.isFile()) continue;
153
+ if (protectedPath !== void 0 && full === protectedPath) {
154
+ protectedEntry = [full, stat.mtimeMs, stat.size];
155
+ continue;
156
+ }
157
+ if (stat.mtimeMs < cutoff) {
158
+ try {
159
+ fs.unlinkSync(full);
160
+ removed++;
161
+ } catch {
162
+ continue;
163
+ }
164
+ } else if (file.endsWith(".json")) {
165
+ kept.push([full, stat.mtimeMs, stat.size]);
166
+ }
167
+ }
168
+ const countBudget = protectedEntry ? Math.max(0, maxCount - 1) : maxCount;
169
+ if (kept.length > countBudget) {
170
+ kept.sort((a, b) => a[1] - b[1]);
171
+ const excess = kept.slice(0, kept.length - countBudget);
172
+ kept = kept.slice(kept.length - countBudget);
173
+ for (const [full] of excess) {
174
+ try {
175
+ fs.unlinkSync(full);
176
+ removed++;
177
+ } catch {
178
+ continue;
179
+ }
180
+ }
181
+ }
182
+ if (Number.isFinite(maxBytes)) {
183
+ kept.sort((a, b) => a[1] - b[1]);
184
+ let total = kept.reduce((sum, [, , size]) => sum + size, 0) + (protectedEntry ? protectedEntry[2] : 0);
185
+ while (total > maxBytes && kept.length > 0) {
186
+ const oldest = kept.shift();
187
+ if (!oldest) break;
188
+ const [full, , size] = oldest;
189
+ try {
190
+ fs.unlinkSync(full);
191
+ removed++;
192
+ total -= size;
193
+ } catch {
194
+ break;
195
+ }
196
+ }
197
+ }
198
+ } catch {
199
+ return removed;
200
+ }
201
+ return removed;
202
+ }
203
+ var SWEEPABLE_CACHE_SUBDIRS = [
204
+ { subdir: "bash_outputs", countCapped: true },
205
+ { subdir: "web_outputs", countCapped: true },
206
+ { subdir: "mcp_outputs", countCapped: true },
207
+ { subdir: "sessions", countCapped: false }
208
+ ];
209
+ function sweepCacheRoots(extraRoots = []) {
210
+ let removed = 0;
211
+ const roots = [tokenGoatHome(), ...extraRoots];
212
+ const seen = /* @__PURE__ */ new Set();
213
+ for (const root of roots) {
214
+ if (!root || seen.has(root)) continue;
215
+ seen.add(root);
216
+ for (const { subdir, countCapped } of SWEEPABLE_CACHE_SUBDIRS) {
217
+ try {
218
+ const defaults = subdirCacheDefaults(subdir);
219
+ removed += pruneBlobDir(
220
+ path.join(root, subdir),
221
+ countCapped ? defaults.maxCount : Number.POSITIVE_INFINITY,
222
+ DEFAULT_MAX_AGE_MS,
223
+ countCapped ? defaults.maxBytes : Number.POSITIVE_INFINITY
224
+ );
225
+ } catch {
226
+ }
227
+ }
228
+ }
229
+ return removed;
230
+ }
231
+
232
+ export {
233
+ DEFAULT_MAX_COUNT,
234
+ DEFAULT_MAX_AGE_MS,
235
+ blobPath,
236
+ isBlobStale,
237
+ storeBlob,
238
+ loadBlob,
239
+ listBlobs,
240
+ pruneBlobs,
241
+ sweepCacheRoots
242
+ };