nyxora 26.6.19 → 26.6.21

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 (83) hide show
  1. package/README.md +18 -1
  2. package/bin/nyxora.mjs +32 -0
  3. package/dist/packages/core/src/agent/reasoning.js +11 -1
  4. package/dist/packages/core/src/config/parser.js +121 -7
  5. package/dist/packages/core/src/gateway/chat.js +82 -0
  6. package/dist/packages/core/src/gateway/cli.js +63 -0
  7. package/dist/packages/core/src/gateway/server.js +117 -56
  8. package/dist/packages/core/src/gateway/setup.js +39 -22
  9. package/dist/packages/core/src/utils/formatter.test.js +40 -0
  10. package/dist/packages/core/src/utils/skillManager.js +91 -0
  11. package/dist/packages/core/src/utils/userWhitelistManager.js +41 -36
  12. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +2 -2
  13. package/dist/packages/core/src/web3/aggregator/defiRouter.js +3 -0
  14. package/dist/packages/core/src/web3/skills/bridgeToken.js +4 -0
  15. package/dist/packages/core/src/web3/skills/checkRegistryStatus.js +13 -0
  16. package/dist/packages/core/src/web3/skills/customTx.js +2 -0
  17. package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
  18. package/dist/packages/core/src/web3/skills/getPrice.js +11 -6
  19. package/dist/packages/core/src/web3/skills/manageCustomTokens.js +18 -32
  20. package/dist/packages/core/src/web3/skills/marketAnalysis.js +3 -1
  21. package/dist/packages/core/src/web3/skills/mintNft.js +2 -0
  22. package/dist/packages/core/src/web3/skills/provideLiquidity.js +2 -0
  23. package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
  24. package/dist/packages/core/src/web3/skills/swapToken.js +4 -2
  25. package/dist/packages/core/src/web3/skills/transfer.js +2 -0
  26. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
  27. package/dist/packages/core/src/web3/utils/tokens.js +9 -1
  28. package/package.json +2 -1
  29. package/packages/core/package.json +1 -1
  30. package/packages/core/src/agent/reasoning.ts +12 -1
  31. package/packages/core/src/config/parser.ts +119 -9
  32. package/packages/core/src/gateway/chat.ts +85 -0
  33. package/packages/core/src/gateway/cli.ts +63 -0
  34. package/packages/core/src/gateway/server.ts +132 -60
  35. package/packages/core/src/gateway/setup.ts +39 -27
  36. package/packages/core/src/utils/formatter.test.ts +41 -0
  37. package/packages/core/src/utils/skillManager.ts +98 -0
  38. package/packages/core/src/utils/userWhitelistManager.ts +48 -39
  39. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +2 -2
  40. package/packages/core/src/web3/aggregator/defiRouter.ts +4 -0
  41. package/packages/core/src/web3/skills/bridgeToken.ts +3 -0
  42. package/packages/core/src/web3/skills/checkRegistryStatus.ts +13 -0
  43. package/packages/core/src/web3/skills/customTx.ts +1 -0
  44. package/packages/core/src/web3/skills/defiLending.ts +1 -0
  45. package/packages/core/src/web3/skills/getPrice.ts +11 -6
  46. package/packages/core/src/web3/skills/manageCustomTokens.ts +18 -29
  47. package/packages/core/src/web3/skills/marketAnalysis.ts +2 -1
  48. package/packages/core/src/web3/skills/mintNft.ts +1 -0
  49. package/packages/core/src/web3/skills/provideLiquidity.ts +1 -0
  50. package/packages/core/src/web3/skills/revokeApprovals.ts +1 -0
  51. package/packages/core/src/web3/skills/swapToken.ts +3 -2
  52. package/packages/core/src/web3/skills/transfer.ts +1 -0
  53. package/packages/core/src/web3/skills/yieldVault.ts +1 -0
  54. package/packages/core/src/web3/utils/tokens.ts +9 -1
  55. package/packages/dashboard/dist/assets/{index-DnQrbB4c.css → index-CQNHWZtN.css} +1 -1
  56. package/packages/dashboard/dist/assets/index-Di9x08yk.js +16 -0
  57. package/packages/dashboard/dist/index.html +2 -2
  58. package/packages/dashboard/package.json +1 -1
  59. package/packages/mcp-server/package.json +1 -1
  60. package/packages/policy/package.json +1 -1
  61. package/packages/signer/package.json +1 -1
  62. package/dist/packages/core/src/agent/limitOrderManager.js +0 -124
  63. package/dist/packages/core/src/system/pluginManager.js +0 -91
  64. package/dist/packages/core/src/system/skills/installSkill.js +0 -52
  65. package/dist/packages/core/src/test-all-routers.js +0 -81
  66. package/dist/packages/core/src/test-router.js +0 -38
  67. package/dist/packages/core/src/web3/skills/autonomousDefi.js +0 -191
  68. package/dist/packages/core/src/web3/skills/createWallet.js +0 -34
  69. package/dist/packages/core/src/web3/skills/limitOrder.js +0 -106
  70. package/dist/packages/core/src/web3/utils/protocolRegistry.js +0 -46
  71. package/dist/tsconfig.tsbuildinfo +0 -1
  72. package/packages/core/src/__tests__/reasoning.test.ts +0 -81
  73. package/packages/core/src/__tests__/tokens.test.ts +0 -55
  74. package/packages/core/src/__tests__/web3.test.ts +0 -50
  75. package/packages/core/src/agent/reasoning.d.ts.map +0 -1
  76. package/packages/core/src/config/parser.d.ts.map +0 -1
  77. package/packages/core/src/gateway/cli.d.ts.map +0 -1
  78. package/packages/core/src/memory/logger.d.ts.map +0 -1
  79. package/packages/core/src/test-all-routers.ts +0 -59
  80. package/packages/core/src/test-router.ts +0 -49
  81. package/packages/core/src/web3/config.d.ts.map +0 -1
  82. package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
  83. package/packages/dashboard/dist/assets/index-BAXifdMN.js +0 -16
