squeezr-ai 1.13.1 → 1.14.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/README.md CHANGED
@@ -564,8 +564,30 @@ The installer configures Squeezr to start automatically on login:
564
564
  | macOS | launchd (`~/Library/LaunchAgents/com.squeezr.plist`) | Shell auto-heal |
565
565
  | Linux | systemd user service (`~/.config/systemd/user/squeezr.service`) | Shell auto-heal |
566
566
  | Windows | Task Scheduler (runs at login, restarts on failure) | — |
567
+ | Windows (robust) | **NSSM Windows Service** (auto-restart on crash) | — |
567
568
  | **WSL2** | systemd → Task Scheduler (cascade) | Shell auto-heal |
568
569
 
570
+ ### Windows: NSSM (recommended over Task Scheduler)
571
+
572
+ The built-in Task Scheduler setup requires admin on every reinstall and does **not** restart Squeezr if it crashes mid-session (e.g. due to `ECONNRESET`). For a more robust setup, use [NSSM](https://nssm.cc) to run Squeezr as a proper Windows service:
573
+
574
+ ```powershell
575
+ # Install NSSM
576
+ winget install nssm
577
+
578
+ # Create the service (run as Administrator, adjust paths if needed)
579
+ $node = (where.exe node | Select-Object -First 1)
580
+ $script = "$(npm root -g)\squeezr-ai\bin\squeezr.js"
581
+ nssm install SqueezrProxy $node $script
582
+ nssm set SqueezrProxy AppExit Default Restart
583
+ nssm set SqueezrProxy AppRestartDelay 3000
584
+ nssm start SqueezrProxy
585
+ ```
586
+
587
+ NSSM gives you: auto-start on boot, automatic restart on crash, stdout/stderr logs, and control via `services.msc`.
588
+
589
+ See [NSSM_WINDOWS_SERVICE.md](./NSSM_WINDOWS_SERVICE.md) for the full guide including log setup, troubleshooting, and uninstall steps.
590
+
569
591
  ### WSL2 support
570
592
 
571
593
  `squeezr setup` detects WSL2 automatically and configures both sides:
package/bin/squeezr.js CHANGED
@@ -190,10 +190,16 @@ function setupWindows() {
190
190
  console.log('Setting up Squeezr for Windows...\n')
191
191
 
192
192
  // 1. Set env vars permanently via setx (user scope, no admin needed)
193
+ const port = process.env.SQUEEZR_PORT || 8080
194
+ const mitmPort = Number(port) + 1
195
+ const caPath = path.join(os.homedir(), '.squeezr', 'mitm-ca', 'ca.crt')
193
196
  const vars = {
194
- ANTHROPIC_BASE_URL: 'http://localhost:8080',
195
- openai_base_url: 'http://localhost:8080',
196
- GEMINI_API_BASE_URL: 'http://localhost:8080',
197
+ ANTHROPIC_BASE_URL: `http://localhost:${port}`,
198
+ openai_base_url: `http://localhost:${port}`,
199
+ GEMINI_API_BASE_URL: `http://localhost:${port}`,
200
+ HTTPS_PROXY: `http://localhost:${mitmPort}`,
201
+ // Node.js does NOT use the Windows Certificate Store — this makes Codex (Node.js) trust the MITM CA
202
+ NODE_EXTRA_CA_CERTS: caPath,
197
203
  }
198
204
  for (const [key, value] of Object.entries(vars)) {
199
205
  try {
@@ -204,29 +210,65 @@ function setupWindows() {
204
210
  }
205
211
  }
206
212
 
207
- // 2. Register Task Scheduler with hidden window so no console pops up on login.
208
- // The action runs: powershell -WindowStyle Hidden -Command "node dist/index.js"
209
- const taskName = 'Squeezr'
210
- const nodeArg = `${nodeExe} \`"${distIndex}\`"`
211
- const ps = [
212
- `$e = Get-ScheduledTask -TaskName '${taskName}' -ErrorAction SilentlyContinue`,
213
- `if ($e) { Unregister-ScheduledTask -TaskName '${taskName}' -Confirm:$false }`,
214
- `$a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-WindowStyle Hidden -NonInteractive -Command "${nodeArg}"' -WorkingDirectory '${ROOT}'`,
215
- `$t = New-ScheduledTaskTrigger -AtLogon`,
216
- `$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1)`,
217
- `Register-ScheduledTask -TaskName '${taskName}' -Action $a -Trigger $t -Settings $s -RunLevel Highest -Force | Out-Null`,
218
- ].join('; ')
213
+ // 2. Auto-start: try NSSM (Windows service, survives crashes) fallback to Task Scheduler
214
+ const logDir = path.join(os.homedir(), '.squeezr')
215
+ const serviceName = 'SqueezrProxy'
216
+ let autoStartOk = false
219
217
 
220
- try {
221
- execSync(`powershell -NoProfile -Command "${ps}"`, { stdio: 'pipe' })
222
- console.log(` [ok] Auto-start registered in Task Scheduler (hidden, starts on login)`)
223
- } catch {
224
- console.log(` [warn] Task Scheduler failed — run as admin for auto-start on login`)
218
+ const nssmAvailable = (() => {
219
+ try { execSync('where nssm', { stdio: 'pipe' }); return true } catch { return false }
220
+ })()
221
+
222
+ if (nssmAvailable) {
223
+ try {
224
+ // Remove existing service if present (ignore errors)
225
+ try { execSync(`nssm stop ${serviceName}`, { stdio: 'pipe' }) } catch {}
226
+ try { execSync(`nssm remove ${serviceName} confirm`, { stdio: 'pipe' }) } catch {}
227
+
228
+ execSync(`nssm install ${serviceName} "${nodeExe}" "${distIndex}"`, { stdio: 'pipe' })
229
+ execSync(`nssm set ${serviceName} AppDirectory "${ROOT}"`, { stdio: 'pipe' })
230
+ execSync(`nssm set ${serviceName} AppStdout "${logDir}\\service-stdout.log"`, { stdio: 'pipe' })
231
+ execSync(`nssm set ${serviceName} AppStderr "${logDir}\\service-stderr.log"`, { stdio: 'pipe' })
232
+ execSync(`nssm set ${serviceName} AppRotateFiles 1`, { stdio: 'pipe' })
233
+ execSync(`nssm set ${serviceName} AppRotateSeconds 86400`, { stdio: 'pipe' })
234
+ execSync(`nssm set ${serviceName} AppExit Default Restart`, { stdio: 'pipe' })
235
+ execSync(`nssm set ${serviceName} AppRestartDelay 3000`, { stdio: 'pipe' })
236
+ execSync(`nssm set ${serviceName} Description "Squeezr AI token compression proxy on port 8080"`, { stdio: 'pipe' })
237
+ execSync(`nssm start ${serviceName}`, { stdio: 'pipe' })
238
+ console.log(` [ok] Auto-start registered as Windows service via NSSM (auto-restart on crash)`)
239
+ autoStartOk = true
240
+ } catch (err) {
241
+ const msg = err.stderr?.toString() || err.message || ''
242
+ if (msg.includes('Access') || msg.includes('admin') || msg.includes('5')) {
243
+ console.log(` [warn] NSSM requires admin — run as Administrator for service install`)
244
+ } else {
245
+ console.log(` [warn] NSSM install failed: ${msg.trim().split('\n')[0]}`)
246
+ }
247
+ }
248
+ }
249
+
250
+ if (!autoStartOk) {
251
+ // Fallback: Task Scheduler (no crash recovery, but no admin needed for user tasks)
252
+ const taskName = 'Squeezr'
253
+ const nodeArg = `${nodeExe} \`"${distIndex}\`"`
254
+ const ps = [
255
+ `$e = Get-ScheduledTask -TaskName '${taskName}' -ErrorAction SilentlyContinue`,
256
+ `if ($e) { Unregister-ScheduledTask -TaskName '${taskName}' -Confirm:$false }`,
257
+ `$a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-WindowStyle Hidden -NonInteractive -Command "${nodeArg}"' -WorkingDirectory '${ROOT}'`,
258
+ `$t = New-ScheduledTaskTrigger -AtLogon`,
259
+ `$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1)`,
260
+ `Register-ScheduledTask -TaskName '${taskName}' -Action $a -Trigger $t -Settings $s -RunLevel Highest -Force | Out-Null`,
261
+ ].join('; ')
262
+ try {
263
+ execSync(`powershell -NoProfile -Command "${ps}"`, { stdio: 'pipe' })
264
+ console.log(` [ok] Auto-start registered in Task Scheduler (install NSSM for crash recovery)`)
265
+ } catch {
266
+ console.log(` [warn] Auto-start failed — install NSSM or run as admin: https://nssm.cc`)
267
+ }
225
268
  }
226
269
 
227
270
  // 3. Start Squeezr right now as a detached background process (no window)
228
271
  // Logs go to ~/.squeezr/squeezr.log
229
- const logDir = path.join(os.homedir(), '.squeezr')
230
272
  const logFile = path.join(logDir, 'squeezr.log')
231
273
  fs.mkdirSync(logDir, { recursive: true })
232
274
  const logFd = fs.openSync(logFile, 'a')
@@ -241,18 +283,49 @@ function setupWindows() {
241
283
  console.log(` [ok] Squeezr started in background (pid ${child.pid})`)
242
284
  console.log(` [ok] Logs → ${logFile}`)
243
285
 
244
- console.log(`
286
+ // 4. Trust MITM CA in Windows Certificate Store (for native apps like browsers)
287
+ // Node.js apps (Codex) use NODE_EXTRA_CA_CERTS set above instead.
288
+ // The CA is generated on first proxy start — wait briefly for it to appear
289
+ const waitForCa = (retries = 10, interval = 500) => new Promise(resolve => {
290
+ const check = (n) => {
291
+ if (fs.existsSync(caPath)) return resolve(true)
292
+ if (n <= 0) return resolve(false)
293
+ setTimeout(() => check(n - 1), interval)
294
+ }
295
+ check(retries)
296
+ })
297
+
298
+ waitForCa().then(found => {
299
+ if (!found) {
300
+ console.log(` [warn] MITM CA not found yet — run 'squeezr setup' again after first start`)
301
+ printDone()
302
+ return
303
+ }
304
+ try {
305
+ execSync(`certutil -addstore -f Root "${caPath}"`, { stdio: 'pipe' })
306
+ console.log(` [ok] MITM CA trusted in Windows Certificate Store (Codex TLS interception ready)`)
307
+ } catch {
308
+ console.log(` [warn] Could not trust MITM CA — run as Administrator or trust manually:`)
309
+ console.log(` certutil -addstore -f Root "${caPath}"`)
310
+ }
311
+ printDone()
312
+ })
313
+
314
+ function printDone() {
315
+ console.log(`
245
316
  Done!
246
317
 
247
- Squeezr is running on http://localhost:8080
318
+ Squeezr is running on http://localhost:${port}
319
+ MITM proxy on http://localhost:${mitmPort} (Codex TLS interception)
248
320
  All CLIs (Claude Code, Codex, Aider, Gemini, Ollama) are configured.
249
321
 
250
- Restart your terminal and Claude Code once for the env vars to take effect.
322
+ Restart your terminal once for the env vars to take effect.
251
323
  After that, everything is automatic — Squeezr starts silently on every login.
252
324
 
253
325
  squeezr status — check it's running
254
326
  squeezr gain — see token savings
255
327
  `)
328
+ }
256
329
  }
257
330
 
258
331
  function setupUnix() {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.13.1";
1
+ export declare const VERSION = "1.14.2";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '1.13.1';
1
+ export const VERSION = '1.14.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squeezr-ai",
3
- "version": "1.13.1",
3
+ "version": "1.14.2",
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",