@nxuss/lemma 0.5.4 → 0.5.6

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 +30 -0
  2. package/dist/cjs/cli/lemma-proxy.d.ts +9 -2
  3. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  4. package/dist/cjs/cli/lemma-proxy.js +187 -118
  5. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  6. package/dist/cjs/cloud/BurstRateLimiter.d.ts +26 -0
  7. package/dist/cjs/cloud/BurstRateLimiter.d.ts.map +1 -0
  8. package/dist/cjs/cloud/BurstRateLimiter.js +63 -0
  9. package/dist/cjs/cloud/BurstRateLimiter.js.map +1 -0
  10. package/dist/cjs/cloud/KeyManager.d.ts +1 -0
  11. package/dist/cjs/cloud/KeyManager.d.ts.map +1 -1
  12. package/dist/cjs/cloud/KeyManager.js +28 -1
  13. package/dist/cjs/cloud/KeyManager.js.map +1 -1
  14. package/dist/cjs/cloud/PostgresQuotaLimiter.d.ts +60 -0
  15. package/dist/cjs/cloud/PostgresQuotaLimiter.d.ts.map +1 -0
  16. package/dist/cjs/cloud/PostgresQuotaLimiter.js +200 -0
  17. package/dist/cjs/cloud/PostgresQuotaLimiter.js.map +1 -0
  18. package/dist/cjs/cloud/TenantCache.d.ts +1 -1
  19. package/dist/cjs/cloud/TenantCache.d.ts.map +1 -1
  20. package/dist/cjs/cloud/TenantCache.js +21 -3
  21. package/dist/cjs/cloud/TenantCache.js.map +1 -1
  22. package/dist/cjs/cloud/server.d.ts +3 -1
  23. package/dist/cjs/cloud/server.d.ts.map +1 -1
  24. package/dist/cjs/cloud/server.js +42 -5
  25. package/dist/cjs/cloud/server.js.map +1 -1
  26. package/dist/cjs/cloud/types.d.ts +3 -0
  27. package/dist/cjs/cloud/types.d.ts.map +1 -1
  28. package/dist/cjs/cloud/types.js +3 -3
  29. package/dist/cjs/cloud/types.js.map +1 -1
  30. package/dist/esm/cli/lemma-proxy.d.ts +9 -2
  31. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  32. package/dist/esm/cli/lemma-proxy.js +187 -118
  33. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  34. package/dist/esm/cloud/BurstRateLimiter.d.ts +26 -0
  35. package/dist/esm/cloud/BurstRateLimiter.d.ts.map +1 -0
  36. package/dist/esm/cloud/BurstRateLimiter.js +59 -0
  37. package/dist/esm/cloud/BurstRateLimiter.js.map +1 -0
  38. package/dist/esm/cloud/KeyManager.d.ts +1 -0
  39. package/dist/esm/cloud/KeyManager.d.ts.map +1 -1
  40. package/dist/esm/cloud/KeyManager.js +28 -1
  41. package/dist/esm/cloud/KeyManager.js.map +1 -1
  42. package/dist/esm/cloud/PostgresQuotaLimiter.d.ts +60 -0
  43. package/dist/esm/cloud/PostgresQuotaLimiter.d.ts.map +1 -0
  44. package/dist/esm/cloud/PostgresQuotaLimiter.js +196 -0
  45. package/dist/esm/cloud/PostgresQuotaLimiter.js.map +1 -0
  46. package/dist/esm/cloud/TenantCache.d.ts +1 -1
  47. package/dist/esm/cloud/TenantCache.d.ts.map +1 -1
  48. package/dist/esm/cloud/TenantCache.js +21 -3
  49. package/dist/esm/cloud/TenantCache.js.map +1 -1
  50. package/dist/esm/cloud/server.d.ts +3 -1
  51. package/dist/esm/cloud/server.d.ts.map +1 -1
  52. package/dist/esm/cloud/server.js +41 -5
  53. package/dist/esm/cloud/server.js.map +1 -1
  54. package/dist/esm/cloud/types.d.ts +3 -0
  55. package/dist/esm/cloud/types.d.ts.map +1 -1
  56. package/dist/esm/cloud/types.js +3 -3
  57. package/dist/esm/cloud/types.js.map +1 -1
  58. package/package.json +1 -2
@@ -24,7 +24,7 @@ function getVersion() {
24
24
  catch { }
25
25
  }
