nyxora 26.6.20 → 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 (80) hide show
  1. package/README.md +18 -1
  2. package/bin/nyxora.mjs +32 -0
  3. package/dist/packages/core/src/agent/reasoning.js +10 -0
  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 +100 -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/manageCustomTokens.js +18 -32
  19. package/dist/packages/core/src/web3/skills/marketAnalysis.js +3 -1
  20. package/dist/packages/core/src/web3/skills/mintNft.js +2 -0
  21. package/dist/packages/core/src/web3/skills/provideLiquidity.js +2 -0
  22. package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
  23. package/dist/packages/core/src/web3/skills/swapToken.js +4 -2
  24. package/dist/packages/core/src/web3/skills/transfer.js +2 -0
  25. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
  26. package/dist/packages/core/src/web3/utils/tokens.js +9 -1
  27. package/package.json +2 -1
  28. package/packages/core/package.json +1 -1
  29. package/packages/core/src/agent/reasoning.ts +11 -0
  30. package/packages/core/src/config/parser.ts +119 -9
  31. package/packages/core/src/gateway/chat.ts +85 -0
  32. package/packages/core/src/gateway/cli.ts +63 -0
  33. package/packages/core/src/gateway/server.ts +115 -60
  34. package/packages/core/src/gateway/setup.ts +39 -27
  35. package/packages/core/src/utils/formatter.test.ts +41 -0
  36. package/packages/core/src/utils/skillManager.ts +98 -0
  37. package/packages/core/src/utils/userWhitelistManager.ts +48 -39
  38. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +2 -2
  39. package/packages/core/src/web3/aggregator/defiRouter.ts +4 -0
  40. package/packages/core/src/web3/skills/bridgeToken.ts +3 -0
  41. package/packages/core/src/web3/skills/checkRegistryStatus.ts +13 -0
  42. package/packages/core/src/web3/skills/customTx.ts +1 -0
  43. package/packages/core/src/web3/skills/defiLending.ts +1 -0
  44. package/packages/core/src/web3/skills/manageCustomTokens.ts +18 -29
  45. package/packages/core/src/web3/skills/marketAnalysis.ts +2 -1
  46. package/packages/core/src/web3/skills/mintNft.ts +1 -0
  47. package/packages/core/src/web3/skills/provideLiquidity.ts +1 -0
  48. package/packages/core/src/web3/skills/revokeApprovals.ts +1 -0
  49. package/packages/core/src/web3/skills/swapToken.ts +3 -2
  50. package/packages/core/src/web3/skills/transfer.ts +1 -0
  51. package/packages/core/src/web3/skills/yieldVault.ts +1 -0
  52. package/packages/core/src/web3/utils/tokens.ts +9 -1
  53. package/packages/dashboard/dist/assets/index-Di9x08yk.js +16 -0
  54. package/packages/dashboard/dist/index.html +1 -1
  55. package/packages/dashboard/package.json +1 -1
  56. package/packages/mcp-server/package.json +1 -1
  57. package/packages/policy/package.json +1 -1
  58. package/packages/signer/package.json +1 -1
  59. package/dist/packages/core/src/agent/limitOrderManager.js +0 -124
  60. package/dist/packages/core/src/system/pluginManager.js +0 -91
  61. package/dist/packages/core/src/system/skills/installSkill.js +0 -52
  62. package/dist/packages/core/src/test-all-routers.js +0 -81
  63. package/dist/packages/core/src/test-router.js +0 -38
  64. package/dist/packages/core/src/web3/skills/autonomousDefi.js +0 -191
  65. package/dist/packages/core/src/web3/skills/createWallet.js +0 -34
  66. package/dist/packages/core/src/web3/skills/limitOrder.js +0 -106
  67. package/dist/packages/core/src/web3/utils/protocolRegistry.js +0 -46
  68. package/dist/tsconfig.tsbuildinfo +0 -1
  69. package/packages/core/src/__tests__/reasoning.test.ts +0 -81
  70. package/packages/core/src/__tests__/tokens.test.ts +0 -55
  71. package/packages/core/src/__tests__/web3.test.ts +0 -50
  72. package/packages/core/src/agent/reasoning.d.ts.map +0 -1
  73. package/packages/core/src/config/parser.d.ts.map +0 -1
  74. package/packages/core/src/gateway/cli.d.ts.map +0 -1
  75. package/packages/core/src/memory/logger.d.ts.map +0 -1
  76. package/packages/core/src/test-all-routers.ts +0 -59
  77. package/packages/core/src/test-router.ts +0 -49
  78. package/packages/core/src/web3/config.d.ts.map +0 -1
  79. package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
  80. package/packages/dashboard/dist/assets/index-O2m42q4p.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)();
