@nxuss/lemma 0.2.1 → 0.3.1

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 (129) hide show
  1. package/README.md +50 -5
  2. package/dist/cjs/api/dashboardRoutes.d.ts +28 -0
  3. package/dist/cjs/api/dashboardRoutes.d.ts.map +1 -0
  4. package/dist/cjs/api/dashboardRoutes.js +239 -0
  5. package/dist/cjs/api/dashboardRoutes.js.map +1 -0
  6. package/dist/cjs/api/server.d.ts +51 -0
  7. package/dist/cjs/api/server.d.ts.map +1 -0
  8. package/dist/cjs/api/server.js +166 -0
  9. package/dist/cjs/api/server.js.map +1 -0
  10. package/dist/cjs/core/AgentRegistry.d.ts +4 -0
  11. package/dist/cjs/core/AgentRegistry.d.ts.map +1 -1
  12. package/dist/cjs/core/AgentRegistry.js +6 -0
  13. package/dist/cjs/core/AgentRegistry.js.map +1 -1
  14. package/dist/cjs/core/DashboardWebSocketServer.d.ts +70 -0
  15. package/dist/cjs/core/DashboardWebSocketServer.d.ts.map +1 -0
  16. package/dist/cjs/core/DashboardWebSocketServer.js +211 -0
  17. package/dist/cjs/core/DashboardWebSocketServer.js.map +1 -0
  18. package/dist/cjs/core/GhostAlchemist.d.ts +18 -0
  19. package/dist/cjs/core/GhostAlchemist.d.ts.map +1 -0
  20. package/dist/cjs/core/GhostAlchemist.js +55 -0
  21. package/dist/cjs/core/GhostAlchemist.js.map +1 -0
  22. package/dist/cjs/core/GhostListener.d.ts +20 -0
  23. package/dist/cjs/core/GhostListener.d.ts.map +1 -0
  24. package/dist/cjs/core/GhostListener.js +97 -0
  25. package/dist/cjs/core/GhostListener.js.map +1 -0
  26. package/dist/cjs/core/OrchestrationEngine.d.ts +22 -0
  27. package/dist/cjs/core/OrchestrationEngine.d.ts.map +1 -1
  28. package/dist/cjs/core/OrchestrationEngine.js +98 -0
  29. package/dist/cjs/core/OrchestrationEngine.js.map +1 -1
  30. package/dist/cjs/core/SubconsciousEngine.d.ts +24 -0
  31. package/dist/cjs/core/SubconsciousEngine.d.ts.map +1 -1
  32. package/dist/cjs/core/SubconsciousEngine.js +110 -0
  33. package/dist/cjs/core/SubconsciousEngine.js.map +1 -1
  34. package/dist/cjs/core/WebSocketServer.d.ts.map +1 -1
  35. package/dist/cjs/core/WebSocketServer.js +4 -0
  36. package/dist/cjs/core/WebSocketServer.js.map +1 -1
  37. package/dist/cjs/db/database.d.ts +60 -0
  38. package/dist/cjs/db/database.d.ts.map +1 -0
  39. package/dist/cjs/db/database.js +297 -0
  40. package/dist/cjs/db/database.js.map +1 -0
  41. package/dist/cjs/db/migrations/001-dashboard-schema.d.ts +18 -0
  42. package/dist/cjs/db/migrations/001-dashboard-schema.d.ts.map +1 -0
  43. package/dist/cjs/db/migrations/001-dashboard-schema.js +175 -0
  44. package/dist/cjs/db/migrations/001-dashboard-schema.js.map +1 -0
  45. package/dist/cjs/embed/index.d.ts +1 -1
  46. package/dist/cjs/embed/index.d.ts.map +1 -1
  47. package/dist/cjs/embed/index.js +17 -9
  48. package/dist/cjs/embed/index.js.map +1 -1
  49. package/dist/cjs/proxy/Gatekeeper.d.ts +43 -0
  50. package/dist/cjs/proxy/Gatekeeper.d.ts.map +1 -0
  51. package/dist/cjs/proxy/Gatekeeper.js +202 -0
  52. package/dist/cjs/proxy/Gatekeeper.js.map +1 -0
  53. package/dist/cjs/proxy/KeyManager.d.ts +56 -0
  54. package/dist/cjs/proxy/KeyManager.d.ts.map +1 -0
  55. package/dist/cjs/proxy/KeyManager.js +220 -0
  56. package/dist/cjs/proxy/KeyManager.js.map +1 -0
  57. package/dist/cjs/proxy/ProjectStore.d.ts +84 -0
  58. package/dist/cjs/proxy/ProjectStore.d.ts.map +1 -0
  59. package/dist/cjs/proxy/ProjectStore.js +214 -0
  60. package/dist/cjs/proxy/ProjectStore.js.map +1 -0
  61. package/dist/cjs/proxy/SseRelay.d.ts +41 -0
  62. package/dist/cjs/proxy/SseRelay.d.ts.map +1 -0
  63. package/dist/cjs/proxy/SseRelay.js +176 -0
  64. package/dist/cjs/proxy/SseRelay.js.map +1 -0
  65. package/dist/esm/api/dashboardRoutes.d.ts +28 -0
  66. package/dist/esm/api/dashboardRoutes.d.ts.map +1 -0
  67. package/dist/esm/api/dashboardRoutes.js +236 -0
  68. package/dist/esm/api/dashboardRoutes.js.map +1 -0
  69. package/dist/esm/api/server.d.ts +51 -0
  70. package/dist/esm/api/server.d.ts.map +1 -0
  71. package/dist/esm/api/server.js +159 -0
  72. package/dist/esm/api/server.js.map +1 -0
  73. package/dist/esm/core/AgentRegistry.d.ts +4 -0
  74. package/dist/esm/core/AgentRegistry.d.ts.map +1 -1
  75. package/dist/esm/core/AgentRegistry.js +6 -0
  76. package/dist/esm/core/AgentRegistry.js.map +1 -1
  77. package/dist/esm/core/DashboardWebSocketServer.d.ts +70 -0
  78. package/dist/esm/core/DashboardWebSocketServer.d.ts.map +1 -0
  79. package/dist/esm/core/DashboardWebSocketServer.js +207 -0
  80. package/dist/esm/core/DashboardWebSocketServer.js.map +1 -0
  81. package/dist/esm/core/GhostAlchemist.d.ts +18 -0
  82. package/dist/esm/core/GhostAlchemist.d.ts.map +1 -0
  83. package/dist/esm/core/GhostAlchemist.js +48 -0
  84. package/dist/esm/core/GhostAlchemist.js.map +1 -0
  85. package/dist/esm/core/GhostListener.d.ts +20 -0
  86. package/dist/esm/core/GhostListener.d.ts.map +1 -0
  87. package/dist/esm/core/GhostListener.js +93 -0
  88. package/dist/esm/core/GhostListener.js.map +1 -0
  89. package/dist/esm/core/OrchestrationEngine.d.ts +22 -0
  90. package/dist/esm/core/OrchestrationEngine.d.ts.map +1 -1
  91. package/dist/esm/core/OrchestrationEngine.js +98 -0
  92. package/dist/esm/core/OrchestrationEngine.js.map +1 -1
  93. package/dist/esm/core/SubconsciousEngine.d.ts +24 -0
  94. package/dist/esm/core/SubconsciousEngine.d.ts.map +1 -1
  95. package/dist/esm/core/SubconsciousEngine.js +110 -0
  96. package/dist/esm/core/SubconsciousEngine.js.map +1 -1
  97. package/dist/esm/core/WebSocketServer.d.ts.map +1 -1
  98. package/dist/esm/core/WebSocketServer.js +4 -0
  99. package/dist/esm/core/WebSocketServer.js.map +1 -1
  100. package/dist/esm/db/database.d.ts +60 -0
  101. package/dist/esm/db/database.d.ts.map +1 -0
  102. package/dist/esm/db/database.js +293 -0
  103. package/dist/esm/db/database.js.map +1 -0
  104. package/dist/esm/db/migrations/001-dashboard-schema.d.ts +18 -0
  105. package/dist/esm/db/migrations/001-dashboard-schema.d.ts.map +1 -0
  106. package/dist/esm/db/migrations/001-dashboard-schema.js +172 -0
  107. package/dist/esm/db/migrations/001-dashboard-schema.js.map +1 -0
  108. package/dist/esm/embed/index.d.ts +1 -1
  109. package/dist/esm/embed/index.d.ts.map +1 -1
  110. package/dist/esm/embed/index.js +17 -9
  111. package/dist/esm/embed/index.js.map +1 -1
  112. package/dist/esm/proxy/Gatekeeper.d.ts +43 -0
  113. package/dist/esm/proxy/Gatekeeper.d.ts.map +1 -0
  114. package/dist/esm/proxy/Gatekeeper.js +165 -0
  115. package/dist/esm/proxy/Gatekeeper.js.map +1 -0
  116. package/dist/esm/proxy/KeyManager.d.ts +56 -0
  117. package/dist/esm/proxy/KeyManager.d.ts.map +1 -0
  118. package/dist/esm/proxy/KeyManager.js +213 -0
  119. package/dist/esm/proxy/KeyManager.js.map +1 -0
  120. package/dist/esm/proxy/ProjectStore.d.ts +84 -0
  121. package/dist/esm/proxy/ProjectStore.d.ts.map +1 -0
  122. package/dist/esm/proxy/ProjectStore.js +207 -0
  123. package/dist/esm/proxy/ProjectStore.js.map +1 -0
  124. package/dist/esm/proxy/SseRelay.d.ts +41 -0
  125. package/dist/esm/proxy/SseRelay.d.ts.map +1 -0
  126. package/dist/esm/proxy/SseRelay.js +169 -0
  127. package/dist/esm/proxy/SseRelay.js.map +1 -0
  128. package/lemma-proxy.cjs +477 -0
  129. package/package.json +20 -4
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * Lemma KeyManager
4
+ *
5
+ * Handles Pro license key lifecycle:
6
+ * - Activation via remote validation (https://lemma.nxus.studio/api/v1/validate)
7
+ * - Graceful fallback to local format check when endpoint is unreachable
8
+ * - Persistence to ~/.lemma-cache/license.json
9
+ * - Auto-migration from legacy license.key format
10
+ *
11
+ * TODO(v0.4.0): Replace HTTP validate call with direct PostgreSQL check.
12
+ */
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.KeyManager = void 0;
18
+ const fs_1 = __importDefault(require("fs"));
19
+ const path_1 = __importDefault(require("path"));
20
+ const https_1 = __importDefault(require("https"));
21
+ // ─── Constants ────────────────────────────────────────────────────────────────
22
+ const CACHE_DIR = path_1.default.join(process.env.HOME || process.env.USERPROFILE || '~', '.lemma-cache');
23
+ const LICENSE_FILE = path_1.default.join(CACHE_DIR, 'license.json');
24
+ const LEGACY_LICENSE_FILE = path_1.default.join(CACHE_DIR, 'license.key');
25
+ const VALIDATE_URL = 'https://lemma.nxus.studio/api/v1/validate';
26
+ const VALIDATE_TIMEOUT_MS = 5000;
27
+ const KEY_FORMAT_REGEX = /^LEMMA-PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
28
+ // ─── KeyManager ───────────────────────────────────────────────────────────────
29
+ class KeyManager {
30
+ constructor() {
31
+ this.license = null;
32
+ this.ensureCacheDir();
33
+ this.license = this.load();
34
+ }
35
+ // ── Public API ──────────────────────────────────────────────────────────────
36
+ isPro() {
37
+ if (!this.license)
38
+ return false;
39
+ if (!this.license.isPro)
40
+ return false;
41
+ // Check expiry if set
42
+ if (this.license.expiresAt) {
43
+ if (Date.now() > new Date(this.license.expiresAt).getTime()) {
44
+ return false;
45
+ }
46
+ }
47
+ return true;
48
+ }
49
+ getLicenseKey() {
50
+ return this.license?.key || null;
51
+ }
52
+ getLicense() {
53
+ return this.license;
54
+ }
55
+ /**
56
+ * Activate a license key.
57
+ * Attempts remote validation first; falls back to format-check if unreachable.
58
+ */
59
+ async activate(key) {
60
+ // Basic format validation first
61
+ if (!KEY_FORMAT_REGEX.test(key.trim())) {
62
+ return {
63
+ success: false,
64
+ isPro: false,
65
+ validatedRemotely: false,
66
+ message: `Invalid key format. Pro keys look like: LEMMA-PRO-XXXX-XXXX-XXXX`,
67
+ };
68
+ }
69
+ // Attempt remote validation
70
+ let validatedRemotely = false;
71
+ let remoteData = null;
72
+ try {
73
+ remoteData = await this.validateRemote(key.trim());
74
+ validatedRemotely = true;
75
+ if (!remoteData.valid) {
76
+ return {
77
+ success: false,
78
+ isPro: false,
79
+ validatedRemotely: true,
80
+ message: remoteData.reason || 'License key is not valid.',
81
+ };
82
+ }
83
+ }
84
+ catch (err) {
85
+ // Network/endpoint unavailable — fall back to local format check
86
+ console.warn(`[KeyManager] Remote validation unavailable: ${err.message}`);
87
+ console.warn(`[KeyManager] Falling back to local format validation.`);
88
+ }
89
+ // Build and persist license
90
+ const license = {
91
+ key: key.trim(),
92
+ tier: 'pro',
93
+ isPro: true,
94
+ activatedAt: new Date().toISOString(),
95
+ validatedRemotely,
96
+ expiresAt: remoteData?.expiresAt,
97
+ email: remoteData?.email,
98
+ };
99
+ this.license = license;
100
+ this.save(license);
101
+ return {
102
+ success: true,
103
+ isPro: true,
104
+ validatedRemotely,
105
+ message: validatedRemotely
106
+ ? `✅ Lemma Pro activated (server-verified). Semantic Mode enabled.`
107
+ : `✅ Lemma Pro activated (format-validated, server offline). Semantic Mode enabled.`,
108
+ license,
109
+ };
110
+ }
111
+ /**
112
+ * Deactivate / remove the current license.
113
+ */
114
+ deactivate() {
115
+ this.license = null;
116
+ if (fs_1.default.existsSync(LICENSE_FILE)) {
117
+ fs_1.default.unlinkSync(LICENSE_FILE);
118
+ }
119
+ }
120
+ /**
121
+ * Reload license from disk (used after activate from another process).
122
+ */
123
+ reload() {
124
+ this.license = this.load();
125
+ }
126
+ // ── Private helpers ─────────────────────────────────────────────────────────
127
+ load() {
128
+ // Try new JSON format
129
+ if (fs_1.default.existsSync(LICENSE_FILE)) {
130
+ try {
131
+ const data = JSON.parse(fs_1.default.readFileSync(LICENSE_FILE, 'utf8'));
132
+ if (data.key && data.isPro)
133
+ return data;
134
+ }
135
+ catch {
136
+ // Corrupt file — ignore
137
+ }
138
+ }
139
+ // Migrate from legacy format
140
+ if (fs_1.default.existsSync(LEGACY_LICENSE_FILE)) {
141
+ try {
142
+ const legacy = JSON.parse(fs_1.default.readFileSync(LEGACY_LICENSE_FILE, 'utf8'));
143
+ if (legacy.key && (legacy.isPro || legacy.tier === 'pro')) {
144
+ const migrated = {
145
+ key: legacy.key,
146
+ tier: 'pro',
147
+ isPro: true,
148
+ activatedAt: legacy.activatedAt || new Date().toISOString(),
149
+ validatedRemotely: false,
150
+ };
151
+ this.save(migrated);
152
+ fs_1.default.unlinkSync(LEGACY_LICENSE_FILE); // Remove legacy file
153
+ console.log('[KeyManager] Migrated license from legacy format.');
154
+ return migrated;
155
+ }
156
+ }
157
+ catch {
158
+ // Ignore
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ save(license) {
164
+ try {
165
+ this.ensureCacheDir();
166
+ fs_1.default.writeFileSync(LICENSE_FILE, JSON.stringify(license, null, 2));
167
+ }
168
+ catch (err) {
169
+ console.error(`[KeyManager] Failed to save license: ${err.message}`);
170
+ }
171
+ }
172
+ ensureCacheDir() {
173
+ if (!fs_1.default.existsSync(CACHE_DIR)) {
174
+ fs_1.default.mkdirSync(CACHE_DIR, { recursive: true });
175
+ }
176
+ }
177
+ /**
178
+ * POST to https://lemma.nxus.studio/api/v1/validate with a timeout.
179
+ * Returns the JSON response body.
180
+ */
181
+ validateRemote(key) {
182
+ return new Promise((resolve, reject) => {
183
+ const body = JSON.stringify({ key });
184
+ const url = new URL(VALIDATE_URL);
185
+ const options = {
186
+ hostname: url.hostname,
187
+ port: url.port || 443,
188
+ path: url.pathname,
189
+ method: 'POST',
190
+ headers: {
191
+ 'Content-Type': 'application/json',
192
+ 'Content-Length': Buffer.byteLength(body),
193
+ 'User-Agent': 'lemma-proxy/0.3.0',
194
+ },
195
+ };
196
+ const req = https_1.default.request(options, (res) => {
197
+ let data = '';
198
+ res.on('data', (chunk) => { data += chunk; });
199
+ res.on('end', () => {
200
+ try {
201
+ resolve(JSON.parse(data));
202
+ }
203
+ catch {
204
+ reject(new Error(`Invalid JSON response from validate endpoint`));
205
+ }
206
+ });
207
+ });
208
+ req.on('error', reject);
209
+ const timer = setTimeout(() => {
210
+ req.destroy();
211
+ reject(new Error(`Validation endpoint timed out after ${VALIDATE_TIMEOUT_MS}ms`));
212
+ }, VALIDATE_TIMEOUT_MS);
213
+ req.on('close', () => clearTimeout(timer));
214
+ req.write(body);
215
+ req.end();
216
+ });
217
+ }
218
+ }
219
+ exports.KeyManager = KeyManager;
220
+ //# sourceMappingURL=KeyManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyManager.js","sourceRoot":"","sources":["../../../src/proxy/KeyManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Lemma ProjectStore
3
+ *
4
+ * Project-aware storage resolver:
5
+ * - Detects project identity from package.json → cwd basename fallback
6
+ * - Hashes project name to a 12-char hex prefix for path safety
7
+ * - Provides Stats and Usage persistence helpers
8
+ *
9
+ * Layout:
10
+ * ~/.lemma-cache/
11
+ * proxy.pid
12
+ * proxy.port
13
+ * stats.json (global, cross-project)
14
+ * usage.json (global free-tier counter)
15
+ * license.json (KeyManager)
16
+ * <12-char hash>/ (per-project)
17
+ * cache.db (future SQLite)
18
+ * project-stats.json
19
+ */
20
+ export declare const CACHE_DIR: string;
21
+ export declare const PID_FILE: string;
22
+ export declare const PORT_FILE: string;
23
+ export declare const STATS_FILE: string;
24
+ export declare const USAGE_FILE: string;
25
+ export declare const FREE_TIER_LIMIT = 300;
26
+ export declare const WARNING_THRESHOLD = 0.8;
27
+ export interface ProjectInfo {
28
+ name: string;
29
+ hash: string;
30
+ cacheDir: string;
31
+ dbFile: string;
32
+ statsFile: string;
33
+ }
34
+ export interface ProjectStats {
35
+ total: number;
36
+ hits: number;
37
+ misses: number;
38
+ totalLatency: number;
39
+ totalTokensSaved: number;
40
+ providers: Record<string, {
41
+ hits: number;
42
+ misses: number;
43
+ }>;
44
+ }
45
+ export interface UsageRecord {
46
+ count: number;
47
+ lastReset: string;
48
+ history: Array<{
49
+ month: string;
50
+ count: number;
51
+ }>;
52
+ }
53
+ export declare class ProjectStore {
54
+ private project;
55
+ private stats;
56
+ private usage;
57
+ constructor(overrideName?: string);
58
+ getProject(): ProjectInfo;
59
+ private resolveProject;
60
+ private detectProjectName;
61
+ getStats(): Record<string, ProjectStats>;
62
+ getProjectStats(): ProjectStats;
63
+ recordRequest(opts: {
64
+ fromCache: boolean;
65
+ latencyMs: number;
66
+ provider: string;
67
+ tokensSaved?: number;
68
+ }): void;
69
+ private emptyProjectStats;
70
+ private loadStats;
71
+ saveStats(): void;
72
+ getUsage(): UsageRecord;
73
+ incrementUsage(): void;
74
+ checkUsageLimit(): {
75
+ allowed: boolean;
76
+ warning?: string;
77
+ remaining: number;
78
+ };
79
+ getNextResetDate(): string;
80
+ private loadUsage;
81
+ saveUsage(override?: UsageRecord): void;
82
+ private ensureCacheDir;
83
+ }
84
+ //# sourceMappingURL=ProjectStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectStore.d.ts","sourceRoot":"","sources":["../../../src/proxy/ProjectStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,eAAO,MAAM,SAAS,QAAkC,CAAC;AACzD,eAAO,MAAM,QAAQ,QAAoC,CAAC;AAC1D,eAAO,MAAM,SAAS,QAAqC,CAAC;AAC5D,eAAO,MAAM,UAAU,QAAqC,CAAC;AAC7D,eAAO,MAAM,UAAU,QAAqC,CAAC;AAE7D,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAIrC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,KAAK,CAAc;gBAEf,YAAY,CAAC,EAAE,MAAM;IASjC,UAAU,IAAI,WAAW;IAIzB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,iBAAiB;IAkBzB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAIxC,eAAe,IAAI,YAAY;IAI/B,aAAa,CAAC,IAAI,EAAE;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,IAAI;IA4BR,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,SAAS;IAWjB,SAAS,IAAI,IAAI;IAUjB,QAAQ,IAAI,WAAW;IAIvB,cAAc,IAAI,IAAI;IAUtB,eAAe,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAuB5E,gBAAgB,IAAI,MAAM;IAK1B,OAAO,CAAC,SAAS;IAwBjB,SAAS,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI;IAUvC,OAAO,CAAC,cAAc;CAKvB"}
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ /**
3
+ * Lemma ProjectStore
4
+ *
5
+ * Project-aware storage resolver:
6
+ * - Detects project identity from package.json → cwd basename fallback
7
+ * - Hashes project name to a 12-char hex prefix for path safety
8
+ * - Provides Stats and Usage persistence helpers
9
+ *
10
+ * Layout:
11
+ * ~/.lemma-cache/
12
+ * proxy.pid
13
+ * proxy.port
14
+ * stats.json (global, cross-project)
15
+ * usage.json (global free-tier counter)
16
+ * license.json (KeyManager)
17
+ * <12-char hash>/ (per-project)
18
+ * cache.db (future SQLite)
19
+ * project-stats.json
20
+ */
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.ProjectStore = exports.WARNING_THRESHOLD = exports.FREE_TIER_LIMIT = exports.USAGE_FILE = exports.STATS_FILE = exports.PORT_FILE = exports.PID_FILE = exports.CACHE_DIR = void 0;
26
+ const fs_1 = __importDefault(require("fs"));
27
+ const path_1 = __importDefault(require("path"));
28
+ const crypto_1 = __importDefault(require("crypto"));
29
+ // ─── Constants ────────────────────────────────────────────────────────────────
30
+ const HOME = process.env.HOME || process.env.USERPROFILE || '~';
31
+ exports.CACHE_DIR = path_1.default.join(HOME, '.lemma-cache');
32
+ exports.PID_FILE = path_1.default.join(exports.CACHE_DIR, 'proxy.pid');
33
+ exports.PORT_FILE = path_1.default.join(exports.CACHE_DIR, 'proxy.port');
34
+ exports.STATS_FILE = path_1.default.join(exports.CACHE_DIR, 'stats.json');
35
+ exports.USAGE_FILE = path_1.default.join(exports.CACHE_DIR, 'usage.json');
36
+ exports.FREE_TIER_LIMIT = 300; // queries per month
37
+ exports.WARNING_THRESHOLD = 0.8; // warn at 80%
38
+ // ─── ProjectStore ─────────────────────────────────────────────────────────────
39
+ class ProjectStore {
40
+ constructor(overrideName) {
41
+ this.ensureCacheDir();
42
+ this.project = this.resolveProject(overrideName);
43
+ this.stats = this.loadStats();
44
+ this.usage = this.loadUsage();
45
+ }
46
+ // ── Project Resolution ──────────────────────────────────────────────────────
47
+ getProject() {
48
+ return this.project;
49
+ }
50
+ resolveProject(overrideName) {
51
+ const name = overrideName || this.detectProjectName();
52
+ const hash = crypto_1.default.createHash('sha1').update(name).digest('hex').slice(0, 12);
53
+ const dir = path_1.default.join(exports.CACHE_DIR, hash);
54
+ if (!fs_1.default.existsSync(dir)) {
55
+ fs_1.default.mkdirSync(dir, { recursive: true });
56
+ }
57
+ // Write a human-readable marker inside the hash dir
58
+ const markerFile = path_1.default.join(dir, 'project-name.txt');
59
+ if (!fs_1.default.existsSync(markerFile)) {
60
+ fs_1.default.writeFileSync(markerFile, name);
61
+ }
62
+ return {
63
+ name,
64
+ hash,
65
+ cacheDir: dir,
66
+ dbFile: path_1.default.join(dir, 'cache.db'),
67
+ statsFile: path_1.default.join(dir, 'project-stats.json'),
68
+ };
69
+ }
70
+ detectProjectName() {
71
+ const cwd = process.cwd();
72
+ const pkgPath = path_1.default.join(cwd, 'package.json');
73
+ if (fs_1.default.existsSync(pkgPath)) {
74
+ try {
75
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
76
+ if (pkg.name)
77
+ return pkg.name;
78
+ }
79
+ catch {
80
+ // Ignore parse errors
81
+ }
82
+ }
83
+ return path_1.default.basename(cwd);
84
+ }
85
+ // ── Stats ───────────────────────────────────────────────────────────────────
86
+ getStats() {
87
+ return this.stats;
88
+ }
89
+ getProjectStats() {
90
+ return this.stats[this.project.name] || this.emptyProjectStats();
91
+ }
92
+ recordRequest(opts) {
93
+ if (!this.stats[this.project.name]) {
94
+ this.stats[this.project.name] = this.emptyProjectStats();
95
+ }
96
+ const s = this.stats[this.project.name];
97
+ s.total++;
98
+ s.totalLatency += opts.latencyMs;
99
+ if (opts.fromCache) {
100
+ s.hits++;
101
+ s.totalTokensSaved += opts.tokensSaved || 0;
102
+ }
103
+ else {
104
+ s.misses++;
105
+ }
106
+ if (!s.providers[opts.provider]) {
107
+ s.providers[opts.provider] = { hits: 0, misses: 0 };
108
+ }
109
+ if (opts.fromCache) {
110
+ s.providers[opts.provider].hits++;
111
+ }
112
+ else {
113
+ s.providers[opts.provider].misses++;
114
+ }
115
+ this.saveStats();
116
+ }
117
+ emptyProjectStats() {
118
+ return { total: 0, hits: 0, misses: 0, totalLatency: 0, totalTokensSaved: 0, providers: {} };
119
+ }
120
+ loadStats() {
121
+ try {
122
+ if (fs_1.default.existsSync(exports.STATS_FILE)) {
123
+ return JSON.parse(fs_1.default.readFileSync(exports.STATS_FILE, 'utf8'));
124
+ }
125
+ }
126
+ catch {
127
+ // Ignore
128
+ }
129
+ return {};
130
+ }
131
+ saveStats() {
132
+ try {
133
+ fs_1.default.writeFileSync(exports.STATS_FILE, JSON.stringify(this.stats, null, 2));
134
+ }
135
+ catch {
136
+ // Ignore
137
+ }
138
+ }
139
+ // ── Usage (Free Tier) ───────────────────────────────────────────────────────
140
+ getUsage() {
141
+ return this.usage;
142
+ }
143
+ incrementUsage() {
144
+ this.usage.count++;
145
+ this.saveUsage();
146
+ if (this.usage.count === Math.floor(exports.FREE_TIER_LIMIT * exports.WARNING_THRESHOLD)) {
147
+ console.log(`\n⚠️ Free tier: ${this.usage.count}/${exports.FREE_TIER_LIMIT} queries used this month`);
148
+ console.log(`💡 Upgrade to Pro: https://lemma.nxus.studio/upgrade\n`);
149
+ }
150
+ }
151
+ checkUsageLimit() {
152
+ const remaining = exports.FREE_TIER_LIMIT - this.usage.count;
153
+ if (remaining <= 0) {
154
+ return {
155
+ allowed: false,
156
+ remaining: 0,
157
+ warning: `Free tier limit reached (${exports.FREE_TIER_LIMIT}/month). Upgrade: https://lemma.nxus.studio/upgrade`,
158
+ };
159
+ }
160
+ const percentUsed = this.usage.count / exports.FREE_TIER_LIMIT;
161
+ if (percentUsed >= exports.WARNING_THRESHOLD) {
162
+ return {
163
+ allowed: true,
164
+ remaining,
165
+ warning: `Approaching free tier limit: ${remaining} queries remaining.`,
166
+ };
167
+ }
168
+ return { allowed: true, remaining };
169
+ }
170
+ getNextResetDate() {
171
+ const now = new Date();
172
+ return new Date(now.getFullYear(), now.getMonth() + 1, 1).toISOString();
173
+ }
174
+ loadUsage() {
175
+ try {
176
+ if (fs_1.default.existsSync(exports.USAGE_FILE)) {
177
+ const u = JSON.parse(fs_1.default.readFileSync(exports.USAGE_FILE, 'utf8'));
178
+ // Check if we need to reset (new month)
179
+ const now = new Date();
180
+ const lastReset = new Date(u.lastReset || 0);
181
+ if (now.getMonth() !== lastReset.getMonth() || now.getFullYear() !== lastReset.getFullYear()) {
182
+ const newRecord = {
183
+ count: 0,
184
+ lastReset: now.toISOString(),
185
+ history: [...(u.history || []), { month: lastReset.toISOString().slice(0, 7), count: u.count }],
186
+ };
187
+ this.saveUsage(newRecord);
188
+ return newRecord;
189
+ }
190
+ return u;
191
+ }
192
+ }
193
+ catch {
194
+ // Ignore
195
+ }
196
+ return { count: 0, lastReset: new Date().toISOString(), history: [] };
197
+ }
198
+ saveUsage(override) {
199
+ try {
200
+ fs_1.default.writeFileSync(exports.USAGE_FILE, JSON.stringify(override || this.usage, null, 2));
201
+ }
202
+ catch {
203
+ // Ignore
204
+ }
205
+ }
206
+ // ── Utilities ───────────────────────────────────────────────────────────────
207
+ ensureCacheDir() {
208
+ if (!fs_1.default.existsSync(exports.CACHE_DIR)) {
209
+ fs_1.default.mkdirSync(exports.CACHE_DIR, { recursive: true });
210
+ }
211
+ }
212
+ }
213
+ exports.ProjectStore = ProjectStore;
214
+ //# sourceMappingURL=ProjectStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectStore.js","sourceRoot":"","sources":["../../../src/proxy/ProjectStore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;;;;AAEH,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAE5B,iFAAiF;AAEjF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;AACnD,QAAA,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AAC5C,QAAA,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAS,EAAE,WAAW,CAAC,CAAC;AAC7C,QAAA,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAS,EAAE,YAAY,CAAC,CAAC;AAC/C,QAAA,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAS,EAAE,YAAY,CAAC,CAAC;AAChD,QAAA,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAS,EAAE,YAAY,CAAC,CAAC;AAEhD,QAAA,eAAe,GAAG,GAAG,CAAC,CAAO,oBAAoB;AACjD,QAAA,iBAAiB,GAAG,GAAG,CAAC,CAAK,cAAc;AA2BxD,iFAAiF;AAEjF,MAAa,YAAY;IAKvB,YAAY,YAAqB;QAC/B,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,+EAA+E;IAE/E,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,cAAc,CAAC,YAAqB;QAC1C,MAAM,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,gBAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,GAAG,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAS,EAAE,IAAI,CAAC,CAAC;QAEvC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,YAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,oDAAoD;QACpD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACtD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,YAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QAED,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,QAAQ,EAAE,GAAG;YACb,MAAM,EAAE,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC;YAClC,SAAS,EAAE,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC;SAChD,CAAC;IACJ,CAAC;IAEO,iBAAiB;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAE/C,IAAI,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;gBACzD,IAAI,GAAG,CAAC,IAAI;oBAAE,OAAO,GAAG,CAAC,IAAI,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC;QAED,OAAO,cAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,+EAA+E;IAE/E,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACnE,CAAC;IAED,aAAa,CAAC,IAKb;QACC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3D,CAAC;QAED,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,CAAC;QAEjC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,CAAC,CAAC,IAAI,EAAE,CAAC;YACT,CAAC,CAAC,gBAAgB,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,CAAC,CAAC,MAAM,EAAE,CAAC;QACb,CAAC;QAED,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,iBAAiB;QACvB,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC/F,CAAC;IAEO,SAAS;QACf,IAAI,CAAC;YACH,IAAI,YAAE,CAAC,UAAU,CAAC,kBAAU,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,kBAAU,EAAE,MAAM,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,SAAS;QACP,IAAI,CAAC;YACH,YAAE,CAAC,aAAa,CAAC,kBAAU,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,+EAA+E;IAE/E,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,uBAAe,GAAG,yBAAiB,CAAC,EAAE,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,uBAAe,0BAA0B,CAAC,CAAC;YAC/F,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED,eAAe;QACb,MAAM,SAAS,GAAG,uBAAe,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAErD,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACnB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,CAAC;gBACZ,OAAO,EAAE,4BAA4B,uBAAe,qDAAqD;aAC1G,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,uBAAe,CAAC;QACvD,IAAI,WAAW,IAAI,yBAAiB,EAAE,CAAC;YACrC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS;gBACT,OAAO,EAAE,gCAAgC,SAAS,qBAAqB;aACxE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAED,gBAAgB;QACd,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1E,CAAC;IAEO,SAAS;QACf,IAAI,CAAC;YACH,IAAI,YAAE,CAAC,UAAU,CAAC,kBAAU,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAgB,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,kBAAU,EAAE,MAAM,CAAC,CAAC,CAAC;gBACvE,wCAAwC;gBACxC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;gBAC7C,IAAI,GAAG,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;oBAC7F,MAAM,SAAS,GAAgB;wBAC7B,KAAK,EAAE,CAAC;wBACR,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;wBAC5B,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;qBAChG,CAAC;oBACF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;oBAC1B,OAAO,SAAS,CAAC;gBACnB,CAAC;gBACD,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACxE,CAAC;IAED,SAAS,CAAC,QAAsB;QAC9B,IAAI,CAAC;YACH,YAAE,CAAC,aAAa,CAAC,kBAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,+EAA+E;IAEvE,cAAc;QACpB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,iBAAS,CAAC,EAAE,CAAC;YAC9B,YAAE,CAAC,SAAS,CAAC,iBAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;CACF;AA/MD,oCA+MC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lemma SseRelay
3
+ *
4
+ * Handles Server-Sent Events (SSE) streaming in two modes:
5
+ *
6
+ * MISS mode: Pipes upstream SSE response from OpenAI/Anthropic to the client
7
+ * in real-time (transparent relay).
8
+ *
9
+ * HIT mode: Simulates an SSE stream from a cached response by chunking the
10
+ * text into ~5-word pieces and emitting them with a small delay.
11
+ * Visually identical to a real stream for IDEs like Cursor.
12
+ *
13
+ * Both modes:
14
+ * - Set correct SSE headers (text/event-stream, no-cache)
15
+ * - Inject X-Lemma-Cache, X-Lemma-Similarity, X-Lemma-Tier headers
16
+ * - Append the `data: [DONE]` sentinel
17
+ * - Support both OpenAI and Anthropic SSE envelope formats
18
+ */
19
+ import { Request, Response } from 'express';
20
+ export type Provider = 'openai' | 'anthropic';
21
+ export interface SseRelayOptions {
22
+ provider: Provider;
23
+ tier: 'standard' | 'pro';
24
+ similarity?: number;
25
+ latencyMs?: number;
26
+ }
27
+ export declare class SseRelay {
28
+ /**
29
+ * Simulate an SSE stream from a cached response.
30
+ */
31
+ static simulateHit(res: Response, cachedResponse: any, opts: SseRelayOptions): Promise<void>;
32
+ /**
33
+ * Relay a live upstream SSE stream transparently.
34
+ */
35
+ static relayMiss(req: Request, res: Response, upstreamBody: any, provider: Provider, opts: SseRelayOptions): Promise<void>;
36
+ private static setSseHeaders;
37
+ private static buildUpstreamRequest;
38
+ private static buildChunkEvent;
39
+ private static extractTextFromResponse;
40
+ }
41
+ //# sourceMappingURL=SseRelay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SseRelay.d.ts","sourceRoot":"","sources":["../../../src/proxy/SseRelay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAS5C,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,qBAAa,QAAQ;IACnB;;OAEG;WACU,WAAW,CACtB,GAAG,EAAE,QAAQ,EACb,cAAc,EAAE,GAAG,EACnB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,IAAI,CAAC;IAuBhB;;OAEG;WACU,SAAS,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,YAAY,EAAE,GAAG,EACjB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,IAAI,CAAC;IA0DhB,OAAO,CAAC,MAAM,CAAC,aAAa;IAa5B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAmBnC,OAAO,CAAC,MAAM,CAAC,eAAe;IA+B9B,OAAO,CAAC,MAAM,CAAC,uBAAuB;CAevC"}