26
26
  }
27
- return '0.5.3';
27
+ return '0.5.4';
28
28
  }
29
29
  const VERSION = getVersion();
30
30
  import ComplexityRouter from '../proxy/ComplexityRouter';
@@ -209,141 +209,207 @@ function ensureGitIgnore() {
209
209
  return added;
210
210
  }
211
211
  const CLIPBOARD_PID_FILE = path.join(CACHE_DIR, 'clipboard.pid');
212
- export function autoConfigureAll(projectName) {
212
+ export function autoConfigureAll(projectName, cliOpts) {
213
213
  const project = projectName || detectProject();
214
214
  const HOME = process.env.HOME || process.env.USERPROFILE || '~';
215
- // 1. Claude Desktop Config
215
+ // Load configuration if it exists
216
+ let configDisabled = false;
217
+ let configEditor = true;
218
+ let configShell = true;
219
+ let configClaude = true;
216
220
  try {
217
- const isWin = process.platform === 'win32';
218
- const claudePath = isWin
219
- ? path.join(process.env.APPDATA || '', 'Claude/claude_desktop_config.json')
220
- : path.join(HOME, 'Library/Application Support/Claude/claude_desktop_config.json');
221
- if (fs.existsSync(path.dirname(claudePath))) {
222
- let config = { mcpServers: {} };
223
- if (fs.existsSync(claudePath)) {
224
- try {
225
- config = JSON.parse(fs.readFileSync(claudePath, 'utf8'));
221
+ const configPath = path.join(process.cwd(), 'lemma.config.json');
222
+ if (fs.existsSync(configPath)) {
223
+ const rawConfig = fs.readFileSync(configPath, 'utf8');
224
+ const config = JSON.parse(rawConfig);
225
+ const systemConfig = config.system || {};
226
+ const autoConfig = systemConfig.autoConfigure || {};
227
+ if (autoConfig.disabled === true)
228
+ configDisabled = true;
229
+ if (autoConfig.editor === false)
230
+ configEditor = false;
231
+ if (autoConfig.shell === false)
232
+ configShell = false;
233
+ if (autoConfig.claude === false)
234
+ configClaude = false;
235
+ }
236
+ }
237
+ catch { }
238
+ // Merge CLI overrides and config
239
+ const shouldConfigure = cliOpts?.configure !== false && !configDisabled;
240
+ if (!shouldConfigure) {
241
+ console.log(`⏭️ [Auto-Configure] Skipped auto-configuration (disabled via options or config).`);
242
+ return;
243
+ }
244
+ const runEditor = cliOpts?.editor !== false && configEditor;
245
+ const runShell = cliOpts?.shell !== false && configShell;
246
+ const runClaude = cliOpts?.claude !== false && configClaude;
247
+ // 1. Claude Desktop Config
248
+ if (runClaude) {
249
+ try {
250
+ const isWin = process.platform === 'win32';
251
+ const claudePath = isWin
252
+ ? path.join(process.env.APPDATA || '', 'Claude/claude_desktop_config.json')
253
+ : path.join(HOME, 'Library/Application Support/Claude/claude_desktop_config.json');
254
+ if (fs.existsSync(path.dirname(claudePath))) {
255
+ let config = { mcpServers: {} };
256
+ if (fs.existsSync(claudePath)) {
257
+ try {
258
+ config = JSON.parse(fs.readFileSync(claudePath, 'utf8'));
259
+ }
260
+ catch { }
226
261
  }
227
- catch { }
262
+ if (!config.mcpServers)
263
+ config.mcpServers = {};
264
+ config.mcpServers.lemma = {
265
+ command: 'npx',
266
+ args: ['-y', '@nxuss/lemma', 'mcp'],
267
+ env: { LEMMA_PROJECT: project }
268
+ };
269
+ fs.writeFileSync(claudePath, JSON.stringify(config, null, 2));
270
+ console.log(`✅ [Claude Desktop] Configured automatically in: ${claudePath} (Restart Claude to activate)`);
228
271
  }
229
- if (!config.mcpServers)
230
- config.mcpServers = {};
231
- config.mcpServers.lemma = {
232
- command: 'npx',
233
- args: ['-y', '@nxuss/lemma', 'mcp'],
234
- env: { LEMMA_PROJECT: project }
235
- };
236
- fs.writeFileSync(claudePath, JSON.stringify(config, null, 2));
237
- console.log(`✅ [Claude Desktop] Configured automatically! (Restart Claude to activate)`);
272
+ }
273
+ catch (e) {
274
+ console.log(`⚠️ Could not auto-configure Claude Desktop: ${e.message}`);
238
275
  }
239
276
  }
240
- catch (e) {
241
- console.log(`⚠️ Could not auto-configure Claude Desktop: ${e.message}`);
277
+ else {
278
+ console.log(`⏭️ [Claude Desktop] Skip auto-configuration.`);
242
279
  }
243
280
  // 2. Shell Profiles Environment Override
244
- try {
245
- const profiles = [
246
- path.join(HOME, '.zshrc'),
247
- path.join(HOME, '.bashrc'),
248
- path.join(HOME, '.bash_profile'),
249
- path.join(HOME, '.profile')
250
- ];
251
- const lines = [
252
- '',
253
- '# Lemma AI Gateway Overrides',
254
- 'export OPENAI_BASE_URL="http://localhost:8081/v1"',
255
- 'export ANTHROPIC_BASE_URL="http://localhost:8081"',
256
- 'export LEMMA_PROJECT="' + project + '"',
257
- ''
258
- ].join('\n');
259
- for (const profile of profiles) {
260
- if (fs.existsSync(profile)) {
261
- try {
262
- const content = fs.readFileSync(profile, 'utf8');
263
- if (!content.includes('OPENAI_BASE_URL') && !content.includes('LEMMA_PROJECT')) {
264
- fs.appendFileSync(profile, lines);
265
- console.log(`✅ [Shell Profile] Configured ${path.basename(profile)} with local redirect variables!`);
281
+ if (runShell) {
282
+ try {
283
+ const profiles = [
284
+ path.join(HOME, '.zshrc'),
285
+ path.join(HOME, '.bashrc'),
286
+ path.join(HOME, '.bash_profile'),
287
+ path.join(HOME, '.profile')
288
+ ];
289
+ const lines = [
290
+ '',
291
+ '# Lemma AI Gateway Overrides',
292
+ 'export OPENAI_BASE_URL="http://localhost:8081/v1"',
293
+ 'export ANTHROPIC_BASE_URL="http://localhost:8081"',
294
+ 'export LEMMA_PROJECT="' + project + '"',
295
+ ''
296
+ ].join('\n');
297
+ for (const profile of profiles) {
298
+ if (fs.existsSync(profile)) {
299
+ try {
300
+ const content = fs.readFileSync(profile, 'utf8');
301
+ if (!content.includes('OPENAI_BASE_URL') && !content.includes('LEMMA_PROJECT')) {
302
+ fs.appendFileSync(profile, lines);
303
+ console.log(`✅ [Shell Profile] Configured ${path.basename(profile)} with local redirect variables!`);
304
+ }
266
305
  }
306
+ catch { }
267
307
  }
268
- catch { }
269
308
  }
270
309
  }
310
+ catch (e) {
311
+ console.log(`⚠️ Could not auto-configure shell profiles: ${e.message}`);
312
+ }
271
313
  }
272
- catch (e) {
273
- console.log(`⚠️ Could not auto-configure shell profiles: ${e.message}`);
314
+ else {
315
+ console.log(`⏭️ [Shell Profile] Skip auto-configuration.`);
274
316
  }
275
317
  // 3. Cursor & Windsurf settings.json updates
276
- try {
277
- const isWin = process.platform === 'win32';
278
- const isMac = process.platform === 'darwin';
279
- const candidates = [];
280
- if (isMac) {
281
- candidates.push({
282
- name: 'Cursor',
283
- path: path.join(HOME, 'Library/Application Support/Cursor/User/settings.json')
284
- });
285
- candidates.push({
286
- name: 'Windsurf',
287
- path: path.join(HOME, 'Library/Application Support/Windsurf/User/settings.json')
288
- });
289
- }
290
- else if (isWin) {
291
- const appData = process.env.APPDATA || '';
292
- candidates.push({
293
- name: 'Cursor',
294
- path: path.join(appData, 'Cursor/User/settings.json')
295
- });
296
- candidates.push({
297
- name: 'Windsurf',
298
- path: path.join(appData, 'Windsurf/User/settings.json')
299
- });
300
- }
301
- else {
302
- candidates.push({
303
- name: 'Cursor',
304
- path: path.join(HOME, '.config/Cursor/User/settings.json')
305
- });
306
- candidates.push({
307
- name: 'Windsurf',
308
- path: path.join(HOME, '.config/Windsurf/User/settings.json')
309
- });
310
- }
311
- for (const cand of candidates) {
312
- const dir = path.dirname(cand.path);
313
- if (fs.existsSync(dir)) {
314
- let settings = {};
315
- if (fs.existsSync(cand.path)) {
316
- try {
317
- settings = JSON.parse(fs.readFileSync(cand.path, 'utf8'));
318
+ if (runEditor) {
319
+ try {
320
+ const isWin = process.platform === 'win32';
321
+ const isMac = process.platform === 'darwin';
322
+ const candidates = [];
323
+ if (isMac) {
324
+ candidates.push({
325
+ name: 'Cursor',
326
+ path: path.join(HOME, 'Library/Application Support/Cursor/User/settings.json')
327
+ });
328
+ candidates.push({
329
+ name: 'Windsurf',
330
+ path: path.join(HOME, 'Library/Application Support/Windsurf/User/settings.json')
331
+ });
332
+ }
333
+ else if (isWin) {
334
+ const appData = process.env.APPDATA || '';
335
+ candidates.push({
336
+ name: 'Cursor',
337
+ path: path.join(appData, 'Cursor/User/settings.json')
338
+ });
339
+ candidates.push({
340
+ name: 'Windsurf',
341
+ path: path.join(appData, 'Windsurf/User/settings.json')
342
+ });
343
+ }
344
+ else {
345
+ candidates.push({
346
+ name: 'Cursor',
347
+ path: path.join(HOME, '.config/Cursor/User/settings.json')
348
+ });
349
+ candidates.push({
350
+ name: 'Windsurf',
351
+ path: path.join(HOME, '.config/Windsurf/User/settings.json')
352
+ });
353
+ }
354
+ for (const cand of candidates) {
355
+ const dir = path.dirname(cand.path);
356
+ if (fs.existsSync(dir)) {
357
+ let settings = {};
358
+ if (fs.existsSync(cand.path)) {
359
+ try {
360
+ settings = JSON.parse(fs.readFileSync(cand.path, 'utf8'));
361
+ }
362
+ catch { }
318
363
  }
319
- catch { }
320
- }
321
- let updated = false;
322
- // Settings configuration to route custom openai/anthropic models through the proxy
323
- const updates = {
324
- "openai.baseURL": "http://localhost:8081/v1",
325
- "openai.apiKey": "dummy-key-for-lemma",
326
- "anthropic.baseURL": "http://localhost:8081",
327
- "anthropic.apiKey": "dummy-key-for-lemma"
328
- };
329
- for (const [key, value] of Object.entries(updates)) {
330
- if (settings[key] !== value) {
331
- settings[key] = value;
332
- updated = true;
364
+ let updated = false;
365
+ // Settings configuration to route custom openai/anthropic models through the proxy
366
+ const updates = {
367
+ "openai.baseURL": "http://localhost:8081/v1",
368
+ "openai.apiKey": "dummy-key-for-lemma",
369
+ "anthropic.baseURL": "http://localhost:8081",
370
+ "anthropic.apiKey": "dummy-key-for-lemma"
371
+ };
372
+ for (const [key, value] of Object.entries(updates)) {
373
+ if (settings[key] !== value) {
374
+ settings[key] = value;
375
+ updated = true;
376
+ }
377
+ }
378
+ if (updated) {
379
+ fs.writeFileSync(cand.path, JSON.stringify(settings, null, 2));
380
+ console.log(`✅ [${cand.name}] Configured editor settings in ${cand.path} to route via local gateway!`);
333
381
  }
334
- }
335
- if (updated) {
336
- fs.writeFileSync(cand.path, JSON.stringify(settings, null, 2));
337
- console.log(`✅ [${cand.name}] Configured editor settings to route via local gateway!`);
338
382
  }
339
383
  }
340
384
  }
385
+ catch (e) {
386
+ console.log(`⚠️ Could not auto-configure editor settings: ${e.message}`);
387
+ }
341
388
  }
342
- catch (e) {
343
- console.log(`⚠️ Could not auto-configure editor settings: ${e.message}`);
389
+ else {
390
+ console.log(`⏭️ [Editor Settings] Skip auto-configuration.`);
344
391
  }
345
392
  }
346
- export function startBackgroundClipboardWatcher() {
393
+ export function startBackgroundClipboardWatcher(cliOpts) {
394
+ // Load configuration if it exists
395
+ let configDisabled = false;
396
+ try {
397
+ const configPath = path.join(process.cwd(), 'lemma.config.json');
398
+ if (fs.existsSync(configPath)) {
399
+ const rawConfig = fs.readFileSync(configPath, 'utf8');
400
+ const config = JSON.parse(rawConfig);
401
+ const systemConfig = config.system || {};
402
+ const clipConfig = systemConfig.clipboardWatcher || {};
403
+ if (clipConfig.disabled === true)
404
+ configDisabled = true;
405
+ }
406
+ }
407
+ catch { }
408
+ const shouldWatch = cliOpts?.clipboard !== false && !configDisabled;
409
+ if (!shouldWatch) {
410
+ console.log(`⏭️ [Clipboard] Background clipboard watcher is disabled (via options or config).`);
411
+ return;
412
+ }
347
413
  try {
348
414
  if (fs.existsSync(CLIPBOARD_PID_FILE)) {
349
415
  const oldPid = fs.readFileSync(CLIPBOARD_PID_FILE, 'utf8');
@@ -1506,6 +1572,8 @@ program.command('start')
1506
1572
  .option('--project <name>', 'Override project name')
1507
1573
  .option('--stack', 'Launch full development stack (Chroma + Router + Dashboard + Autopilot)')
1508
1574
  .option('--autopilot', 'Launch background compiler watcher & healer', false)
1575
+ .option('--no-configure', 'Skip automatic configuration of editors, shell overrides, and Claude Desktop')
1576
+ .option('--no-clipboard', 'Skip launching background clipboard watcher')
1509
1577
  .action(async (opts) => {
1510
1578
  const port = parseInt(opts.port, 10);
1511
1579
  // Ensure ChromaDB is running for semantic caching/search
@@ -1517,15 +1585,15 @@ program.command('start')
1517
1585
  console.log(`💡 Use 'lemma status' for details or 'lemma stop' to restart.\n`);
1518
1586
  // Keep configurations up to date even if server was already started
1519
1587
  const runningProject = resp.data.project || detectProject();
1520
- autoConfigureAll(runningProject);
1521
- startBackgroundClipboardWatcher();
1588
+ autoConfigureAll(runningProject, { configure: opts.configure });
1589
+ startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
1522
1590
  process.exit(0);
1523
1591
  }
1524
1592
  }
1525
1593
  catch { }
1526
1594
  const startProject = opts.project || detectProject();
1527
- autoConfigureAll(startProject);
1528
- startBackgroundClipboardWatcher();
1595
+ autoConfigureAll(startProject, { configure: opts.configure });
1596
+ startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
1529
1597
  // Automatically enable autopilot if stack is enabled and user is Pro
1530
1598
  const runAutopilot = opts.autopilot || opts.stack;
1531
1599
  if (runAutopilot) {
@@ -1701,7 +1769,8 @@ program.command('activate <key>')
1701
1769
  });
1702
1770
  program.command('init')
1703
1771
  .description('Initialize Lemma in the current project (auto-discovery)')
1704
- .action(async () => {
1772
+ .option('--no-configure', 'Skip automatic configuration of editors, shell overrides, and Claude Desktop')
1773
+ .action(async (opts) => {
1705
1774
  const project = detectProject();
1706
1775
  console.log(`\n🛠️ Initializing Lemma for [${project}]...`);
1707
1776
  // Ensure ChromaDB is running for semantic caching/search
@@ -1731,7 +1800,7 @@ program.command('init')
1731
1800
  console.log('✅ Created .env with Lemma configuration');
1732
1801
  }
1733
1802
  console.log('\n🧠 \x1b[35mAuto-Setup MCP and redirection overrides for AI IDEs...\x1b[0m');
1734
- autoConfigureAll(project);
1803
+ autoConfigureAll(project, { configure: opts.configure });
1735
1804
  console.log('\n✨ Project initialized for the Agentic Era!');
1736
1805
  console.log('🚀 Run "lemma start" to begin.\n');
1737
1806
  });