@@ -604,14 +650,8 @@ app.get('/api/wallet', async (req, res) => {
604
650
  app.get('/api/portfolio', async (req, res) => {
605
651
  try {
606
652
  const userAddress = await (0, config_1.getAddress)();
607
- const customTokensPath = path_1.default.join((0, paths_1.getPath)('custom_tokens.json'));
608
- let customTokens = {};
609
- if (fs_1.default.existsSync(customTokensPath)) {
610
- try {
611
- customTokens = JSON.parse(fs_1.default.readFileSync(customTokensPath, 'utf8'));
612
- }
613
- catch (e) { }
614
- }
653
+ const whitelist = (0, userWhitelistManager_1.getUserWhitelist)();
654
+ const userCustomTokens = whitelist[userAddress.toLowerCase()] || [];
615
655
  const portfolio = {};
616
656
  await Promise.all(config_1.SUPPORTED_CHAIN_NAMES.map(async (chainName) => {
617
657
  portfolio[chainName] = [];
@@ -628,11 +668,14 @@ app.get('/api/portfolio', async (req, res) => {
628
668
  isNative: true
629
669
  });
630
670
  }
631
- // 2. Combine TOKEN_MAP and customTokens for this chain
671
+ // 2. Combine TOKEN_MAP and YAML whitelist for this chain
632
672
  const tokensToQuery = { ...(tokens_1.TOKEN_MAP[chainName] || {}) };
633
- if (customTokens[chainName]) {
634
- Object.assign(tokensToQuery, customTokens[chainName]);
635
- }
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
+ });
636
679
  // 3. Query all ERC-20 balances in parallel
637
680
  await Promise.all(Object.entries(tokensToQuery).map(async ([symbol, address]) => {
638
681
  if (address === '0x0000000000000000000000000000000000000000')
@@ -650,7 +693,8 @@ app.get('/api/portfolio', async (req, res) => {
650
693
  functionName: 'decimals'
651
694
  });
652
695
  const [bal, decimals] = await Promise.all([balPromise, decPromise]);
653
- if (bal > 0n) {
696
+ const isCustom = userCustomTokens.some(t => t.chainName === chainName && t.address === address);
697
+ if (bal > 0n || isCustom) {
654
698
  portfolio[chainName].push({
655
699
  symbol,
656
700
  address,
@@ -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) {
@@ -50,8 +50,8 @@ async function fetchMainnetBestRoute(fromChain, toChain, fromToken, toToken, amo
50
50
  if (isProviderHealthy('openocean')) {
51
51
  promises.push(fetchOpenOcean(fromChain, toChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance, keys.openocean_key));
52
52
  }
53
- // Provider 6: KyberSwap (Cross-chain & Same-chain)
54
- if (isProviderHealthy('kyberswap')) {
53
+ // Provider 6: KyberSwap (Same-chain ONLY)
54
+ if (!isCrossChain && isProviderHealthy('kyberswap')) {
55
55
  promises.push(fetchKyberSwap(fromChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance));
56
56
  }
57
57
  // Execute all healthy providers concurrently with a 4s timeout
@@ -4,6 +4,9 @@ exports.routeTransaction = routeTransaction;
4
4
  const aggregatorMainnet_1 = require("./aggregatorMainnet");
5
5
  const aggregatorTestnet_1 = require("./aggregatorTestnet");
6
6
  async function routeTransaction(fromChain, toChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance = "auto") {
7
+ if (!fromChain || !toChain) {
8
+ throw new Error("Missing source or destination chain in routing.");
9
+ }
7
10
  // Auto-correct: If one is testnet and the other is mainnet, assume they meant testnet
8
11
  if (fromChain.includes('sepolia') && !toChain.includes('sepolia')) {
9
12
  if (toChain === 'base')
@@ -11,6 +11,10 @@ const tokens_1 = require("../utils/tokens");
11
11
  const defiRouter_1 = require("../aggregator/defiRouter");
12
12
  async function prepareBridgeToken(fromChain, toChain, tokenSymbol, amountStr, mode = "auto", providerName = "auto", slippagePercent) {
13
13
  try {
14
+ if (!fromChain || !toChain)
15
+ throw new Error("Source or destination chain not provided by AI.");
16
+ if (!amountStr)
17
+ throw new Error("Bridge amount not provided by AI.");
14
18
  const userAddress = await (0, vaultClient_1.getAddress)();
15
19
  // Auto-correct: If one is testnet and the other is mainnet, assume they meant testnet
16
20
  if (fromChain.includes('sepolia') && !toChain.includes('sepolia')) {
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkRegistryStatusToolDefinition = void 0;
3
4
  exports.checkRegistryStatus = checkRegistryStatus;
4
5
  const config_1 = require("../config");
5
6
  const config_2 = require("../config");
@@ -77,3 +78,15 @@ async function checkRegistryStatus() {
77
78
  return { isActive: true, reason: 'Registry check skipped due to network error.' };
78
79
  }
79
80
  }
81
+ exports.checkRegistryStatusToolDefinition = {
82
+ type: "function",
83
+ function: {
84
+ name: "check_registry_status",
85
+ description: "Internal Security Middleware: Checks the On-Chain Kill Switch Registry before executing any transaction. If the agent is deactivated, the transaction will be strictly blocked for safety.",
86
+ parameters: {
87
+ type: "object",
88
+ properties: {},
89
+ required: []
90
+ }
91
+ }
92
+ };