squeezr-ai 1.17.7 → 1.17.12

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/bin/squeezr.js CHANGED
@@ -199,8 +199,8 @@ Usage:
199
199
  squeezr discover Show pattern coverage report (proxy must be running)
200
200
  squeezr status Check if proxy is running
201
201
  squeezr config Print config file path and current settings
202
- squeezr mcp install Register Squeezr MCP server in Claude Code, Cursor, Windsurf & Cline
203
- squeezr mcp uninstall Remove Squeezr MCP registration
202
+ squeezr mcp install Register Squeezr MCP server in Claude Code, Cursor, Windsurf & Cline
203
+ squeezr mcp uninstall Remove Squeezr MCP registration
204
204
  squeezr ports Change HTTP and MITM proxy ports
205
205
  squeezr tunnel Expose proxy via Cloudflare Tunnel for Cursor IDE
206
206
  squeezr update Kill old processes, install latest from npm, restart
@@ -248,6 +248,7 @@ async function startDaemon() {
248
248
  console.log(`Squeezr is already running (v${pkg.version})`)
249
249
  console.log(` HTTP proxy (Claude/Aider/Gemini): http://localhost:${port}`)
250
250
  console.log(` MITM proxy (Codex): http://localhost:${mitmPort}`)
251
+ console.log(` Dashboard: http://localhost:${port}/squeezr/dashboard`)
251
252
  return
252
253
  }
253
254
  // Version mismatch — old process from before npm update. Kill and restart.
@@ -275,6 +276,7 @@ async function startDaemon() {
275
276
  console.log(`Squeezr started (pid ${child.pid})`)
276
277
  console.log(` HTTP proxy (Claude/Aider/Gemini): http://localhost:${port}`)
277
278
  console.log(` MITM proxy (Codex): http://localhost:${mitmPort}`)
279
+ console.log(` Dashboard: http://localhost:${port}/squeezr/dashboard`)
278
280
  console.log(` Logs: ${logFile}`)
279
281
 
280
282
  }
@@ -296,6 +298,20 @@ function showLogs() {
296
298
  console.log(tail.join('\n'))
297
299
  }
298
300
 
301
+ function killMcpProcesses() {
302
+ if (process.platform === 'win32') {
303
+ try {
304
+ execSync(
305
+ `powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \\"name='node.exe'\\" | Where-Object { $_.CommandLine -like '*squeezr*mcp*' -or $_.CommandLine -like '*mcp.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"`,
306
+ { stdio: 'pipe' }
307
+ )
308
+ } catch {}
309
+ } else {
310
+ try { execSync(`pkill -f 'squeezr.*mcp' 2>/dev/null`, { stdio: 'pipe' }) } catch {}
311
+ try { execSync(`pkill -f 'mcp\\.js' 2>/dev/null`, { stdio: 'pipe' }) } catch {}
312
+ }
313
+ }
314
+
299
315
  function stopProxy() {
300
316
  const port = getPort()
301
317
  const mitmPort = getMitmPort(port)
@@ -332,6 +348,10 @@ function stopProxy() {
332
348
  }
333
349
  } catch {}
334
350
  }
351
+
352
+ // Also stop the MCP server process
353
+ killMcpProcesses()
354
+
335
355
  // Clear HTTPS_PROXY so npm and other tools don't try to use the dead proxy
