@nxuss/lemma 0.8.0 → 0.8.2
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 +56 -1
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +80 -126
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/cloud/server.d.ts.map +1 -1
- package/dist/cjs/cloud/server.js +81 -3
- package/dist/cjs/cloud/server.js.map +1 -1
- package/dist/cjs/config/index.d.ts.map +1 -1
- package/dist/cjs/config/index.js +2 -1
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/mcp/tools.js +23 -0
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/mcp/utils.d.ts +13 -0
- package/dist/cjs/mcp/utils.d.ts.map +1 -1
- package/dist/cjs/mcp/utils.js +61 -0
- package/dist/cjs/mcp/utils.js.map +1 -1
- package/dist/cjs/observability/IdeContextSync.js +3 -3
- package/dist/cjs/observability/IdeContextSync.js.map +1 -1
- package/dist/cjs/proxy/KeyManager.d.ts.map +1 -1
- package/dist/cjs/proxy/KeyManager.js +9 -1
- package/dist/cjs/proxy/KeyManager.js.map +1 -1
- package/dist/cjs/proxy/ProjectStore.d.ts +2 -0
- package/dist/cjs/proxy/ProjectStore.d.ts.map +1 -1
- package/dist/cjs/proxy/ProjectStore.js +3 -1
- package/dist/cjs/proxy/ProjectStore.js.map +1 -1
- package/dist/cjs/proxy/SseRelay.d.ts +4 -25
- package/dist/cjs/proxy/SseRelay.d.ts.map +1 -1
- package/dist/cjs/proxy/SseRelay.js +60 -63
- package/dist/cjs/proxy/SseRelay.js.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +52 -98
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/cloud/server.d.ts.map +1 -1
- package/dist/esm/cloud/server.js +81 -3
- package/dist/esm/cloud/server.js.map +1 -1
- package/dist/esm/config/index.d.ts.map +1 -1
- package/dist/esm/config/index.js +2 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/mcp/tools.js +24 -1
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/mcp/utils.d.ts +13 -0
- package/dist/esm/mcp/utils.d.ts.map +1 -1
- package/dist/esm/mcp/utils.js +59 -0
- package/dist/esm/mcp/utils.js.map +1 -1
- package/dist/esm/observability/IdeContextSync.js +1 -1
- package/dist/esm/observability/IdeContextSync.js.map +1 -1
- package/dist/esm/proxy/KeyManager.d.ts.map +1 -1
- package/dist/esm/proxy/KeyManager.js +9 -1
- package/dist/esm/proxy/KeyManager.js.map +1 -1
- package/dist/esm/proxy/ProjectStore.d.ts +2 -0
- package/dist/esm/proxy/ProjectStore.d.ts.map +1 -1
- package/dist/esm/proxy/ProjectStore.js +2 -0
- package/dist/esm/proxy/ProjectStore.js.map +1 -1
- package/dist/esm/proxy/SseRelay.d.ts +4 -25
- package/dist/esm/proxy/SseRelay.d.ts.map +1 -1
- package/dist/esm/proxy/SseRelay.js +60 -60
- package/dist/esm/proxy/SseRelay.js.map +1 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/index.d.ts.map +1 -1
- package/package.json +4 -1
- package/dist/cjs/protocol/iap.d.ts +0 -54
- package/dist/cjs/protocol/iap.d.ts.map +0 -1
- package/dist/cjs/protocol/iap.js +0 -108
- package/dist/cjs/protocol/iap.js.map +0 -1
- package/dist/cjs/subconscious/cache.d.ts +0 -34
- package/dist/cjs/subconscious/cache.d.ts.map +0 -1
- package/dist/cjs/subconscious/cache.js +0 -156
- package/dist/cjs/subconscious/cache.js.map +0 -1
- package/dist/cjs/subconscious/embeddings.d.ts +0 -25
- package/dist/cjs/subconscious/embeddings.d.ts.map +0 -1
- package/dist/cjs/subconscious/embeddings.js +0 -65
- package/dist/cjs/subconscious/embeddings.js.map +0 -1
- package/dist/esm/protocol/iap.d.ts +0 -54
- package/dist/esm/protocol/iap.d.ts.map +0 -1
- package/dist/esm/protocol/iap.js +0 -104
- package/dist/esm/protocol/iap.js.map +0 -1
- package/dist/esm/subconscious/cache.d.ts +0 -34
- package/dist/esm/subconscious/cache.d.ts.map +0 -1
- package/dist/esm/subconscious/cache.js +0 -152
- package/dist/esm/subconscious/cache.js.map +0 -1
- package/dist/esm/subconscious/embeddings.d.ts +0 -25
- package/dist/esm/subconscious/embeddings.d.ts.map +0 -1
- package/dist/esm/subconscious/embeddings.js +0 -58
- package/dist/esm/subconscious/embeddings.js.map +0 -1
|
@@ -38,25 +38,22 @@ import SemanticScrubber from '../security/SemanticScrubber';
|
|
|
38
38
|
import CloudSyncClient from '../cloud/CloudSyncClient';
|
|
39
39
|
import { pruneHistoryMessages, squeezePrompt } from '../utils/ContextSqueezer';
|
|
40
40
|
import { AgentMultiplexer } from '../proxy/AgentMultiplexer';
|
|
41
|
+
import { SseRelay } from '../proxy/SseRelay';
|
|
42
|
+
import { KeyManager } from '../proxy/KeyManager';
|
|
43
|
+
import { Gatekeeper } from '../proxy/Gatekeeper';
|
|
41
44
|
import { ProjectRegistry } from '../proxy/ProjectRegistry';
|
|
42
45
|
import { shouldInject, buildMinimalContext, buildDeepContext, sniffProjectFromMessages } from '../proxy/ContextInjector';
|
|
43
46
|
import { savingsLedger } from '../utils/SavingsLedger';
|
|
47
|
+
import { CACHE_DIR, PID_FILE, PORT_FILE, STATS_FILE, USAGE_FILE, LICENSE_FILE, CLIPBOARD_PID_FILE, FREE_TIER_LIMIT } from '../proxy/ProjectStore';
|
|
44
48
|
const chroma = new ChromaClient({ host: 'localhost', port: 8000 });
|
|
45
49
|
const dummyEmbeddingFunction = { generate: (texts) => Promise.resolve([]) };
|
|
46
50
|
const complexityRouter = new ComplexityRouter();
|
|
47
51
|
const semanticScrubber = new SemanticScrubber();
|
|
48
52
|
const cloudSync = new CloudSyncClient();
|
|
49
53
|
const agentMultiplexer = new AgentMultiplexer();
|
|
54
|
+
const keyManager = new KeyManager();
|
|
55
|
+
const gatekeeper = new Gatekeeper(keyManager);
|
|
50
56
|
// ── Paths ──────────────────────────────────────────────────────────────────────
|
|
51
|
-
const HOME = os.homedir();
|
|
52
|
-
const CACHE_DIR = path.join(HOME, '.lemma-cache');
|
|
53
|
-
const PID_FILE = path.join(CACHE_DIR, 'proxy.pid');
|
|
54
|
-
const PORT_FILE = path.join(CACHE_DIR, 'proxy.port');
|
|
55
|
-
const STATS_FILE = path.join(CACHE_DIR, 'stats.json');
|
|
56
|
-
const USAGE_FILE = path.join(CACHE_DIR, 'usage.json');
|
|
57
|
-
const LICENSE_FILE = path.join(CACHE_DIR, 'license.json');
|
|
58
|
-
const CLIPBOARD_PID_FILE = path.join(CACHE_DIR, 'clipboard.pid');
|
|
59
|
-
const FREE_LIMIT = 300;
|
|
60
57
|
const VALIDATE_URL = 'https://lemma.nxus.studio/api/validate-license';
|
|
61
58
|
async function checkDependency(url) {
|
|
62
59
|
try {
|
|
@@ -504,21 +501,8 @@ export function startBackgroundClipboardWatcher(cliOpts) {
|
|
|
504
501
|
}
|
|
505
502
|
}
|
|
506
503
|
// ── License helpers ────────────────────────────────────────────────────────────
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const LICENSE_CACHE_TTL = 10000; // 10 seconds cache
|
|
510
|
-
async function loadLicense() {
|
|
511
|
-
return readJson(LICENSE_FILE, { isPro: false });
|
|
512
|
-
}
|
|
513
|
-
async function isPro() {
|
|
514
|
-
const now = Date.now();
|
|
515
|
-
if (isProCached !== null && (now - lastLicenseCheck) < LICENSE_CACHE_TTL) {
|
|
516
|
-
return isProCached;
|
|
517
|
-
}
|
|
518
|
-
const l = await loadLicense();
|
|
519
|
-
isProCached = !!(l && l.isPro);
|
|
520
|
-
lastLicenseCheck = now;
|
|
521
|
-
return isProCached;
|
|
504
|
+
function isPro() {
|
|
505
|
+
return gatekeeper.getMode() === 'pro';
|
|
522
506
|
}
|
|
523
507
|
async function validateKeyRemote(key) {
|
|
524
508
|
return new Promise((resolve, reject) => {
|
|
@@ -642,7 +626,7 @@ async function getEmbedding(text) {
|
|
|
642
626
|
}
|
|
643
627
|
}
|
|
644
628
|
async function semanticGet(provider, prompt, projectName = 'global') {
|
|
645
|
-
if (!
|
|
629
|
+
if (!isPro())
|
|
646
630
|
return null;
|
|
647
631
|
const emb = await getEmbedding(prompt);
|
|
648
632
|
if (!emb)
|
|
@@ -735,7 +719,7 @@ async function semanticGet(provider, prompt, projectName = 'global') {
|
|
|
735
719
|
return null;
|
|
736
720
|
}
|
|
737
721
|
async function semanticSet(provider, prompt, data, projectName = 'global') {
|
|
738
|
-
if (!
|
|
722
|
+
if (!isPro())
|
|
739
723
|
return;
|
|
740
724
|
const emb = await getEmbedding(prompt);
|
|
741
725
|
if (!emb)
|
|
@@ -838,53 +822,6 @@ function setSseHeaders(res, fromCache, similarity, tier) {
|
|
|
838
822
|
res.setHeader('X-Lemma-Similarity', (similarity || 1.0).toFixed(3));
|
|
839
823
|
res.setHeader('X-Lemma-Tier', tier);
|
|
840
824
|
}
|
|
841
|
-
function buildOaiChunk(id, model, text, stop) {
|
|
842
|
-
return { id, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model,
|
|
843
|
-
choices: [{ index: 0, delta: stop ? {} : { content: text }, finish_reason: stop ? 'stop' : null }] };
|
|
844
|
-
}
|
|
845
|
-
function buildAntChunk(text, stop) {
|
|
846
|
-
if (stop)
|
|
847
|
-
return { type: 'message_stop' };
|
|
848
|
-
return { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text } };
|
|
849
|
-
}
|
|
850
|
-
function buildGemChunk(text, stop) {
|
|
851
|
-
return { candidates: [{ content: { parts: [{ text }] }, finishReason: stop ? 'STOP' : undefined }] };
|
|
852
|
-
}
|
|
853
|
-
async function simulateHitStream(res, cachedData, provider, model, tier, similarity, cacheHeader = 'HIT') {
|
|
854
|
-
setSseHeaders(res, cacheHeader, similarity, tier);
|
|
855
|
-
const id = `lemma-${Date.now()}`;
|
|
856
|
-
let text = '';
|
|
857
|
-
try {
|
|
858
|
-
if (provider === 'openai')
|
|
859
|
-
text = cachedData?.choices?.[0]?.message?.content || '';
|
|
860
|
-
else if (provider === 'anthropic') {
|
|
861
|
-
const c = cachedData?.content;
|
|
862
|
-
text = Array.isArray(c) ? c.map((x) => x.text || '').join('') : String(c || '');
|
|
863
|
-
}
|
|
864
|
-
else if (provider === 'gemini') {
|
|
865
|
-
text = cachedData?.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
catch { }
|
|
869
|
-
const chunkSize = 30;
|
|
870
|
-
for (let i = 0; i < text.length; i += chunkSize) {
|
|
871
|
-
const chunk = text.slice(i, i + chunkSize);
|
|
872
|
-
let evt;
|
|
873
|
-
if (provider === 'openai')
|
|
874
|
-
evt = buildOaiChunk(id, model, chunk, false);
|
|
875
|
-
else if (provider === 'anthropic')
|
|
876
|
-
evt = buildAntChunk(chunk, false);
|
|
877
|
-
else if (provider === 'gemini')
|
|
878
|
-
evt = buildGemChunk(chunk, false);
|
|
879
|
-
res.write(provider === 'gemini' ? `${JSON.stringify(evt)}\n` : `data: ${JSON.stringify(evt)}\n\n`);
|
|
880
|
-
await new Promise(r => setTimeout(r, 15));
|
|
881
|
-
}
|
|
882
|
-
const stopEvt = provider === 'openai' ? buildOaiChunk(id, model, '', true) : (provider === 'anthropic' ? buildAntChunk('', true) : buildGemChunk('', true));
|
|
883
|
-
res.write(provider === 'gemini' ? `${JSON.stringify(stopEvt)}\n` : `data: ${JSON.stringify(stopEvt)}\n\n`);
|
|
884
|
-
if (provider !== 'gemini')
|
|
885
|
-
res.write('data: [DONE]\n\n');
|
|
886
|
-
res.end();
|
|
887
|
-
}
|
|
888
825
|
async function relayMissStream(res, body, provider, tier) {
|
|
889
826
|
setSseHeaders(res, false, 1.0, tier);
|
|
890
827
|
const apiKey = provider === 'openai' ? process.env.OPENAI_API_KEY : process.env.ANTHROPIC_API_KEY;
|
|
@@ -994,8 +931,8 @@ class LemmaServer {
|
|
|
994
931
|
}
|
|
995
932
|
setupRoutes() {
|
|
996
933
|
this.app.get('/health', async (req, res) => {
|
|
997
|
-
const
|
|
998
|
-
res.json({ status: 'ok', project: this.projectName, mode:
|
|
934
|
+
const gs = gatekeeper.getStatus();
|
|
935
|
+
res.json({ status: 'ok', project: this.projectName, mode: gs.mode, tier: gs.isPro ? 'pro' : 'free', port: this.port });
|
|
999
936
|
});
|
|
1000
937
|
// MCP Status endpoint
|
|
1001
938
|
this.app.get('/api/mcp-status', (req, res) => {
|
|
@@ -1094,7 +1031,7 @@ class LemmaServer {
|
|
|
1094
1031
|
activeAgents,
|
|
1095
1032
|
totalAgents,
|
|
1096
1033
|
uptime: process.uptime(),
|
|
1097
|
-
tier: (
|
|
1034
|
+
tier: (isPro()) ? 'pro' : 'free'
|
|
1098
1035
|
});
|
|
1099
1036
|
});
|
|
1100
1037
|
this.app.get('/api/agents', async (req, res) => {
|
|
@@ -1681,14 +1618,14 @@ class LemmaServer {
|
|
|
1681
1618
|
}
|
|
1682
1619
|
async handleCompletion(req, res, provider) {
|
|
1683
1620
|
const t0 = Date.now();
|
|
1684
|
-
const pro =
|
|
1621
|
+
const pro = isPro();
|
|
1685
1622
|
const tier = pro ? 'pro' : 'standard';
|
|
1686
1623
|
const isStream = !!req.body.stream;
|
|
1687
1624
|
let historyTokensSaved = 0;
|
|
1688
1625
|
if (!pro) {
|
|
1689
1626
|
const usage = await loadUsage();
|
|
1690
|
-
if (usage.count >=
|
|
1691
|
-
return res.status(429).json({ error: { message: `Free tier limit (${
|
|
1627
|
+
if (usage.count >= FREE_TIER_LIMIT) {
|
|
1628
|
+
return res.status(429).json({ error: { message: `Free tier limit (${FREE_TIER_LIMIT}/mo). Upgrade: https://lemma.nxus.studio/upgrade` } });
|
|
1692
1629
|
}
|
|
1693
1630
|
}
|
|
1694
1631
|
const rawPrompt = extractPrompt(req.body, provider);
|
|
@@ -1707,7 +1644,7 @@ class LemmaServer {
|
|
|
1707
1644
|
await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, cacheTokensSaved);
|
|
1708
1645
|
const unmaskedData = semanticScrubber.unmask(hit.data, tokenMap);
|
|
1709
1646
|
if (isStream)
|
|
1710
|
-
return
|
|
1647
|
+
return SseRelay.simulateHit(res, unmaskedData, { provider: provider, tier, similarity: 1.0 });
|
|
1711
1648
|
res.setHeader('X-Lemma-Cache', 'HIT');
|
|
1712
1649
|
res.setHeader('X-Lemma-Similarity', '1.000');
|
|
1713
1650
|
res.setHeader('X-Lemma-Tier', tier);
|
|
@@ -1722,7 +1659,7 @@ class LemmaServer {
|
|
|
1722
1659
|
const unmaskedData = semanticScrubber.unmask(semHit.data, tokenMap);
|
|
1723
1660
|
const cacheHeader = semHit.hiveMind ? 'HIT-HIVE-MIND' : 'HIT';
|
|
1724
1661
|
if (isStream)
|
|
1725
|
-
return
|
|
1662
|
+
return SseRelay.simulateHit(res, unmaskedData, { provider: provider, tier, similarity: semHit.similarity, cacheHeader });
|
|
1726
1663
|
res.setHeader('X-Lemma-Cache', cacheHeader);
|
|
1727
1664
|
res.setHeader('X-Lemma-Similarity', semHit.similarity.toFixed(3));
|
|
1728
1665
|
res.setHeader('X-Lemma-Tier', tier);
|
|
@@ -1772,7 +1709,7 @@ Adjusted Answer:`;
|
|
|
1772
1709
|
await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, 2000);
|
|
1773
1710
|
const unmaskedData = semanticScrubber.unmask(synthesizedData, tokenMap);
|
|
1774
1711
|
if (isStream) {
|
|
1775
|
-
return
|
|
1712
|
+
return SseRelay.simulateHit(res, unmaskedData, { provider: provider, tier, similarity: semHit.similarity });
|
|
1776
1713
|
}
|
|
1777
1714
|
res.setHeader('X-Lemma-Cache', 'HIT-CARS');
|
|
1778
1715
|
res.setHeader('X-Lemma-Similarity', semHit.similarity.toFixed(3));
|
|
@@ -1792,7 +1729,7 @@ Adjusted Answer:`;
|
|
|
1792
1729
|
await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, 2000);
|
|
1793
1730
|
const unmaskedData = semanticScrubber.unmask(cloudHit.data, tokenMap);
|
|
1794
1731
|
if (isStream)
|
|
1795
|
-
return
|
|
1732
|
+
return SseRelay.simulateHit(res, unmaskedData, { provider: provider, tier, similarity: cloudHit.similarity || 0.95 });
|
|
1796
1733
|
res.setHeader('X-Lemma-Cache', 'HIT');
|
|
1797
1734
|
res.setHeader('X-Lemma-Similarity', (cloudHit.similarity || 0.95).toFixed(3));
|
|
1798
1735
|
res.setHeader('X-Lemma-Tier', tier);
|
|
@@ -1999,14 +1936,23 @@ Adjusted Answer:`;
|
|
|
1999
1936
|
}
|
|
2000
1937
|
throw e;
|
|
2001
1938
|
});
|
|
2002
|
-
|
|
1939
|
+
const BIND_HOST = process.env.LEMMA_BIND_HOST || '127.0.0.1';
|
|
1940
|
+
server.listen(this.port, BIND_HOST, async () => {
|
|
2003
1941
|
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
2004
1942
|
fs.writeFileSync(PORT_FILE, String(this.port));
|
|
2005
|
-
|
|
1943
|
+
// Security warning if binding to all interfaces without auth
|
|
1944
|
+
if (BIND_HOST !== '127.0.0.1' && BIND_HOST !== 'localhost') {
|
|
1945
|
+
const authEnabled = process.env.AUTH_ENABLED === 'true';
|
|
1946
|
+
if (!authEnabled) {
|
|
1947
|
+
console.warn(`\n\x1b[33m⚠️ [SECURITY] Lemma proxy is bound to ${BIND_HOST} (all interfaces) with AUTH_ENABLED=false.`);
|
|
1948
|
+
console.warn(` Set AUTH_ENABLED=true and AUTH_REQUIRE_API_KEY=true before exposing to any network.\x1b[0m\n`);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
const pro = isPro();
|
|
2006
1952
|
console.log(`\x1b[1m\x1b[35m┌──────────────────────────────────────────────────────────┐\x1b[0m`);
|
|
2007
1953
|
console.log(`\x1b[1m\x1b[35m│ 🚀 LEMMA PROXY v${VERSION} — THE UNIVERSAL AI GATEWAY │\x1b[0m`);
|
|
2008
1954
|
console.log(`\x1b[1m\x1b[35m└──────────────────────────────────────────────────────────┘\x1b[0m`);
|
|
2009
|
-
console.log(`\n📁 Project : ${this.projectName}\n🔌 Port : ${this.port}`);
|
|
1955
|
+
console.log(`\n📁 Project : ${this.projectName}\n🔌 Port : ${this.port} (bound to ${BIND_HOST})`);
|
|
2010
1956
|
if (this.dashboardPath && fs.existsSync(this.dashboardPath)) {
|
|
2011
1957
|
console.log(`📊 Dashboard UI : \x1b[36mhttp://localhost:${this.port}/dashboard/\x1b[0m`);
|
|
2012
1958
|
if (this.openBrowser) {
|
|
@@ -2045,12 +1991,19 @@ Adjusted Answer:`;
|
|
|
2045
1991
|
console.log(`\n \x1b[1m\x1b[33mlemma mcp\x1b[0m\n`);
|
|
2046
1992
|
console.log('────────────────────────────────────────────────\n');
|
|
2047
1993
|
// Auto-start the Local Clipboard Optimizer Watcher in the background
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
1994
|
+
const clipboardDisabled = process.env.LEMMA_DISABLE_CLIPBOARD === 'true';
|
|
1995
|
+
if (clipboardDisabled) {
|
|
1996
|
+
console.log(`📋 Clipboard Watcher : \x1b[90m[DISABLED via LEMMA_DISABLE_CLIPBOARD]\x1b[0m`);
|
|
2051
1997
|
}
|
|
2052
|
-
|
|
2053
|
-
console.
|
|
1998
|
+
else {
|
|
1999
|
+
console.log(`📋 Clipboard Watcher : \x1b[33m[ACTIVE]\x1b[0m — polling clipboard every 1s (disable: LEMMA_DISABLE_CLIPBOARD=true)`);
|
|
2000
|
+
try {
|
|
2001
|
+
const { startClipboardWatcher } = require('../utils/ClipboardWatcher');
|
|
2002
|
+
startClipboardWatcher(1000);
|
|
2003
|
+
}
|
|
2004
|
+
catch (err) {
|
|
2005
|
+
console.warn(`⚠️ Could not auto-initialize Clipboard Watcher: ${err.message}`);
|
|
2006
|
+
}
|
|
2054
2007
|
}
|
|
2055
2008
|
});
|
|
2056
2009
|
process.on('SIGTERM', () => server.close());
|
|
@@ -2089,7 +2042,7 @@ program
|
|
|
2089
2042
|
.version(VERSION)
|
|
2090
2043
|
.addHelpText('after', `
|
|
2091
2044
|
\x1b[1mPlan Comparison\x1b[0m
|
|
2092
|
-
\x1b[32mFree\x1b[0m ${
|
|
2045
|
+
\x1b[32mFree\x1b[0m ${FREE_TIER_LIMIT} req/mo · Exact cache · Privacy firewall · MCP tools
|
|
2093
2046
|
\x1b[35mPro\x1b[0m Unlimited · Semantic cache · Cloud sync · Autopilot · Heal
|
|
2094
2047
|
\x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m
|
|
2095
2048
|
|
|
@@ -2165,7 +2118,7 @@ program.command('start')
|
|
|
2165
2118
|
const runAutopilot = opts.autopilot || opts.stack;
|
|
2166
2119
|
if (runAutopilot) {
|
|
2167
2120
|
try {
|
|
2168
|
-
const pro =
|
|
2121
|
+
const pro = isPro();
|
|
2169
2122
|
if (!pro) {
|
|
2170
2123
|
if (opts.autopilot) {
|
|
2171
2124
|
console.log(`\n🛸 \x1b[35m[Autopilot]\x1b[0m Active background auto-healing watcher is a Lemma Pro feature.`);
|
|
@@ -2309,9 +2262,9 @@ program.command('stats')
|
|
|
2309
2262
|
console.log(`Cache Hits : ${s.hits} (${((s.hits / s.total || 0) * 100).toFixed(1)}%)`);
|
|
2310
2263
|
console.log(`Tokens Saved : ${s.totalTokensSaved.toLocaleString()}`);
|
|
2311
2264
|
console.log(`Avg Latency : ${(s.totalLatency / (s.total || 1)).toFixed(2)}ms`);
|
|
2312
|
-
console.log(`Tier : ${(
|
|
2313
|
-
if (!
|
|
2314
|
-
console.log(`Free Usage : ${usage.count}/${
|
|
2265
|
+
console.log(`Tier : ${(isPro()) ? 'PRO' : 'FREE'}`);
|
|
2266
|
+
if (!isPro())
|
|
2267
|
+
console.log(`Free Usage : ${usage.count}/${FREE_TIER_LIMIT} requests`);
|
|
2315
2268
|
console.log(`──────────────────────────────────\n`);
|
|
2316
2269
|
});
|
|
2317
2270
|
program.command('activate <key>')
|
|
@@ -2327,6 +2280,7 @@ program.command('activate <key>')
|
|
|
2327
2280
|
const res = await validateKeyRemote(key.trim());
|
|
2328
2281
|
if (res.valid) {
|
|
2329
2282
|
await writeJson(LICENSE_FILE, { key, isPro: true, activatedAt: new Date().toISOString(), validatedRemotely: true });
|
|
2283
|
+
gatekeeper.reload();
|
|
2330
2284
|
console.log('✅ License activated! Enjoy Pro features (Server-verified).');
|
|
2331
2285
|
}
|
|
2332
2286
|
else {
|
|
@@ -2567,7 +2521,7 @@ program.command('heal')
|
|
|
2567
2521
|
.option('--apply', 'Apply the auto-generated code fix automatically', false)
|
|
2568
2522
|
.action(async (opts) => {
|
|
2569
2523
|
try {
|
|
2570
|
-
const pro =
|
|
2524
|
+
const pro = isPro();
|
|
2571
2525
|
if (!pro) {
|
|
2572
2526
|
console.log(`\n🩺 \x1b[35m[Heal]\x1b[0m Guided code diagnostics and auto-healing is a Lemma Pro feature.`);
|
|
2573
2527
|
console.log(`💡 Upgrade to Pro in your Dashboard or visit: \x1b[4mhttps://lemma.nxus.studio/upgrade\x1b[0m\n`);
|
|
@@ -2596,7 +2550,7 @@ program.command('autopilot')
|
|
|
2596
2550
|
.option('--dir <directory>', 'Directory to monitor for changes', 'src')
|
|
2597
2551
|
.action(async (opts) => {
|
|
2598
2552
|
try {
|
|
2599
|
-
const pro =
|
|
2553
|
+
const pro = isPro();
|
|
2600
2554
|
if (!pro) {
|
|
2601
2555
|
console.log(`\n🛸 \x1b[35m[Autopilot]\x1b[0m Active background auto-healing watcher is a Lemma Pro feature.`);
|
|
2602
2556
|
console.log(`💡 Upgrade to Pro in your Dashboard or visit: \x1b[4mhttps://lemma.nxus.studio/upgrade\x1b[0m\n`);
|