@nxuss/lemma 0.4.3 → 0.4.5

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/lemma-proxy.cjs +131 -12
  2. package/package.json +2 -1
package/lemma-proxy.cjs CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  /**
4
- * Lemma Proxy v0.4.2 — Universal AI Cache CLI
4
+ * Lemma Proxy — Universal AI Cache CLI
5
5
  * Commands: start, stop, stats, status, activate <key>
6
6
  */
7
+ const pkg = require('./package.json');
8
+ const VERSION = pkg.version;
7
9
 
8
10
  const { program } = require('commander');
9
11
  const express = require('express');
@@ -76,6 +78,46 @@ function ensureProjectDir(name) {
76
78
  return dir;
77
79
  }
78
80
 
81
+ function getPidByPort(port) {
82
+ const { execSync } = require('child_process');
83
+ try {
84
+ if (process.platform === 'win32') {
85
+ const out = execSync(`netstat -ano | findstr :${port}`).toString();
86
+ const lines = out.split('\n').filter(l => l.includes('LISTENING'));
87
+ if (lines.length > 0) return lines[0].trim().split(/\s+/).pop();
88
+ } else {
89
+ const out = execSync(`lsof -t -i :${port}`).toString().trim();
90
+ return out.split('\n')[0]; // Take first if multiple
91
+ }
92
+ } catch { return null; }
93
+ return null;
94
+ }
95
+
96
+ async function checkDependency(url) {
97
+ try {
98
+ const res = await axios.get(url, { timeout: 1000, validateStatus: () => true });
99
+ return res.status >= 200 && res.status < 500;
100
+ } catch { return false; }
101
+ }
102
+
103
+ function ensureGitIgnore() {
104
+ const gi = path.join(process.cwd(), '.gitignore');
105
+ const line = '.lemma/';
106
+ try {
107
+ if (fs.existsSync(gi)) {
108
+ const content = fs.readFileSync(gi, 'utf8');
109
+ if (!content.includes(line)) {
110
+ fs.appendFileSync(gi, `\n# Lemma Context Logs\n${line}\n`);
111
+ return true;
112
+ }
113
+ } else {
114
+ fs.writeFileSync(gi, `# Lemma Context Logs\n${line}\n`);
115
+ return true;
116
+ }
117
+ } catch {}
118
+ return false;
119
+ }
120
+
79
121
  // ── License helpers ────────────────────────────────────────────────────────────
