@nxuss/lemma 0.8.1 → 0.9.0
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 +144 -236
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +57 -119
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +868 -0
- package/dist/cjs/mcp/tools.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/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/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +29 -91
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +868 -0
- package/dist/esm/mcp/tools.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/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/package.json +1 -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
|
@@ -1,64 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Lemma SseRelay
|
|
4
|
-
*
|
|
5
|
-
* Handles Server-Sent Events (SSE) streaming in two modes:
|
|
6
|
-
*
|
|
7
|
-
* MISS mode: Pipes upstream SSE response from OpenAI/Anthropic to the client
|
|
8
|
-
* in real-time (transparent relay).
|
|
9
|
-
*
|
|
10
|
-
* HIT mode: Simulates an SSE stream from a cached response by chunking the
|
|
11
|
-
* text into ~5-word pieces and emitting them with a small delay.
|
|
12
|
-
* Visually identical to a real stream for IDEs like Cursor.
|
|
13
|
-
*
|
|
14
|
-
* Both modes:
|
|
15
|
-
* - Set correct SSE headers (text/event-stream, no-cache)
|
|
16
|
-
* - Inject X-Lemma-Cache, X-Lemma-Similarity, X-Lemma-Tier headers
|
|
17
|
-
* - Append the `data: [DONE]` sentinel
|
|
18
|
-
* - Support both OpenAI and Anthropic SSE envelope formats
|
|
19
|
-
*/
|
|
20
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
-
};
|
|
23
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
3
|
exports.SseRelay = void 0;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
28
|
-
const CHUNK_WORDS = 5; // words per simulated chunk
|
|
29
|
-
const CHUNK_DELAY_MS = 15; // ms between simulated chunks
|
|
30
|
-
// ─── SseRelay ─────────────────────────────────────────────────────────────────
|
|
4
|
+
const CHUNK_SIZE = 30;
|
|
5
|
+
const CHUNK_DELAY_MS = 15;
|
|
31
6
|
class SseRelay {
|
|
32
|
-
/**
|
|
33
|
-
* Simulate an SSE stream from a cached response.
|
|
34
|
-
*/
|
|
35
7
|
static async simulateHit(res, cachedResponse, opts) {
|
|
36
8
|
SseRelay.setSseHeaders(res, opts);
|
|
37
9
|
const text = SseRelay.extractTextFromResponse(cachedResponse, opts.provider);
|
|
38
|
-
const model = cachedResponse?.model || (opts.provider
|
|
39
|
-
const id = `lemma
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const sseData = SseRelay.buildChunkEvent(chunk, id, model, opts.provider, false);
|
|
45
|
-
res.write(`data: ${JSON.stringify(sseData)}\n\n`);
|
|
10
|
+
const model = cachedResponse?.model || SseRelay.defaultModel(opts.provider);
|
|
11
|
+
const id = `lemma-${Date.now()}`;
|
|
12
|
+
for (let i = 0; i < text.length; i += CHUNK_SIZE) {
|
|
13
|
+
const chunk = text.slice(i, i + CHUNK_SIZE);
|
|
14
|
+
const evt = SseRelay.buildChunkEvent(chunk, id, model, opts.provider, false);
|
|
15
|
+
res.write(SseRelay.formatSSE(evt, opts.provider));
|
|
46
16
|
await sleep(CHUNK_DELAY_MS);
|
|
47
17
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
18
|
+
const stopEvt = SseRelay.buildChunkEvent('', id, model, opts.provider, true);
|
|
19
|
+
res.write(SseRelay.formatSSE(stopEvt, opts.provider));
|
|
20
|
+
if (opts.provider !== 'gemini') {
|
|
21
|
+
res.write('data: [DONE]\n\n');
|
|
22
|
+
}
|
|
52
23
|
res.end();
|
|
53
24
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Relay a live upstream SSE stream transparently.
|
|
56
|
-
*/
|
|
57
25
|
static async relayMiss(req, res, upstreamBody, provider, opts) {
|
|
58
26
|
SseRelay.setSseHeaders(res, opts);
|
|
59
27
|
const apiKey = provider === 'openai'
|
|
60
28
|
? process.env.OPENAI_API_KEY
|
|
61
|
-
:
|
|
29
|
+
: provider === 'anthropic'
|
|
30
|
+
? process.env.ANTHROPIC_API_KEY
|
|
31
|
+
: process.env.GEMINI_API_KEY;
|
|
62
32
|
if (!apiKey) {
|
|
63
33
|
res.write(`data: ${JSON.stringify({ error: `${provider.toUpperCase()}_API_KEY not set` })}\n\n`);
|
|
64
34
|
res.write('data: [DONE]\n\n');
|
|
@@ -68,7 +38,7 @@ class SseRelay {
|
|
|
68
38
|
const { hostname, path, headers } = SseRelay.buildUpstreamRequest(provider, apiKey);
|
|
69
39
|
const body = JSON.stringify({ ...upstreamBody, stream: true });
|
|
70
40
|
await new Promise((resolve, reject) => {
|
|
71
|
-
const lib = hostname.startsWith('localhost') ?
|
|
41
|
+
const lib = hostname.startsWith('localhost') ? require('http') : require('https');
|
|
72
42
|
const reqOpts = {
|
|
73
43
|
hostname,
|
|
74
44
|
port: hostname.startsWith('localhost') ? 80 : 443,
|
|
@@ -101,19 +71,25 @@ class SseRelay {
|
|
|
101
71
|
upstreamReq.end();
|
|
102
72
|
});
|
|
103
73
|
}
|
|
104
|
-
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
105
74
|
static setSseHeaders(res, opts) {
|
|
106
75
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
107
76
|
res.setHeader('Cache-Control', 'no-cache');
|
|
108
77
|
res.setHeader('Connection', 'keep-alive');
|
|
109
78
|
res.setHeader('X-Accel-Buffering', 'no');
|
|
110
|
-
res.setHeader('X-Lemma-Cache', 'HIT');
|
|
79
|
+
res.setHeader('X-Lemma-Cache', opts.cacheHeader || 'HIT');
|
|
111
80
|
res.setHeader('X-Lemma-Similarity', (opts.similarity ?? 1.0).toFixed(3));
|
|
112
81
|
res.setHeader('X-Lemma-Tier', opts.tier);
|
|
113
82
|
if (opts.latencyMs !== undefined) {
|
|
114
83
|
res.setHeader('X-Lemma-Latency', `${opts.latencyMs}ms`);
|
|
115
84
|
}
|
|
116
85
|
}
|
|
86
|
+
static defaultModel(provider) {
|
|
87
|
+
if (provider === 'openai')
|
|
88
|
+
return 'gpt-4';
|
|
89
|
+
if (provider === 'anthropic')
|
|
90
|
+
return 'claude-3-5-sonnet-20241022';
|
|
91
|
+
return 'gemini-1.5-pro';
|
|
92
|
+
}
|
|
117
93
|
static buildUpstreamRequest(provider, apiKey) {
|
|
118
94
|
if (provider === 'openai') {
|
|
119
95
|
return {
|
|
@@ -122,10 +98,17 @@ class SseRelay {
|
|
|
122
98
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
123
99
|
};
|
|
124
100
|
}
|
|
101
|
+
if (provider === 'anthropic') {
|
|
102
|
+
return {
|
|
103
|
+
hostname: 'api.anthropic.com',
|
|
104
|
+
path: '/v1/messages',
|
|
105
|
+
headers: { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
125
108
|
return {
|
|
126
|
-
hostname: '
|
|
127
|
-
path:
|
|
128
|
-
headers: {
|
|
109
|
+
hostname: 'generativelanguage.googleapis.com',
|
|
110
|
+
path: `/v1beta/models/${apiKey}`,
|
|
111
|
+
headers: {},
|
|
129
112
|
};
|
|
130
113
|
}
|
|
131
114
|
static buildChunkEvent(text, id, model, provider, isStop) {
|
|
@@ -142,27 +125,41 @@ class SseRelay {
|
|
|
142
125
|
}],
|
|
143
126
|
};
|
|
144
127
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
128
|
+
if (provider === 'anthropic') {
|
|
129
|
+
if (isStop)
|
|
130
|
+
return { type: 'message_stop' };
|
|
131
|
+
return {
|
|
132
|
+
type: 'content_block_delta',
|
|
133
|
+
index: 0,
|
|
134
|
+
delta: { type: 'text_delta', text },
|
|
135
|
+
};
|
|
148
136
|
}
|
|
149
137
|
return {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
candidates: [{
|
|
139
|
+
content: { parts: [{ text }] },
|
|
140
|
+
finishReason: isStop ? 'STOP' : undefined,
|
|
141
|
+
}],
|
|
153
142
|
};
|
|
154
143
|
}
|
|
144
|
+
static formatSSE(data, provider) {
|
|
145
|
+
if (provider === 'gemini') {
|
|
146
|
+
return `${JSON.stringify(data)}\n`;
|
|
147
|
+
}
|
|
148
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
149
|
+
}
|
|
155
150
|
static extractTextFromResponse(response, provider) {
|
|
156
151
|
try {
|
|
157
152
|
if (provider === 'openai') {
|
|
158
153
|
return response?.choices?.[0]?.message?.content || '';
|
|
159
154
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
if (provider === 'anthropic') {
|
|
156
|
+
const content = response?.content;
|
|
157
|
+
if (Array.isArray(content)) {
|
|
158
|
+
return content.map((c) => c.text || '').join('');
|
|
159
|
+
}
|
|
160
|
+
return String(content || '');
|
|
164
161
|
}
|
|
165
|
-
return
|
|
162
|
+
return response?.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
166
163
|
}
|
|
167
164
|
catch {
|
|
168
165
|
return '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SseRelay.js","sourceRoot":"","sources":["../../../src/proxy/SseRelay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SseRelay.js","sourceRoot":"","sources":["../../../src/proxy/SseRelay.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AAsbA,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4DvF;AAmgED,wBAAsB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6KhL"}
|
|
@@ -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);
|
|
@@ -2011,7 +1948,7 @@ Adjusted Answer:`;
|
|
|
2011
1948
|
console.warn(` Set AUTH_ENABLED=true and AUTH_REQUIRE_API_KEY=true before exposing to any network.\x1b[0m\n`);
|
|
2012
1949
|
}
|
|
2013
1950
|
}
|
|
2014
|
-
const pro =
|
|
1951
|
+
const pro = isPro();
|
|
2015
1952
|
console.log(`\x1b[1m\x1b[35m┌──────────────────────────────────────────────────────────┐\x1b[0m`);
|
|
2016
1953
|
console.log(`\x1b[1m\x1b[35m│ 🚀 LEMMA PROXY v${VERSION} — THE UNIVERSAL AI GATEWAY │\x1b[0m`);
|
|
2017
1954
|
console.log(`\x1b[1m\x1b[35m└──────────────────────────────────────────────────────────┘\x1b[0m`);
|
|
@@ -2105,7 +2042,7 @@ program
|
|
|
2105
2042
|
.version(VERSION)
|
|
2106
2043
|
.addHelpText('after', `
|
|
2107
2044
|
\x1b[1mPlan Comparison\x1b[0m
|
|
2108
|
-
\x1b[32mFree\x1b[0m ${
|
|
2045
|
+
\x1b[32mFree\x1b[0m ${FREE_TIER_LIMIT} req/mo · Exact cache · Privacy firewall · MCP tools
|
|
2109
2046
|
\x1b[35mPro\x1b[0m Unlimited · Semantic cache · Cloud sync · Autopilot · Heal
|
|
2110
2047
|
\x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m
|
|
2111
2048
|
|
|
@@ -2181,7 +2118,7 @@ program.command('start')
|
|
|
2181
2118
|
const runAutopilot = opts.autopilot || opts.stack;
|
|
2182
2119
|
if (runAutopilot) {
|
|
2183
2120
|
try {
|
|
2184
|
-
const pro =
|
|
2121
|
+
const pro = isPro();
|
|
2185
2122
|
if (!pro) {
|
|
2186
2123
|
if (opts.autopilot) {
|
|
2187
2124
|
console.log(`\n🛸 \x1b[35m[Autopilot]\x1b[0m Active background auto-healing watcher is a Lemma Pro feature.`);
|
|
@@ -2325,9 +2262,9 @@ program.command('stats')
|
|
|
2325
2262
|
console.log(`Cache Hits : ${s.hits} (${((s.hits / s.total || 0) * 100).toFixed(1)}%)`);
|
|
2326
2263
|
console.log(`Tokens Saved : ${s.totalTokensSaved.toLocaleString()}`);
|
|
2327
2264
|
console.log(`Avg Latency : ${(s.totalLatency / (s.total || 1)).toFixed(2)}ms`);
|
|
2328
|
-
console.log(`Tier : ${(
|
|
2329
|
-
if (!
|
|
2330
|
-
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`);
|
|
2331
2268
|
console.log(`──────────────────────────────────\n`);
|
|
2332
2269
|
});
|
|
2333
2270
|
program.command('activate <key>')
|
|
@@ -2343,6 +2280,7 @@ program.command('activate <key>')
|
|
|
2343
2280
|
const res = await validateKeyRemote(key.trim());
|
|
2344
2281
|
if (res.valid) {
|
|
2345
2282
|
await writeJson(LICENSE_FILE, { key, isPro: true, activatedAt: new Date().toISOString(), validatedRemotely: true });
|
|
2283
|
+
gatekeeper.reload();
|
|
2346
2284
|
console.log('✅ License activated! Enjoy Pro features (Server-verified).');
|
|
2347
2285
|
}
|
|
2348
2286
|
else {
|
|
@@ -2583,7 +2521,7 @@ program.command('heal')
|
|
|
2583
2521
|
.option('--apply', 'Apply the auto-generated code fix automatically', false)
|
|
2584
2522
|
.action(async (opts) => {
|
|
2585
2523
|
try {
|
|
2586
|
-
const pro =
|
|
2524
|
+
const pro = isPro();
|
|
2587
2525
|
if (!pro) {
|
|
2588
2526
|
console.log(`\n🩺 \x1b[35m[Heal]\x1b[0m Guided code diagnostics and auto-healing is a Lemma Pro feature.`);
|
|
2589
2527
|
console.log(`💡 Upgrade to Pro in your Dashboard or visit: \x1b[4mhttps://lemma.nxus.studio/upgrade\x1b[0m\n`);
|
|
@@ -2612,7 +2550,7 @@ program.command('autopilot')
|
|
|
2612
2550
|
.option('--dir <directory>', 'Directory to monitor for changes', 'src')
|
|
2613
2551
|
.action(async (opts) => {
|
|
2614
2552
|
try {
|
|
2615
|
-
const pro =
|
|
2553
|
+
const pro = isPro();
|
|
2616
2554
|
if (!pro) {
|
|
2617
2555
|
console.log(`\n🛸 \x1b[35m[Autopilot]\x1b[0m Active background auto-healing watcher is a Lemma Pro feature.`);
|
|
2618
2556
|
console.log(`💡 Upgrade to Pro in your Dashboard or visit: \x1b[4mhttps://lemma.nxus.studio/upgrade\x1b[0m\n`);
|