@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.
Files changed (58) hide show
  1. package/README.md +144 -236
  2. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  3. package/dist/cjs/cli/lemma-proxy.js +57 -119
  4. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  5. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  6. package/dist/cjs/mcp/tools.js +868 -0
  7. package/dist/cjs/mcp/tools.js.map +1 -1
  8. package/dist/cjs/observability/IdeContextSync.js +3 -3
  9. package/dist/cjs/observability/IdeContextSync.js.map +1 -1
  10. package/dist/cjs/proxy/ProjectStore.d.ts +2 -0
  11. package/dist/cjs/proxy/ProjectStore.d.ts.map +1 -1
  12. package/dist/cjs/proxy/ProjectStore.js +3 -1
  13. package/dist/cjs/proxy/ProjectStore.js.map +1 -1
  14. package/dist/cjs/proxy/SseRelay.d.ts +4 -25
  15. package/dist/cjs/proxy/SseRelay.d.ts.map +1 -1
  16. package/dist/cjs/proxy/SseRelay.js +60 -63
  17. package/dist/cjs/proxy/SseRelay.js.map +1 -1
  18. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  19. package/dist/esm/cli/lemma-proxy.js +29 -91
  20. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  21. package/dist/esm/mcp/tools.d.ts.map +1 -1
  22. package/dist/esm/mcp/tools.js +868 -0
  23. package/dist/esm/mcp/tools.js.map +1 -1
  24. package/dist/esm/observability/IdeContextSync.js +1 -1
  25. package/dist/esm/observability/IdeContextSync.js.map +1 -1
  26. package/dist/esm/proxy/ProjectStore.d.ts +2 -0
  27. package/dist/esm/proxy/ProjectStore.d.ts.map +1 -1
  28. package/dist/esm/proxy/ProjectStore.js +2 -0
  29. package/dist/esm/proxy/ProjectStore.js.map +1 -1
  30. package/dist/esm/proxy/SseRelay.d.ts +4 -25
  31. package/dist/esm/proxy/SseRelay.d.ts.map +1 -1
  32. package/dist/esm/proxy/SseRelay.js +60 -60
  33. package/dist/esm/proxy/SseRelay.js.map +1 -1
  34. package/package.json +1 -1
  35. package/dist/cjs/protocol/iap.d.ts +0 -54
  36. package/dist/cjs/protocol/iap.d.ts.map +0 -1
  37. package/dist/cjs/protocol/iap.js +0 -108
  38. package/dist/cjs/protocol/iap.js.map +0 -1
  39. package/dist/cjs/subconscious/cache.d.ts +0 -34
  40. package/dist/cjs/subconscious/cache.d.ts.map +0 -1
  41. package/dist/cjs/subconscious/cache.js +0 -156
  42. package/dist/cjs/subconscious/cache.js.map +0 -1
  43. package/dist/cjs/subconscious/embeddings.d.ts +0 -25
  44. package/dist/cjs/subconscious/embeddings.d.ts.map +0 -1
  45. package/dist/cjs/subconscious/embeddings.js +0 -65
  46. package/dist/cjs/subconscious/embeddings.js.map +0 -1
  47. package/dist/esm/protocol/iap.d.ts +0 -54
  48. package/dist/esm/protocol/iap.d.ts.map +0 -1
  49. package/dist/esm/protocol/iap.js +0 -104
  50. package/dist/esm/protocol/iap.js.map +0 -1
  51. package/dist/esm/subconscious/cache.d.ts +0 -34
  52. package/dist/esm/subconscious/cache.d.ts.map +0 -1
  53. package/dist/esm/subconscious/cache.js +0 -152
  54. package/dist/esm/subconscious/cache.js.map +0 -1
  55. package/dist/esm/subconscious/embeddings.d.ts +0 -25
  56. package/dist/esm/subconscious/embeddings.d.ts.map +0 -1
  57. package/dist/esm/subconscious/embeddings.js +0 -58
  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 http_1 = __importDefault(require("http"));