@@ -52,6 +52,8 @@ const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
52
52
  const getTxHistory_1 = require("../web3/skills/getTxHistory");
53
53
  const checkRegistryStatus_1 = require("../web3/skills/checkRegistryStatus");
54
54
  const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
55
+ const userWhitelistManager_1 = require("../utils/userWhitelistManager");
56
+ const tokens_2 = require("../web3/utils/tokens");
55
57
  // System Skills
56
58
  const browseWeb_1 = require("../system/skills/browseWeb");
57
59
  const executeShell_1 = require("../system/skills/executeShell");
@@ -77,6 +79,8 @@ const episodic_1 = require("../memory/episodic");
77
79
  const reflection_1 = require("../memory/reflection");
78
80
  // Initialize Google Auth
79
81
  (0, googleAuthModule_1.initGoogleAuth)();
82
+ // Synchronize all active skills to config.yaml on startup
83
+ (0, skillManager_1.syncAllSkillsToConfig)();
80
84
  const util_1 = __importDefault(require("util"));
81
85
  // Intercept console.log and console.error
82
86
  const originalLog = console.log;
@@ -307,34 +311,62 @@ app.post('/api/defi-keys', (req, res) => {
307
311
  res.status(500).json({ error: error.message });
308
312
  }
309
313
  });
314
+ const allSkills = [
315
+ getBalance_1.getBalanceToolDefinition,
316
+ transfer_1.transferToolDefinition,
317
+ getPrice_1.getPriceToolDefinition,
318
+ swapToken_1.swapTokenToolDefinition,
319
+ bridgeToken_1.bridgeTokenToolDefinition,
320
+ mintNft_1.mintNftToolDefinition,
321
+ customTx_1.customTxToolDefinition,
322
+ checkAddress_1.checkAddressToolDefinition,
323
+ getMyAddress_1.getMyAddressToolDefinition,
324
+ checkSecurity_1.checkSecurityToolDefinition,
325
+ checkPortfolio_1.checkPortfolioToolDefinition,
326
+ marketAnalysis_1.marketAnalysisToolDefinition,
327
+ manageCustomTokens_1.manageCustomTokensDefinition,
328
+ defiLending_1.aaveSupplyToolDefinition,
329
+ revokeApprovals_2.revokeApprovalToolDefinition,
330
+ yieldVault_1.vaultDepositToolDefinition,
331
+ provideLiquidity_1.provideLiquidityToolDefinition,
332
+ getTxHistory_1.getTxHistoryToolDefinition,
333
+ createLimitOrder_1.createLimitOrderToolDefinition,
334
+ checkRegistryStatus_1.checkRegistryStatusToolDefinition
335
+ ];
336
+ const systemSkills = [
337
+ executeShell_1.runTerminalCommandToolDefinition,
338
+ readFile_1.readLocalFileToolDefinition,
339
+ writeFile_1.writeLocalFileToolDefinition,
340
+ generateExcel_1.generateExcelToolDefinition,
341
+ browseWeb_1.browseWebsiteToolDefinition,
342
+ updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
343
+ analyzeDocument_1.analyzeDocumentToolDefinition,
344
+ searchWeb_1.searchWebToolDefinition,
345
+ googleWorkspace_1.readGmailInboxToolDefinition,
346
+ googleWorkspace_1.listCalendarEventsToolDefinition,
347
+ googleWorkspace_1.appendRowToSheetsToolDefinition,
348
+ googleWorkspace_1.readGoogleDocsToolDefinition,
349
+ googleWorkspace_1.readGoogleFormResponsesToolDefinition,
350
+ editFile_1.editLocalFileToolDefinition,
351
+ gitManager_1.gitManagerToolDefinition,
352
+ xManager_1.xManagerToolDefinition,
353
+ notionWorkspace_1.notionWorkspaceToolDefinition,
354
+ audioTranscribe_1.audioTranscribeToolDefinition,
355
+ summarizeText_1.summarizeTextToolDefinition
356
+ ];
310
357
  app.get('/api/stats', (req, res) => {
311
- res.json(tracker_1.Tracker.getStats());
358
+ const stats = tracker_1.Tracker.getStats();
359
+ const dbPath = (0, paths_1.getPath)('memory.db');
360
+ const activeWeb3 = allSkills.filter(s => (0, skillManager_1.isSkillActive)(s.function.name)).length;
361
+ const activeSystem = systemSkills.filter(s => (0, skillManager_1.isSkillActive)(s.function.name)).length;
362
+ const totalSkills = allSkills.length + systemSkills.length;
363
+ const activeSkills = activeWeb3 + activeSystem;
364
+ res.json({ ...stats, memoryPath: dbPath, totalSkills, activeSkills });
312
365
  });