80
122
  function loadLicense() {
81
123
  return readJson(LICENSE_FILE, { isPro: false });
@@ -87,7 +129,7 @@ async function validateKeyRemote(key) {
87
129
  const body = JSON.stringify({ key });
88
130
  const u = new URL(VALIDATE_URL);
89
131
  const req = https.request({ hostname: u.hostname, port: 443, path: u.pathname, method: 'POST',
90
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'User-Agent': 'lemma-proxy/0.3.2' }
132
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'User-Agent': `lemma-proxy/${VERSION}` }
91
133
  }, res => {
92
134
  let d = '';
93
135
  res.on('data', c => d += c);
@@ -464,17 +506,53 @@ class LemmaServer {
464
506
 
465
507
  start() {
466
508
  const server = http.createServer(this.app);
467
- server.listen(this.port, () => {
509
+ server.on('error', (e) => {
510
+ if (e.code === 'EADDRINUSE') {
511
+ console.error(`\n❌ Error: Port ${this.port} is already in use.`);
512
+ console.error(`💡 Try stopping the existing process with 'lemma stop' or use a different port with '--port <number>'.\n`);
513
+ process.exit(1);
514
+ }
515
+ throw e;
516
+ });
517
+ server.listen(this.port, async () => {
468
518
  fs.writeFileSync(PID_FILE, String(process.pid));
469
519
  fs.writeFileSync(PORT_FILE, String(this.port));
470
- console.log(`\n🚀 Lemma Proxy v0.4.2\n📁 Project : ${this.projectName}\n🔌 Port : ${this.port}\n`);
520
+
521
+ const pro = isPro();
522
+ console.log(`\n🚀 Lemma Proxy v${VERSION} — ${pro ? 'PRO' : 'STANDARD'}`);
523
+ console.log(`📁 Project : ${this.projectName}\n🔌 Port : ${this.port}`);
524
+
525
+ console.log('\n🧠 Intelligence Report');
526
+ console.log('────────────────────────────────────────────────');
527
+ console.log(`🔒 Privacy Firewall : \x1b[32m[ACTIVE]\x1b[0m`);
528
+ console.log(`🔀 Complexity Router : \x1b[32m[ACTIVE]\x1b[0m`);
529
+ console.log(`💾 Exact Cache : \x1b[32m[ACTIVE]\x1b[0m`);
530
+
531
+ const ollamaOk = await checkDependency('http://localhost:11434/api/tags');
532
+ const chromaOk = await checkDependency('http://localhost:8000/'); // Basic check for Chroma
533
+
534
+ if (pro) {
535
+ console.log(`🎯 Semantic Cache : ${ollamaOk && chromaOk ? '\x1b[32m[ACTIVE]\x1b[0m' : '\x1b[33m[OFFLINE - Check Ollama/Chroma]\x1b[0m'}`);
536
+ console.log(`🌐 Hive Mind (Cloud) : \x1b[32m[ACTIVE]\x1b[0m`);
537
+ } else {
538
+ console.log(`🎯 Semantic Cache : \x1b[90m[PRO ONLY]\x1b[0m -> https://lemma.nxus.studio/upgrade`);
539
+ console.log(`🌐 Hive Mind (Cloud) : \x1b[90m[PRO ONLY]\x1b[0m -> https://lemma.nxus.studio/upgrade`);
540
+ }
541
+
542
+ const hasStackDir = fs.existsSync(path.join(process.cwd(), '.lemma'));
543
+ console.log(`📡 Telepathic Sync : ${hasStackDir ? '\x1b[32m[READY]\x1b[0m' : '\x1b[90m[DISABLED - Run "lemma init"]\x1b[0m'}`);
544
+ console.log('────────────────────────────────────────────────\n');
545
+
546
+ if (!pro) {
547
+ console.log('\x1b[36m💡 Unlock Semantic Search & Team Caching at https://lemma.nxus.studio/upgrade\x1b[0m\n');
548
+ }
471
549
  });
472
550
  process.on('SIGTERM', () => server.close());
473
551
  }
474
552
  }
475
553
 
476
554
  // ── CLI ────────────────────────────────────────────────────────────────────────
477
- program.name('lemma').description('Lemma Proxy CLI — Intelligent AI Gateway').version('0.4.2');
555
+ program.name('lemma').description('Lemma Proxy CLI — Intelligent AI Gateway').version(VERSION);
478
556
 
479
557
  program.command('start')
480
558
  .description('Start the proxy server')
@@ -484,6 +562,16 @@ program.command('start')
484
562
  .action(async (opts) => {
485
563
  const port = parseInt(opts.port, 10);
486
564
 
565
+ // Check if already running
566
+ try {
567
+ const resp = await axios.get(`http://localhost:${port}/health`, { timeout: 500 });
568
+ if (resp.data && resp.data.status === 'ok') {
569
+ console.log(`\n⚠️ Lemma Proxy is already running on port ${port} (Project: ${resp.data.project})`);
570
+ console.log(`💡 Use 'lemma status' for details or 'lemma stop' to restart.\n`);
571
+ process.exit(0);
572
+ }
573
+ } catch {}
574
+
487
575
  if (opts.stack) {
488
576
  const { spawn } = require('child_process');
489
577
  console.log('🚀 Launching Lemma Full Stack...');
@@ -534,15 +622,32 @@ program.command('start')
534
622
 
535
623
  program.command('stop')
536
624
  .description('Stop the proxy server')
537
- .action(() => {
625
+ .option('-p, --port <number>', 'Port to check', '8081')
626
+ .action((opts) => {
627
+ const port = parseInt(opts.port, 10);
628
+ let stopped = false;
538
629
  try {
539
630
  if (fs.existsSync(PID_FILE)) {
540
631
  const pid = fs.readFileSync(PID_FILE, 'utf8');
541
- process.kill(parseInt(pid), 'SIGTERM');
632
+ try {
633
+ process.kill(parseInt(pid), 'SIGTERM');
634
+ stopped = true;
635
+ } catch {}
636
+ }
637
+
638
+ if (!stopped) {
639
+ const pidByPort = getPidByPort(port);
640
+ if (pidByPort) {
641
+ process.kill(parseInt(pidByPort), 'SIGTERM');
642
+ console.log(`✅ Stopped process ${pidByPort} on port ${port}`);
643
+ stopped = true;
644
+ }
645
+ }
646
+
647
+ if (stopped) {
542
648
  console.log('✅ Stopped');
543
649
  } else {
544
- console.log('⚠️ No PID file found. Checking for processes...');
545
- // Fallback for windows/mac: kill by port or name
650
+ console.log('⚠️ No running process found.');
546
651
  }
547
652
  } catch { console.log('⚠️ Could not stop process.'); }
548
653
  if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);
@@ -605,15 +710,28 @@ program.command('init')
605
710
  .description('Initialize Lemma in the current project (auto-discovery)')
606
711
  .action(() => {
607
712
  const project = detectProject();
608
- console.log(`🛠️ Initializing Lemma for [${project}]...`);
713
+ console.log(`\n🛠️ Initializing Lemma for [${project}]...`);
609
714
 
715
+ // 1. Create .lemma directory
716
+ const lemmaDir = path.join(process.cwd(), '.lemma');
717
+ if (!fs.existsSync(lemmaDir)) {
718
+ fs.mkdirSync(lemmaDir, { recursive: true });
719
+ console.log('✅ Created .lemma directory (Telepathic Sync enabled)');
720
+ }
721
+
722
+ // 2. Update .gitignore
723
+ if (ensureGitIgnore()) {
724
+ console.log('✅ Added .lemma/ to .gitignore');
725
+ }
726
+
727
+ // 3. Update .env
610
728
  const envFile = path.join(process.cwd(), '.env');
611
729
  const lemmaConfig = `\n# Lemma AI Gateway Configuration\nOPENAI_BASE_URL=http://localhost:8081/v1\nLEMMA_PROJECT=${project}\n`;
612
730
 
613
731
  if (fs.existsSync(envFile)) {
614
732
  const content = fs.readFileSync(envFile, 'utf8');
615
733
  if (content.includes('OPENAI_BASE_URL')) {
616
- console.log('⚠️ OPENAI_BASE_URL already exists in .env. Please update it manually to: http://localhost:8081/v1');
734
+ console.log('⚠️ OPENAI_BASE_URL already exists in .env. Update it to: http://localhost:8081/v1');
617
735
  } else {
618
736
  fs.appendFileSync(envFile, lemmaConfig);
619
737
  console.log('✅ Added Lemma configuration to .env');
@@ -623,7 +741,8 @@ program.command('init')
623
741
  console.log('✅ Created .env with Lemma configuration');
624
742
  }
625
743
 
626
- console.log('\n🚀 Done! Run "lemma start" to begin.');
744
+ console.log('\n Project initialized for the Agentic Era!');
745
+ console.log('🚀 Run "lemma start" to begin.\n');
627
746
  });
628
747
 
629
748
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxuss/lemma",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Intelligent AI Gateway — Semantic cache, Privacy Firewall, and Autonomous Cost-Optimization for AI Agents.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -161,6 +161,7 @@
161
161
  "homepage": "https://github.com/Nxusbets/lemma#readme",
162
162
  "dependencies": {
163
163
  "@chroma-core/default-embed": "^0.1.9",
164
+ "@nxuss/lemma": "^0.4.3",
164
165
  "@types/cors": "^2.8.19",
165
166
  "axios": "^1.6.0",
166
167
  "commander": "^14.0.3",