squeezr-ai 1.10.5 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/bin/squeezr.js +194 -9
- package/dist/gain.js +17 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,8 @@ localhost:8080 (Squeezr proxy)
|
|
|
103
103
|
Your provider's API (Anthropic / OpenAI / Google / Ollama)
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
**MCP tool results are compressed automatically.** Any tool result that passes through the proxy — including results from MCP servers (Linear, GitHub, Slack, planning tools, custom MCPs) — goes through the same compression pipeline. No configuration needed; Squeezr treats MCP tool results identically to built-in tools. In practice MCP responses are often large JSON payloads that compress 70-94%.
|
|
107
|
+
|
|
106
108
|
Recent content is always preserved untouched — by default the last 3 tool results are never compressed. Your CLI always has full context for what it's currently working on.
|
|
107
109
|
|
|
108
110
|
---
|
|
@@ -454,11 +456,28 @@ More importantly: sending 60-80% fewer tokens means Claude processes a smaller c
|
|
|
454
456
|
|
|
455
457
|
The installer configures Squeezr to start automatically on login:
|
|
456
458
|
|
|
457
|
-
| OS | Method |
|
|
458
|
-
|
|
459
|
-
| macOS | launchd (`~/Library/LaunchAgents/com.squeezr.plist`) |
|
|
460
|
-
| Linux | systemd user service (`~/.config/systemd/user/squeezr.service`) |
|
|
461
|
-
| Windows | Task Scheduler (runs at login, restarts on failure) |
|
|
459
|
+
| OS | Method | Fallback |
|
|
460
|
+
|---|---|---|
|
|
461
|
+
| macOS | launchd (`~/Library/LaunchAgents/com.squeezr.plist`) | Shell auto-heal |
|
|
462
|
+
| Linux | systemd user service (`~/.config/systemd/user/squeezr.service`) | Shell auto-heal |
|
|
463
|
+
| Windows | Task Scheduler (runs at login, restarts on failure) | — |
|
|
464
|
+
| **WSL2** | systemd → Task Scheduler (cascade) | Shell auto-heal |
|
|
465
|
+
|
|
466
|
+
### WSL2 support
|
|
467
|
+
|
|
468
|
+
`squeezr setup` detects WSL2 automatically and configures both sides:
|
|
469
|
+
|
|
470
|
+
- **WSL shell**: env vars + auto-heal guard in `.bashrc` / `.zshrc`
|
|
471
|
+
- **Windows**: env vars via `setx` (persistent in registry)
|
|
472
|
+
- **Auto-start**: tries systemd first (WSL2 with `systemd=true` in `/etc/wsl.conf`), falls back to Windows Task Scheduler via `powershell.exe`
|
|
473
|
+
|
|
474
|
+
### Auto-heal
|
|
475
|
+
|
|
476
|
+
On every platform, `squeezr setup` adds a lightweight guard to your shell profile. Each time you open a terminal, it checks if the proxy is alive (`curl localhost:8080/squeezr/health`). If not, it starts it in the background — silently, in ~100ms. This means:
|
|
477
|
+
|
|
478
|
+
- If the service manager fails, the proxy still starts on your next terminal
|
|
479
|
+
- If the proxy crashes mid-session, the next terminal restores it
|
|
480
|
+
- Zero manual intervention after `squeezr setup`, ever
|
|
462
481
|
|
|
463
482
|
---
|
|
464
483
|
|
package/bin/squeezr.js
CHANGED
|
@@ -211,12 +211,20 @@ function setupUnix() {
|
|
|
211
211
|
|
|
212
212
|
console.log(`Setting up Squeezr for ${platform === 'darwin' ? 'macOS' : 'Linux'}...\n`)
|
|
213
213
|
|
|
214
|
-
// 1. Set env vars in shell profile
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
// 1. Set env vars + auto-heal guard in shell profile
|
|
215
|
+
const distIndex = path.join(ROOT, 'dist', 'index.js')
|
|
216
|
+
const port = process.env.SQUEEZR_PORT || 8080
|
|
217
|
+
const shellBlock = [
|
|
218
|
+
`# squeezr env vars`,
|
|
219
|
+
`export ANTHROPIC_BASE_URL=http://localhost:${port}`,
|
|
220
|
+
`export OPENAI_BASE_URL=http://localhost:${port}`,
|
|
221
|
+
`export GEMINI_API_BASE_URL=http://localhost:${port}`,
|
|
222
|
+
`# squeezr auto-heal: start proxy if not running`,
|
|
223
|
+
`if ! curl -sf http://localhost:${port}/squeezr/health >/dev/null 2>&1; then`,
|
|
224
|
+
` nohup ${nodeExe} ${distIndex} >> "${os.homedir()}/.squeezr/squeezr.log" 2>&1 &`,
|
|
225
|
+
` disown`,
|
|
226
|
+
`fi`,
|
|
227
|
+
].join('\n')
|
|
220
228
|
const marker = '# squeezr env vars'
|
|
221
229
|
const profiles = [
|
|
222
230
|
path.join(os.homedir(), '.zshrc'),
|
|
@@ -226,10 +234,19 @@ function setupUnix() {
|
|
|
226
234
|
const profile = profiles.find(p => fs.existsSync(p)) ?? profiles[0]
|
|
227
235
|
const existing = fs.existsSync(profile) ? fs.readFileSync(profile, 'utf-8') : ''
|
|
228
236
|
if (!existing.includes(marker)) {
|
|
229
|
-
fs.appendFileSync(profile, `\n${
|
|
230
|
-
console.log(` [ok] Env vars added to ${profile}`)
|
|
237
|
+
fs.appendFileSync(profile, `\n${shellBlock}\n`)
|
|
238
|
+
console.log(` [ok] Env vars + auto-heal added to ${profile}`)
|
|
231
239
|
} else {
|
|
232
|
-
|
|
240
|
+
if (!existing.includes('squeezr auto-heal')) {
|
|
241
|
+
const updatedContent = existing.replace(
|
|
242
|
+
/# squeezr env vars\n(?:export [A-Z_]+=http:\/\/localhost:\d+\n?)*/,
|
|
243
|
+
shellBlock + '\n'
|
|
244
|
+
)
|
|
245
|
+
fs.writeFileSync(profile, updatedContent)
|
|
246
|
+
console.log(` [ok] Auto-heal guard added to ${profile}`)
|
|
247
|
+
} else {
|
|
248
|
+
console.log(` [skip] Env vars + auto-heal already in ${profile}`)
|
|
249
|
+
}
|
|
233
250
|
}
|
|
234
251
|
|
|
235
252
|
// 2a. macOS — launchd
|
|
@@ -300,6 +317,173 @@ Done!
|
|
|
300
317
|
`)
|
|
301
318
|
}
|
|
302
319
|
|
|
320
|
+
// ── WSL2 detection ───────────────────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
function isWSL() {
|
|
323
|
+
try {
|
|
324
|
+
const release = fs.readFileSync('/proc/version', 'utf-8')
|
|
325
|
+
return /microsoft|wsl/i.test(release)
|
|
326
|
+
} catch {
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── squeezr setup — WSL2 ────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
function setupWSL() {
|
|
334
|
+
const nodeExe = process.execPath
|
|
335
|
+
const distIndex = path.join(ROOT, 'dist', 'index.js')
|
|
336
|
+
|
|
337
|
+
console.log('Setting up Squeezr for WSL2...\n')
|
|
338
|
+
|
|
339
|
+
// 1. Set env vars + auto-heal guard in WSL shell profile (.bashrc / .zshrc)
|
|
340
|
+
// The guard checks if the proxy is alive on terminal open. If not, it starts
|
|
341
|
+
// it in the background. This is the safety net for WSL2 where systemd and
|
|
342
|
+
// Task Scheduler may both fail.
|
|
343
|
+
const port = process.env.SQUEEZR_PORT || 8080
|
|
344
|
+
const shellBlock = [
|
|
345
|
+
`# squeezr env vars`,
|
|
346
|
+
`export ANTHROPIC_BASE_URL=http://localhost:${port}`,
|
|
347
|
+
`export OPENAI_BASE_URL=http://localhost:${port}`,
|
|
348
|
+
`export GEMINI_API_BASE_URL=http://localhost:${port}`,
|
|
349
|
+
`# squeezr auto-heal: start proxy if not running`,
|
|
350
|
+
`if ! curl -sf http://localhost:${port}/squeezr/health >/dev/null 2>&1; then`,
|
|
351
|
+
` nohup ${nodeExe} ${distIndex} >> "${os.homedir()}/.squeezr/squeezr.log" 2>&1 &`,
|
|
352
|
+
` disown`,
|
|
353
|
+
`fi`,
|
|
354
|
+
].join('\n')
|
|
355
|
+
const marker = '# squeezr env vars'
|
|
356
|
+
const profiles = [
|
|
357
|
+
path.join(os.homedir(), '.zshrc'),
|
|
358
|
+
path.join(os.homedir(), '.bashrc'),
|
|
359
|
+
path.join(os.homedir(), '.bash_profile'),
|
|
360
|
+
]
|
|
361
|
+
const profile = profiles.find(p => fs.existsSync(p)) ?? profiles[1]
|
|
362
|
+
const existing = fs.existsSync(profile) ? fs.readFileSync(profile, 'utf-8') : ''
|
|
363
|
+
if (!existing.includes(marker)) {
|
|
364
|
+
fs.appendFileSync(profile, `\n${shellBlock}\n`)
|
|
365
|
+
console.log(` [ok] Env vars + auto-heal added to ${profile}`)
|
|
366
|
+
} else {
|
|
367
|
+
// Update existing block to include auto-heal if missing
|
|
368
|
+
if (!existing.includes('squeezr auto-heal')) {
|
|
369
|
+
const updatedContent = existing.replace(
|
|
370
|
+
/# squeezr env vars\n(?:export [A-Z_]+=http:\/\/localhost:\d+\n?)*/,
|
|
371
|
+
shellBlock + '\n'
|
|
372
|
+
)
|
|
373
|
+
fs.writeFileSync(profile, updatedContent)
|
|
374
|
+
console.log(` [ok] Auto-heal guard added to ${profile}`)
|
|
375
|
+
} else {
|
|
376
|
+
console.log(` [skip] Env vars + auto-heal already in ${profile}`)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// 2. Set Windows env vars via setx.exe (so Windows-launched CLIs see them)
|
|
381
|
+
const setxExe = '/mnt/c/Windows/System32/setx.exe'
|
|
382
|
+
const winVars = {
|
|
383
|
+
ANTHROPIC_BASE_URL: 'http://localhost:8080',
|
|
384
|
+
OPENAI_BASE_URL: 'http://localhost:8080',
|
|
385
|
+
GEMINI_API_BASE_URL: 'http://localhost:8080',
|
|
386
|
+
}
|
|
387
|
+
if (fs.existsSync(setxExe)) {
|
|
388
|
+
for (const [key, value] of Object.entries(winVars)) {
|
|
389
|
+
try {
|
|
390
|
+
execSync(`"${setxExe}" ${key} "${value}"`, { stdio: 'pipe' })
|
|
391
|
+
console.log(` [ok] Windows env: ${key}=${value}`)
|
|
392
|
+
} catch {
|
|
393
|
+
console.log(` [skip] Windows env: ${key} could not be set`)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
console.log(' [skip] setx.exe not found — Windows env vars not set')
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 3. Auto-start: try systemd first (WSL2 with systemd enabled), fallback to
|
|
401
|
+
// Windows Task Scheduler, then plain background process
|
|
402
|
+
let autoStartDone = false
|
|
403
|
+
|
|
404
|
+
// 3a. Try systemd (works on newer WSL2 with [boot] systemd=true in wsl.conf)
|
|
405
|
+
try {
|
|
406
|
+
const serviceDir = path.join(os.homedir(), '.config', 'systemd', 'user')
|
|
407
|
+
fs.mkdirSync(serviceDir, { recursive: true })
|
|
408
|
+
const servicePath = path.join(serviceDir, 'squeezr.service')
|
|
409
|
+
fs.writeFileSync(servicePath, `[Unit]
|
|
410
|
+
Description=Squeezr AI proxy
|
|
411
|
+
After=network.target
|
|
412
|
+
|
|
413
|
+
[Service]
|
|
414
|
+
ExecStart=${nodeExe} ${distIndex}
|
|
415
|
+
Restart=always
|
|
416
|
+
RestartSec=5
|
|
417
|
+
WorkingDirectory=${ROOT}
|
|
418
|
+
|
|
419
|
+
[Install]
|
|
420
|
+
WantedBy=default.target
|
|
421
|
+
`)
|
|
422
|
+
execSync('systemctl --user daemon-reload && systemctl --user enable --now squeezr', { stdio: 'pipe' })
|
|
423
|
+
console.log(' [ok] Auto-start registered via systemd')
|
|
424
|
+
autoStartDone = true
|
|
425
|
+
} catch {
|
|
426
|
+
// systemd not available — try Windows Task Scheduler
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 3b. Fallback: Windows Task Scheduler via powershell.exe
|
|
430
|
+
if (!autoStartDone) {
|
|
431
|
+
const winNodeExe = execSync('wslpath -w "$(which node)"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
|
|
432
|
+
const winDistIndex = execSync(`wslpath -w "${distIndex}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
|
|
433
|
+
const winRoot = execSync(`wslpath -w "${ROOT}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
|
|
434
|
+
const taskName = 'Squeezr'
|
|
435
|
+
const ps = [
|
|
436
|
+
`$e = Get-ScheduledTask -TaskName '${taskName}' -ErrorAction SilentlyContinue`,
|
|
437
|
+
`if ($e) { Unregister-ScheduledTask -TaskName '${taskName}' -Confirm:$false }`,
|
|
438
|
+
`$a = New-ScheduledTaskAction -Execute 'wsl.exe' -Argument '-d ${os.hostname()} -- ${nodeExe} ${distIndex}' -WorkingDirectory '${winRoot}'`,
|
|
439
|
+
`$t = New-ScheduledTaskTrigger -AtLogon`,
|
|
440
|
+
`$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1)`,
|
|
441
|
+
`Register-ScheduledTask -TaskName '${taskName}' -Action $a -Trigger $t -Settings $s -Force | Out-Null`,
|
|
442
|
+
].join('; ')
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
execSync(`powershell.exe -NoProfile -Command "${ps}"`, { stdio: 'pipe' })
|
|
446
|
+
console.log(' [ok] Auto-start registered via Windows Task Scheduler')
|
|
447
|
+
autoStartDone = true
|
|
448
|
+
} catch {
|
|
449
|
+
console.log(' [warn] Task Scheduler failed — run PowerShell as admin for auto-start')
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 4. Start proxy now as a detached background process
|
|
454
|
+
const logDir = path.join(os.homedir(), '.squeezr')
|
|
455
|
+
const logFile = path.join(logDir, 'squeezr.log')
|
|
456
|
+
fs.mkdirSync(logDir, { recursive: true })
|
|
457
|
+
const logFd = fs.openSync(logFile, 'a')
|
|
458
|
+
const child = spawn(nodeExe, [distIndex], {
|
|
459
|
+
detached: true,
|
|
460
|
+
stdio: ['ignore', logFd, logFd],
|
|
461
|
+
cwd: ROOT,
|
|
462
|
+
})
|
|
463
|
+
child.unref()
|
|
464
|
+
fs.closeSync(logFd)
|
|
465
|
+
console.log(` [ok] Squeezr started in background (pid ${child.pid})`)
|
|
466
|
+
console.log(` [ok] Logs → ${logFile}`)
|
|
467
|
+
|
|
468
|
+
// 5. Apply env vars to current process so calling `source` is not needed
|
|
469
|
+
// This won't affect the parent shell, but at least prints guidance.
|
|
470
|
+
console.log(`
|
|
471
|
+
Done!
|
|
472
|
+
|
|
473
|
+
Squeezr is running on http://localhost:8080
|
|
474
|
+
All CLIs (Claude Code, Codex, Aider, Gemini, Ollama) are configured.
|
|
475
|
+
|
|
476
|
+
Windows env vars are set (effective in new terminals immediately).
|
|
477
|
+
WSL env vars added to ${profile}.
|
|
478
|
+
|
|
479
|
+
To activate in THIS terminal: source ${profile}
|
|
480
|
+
New terminals will have everything configured automatically.
|
|
481
|
+
|
|
482
|
+
squeezr status — check it's running
|
|
483
|
+
squeezr gain — see token savings
|
|
484
|
+
`)
|
|
485
|
+
}
|
|
486
|
+
|
|
303
487
|
// ── CLI router ────────────────────────────────────────────────────────────────
|
|
304
488
|
|
|
305
489
|
switch (command) {
|
|
@@ -310,6 +494,7 @@ switch (command) {
|
|
|
310
494
|
|
|
311
495
|
case 'setup':
|
|
312
496
|
if (process.platform === 'win32') setupWindows()
|
|
497
|
+
else if (isWSL()) setupWSL()
|
|
313
498
|
else setupUnix()
|
|
314
499
|
break
|
|
315
500
|
|
package/dist/gain.js
CHANGED
|
@@ -27,21 +27,30 @@ const savedChars = data.total_saved_chars;
|
|
|
27
27
|
const originalChars = data.total_original_chars;
|
|
28
28
|
const CHARS_PER_TOKEN = 3.5;
|
|
29
29
|
const savedTokens = Math.round(savedChars / CHARS_PER_TOKEN);
|
|
30
|
-
const pct = originalChars > 0 ? Math.round((savedChars / originalChars) * 1000) / 10 : 0;
|
|
31
30
|
const byTool = (data.by_tool ?? {});
|
|
31
|
+
// Savings % on tool results only (what Squeezr actually compresses).
|
|
32
|
+
// Using total context chars gives a misleadingly low number because the
|
|
33
|
+
// denominator includes user messages, Claude responses, and history
|
|
34
|
+
// re-sent on every request — none of which Squeezr touches.
|
|
35
|
+
const toolOriginal = Object.values(byTool).reduce((s, d) => s + d.originalChars, 0);
|
|
36
|
+
const toolSaved = Object.values(byTool).reduce((s, d) => s + d.savedChars, 0);
|
|
37
|
+
const toolPct = toolOriginal > 0 ? Math.round((toolSaved / toolOriginal) * 1000) / 10 : 0;
|
|
38
|
+
// Context reduction: how much smaller the overall payload became
|
|
39
|
+
const ctxPct = originalChars > 0 ? Math.round((savedChars / originalChars) * 1000) / 10 : 0;
|
|
32
40
|
console.log('┌─────────────────────────────────────────┐');
|
|
33
41
|
console.log('│ Squeezr — Token Savings │');
|
|
34
42
|
console.log('├─────────────────────────────────────────┤');
|
|
35
|
-
console.log(`│ Requests
|
|
36
|
-
console.log(`│ Saved chars
|
|
37
|
-
console.log(`│ Saved tokens
|
|
38
|
-
console.log(`│
|
|
43
|
+
console.log(`│ Requests ${String(requests).padEnd(23)}│`);
|
|
44
|
+
console.log(`│ Saved chars ${String(savedChars.toLocaleString()).padEnd(23)}│`);
|
|
45
|
+
console.log(`│ Saved tokens ${String(savedTokens.toLocaleString()).padEnd(23)}│`);
|
|
46
|
+
console.log(`│ Tool savings ${String(`${toolPct}%`).padEnd(23)}│`);
|
|
47
|
+
console.log(`│ Context reduction ${String(`${ctxPct}%`).padEnd(22)}│`);
|
|
39
48
|
if (Object.keys(byTool).length > 0) {
|
|
40
49
|
console.log('├─────────────────────────────────────────┤');
|
|
41
50
|
console.log('│ By Tool │');
|
|
42
|
-
for (const [tool, d] of Object.entries(byTool)) {
|
|
43
|
-
const
|
|
44
|
-
const line = ` ${tool} (${d.count}x): -${
|
|
51
|
+
for (const [tool, d] of Object.entries(byTool).sort((a, b) => b[1].savedChars - a[1].savedChars)) {
|
|
52
|
+
const pct = d.originalChars > 0 ? Math.round((d.savedChars / d.originalChars) * 1000) / 10 : 0;
|
|
53
|
+
const line = ` ${tool} (${d.count}x): -${pct}%`;
|
|
45
54
|
console.log(`│${line.padEnd(41)}│`);
|
|
46
55
|
}
|
|
47
56
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.11.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '1.
|
|
1
|
+
export const VERSION = '1.11.0';
|
package/package.json
CHANGED