336
356
  if (process.platform === 'win32') {
337
357
  try { execSync('setx HTTPS_PROXY ""', { stdio: 'pipe' }) } catch {}
@@ -344,8 +364,6 @@ function stopProxy() {
344
364
 
345
365
  if (killed) {
346
366
  console.log(`Squeezr stopped`)
347
- console.log(` HTTP proxy (Claude/Aider/Gemini): http://localhost:${port}`)
348
- console.log(` MITM proxy (Codex): http://localhost:${mitmPort}`)
349
367
  } else {
350
368
  console.log(`Squeezr is not running`)
351
369
  }
@@ -364,6 +382,7 @@ async function checkStatus() {
364
382
  console.log(`Squeezr is running (v${json.version})`)
365
383
  console.log(` HTTP proxy (Claude/Aider/Gemini): http://localhost:${port}`)
366
384
  console.log(` MITM proxy (Codex): http://localhost:${mitmPort}`)
385
+ console.log(` Dashboard: http://localhost:${port}/squeezr/dashboard`)
367
386
  } catch {
368
387
  console.log(`Squeezr is running on port ${port}`)
369
388
  }
@@ -393,100 +412,102 @@ function showConfig() {
393
412
  }
394
413
  }
395
414
 
396
-
397
- // ── squeezr mcp ───────────────────────────────────────────────────────────────
398
-
399
- async function mcpInstall() {
400
- const mcpServerPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'mcp.js')
401
- const entry = {
402
- type: 'stdio',
403
- command: 'node',
404
- args: [mcpServerPath],
405
- }
406
-
407
- const targets = [
408
- {
409
- name: 'Claude Code',
410
- file: path.join(os.homedir(), '.claude.json'),
411
- key: 'mcpServers',
412
- },
413
- {
414
- name: 'Cursor',
415
- file: path.join(os.homedir(), '.cursor', 'mcp.json'),
416
- key: 'mcpServers',
417
- },
418
- {
419
- name: 'Windsurf',
420
- file: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
421
- key: 'mcpServers',
422
- },
423
- {
424
- name: 'Cline / Roo-Cline',
425
- file: path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
426
- key: 'mcpServers',
427
- },
428
- ]
429
-
430
- let installed = 0
431
-
432
- for (const target of targets) {
433
- try {
434
- // Only install into configs that already exist (user has that tool)
435
- if (!fs.existsSync(target.file) && target.name !== 'Claude Code') continue
436
-
437
- let cfg = {}
438
- if (fs.existsSync(target.file)) {
439
- try { cfg = JSON.parse(fs.readFileSync(target.file, 'utf-8')) } catch { cfg = {} }
440
- }
441
- cfg[target.key] = cfg[target.key] || {}
442
- cfg[target.key].squeezr = entry
443
-
444
- const dir = path.dirname(target.file)
445
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
446
- fs.writeFileSync(target.file, JSON.stringify(cfg, null, 2))
447
- console.log()
448
- console.log(' ok ' + target.name + ': ' + target.file)
449
- } catch (e) {
450
- console.warn()
451
- console.warn(' warn ' + target.name + ': ' + (e.message || e))
452
- }
453
-
454
- console.log()
455
- console.log('MCP server registered in ' + installed + ' client(s).')
456
- console.log('Server binary: ' + mcpServerPath)
457
- console.log('')
458
- console.log('Available tools in Claude/Codex/Cursor:')
459
- console.log(' squeezr_status — Check if Squeezr is running')
460
- console.log(' squeezr_stats — Real-time token savings')
461
- console.log(' squeezr_set_mode Change compression aggressiveness')
462
- console.log(' squeezr_config Current configuration')
463
- console.log(' squeezr_habits Wasteful pattern report')
464
- }
465
-
466
- async function mcpUninstall() {
467
- const files = [
468
- path.join(os.homedir(), '.claude.json'),
469
- path.join(os.homedir(), '.cursor', 'mcp.json'),
470
- path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
471
- path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
472
- ]
473
- let removed = 0
474
- for (const file of files) {
475
- if (!fs.existsSync(file)) continue
476
- try {
477
- const cfg = JSON.parse(fs.readFileSync(file, 'utf-8'))
478
- if (cfg.mcpServers?.squeezr) {
479
- delete cfg.mcpServers.squeezr
480
- fs.writeFileSync(file, JSON.stringify(cfg, null, 2))
481
- console.log()
482
- removed++
483
- }
484
- } catch { /* ignore */ }
485
- }
486
- if (removed === 0) console.log('Squeezr MCP not found in any config.')
487
- else console.log()
488
- }
489
-
415
+
416
+ // ── squeezr mcp ───────────────────────────────────────────────────────────────
417
+
418
+ async function mcpInstall() {
419
+ const mcpServerPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'mcp.js')
420
+ const entry = {
421
+ type: 'stdio',
422
+ command: 'node',
423
+ args: [mcpServerPath],
424
+ }
425
+
426
+ const targets = [
427
+ {
428
+ name: 'Claude Code',
429
+ file: path.join(os.homedir(), '.claude.json'),
430
+ key: 'mcpServers',
431
+ },
432
+ {
433
+ name: 'Cursor',
434
+ file: path.join(os.homedir(), '.cursor', 'mcp.json'),
435
+ key: 'mcpServers',
436
+ },
437
+ {
438
+ name: 'Windsurf',
439
+ file: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
440
+ key: 'mcpServers',
441
+ },
442
+ {
443
+ name: 'Cline / Roo-Cline',
444
+ file: path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
445
+ key: 'mcpServers',
446
+ },
447
+ ]
448
+
449
+ let installed = 0
450
+
451
+ for (const target of targets) {
452
+ try {
453
+ // Only install into configs that already exist (user has that tool)
454
+ if (!fs.existsSync(target.file) && target.name !== 'Claude Code') continue
455
+
456
+ let cfg = {}
457
+ if (fs.existsSync(target.file)) {
458
+ try { cfg = JSON.parse(fs.readFileSync(target.file, 'utf-8')) } catch { cfg = {} }
459
+ }
460
+ cfg[target.key] = cfg[target.key] || {}
461
+ cfg[target.key].squeezr = entry
462
+
463
+ const dir = path.dirname(target.file)
464
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
465
+ fs.writeFileSync(target.file, JSON.stringify(cfg, null, 2))
466
+ installed++
467
+ console.log()
468
+ console.log(' ok ' + target.name + ': ' + target.file)
469
+ } catch (e) {
470
+ console.warn()
471
+ console.warn(' warn ' + target.name + ': ' + (e.message || e))
472
+ }
473
+ }
474
+
475
+ console.log()
476
+ console.log('MCP server registered in ' + installed + ' client(s).')
477
+ console.log('Server binary: ' + mcpServerPath)
478
+ console.log('')
479
+ console.log('Available tools in Claude/Codex/Cursor:')
480
+ console.log(' squeezr_status Check if Squeezr is running')
481
+ console.log(' squeezr_stats Real-time token savings')
482
+ console.log(' squeezr_set_mode Change compression aggressiveness')
483
+ console.log(' squeezr_config — Current configuration')
484
+ console.log(' squeezr_habits — Wasteful pattern report')
485
+ }
486
+
487
+ async function mcpUninstall() {
488
+ const files = [
489
+ path.join(os.homedir(), '.claude.json'),
490
+ path.join(os.homedir(), '.cursor', 'mcp.json'),
491
+ path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
492
+ path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
493
+ ]
494
+ let removed = 0
495
+ for (const file of files) {
496
+ if (!fs.existsSync(file)) continue
497
+ try {
498
+ const cfg = JSON.parse(fs.readFileSync(file, 'utf-8'))
499
+ if (cfg.mcpServers?.squeezr) {
500
+ delete cfg.mcpServers.squeezr
501
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2))
502
+ console.log()
503
+ removed++
504
+ }
505
+ } catch { /* ignore */ }
506
+ }
507
+ if (removed === 0) console.log('Squeezr MCP not found in any config.')
508
+ else console.log()
509
+ }
510
+
490
511
  // ── squeezr ports ─────────────────────────────────────────────────────────────
