squeezr-ai 1.16.1 → 1.16.3

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.
Files changed (2) hide show
  1. package/bin/squeezr.js +121 -13
  2. package/package.json +1 -1
package/bin/squeezr.js CHANGED
@@ -16,6 +16,64 @@ const pkg = require(path.join(ROOT, 'package.json'))
16
16
  const args = process.argv.slice(2)
17
17
  const command = args[0]
18
18
 
19
+ // ── update check (non-blocking) ───────────────────────────────────────────────
20
+
21
+ const UPDATE_CHECK_FILE = path.join(os.homedir(), '.squeezr', 'update-check.json')
22
+ const UPDATE_CHECK_INTERVAL = 4 * 60 * 60 * 1000 // 4 hours
23
+
24
+ // Fire and forget — runs in background, never blocks CLI
25
+ const updateCheckPromise = (async () => {
26
+ try {
27
+ // Read cached check
28
+ let cached = null
29
+ try { cached = JSON.parse(fs.readFileSync(UPDATE_CHECK_FILE, 'utf-8')) } catch {}
30
+ if (cached && Date.now() - cached.checkedAt < UPDATE_CHECK_INTERVAL) {
31
+ return cached.latest !== pkg.version ? cached.latest : null
32
+ }
33
+ // Fetch latest from npm (with timeout)
34
+ const { get } = await import('https')
35
+ const latest = await new Promise((resolve, reject) => {
36
+ const req = get('https://registry.npmjs.org/squeezr-ai/latest', { timeout: 3000 }, res => {
37
+ let data = ''
38
+ res.on('data', chunk => { data += chunk })
39
+ res.on('end', () => {
40
+ try { resolve(JSON.parse(data).version) } catch { resolve(null) }
41
+ })
42
+ })
43
+ req.on('error', () => resolve(null))
44
+ req.setTimeout(3000, () => { req.destroy(); resolve(null) })
45
+ })
46
+ if (!latest) return null
47
+ // Cache result
48
+ const dir = path.dirname(UPDATE_CHECK_FILE)
49
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
50
+ fs.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify({ latest, checkedAt: Date.now() }))
51
+ return latest !== pkg.version ? latest : null
52
+ } catch { return null }
53
+ })()
54
+
55
+ async function showUpdateBanner() {
56
+ try {
57
+ const latest = await Promise.race([updateCheckPromise, new Promise(r => setTimeout(() => r(null), 500))])
58
+ if (latest) {
59
+ console.log('')
60
+ console.log(` ╭─────────────────────────────────────────────────────────╮`)
61
+ console.log(` │ Update available: v${pkg.version} → v${latest}${' '.repeat(Math.max(0, 30 - pkg.version.length - latest.length))}│`)
62
+ console.log(` │ Run: npm install -g squeezr-ai │`)
63
+ console.log(` ╰─────────────────────────────────────────────────────────╯`)
64
+ }
65
+ } catch {}
66
+ }
67
+
68
+ function getPortFromToml() {
69
+ try {
70
+ const toml = fs.readFileSync(path.join(ROOT, 'squeezr.toml'), 'utf-8')
71
+ const m = toml.match(/^port\s*=\s*(\d+)/m)
72
+ if (m) return parseInt(m[1])
73
+ } catch {}
74
+ return null
75
+ }
76
+
19
77
  function getMitmPort(port) {
20
78
  const envMitm = process.env.SQUEEZR_MITM_PORT
21
79
  if (envMitm) return parseInt(envMitm)
@@ -27,6 +85,10 @@ function getMitmPort(port) {
27
85
  return Number(port) + 1
28
86
  }
29
87
 
88
+ function getPort() {
89
+ return process.env.SQUEEZR_PORT || getPortFromToml() || 8080
90
+ }
91
+
30
92
  const HELP = `
31
93
  Squeezr v${pkg.version} — AI context compressor for Claude Code, Codex, Aider, Gemini CLI and Ollama
32
94
 
@@ -68,7 +130,7 @@ async function startDaemon() {
68
130
  }
69
131
 
70
132
  // Check if already running — and if the version matches
71
- const port = process.env.SQUEEZR_PORT || 8080
133
+ const port = getPort()
72
134
  const runningVersion = await new Promise(resolve => {
73
135
  const req = http.get(`http://localhost:${port}/squeezr/health`, res => {
74
136
  let data = ''
@@ -134,7 +196,7 @@ function showLogs() {
134
196
  }
135
197
 
136
198
  function stopProxy() {
137
- const port = process.env.SQUEEZR_PORT || 8080
199
+ const port = getPort()
138
200
  const mitmPort = getMitmPort(port)
139
201
  const ports = [port, mitmPort]
140
202
  let killed = false
@@ -179,7 +241,7 @@ function stopProxy() {
179
241
  }
180
242
 
181
243
  async function checkStatus() {
182
- const port = process.env.SQUEEZR_PORT || 8080
244
+ const port = getPort()
183
245
  const mitmPort = getMitmPort(port)
184
246
  return new Promise(resolve => {
185
247
  const req = http.get(`http://localhost:${port}/squeezr/health`, res => {
@@ -284,15 +346,59 @@ async function configurePorts() {
284
346
  try { execSync(`setx HTTPS_PROXY "http://localhost:${finalMitm}"`, { stdio: 'pipe' }) } catch {}
285
347
  console.log('Environment variables updated. Restart your terminal for changes to take effect.')
286
348
  } else {
287
- console.log(`\nUpdate your shell profile:`)
288
- console.log(` export SQUEEZR_PORT=${finalPort}`)
289
- console.log(` export SQUEEZR_MITM_PORT=${finalMitm}`)
290
- console.log(` export ANTHROPIC_BASE_URL=http://localhost:${finalPort}`)
291
- console.log(` export HTTPS_PROXY=http://localhost:${finalMitm}`)
349
+ // Update shell profiles directly
350
+ const profiles = [
351
+ path.join(os.homedir(), '.zshrc'),
352
+ path.join(os.homedir(), '.bashrc'),
353
+ path.join(os.homedir(), '.bash_profile'),
354
+ ]
355
+ const envBlock = [
356
+ `export SQUEEZR_PORT=${finalPort}`,
357
+ `export SQUEEZR_MITM_PORT=${finalMitm}`,
358
+ `export ANTHROPIC_BASE_URL=http://localhost:${finalPort}`,
359
+ `export GEMINI_API_BASE_URL=http://localhost:${finalPort}`,
360
+ `export HTTPS_PROXY=http://localhost:${finalMitm}`,
361
+ ].join('\n')
362
+ for (const p of profiles) {
363
+ try {
364
+ let content = fs.readFileSync(p, 'utf-8')
365
+ if (content.includes('# squeezr env vars')) {
366
+ // Replace existing block (from marker to the closing fi)
367
+ content = content.replace(
368
+ /# squeezr env vars[\s\S]*?fi/,
369
+ `# squeezr env vars\n${envBlock}\n# squeezr auto-heal\nif ! curl -sf http://localhost:${finalPort}/squeezr/health > /dev/null 2>&1; then squeezr start > /dev/null 2>&1; fi`
370
+ )
371
+ fs.writeFileSync(p, content)
372
+ console.log(` [ok] Updated ${p}`)
373
+ }
374
+ } catch {}
375
+ }
376
+ // Also update env for WSL setx if on WSL
377
+ try {
378
+ const procVersion = fs.readFileSync('/proc/version', 'utf-8')
379
+ if (/microsoft|wsl/i.test(procVersion)) {
380
+ const setx = '/mnt/c/Windows/System32/setx.exe'
381
+ try { execSync(`"${setx}" SQUEEZR_PORT "${finalPort}"`, { stdio: 'pipe' }) } catch {}
382
+ try { execSync(`"${setx}" SQUEEZR_MITM_PORT "${finalMitm}"`, { stdio: 'pipe' }) } catch {}
383
+ try { execSync(`"${setx}" ANTHROPIC_BASE_URL "http://localhost:${finalPort}"`, { stdio: 'pipe' }) } catch {}
384
+ try { execSync(`"${setx}" GEMINI_API_BASE_URL "http://localhost:${finalPort}"`, { stdio: 'pipe' }) } catch {}
385
+ try { execSync(`"${setx}" HTTPS_PROXY "http://localhost:${finalMitm}"`, { stdio: 'pipe' }) } catch {}
386
+ }
387
+ } catch {}
292
388
  }
293
389
 
294
- console.log(`\nRestart squeezr for changes to take effect:`)
295
- console.log(` squeezr stop && squeezr start`)
390
+ // Apply to current process so stop/start works immediately
391
+ process.env.SQUEEZR_PORT = String(finalPort)
392
+ process.env.SQUEEZR_MITM_PORT = String(finalMitm)
393
+ process.env.ANTHROPIC_BASE_URL = `http://localhost:${finalPort}`
394
+ process.env.HTTPS_PROXY = `http://localhost:${finalMitm}`
395
+
396
+ // Auto stop + start
397
+ console.log('')
398
+ stopProxy()
399
+ await new Promise(r => setTimeout(r, 1500))
400
+ await startDaemon()
401
+ console.log(`\nOpen a new terminal for env vars to apply to other tools.`)
296
402
  }
297
403
 
298
404
  // ── squeezr uninstall ─────────────────────────────────────────────────────────
@@ -393,7 +499,7 @@ function setupWindows() {
393
499
  console.log('Setting up Squeezr for Windows...\n')
394
500
 
395
501
  // 1. Set env vars permanently via setx (user scope, no admin needed)
396
- const port = process.env.SQUEEZR_PORT || 8080
502
+ const port = getPort()
397
503
  const mitmPort = getMitmPort(port)
398
504
  const caPath = path.join(os.homedir(), '.squeezr', 'mitm-ca', 'ca.crt')
399
505
  const vars = {
@@ -548,7 +654,7 @@ function setupUnix() {
548
654
 
549
655
  // 1. Set env vars + auto-heal guard in shell profile
550
656
  const distIndex = path.join(ROOT, 'dist', 'index.js')
551
- const port = process.env.SQUEEZR_PORT || 8080
657
+ const port = getPort()
552
658
  const mitmPort = getMitmPort(port)
553
659
  const bundlePath = path.join(os.homedir(), '.squeezr', 'mitm-ca', 'bundle.crt')
554
660
  const shellBlock = [
@@ -681,7 +787,7 @@ function setupWSL() {
681
787
  // The guard checks if the proxy is alive on terminal open. If not, it starts
682
788
  // it in the background. This is the safety net for WSL2 where systemd and
683
789
  // Task Scheduler may both fail.
684
- const port = process.env.SQUEEZR_PORT || 8080
790
+ const port = getPort()
685
791
  const mitmPort = getMitmPort(port)
686
792
  const bundlePath = path.join(os.homedir(), '.squeezr', 'mitm-ca', 'bundle.crt')
687
793
  const shellBlock = [
@@ -890,3 +996,5 @@ switch (command) {
890
996
  console.log(HELP)
891
997
  process.exit(1)
892
998
  }
999
+
1000
+ await showUpdateBanner()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squeezr-ai",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
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",