context-compress 2026.3.20 → 2026.3.22
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/dist/executor.d.ts +10 -2
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +70 -13
- package/dist/executor.js.map +1 -1
- package/dist/filters.d.ts +18 -0
- package/dist/filters.d.ts.map +1 -0
- package/dist/filters.js +167 -0
- package/dist/filters.js.map +1 -0
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +17 -21
- package/dist/network.js.map +1 -1
- package/dist/server.bundle.mjs +409 -89
- package/dist/server.bundle.mjs.map +4 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +108 -57
- package/dist/server.js.map +1 -1
- package/dist/stats.d.ts +7 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +66 -1
- package/dist/stats.js.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +11 -4
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +54 -0
- package/dist/utils.js.map +1 -0
- package/docs/token-reduction-report.md +130 -83
- package/package.json +1 -1
package/dist/server.bundle.mjs
CHANGED
|
@@ -11061,7 +11061,7 @@ function debug(...args) {
|
|
|
11061
11061
|
}
|
|
11062
11062
|
|
|
11063
11063
|
// src/server.ts
|
|
11064
|
-
import { readFileSync as
|
|
11064
|
+
import { readFileSync as readFileSync3, realpathSync, statSync } from "node:fs";
|
|
11065
11065
|
import { dirname, join as join4, resolve } from "node:path";
|
|
11066
11066
|
import { fileURLToPath } from "node:url";
|
|
11067
11067
|
|
|
@@ -21216,7 +21216,182 @@ import { execFileSync, spawn } from "node:child_process";
|
|
|
21216
21216
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
21217
21217
|
import { tmpdir } from "node:os";
|
|
21218
21218
|
import { join as join2 } from "node:path";
|
|
21219
|
+
|
|
21220
|
+
// src/filters.ts
|
|
21221
|
+
function applyCommandFilter(code, stdout) {
|
|
21222
|
+
const cmd = code.trim().split(/\s+/)[0];
|
|
21223
|
+
const fullCmd = code.trim();
|
|
21224
|
+
if (cmd === "git") return filterGit(fullCmd, stdout);
|
|
21225
|
+
if (cmd === "npm" || cmd === "yarn" || cmd === "pnpm" || cmd === "bun")
|
|
21226
|
+
return filterPackageManager(fullCmd, stdout);
|
|
21227
|
+
if (fullCmd.includes("test") || fullCmd.includes("jest") || fullCmd.includes("vitest") || fullCmd.includes("pytest") || fullCmd.includes("cargo test")) {
|
|
21228
|
+
return filterTestOutput(stdout);
|
|
21229
|
+
}
|
|
21230
|
+
if (cmd === "cargo" || cmd === "make" || cmd === "gradle")
|
|
21231
|
+
return filterBuildOutput(fullCmd, stdout);
|
|
21232
|
+
if (cmd === "docker" || cmd === "kubectl") return filterContainerOutput(fullCmd, stdout);
|
|
21233
|
+
if (cmd === "ls" || cmd === "find" || cmd === "tree") return filterFileList(fullCmd, stdout);
|
|
21234
|
+
return { output: stdout, filtered: false };
|
|
21235
|
+
}
|
|
21236
|
+
function filterGit(cmd, stdout) {
|
|
21237
|
+
if (/git\s+(push|pull|fetch|clone)/.test(cmd)) {
|
|
21238
|
+
const lines = stdout.split("\n");
|
|
21239
|
+
const filtered = lines.filter(
|
|
21240
|
+
(l) => !l.startsWith("remote: Counting") && !l.startsWith("remote: Compressing") && !l.startsWith("remote: Total") && !l.includes("Unpacking objects:") && !l.includes("Receiving objects:") && !l.includes("Resolving deltas:") && !/^\s*\d+%/.test(l)
|
|
21241
|
+
);
|
|
21242
|
+
return { output: filtered.join("\n"), filtered: true };
|
|
21243
|
+
}
|
|
21244
|
+
if (/git\s+status/.test(cmd)) {
|
|
21245
|
+
const lines = stdout.split("\n");
|
|
21246
|
+
const filtered = lines.filter((l) => !l.startsWith(" (use ") && l.trim() !== "");
|
|
21247
|
+
return { output: filtered.join("\n"), filtered: true };
|
|
21248
|
+
}
|
|
21249
|
+
return { output: stdout, filtered: false };
|
|
21250
|
+
}
|
|
21251
|
+
function filterPackageManager(cmd, stdout) {
|
|
21252
|
+
if (/\b(install|add|i)\b/.test(cmd)) {
|
|
21253
|
+
const lines = stdout.split("\n");
|
|
21254
|
+
const filtered = lines.filter(
|
|
21255
|
+
(l) => !l.startsWith("npm warn") && !l.includes("packages are looking for funding") && !l.includes("run `npm fund`") && !l.startsWith("npm notice") && !/^[\s\u2502\u251C\u2514\u2500]+$/.test(l) && // tree-drawing characters
|
|
21256
|
+
!/^\s*$/.test(l)
|
|
21257
|
+
);
|
|
21258
|
+
return { output: filtered.join("\n"), filtered: true };
|
|
21259
|
+
}
|
|
21260
|
+
if (/\btest\b/.test(cmd)) {
|
|
21261
|
+
return filterTestOutput(stdout);
|
|
21262
|
+
}
|
|
21263
|
+
return { output: stdout, filtered: false };
|
|
21264
|
+
}
|
|
21265
|
+
var FAIL_MARKER_RE = /^\s*[\u2717\u2718\u00D7]\s/;
|
|
21266
|
+
var FAIL_WORD_RE = /\bFAIL\b/;
|
|
21267
|
+
var FAILED_RE = /\bfailed?\b/i;
|
|
21268
|
+
var ERROR_RE = /\bERROR\b/;
|
|
21269
|
+
var SUMMARY_RE = /^\s*(Tests?|Suites?|Test Suites)\s*:|^\s*(pass|fail|skip|pending|todo)\s|\b\d+\s+(passing|failing|pending|skipped)\b|^(ok|not ok)\s|^\u2139\s|^(PASS|FAIL)\s/i;
|
|
21270
|
+
function isFailMarker(line) {
|
|
21271
|
+
return FAIL_MARKER_RE.test(line) || FAIL_WORD_RE.test(line) || FAILED_RE.test(line) || ERROR_RE.test(line);
|
|
21272
|
+
}
|
|
21273
|
+
function isSummaryLine(line) {
|
|
21274
|
+
return SUMMARY_RE.test(line);
|
|
21275
|
+
}
|
|
21276
|
+
function filterTestOutput(stdout) {
|
|
21277
|
+
const lines = stdout.split("\n");
|
|
21278
|
+
const failures = [];
|
|
21279
|
+
const summary = [];
|
|
21280
|
+
let inFailure = false;
|
|
21281
|
+
let failCount = 0;
|
|
21282
|
+
for (const line of lines) {
|
|
21283
|
+
if (isFailMarker(line)) {
|
|
21284
|
+
inFailure = true;
|
|
21285
|
+
failCount++;
|
|
21286
|
+
}
|
|
21287
|
+
if (inFailure) {
|
|
21288
|
+
failures.push(line);
|
|
21289
|
+
if (line.trim() === "" && failures.length > 3) inFailure = false;
|
|
21290
|
+
}
|
|
21291
|
+
if (isSummaryLine(line)) {
|
|
21292
|
+
summary.push(line);
|
|
21293
|
+
}
|
|
21294
|
+
}
|
|
21295
|
+
if (failCount === 0 && summary.length > 0) {
|
|
21296
|
+
return { output: summary.join("\n"), filtered: true };
|
|
21297
|
+
}
|
|
21298
|
+
if (failures.length > 0) {
|
|
21299
|
+
const result = [...failures, "", ...summary].join("\n");
|
|
21300
|
+
return { output: result, filtered: true };
|
|
21301
|
+
}
|
|
21302
|
+
return { output: stdout, filtered: false };
|
|
21303
|
+
}
|
|
21304
|
+
function filterBuildOutput(cmd, stdout) {
|
|
21305
|
+
const lines = stdout.split("\n");
|
|
21306
|
+
const filtered = lines.filter(
|
|
21307
|
+
(l) => !l.includes("Downloading") && !l.includes("Downloaded") && !/Compiling\s+\d+\s+of\s+\d+/.test(l) && !l.includes("Blocking waiting for file lock") && !/^\s*$/.test(l)
|
|
21308
|
+
);
|
|
21309
|
+
return { output: filtered.join("\n"), filtered: filtered.length < lines.length };
|
|
21310
|
+
}
|
|
21311
|
+
function filterContainerOutput(cmd, stdout) {
|
|
21312
|
+
if (/docker\s+build/.test(cmd)) {
|
|
21313
|
+
const lines = stdout.split("\n");
|
|
21314
|
+
const filtered = lines.filter(
|
|
21315
|
+
(l) => !l.startsWith(" ---> ") && !l.startsWith("Sending build context")
|
|
21316
|
+
);
|
|
21317
|
+
return { output: filtered.join("\n"), filtered: true };
|
|
21318
|
+
}
|
|
21319
|
+
return { output: stdout, filtered: false };
|
|
21320
|
+
}
|
|
21321
|
+
function filterFileList(cmd, stdout) {
|
|
21322
|
+
const lines = stdout.split("\n").filter((l) => l.trim() !== "");
|
|
21323
|
+
if (lines.length <= 30) return { output: stdout, filtered: false };
|
|
21324
|
+
if (cmd.includes("-R") || cmd.startsWith("find")) {
|
|
21325
|
+
const dirs = /* @__PURE__ */ new Map();
|
|
21326
|
+
for (const line of lines) {
|
|
21327
|
+
const parts = line.split("/");
|
|
21328
|
+
const dir = parts.length > 1 ? parts.slice(0, -1).join("/") : ".";
|
|
21329
|
+
dirs.set(dir, (dirs.get(dir) ?? 0) + 1);
|
|
21330
|
+
}
|
|
21331
|
+
if (dirs.size > 5 && lines.length > 50) {
|
|
21332
|
+
const summary = Array.from(dirs.entries()).sort((a, b) => b[1] - a[1]).map(([dir, count]) => ` ${dir}/ (${count} files)`).join("\n");
|
|
21333
|
+
return {
|
|
21334
|
+
output: `${lines.length} files found:
|
|
21335
|
+
${summary}`,
|
|
21336
|
+
filtered: true
|
|
21337
|
+
};
|
|
21338
|
+
}
|
|
21339
|
+
}
|
|
21340
|
+
return { output: stdout, filtered: false };
|
|
21341
|
+
}
|
|
21342
|
+
|
|
21343
|
+
// src/utils.ts
|
|
21344
|
+
function detectInjectionPatterns(content) {
|
|
21345
|
+
const warnings = [];
|
|
21346
|
+
const patterns = [
|
|
21347
|
+
{ re: /ignore\s+(all\s+)?previous\s+instructions/i, label: "instruction override" },
|
|
21348
|
+
{ re: /you\s+are\s+now\s+/i, label: "role reassignment" },
|
|
21349
|
+
{
|
|
21350
|
+
re: /(?:^|\n)\s*system\s*:\s*(?:you are|you're|as an? )/im,
|
|
21351
|
+
label: "system prompt injection"
|
|
21352
|
+
},
|
|
21353
|
+
{ re: /\[INST\]|\[\/INST\]|<\|im_start\|>|<\|im_end\|>/i, label: "chat template injection" },
|
|
21354
|
+
{ re: /\n\n(?:Human|Assistant):/m, label: "chat delimiter injection" },
|
|
21355
|
+
{ re: /reveal\s+(your|the)\s+(system|secret|confidential)/i, label: "data exfiltration" },
|
|
21356
|
+
{ re: /act\s+as\s+(if\s+you\s+are|a)\s+/i, label: "role manipulation" }
|
|
21357
|
+
];
|
|
21358
|
+
for (const { re, label } of patterns) {
|
|
21359
|
+
if (re.test(content)) {
|
|
21360
|
+
warnings.push(label);
|
|
21361
|
+
}
|
|
21362
|
+
}
|
|
21363
|
+
return warnings;
|
|
21364
|
+
}
|
|
21365
|
+
async function limitConcurrency(tasks, limit) {
|
|
21366
|
+
const results = new Array(tasks.length);
|
|
21367
|
+
let nextIndex = 0;
|
|
21368
|
+
async function runNext() {
|
|
21369
|
+
while (nextIndex < tasks.length) {
|
|
21370
|
+
const index = nextIndex++;
|
|
21371
|
+
try {
|
|
21372
|
+
const value = await tasks[index]();
|
|
21373
|
+
results[index] = { status: "fulfilled", value };
|
|
21374
|
+
} catch (reason) {
|
|
21375
|
+
results[index] = { status: "rejected", reason };
|
|
21376
|
+
}
|
|
21377
|
+
}
|
|
21378
|
+
}
|
|
21379
|
+
const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => runNext());
|
|
21380
|
+
await Promise.all(workers);
|
|
21381
|
+
return results;
|
|
21382
|
+
}
|
|
21383
|
+
function formatBytes(bytes) {
|
|
21384
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
21385
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
21386
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
21387
|
+
}
|
|
21388
|
+
|
|
21389
|
+
// src/executor.ts
|
|
21219
21390
|
var DEFAULT_TIMEOUT = 3e4;
|
|
21391
|
+
var ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]/;
|
|
21392
|
+
function stripAnsi(str) {
|
|
21393
|
+
return str.replace(new RegExp(ANSI_RE.source, "g"), "");
|
|
21394
|
+
}
|
|
21220
21395
|
var SAFE_ENV_KEYS = [
|
|
21221
21396
|
"PATH",
|
|
21222
21397
|
"HOME",
|
|
@@ -21269,6 +21444,21 @@ function killProcessTree(pid) {
|
|
|
21269
21444
|
}
|
|
21270
21445
|
}
|
|
21271
21446
|
}
|
|
21447
|
+
function stripProgressLines(output) {
|
|
21448
|
+
const lines = output.split("\n");
|
|
21449
|
+
const filtered = lines.filter((l) => {
|
|
21450
|
+
const trimmed = l.trim();
|
|
21451
|
+
if (ANSI_RE.test(l) && trimmed.replace(new RegExp(ANSI_RE.source, "g"), "").trim() === "")
|
|
21452
|
+
return false;
|
|
21453
|
+
if (/^[\s\[│├└─═━▓░█▒▏▎▍▌▋▊▉\]>=#\-.\d%]+$/.test(trimmed) && trimmed.length > 3) return false;
|
|
21454
|
+
if (/^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\-\\|/]\s/.test(trimmed)) return false;
|
|
21455
|
+
if (/(?:downloading|uploading|fetching|resolving)\s+[\d.]+\s*[kmg]?b/i.test(trimmed))
|
|
21456
|
+
return false;
|
|
21457
|
+
if (/\d+\.?\d*\s*[kmg]?b\/s/i.test(trimmed) && /eta|remaining/i.test(trimmed)) return false;
|
|
21458
|
+
return true;
|
|
21459
|
+
});
|
|
21460
|
+
return filtered.join("\n");
|
|
21461
|
+
}
|
|
21272
21462
|
function deduplicateLines(output) {
|
|
21273
21463
|
const lines = output.split("\n");
|
|
21274
21464
|
if (lines.length < 3) return output;
|
|
@@ -21300,12 +21490,12 @@ function deduplicateLines(output) {
|
|
|
21300
21490
|
function groupErrorLines(output) {
|
|
21301
21491
|
const lines = output.split("\n");
|
|
21302
21492
|
if (lines.length < 5) return output;
|
|
21303
|
-
const
|
|
21493
|
+
const ERROR_RE2 = /^(.*?(?:error|warning|Error|Warning|ERR|WARN)[:\s])\s*(.+?)(?:\s+(?:at|in|on)\s+(?:line\s+)?(\d+))?$/i;
|
|
21304
21494
|
const errorGroups = /* @__PURE__ */ new Map();
|
|
21305
21495
|
const resultLines = [];
|
|
21306
21496
|
let groupedCount = 0;
|
|
21307
21497
|
for (const line of lines) {
|
|
21308
|
-
const match = line.match(
|
|
21498
|
+
const match = line.match(ERROR_RE2);
|
|
21309
21499
|
if (match) {
|
|
21310
21500
|
const prefix = match[1].trim();
|
|
21311
21501
|
const msg = match[2].trim();
|
|
@@ -21380,20 +21570,26 @@ function smartTruncate(output, maxBytes) {
|
|
|
21380
21570
|
`;
|
|
21381
21571
|
return headLines.join("\n") + separator + tailLines.join("\n");
|
|
21382
21572
|
}
|
|
21383
|
-
function formatBytes(bytes) {
|
|
21384
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
21385
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
21386
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
21387
|
-
}
|
|
21388
21573
|
var SubprocessExecutor = class {
|
|
21389
21574
|
runtimes;
|
|
21390
21575
|
config;
|
|
21391
21576
|
env;
|
|
21577
|
+
activeProcesses = /* @__PURE__ */ new Set();
|
|
21392
21578
|
constructor(runtimes, config3) {
|
|
21393
21579
|
this.runtimes = runtimes;
|
|
21394
21580
|
this.config = config3;
|
|
21395
21581
|
this.env = buildEnv(config3);
|
|
21396
21582
|
}
|
|
21583
|
+
/** Kill all active child processes and their process trees. */
|
|
21584
|
+
shutdown() {
|
|
21585
|
+
for (const proc of this.activeProcesses) {
|
|
21586
|
+
try {
|
|
21587
|
+
if (proc.pid) killProcessTree(proc.pid);
|
|
21588
|
+
} catch {
|
|
21589
|
+
}
|
|
21590
|
+
}
|
|
21591
|
+
this.activeProcesses.clear();
|
|
21592
|
+
}
|
|
21397
21593
|
/**
|
|
21398
21594
|
* Execute code in a subprocess.
|
|
21399
21595
|
*/
|
|
@@ -21451,7 +21647,8 @@ var SubprocessExecutor = class {
|
|
|
21451
21647
|
tmpDir,
|
|
21452
21648
|
timeout,
|
|
21453
21649
|
maxOutput,
|
|
21454
|
-
plugin.needsShell
|
|
21650
|
+
plugin.needsShell,
|
|
21651
|
+
opts.language === "shell" ? opts.code : void 0
|
|
21455
21652
|
);
|
|
21456
21653
|
} finally {
|
|
21457
21654
|
setTimeout(() => this.cleanupTempDir(tmpDir), 100).unref();
|
|
@@ -21478,7 +21675,7 @@ var SubprocessExecutor = class {
|
|
|
21478
21675
|
}
|
|
21479
21676
|
return this.execute({ ...opts, code });
|
|
21480
21677
|
}
|
|
21481
|
-
spawnAndCapture(cmd, args, cwd, timeout, maxOutput, useShell) {
|
|
21678
|
+
spawnAndCapture(cmd, args, cwd, timeout, maxOutput, useShell, shellCode) {
|
|
21482
21679
|
return new Promise((resolve2) => {
|
|
21483
21680
|
const hardCap = this.config.hardCapBytes;
|
|
21484
21681
|
const stdoutChunks = [];
|
|
@@ -21495,6 +21692,7 @@ var SubprocessExecutor = class {
|
|
|
21495
21692
|
shell: useShell,
|
|
21496
21693
|
detached: process.platform !== "win32"
|
|
21497
21694
|
});
|
|
21695
|
+
this.activeProcesses.add(proc);
|
|
21498
21696
|
proc.stdout?.on("data", (chunk) => {
|
|
21499
21697
|
totalBytes += chunk.length;
|
|
21500
21698
|
if (totalBytes > hardCap) {
|
|
@@ -21515,6 +21713,7 @@ var SubprocessExecutor = class {
|
|
|
21515
21713
|
});
|
|
21516
21714
|
proc.on("error", (err) => {
|
|
21517
21715
|
debug("Process error:", err.message);
|
|
21716
|
+
this.activeProcesses.delete(proc);
|
|
21518
21717
|
if (!resolved) {
|
|
21519
21718
|
resolved = true;
|
|
21520
21719
|
resolve2({
|
|
@@ -21527,6 +21726,7 @@ var SubprocessExecutor = class {
|
|
|
21527
21726
|
}
|
|
21528
21727
|
});
|
|
21529
21728
|
proc.on("close", (code) => {
|
|
21729
|
+
this.activeProcesses.delete(proc);
|
|
21530
21730
|
if (resolved) return;
|
|
21531
21731
|
resolved = true;
|
|
21532
21732
|
let stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
@@ -21540,8 +21740,18 @@ var SubprocessExecutor = class {
|
|
|
21540
21740
|
stdout += `
|
|
21541
21741
|
[output capped at ${formatBytes(hardCap)} \u2014 process killed]`;
|
|
21542
21742
|
}
|
|
21543
|
-
|
|
21544
|
-
|
|
21743
|
+
if (shellCode && stdout) {
|
|
21744
|
+
const filtered = applyCommandFilter(shellCode, stdout);
|
|
21745
|
+
if (filtered.filtered) {
|
|
21746
|
+
stdout = filtered.output;
|
|
21747
|
+
}
|
|
21748
|
+
}
|
|
21749
|
+
stdout = stripAnsi(stdout);
|
|
21750
|
+
if (stdout.length > 1e4) {
|
|
21751
|
+
stdout = stripProgressLines(stdout);
|
|
21752
|
+
stdout = deduplicateLines(stdout);
|
|
21753
|
+
stdout = groupErrorLines(stdout);
|
|
21754
|
+
}
|
|
21545
21755
|
const truncated = Buffer.byteLength(stdout) > maxOutput;
|
|
21546
21756
|
if (truncated) {
|
|
21547
21757
|
stdout = smartTruncate(stdout, maxOutput);
|
|
@@ -21612,24 +21822,24 @@ async function resolveAndValidate(url) {
|
|
|
21612
21822
|
let resolvedIp = null;
|
|
21613
21823
|
let v4Error = false;
|
|
21614
21824
|
let v6Error = false;
|
|
21615
|
-
|
|
21616
|
-
|
|
21617
|
-
|
|
21618
|
-
|
|
21619
|
-
|
|
21620
|
-
|
|
21621
|
-
|
|
21622
|
-
|
|
21825
|
+
const [v4Result, v6Result] = await Promise.allSettled([
|
|
21826
|
+
dns.promises.lookup(hostname2, { family: 4 }),
|
|
21827
|
+
dns.promises.lookup(hostname2, { family: 6 })
|
|
21828
|
+
]);
|
|
21829
|
+
if (v4Result.status === "fulfilled") {
|
|
21830
|
+
if (isPrivateHost(v4Result.value.address)) {
|
|
21831
|
+
throw new Error(`Blocked: ${hostname2} resolved to private IP ${v4Result.value.address}`);
|
|
21832
|
+
}
|
|
21833
|
+
resolvedIp = v4Result.value.address;
|
|
21834
|
+
} else {
|
|
21623
21835
|
v4Error = true;
|
|
21624
21836
|
}
|
|
21625
|
-
|
|
21626
|
-
|
|
21627
|
-
|
|
21628
|
-
throw new Error(`Blocked: ${hostname2} resolved to private IPv6 ${address}`);
|
|
21837
|
+
if (v6Result.status === "fulfilled") {
|
|
21838
|
+
if (isPrivateHost(v6Result.value.address)) {
|
|
21839
|
+
throw new Error(`Blocked: ${hostname2} resolved to private IPv6 ${v6Result.value.address}`);
|
|
21629
21840
|
}
|
|
21630
|
-
if (!resolvedIp) resolvedIp = address;
|
|
21631
|
-
}
|
|
21632
|
-
if (err instanceof Error && err.message.startsWith("Blocked:")) throw err;
|
|
21841
|
+
if (!resolvedIp) resolvedIp = v6Result.value.address;
|
|
21842
|
+
} else {
|
|
21633
21843
|
v6Error = true;
|
|
21634
21844
|
}
|
|
21635
21845
|
if (v4Error && v6Error) {
|
|
@@ -21954,6 +22164,7 @@ function hasBun(runtimes) {
|
|
|
21954
22164
|
}
|
|
21955
22165
|
|
|
21956
22166
|
// src/stats.ts
|
|
22167
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
21957
22168
|
var BAR_WIDTH = 20;
|
|
21958
22169
|
function asciiBar(ratio, width = BAR_WIDTH) {
|
|
21959
22170
|
const filled = Math.round(ratio * width);
|
|
@@ -21973,6 +22184,10 @@ var SessionTracker = class {
|
|
|
21973
22184
|
bytesSandboxed: 0,
|
|
21974
22185
|
sessionStart: Date.now()
|
|
21975
22186
|
};
|
|
22187
|
+
cumulativeFile;
|
|
22188
|
+
constructor(cumulativeFile) {
|
|
22189
|
+
this.cumulativeFile = cumulativeFile ?? null;
|
|
22190
|
+
}
|
|
21976
22191
|
trackCall(toolName, responseBytes) {
|
|
21977
22192
|
this.stats.calls[toolName] = (this.stats.calls[toolName] ?? 0) + 1;
|
|
21978
22193
|
this.stats.bytesReturned[toolName] = (this.stats.bytesReturned[toolName] ?? 0) + responseBytes;
|
|
@@ -21986,6 +22201,47 @@ var SessionTracker = class {
|
|
|
21986
22201
|
getSnapshot() {
|
|
21987
22202
|
return { ...this.stats };
|
|
21988
22203
|
}
|
|
22204
|
+
/** Load cumulative stats from disk */
|
|
22205
|
+
loadCumulative() {
|
|
22206
|
+
if (!this.cumulativeFile) return null;
|
|
22207
|
+
try {
|
|
22208
|
+
const data = readFileSync2(this.cumulativeFile, "utf-8");
|
|
22209
|
+
return JSON.parse(data);
|
|
22210
|
+
} catch {
|
|
22211
|
+
return null;
|
|
22212
|
+
}
|
|
22213
|
+
}
|
|
22214
|
+
/** Save current session stats to cumulative file */
|
|
22215
|
+
saveCumulative() {
|
|
22216
|
+
if (!this.cumulativeFile) return;
|
|
22217
|
+
const snap = this.stats;
|
|
22218
|
+
const keptOut = snap.bytesIndexed + snap.bytesSandboxed;
|
|
22219
|
+
const totalReturned = Object.values(snap.bytesReturned).reduce((a, b) => a + b, 0);
|
|
22220
|
+
const cumulative = this.loadCumulative() ?? {
|
|
22221
|
+
totalBytesSaved: 0,
|
|
22222
|
+
totalBytesProcessed: 0,
|
|
22223
|
+
totalCalls: 0,
|
|
22224
|
+
totalSessions: 0,
|
|
22225
|
+
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22226
|
+
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22227
|
+
perCommand: {}
|
|
22228
|
+
};
|
|
22229
|
+
cumulative.totalBytesSaved += keptOut;
|
|
22230
|
+
cumulative.totalBytesProcessed += keptOut + totalReturned;
|
|
22231
|
+
cumulative.totalCalls += Object.values(snap.calls).reduce((a, b) => a + b, 0);
|
|
22232
|
+
cumulative.totalSessions += 1;
|
|
22233
|
+
cumulative.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
22234
|
+
for (const [name, calls] of Object.entries(snap.calls)) {
|
|
22235
|
+
if (!cumulative.perCommand[name]) {
|
|
22236
|
+
cumulative.perCommand[name] = { calls: 0, bytesSaved: 0 };
|
|
22237
|
+
}
|
|
22238
|
+
cumulative.perCommand[name].calls += calls;
|
|
22239
|
+
}
|
|
22240
|
+
try {
|
|
22241
|
+
writeFileSync2(this.cumulativeFile, JSON.stringify(cumulative, null, 2));
|
|
22242
|
+
} catch {
|
|
22243
|
+
}
|
|
22244
|
+
}
|
|
21989
22245
|
formatReport() {
|
|
21990
22246
|
const snap = this.stats;
|
|
21991
22247
|
const elapsed = Date.now() - snap.sessionStart;
|
|
@@ -22046,6 +22302,18 @@ var SessionTracker = class {
|
|
|
22046
22302
|
`
|
|
22047
22303
|
Context-compress kept ${formatBytes(keptOut)} out of context (${reductionPct}% savings).`
|
|
22048
22304
|
);
|
|
22305
|
+
const cumulative = this.loadCumulative();
|
|
22306
|
+
if (cumulative) {
|
|
22307
|
+
lines.push("\n## Cumulative Savings (All Sessions)\n");
|
|
22308
|
+
lines.push("| Metric | Value |");
|
|
22309
|
+
lines.push("|--------|-------|");
|
|
22310
|
+
lines.push(`| Sessions tracked | ${cumulative.totalSessions} |`);
|
|
22311
|
+
lines.push(`| Total data processed | ${formatBytes(cumulative.totalBytesProcessed)} |`);
|
|
22312
|
+
lines.push(`| Total kept out of context | ${formatBytes(cumulative.totalBytesSaved)} |`);
|
|
22313
|
+
const cumTokensMid = Math.round(cumulative.totalBytesSaved / 4);
|
|
22314
|
+
lines.push(`| Est. total tokens saved | ~${cumTokensMid.toLocaleString()} |`);
|
|
22315
|
+
lines.push(`| Tracking since | ${cumulative.firstSeen.split("T")[0]} |`);
|
|
22316
|
+
}
|
|
22049
22317
|
return lines.join("\n");
|
|
22050
22318
|
}
|
|
22051
22319
|
};
|
|
@@ -22216,17 +22484,20 @@ function sanitizeQuery(raw) {
|
|
|
22216
22484
|
const words = q.split(/\s+/).filter((w) => w.length >= 2).map((w) => `"${w}"`);
|
|
22217
22485
|
return words.length > 0 ? words.join(" OR ") : "";
|
|
22218
22486
|
}
|
|
22219
|
-
function levenshtein(a, b) {
|
|
22487
|
+
function levenshtein(a, b, maxDist) {
|
|
22220
22488
|
if (a.length === 0) return b.length;
|
|
22221
22489
|
if (b.length === 0) return a.length;
|
|
22222
22490
|
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
22223
22491
|
let curr = new Array(b.length + 1);
|
|
22224
22492
|
for (let i = 1; i <= a.length; i++) {
|
|
22225
22493
|
curr[0] = i;
|
|
22494
|
+
let rowMin = curr[0];
|
|
22226
22495
|
for (let j = 1; j <= b.length; j++) {
|
|
22227
22496
|
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
22228
22497
|
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
22498
|
+
if (curr[j] < rowMin) rowMin = curr[j];
|
|
22229
22499
|
}
|
|
22500
|
+
if (maxDist !== void 0 && rowMin > maxDist) return maxDist + 1;
|
|
22230
22501
|
[prev, curr] = [curr, prev];
|
|
22231
22502
|
}
|
|
22232
22503
|
return prev[b.length];
|
|
@@ -22422,7 +22693,7 @@ var ContentStore = class {
|
|
|
22422
22693
|
let bestWord = word;
|
|
22423
22694
|
let bestDist = maxDist + 1;
|
|
22424
22695
|
for (const { word: candidate } of candidates) {
|
|
22425
|
-
const dist = levenshtein(word.toLowerCase(), candidate.toLowerCase());
|
|
22696
|
+
const dist = levenshtein(word.toLowerCase(), candidate.toLowerCase(), maxDist);
|
|
22426
22697
|
if (dist < bestDist && dist <= maxDist) {
|
|
22427
22698
|
bestDist = dist;
|
|
22428
22699
|
bestWord = candidate;
|
|
@@ -22439,7 +22710,8 @@ var ContentStore = class {
|
|
|
22439
22710
|
updateVocabulary(content) {
|
|
22440
22711
|
const currentCount = this.vocabCountStmt.get().cnt;
|
|
22441
22712
|
if (currentCount >= MAX_VOCABULARY) return;
|
|
22442
|
-
const
|
|
22713
|
+
const sample = content.length > 51200 ? content.slice(0, 51200) : content;
|
|
22714
|
+
const words = sample.split(WORD_SPLIT_RE).filter((w) => w.length >= 3 && !STOPWORDS.has(w.toLowerCase()));
|
|
22443
22715
|
const unique = new Set(words.map((w) => w.toLowerCase()));
|
|
22444
22716
|
const insert = this.vocabInsertStmt;
|
|
22445
22717
|
let added = 0;
|
|
@@ -22659,7 +22931,7 @@ function getVersion() {
|
|
|
22659
22931
|
try {
|
|
22660
22932
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22661
22933
|
const pkgPath = join4(__dirname, "..", "package.json");
|
|
22662
|
-
const pkg = JSON.parse(
|
|
22934
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
22663
22935
|
return pkg.version ?? "1.0.0";
|
|
22664
22936
|
} catch {
|
|
22665
22937
|
return "1.0.0";
|
|
@@ -22677,45 +22949,6 @@ function compactLabel(normal, level) {
|
|
|
22677
22949
|
}
|
|
22678
22950
|
return normal;
|
|
22679
22951
|
}
|
|
22680
|
-
async function limitConcurrency(tasks, limit) {
|
|
22681
|
-
const results = new Array(tasks.length);
|
|
22682
|
-
let nextIndex = 0;
|
|
22683
|
-
async function runNext() {
|
|
22684
|
-
while (nextIndex < tasks.length) {
|
|
22685
|
-
const index = nextIndex++;
|
|
22686
|
-
try {
|
|
22687
|
-
const value = await tasks[index]();
|
|
22688
|
-
results[index] = { status: "fulfilled", value };
|
|
22689
|
-
} catch (reason) {
|
|
22690
|
-
results[index] = { status: "rejected", reason };
|
|
22691
|
-
}
|
|
22692
|
-
}
|
|
22693
|
-
}
|
|
22694
|
-
const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => runNext());
|
|
22695
|
-
await Promise.all(workers);
|
|
22696
|
-
return results;
|
|
22697
|
-
}
|
|
22698
|
-
function detectInjectionPatterns(content) {
|
|
22699
|
-
const warnings = [];
|
|
22700
|
-
const patterns = [
|
|
22701
|
-
{ re: /ignore\s+(all\s+)?previous\s+instructions/i, label: "instruction override" },
|
|
22702
|
-
{ re: /you\s+are\s+now\s+/i, label: "role reassignment" },
|
|
22703
|
-
{
|
|
22704
|
-
re: /(?:^|\n)\s*system\s*:\s*(?:you are|you're|as an? )/im,
|
|
22705
|
-
label: "system prompt injection"
|
|
22706
|
-
},
|
|
22707
|
-
{ re: /\[INST\]|\[\/INST\]|<\|im_start\|>|<\|im_end\|>/i, label: "chat template injection" },
|
|
22708
|
-
{ re: /\n\n(?:Human|Assistant):/m, label: "chat delimiter injection" },
|
|
22709
|
-
{ re: /reveal\s+(your|the)\s+(system|secret|confidential)/i, label: "data exfiltration" },
|
|
22710
|
-
{ re: /act\s+as\s+(if\s+you\s+are|a)\s+/i, label: "role manipulation" }
|
|
22711
|
-
];
|
|
22712
|
-
for (const { re, label } of patterns) {
|
|
22713
|
-
if (re.test(content)) {
|
|
22714
|
-
warnings.push(label);
|
|
22715
|
-
}
|
|
22716
|
-
}
|
|
22717
|
-
return warnings;
|
|
22718
|
-
}
|
|
22719
22952
|
async function createServer(config3) {
|
|
22720
22953
|
const version2 = getVersion();
|
|
22721
22954
|
debug("Version:", version2);
|
|
@@ -22733,7 +22966,22 @@ async function createServer(config3) {
|
|
|
22733
22966
|
store = new ContentStore(":memory:");
|
|
22734
22967
|
dbFallback = true;
|
|
22735
22968
|
}
|
|
22736
|
-
const
|
|
22969
|
+
const cumulativeFile = config3.persistDb ? join4(config3.dbDir ?? join4(projectDir, ".context-compress"), "stats.json") : void 0;
|
|
22970
|
+
const tracker = new SessionTracker(cumulativeFile);
|
|
22971
|
+
let activeExecutions = 0;
|
|
22972
|
+
const MAX_CONCURRENT_EXECUTIONS = 8;
|
|
22973
|
+
const EXECUTION_LIMIT_ERROR = "Error: too many concurrent executions. Try again shortly.";
|
|
22974
|
+
async function withExecutionLimit(fn) {
|
|
22975
|
+
if (activeExecutions >= MAX_CONCURRENT_EXECUTIONS) {
|
|
22976
|
+
throw new Error(EXECUTION_LIMIT_ERROR);
|
|
22977
|
+
}
|
|
22978
|
+
activeExecutions++;
|
|
22979
|
+
try {
|
|
22980
|
+
return await fn();
|
|
22981
|
+
} finally {
|
|
22982
|
+
activeExecutions--;
|
|
22983
|
+
}
|
|
22984
|
+
}
|
|
22737
22985
|
function applyIntentFilter(output, intent, sourceLabel) {
|
|
22738
22986
|
if (Buffer.byteLength(output) <= config3.intentSearchThreshold) return output;
|
|
22739
22987
|
const indexed = store.index(output, sourceLabel);
|
|
@@ -22758,6 +23006,14 @@ Searchable terms: ${terms.join(", ")}
|
|
|
22758
23006
|
return compactLabel(filtered, config3.compressionLevel);
|
|
22759
23007
|
}
|
|
22760
23008
|
const shutdown = () => {
|
|
23009
|
+
try {
|
|
23010
|
+
tracker.saveCumulative();
|
|
23011
|
+
} catch {
|
|
23012
|
+
}
|
|
23013
|
+
try {
|
|
23014
|
+
executor.shutdown();
|
|
23015
|
+
} catch {
|
|
23016
|
+
}
|
|
22761
23017
|
try {
|
|
22762
23018
|
store.close();
|
|
22763
23019
|
} catch {
|
|
@@ -22766,6 +23022,16 @@ Searchable terms: ${terms.join(", ")}
|
|
|
22766
23022
|
process.on("SIGINT", shutdown);
|
|
22767
23023
|
process.on("SIGTERM", shutdown);
|
|
22768
23024
|
process.on("beforeExit", shutdown);
|
|
23025
|
+
process.on("uncaughtException", (err) => {
|
|
23026
|
+
debug("Uncaught exception:", err);
|
|
23027
|
+
shutdown();
|
|
23028
|
+
process.exit(1);
|
|
23029
|
+
});
|
|
23030
|
+
process.on("unhandledRejection", (err) => {
|
|
23031
|
+
debug("Unhandled rejection:", err);
|
|
23032
|
+
shutdown();
|
|
23033
|
+
process.exit(1);
|
|
23034
|
+
});
|
|
22769
23035
|
const searchCalls = [];
|
|
22770
23036
|
const server2 = new McpServer({
|
|
22771
23037
|
name: "context-compress",
|
|
@@ -22787,7 +23053,24 @@ PREFER THIS OVER BASH for: API calls (gh, curl, aws), test runners (npm test, py
|
|
|
22787
23053
|
timeout: external_exports.number().default(3e4).describe("Max execution time in ms")
|
|
22788
23054
|
},
|
|
22789
23055
|
async ({ language, code, intent, timeout }) => {
|
|
22790
|
-
const
|
|
23056
|
+
const codeBytes = Buffer.byteLength(code);
|
|
23057
|
+
if (codeBytes > 1024e3) {
|
|
23058
|
+
return {
|
|
23059
|
+
content: [
|
|
23060
|
+
{
|
|
23061
|
+
type: "text",
|
|
23062
|
+
text: `Error: code too large (${(codeBytes / 1024).toFixed(0)}KB). Max 1MB.`
|
|
23063
|
+
}
|
|
23064
|
+
]
|
|
23065
|
+
};
|
|
23066
|
+
}
|
|
23067
|
+
let result;
|
|
23068
|
+
try {
|
|
23069
|
+
result = await withExecutionLimit(() => executor.execute({ language, code, timeout }));
|
|
23070
|
+
} catch (e) {
|
|
23071
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
23072
|
+
return { content: [{ type: "text", text: msg }] };
|
|
23073
|
+
}
|
|
22791
23074
|
if (result.networkBytes) {
|
|
22792
23075
|
tracker.trackSandboxed(result.networkBytes);
|
|
22793
23076
|
}
|
|
@@ -22819,6 +23102,17 @@ ${result.stderr}`;
|
|
|
22819
23102
|
timeout: external_exports.number().default(3e4).describe("Max execution time in ms")
|
|
22820
23103
|
},
|
|
22821
23104
|
async ({ path: filePath, language, code, intent, timeout }) => {
|
|
23105
|
+
const codeBytes = Buffer.byteLength(code);
|
|
23106
|
+
if (codeBytes > 1024e3) {
|
|
23107
|
+
return {
|
|
23108
|
+
content: [
|
|
23109
|
+
{
|
|
23110
|
+
type: "text",
|
|
23111
|
+
text: `Error: code too large (${(codeBytes / 1024).toFixed(0)}KB). Max 1MB.`
|
|
23112
|
+
}
|
|
23113
|
+
]
|
|
23114
|
+
};
|
|
23115
|
+
}
|
|
22822
23116
|
const absPath = resolve(projectDir, filePath);
|
|
22823
23117
|
if (!isWithinProject(absPath)) {
|
|
22824
23118
|
return {
|
|
@@ -22830,12 +23124,20 @@ ${result.stderr}`;
|
|
|
22830
23124
|
]
|
|
22831
23125
|
};
|
|
22832
23126
|
}
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
22838
|
-
|
|
23127
|
+
let result;
|
|
23128
|
+
try {
|
|
23129
|
+
result = await withExecutionLimit(
|
|
23130
|
+
() => executor.executeFile({
|
|
23131
|
+
language,
|
|
23132
|
+
code,
|
|
23133
|
+
filePath: absPath,
|
|
23134
|
+
timeout
|
|
23135
|
+
})
|
|
23136
|
+
);
|
|
23137
|
+
} catch (e) {
|
|
23138
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
23139
|
+
return { content: [{ type: "text", text: msg }] };
|
|
23140
|
+
}
|
|
22839
23141
|
let output = result.stdout;
|
|
22840
23142
|
if (result.stderr && result.exitCode !== 0) {
|
|
22841
23143
|
output += `
|
|
@@ -22886,7 +23188,7 @@ ${result.stderr}`;
|
|
|
22886
23188
|
]
|
|
22887
23189
|
};
|
|
22888
23190
|
}
|
|
22889
|
-
text =
|
|
23191
|
+
text = readFileSync3(absPath, "utf-8");
|
|
22890
23192
|
label = source ?? filePath;
|
|
22891
23193
|
} catch (e) {
|
|
22892
23194
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -23020,11 +23322,19 @@ ${hit.snippet}
|
|
|
23020
23322
|
}
|
|
23021
23323
|
const label = source ?? url;
|
|
23022
23324
|
const fetchCode = buildFetchCode(url, resolvedIp);
|
|
23023
|
-
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
|
|
23027
|
-
|
|
23325
|
+
let result;
|
|
23326
|
+
try {
|
|
23327
|
+
result = await withExecutionLimit(
|
|
23328
|
+
() => executor.execute({
|
|
23329
|
+
language: "javascript",
|
|
23330
|
+
code: fetchCode,
|
|
23331
|
+
timeout: 3e4
|
|
23332
|
+
})
|
|
23333
|
+
);
|
|
23334
|
+
} catch (e) {
|
|
23335
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
23336
|
+
return { content: [{ type: "text", text: msg }] };
|
|
23337
|
+
}
|
|
23028
23338
|
if (result.exitCode !== 0 || !result.stdout.trim()) {
|
|
23029
23339
|
const errMsg = `Failed to fetch ${url}: ${result.stderr || "empty response"}`;
|
|
23030
23340
|
tracker.trackCall("fetch_and_index", Buffer.byteLength(errMsg));
|
|
@@ -23076,11 +23386,13 @@ Searchable terms: ${terms.join(", ")}`;
|
|
|
23076
23386
|
async ({ commands, queries, timeout }) => {
|
|
23077
23387
|
const commandResults = await limitConcurrency(
|
|
23078
23388
|
commands.map((cmd) => async () => {
|
|
23079
|
-
const result = await
|
|
23080
|
-
|
|
23081
|
-
|
|
23082
|
-
|
|
23083
|
-
|
|
23389
|
+
const result = await withExecutionLimit(
|
|
23390
|
+
() => executor.execute({
|
|
23391
|
+
language: "shell",
|
|
23392
|
+
code: cmd.command,
|
|
23393
|
+
timeout
|
|
23394
|
+
})
|
|
23395
|
+
);
|
|
23084
23396
|
return { label: cmd.label, result };
|
|
23085
23397
|
}),
|
|
23086
23398
|
4
|
|
@@ -23157,6 +23469,7 @@ Searchable terms: ${terms.join(", ")}`;
|
|
|
23157
23469
|
"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, context savings ratio, and visual charts.",
|
|
23158
23470
|
{},
|
|
23159
23471
|
async () => {
|
|
23472
|
+
tracker.saveCumulative();
|
|
23160
23473
|
const report = tracker.formatReport();
|
|
23161
23474
|
tracker.trackCall("stats", Buffer.byteLength(report));
|
|
23162
23475
|
return { content: [{ type: "text", text: report }] };
|
|
@@ -23262,7 +23575,14 @@ const resp = await fetch(url, { redirect: 'error' });`;
|
|
|
23262
23575
|
}
|
|
23263
23576
|
return `${fetchSetup}
|
|
23264
23577
|
if (!resp.ok) { console.error("HTTP " + resp.status); process.exit(1); }
|
|
23578
|
+
const cl = resp.headers.get('content-length');
|
|
23579
|
+
if (cl && parseInt(cl, 10) > 10 * 1024 * 1024) {
|
|
23580
|
+
console.error("Response too large: " + cl + " bytes"); process.exit(1);
|
|
23581
|
+
}
|
|
23265
23582
|
const html = await resp.text();
|
|
23583
|
+
if (html.length > 10 * 1024 * 1024) {
|
|
23584
|
+
console.error("Response body too large: " + html.length + " chars"); process.exit(1);
|
|
23585
|
+
}
|
|
23266
23586
|
|
|
23267
23587
|
// Strip unwanted tags
|
|
23268
23588
|
let md = html
|