squeezr-ai 1.81.2 → 1.99.2
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/bin/squeezr.js +82 -1
- package/dist/__tests__/bench.test.d.ts +1 -0
- package/dist/__tests__/bench.test.js +55 -0
- package/dist/__tests__/cacheAligner.test.d.ts +1 -0
- package/dist/__tests__/cacheAligner.test.js +75 -0
- package/dist/__tests__/codeStructure.test.d.ts +1 -0
- package/dist/__tests__/codeStructure.test.js +74 -0
- package/dist/__tests__/compressor.test.js +5 -1
- package/dist/__tests__/contentRouter.test.d.ts +1 -0
- package/dist/__tests__/contentRouter.test.js +76 -0
- package/dist/__tests__/controlEndpointGuard.test.d.ts +1 -0
- package/dist/__tests__/controlEndpointGuard.test.js +61 -0
- package/dist/__tests__/deterministic.test.js +13 -0
- package/dist/__tests__/doctor.test.d.ts +1 -0
- package/dist/__tests__/doctor.test.js +86 -0
- package/dist/__tests__/jsonCrush.test.d.ts +1 -0
- package/dist/__tests__/jsonCrush.test.js +168 -0
- package/dist/__tests__/learn.test.d.ts +1 -0
- package/dist/__tests__/learn.test.js +159 -0
- package/dist/__tests__/outputSavings.test.d.ts +1 -0
- package/dist/__tests__/outputSavings.test.js +49 -0
- package/dist/__tests__/outputShaper.test.d.ts +1 -0
- package/dist/__tests__/outputShaper.test.js +216 -0
- package/dist/__tests__/relevance.test.d.ts +1 -0
- package/dist/__tests__/relevance.test.js +66 -0
- package/dist/__tests__/secureKeyFile.test.d.ts +1 -0
- package/dist/__tests__/secureKeyFile.test.js +27 -0
- package/dist/__tests__/serverBinding.test.d.ts +1 -0
- package/dist/__tests__/serverBinding.test.js +18 -0
- package/dist/__tests__/statsOutput.test.d.ts +1 -0
- package/dist/__tests__/statsOutput.test.js +46 -0
- package/dist/__tests__/textCrusher.test.d.ts +1 -0
- package/dist/__tests__/textCrusher.test.js +133 -0
- package/dist/bench.d.ts +45 -0
- package/dist/bench.js +114 -0
- package/dist/cacheAligner.d.ts +34 -0
- package/dist/cacheAligner.js +73 -0
- package/dist/codexMitm.d.ts +1 -0
- package/dist/codexMitm.js +26 -2
- package/dist/compressor.js +42 -4
- package/dist/config.d.ts +6 -0
- package/dist/config.js +25 -0
- package/dist/contentRouter.d.ts +30 -0
- package/dist/contentRouter.js +112 -0
- package/dist/dashboard.d.ts +1 -1
- package/dist/dashboard.js +26 -7
- package/dist/deterministic.d.ts +4 -1
- package/dist/deterministic.js +43 -10
- package/dist/doctor.d.ts +38 -0
- package/dist/doctor.js +113 -0
- package/dist/index.js +4 -1
- package/dist/jsonCrush.d.ts +34 -0
- package/dist/jsonCrush.js +177 -0
- package/dist/learn.d.ts +65 -0
- package/dist/learn.js +244 -0
- package/dist/outputSavings.d.ts +20 -0
- package/dist/outputSavings.js +52 -0
- package/dist/outputShaper.d.ts +71 -0
- package/dist/outputShaper.js +155 -0
- package/dist/relevance.d.ts +27 -0
- package/dist/relevance.js +78 -0
- package/dist/server.js +106 -11
- package/dist/stats.d.ts +17 -0
- package/dist/stats.js +55 -0
- package/dist/systemPrompt.js +3 -2
- package/dist/textCrusher.d.ts +35 -0
- package/dist/textCrusher.js +160 -0
- package/package.json +69 -69
- package/squeezr.toml +15 -1
package/bin/squeezr.js
CHANGED
|
@@ -5,7 +5,7 @@ import http from 'http'
|
|
|
5
5
|
import path from 'path'
|
|
6
6
|
import fs from 'fs'
|
|
7
7
|
import os from 'os'
|
|
8
|
-
import { fileURLToPath } from 'url'
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
9
9
|
import { createRequire } from 'module'
|
|
10
10
|
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -250,8 +250,14 @@ Usage:
|
|
|
250
250
|
squeezr gain Show token savings stats
|
|
251
251
|
squeezr gain --reset Reset saved stats
|
|
252
252
|
squeezr discover Show pattern coverage report (proxy must be running)
|
|
253
|
+
squeezr learn Mine Claude Code sessions for token-wasting loops
|
|
254
|
+
squeezr learn --apply Write the learned rules to ./CLAUDE.local.md
|
|
255
|
+
squeezr bench Run the compression + fact-recall benchmark
|
|
253
256
|
squeezr status Check if proxy is running
|
|
257
|
+
squeezr doctor Diagnose proxy/version/routing/bypass (exit 0/1/2)
|
|
254
258
|
squeezr config Print config file path and current settings
|
|
259
|
+
squeezr rc [args...] Launch 'claude --rc' (Remote Control) direct to api.anthropic.com
|
|
260
|
+
(proxy bypassed for that session — Claude Code requires it)
|
|
255
261
|
squeezr mcp install Register Squeezr MCP server in Claude Code, Cursor, Windsurf & Cline
|
|
256
262
|
squeezr mcp uninstall Remove Squeezr MCP registration
|
|
257
263
|
squeezr ports Change HTTP and MITM proxy ports
|
|
@@ -284,6 +290,47 @@ function runNode(script, extraArgs = []) {
|
|
|
284
290
|
child.on('exit', code => process.exit(code ?? 0))
|
|
285
291
|
}
|
|
286
292
|
|
|
293
|
+
// Launch Claude Code with Remote Control, bypassing the Squeezr proxy for THIS
|
|
294
|
+
// session only. Claude Code refuses Remote Control unless it talks DIRECTLY to
|
|
295
|
+
// api.anthropic.com — the binary guard is literally "Remote Control is only
|
|
296
|
+
// available when using Claude via api.anthropic.com." Squeezr's terminal setup
|
|
297
|
+
// exports ANTHROPIC_BASE_URL=http://localhost:<port>, which is not that host, so
|
|
298
|
+
// `claude --rc` errors out. We strip that var (and the MITM CA) from the CHILD
|
|
299
|
+
// env only. Global config is untouched: every other `claude` session still routes
|
|
300
|
+
// through Squeezr and compresses normally. Trade-off: this remote session is not
|
|
301
|
+
// compressed — remote sessions are short/interactive, so that's the right call.
|
|
302
|
+
function claudeRc(extraArgs) {
|
|
303
|
+
const env = { ...process.env }
|
|
304
|
+
delete env.ANTHROPIC_BASE_URL
|
|
305
|
+
delete env.anthropic_base_url
|
|
306
|
+
delete env.NODE_EXTRA_CA_CERTS // Squeezr's MITM CA — irrelevant talking direct
|
|
307
|
+
|
|
308
|
+
// Ensure --rc / --remote-control is present exactly once, then pass the rest through.
|
|
309
|
+
const rest = extraArgs.filter(a => a !== 'rc' && a !== 'remote-control')
|
|
310
|
+
const hasRc = rest.some(a => a === '--rc' || a === '--remote-control')
|
|
311
|
+
const claudeArgs = hasRc ? rest : ['--rc', ...rest]
|
|
312
|
+
|
|
313
|
+
console.log('Launching Claude Code with Remote Control — direct to api.anthropic.com')
|
|
314
|
+
console.log('(Squeezr proxy bypassed for THIS session only; global config untouched).\n')
|
|
315
|
+
|
|
316
|
+
// Never resolves — the child owns the terminal (stdio inherited) and we exit the
|
|
317
|
+
// wrapper with the child's code. Awaiting this blocks the trailing update banner
|
|
318
|
+
// from printing over Claude's interactive UI.
|
|
319
|
+
return new Promise(() => {
|
|
320
|
+
const child = spawn('claude', claudeArgs, {
|
|
321
|
+
stdio: 'inherit',
|
|
322
|
+
env,
|
|
323
|
+
shell: process.platform === 'win32', // resolve claude.exe via PATH on Windows
|
|
324
|
+
})
|
|
325
|
+
child.on('exit', code => process.exit(code ?? 0))
|
|
326
|
+
child.on('error', err => {
|
|
327
|
+
console.error(`\nFailed to launch claude: ${err.message}`)
|
|
328
|
+
console.error('Is Claude Code installed and on your PATH? Try running `claude --version`.')
|
|
329
|
+
process.exit(1)
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
287
334
|
async function startDaemon() {
|
|
288
335
|
const distIndex = path.join(ROOT, 'dist', 'index.js')
|
|
289
336
|
if (!fs.existsSync(distIndex)) {
|
|
@@ -2504,6 +2551,11 @@ switch (command) {
|
|
|
2504
2551
|
showConfig()
|
|
2505
2552
|
break
|
|
2506
2553
|
|
|
2554
|
+
case 'rc':
|
|
2555
|
+
case 'remote-control':
|
|
2556
|
+
await claudeRc(args.slice(1))
|
|
2557
|
+
break
|
|
2558
|
+
|
|
2507
2559
|
case 'mcp': {
|
|
2508
2560
|
const subCmd = args[1] ?? 'install'
|
|
2509
2561
|
if (subCmd === 'uninstall') await mcpUninstall()
|
|
@@ -2514,6 +2566,35 @@ case 'zest':
|
|
|
2514
2566
|
await installZest()
|
|
2515
2567
|
break
|
|
2516
2568
|
|
|
2569
|
+
case 'doctor': {
|
|
2570
|
+
const { runDoctor } = await import(pathToFileURL(path.join(ROOT, 'dist', 'doctor.js')).href)
|
|
2571
|
+
const res = await runDoctor()
|
|
2572
|
+
console.log(res.report)
|
|
2573
|
+
if (command !== 'update') await showUpdateBanner()
|
|
2574
|
+
process.exit(res.exitCode)
|
|
2575
|
+
break
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
case 'bench': {
|
|
2579
|
+
const { runBench, formatBench } = await import(pathToFileURL(path.join(ROOT, 'dist', 'bench.js')).href)
|
|
2580
|
+
console.log(formatBench(runBench()))
|
|
2581
|
+
break
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
case 'learn': {
|
|
2585
|
+
const apply = args.includes('--apply')
|
|
2586
|
+
const tIdx = args.indexOf('--target')
|
|
2587
|
+
const targetFile = tIdx >= 0 ? args[tIdx + 1] : undefined
|
|
2588
|
+
const { runLearn } = await import(pathToFileURL(path.join(ROOT, 'dist', 'learn.js')).href)
|
|
2589
|
+
const res = runLearn({ apply, targetFile })
|
|
2590
|
+
console.log(res.report)
|
|
2591
|
+
if (res.loops.length > 0) {
|
|
2592
|
+
if (res.applied) console.log(`\nWrote ${res.loops.length} rule(s) to ${res.targetFile}`)
|
|
2593
|
+
else console.log(`\nDry run. Re-run with --apply to write these rules to ${res.targetFile}.`)
|
|
2594
|
+
}
|
|
2595
|
+
break
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2517
2598
|
case 'version':
|
|
2518
2599
|
case '--version':
|
|
2519
2600
|
case '-v':
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { factRecall, runBench, FIXTURES } from '../bench.js';
|
|
3
|
+
describe('factRecall', () => {
|
|
4
|
+
it('returns 1 when every fact is present verbatim', () => {
|
|
5
|
+
expect(factRecall('error E123 in file foo.ts at line 42', ['E123', 'foo.ts', '42'])).toBe(1);
|
|
6
|
+
});
|
|
7
|
+
it('returns the fraction present', () => {
|
|
8
|
+
expect(factRecall('only E123 here', ['E123', 'MISSING'])).toBe(0.5);
|
|
9
|
+
});
|
|
10
|
+
it('returns 1 for an empty fact list (nothing to lose)', () => {
|
|
11
|
+
expect(factRecall('whatever', [])).toBe(1);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('FIXTURES', () => {
|
|
15
|
+
it('every fixture declares its critical facts', () => {
|
|
16
|
+
expect(FIXTURES.length).toBeGreaterThan(0);
|
|
17
|
+
for (const f of FIXTURES) {
|
|
18
|
+
expect(f.facts.length).toBeGreaterThan(0);
|
|
19
|
+
// sanity: the facts must actually be in the original content
|
|
20
|
+
for (const fact of f.facts)
|
|
21
|
+
expect(f.content.includes(fact)).toBe(true);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('runBench', () => {
|
|
26
|
+
it('produces a row per fixture with a valid shape', () => {
|
|
27
|
+
const summary = runBench();
|
|
28
|
+
expect(summary.rows.length).toBe(FIXTURES.length);
|
|
29
|
+
for (const r of summary.rows) {
|
|
30
|
+
expect(r.originalChars).toBeGreaterThan(0);
|
|
31
|
+
expect(r.compressedChars).toBeGreaterThan(0);
|
|
32
|
+
expect(r.compressionPct).toBeGreaterThanOrEqual(0);
|
|
33
|
+
expect(r.inlineRecall).toBeGreaterThanOrEqual(0);
|
|
34
|
+
expect(r.inlineRecall).toBeLessThanOrEqual(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
it('achieves real compression on at least one fixture', () => {
|
|
38
|
+
const summary = runBench();
|
|
39
|
+
expect(summary.rows.some(r => r.compressionPct > 10)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('never drops a critical fact from the highly-compressible JSON fixture', () => {
|
|
42
|
+
// The homogeneous JSON array is crushed to a table — all VALUES must stay inline.
|
|
43
|
+
const summary = runBench();
|
|
44
|
+
const json = summary.rows.find(r => r.name.includes('json'));
|
|
45
|
+
expect(json).toBeDefined();
|
|
46
|
+
expect(json.compressionPct).toBeGreaterThan(10);
|
|
47
|
+
expect(json.inlineRecall).toBe(1);
|
|
48
|
+
});
|
|
49
|
+
it('reports honest averages', () => {
|
|
50
|
+
const summary = runBench();
|
|
51
|
+
expect(summary.avgCompressionPct).toBeGreaterThan(0);
|
|
52
|
+
expect(summary.avgInlineRecall).toBeGreaterThan(0);
|
|
53
|
+
expect(summary.avgInlineRecall).toBeLessThanOrEqual(1);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { detectVolatile, analyzeSystemPrompt, formatVolatileWarning, _resetWarnedForTest, warnIfVolatile, } from '../cacheAligner.js';
|
|
3
|
+
describe('detectVolatile', () => {
|
|
4
|
+
it('detects a UUID', () => {
|
|
5
|
+
const f = detectVolatile('session 550e8400-e29b-41d4-a716-446655440000 started');
|
|
6
|
+
expect(f.find(x => x.kind === 'uuid')?.count).toBe(1);
|
|
7
|
+
});
|
|
8
|
+
it('detects ISO timestamps', () => {
|
|
9
|
+
const f = detectVolatile('at 2026-07-21T10:00:00Z and 2026-07-21T11:00:00Z');
|
|
10
|
+
expect(f.find(x => x.kind === 'timestamp')?.count).toBe(2);
|
|
11
|
+
});
|
|
12
|
+
it('detects a JWT', () => {
|
|
13
|
+
const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
|
14
|
+
const f = detectVolatile(`token=${jwt}`);
|
|
15
|
+
expect(f.find(x => x.kind === 'jwt')?.count).toBe(1);
|
|
16
|
+
});
|
|
17
|
+
it('detects long hex hashes', () => {
|
|
18
|
+
const sha = 'a'.repeat(64);
|
|
19
|
+
const f = detectVolatile(`commit ${sha}`);
|
|
20
|
+
expect(f.find(x => x.kind === 'hash')?.count).toBe(1);
|
|
21
|
+
});
|
|
22
|
+
it('returns nothing for clean stable text', () => {
|
|
23
|
+
expect(detectVolatile('You are a helpful coding assistant. Follow the rules.')).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
it('does not double-count a JWT as a hash', () => {
|
|
26
|
+
const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
|
27
|
+
const f = detectVolatile(jwt);
|
|
28
|
+
expect(f.find(x => x.kind === 'hash')).toBeUndefined();
|
|
29
|
+
expect(f.find(x => x.kind === 'jwt')?.count).toBe(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('analyzeSystemPrompt', () => {
|
|
33
|
+
it('handles a string system prompt', () => {
|
|
34
|
+
const r = analyzeSystemPrompt('build id 550e8400-e29b-41d4-a716-446655440000');
|
|
35
|
+
expect(r.hasVolatile).toBe(true);
|
|
36
|
+
expect(r.findings.length).toBeGreaterThan(0);
|
|
37
|
+
});
|
|
38
|
+
it('aggregates across an array of text blocks', () => {
|
|
39
|
+
const sys = [
|
|
40
|
+
{ type: 'text', text: 'stable preamble' },
|
|
41
|
+
{ type: 'text', text: 'ts 2026-07-21T10:00:00Z' },
|
|
42
|
+
];
|
|
43
|
+
const r = analyzeSystemPrompt(sys);
|
|
44
|
+
expect(r.findings.find(f => f.kind === 'timestamp')?.count).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
it('reports clean when there is nothing volatile', () => {
|
|
47
|
+
const r = analyzeSystemPrompt('You are Claude, a coding assistant.');
|
|
48
|
+
expect(r.hasVolatile).toBe(false);
|
|
49
|
+
expect(r.findings).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
it('ignores non-text blocks and undefined', () => {
|
|
52
|
+
expect(analyzeSystemPrompt(undefined).hasVolatile).toBe(false);
|
|
53
|
+
const sys = [{ type: 'image', source: {} }, { type: 'text', text: 'clean' }];
|
|
54
|
+
expect(analyzeSystemPrompt(sys).hasVolatile).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('formatVolatileWarning', () => {
|
|
58
|
+
it('mentions each detected kind', () => {
|
|
59
|
+
const msg = formatVolatileWarning([{ kind: 'uuid', count: 2 }, { kind: 'timestamp', count: 1 }]);
|
|
60
|
+
expect(msg.includes('uuid')).toBe(true);
|
|
61
|
+
expect(msg.includes('timestamp')).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('warnIfVolatile', () => {
|
|
65
|
+
it('warns once per distinct finding signature (no per-request spam)', () => {
|
|
66
|
+
_resetWarnedForTest();
|
|
67
|
+
const sys = 'id 550e8400-e29b-41d4-a716-446655440000';
|
|
68
|
+
expect(warnIfVolatile(sys)).toBe(true); // first time → warns
|
|
69
|
+
expect(warnIfVolatile(sys)).toBe(false); // same signature → suppressed
|
|
70
|
+
});
|
|
71
|
+
it('does not warn on clean prompts', () => {
|
|
72
|
+
_resetWarnedForTest();
|
|
73
|
+
expect(warnIfVolatile('perfectly stable system prompt')).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { detectCodeLanguage, extractCodeStructure } from '../deterministic.js';
|
|
3
|
+
import { retrieveOriginal } from '../expand.js';
|
|
4
|
+
describe('detectCodeLanguage', () => {
|
|
5
|
+
it('detects the existing languages', () => {
|
|
6
|
+
expect(detectCodeLanguage("import x from 'y'\nexport function f(): void {}")).toBe('ts');
|
|
7
|
+
expect(detectCodeLanguage('from os import path\ndef f():\n pass')).toBe('py');
|
|
8
|
+
expect(detectCodeLanguage('package main\nfunc main() {}')).toBe('go');
|
|
9
|
+
expect(detectCodeLanguage('use std::io;\npub fn main() {}')).toBe('rs');
|
|
10
|
+
});
|
|
11
|
+
it('detects Java', () => {
|
|
12
|
+
const java = 'package com.acme.app;\nimport java.util.List;\npublic class Service {\n public void run() {}\n}';
|
|
13
|
+
expect(detectCodeLanguage(java)).toBe('java');
|
|
14
|
+
});
|
|
15
|
+
it('detects C++ (has std::/class/namespace)', () => {
|
|
16
|
+
const cpp = '#include <vector>\n#include <string>\nnamespace app {\nclass Widget {\n void draw() {}\n};\n}';
|
|
17
|
+
expect(detectCodeLanguage(cpp)).toBe('cpp');
|
|
18
|
+
});
|
|
19
|
+
it('detects C (includes, no C++ markers)', () => {
|
|
20
|
+
const c = '#include <stdio.h>\n#include <stdlib.h>\nint main(int argc, char **argv) {\n return 0;\n}';
|
|
21
|
+
expect(detectCodeLanguage(c)).toBe('c');
|
|
22
|
+
});
|
|
23
|
+
it('returns null for non-code prose', () => {
|
|
24
|
+
expect(detectCodeLanguage('This is a plain english log line.\nAnother sentence here.')).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('extractCodeStructure — Java', () => {
|
|
28
|
+
function bigJava() {
|
|
29
|
+
const lines = ['package com.acme;', 'import java.util.List;', '', 'public class Orders {'];
|
|
30
|
+
for (let m = 0; m < 6; m++) {
|
|
31
|
+
lines.push(` public int method${m}(int a, int b) {`);
|
|
32
|
+
for (let b = 0; b < 15; b++)
|
|
33
|
+
lines.push(` int local${b} = a + b + ${m};`);
|
|
34
|
+
lines.push(' return a;');
|
|
35
|
+
lines.push(' }');
|
|
36
|
+
}
|
|
37
|
+
lines.push('}');
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
}
|
|
40
|
+
it('keeps signatures, elides bodies, and stays smaller', () => {
|
|
41
|
+
const input = bigJava();
|
|
42
|
+
const out = extractCodeStructure(input, 'java');
|
|
43
|
+
expect(out).not.toBe(input);
|
|
44
|
+
expect(out.length).toBeLessThan(input.length);
|
|
45
|
+
// signatures survive
|
|
46
|
+
expect(out.includes('public int method0(int a, int b)')).toBe(true);
|
|
47
|
+
expect(out.includes('public class Orders')).toBe(true);
|
|
48
|
+
// an omitted-lines marker is present
|
|
49
|
+
expect(/implementation lines omitted|lines omitted/.test(out)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it('is fully recoverable via the parent expand id', () => {
|
|
52
|
+
const input = bigJava();
|
|
53
|
+
const out = extractCodeStructure(input, 'java');
|
|
54
|
+
const m = out.match(/squeezr_expand\("([0-9a-f]{6,})"\)/);
|
|
55
|
+
expect(m).not.toBeNull();
|
|
56
|
+
expect(retrieveOriginal(m[1])).toBe(input);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('extractCodeStructure — C++', () => {
|
|
60
|
+
it('keeps class + method signatures', () => {
|
|
61
|
+
const lines = ['#include <vector>', '', 'class Engine {', 'public:'];
|
|
62
|
+
for (let m = 0; m < 5; m++) {
|
|
63
|
+
lines.push(` void step${m}(double dt) {`);
|
|
64
|
+
for (let b = 0; b < 12; b++)
|
|
65
|
+
lines.push(` acc += dt * ${b};`);
|
|
66
|
+
lines.push(' }');
|
|
67
|
+
}
|
|
68
|
+
lines.push('};');
|
|
69
|
+
const input = lines.join('\n');
|
|
70
|
+
const out = extractCodeStructure(input, 'cpp');
|
|
71
|
+
expect(out.includes('void step0(double dt)')).toBe(true);
|
|
72
|
+
expect(out.length).toBeLessThan(input.length);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -296,7 +296,11 @@ describe('compressGeminiContents', () => {
|
|
|
296
296
|
const cts = makeContents(['g'.repeat(200), 'h'.repeat(200)]);
|
|
297
297
|
await compressGeminiContents(cts, 'my-google-key', baseConfig);
|
|
298
298
|
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('generativelanguage.googleapis.com'), expect.any(Object));
|
|
299
|
-
|
|
299
|
+
// Security: the key travels in the x-goog-api-key header, never in the URL.
|
|
300
|
+
const [calledUrl, calledOpts] = mockFetch.mock.calls.at(-1);
|
|
301
|
+
expect(calledUrl).not.toContain('my-google-key');
|
|
302
|
+
expect(calledUrl).not.toContain('key=');
|
|
303
|
+
expect(calledOpts.headers['x-goog-api-key']).toBe('my-google-key');
|
|
300
304
|
});
|
|
301
305
|
it('returns dry-run without modifications', async () => {
|
|
302
306
|
const oldText = 'g'.repeat(200);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { findObjectArraySpans, crushEmbeddedJson, TABLE_MARKER_RE } from '../contentRouter.js';
|
|
3
|
+
import { retrieveOriginal } from '../expand.js';
|
|
4
|
+
function bigArray(prefix = '', suffix = '') {
|
|
5
|
+
const rows = Array.from({ length: 20 }, (_, i) => ({ id: i, name: `svc-${i}`, status: 'ok', region: 'us-east-1' }));
|
|
6
|
+
return `${prefix}${JSON.stringify(rows)}${suffix}`;
|
|
7
|
+
}
|
|
8
|
+
describe('findObjectArraySpans', () => {
|
|
9
|
+
it('finds a whole-text array of objects', () => {
|
|
10
|
+
const t = JSON.stringify([{ a: 1 }, { a: 2 }]);
|
|
11
|
+
const spans = findObjectArraySpans(t);
|
|
12
|
+
expect(spans.length).toBe(1);
|
|
13
|
+
expect(t.slice(spans[0].start, spans[0].end)).toBe(t);
|
|
14
|
+
});
|
|
15
|
+
it('finds an array embedded between prose', () => {
|
|
16
|
+
const t = bigArray('Here are the pods:\n', '\nDone.');
|
|
17
|
+
const spans = findObjectArraySpans(t);
|
|
18
|
+
expect(spans.length).toBe(1);
|
|
19
|
+
const sub = t.slice(spans[0].start, spans[0].end);
|
|
20
|
+
expect(sub.startsWith('[{')).toBe(true);
|
|
21
|
+
expect(sub.endsWith('}]')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('handles brackets inside string values without breaking', () => {
|
|
24
|
+
const t = 'x ' + JSON.stringify([{ note: 'has ] and } inside' }, { note: 'more [ [ {' }]) + ' y';
|
|
25
|
+
const spans = findObjectArraySpans(t);
|
|
26
|
+
expect(spans.length).toBe(1);
|
|
27
|
+
expect(() => JSON.parse(t.slice(spans[0].start, spans[0].end))).not.toThrow();
|
|
28
|
+
});
|
|
29
|
+
it('finds two separate embedded arrays', () => {
|
|
30
|
+
const t = `first ${JSON.stringify([{ a: 1 }, { a: 2 }])} middle ${JSON.stringify([{ b: 3 }, { b: 4 }])} end`;
|
|
31
|
+
expect(findObjectArraySpans(t).length).toBe(2);
|
|
32
|
+
});
|
|
33
|
+
it('ignores arrays of scalars (not object arrays)', () => {
|
|
34
|
+
expect(findObjectArraySpans('[1, 2, 3, 4, 5]').length).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('crushEmbeddedJson', () => {
|
|
38
|
+
it('crushes an array embedded in prose, preserving the surrounding text', () => {
|
|
39
|
+
const t = bigArray('POD STATUS REPORT\n', '\n(end of report)');
|
|
40
|
+
const res = crushEmbeddedJson(t);
|
|
41
|
+
expect(res.savedChars).toBeGreaterThan(0);
|
|
42
|
+
expect(res.text.startsWith('POD STATUS REPORT')).toBe(true);
|
|
43
|
+
expect(res.text.includes('(end of report)')).toBe(true);
|
|
44
|
+
expect(TABLE_MARKER_RE.test(res.text)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
it('keeps the embedded original recoverable via expand', () => {
|
|
47
|
+
const t = bigArray('label: ', '');
|
|
48
|
+
const res = crushEmbeddedJson(t);
|
|
49
|
+
const m = res.text.match(TABLE_MARKER_RE);
|
|
50
|
+
expect(m).not.toBeNull();
|
|
51
|
+
const original = retrieveOriginal(m[1]);
|
|
52
|
+
expect(original).not.toBeUndefined();
|
|
53
|
+
expect(() => JSON.parse(original)).not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
it('crushes the whole-text case too (parity with jsonCrush)', () => {
|
|
56
|
+
const res = crushEmbeddedJson(bigArray());
|
|
57
|
+
expect(res.savedChars).toBeGreaterThan(0);
|
|
58
|
+
expect(TABLE_MARKER_RE.test(res.text)).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('leaves text with no crushable arrays unchanged', () => {
|
|
61
|
+
const t = 'just a normal log line\nwith no json at all';
|
|
62
|
+
const res = crushEmbeddedJson(t);
|
|
63
|
+
expect(res.savedChars).toBe(0);
|
|
64
|
+
expect(res.text).toBe(t);
|
|
65
|
+
});
|
|
66
|
+
it('leaves a small (non-crushable) embedded array untouched, surroundings intact', () => {
|
|
67
|
+
const t = 'before [{"a":1},{"a":2}] after';
|
|
68
|
+
const res = crushEmbeddedJson(t);
|
|
69
|
+
expect(res.text).toBe(t);
|
|
70
|
+
expect(res.savedChars).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it('is deterministic', () => {
|
|
73
|
+
const t = bigArray('p ', ' s');
|
|
74
|
+
expect(crushEmbeddedJson(t).text).toBe(crushEmbeddedJson(t).text);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { app } from '../server.js';
|
|
3
|
+
// Security regression: control endpoints (/squeezr/*) must not be usable from a
|
|
4
|
+
// cross-origin browser context. A malicious page open in the user's browser must
|
|
5
|
+
// not be able to change state or read control responses on http://localhost:<port>.
|
|
6
|
+
// We hit *non-existent* control paths so the guard runs but no real handler fires
|
|
7
|
+
// (403 = guard blocked; 404 = guard allowed and routing missed) — no side effects.
|
|
8
|
+
describe('control-endpoint CSRF/CORS guard', () => {
|
|
9
|
+
it('rejects a cross-origin mutation to a control endpoint with 403', async () => {
|
|
10
|
+
const res = await app.request('/squeezr/__guard_probe__', {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: { origin: 'http://evil.example' },
|
|
13
|
+
});
|
|
14
|
+
expect(res.status).toBe(403);
|
|
15
|
+
});
|
|
16
|
+
it('rejects a file:// (null) origin mutation to a control endpoint', async () => {
|
|
17
|
+
const res = await app.request('/squeezr/__guard_probe__', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { origin: 'null' },
|
|
20
|
+
});
|
|
21
|
+
expect(res.status).toBe(403);
|
|
22
|
+
});
|
|
23
|
+
it('allows a mutation with no Origin (curl / MCP / native over loopback)', async () => {
|
|
24
|
+
const res = await app.request('/squeezr/__guard_probe__', { method: 'POST' });
|
|
25
|
+
expect(res.status).toBe(404); // guard passed; route simply does not exist
|
|
26
|
+
});
|
|
27
|
+
it('allows a mutation from a loopback browser origin', async () => {
|
|
28
|
+
const res = await app.request('/squeezr/__guard_probe__', {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { origin: 'http://localhost:8899' },
|
|
31
|
+
});
|
|
32
|
+
expect(res.status).toBe(404); // guard passed; route simply does not exist
|
|
33
|
+
});
|
|
34
|
+
it('does not guard the proxy endpoints (Cursor needs cross-origin)', async () => {
|
|
35
|
+
const res = await app.request('/v1/__proxy_probe__', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { origin: 'http://evil.example' },
|
|
38
|
+
});
|
|
39
|
+
expect(res.status).not.toBe(403);
|
|
40
|
+
});
|
|
41
|
+
it('never reflects a wildcard ACAO on control endpoints', async () => {
|
|
42
|
+
const res = await app.request('/squeezr/health', {
|
|
43
|
+
headers: { origin: 'http://evil.example' },
|
|
44
|
+
});
|
|
45
|
+
expect(res.headers.get('access-control-allow-origin')).not.toBe('*');
|
|
46
|
+
});
|
|
47
|
+
it('reflects the loopback origin (not wildcard) on control endpoints', async () => {
|
|
48
|
+
const res = await app.request('/squeezr/health', {
|
|
49
|
+
headers: { origin: 'http://127.0.0.1:1234' },
|
|
50
|
+
});
|
|
51
|
+
expect(res.headers.get('access-control-allow-origin')).toBe('http://127.0.0.1:1234');
|
|
52
|
+
expect(res.headers.get('vary')).toBe('Origin');
|
|
53
|
+
});
|
|
54
|
+
it('keeps wildcard ACAO on proxy endpoints for browser tooling', async () => {
|
|
55
|
+
const res = await app.request('/v1/__proxy_probe__', {
|
|
56
|
+
method: 'OPTIONS',
|
|
57
|
+
headers: { origin: 'http://evil.example' },
|
|
58
|
+
});
|
|
59
|
+
expect(res.headers.get('access-control-allow-origin')).toBe('*');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -105,6 +105,19 @@ describe('preprocess - base pipeline', () => {
|
|
|
105
105
|
const input = 'a\nb\na';
|
|
106
106
|
expect(preprocess(input)).toBe('a\nb\na');
|
|
107
107
|
});
|
|
108
|
+
// Regression (1.82.0): repeated bare object keys like `body:` were folded into
|
|
109
|
+
// "... [repeated N more times]", which corrupted code when it round-tripped to disk.
|
|
110
|
+
it('never folds repeated bare object keys (body:)', () => {
|
|
111
|
+
// Mirrors the real incident: identical keys, unique values on the next line.
|
|
112
|
+
const input = Array.from({ length: 5 }, (_, i) => ` body:\n "unique text ${i}",`).join('\n');
|
|
113
|
+
const out = preprocess(input);
|
|
114
|
+
expect(out).not.toContain('repeated');
|
|
115
|
+
expect(out.split('\n').filter(l => l.trim() === 'body:')).toHaveLength(5);
|
|
116
|
+
});
|
|
117
|
+
it('still folds genuine repeated log prose (colon with a value after)', () => {
|
|
118
|
+
const input = Array(5).fill('ERROR: connection refused').join('\n');
|
|
119
|
+
expect(preprocess(input)).toContain('repeated 4 more times');
|
|
120
|
+
});
|
|
108
121
|
it('minifies inline JSON blobs > 200 chars', () => {
|
|
109
122
|
const obj = Object.fromEntries(Array.from({ length: 20 }, (_, i) => [`key_${i}`, `value_number_${i}`]));
|
|
110
123
|
const pretty = JSON.stringify(obj, null, 2);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { checkProxy, checkVersion, checkEnv, checkBypass, checkSavings, computeExitCode, formatDoctor, } from '../doctor.js';
|
|
3
|
+
describe('checkProxy', () => {
|
|
4
|
+
it('passes when health is present', () => {
|
|
5
|
+
expect(checkProxy({ identity: 'squeezr', version: '1.89.0' }).status).toBe('pass');
|
|
6
|
+
});
|
|
7
|
+
it('fails when the proxy is unreachable', () => {
|
|
8
|
+
expect(checkProxy(null).status).toBe('fail');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
describe('checkVersion', () => {
|
|
12
|
+
it('passes when installed matches running', () => {
|
|
13
|
+
expect(checkVersion('1.89.0', '1.89.0').status).toBe('pass');
|
|
14
|
+
});
|
|
15
|
+
it('warns on a stale running proxy', () => {
|
|
16
|
+
const r = checkVersion('1.89.0', '1.88.0');
|
|
17
|
+
expect(r.status).toBe('warn');
|
|
18
|
+
expect(r.detail.includes('1.88.0')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('skips when the running version is unknown (proxy down)', () => {
|
|
21
|
+
expect(checkVersion('1.89.0', null).status).toBe('skip');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('checkEnv', () => {
|
|
25
|
+
it('passes when ANTHROPIC_BASE_URL points at the local proxy', () => {
|
|
26
|
+
expect(checkEnv('http://localhost:8080', 8080).status).toBe('pass');
|
|
27
|
+
expect(checkEnv('http://127.0.0.1:8080', 8080).status).toBe('pass');
|
|
28
|
+
});
|
|
29
|
+
it('warns when unset (traffic not routed through squeezr)', () => {
|
|
30
|
+
expect(checkEnv(undefined, 8080).status).toBe('warn');
|
|
31
|
+
expect(checkEnv('', 8080).status).toBe('warn');
|
|
32
|
+
});
|
|
33
|
+
it('fails when it points somewhere else', () => {
|
|
34
|
+
expect(checkEnv('https://api.anthropic.com', 8080).status).toBe('fail');
|
|
35
|
+
expect(checkEnv('http://localhost:9999', 8080).status).toBe('fail');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('checkBypass', () => {
|
|
39
|
+
it('warns when bypass is on (compression disabled)', () => {
|
|
40
|
+
expect(checkBypass(true).status).toBe('warn');
|
|
41
|
+
});
|
|
42
|
+
it('passes when bypass is off', () => {
|
|
43
|
+
expect(checkBypass(false).status).toBe('pass');
|
|
44
|
+
});
|
|
45
|
+
it('skips when unknown', () => {
|
|
46
|
+
expect(checkBypass(undefined).status).toBe('skip');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('checkSavings', () => {
|
|
50
|
+
it('skips when the proxy is down (requests unknown)', () => {
|
|
51
|
+
expect(checkSavings(undefined, undefined).status).toBe('skip');
|
|
52
|
+
});
|
|
53
|
+
it('skips when there is no traffic yet', () => {
|
|
54
|
+
expect(checkSavings(0, 0).status).toBe('skip');
|
|
55
|
+
});
|
|
56
|
+
it('warns when traffic is flowing but nothing is being saved', () => {
|
|
57
|
+
expect(checkSavings(120, 0).status).toBe('warn');
|
|
58
|
+
});
|
|
59
|
+
it('passes when savings are flowing', () => {
|
|
60
|
+
const r = checkSavings(120, 34);
|
|
61
|
+
expect(r.status).toBe('pass');
|
|
62
|
+
expect(r.detail.includes('34')).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('computeExitCode', () => {
|
|
66
|
+
it('0 when all pass/skip', () => {
|
|
67
|
+
expect(computeExitCode([{ name: 'a', status: 'pass', detail: '' }, { name: 'b', status: 'skip', detail: '' }])).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
it('1 when there is a warning but no failure', () => {
|
|
70
|
+
expect(computeExitCode([{ name: 'a', status: 'pass', detail: '' }, { name: 'b', status: 'warn', detail: '' }])).toBe(1);
|
|
71
|
+
});
|
|
72
|
+
it('2 when there is any failure', () => {
|
|
73
|
+
expect(computeExitCode([{ name: 'a', status: 'warn', detail: '' }, { name: 'b', status: 'fail', detail: '' }])).toBe(2);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('formatDoctor', () => {
|
|
77
|
+
it('renders every check line and a summary', () => {
|
|
78
|
+
const out = formatDoctor([
|
|
79
|
+
{ name: 'Proxy', status: 'pass', detail: 'running v1.89.0' },
|
|
80
|
+
{ name: 'Env', status: 'warn', detail: 'not set' },
|
|
81
|
+
], 1);
|
|
82
|
+
expect(out.includes('Proxy')).toBe(true);
|
|
83
|
+
expect(out.includes('Env')).toBe(true);
|
|
84
|
+
expect(out.toLowerCase().includes('warn')).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|