26
- const https_1 = __importDefault(require("https"));
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 === 'openai' ? 'gpt-4' : 'claude-3-5-sonnet-20241022');
39
- const id = `lemma-hit-${Date.now()}`;
40
- const words = text.split(' ');
41
- // Emit chunks
42
- for (let i = 0; i < words.length; i += CHUNK_WORDS) {
43
- const chunk = words.slice(i, i + CHUNK_WORDS).join(' ');
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
- // Emit final chunk (finish_reason: stop)
49
- const stopEvent = SseRelay.buildChunkEvent('', id, model, opts.provider, true);
50
- res.write(`data: ${JSON.stringify(stopEvent)}\n\n`);
51
- res.write('data: [DONE]\n\n');
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
- : process.env.ANTHROPIC_API_KEY;
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') ? http_1.default : https_1.default;
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: 'api.anthropic.com',
127
- path: '/v1/messages',
128
- headers: { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
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
- // Anthropic format
146
- if (isStop) {
147
- return { type: 'message_stop' };
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
- type: 'content_block_delta',
151
- index: 0,
152
- delta: { type: 'text_delta', text },
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
- // Anthropic
161
- const content = response?.content;
162
- if (Array.isArray(content)) {
163
- return content.map((c) => c.text || '').join('');
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 String(content || '');
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":"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"}
1
+ {"version":3,"file":"SseRelay.js","sourceRoot":"","sources":["../../../src/proxy/SseRelay.ts"],"names":[],"mappings":";;;AAEA,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,cAAc,GAAG,EAAE,CAAC;AAY1B,MAAa,QAAQ;IACnB,MAAM,CAAC,KAAK,CAAC,WAAW,CACtB,GAAa,EACb,cAAmB,EACnB,IAAqB;QAErB,QAAQ,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,IAAI,GAAG,QAAQ,CAAC,uBAAuB,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7E,MAAM,KAAK,GAAG,cAAc,EAAE,KAAK,IAAI,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5E,MAAM,EAAE,GAAG,SAAS,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC;YAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC7E,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClD,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7E,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtD,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/B,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAChC,CAAC;QACD,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,SAAS,CACpB,GAAY,EACZ,GAAa,EACb,YAAiB,EACjB,QAAkB,EAClB,IAAqB;QAErB,QAAQ,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,QAAQ,KAAK,QAAQ;YAClC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;YAC5B,CAAC,CAAC,QAAQ,KAAK,WAAW;gBACxB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB;gBAC/B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAEjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,WAAW,EAAE,kBAAkB,EAAE,CAAC,MAAM,CAAC,CAAC;YACjG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAC9B,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAE/D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAClF,MAAM,OAAO,GAAG;gBACd,QAAQ;gBACR,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;gBACjD,IAAI;gBACJ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,GAAG,OAAO;oBACV,cAAc,EAAE,kBAAkB;oBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;iBAC1C;aACF,CAAC;YAEF,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,WAAgB,EAAE,EAAE;gBAC5D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBACvC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnB,CAAC,CAAC,CAAC;gBACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACzB,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;oBAC9B,GAAG,CAAC,GAAG,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;gBACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;YAEH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBACrC,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;gBACjE,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBAC9B,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,WAAW,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,aAAa,CAAC,GAAa,EAAE,IAAqB;QAC/D,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;QACnD,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAC3C,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC1C,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;QACzC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;QAC1D,GAAG,CAAC,SAAS,CAAC,oBAAoB,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACzE,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,YAAY,CAAC,QAAkB;QAC5C,IAAI,QAAQ,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;QAC1C,IAAI,QAAQ,KAAK,WAAW;YAAE,OAAO,4BAA4B,CAAC;QAClE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAEO,MAAM,CAAC,oBAAoB,CAAC,QAAkB,EAAE,MAAc;QAKpE,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO;gBACL,QAAQ,EAAE,gBAAgB;gBAC1B,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;aAC/C,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YAC7B,OAAO;gBACL,QAAQ,EAAE,mBAAmB;gBAC7B,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE;aACpE,CAAC;QACJ,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,mCAAmC;YAC7C,IAAI,EAAE,kBAAkB,MAAM,EAAE;YAChC,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,eAAe,CAC5B,IAAY,EACZ,EAAU,EACV,KAAa,EACb,QAAkB,EAClB,MAAe;QAEf,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO;gBACL,EAAE;gBACF,MAAM,EAAE,uBAAuB;gBAC/B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;gBACtC,KAAK;gBACL,OAAO,EAAE,CAAC;wBACR,KAAK,EAAE,CAAC;wBACR,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;wBACtC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;qBACtC,CAAC;aACH,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YAC7B,IAAI,MAAM;gBAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;YAC5C,OAAO;gBACL,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;aACpC,CAAC;QACJ,CAAC;QACD,OAAO;YACL,UAAU,EAAE,CAAC;oBACX,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;oBAC9B,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;iBAC1C,CAAC;SACH,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,SAAS,CAAC,IAAY,EAAE,QAAkB;QACvD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,CAAC;QACD,OAAO,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;IAC7C,CAAC;IAEO,MAAM,CAAC,uBAAuB,CAAC,QAAa,EAAE,QAAkB;QACtE,IAAI,CAAC;YACH,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;YACxD,CAAC;YACD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,CAAC;gBAClC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxD,CAAC;gBACD,OAAO,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAnMD,4BAmMC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AA0bA,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4DvF;AA2jED,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"}
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
- let isProCached = null;
508
- let lastLicenseCheck = 0;
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 (!await isPro())
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 (!await isPro())
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 pro = await isPro();
998
- res.json({ status: 'ok', project: this.projectName, mode: pro ? 'pro' : 'standard', tier: pro ? 'pro' : 'free', port: this.port });
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: (await isPro()) ? 'pro' : 'free'
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 = await isPro();
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 >= FREE_LIMIT) {
1691
- return res.status(429).json({ error: { message: `Free tier limit (${FREE_LIMIT}/mo). Upgrade: https://lemma.nxus.studio/upgrade` } });
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 simulateHitStream(res, unmaskedData, provider, originalModel, tier, 1.0);
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 simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity, cacheHeader);
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 simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity);
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 simulateHitStream(res, unmaskedData, provider, originalModel, tier, cloudHit.similarity || 0.95);
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 = await isPro();
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 ${FREE_LIMIT} req/mo · Exact cache · Privacy firewall · MCP tools
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 = await isPro();
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 : ${(await isPro()) ? 'PRO' : 'FREE'}`);
2329
- if (!await isPro())
2330
- console.log(`Free Usage : ${usage.count}/${FREE_LIMIT} requests`);
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 = await isPro();
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 = await isPro();
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`);