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 +164 -125
- package/dist/dashboard.d.ts +4 -6
- package/dist/dashboard.js +1220 -425
- package/dist/history.d.ts +49 -0
- package/dist/history.js +127 -0
- package/dist/index.js +4 -1
- package/dist/limits.d.ts +81 -0
- package/dist/limits.js +242 -0
- package/dist/mcp.js +121 -0
- package/dist/server.js +142 -10
- package/dist/stats.d.ts +11 -0
- package/dist/stats.js +30 -0
- package/package.json +1 -1
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
|
-
|
|
448
|
-
console.log(
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
console.warn(
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
console.log(
|
|
457
|
-
console.log('')
|
|
458
|
-
console.log('
|
|
459
|
-
console.log('
|
|
460
|
-
console.log('
|
|
461
|
-
console.log('
|
|
462
|
-
console.log('
|
|
463
|
-
console.log('
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
path.join(os.homedir(), '.
|
|
471
|
-
path.join(os.homedir(), '.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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}
|
|
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.
|
|
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
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
-
|
|
1386
|
-
|
|
1387
|
-
} catch {
|
|
1403
|
+
let installed = false
|
|
1404
|
+
for (let attempt = 1; attempt <= 4; attempt++) {
|
|
1388
1405
|
try {
|
|
1389
|
-
execSync('
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
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':
|