491
512
 
492
513
  async function configurePorts() {
@@ -505,7 +526,8 @@ async function configurePorts() {
505
526
 
506
527
  console.log(`\nCurrent ports:`)
507
528
  console.log(` HTTP proxy (Claude/Aider/Gemini): ${currentPort}`)
508
- console.log(` MITM proxy (Codex): ${currentMitm}\n`)
529
+ console.log(` MITM proxy (Codex): ${currentMitm}`)
530
+ console.log(` Dashboard: ${currentPort}/squeezr/dashboard (same port as proxy)\n`)
509
531
 
510
532
  const newPort = await ask(`HTTP proxy port [${currentPort}]: `)
511
533
  const newMitm = await ask(`MITM proxy port [${currentMitm}]: `)
@@ -718,7 +740,11 @@ async function uninstall() {
718
740
  } catch {}
719
741
  }
720
742
 
721
- // 8. npm uninstall -g (clear HTTPS_PROXY first so npm doesn't hit dead proxy)
743
+ // 8. Remove MCP registrations
744
+ console.log(' [..] Removing MCP registrations...')
745
+ try { await mcpUninstall() } catch {}
746
+
747
+ // 9. npm uninstall -g (clear HTTPS_PROXY first so npm doesn't hit dead proxy)
722
748
  console.log(' [..] Uninstalling npm package...')
723
749
  const cleanEnv = { ...process.env, HTTPS_PROXY: '', https_proxy: '', HTTP_PROXY: '', http_proxy: '' }
724
750
  try {
@@ -1367,31 +1393,43 @@ switch (command) {
1367
1393
  case 'update':
1368
1394
  await (async () => {
1369
1395
  console.log('Stopping Squeezr...')
1370
- stopProxy()
1371
- // Also kill anything on the ports by brute force
1372
- const uPort = getPort()
1373
- const uMitmPort = getMitmPort(uPort)
1374
- if (process.platform === 'win32') {
1375
- 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 {}
1376
- 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 {}
1377
- } else {
1378
- try { execSync(`kill -9 $(lsof -ti:${uPort}) 2>/dev/null`, { stdio: 'pipe' }) } catch {}
1379
- try { execSync(`kill -9 $(lsof -ti:${uMitmPort}) 2>/dev/null`, { stdio: 'pipe' }) } catch {}
1380
- }
1381
- await new Promise(r => setTimeout(r, 1000))
1396
+ stopProxy() // also kills MCP via killMcpProcesses()
1397
+ console.log('Releasing file locks...')
1398
+ killMcpProcesses() // double-kill in case stopProxy was too fast
1399
+ await new Promise(r => setTimeout(r, 2000))
1382
1400
 
1383
1401
  console.log('Installing latest version...')
1384
1402
  const cleanEnv = { ...process.env, HTTPS_PROXY: '', https_proxy: '', HTTP_PROXY: '', http_proxy: '' }
1385
- try {
1386
- execSync('npm install -g squeezr-ai@latest', { stdio: 'inherit', env: cleanEnv })
1387
- } catch {
1403
+ let installed = false
1404
+ for (let attempt = 1; attempt <= 4; attempt++) {
1388
1405
  try {
1389
- execSync('sudo npm install -g squeezr-ai@latest', { stdio: 'inherit', env: cleanEnv })
1390
- } catch {
1391
- console.error('npm install failed. Try manually: HTTPS_PROXY= npm install -g squeezr-ai')
1392
- process.exit(1)
1406
+ execSync('npm install -g squeezr-ai@latest', { stdio: 'inherit', env: cleanEnv })
1407
+ installed = true
1408
+ break
1409
+ } catch (err) {
1410
+ const msg = String(err?.stderr || err?.message || '')
1411
+ if ((msg.includes('EBUSY') || msg.includes('EPERM')) && attempt < 4) {
1412
+ console.log(` Files still locked, retrying in 3s (attempt ${attempt}/4)...`)
1413
+ // Try harder to release locks on retry
1414
+ killMcpProcesses()
1415
+ await new Promise(r => setTimeout(r, 3000))
1416
+ } else if (!msg.includes('EBUSY') && !msg.includes('EPERM') && process.platform !== 'win32') {
1417
+ // On Unix, try sudo as fallback (not useful on Windows)
1418
+ try {
1419
+ execSync('sudo npm install -g squeezr-ai@latest', { stdio: 'inherit', env: cleanEnv })
1420
+ installed = true
1421
+ } catch {}
1422
+ break
1423
+ } else {
1424
+ break
1425
+ }
1393
1426
  }
1394
1427
  }
1428
+ if (!installed) {
1429
+ console.error('\nUpdate failed: files are still locked.')
1430
+ console.error('Fix: close Claude Code completely (this releases the MCP server lock), then run "squeezr update" again.')
1431
+ process.exit(1)
1432
+ }
1395
1433
 
1396
1434
  // Clear update check cache
1397
1435
  try { fs.unlinkSync(UPDATE_CHECK_FILE) } catch {}
@@ -1433,6 +1471,7 @@ switch (command) {
1433
1471
  console.log(`Squeezr started (pid ${child.pid})`)
1434
1472
  console.log(` HTTP proxy (Claude/Aider/Gemini): http://localhost:${startPort}`)
1435
1473
  console.log(` MITM proxy (Codex): http://localhost:${startMitmPort}`)
1474
+ console.log(` Dashboard: http://localhost:${startPort}/squeezr/dashboard`)
1436
1475
  console.log(` Logs: ${logFile}`)
1437
1476
 
1438
1477
  // Ensure PowerShell wrapper is installed (so env vars refresh automatically)
@@ -1474,12 +1513,12 @@ switch (command) {
1474
1513
  showConfig()
1475
1514
  break
1476
1515
 
1477
- case 'mcp': {
1478
- const subCmd = args[0] ?? 'install'
1479
- if (subCmd === 'uninstall') await mcpUninstall()
1480
- else await mcpInstall()
1481
- break
1482
- }
1516
+ case 'mcp': {
1517
+ const subCmd = args[0] ?? 'install'
1518
+ if (subCmd === 'uninstall') await mcpUninstall()
1519
+ else await mcpInstall()
1520
+ break
1521
+ }
1483
1522
  case 'version':
1484
1523
  case '--version':
1485
1524
  case '-v':