squeezr-ai 1.16.2 → 1.16.4

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
@@ -172,6 +172,7 @@ Squeezr uses cheap/free models for AI compression (the deterministic layer is pu
172
172
  ```bash
173
173
  squeezr setup # configure env vars, auto-start, CA trust
174
174
  squeezr start # start the proxy (auto-restarts if version mismatch after update)
175
+ squeezr update # kill old processes, install latest from npm, restart
175
176
  squeezr stop # stop the proxy
176
177
  squeezr status # check if proxy is running
177
178
  squeezr logs # show last 50 log lines
package/bin/squeezr.js CHANGED
@@ -16,6 +16,55 @@ 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: squeezr update │`)
63
+ console.log(` ╰─────────────────────────────────────────────────────────╯`)
64
+ }
65
+ } catch {}
66
+ }
67
+
19
68
  function getPortFromToml() {
20
69
  try {
21
70
  const toml = fs.readFileSync(path.join(ROOT, 'squeezr.toml'), 'utf-8')
@@ -55,6 +104,7 @@ Usage:
55
104
  squeezr status Check if proxy is running
56
105
  squeezr config Print config file path and current settings
57
106
  squeezr ports Change HTTP and MITM proxy ports
107
+ squeezr update Kill old processes, install latest from npm, restart
58
108
  squeezr uninstall Remove Squeezr completely (env vars, CA, auto-start, logs)
59
109
  squeezr version Print version
60
110
  squeezr help Show this help
@@ -900,6 +950,44 @@ switch (command) {
900
950
  else setupUnix()
901
951
  break
902
952
 
953
+ case 'update':
954
+ await (async () => {
955
+ console.log('Stopping Squeezr...')
956
+ stopProxy()
957
+ // Also kill anything on the ports by brute force
958
+ const uPort = getPort()
959
+ const uMitmPort = getMitmPort(uPort)
960
+ if (process.platform === 'win32') {
961
+ try { execSync(`for /f "tokens=5" %a in ('netstat -ano ^| findstr ":${uPort} " ^| findstr LISTENING') do taskkill /F /PID %a`, { stdio: 'pipe', shell: 'cmd.exe' }) } catch {}
962
+ try { execSync(`for /f "tokens=5" %a in ('netstat -ano ^| findstr ":${uMitmPort} " ^| findstr LISTENING') do taskkill /F /PID %a`, { stdio: 'pipe', shell: 'cmd.exe' }) } catch {}
963
+ } else {
964
+ try { execSync(`kill -9 $(lsof -ti:${uPort}) 2>/dev/null`, { stdio: 'pipe' }) } catch {}
965
+ try { execSync(`kill -9 $(lsof -ti:${uMitmPort}) 2>/dev/null`, { stdio: 'pipe' }) } catch {}
966
+ }
967
+ await new Promise(r => setTimeout(r, 1000))
968
+
969
+ console.log('Installing latest version...')
970
+ try {
971
+ const npmCmd = process.platform === 'win32' ? 'npm' : 'HTTPS_PROXY= npm'
972
+ execSync(`${npmCmd} install -g squeezr-ai@latest`, { stdio: 'inherit' })
973
+ } catch (e) {
974
+ // On Unix, might need sudo
975
+ try {
976
+ execSync('sudo HTTPS_PROXY= npm install -g squeezr-ai@latest', { stdio: 'inherit' })
977
+ } catch {
978
+ console.error('npm install failed. Try manually: npm install -g squeezr-ai')
979
+ process.exit(1)
980
+ }
981
+ }
982
+
983
+ console.log('\nStarting Squeezr...')
984
+ // Re-exec the new binary so we run the updated code
985
+ const squeezrBin = process.argv[1]
986
+ try {
987
+ execSync(`node "${squeezrBin}" start`, { stdio: 'inherit' })
988
+ } catch {}
989
+ })()
990
+ break
903
991
  case 'stop':
904
992
  stopProxy()
905
993
  break
@@ -947,3 +1035,5 @@ switch (command) {
947
1035
  console.log(HELP)
948
1036
  process.exit(1)
949
1037
  }
1038
+
1039
+ await showUpdateBanner()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squeezr-ai",
3
- "version": "1.16.2",
3
+ "version": "1.16.4",
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",
@@ -50,6 +50,7 @@
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.14.0",
52
52
  "@types/node-forge": "^1.3.14",
53
+ "@vitest/coverage-v8": "^4.1.2",
53
54
  "tsx": "^4.19.3",
54
55
  "typescript": "^5.8.3",
55
56
  "vitest": "^3.1.1"