squeezr-ai 1.10.6 → 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 CHANGED
@@ -456,11 +456,28 @@ More importantly: sending 60-80% fewer tokens means Claude processes a smaller c
456
456
 
457
457
  The installer configures Squeezr to start automatically on login:
458
458
 
459
- | OS | Method |
460
- |---|---|
461
- | macOS | launchd (`~/Library/LaunchAgents/com.squeezr.plist`) |
462
- | Linux | systemd user service (`~/.config/systemd/user/squeezr.service`) |
463
- | 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
464
481
 
465
482
  ---
466
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 exportLines = [
216
- 'export ANTHROPIC_BASE_URL=http://localhost:8080',
217
- 'export OPENAI_BASE_URL=http://localhost:8080',
218
- 'export GEMINI_API_BASE_URL=http://localhost:8080',
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${marker}\n${exportLines.join('\n')}\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
- console.log(` [skip] Env vars already in ${profile}`)
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/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.10.6";
1
+ export declare const VERSION = "1.11.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '1.10.6';
1
+ export const VERSION = '1.11.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squeezr-ai",
3
- "version": "1.10.6",
3
+ "version": "1.11.0",
4
4
  "description": "AI proxy that compresses Claude Code, Codex, Aider, Gemini CLI and Ollama context windows to save thousands of tokens per session",
5
5
  "keywords": [
6
6
  "claude",