313
366
  app.get('/api/logs', (req, res) => {
314
367
  res.json(tracker_1.Tracker.getLogs());
315
368
  });
316
369
  app.get('/api/skills', (req, res) => {
317
- const allSkills = [
318
- getBalance_1.getBalanceToolDefinition,
319
- transfer_1.transferToolDefinition,
320
- getPrice_1.getPriceToolDefinition,
321
- swapToken_1.swapTokenToolDefinition,
322
- bridgeToken_1.bridgeTokenToolDefinition,
323
- mintNft_1.mintNftToolDefinition,
324
- customTx_1.customTxToolDefinition,
325
- checkAddress_1.checkAddressToolDefinition,
326
- getMyAddress_1.getMyAddressToolDefinition,
327
- checkSecurity_1.checkSecurityToolDefinition,
328
- checkPortfolio_1.checkPortfolioToolDefinition,
329
- marketAnalysis_1.marketAnalysisToolDefinition,
330
- manageCustomTokens_1.manageCustomTokensDefinition,
331
- defiLending_1.aaveSupplyToolDefinition,
332
- revokeApprovals_2.revokeApprovalToolDefinition,
333
- yieldVault_1.vaultDepositToolDefinition,
334
- provideLiquidity_1.provideLiquidityToolDefinition,
335
- getTxHistory_1.getTxHistoryToolDefinition,
336
- createLimitOrder_1.createLimitOrderToolDefinition
337
- ];
338
370
  const skillsWithStatus = allSkills.map(skill => ({
339
371
  ...skill,
340
372
  isActive: (0, skillManager_1.isSkillActive)(skill.function.name)
@@ -342,27 +374,6 @@ app.get('/api/skills', (req, res) => {
342
374
  res.json(skillsWithStatus);
343
375
  });
344
376
  app.get('/api/skills/system', (req, res) => {
345
- const systemSkills = [
346
- executeShell_1.runTerminalCommandToolDefinition,
347
- readFile_1.readLocalFileToolDefinition,
348
- writeFile_1.writeLocalFileToolDefinition,
349
- generateExcel_1.generateExcelToolDefinition,
350
- browseWeb_1.browseWebsiteToolDefinition,
351
- updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
352
- analyzeDocument_1.analyzeDocumentToolDefinition,
353
- searchWeb_1.searchWebToolDefinition,
354
- googleWorkspace_1.readGmailInboxToolDefinition,
355
- googleWorkspace_1.listCalendarEventsToolDefinition,
356
- googleWorkspace_1.appendRowToSheetsToolDefinition,
357
- googleWorkspace_1.readGoogleDocsToolDefinition,
358
- googleWorkspace_1.readGoogleFormResponsesToolDefinition,
359
- editFile_1.editLocalFileToolDefinition,
360
- gitManager_1.gitManagerToolDefinition,
361
- xManager_1.xManagerToolDefinition,
362
- notionWorkspace_1.notionWorkspaceToolDefinition,
363
- audioTranscribe_1.audioTranscribeToolDefinition,
364
- summarizeText_1.summarizeTextToolDefinition
365
- ];
366
377
  const skillsWithStatus = systemSkills.map(skill => ({
367
378
  ...skill,
368
379
  isActive: (0, skillManager_1.isSkillActive)(skill.function.name)
@@ -377,6 +388,41 @@ app.post('/api/skills/toggle', (req, res) => {
377
388
  (0, skillManager_1.toggleSkill)(skillName, active);
378
389
  res.json({ success: true, skillName, active });
379
390
  });
391
+ // Portfolio Whitelist Routes
392
+ app.get('/api/portfolio/whitelist', async (req, res) => {
393
+ const whitelist = (0, userWhitelistManager_1.getUserWhitelist)();
394
+ res.json(whitelist);
395
+ });
396
+ app.post('/api/portfolio/whitelist', async (req, res) => {
397
+ const { walletAddress, chainName, tokenAddress, symbol, decimals } = req.body;
398
+ if (!walletAddress || !chainName || !tokenAddress) {
399
+ return res.status(400).json({ error: 'Missing required fields' });
400
+ }
401
+ await (0, userWhitelistManager_1.saveTokenToWhitelist)(walletAddress, chainName, tokenAddress, 'manual', symbol, decimals);
402
+ res.json({ success: true });
403
+ });
404
+ app.delete('/api/portfolio/whitelist', (req, res) => {
405
+ const { walletAddress, chainName, tokenAddress } = req.body;
406
+ if (!walletAddress || !chainName || !tokenAddress) {
407
+ return res.status(400).json({ error: 'Missing required fields' });
408
+ }
409
+ (0, userWhitelistManager_1.removeTokenFromWhitelist)(walletAddress, chainName, tokenAddress);
410
+ res.json({ success: true });
411
+ });
412
+ app.get('/api/portfolio/token-metadata', async (req, res) => {
413
+ const { chain, address } = req.query;
414
+ if (!chain || !address || typeof address !== 'string') {
415
+ return res.status(400).json({ error: 'Missing chain or address' });
416
+ }
417
+ try {
418
+ const client = (0, config_1.getPublicClient)(chain);
419
+ const metadata = await (0, tokens_2.getTokenMetadata)(client, address);
420
+ res.json(metadata);
421
+ }
422
+ catch (err) {
423
+ res.status(500).json({ error: err.message });
424
+ }
425
+ });
380
426
  // Google Workspace Auth Routes
381
427
  app.get('/api/auth/google/url', (req, res) => {
382
428
  const url = (0, googleAuthModule_1.getAuthUrl)();
@@ -563,6 +609,7 @@ app.post('/api/transactions/:id/reject', async (req, res) => {
563
609
  let cachedTrending = null;
564
610
  let lastTrendingFetch = 0;
565
611
  let cachedPrices = {};
612
+ let cachedPriceChanges = {};
566
613
  let lastPricesFetch = 0;
567
614
  app.get('/api/trending', async (req, res) => {
568
615
  const now = Date.now();
@@ -591,17 +638,20 @@ app.get('/api/trending', async (req, res) => {
591
638
  res.status(500).json({ error: err.message });
592
639
  }
593
640
  });
641
+ app.get('/api/wallet', async (req, res) => {
642
+ try {
643
+ const userAddress = await (0, config_1.getAddress)();
644
+ res.json({ address: userAddress });
645
+ }
646
+ catch (error) {
647
+ res.status(500).json({ error: error.message });
648
+ }
649
+ });
594
650
  app.get('/api/portfolio', async (req, res) => {
595
651
  try {
596
652
  const userAddress = await (0, config_1.getAddress)();
597
- const customTokensPath = path_1.default.join((0, paths_1.getPath)('custom_tokens.json'));
598
- let customTokens = {};
599
- if (fs_1.default.existsSync(customTokensPath)) {
600
- try {
601
- customTokens = JSON.parse(fs_1.default.readFileSync(customTokensPath, 'utf8'));
602
- }
603
- catch (e) { }
604
- }
653
+ const whitelist = (0, userWhitelistManager_1.getUserWhitelist)();
654
+ const userCustomTokens = whitelist[userAddress.toLowerCase()] || [];
605
655
  const portfolio = {};
606
656
  await Promise.all(config_1.SUPPORTED_CHAIN_NAMES.map(async (chainName) => {
607
657
  portfolio[chainName] = [];
@@ -618,11 +668,14 @@ app.get('/api/portfolio', async (req, res) => {
618
668
  isNative: true
619
669
  });
620
670
  }
621
- // 2. Combine TOKEN_MAP and customTokens for this chain
671
+ // 2. Combine TOKEN_MAP and YAML whitelist for this chain
622
672
  const tokensToQuery = { ...(tokens_1.TOKEN_MAP[chainName] || {}) };
623
- if (customTokens[chainName]) {
624
- Object.assign(tokensToQuery, customTokens[chainName]);
625
- }
673
+ // Inject whitelisted tokens
674
+ userCustomTokens.forEach(t => {
675
+ if (t.chainName === chainName && t.symbol && t.address) {
676
+ tokensToQuery[t.symbol.toUpperCase()] = t.address;
677
+ }
678
+ });
626
679
  // 3. Query all ERC-20 balances in parallel
627
680
  await Promise.all(Object.entries(tokensToQuery).map(async ([symbol, address]) => {
628
681
  if (address === '0x0000000000000000000000000000000000000000')
@@ -640,7 +693,8 @@ app.get('/api/portfolio', async (req, res) => {
640
693
  functionName: 'decimals'
641
694
  });
642
695
  const [bal, decimals] = await Promise.all([balPromise, decPromise]);
643
- if (bal > 0n) {
696
+ const isCustom = userCustomTokens.some(t => t.chainName === chainName && t.address === address);
697
+ if (bal > 0n || isCustom) {
644
698
  portfolio[chainName].push({
645
699
  symbol,
646
700
  address,
@@ -681,9 +735,11 @@ app.get('/api/portfolio', async (req, res) => {
681
735
  const uniqueAddrs = Array.from(addressesToFetch);
682
736
  const now = Date.now();
683
737
  let priceMap = cachedPrices;
738
+ let changeMap = cachedPriceChanges;
684
739
  if (uniqueAddrs.length > 0 && now - lastPricesFetch > 2 * 60 * 1000) {
685
740
  try {
686
741
  const newPrices = {};
742
+ const newChanges = {};
687
743
  await Promise.all(uniqueAddrs.map(async (addr) => {
688
744
  try {
689
745
  const res = await (0, httpClient_1.safeFetch)(`https://api.dexscreener.com/latest/dex/tokens/${addr}`);
@@ -706,9 +762,11 @@ app.get('/api/portfolio', async (req, res) => {
706
762
  const quoteAddr = bestPair.quoteToken?.address?.toLowerCase();
707
763
  if (baseAddr === addr) {
708
764
  newPrices[addr] = parseFloat(bestPair.priceUsd);
765
+ newChanges[addr] = bestPair.priceChange?.h24 || 0;
709
766
  }
710
767
  else if (quoteAddr === addr && bestPair.priceNative && parseFloat(bestPair.priceNative) > 0) {
711
768
  newPrices[addr] = parseFloat(bestPair.priceUsd) / parseFloat(bestPair.priceNative);
769
+ newChanges[addr] = bestPair.priceChange?.h24 || 0;
712
770
  }
713
771
  }
714
772
  }
@@ -720,7 +778,9 @@ app.get('/api/portfolio', async (req, res) => {
720
778
  }));
721
779
  console.log('DexScreener Fetched Prices:', newPrices);
722
780
  cachedPrices = { ...cachedPrices, ...newPrices };
781
+ cachedPriceChanges = { ...cachedPriceChanges, ...newChanges };
723
782
  priceMap = cachedPrices;
783
+ changeMap = cachedPriceChanges;
724
784
  lastPricesFetch = now;
725
785
  }
726
786
  catch (e) {
@@ -735,6 +795,7 @@ app.get('/api/portfolio', async (req, res) => {
735
795
  lookupAddr = ((tokens_1.TOKEN_MAP[chain]?.[wToken]) || '').toLowerCase();
736
796
  }
737
797
  t.priceUsd = priceMap[lookupAddr] || 0;
798
+ t.priceChange24h = changeMap[lookupAddr] || 0;
738
799
  }
739
800
  }
740
801
  res.json(portfolio);
@@ -10,22 +10,6 @@ const picocolors_1 = __importDefault(require("picocolors"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const paths_1 = require("../config/paths");
12
12
  const parser_1 = require("../config/parser");
13
- const crypto_1 = __importDefault(require("crypto"));
14
- function encryptKey(privateKey, password) {
15
- const salt = crypto_1.default.randomBytes(16);
16
- const key = crypto_1.default.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
17
- const iv = crypto_1.default.randomBytes(12);
18
- const cipher = crypto_1.default.createCipheriv('aes-256-gcm', key, iv);
19
- let encrypted = cipher.update(privateKey, 'utf8', 'hex');
20
- encrypted += cipher.final('hex');
21
- const authTag = cipher.getAuthTag().toString('hex');
22
- return {
23
- salt: salt.toString('hex'),
24
- iv: iv.toString('hex'),
25
- authTag,
26
- encryptedData: encrypted
27
- };
28
- }
29
13
  const accounts_1 = require("viem/accounts");
30
14
  async function runSetupWizard() {
31
15
  console.clear();
@@ -274,9 +258,9 @@ Provider: ${config.llm.provider}`;
274
258
  privateKey = '0x' + Buffer.from(account.getHdKey().privateKey).toString('hex');
275
259
  prompts_1.log.success('New Wallet Generated!');
276
260
  prompts_1.log.info(`Address: ${account.address}`);
277
- prompts_1.log.info(`Private Key: ${privateKey}`);
261
+ prompts_1.log.info(`Private Key: [REDACTED - Saved securely to vault]`);
278
262
  prompts_1.log.info(`Seed Phrase (Mnemonic): ${seedPhrase}`);
279
- prompts_1.log.warn('IMPORTANT: Write down these 12 words (or the Private Key) NOW! This is your ONLY backup. The credentials have been securely injected into your local OS vault.');
263
+ prompts_1.log.warn('IMPORTANT: Write down these 12 words NOW! This is your ONLY backup. The credentials have been securely injected into your local OS vault.');
280
264
  }
281
265
  }
282
266
  // --- OS SKILLS ---
@@ -343,16 +327,26 @@ Provider: ${config.llm.provider}`;
343
327
  }));
344
328
  if ((0, prompts_1.isCancel)(telegramToken))
345
329
  return process.exit(0);
346
- if (telegramToken && !authorizedChatId) {
330
+ if (telegramToken && telegramToken.trim() !== '') {
331
+ authorizedChatId = undefined;
332
+ }
333
+ const activeToken = telegramToken || config.integrations?.telegram?.bot_token;
334
+ if (activeToken && !authorizedChatId) {
347
335
  const s = (0, prompts_1.spinner)();
348
336
  const pin = Math.floor(100000 + Math.random() * 900000).toString();
349
337
  (0, prompts_1.note)(picocolors_1.default.cyan(`1. Open Telegram and search for your Bot.\n2. Send this exact message to your bot:\n\n /auth ${pin}\n\nWaiting for your message...`), 'Telegram Pairing Required');
350
338
  s.start(`Waiting for /auth ${pin} on Telegram...`);
339
+ let bot = null;
351
340
  try {
352
341
  const { Telegraf } = require('telegraf');
353
- const bot = new Telegraf(telegramToken);
342
+ bot = new Telegraf(activeToken);
354
343
  let paired = false;
344
+ let failedAttempts = {};
355
345
  bot.command('auth', (ctx) => {
346
+ const chatId = ctx.chat.id.toString();
347
+ if (failedAttempts[chatId] >= 5) {
348
+ return ctx.reply('❌ Too many failed attempts. You are locked out.');
349
+ }
356
350
  const text = ctx.message.text.split(' ');
357
351
  if (text[1] === pin) {
358
352
  authorizedChatId = ctx.chat.id;
@@ -361,18 +355,38 @@ Provider: ${config.llm.provider}`;
361
355
  bot.stop();
362
356
  }
363
357
  else {
358
+ failedAttempts[chatId] = (failedAttempts[chatId] || 0) + 1;
364
359
  ctx.reply('❌ Invalid PIN.');
365
360
  }
366
361
  });
367
362
  bot.launch();
368
363
  // Wait until paired
369
- while (!paired) {
364
+ let attempts = 0;
365
+ while (!paired && attempts < 120) {
370
366
  await new Promise(r => setTimeout(r, 1000));
367
+ attempts++;
368
+ }
369
+ if (!paired) {
370
+ s.stop('Timeout waiting for Telegram pairing. Setup will continue, but Telegram integration is disabled.');
371
+ try {
372
+ bot.stop();
373
+ }
374
+ catch (e) { }
375
+ authorizedChatId = undefined;
376
+ telegramToken = '';
377
+ }
378
+ else {
379
+ s.stop(`Bot successfully paired with Chat ID: ${authorizedChatId}`);
371
380
  }
372
- s.stop(`Bot successfully paired with Chat ID: ${authorizedChatId}`);
373
381
  }
374
382
  catch (err) {
375
383
  s.stop(`Failed to start bot listener: ${err.message}. You can pair it later.`);
384
+ // Try to stop the bot if it was initialized before the error (L17)
385
+ try {
386
+ if (bot)
387
+ bot.stop();
388
+ }
389
+ catch (e) { }
376
390
  }
377
391
  }
378
392
  }
@@ -424,6 +438,9 @@ Provider: ${config.llm.provider}`;
424
438
  if (authorizedChatId) {
425
439
  config.integrations.telegram.authorized_chat_id = authorizedChatId;
426
440
  }
441
+ else if (config.integrations.telegram) {
442
+ delete config.integrations.telegram.authorized_chat_id;
443
+ }
427
444
  (0, parser_1.saveConfig)(config);
428
445
  // Sync disabled_skills.json based on user selection
429
446
  const allWeb3Skills = [
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const formatter_1 = require("./formatter");
5
+ (0, vitest_1.describe)('Formatter Utilities', () => {
6
+ (0, vitest_1.it)('should format transaction success correctly for swap', () => {
7
+ const tx = {
8
+ id: 'test-1',
9
+ type: 'swap',
10
+ chainName: 'ethereum',
11
+ status: 'pending',
12
+ nonce: "0",
13
+ details: {
14
+ fromToken: 'eth',
15
+ toToken: 'usdc',
16
+ amountStr: '1'
17
+ },
18
+ createdAt: Date.now()
19
+ };
20
+ const rawResult = '{"txHash":"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"}';
21
+ const output = (0, formatter_1.formatTransactionSuccess)(tx, rawResult);
22
+ (0, vitest_1.expect)(output).toContain('Network:** Ethereum');
23
+ (0, vitest_1.expect)(output).toContain('Action:** Swapped 1 ETH to USDC');
24
+ (0, vitest_1.expect)(output).toContain('Tx Hash:** `0x1234...cdef`');
25
+ });
26
+ (0, vitest_1.it)('should format transaction error correctly', () => {
27
+ const tx = {
28
+ id: 'test-2',
29
+ type: 'bridge',
30
+ chainName: 'base_sepolia',
31
+ status: 'failed',
32
+ nonce: "0",
33
+ details: {},
34
+ createdAt: Date.now()
35
+ };
36
+ const output = (0, formatter_1.formatTransactionError)(tx, 'Insufficient funds');
37
+ (0, vitest_1.expect)(output).toContain('Transaction Failed (Base Sepolia)');
38
+ (0, vitest_1.expect)(output).toContain('Error: Insufficient funds');
39
+ });
40
+ });
@@ -6,9 +6,54 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getDisabledSkills = getDisabledSkills;
7
7
  exports.toggleSkill = toggleSkill;
8
8
  exports.isSkillActive = isSkillActive;
9
+ exports.syncAllSkillsToConfig = syncAllSkillsToConfig;
9
10
  const fs_1 = __importDefault(require("fs"));
10
11
  const paths_1 = require("../config/paths");
12
+ const parser_1 = require("../config/parser");
11
13
  let disabledSkillsCache = null;
14
+ const reverseSkillMapping = {
15
+ // OS Skills
16
+ 'read_local_file': { category: 'os', name: 'readFile' },
17
+ 'write_local_file': { category: 'os', name: 'writeFile' },
18
+ 'edit_local_file': { category: 'os', name: 'editFile' },
19
+ 'generate_excel_file': { category: 'os', name: 'generateExcel' },
20
+ 'analyze_document': { category: 'os', name: 'analyzeDocument' },
21
+ 'run_terminal_command': { category: 'os', name: 'executeShell' },
22
+ 'browse_website': { category: 'os', name: 'browseWeb' },
23
+ 'search_web': { category: 'os', name: 'searchWeb' },
24
+ 'read_gmail_inbox': { category: 'os', name: 'readGmail' },
25
+ 'list_calendar_events': { category: 'os', name: 'listCalendar' },
26
+ 'append_row_to_sheets': { category: 'os', name: 'appendSheets' },
27
+ 'read_google_docs': { category: 'os', name: 'readDocs' },
28
+ 'read_google_form_responses': { category: 'os', name: 'readForms' },
29
+ 'execute_git_command': { category: 'os', name: 'gitManager' },
30
+ 'manage_twitter': { category: 'os', name: 'xManager' },
31
+ 'manage_notion': { category: 'os', name: 'notionWorkspace' },
32
+ 'transcribe_audio': { category: 'os', name: 'audioTranscribe' },
33
+ 'summarize_text': { category: 'os', name: 'summarizeText' },
34
+ 'update_security_policy': { category: 'os', name: 'updateSecurityPolicy' },
35
+ // Web3 Skills
36
+ 'transfer_token': { category: 'web3', name: 'transfer' },
37
+ 'transfer_native': { category: 'web3', name: 'transfer' },
38
+ 'swap_token': { category: 'web3', name: 'swap' },
39
+ 'bridge_token': { category: 'web3', name: 'bridge' },
40
+ 'mint_nft': { category: 'web3', name: 'mintNft' },
41
+ 'custom_tx': { category: 'web3', name: 'customTx' },
42
+ 'check_address': { category: 'web3', name: 'checkAddress' },
43
+ 'get_my_address': { category: 'web3', name: 'getMyAddress' },
44
+ 'check_token_security': { category: 'web3', name: 'checkSecurity' },
45
+ 'check_portfolio': { category: 'web3', name: 'checkPortfolio' },
46
+ 'analyze_market': { category: 'web3', name: 'marketAnalysis' },
47
+ 'manage_custom_tokens': { category: 'web3', name: 'manageCustomTokens' },
48
+ 'get_price': { category: 'web3', name: 'getPrice' },
49
+ 'supply_aave': { category: 'web3', name: 'aaveSupply' },
50
+ 'revoke_approval': { category: 'web3', name: 'revokeApproval' },
51
+ 'deposit_yield_vault': { category: 'web3', name: 'vaultDeposit' },
52
+ 'provide_liquidity_v3': { category: 'web3', name: 'provideLiquidity' },
53
+ 'get_tx_history': { category: 'web3', name: 'getTxHistory' },
54
+ 'check_registry_status': { category: 'web3', name: 'checkRegistryStatus' },
55
+ 'create_limit_order': { category: 'web3', name: 'createLimitOrder' }
56
+ };
12
57
  function getDisabledSkillsFile() {
13
58
  return (0, paths_1.getPath)('disabled_skills.json');
14
59
  }
@@ -43,8 +88,54 @@ function toggleSkill(skillName, active) {
43
88
  }
44
89
  disabledSkillsCache = Array.from(current);
45
90
  fs_1.default.writeFileSync(getDisabledSkillsFile(), JSON.stringify(disabledSkillsCache, null, 2));
91
+ // Sync to config.yaml
92
+ const mapping = reverseSkillMapping[skillName];
93
+ if (mapping) {
94
+ const config = (0, parser_1.loadConfig)();
95
+ if (!config.skills)
96
+ config.skills = { web3: [], os: [] };
97
+ const categoryArray = mapping.category === 'web3' ? config.skills.web3 : config.skills.os;
98
+ if (active && !categoryArray.includes(mapping.name)) {
99
+ categoryArray.push(mapping.name);
100
+ }
101
+ else if (!active) {
102
+ const index = categoryArray.indexOf(mapping.name);
103
+ if (index !== -1) {
104
+ categoryArray.splice(index, 1);
105
+ }
106
+ }
107
+ (0, parser_1.saveConfig)(config);
108
+ }
46
109
  }
47
110
  function isSkillActive(skillName) {
48
111
  const disabled = getDisabledSkills();
49
112
  return !disabled.includes(skillName);
50
113
  }
114
+ function syncAllSkillsToConfig() {
115
+ const config = (0, parser_1.loadConfig)();
116
+ if (!config.skills)
117
+ config.skills = { web3: [], os: [] };
118
+ const activeWeb3 = new Set();
119
+ const activeOs = new Set();
120
+ for (const [skillName, mapping] of Object.entries(reverseSkillMapping)) {
121
+ if (isSkillActive(skillName)) {
122
+ if (mapping.category === 'web3') {
123
+ activeWeb3.add(mapping.name);
124
+ }
125
+ else {
126
+ activeOs.add(mapping.name);
127
+ }
128
+ }
129
+ }
130
+ // Check if arrays are different to avoid unnecessary saves
131
+ const currentWeb3 = config.skills.web3 || [];
132
+ const currentOs = config.skills.os || [];
133
+ const web3Changed = currentWeb3.length !== activeWeb3.size || !currentWeb3.every(s => activeWeb3.has(s));
134
+ const osChanged = currentOs.length !== activeOs.size || !currentOs.every(s => activeOs.has(s));
135
+ if (web3Changed || osChanged) {
136
+ config.skills.web3 = Array.from(activeWeb3);
137
+ config.skills.os = Array.from(activeOs);
138
+ (0, parser_1.saveConfig)(config);
139
+ console.log('[SkillManager] Automatically synchronized active skills to config.yaml');
140
+ }
141
+ }
@@ -10,70 +10,75 @@ exports.getUserTokens = getUserTokens;
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const os_1 = __importDefault(require("os"));
13
- const WHITELIST_FILE_PATH = path_1.default.join(os_1.default.homedir(), '.nyxora', 'user_whitelist.json');
13
+ const yaml_1 = __importDefault(require("yaml"));
14
+ const config_1 = require("../web3/config");
15
+ const tokens_1 = require("../web3/utils/tokens");
16
+ const WHITELIST_FILE_PATH = path_1.default.join(os_1.default.homedir(), '.nyxora', 'user_whitelist.yaml');
14
17
  function getUserWhitelist() {
15
18
  try {
16
19
  if (!fs_1.default.existsSync(WHITELIST_FILE_PATH)) {
20
+ // Try migrating from JSON if YAML doesn't exist yet
21
+ const oldJsonPath = path_1.default.join(os_1.default.homedir(), '.nyxora', 'user_whitelist.json');
22
+ if (fs_1.default.existsSync(oldJsonPath)) {
23
+ const data = fs_1.default.readFileSync(oldJsonPath, 'utf-8');
24
+ const parsed = JSON.parse(data);
25
+ fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(parsed), 'utf-8');
26
+ // Rename old file so it doesn't get used again
27
+ fs_1.default.renameSync(oldJsonPath, `${oldJsonPath}.bak`);
28
+ return parsed;
29
+ }
17
30
  return {};
18
31
  }
19
32
  const data = fs_1.default.readFileSync(WHITELIST_FILE_PATH, 'utf-8');
20
- const parsed = JSON.parse(data);
21
- // Auto-migrate legacy format
22
- let migrated = false;
23
- for (const addr in parsed) {
24
- if (parsed[addr] && !Array.isArray(parsed[addr])) {
25
- const newArray = [];
26
- for (const chain in parsed[addr]) {
27
- const tokens = parsed[addr][chain];
28
- if (Array.isArray(tokens)) {
29
- for (const t of tokens) {
30
- newArray.push({
31
- chainName: chain,
32
- address: typeof t === 'string' ? t.toLowerCase() : (t.address || '').toLowerCase(),
33
- source: 'manual',
34
- lastSeen: Date.now()
35
- });
36
- }
37
- }
38
- }
39
- parsed[addr] = newArray;
40
- migrated = true;
41
- }
42
- }
43
- if (migrated) {
44
- fs_1.default.writeFileSync(WHITELIST_FILE_PATH, JSON.stringify(parsed, null, 2), 'utf-8');
45
- }
46
- return parsed;
33
+ return yaml_1.default.parse(data) || {};
47
34
  }
48
35
  catch (err) {
49
- console.error('[Whitelist] Error reading user_whitelist.json', err);
36
+ console.error('[Whitelist] Error reading user_whitelist.yaml', err);
50
37
  return {};
51
38
  }
52
39
  }
53
- function saveTokenToWhitelist(walletAddress, chainName, tokenAddress, source = 'manual') {
40
+ async function saveTokenToWhitelist(walletAddress, chainName, tokenAddress, source = 'manual', symbol, decimals) {
54
41
  try {
55
42
  const whitelist = getUserWhitelist();
56
43
  const addr = walletAddress.toLowerCase();
57
44
  const tokenAddr = tokenAddress.toLowerCase();
45
+ // Auto-fetch metadata if not provided
46
+ if (!symbol || decimals === undefined) {
47
+ try {
48
+ const client = (0, config_1.getPublicClient)(chainName);
49
+ const metadata = await (0, tokens_1.getTokenMetadata)(client, tokenAddr);
50
+ symbol = metadata.symbol;
51
+ decimals = metadata.decimals;
52
+ }
53
+ catch (err) {
54
+ console.warn(`[Whitelist] Could not fetch metadata for ${tokenAddr} on ${chainName}`, err);
55
+ }
56
+ }
58
57
  if (!whitelist[addr])
59
58
  whitelist[addr] = [];
60
59
  const existingIndex = whitelist[addr].findIndex(t => t.chainName === chainName && t.address === tokenAddr);
61
60
  if (existingIndex >= 0) {
62
61
  whitelist[addr][existingIndex].lastSeen = Date.now();
62
+ if (symbol)
63
+ whitelist[addr][existingIndex].symbol = symbol;
64
+ if (decimals !== undefined)
65
+ whitelist[addr][existingIndex].decimals = decimals;
63
66
  }
64
67
  else {
65
68
  whitelist[addr].push({
66
69
  chainName,
67
70
  address: tokenAddr,
71
+ symbol,
72
+ decimals,
68
73
  source,
69
74
  lastSeen: Date.now()
70
75
  });
71
- console.log(`[Whitelist] Added ${tokenAddr} to ${chainName} for ${addr} via ${source}`);
76
+ console.log(`[Whitelist] Added ${symbol || tokenAddr} to ${chainName} for ${addr} via ${source}`);
72
77
  }
73
- fs_1.default.writeFileSync(WHITELIST_FILE_PATH, JSON.stringify(whitelist, null, 2), 'utf-8');
78
+ fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(whitelist), 'utf-8');
74
79
  }
75
80
  catch (err) {
76
- console.error('[Whitelist] Error saving token to user_whitelist.json', err);
81
+ console.error('[Whitelist] Error saving token to user_whitelist.yaml', err);
77
82
  }
78
83
  }
79
84
  function removeTokenFromWhitelist(walletAddress, chainName, tokenAddress) {
@@ -86,12 +91,12 @@ function removeTokenFromWhitelist(walletAddress, chainName, tokenAddress) {
86
91
  const initialLength = whitelist[addr].length;
87
92
  whitelist[addr] = whitelist[addr].filter(t => !(t.chainName === chainName && t.address === tokenAddr));
88
93
  if (whitelist[addr].length !== initialLength) {
89
- fs_1.default.writeFileSync(WHITELIST_FILE_PATH, JSON.stringify(whitelist, null, 2), 'utf-8');
90
- console.log(`[Whitelist] Removed garbage token ${tokenAddr} on ${chainName} for ${addr}`);
94
+ fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(whitelist), 'utf-8');
95
+ console.log(`[Whitelist] Removed token ${tokenAddr} on ${chainName} for ${addr}`);
91
96
  }
92
97
  }
93
98
  catch (err) {
94
- console.error('[Whitelist] Error removing token from user_whitelist.json', err);
99
+ console.error('[Whitelist] Error removing token from user_whitelist.yaml', err);
95
100
  }
96
101
  }
97
102
  function getUserTokens(walletAddress, chainName) {