@nxuss/lemma 0.2.1 → 0.3.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 (113) 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/OrchestrationEngine.d.ts +22 -0
  19. package/dist/cjs/core/OrchestrationEngine.d.ts.map +1 -1
  20. package/dist/cjs/core/OrchestrationEngine.js +98 -0
  21. package/dist/cjs/core/OrchestrationEngine.js.map +1 -1
  22. package/dist/cjs/core/SubconsciousEngine.d.ts +24 -0
  23. package/dist/cjs/core/SubconsciousEngine.d.ts.map +1 -1
  24. package/dist/cjs/core/SubconsciousEngine.js +110 -0
  25. package/dist/cjs/core/SubconsciousEngine.js.map +1 -1
  26. package/dist/cjs/core/WebSocketServer.d.ts.map +1 -1
  27. package/dist/cjs/core/WebSocketServer.js +4 -0
  28. package/dist/cjs/core/WebSocketServer.js.map +1 -1
  29. package/dist/cjs/db/database.d.ts +60 -0
  30. package/dist/cjs/db/database.d.ts.map +1 -0
  31. package/dist/cjs/db/database.js +297 -0
  32. package/dist/cjs/db/database.js.map +1 -0
  33. package/dist/cjs/db/migrations/001-dashboard-schema.d.ts +18 -0
  34. package/dist/cjs/db/migrations/001-dashboard-schema.d.ts.map +1 -0
  35. package/dist/cjs/db/migrations/001-dashboard-schema.js +175 -0
  36. package/dist/cjs/db/migrations/001-dashboard-schema.js.map +1 -0
  37. package/dist/cjs/embed/index.d.ts +1 -1
  38. package/dist/cjs/embed/index.d.ts.map +1 -1
  39. package/dist/cjs/embed/index.js +17 -9
  40. package/dist/cjs/embed/index.js.map +1 -1
  41. package/dist/cjs/proxy/Gatekeeper.d.ts +43 -0
  42. package/dist/cjs/proxy/Gatekeeper.d.ts.map +1 -0
  43. package/dist/cjs/proxy/Gatekeeper.js +202 -0
  44. package/dist/cjs/proxy/Gatekeeper.js.map +1 -0
  45. package/dist/cjs/proxy/KeyManager.d.ts +56 -0
  46. package/dist/cjs/proxy/KeyManager.d.ts.map +1 -0
  47. package/dist/cjs/proxy/KeyManager.js +220 -0
  48. package/dist/cjs/proxy/KeyManager.js.map +1 -0
  49. package/dist/cjs/proxy/ProjectStore.d.ts +84 -0
  50. package/dist/cjs/proxy/ProjectStore.d.ts.map +1 -0
  51. package/dist/cjs/proxy/ProjectStore.js +214 -0
  52. package/dist/cjs/proxy/ProjectStore.js.map +1 -0
  53. package/dist/cjs/proxy/SseRelay.d.ts +41 -0
  54. package/dist/cjs/proxy/SseRelay.d.ts.map +1 -0
  55. package/dist/cjs/proxy/SseRelay.js +176 -0
  56. package/dist/cjs/proxy/SseRelay.js.map +1 -0
  57. package/dist/esm/api/dashboardRoutes.d.ts +28 -0
  58. package/dist/esm/api/dashboardRoutes.d.ts.map +1 -0
  59. package/dist/esm/api/dashboardRoutes.js +236 -0
  60. package/dist/esm/api/dashboardRoutes.js.map +1 -0
  61. package/dist/esm/api/server.d.ts +51 -0
  62. package/dist/esm/api/server.d.ts.map +1 -0
  63. package/dist/esm/api/server.js +159 -0
  64. package/dist/esm/api/server.js.map +1 -0
  65. package/dist/esm/core/AgentRegistry.d.ts +4 -0
  66. package/dist/esm/core/AgentRegistry.d.ts.map +1 -1
  67. package/dist/esm/core/AgentRegistry.js +6 -0
  68. package/dist/esm/core/AgentRegistry.js.map +1 -1
  69. package/dist/esm/core/DashboardWebSocketServer.d.ts +70 -0
  70. package/dist/esm/core/DashboardWebSocketServer.d.ts.map +1 -0
  71. package/dist/esm/core/DashboardWebSocketServer.js +207 -0
  72. package/dist/esm/core/DashboardWebSocketServer.js.map +1 -0
  73. package/dist/esm/core/OrchestrationEngine.d.ts +22 -0
  74. package/dist/esm/core/OrchestrationEngine.d.ts.map +1 -1
  75. package/dist/esm/core/OrchestrationEngine.js +98 -0
  76. package/dist/esm/core/OrchestrationEngine.js.map +1 -1
  77. package/dist/esm/core/SubconsciousEngine.d.ts +24 -0
  78. package/dist/esm/core/SubconsciousEngine.d.ts.map +1 -1
  79. package/dist/esm/core/SubconsciousEngine.js +110 -0
  80. package/dist/esm/core/SubconsciousEngine.js.map +1 -1
  81. package/dist/esm/core/WebSocketServer.d.ts.map +1 -1
  82. package/dist/esm/core/WebSocketServer.js +4 -0
  83. package/dist/esm/core/WebSocketServer.js.map +1 -1
  84. package/dist/esm/db/database.d.ts +60 -0
  85. package/dist/esm/db/database.d.ts.map +1 -0
  86. package/dist/esm/db/database.js +293 -0
  87. package/dist/esm/db/database.js.map +1 -0
  88. package/dist/esm/db/migrations/001-dashboard-schema.d.ts +18 -0
  89. package/dist/esm/db/migrations/001-dashboard-schema.d.ts.map +1 -0
  90. package/dist/esm/db/migrations/001-dashboard-schema.js +172 -0
  91. package/dist/esm/db/migrations/001-dashboard-schema.js.map +1 -0
  92. package/dist/esm/embed/index.d.ts +1 -1
  93. package/dist/esm/embed/index.d.ts.map +1 -1
  94. package/dist/esm/embed/index.js +17 -9
  95. package/dist/esm/embed/index.js.map +1 -1
  96. package/dist/esm/proxy/Gatekeeper.d.ts +43 -0
  97. package/dist/esm/proxy/Gatekeeper.d.ts.map +1 -0
  98. package/dist/esm/proxy/Gatekeeper.js +165 -0
  99. package/dist/esm/proxy/Gatekeeper.js.map +1 -0
  100. package/dist/esm/proxy/KeyManager.d.ts +56 -0
  101. package/dist/esm/proxy/KeyManager.d.ts.map +1 -0
  102. package/dist/esm/proxy/KeyManager.js +213 -0
  103. package/dist/esm/proxy/KeyManager.js.map +1 -0
  104. package/dist/esm/proxy/ProjectStore.d.ts +84 -0
  105. package/dist/esm/proxy/ProjectStore.d.ts.map +1 -0
  106. package/dist/esm/proxy/ProjectStore.js +207 -0
  107. package/dist/esm/proxy/ProjectStore.js.map +1 -0
  108. package/dist/esm/proxy/SseRelay.d.ts +41 -0
  109. package/dist/esm/proxy/SseRelay.d.ts.map +1 -0
  110. package/dist/esm/proxy/SseRelay.js +169 -0
  111. package/dist/esm/proxy/SseRelay.js.map +1 -0
  112. package/lemma-proxy.js +810 -0
  113. package/package.json +17 -3
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Lemma KeyManager
3
+ *
4
+ * Handles Pro license key lifecycle:
5
+ * - Activation via remote validation (https://lemma.nxus.studio/api/v1/validate)
6
+ * - Graceful fallback to local format check when endpoint is unreachable
7
+ * - Persistence to ~/.lemma-cache/license.json
8
+ * - Auto-migration from legacy license.key format
9
+ *
10
+ * TODO(v0.4.0): Replace HTTP validate call with direct PostgreSQL check.
11
+ */
12
+ export interface LicenseData {
13
+ key: string;
14
+ tier: 'pro' | 'free';
15
+ isPro: boolean;
16
+ activatedAt: string;
17
+ expiresAt?: string;
18
+ validatedRemotely: boolean;
19
+ email?: string;
20
+ }
21
+ export interface ActivationResult {
22
+ success: boolean;
23
+ isPro: boolean;
24
+ message: string;
25
+ license?: LicenseData;
26
+ validatedRemotely: boolean;
27
+ }
28
+ export declare class KeyManager {
29
+ private license;
30
+ constructor();
31
+ isPro(): boolean;
32
+ getLicenseKey(): string | null;
33
+ getLicense(): LicenseData | null;
34
+ /**
35
+ * Activate a license key.
36
+ * Attempts remote validation first; falls back to format-check if unreachable.
37
+ */
38
+ activate(key: string): Promise<ActivationResult>;
39
+ /**
40
+ * Deactivate / remove the current license.
41
+ */
42
+ deactivate(): void;
43
+ /**
44
+ * Reload license from disk (used after activate from another process).
45
+ */
46
+ reload(): void;
47
+ private load;
48
+ private save;
49
+ private ensureCacheDir;
50
+ /**
51
+ * POST to https://lemma.nxus.studio/api/v1/validate with a timeout.
52
+ * Returns the JSON response body.
53
+ */
54
+ private validateRemote;
55
+ }
56
+ //# sourceMappingURL=KeyManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyManager.d.ts","sourceRoot":"","sources":["../../../src/proxy/KeyManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAA4B;;IAS3C,KAAK,IAAI,OAAO;IAYhB,aAAa,IAAI,MAAM,GAAG,IAAI;IAI9B,UAAU,IAAI,WAAW,GAAG,IAAI;IAIhC;;;OAGG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0DtD;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;OAEG;IACH,MAAM,IAAI,IAAI;IAMd,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,cAAc;CAyCvB"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Lemma KeyManager
3
+ *
4
+ * Handles Pro license key lifecycle:
5
+ * - Activation via remote validation (https://lemma.nxus.studio/api/v1/validate)
6
+ * - Graceful fallback to local format check when endpoint is unreachable
7
+ * - Persistence to ~/.lemma-cache/license.json
8
+ * - Auto-migration from legacy license.key format
9
+ *
10
+ * TODO(v0.4.0): Replace HTTP validate call with direct PostgreSQL check.
11
+ */
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+ import https from 'https';
15
+ // ─── Constants ────────────────────────────────────────────────────────────────
16
+ const CACHE_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '~', '.lemma-cache');
17
+ const LICENSE_FILE = path.join(CACHE_DIR, 'license.json');
18
+ const LEGACY_LICENSE_FILE = path.join(CACHE_DIR, 'license.key');
19
+ const VALIDATE_URL = 'https://lemma.nxus.studio/api/v1/validate';
20
+ const VALIDATE_TIMEOUT_MS = 5000;
21
+ const KEY_FORMAT_REGEX = /^LEMMA-PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
22
+ // ─── KeyManager ───────────────────────────────────────────────────────────────
23
+ export class KeyManager {
24
+ constructor() {
25
+ this.license = null;
26
+ this.ensureCacheDir();
27
+ this.license = this.load();
28
+ }
29
+ // ── Public API ──────────────────────────────────────────────────────────────
30
+ isPro() {
31
+ if (!this.license)
32
+ return false;
33
+ if (!this.license.isPro)
34
+ return false;
35
+ // Check expiry if set
36
+ if (this.license.expiresAt) {
37
+ if (Date.now() > new Date(this.license.expiresAt).getTime()) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ }
43
+ getLicenseKey() {
44
+ return this.license?.key || null;
45
+ }
46
+ getLicense() {
47
+ return this.license;
48
+ }
49
+ /**
50
+ * Activate a license key.
51
+ * Attempts remote validation first; falls back to format-check if unreachable.
52
+ */
53
+ async activate(key) {
54
+ // Basic format validation first
55
+ if (!KEY_FORMAT_REGEX.test(key.trim())) {
56
+ return {
57
+ success: false,
58
+ isPro: false,
59
+ validatedRemotely: false,
60
+ message: `Invalid key format. Pro keys look like: LEMMA-PRO-XXXX-XXXX-XXXX`,
61
+ };
62
+ }
63
+ // Attempt remote validation
64
+ let validatedRemotely = false;
65
+ let remoteData = null;
66
+ try {
67
+ remoteData = await this.validateRemote(key.trim());
68
+ validatedRemotely = true;
69
+ if (!remoteData.valid) {
70
+ return {
71
+ success: false,
72
+ isPro: false,
73
+ validatedRemotely: true,
74
+ message: remoteData.reason || 'License key is not valid.',
75
+ };
76
+ }
77
+ }
78
+ catch (err) {
79
+ // Network/endpoint unavailable — fall back to local format check
80
+ console.warn(`[KeyManager] Remote validation unavailable: ${err.message}`);
81
+ console.warn(`[KeyManager] Falling back to local format validation.`);
82
+ }
83
+ // Build and persist license
84
+ const license = {
85
+ key: key.trim(),
86
+ tier: 'pro',
87
+ isPro: true,
88
+ activatedAt: new Date().toISOString(),
89
+ validatedRemotely,
90
+ expiresAt: remoteData?.expiresAt,
91
+ email: remoteData?.email,
92
+ };
93
+ this.license = license;
94
+ this.save(license);
95
+ return {
96
+ success: true,
97
+ isPro: true,
98
+ validatedRemotely,
99
+ message: validatedRemotely
100
+ ? `✅ Lemma Pro activated (server-verified). Semantic Mode enabled.`
101
+ : `✅ Lemma Pro activated (format-validated, server offline). Semantic Mode enabled.`,
102
+ license,
103
+ };
104
+ }
105
+ /**
106
+ * Deactivate / remove the current license.
107
+ */
108
+ deactivate() {
109
+ this.license = null;
110
+ if (fs.existsSync(LICENSE_FILE)) {
111
+ fs.unlinkSync(LICENSE_FILE);
112
+ }
113
+ }
114
+ /**
115
+ * Reload license from disk (used after activate from another process).
116
+ */
117
+ reload() {
118
+ this.license = this.load();
119
+ }
120
+ // ── Private helpers ─────────────────────────────────────────────────────────
121
+ load() {
122
+ // Try new JSON format
123
+ if (fs.existsSync(LICENSE_FILE)) {
124
+ try {
125
+ const data = JSON.parse(fs.readFileSync(LICENSE_FILE, 'utf8'));
126
+ if (data.key && data.isPro)
127
+ return data;
128
+ }
129
+ catch {
130
+ // Corrupt file — ignore
131
+ }
132
+ }
133
+ // Migrate from legacy format
134
+ if (fs.existsSync(LEGACY_LICENSE_FILE)) {
135
+ try {
136
+ const legacy = JSON.parse(fs.readFileSync(LEGACY_LICENSE_FILE, 'utf8'));
137
+ if (legacy.key && (legacy.isPro || legacy.tier === 'pro')) {
138
+ const migrated = {
139
+ key: legacy.key,
140
+ tier: 'pro',
141
+ isPro: true,
142
+ activatedAt: legacy.activatedAt || new Date().toISOString(),
143
+ validatedRemotely: false,
144
+ };
145
+ this.save(migrated);
146
+ fs.unlinkSync(LEGACY_LICENSE_FILE); // Remove legacy file
147
+ console.log('[KeyManager] Migrated license from legacy format.');
148
+ return migrated;
149
+ }
150
+ }
151
+ catch {
152
+ // Ignore
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+ save(license) {
158
+ try {
159
+ this.ensureCacheDir();
160
+ fs.writeFileSync(LICENSE_FILE, JSON.stringify(license, null, 2));
161
+ }
162
+ catch (err) {
163
+ console.error(`[KeyManager] Failed to save license: ${err.message}`);
164
+ }
165
+ }
166
+ ensureCacheDir() {
167
+ if (!fs.existsSync(CACHE_DIR)) {
168
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
169
+ }
170
+ }
171
+ /**
172
+ * POST to https://lemma.nxus.studio/api/v1/validate with a timeout.
173
+ * Returns the JSON response body.
174
+ */
175
+ validateRemote(key) {
176
+ return new Promise((resolve, reject) => {
177
+ const body = JSON.stringify({ key });
178
+ const url = new URL(VALIDATE_URL);
179
+ const options = {
180
+ hostname: url.hostname,
181
+ port: url.port || 443,
182
+ path: url.pathname,
183
+ method: 'POST',
184
+ headers: {
185
+ 'Content-Type': 'application/json',
186
+ 'Content-Length': Buffer.byteLength(body),
187
+ 'User-Agent': 'lemma-proxy/0.3.0',
188
+ },
189
+ };
190
+ const req = https.request(options, (res) => {
191
+ let data = '';
192
+ res.on('data', (chunk) => { data += chunk; });
193
+ res.on('end', () => {
194
+ try {
195
+ resolve(JSON.parse(data));
196
+ }
197
+ catch {
198
+ reject(new Error(`Invalid JSON response from validate endpoint`));
199
+ }
200
+ });
201
+ });
202
+ req.on('error', reject);
203
+ const timer = setTimeout(() => {
204
+ req.destroy();
205
+ reject(new Error(`Validation endpoint timed out after ${VALIDATE_TIMEOUT_MS}ms`));
206
+ }, VALIDATE_TIMEOUT_MS);
207
+ req.on('close', () => clearTimeout(timer));
208
+ req.write(body);
209
+ req.end();
210
+ });
211
+ }
212
+ }
213
+ //# 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,207 @@
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
+ import fs from 'fs';
21
+ import path from 'path';
22
+ import crypto from 'crypto';
23
+ // ─── Constants ────────────────────────────────────────────────────────────────
24
+ const HOME = process.env.HOME || process.env.USERPROFILE || '~';
25
+ export const CACHE_DIR = path.join(HOME, '.lemma-cache');
26
+ export const PID_FILE = path.join(CACHE_DIR, 'proxy.pid');
27
+ export const PORT_FILE = path.join(CACHE_DIR, 'proxy.port');
28
+ export const STATS_FILE = path.join(CACHE_DIR, 'stats.json');
29
+ export const USAGE_FILE = path.join(CACHE_DIR, 'usage.json');
30
+ export const FREE_TIER_LIMIT = 300; // queries per month
31
+ export const WARNING_THRESHOLD = 0.8; // warn at 80%
32
+ // ─── ProjectStore ─────────────────────────────────────────────────────────────
33
+ export class ProjectStore {
34
+ constructor(overrideName) {
35
+ this.ensureCacheDir();
36
+ this.project = this.resolveProject(overrideName);
37
+ this.stats = this.loadStats();
38
+ this.usage = this.loadUsage();
39
+ }
40
+ // ── Project Resolution ──────────────────────────────────────────────────────
41
+ getProject() {
42
+ return this.project;
43
+ }
44
+ resolveProject(overrideName) {
45
+ const name = overrideName || this.detectProjectName();
46
+ const hash = crypto.createHash('sha1').update(name).digest('hex').slice(0, 12);
47
+ const dir = path.join(CACHE_DIR, hash);
48
+ if (!fs.existsSync(dir)) {
49
+ fs.mkdirSync(dir, { recursive: true });
50
+ }
51
+ // Write a human-readable marker inside the hash dir
52
+ const markerFile = path.join(dir, 'project-name.txt');
53
+ if (!fs.existsSync(markerFile)) {
54
+ fs.writeFileSync(markerFile, name);
55
+ }
56
+ return {
57
+ name,
58
+ hash,
59
+ cacheDir: dir,
60
+ dbFile: path.join(dir, 'cache.db'),
61
+ statsFile: path.join(dir, 'project-stats.json'),
62
+ };
63
+ }
64
+ detectProjectName() {
65
+ const cwd = process.cwd();
66
+ const pkgPath = path.join(cwd, 'package.json');
67
+ if (fs.existsSync(pkgPath)) {
68
+ try {
69
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
70
+ if (pkg.name)
71
+ return pkg.name;
72
+ }
73
+ catch {
74
+ // Ignore parse errors
75
+ }
76
+ }
77
+ return path.basename(cwd);
78
+ }
79
+ // ── Stats ───────────────────────────────────────────────────────────────────
80
+ getStats() {
81
+ return this.stats;
82
+ }
83
+ getProjectStats() {
84
+ return this.stats[this.project.name] || this.emptyProjectStats();
85
+ }
86
+ recordRequest(opts) {
87
+ if (!this.stats[this.project.name]) {
88
+ this.stats[this.project.name] = this.emptyProjectStats();
89
+ }
90
+ const s = this.stats[this.project.name];
91
+ s.total++;
92
+ s.totalLatency += opts.latencyMs;
93
+ if (opts.fromCache) {
94
+ s.hits++;
95
+ s.totalTokensSaved += opts.tokensSaved || 0;
96
+ }
97
+ else {
98
+ s.misses++;
99
+ }
100
+ if (!s.providers[opts.provider]) {
101
+ s.providers[opts.provider] = { hits: 0, misses: 0 };
102
+ }
103
+ if (opts.fromCache) {
104
+ s.providers[opts.provider].hits++;
105
+ }
106
+ else {
107
+ s.providers[opts.provider].misses++;
108
+ }
109
+ this.saveStats();
110
+ }
111
+ emptyProjectStats() {
112
+ return { total: 0, hits: 0, misses: 0, totalLatency: 0, totalTokensSaved: 0, providers: {} };
113
+ }
114
+ loadStats() {
115
+ try {
116
+ if (fs.existsSync(STATS_FILE)) {
117
+ return JSON.parse(fs.readFileSync(STATS_FILE, 'utf8'));
118
+ }
119
+ }
120
+ catch {
121
+ // Ignore
122
+ }
123
+ return {};
124
+ }
125
+ saveStats() {
126
+ try {
127
+ fs.writeFileSync(STATS_FILE, JSON.stringify(this.stats, null, 2));
128
+ }
129
+ catch {
130
+ // Ignore
131
+ }
132
+ }
133
+ // ── Usage (Free Tier) ───────────────────────────────────────────────────────
134
+ getUsage() {
135
+ return this.usage;
136
+ }
137
+ incrementUsage() {
138
+ this.usage.count++;
139
+ this.saveUsage();
140
+ if (this.usage.count === Math.floor(FREE_TIER_LIMIT * WARNING_THRESHOLD)) {
141
+ console.log(`\n⚠️ Free tier: ${this.usage.count}/${FREE_TIER_LIMIT} queries used this month`);
142
+ console.log(`💡 Upgrade to Pro: https://lemma.nxus.studio/upgrade\n`);
143
+ }
144
+ }
145
+ checkUsageLimit() {
146
+ const remaining = FREE_TIER_LIMIT - this.usage.count;
147
+ if (remaining <= 0) {
148
+ return {
149
+ allowed: false,
150
+ remaining: 0,
151
+ warning: `Free tier limit reached (${FREE_TIER_LIMIT}/month). Upgrade: https://lemma.nxus.studio/upgrade`,
152
+ };
153
+ }
154
+ const percentUsed = this.usage.count / FREE_TIER_LIMIT;
155
+ if (percentUsed >= WARNING_THRESHOLD) {
156
+ return {
157
+ allowed: true,
158
+ remaining,
159
+ warning: `Approaching free tier limit: ${remaining} queries remaining.`,
160
+ };
161
+ }
162
+ return { allowed: true, remaining };
163
+ }
164
+ getNextResetDate() {
165
+ const now = new Date();
166
+ return new Date(now.getFullYear(), now.getMonth() + 1, 1).toISOString();
167
+ }
168
+ loadUsage() {
169
+ try {
170
+ if (fs.existsSync(USAGE_FILE)) {
171
+ const u = JSON.parse(fs.readFileSync(USAGE_FILE, 'utf8'));
172
+ // Check if we need to reset (new month)
173
+ const now = new Date();
174
+ const lastReset = new Date(u.lastReset || 0);
175
+ if (now.getMonth() !== lastReset.getMonth() || now.getFullYear() !== lastReset.getFullYear()) {
176
+ const newRecord = {
177
+ count: 0,
178
+ lastReset: now.toISOString(),
179
+ history: [...(u.history || []), { month: lastReset.toISOString().slice(0, 7), count: u.count }],
180
+ };
181
+ this.saveUsage(newRecord);
182
+ return newRecord;
183
+ }
184
+ return u;
185
+ }
186
+ }
187
+ catch {
188
+ // Ignore
189
+ }
190
+ return { count: 0, lastReset: new Date().toISOString(), history: [] };
191
+ }
192
+ saveUsage(override) {
193
+ try {
194
+ fs.writeFileSync(USAGE_FILE, JSON.stringify(override || this.usage, null, 2));
195
+ }
196
+ catch {
197
+ // Ignore
198
+ }
199
+ }
200
+ // ── Utilities ───────────────────────────────────────────────────────────────
201
+ ensureCacheDir() {
202
+ if (!fs.existsSync(CACHE_DIR)) {
203
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
204
+ }
205
+ }
206
+ }
207
+ //# sourceMappingURL=ProjectStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectStore.js","sourceRoot":"","sources":["../../../src/proxy/ProjectStore.ts"],"names":[],"mappings":"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"}
@@ -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"}