nyxora 26.6.21 → 26.6.23

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 (64) hide show
  1. package/README.md +3 -4
  2. package/bin/nyxora.mjs +14 -2
  3. package/dist/packages/core/src/agent/cronManager.js +107 -0
  4. package/dist/packages/core/src/agent/reasoning.js +85 -22
  5. package/dist/packages/core/src/agent/transactionManager.js +2 -2
  6. package/dist/packages/core/src/agent/updateIdentity.js +71 -0
  7. package/dist/packages/core/src/config/paths.js +5 -20
  8. package/dist/packages/core/src/gateway/WebSocketManager.js +98 -0
  9. package/dist/packages/core/src/gateway/chat.js +38 -0
  10. package/dist/packages/core/src/gateway/cli.js +20 -20
  11. package/dist/packages/core/src/gateway/server.js +63 -8
  12. package/dist/packages/core/src/gateway/setup.js +9 -6
  13. package/dist/packages/core/src/gateway/telegram.js +43 -0
  14. package/dist/packages/core/src/gateway/tracker.js +63 -0
  15. package/dist/packages/core/src/memory/logger.js +1 -1
  16. package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
  17. package/dist/packages/core/src/system/skills/editFile.js +5 -0
  18. package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
  19. package/dist/packages/core/src/system/skills/searchWeb.js +61 -8
  20. package/dist/packages/core/src/system/skills/writeFile.js +5 -0
  21. package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
  22. package/dist/packages/core/src/web3/utils/vaultClient.js +79 -29
  23. package/dist/packages/policy/src/server.js +29 -1
  24. package/package.json +7 -1
  25. package/packages/core/package.json +8 -1
  26. package/packages/core/src/agent/cronManager.ts +87 -0
  27. package/packages/core/src/agent/reasoning.ts +91 -21
  28. package/packages/core/src/agent/transactionManager.ts +2 -1
  29. package/packages/core/src/agent/updateIdentity.ts +68 -0
  30. package/packages/core/src/config/parser.ts +1 -1
  31. package/packages/core/src/config/paths.ts +5 -23
  32. package/packages/core/src/gateway/WebSocketManager.ts +114 -0
  33. package/packages/core/src/gateway/chat.ts +40 -1
  34. package/packages/core/src/gateway/cli.ts +10 -10
  35. package/packages/core/src/gateway/server.ts +66 -7
  36. package/packages/core/src/gateway/setup.ts +8 -5
  37. package/packages/core/src/gateway/telegram.ts +49 -0
  38. package/packages/core/src/gateway/tracker.ts +61 -0
  39. package/packages/core/src/memory/logger.ts +1 -1
  40. package/packages/core/src/system/skills/cancelTask.ts +38 -0
  41. package/packages/core/src/system/skills/editFile.ts +7 -0
  42. package/packages/core/src/system/skills/scheduleTask.ts +38 -0
  43. package/packages/core/src/system/skills/searchWeb.ts +56 -8
  44. package/packages/core/src/system/skills/writeFile.ts +7 -0
  45. package/packages/core/src/web3/skills/getPrice.ts +1 -1
  46. package/packages/core/src/web3/utils/vaultClient.ts +86 -26
  47. package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
  48. package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
  49. package/packages/dashboard/dist/index.html +2 -2
  50. package/packages/dashboard/dist/routers/0x.png +0 -0
  51. package/packages/dashboard/dist/routers/1inch.png +0 -0
  52. package/packages/dashboard/dist/routers/cmc.png +0 -0
  53. package/packages/dashboard/dist/routers/kyberswap.png +0 -0
  54. package/packages/dashboard/dist/routers/lifi.png +0 -0
  55. package/packages/dashboard/dist/routers/openocean.png +0 -0
  56. package/packages/dashboard/dist/routers/relay.png +0 -0
  57. package/packages/dashboard/dist/routers/zerion.png +0 -0
  58. package/packages/dashboard/package.json +1 -1
  59. package/packages/mcp-server/package.json +1 -1
  60. package/packages/policy/package.json +4 -3
  61. package/packages/policy/src/server.ts +29 -1
  62. package/packages/signer/package.json +1 -1
  63. package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
  64. package/packages/dashboard/dist/assets/index-Di9x08yk.js +0 -16
@@ -52,7 +52,6 @@ const parser_1 = require("../config/parser");
52
52
  async function main() {
53
53
  // 1. Determine configuration directory
54
54
  const appDir = (0, paths_1.getAppDir)();
55
- const isGlobalMode = appDir !== process.cwd();
56
55
  console.log(`================================`);
57
56
  console.log(`🤖 Nyxora CLI Agent Booting Up...`);
58
57
  console.log(`📂 Config Directory: ${appDir}`);
@@ -219,30 +218,31 @@ async function main() {
219
218
  console.log(picocolors_1.default.green(' npm uninstall -g nyxora\n'));
220
219
  process.exit(0);
221
220
  }
222
- // 2. Setup boilerplate files if in global mode and they don't exist
221
+ // 2. Setup boilerplate files since we enforce global mode
223
222
  let isFirstBoot = false;
224
- if (isGlobalMode) {
225
- const globalConfigPath = (0, paths_1.getPath)('config.yaml');
226
- const globalUserMdPath = (0, paths_1.getPath)('user.md');
227
- const globalIdentityMdPath = (0, paths_1.getPath)('IDENTITY.md');
228
- // Copy default config.yaml
229
- if (!fs_1.default.existsSync(globalConfigPath)) {
230
- isFirstBoot = true;
231
- const exampleConfigPath = path_1.default.resolve(__dirname, '../../../config.yaml');
232
- if (fs_1.default.existsSync(exampleConfigPath)) {
233
- fs_1.default.copyFileSync(exampleConfigPath, globalConfigPath);
234
- }
235
- else {
236
- fs_1.default.writeFileSync(globalConfigPath, 'agent:\n name: Nyxora-Agent\n default_chain: base\nllm:\n provider: openai\n model: gpt-4\n temperature: 0.7\nmemory:\n type: file\n path: memory.json\n');
237
- }
223
+ const globalConfigPath = (0, paths_1.getPath)('config.yaml');
224
+ const globalUserMdPath = (0, paths_1.getPath)('user.md');
225
+ const globalIdentityMdPath = (0, paths_1.getPath)('IDENTITY.md');
226
+ // Copy default config.yaml
227
+ if (!fs_1.default.existsSync(globalConfigPath)) {
228
+ isFirstBoot = true;
229
+ let exampleConfigPath = path_1.default.resolve(__dirname, '../../../config.yaml'); // Dev
230
+ if (!fs_1.default.existsSync(exampleConfigPath)) {
231
+ exampleConfigPath = path_1.default.resolve(__dirname, '../../../../../config.yaml'); // Compiled
238
232
  }
239
- if (!fs_1.default.existsSync(globalUserMdPath)) {
240
- fs_1.default.writeFileSync(globalUserMdPath, 'Write custom instructions, special rules, user profiles, or the persona you want for Nyxora AI in this file.\n');
233
+ if (fs_1.default.existsSync(exampleConfigPath)) {
234
+ fs_1.default.copyFileSync(exampleConfigPath, globalConfigPath);
241
235
  }
242
- if (!fs_1.default.existsSync(globalIdentityMdPath)) {
243
- fs_1.default.writeFileSync(globalIdentityMdPath, 'You are a Web3 AI assistant named Nyxora.\n');
236
+ else {
237
+ fs_1.default.writeFileSync(globalConfigPath, 'agent:\n name: Nyxora-Agent\n default_chain: base\nllm:\n provider: openai\n model: gpt-4\n temperature: 0.7\nmemory:\n type: file\n path: memory.json\n');
244
238
  }
245
239
  }
240
+ if (!fs_1.default.existsSync(globalUserMdPath)) {
241
+ fs_1.default.writeFileSync(globalUserMdPath, 'Write custom instructions, special rules, user profiles, or the persona you want for Nyxora AI in this file.\n');
242
+ }
243
+ if (!fs_1.default.existsSync(globalIdentityMdPath)) {
244
+ fs_1.default.writeFileSync(globalIdentityMdPath, 'You are a Web3 AI assistant named Nyxora.\n');
245
+ }
246
246
  if (isFirstBoot) {
247
247
  console.log('[Setup] New installation detected. Starting Setup Wizard...');
248
248
  await (0, setup_1.runSetupWizard)();
@@ -18,8 +18,11 @@ process.on('uncaughtException', (error) => {
18
18
  const path_1 = __importDefault(require("path"));
19
19
  const helmet_1 = __importDefault(require("helmet"));
20
20
  const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
21
+ const crypto_1 = __importDefault(require("crypto"));
22
+ const os_1 = __importDefault(require("os"));
21
23
  const paths_1 = require("../config/paths");
22
24
  const state_1 = require("../utils/state");
25
+ const WebSocketManager_1 = require("./WebSocketManager");
23
26
  const fs_1 = __importDefault(require("fs"));
24
27
  const yaml_1 = __importDefault(require("yaml"));
25
28
  const reasoning_1 = require("../agent/reasoning");
@@ -29,6 +32,7 @@ const config_1 = require("../web3/config");
29
32
  const tokens_1 = require("../web3/utils/tokens");
30
33
  const tracker_1 = require("./tracker");
31
34
  const transactionManager_1 = require("../agent/transactionManager");
35
+ const multer_1 = __importDefault(require("multer"));
32
36
  const transfer_1 = require("../web3/skills/transfer");
33
37
  const swapToken_1 = require("../web3/skills/swapToken");
34
38
  const getBalance_1 = require("../web3/skills/getBalance");
@@ -64,6 +68,9 @@ const xManager_1 = require("../system/skills/xManager");
64
68
  const notionWorkspace_1 = require("../system/skills/notionWorkspace");
65
69
  const audioTranscribe_1 = require("../system/skills/audioTranscribe");
66
70
  const summarizeText_1 = require("../system/skills/summarizeText");
71
+ const scheduleTask_1 = require("../system/skills/scheduleTask");
72
+ const cancelTask_1 = require("../system/skills/cancelTask");
73
+ const cronManager_1 = require("../agent/cronManager");
67
74
  const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
68
75
  const writeFile_1 = require("../system/skills/writeFile");
69
76
  const generateExcel_1 = require("../system/skills/generateExcel");
@@ -141,14 +148,11 @@ app.use('/api', (req, res, next) => {
141
148
  next();
142
149
  });
143
150
  // Serve Static Dashboard
144
- let rootDir = __dirname;
145
- while (!fs_1.default.existsSync(path_1.default.join(rootDir, 'packages', 'dashboard'))) {
146
- const nextDir = path_1.default.dirname(rootDir);
147
- if (nextDir === rootDir || rootDir === '/' || rootDir === 'C:\\')
148
- break;
149
- rootDir = nextDir;
151
+ // __dirname is packages/core/dist/gateway (compiled) OR packages/core/src/gateway (dev)
152
+ let dashboardPath = path_1.default.join(__dirname, '..', '..', '..', 'dashboard', 'dist'); // Dev
153
+ if (!fs_1.default.existsSync(dashboardPath)) {
154
+ dashboardPath = path_1.default.join(__dirname, '..', '..', '..', '..', '..', 'packages', 'dashboard', 'dist'); // Compiled
150
155
  }
151
- const dashboardPath = path_1.default.join(rootDir, 'packages', 'dashboard', 'dist');
152
156
  app.use(express_1.default.static(dashboardPath));
153
157
  app.get('/', (req, res) => {
154
158
  res.sendFile(path_1.default.join(dashboardPath, 'index.html'));
@@ -159,6 +163,31 @@ app.get('/privacy', (req, res) => {
159
163
  app.get('/tos', (req, res) => {
160
164
  res.send((0, legalGenerator_1.generateTosHtml)());
161
165
  });
166
+ const storage = multer_1.default.diskStorage({
167
+ destination: function (req, file, cb) {
168
+ const docsDir = path_1.default.join(os_1.default.homedir(), '.nyxora', 'docs');
169
+ if (!fs_1.default.existsSync(docsDir))
170
+ fs_1.default.mkdirSync(docsDir, { recursive: true });
171
+ cb(null, docsDir);
172
+ },
173
+ filename: function (req, file, cb) {
174
+ const safeName = file.originalname.replace(/[^a-zA-Z0-9.\-_]/g, '_');
175
+ cb(null, `${Date.now()}-${safeName}`);
176
+ }
177
+ });
178
+ const upload = (0, multer_1.default)({ storage });
179
+ app.post('/api/upload', upload.single('file'), (req, res) => {
180
+ try {
181
+ if (!req.file) {
182
+ return res.status(400).json({ error: 'No file uploaded' });
183
+ }
184
+ return res.json({ filePath: req.file.path });
185
+ }
186
+ catch (err) {
187
+ console.error('[Upload] Error:', err);
188
+ return res.status(500).json({ error: 'Failed to save file' });
189
+ }
190
+ });
162
191
  app.post('/api/upload-google-credentials', (req, res) => {
163
192
  try {
164
193
  const credentials = req.body.credentials;
@@ -352,7 +381,9 @@ const systemSkills = [
352
381
  xManager_1.xManagerToolDefinition,
353
382
  notionWorkspace_1.notionWorkspaceToolDefinition,
354
383
  audioTranscribe_1.audioTranscribeToolDefinition,
355
- summarizeText_1.summarizeTextToolDefinition
384
+ summarizeText_1.summarizeTextToolDefinition,
385
+ scheduleTask_1.scheduleTaskDefinition,
386
+ cancelTask_1.cancelTaskDefinition
356
387
  ];
357
388
  app.get('/api/stats', (req, res) => {
358
389
  const stats = tracker_1.Tracker.getStats();
@@ -366,6 +397,12 @@ app.get('/api/stats', (req, res) => {
366
397
  app.get('/api/logs', (req, res) => {
367
398
  res.json(tracker_1.Tracker.getLogs());
368
399
  });
400
+ app.get('/api/cron', (req, res) => {
401
+ res.json({
402
+ activeJobs: cronManager_1.cronManager.getActiveJobsCount(),
403
+ jobs: cronManager_1.cronManager.getJobs()
404
+ });
405
+ });
369
406
  app.get('/api/skills', (req, res) => {
370
407
  const skillsWithStatus = allSkills.map(skill => ({
371
408
  ...skill,
@@ -815,6 +852,22 @@ function resetIdleTimer() {
815
852
  reflection_1.ReflectionEngine.runReflection();
816
853
  }, 3 * 60 * 1000); // 3 minutes idle
817
854
  }
855
+ app.post('/api/v1/trade', async (req, res) => {
856
+ try {
857
+ const { message, session_id } = req.body;
858
+ if (!message)
859
+ return res.status(400).json({ error: 'Message is required' });
860
+ const traceId = crypto_1.default.randomBytes(8).toString('hex');
861
+ // Asynchronous background execution
862
+ (0, reasoning_1.processUserInput)(message, session_id || traceId).catch(err => {
863
+ console.error(`[TradeAPI] Error:`, err);
864
+ });
865
+ res.status(200).json({ status: 'processing', traceId });
866
+ }
867
+ catch (error) {
868
+ res.status(500).json({ error: error.message });
869
+ }
870
+ });
818
871
  app.post('/api/chat', async (req, res) => {
819
872
  try {
820
873
  const { message, session_id } = req.body;
@@ -965,6 +1018,8 @@ function startServer() {
965
1018
  const PORT = Number(process.env.PORT || 3000);
966
1019
  const server = app.listen(PORT, '127.0.0.1', () => {
967
1020
  console.log(`🤖 Nyxora API Server running on port ${PORT}`);
1021
+ // Initialize WebSocket Manager
1022
+ (0, WebSocketManager_1.initWebSocket)(server);
968
1023
  // Start the Telegram bot listener
969
1024
  (0, telegram_1.startTelegramBot)();
970
1025
  // Start Asynchronous Bridge Watcher
@@ -276,7 +276,7 @@ Provider: ${config.llm.provider}`;
276
276
  { value: 'gitManager', label: 'Git Operations (Commit/Push/Pull)' },
277
277
  { value: 'updateSecurityPolicy', label: 'Update policy.yaml rules', hint: 'safeguard' },
278
278
  { value: 'browseWeb', label: 'Browse & Scrape Webpages' },
279
- { value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave)', hint: 'Requires API Key' },
279
+ { value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave/DuckDuckGo)', hint: 'Optional API Key' },
280
280
  { value: 'googleWorkspace', label: 'Google Workspace (Gmail, Docs, Sheets, Forms)', hint: 'Requires OAuth' },
281
281
  { value: 'notionWorkspace', label: 'Notion Integration' },
282
282
  { value: 'xManager', label: 'X/Twitter Management' },
@@ -308,15 +308,18 @@ Provider: ${config.llm.provider}`;
308
308
  options: [
309
309
  { value: 'tavily', label: 'Tavily Search (Built for AI - 1000 free/mo)' },
310
310
  { value: 'brave', label: 'Brave Search (Privacy focused - 2000 free/mo)' },
311
+ { value: 'duckduckgo', label: 'DuckDuckGo (Free & Built-in)' },
311
312
  ],
312
313
  });
313
314
  if ((0, prompts_1.isCancel)(searchProvider))
314
315
  return process.exit(0);
315
- searchApiKey = (await (0, prompts_1.password)({
316
- message: `Enter API Key for ${searchProvider} (Get it free at ${searchProvider === 'tavily' ? 'tavily.com' : 'search.brave.com'}):`,
317
- }));
318
- if ((0, prompts_1.isCancel)(searchApiKey))
319
- return process.exit(0);
316
+ if (searchProvider !== 'duckduckgo') {
317
+ searchApiKey = (await (0, prompts_1.password)({
318
+ message: `Enter API Key for ${searchProvider} (Get it free at ${searchProvider === 'tavily' ? 'tavily.com' : 'search.brave.com'}):`,
319
+ }));
320
+ if ((0, prompts_1.isCancel)(searchApiKey))
321
+ return process.exit(0);
322
+ }
320
323
  }
321
324
  const setupTelegram = activeChannels.includes('telegram');
322
325
  let telegramToken = '';
@@ -19,6 +19,9 @@ const executeDefi_1 = require("../web3/skills/executeDefi");
19
19
  const revokeApprovals_1 = require("../web3/skills/revokeApprovals");
20
20
  const formatter_1 = require("../utils/formatter");
21
21
  const picocolors_1 = __importDefault(require("picocolors"));
22
+ const fs_1 = __importDefault(require("fs"));
23
+ const path_1 = __importDefault(require("path"));
24
+ const os_1 = __importDefault(require("os"));
22
25
  let globalBotInstance = null;
23
26
  function formatToTelegramHTML(text) {
24
27
  if (!text)
@@ -162,6 +165,46 @@ function startTelegramBot() {
162
165
  await ctx.reply('❌ Sorry, I encountered an error while processing your message.');
163
166
  }
164
167
  });
168
+ bot.on('document', async (ctx) => {
169
+ const doc = ctx.message.document;
170
+ const caption = ctx.message.caption || '';
171
+ console.log(`[Telegram] Received document from ${ctx.from?.first_name || 'User'}: ${doc.file_name}`);
172
+ await ctx.sendChatAction('typing');
173
+ try {
174
+ const fileLink = await ctx.telegram.getFileLink(doc.file_id);
175
+ const docsDir = path_1.default.join(os_1.default.homedir(), '.nyxora', 'docs');
176
+ if (!fs_1.default.existsSync(docsDir))
177
+ fs_1.default.mkdirSync(docsDir, { recursive: true });
178
+ const safeName = (doc.file_name || 'telegram_doc').replace(/[^a-zA-Z0-9.\-_]/g, '_');
179
+ const localFilePath = path_1.default.join(docsDir, `${Date.now()}-${safeName}`);
180
+ const res = await fetch(fileLink.toString());
181
+ const buffer = await res.arrayBuffer();
182
+ fs_1.default.writeFileSync(localFilePath, Buffer.from(buffer));
183
+ const prompt = `Tolong analisis dokumen ini: ${localFilePath}\n\n${caption}`;
184
+ let progressMsgId = null;
185
+ const onProgress = async (progressText) => {
186
+ try {
187
+ if (!progressMsgId) {
188
+ const sent = await ctx.reply(`<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
189
+ progressMsgId = sent.message_id;
190
+ }
191
+ else {
192
+ await ctx.telegram.editMessageText(ctx.chat.id, progressMsgId, undefined, `<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
193
+ }
194
+ }
195
+ catch (e) { }
196
+ };
197
+ const response = await (0, reasoning_1.processUserInput)(prompt, 'user', onProgress, ctx.chat?.id.toString());
198
+ if (progressMsgId) {
199
+ await ctx.telegram.deleteMessage(ctx.chat.id, progressMsgId).catch(() => { });
200
+ }
201
+ await ctx.reply(formatToTelegramHTML(response), { parse_mode: 'HTML' });
202
+ }
203
+ catch (error) {
204
+ console.error('[Telegram] Error processing document:', error);
205
+ await ctx.reply('❌ Sorry, I failed to download or analyze the document.');
206
+ }
207
+ });
165
208
  // Handle callbacks
166
209
  bot.action(/^approve_(.+)$/, async (ctx) => {
167
210
  const txId = ctx.match[1];
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.Tracker = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const WebSocketManager_1 = require("./WebSocketManager");
9
+ const paths_1 = require("../config/paths");
4
10
  const stats = {
5
11
  cost: 0,
6
12
  tokens: 0,
@@ -9,6 +15,55 @@ const stats = {
9
15
  const eventLogs = [];
10
16
  const gatewayLogs = [];
11
17
  const MAX_LOGS = 100;
18
+ let trackerFile = '';
19
+ try {
20
+ trackerFile = (0, paths_1.getPath)('tracker.json');
21
+ }
22
+ catch (e) {
23
+ // Fallback
24
+ }
25
+ function loadState() {
26
+ if (!trackerFile)
27
+ return;
28
+ try {
29
+ if (fs_1.default.existsSync(trackerFile)) {
30
+ const data = JSON.parse(fs_1.default.readFileSync(trackerFile, 'utf8'));
31
+ if (data.stats)
32
+ Object.assign(stats, data.stats);
33
+ if (data.eventLogs && Array.isArray(data.eventLogs)) {
34
+ eventLogs.splice(0, eventLogs.length, ...data.eventLogs);
35
+ }
36
+ if (data.gatewayLogs && Array.isArray(data.gatewayLogs)) {
37
+ gatewayLogs.splice(0, gatewayLogs.length, ...data.gatewayLogs);
38
+ }
39
+ }
40
+ }
41
+ catch (e) { }
42
+ }
43
+ let savePending = false;
44
+ function saveState() {
45
+ if (!trackerFile)
46
+ return;
47
+ if (savePending)
48
+ return;
49
+ savePending = true;
50
+ setTimeout(() => {
51
+ flushState();
52
+ savePending = false;
53
+ }, 1000);
54
+ }
55
+ function flushState() {
56
+ if (!trackerFile)
57
+ return;
58
+ try {
59
+ fs_1.default.writeFileSync(trackerFile, JSON.stringify({ stats, eventLogs, gatewayLogs }));
60
+ }
61
+ catch (e) { }
62
+ }
63
+ process.on('exit', flushState);
64
+ process.on('SIGTERM', () => { flushState(); process.exit(0); });
65
+ process.on('SIGINT', () => { flushState(); process.exit(0); });
66
+ loadState();
12
67
  function formatTime() {
13
68
  const now = new Date();
14
69
  return now.toTimeString().split(' ')[0]; // Returns HH:MM:SS
@@ -23,9 +78,11 @@ exports.Tracker = {
23
78
  else if (provider === 'gemini')
24
79
  rate = 0.00001;
25
80
  stats.cost += (amount * rate);
81
+ saveState();
26
82
  },
27
83
  addMessage: () => {
28
84
  stats.messages += 1;
85
+ saveState();
29
86
  },
30
87
  getStats: () => {
31
88
  return { ...stats, cost: Number(stats.cost.toFixed(4)) };
@@ -34,11 +91,17 @@ exports.Tracker = {
34
91
  eventLogs.unshift({ timestamp: formatTime(), event, meta });
35
92
  if (eventLogs.length > MAX_LOGS)
36
93
  eventLogs.pop();
94
+ saveState();
37
95
  },
38
96
  addGatewayLog: (message, meta) => {
39
97
  gatewayLogs.unshift({ timestamp: formatTime(), message, meta });
40
98
  if (gatewayLogs.length > MAX_LOGS)
41
99
  gatewayLogs.pop();
100
+ saveState();
101
+ // Broadcast terminal logs to Dashboard via WebSocket
102
+ if (WebSocketManager_1.wsManager) {
103
+ WebSocketManager_1.wsManager.broadcastAll(`[${formatTime()}] ${message}`, meta?.level || 'info');
104
+ }
42
105
  },
43
106
  getLogs: () => {
44
107
  return {
@@ -169,7 +169,7 @@ class Logger {
169
169
  SELECT * FROM (
170
170
  SELECT role, content, name, tool_call_id, tool_calls, session_id, id
171
171
  FROM messages
172
- WHERE session_id IS NULL
172
+ WHERE session_id IS NULL
173
173
  ORDER BY id DESC LIMIT 40
174
174
  ) ORDER BY id ASC
175
175
  `).all();
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cancelTaskDefinition = void 0;
4
+ exports.executeCancelTask = executeCancelTask;
5
+ const cronManager_1 = require("../../agent/cronManager");
6
+ exports.cancelTaskDefinition = {
7
+ type: "function",
8
+ function: {
9
+ name: "cancel_task",
10
+ description: "Cancel a scheduled background task (cron job). Use this when the user asks you to stop monitoring or cancel a previously scheduled task.",
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ jobId: {
15
+ type: "string",
16
+ description: "The unique Job ID of the scheduled task to cancel."
17
+ }
18
+ },
19
+ required: ["jobId"]
20
+ }
21
+ }
22
+ };
23
+ async function executeCancelTask(args) {
24
+ const { jobId } = args;
25
+ if (!jobId) {
26
+ return "Error: Missing required parameter jobId.";
27
+ }
28
+ try {
29
+ const success = cronManager_1.cronManager.removeJob(jobId);
30
+ if (success) {
31
+ return `Success! I have cancelled the scheduled background task with Job ID: ${jobId}.`;
32
+ }
33
+ else {
34
+ return `Failed to cancel task. No active task found with Job ID: ${jobId}. You can check active jobs by asking me what tasks are currently running.`;
35
+ }
36
+ }
37
+ catch (error) {
38
+ return `Failed to cancel task: ${error.message}`;
39
+ }
40
+ }
@@ -10,6 +10,11 @@ const path_1 = __importDefault(require("path"));
10
10
  function editLocalFile(filePath, searchString, replacementString) {
11
11
  try {
12
12
  const absolutePath = path_1.default.resolve(filePath);
13
+ // Security Firewall: Block modification of core configuration files
14
+ const basename = path_1.default.basename(absolutePath);
15
+ if (['config.yaml', 'rpc_key.yaml', 'policy.yaml'].includes(basename)) {
16
+ return `Error: Access Denied. You are strictly forbidden from modifying core configuration files directly. If you need to update your agent name, use the update_identity tool instead.`;
17
+ }
13
18
  if (!fs_1.default.existsSync(absolutePath)) {
14
19
  return `Error: File not found at ${absolutePath}`;
15
20
  }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scheduleTaskDefinition = void 0;
4
+ exports.executeScheduleTask = executeScheduleTask;
5
+ const cronManager_1 = require("../../agent/cronManager");
6
+ exports.scheduleTaskDefinition = {
7
+ type: "function",
8
+ function: {
9
+ name: "schedule_task",
10
+ description: "Schedule a recurring background task for the AI to execute automatically using a cron expression. Use this when the user asks you to remind them or monitor something periodically (e.g. 'check price every hour', 'monitor my wallet every 5 minutes'). The AI will execute the provided prompt at the scheduled interval and send the result via Telegram notification.",
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ cronExpression: {
15
+ type: "string",
16
+ description: "A standard 5-field cron expression (minute hour day month day-of-week). Examples: '*/5 * * * *' (every 5 mins), '0 * * * *' (every hour), '0 8 * * *' (every day at 8 AM)."
17
+ },
18
+ prompt: {
19
+ type: "string",
20
+ description: "The prompt/command that the AI should execute when the cron triggers. E.g., 'What is the current price of Ethereum?'"
21
+ }
22
+ },
23
+ required: ["cronExpression", "prompt"]
24
+ }
25
+ }
26
+ };
27
+ async function executeScheduleTask(args) {
28
+ const { cronExpression, prompt } = args;
29
+ if (!cronExpression || !prompt) {
30
+ return "Error: Missing required parameters cronExpression or prompt.";
31
+ }
32
+ try {
33
+ const jobId = cronManager_1.cronManager.addJob(cronExpression, prompt);
34
+ return `Success! I have scheduled the background task.\nJob ID: ${jobId}\nSchedule: ${cronExpression}\nPrompt to execute: "${prompt}"\n\nYou will receive a notification via Telegram every time this task completes.`;
35
+ }
36
+ catch (error) {
37
+ return `Failed to schedule task: ${error.message}. Please ensure the cron expression is valid.`;
38
+ }
39
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.searchWebToolDefinition = void 0;
4
4
  exports.searchWeb = searchWeb;
5
5
  const parser_1 = require("../../config/parser");
6
+ const duck_duck_scrape_1 = require("duck-duck-scrape");
6
7
  const SEARXNG_INSTANCES = [
7
8
  'https://search.mdosch.de',
8
9
  'https://searx.tiekoetter.com',
@@ -79,6 +80,25 @@ async function searchSearxng(query, depth = 1) {
79
80
  }
80
81
  throw new Error('[SearXNG Error] All decentralized instances failed.');
81
82
  }
83
+ async function searchDuckDuckGo(query, depth = 1) {
84
+ try {
85
+ const searchResults = await (0, duck_duck_scrape_1.search)(query, {
86
+ safeSearch: duck_duck_scrape_1.SafeSearchType.MODERATE
87
+ });
88
+ if (!searchResults.noResults && searchResults.results.length > 0) {
89
+ const maxResults = depth > 1 ? 15 : 8;
90
+ return searchResults.results.slice(0, maxResults).map(r => ({
91
+ title: r.title,
92
+ url: r.url,
93
+ content: r.description || r.title
94
+ }));
95
+ }
96
+ return [];
97
+ }
98
+ catch (e) {
99
+ throw new Error(`[DuckDuckGo Error] Failed to scrape: ${e.message}`);
100
+ }
101
+ }
82
102
  const searchCache = new Map();
83
103
  async function searchWeb(query, depth = 1) {
84
104
  // Auto-inject current year for time-sensitive queries
@@ -119,13 +139,25 @@ async function searchWeb(query, depth = 1) {
119
139
  results = await searchBrave(finalQuery, creds.brave_key, depth);
120
140
  }
121
141
  catch (e2) {
122
- console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to SearXNG Mesh...');
123
- results = await searchSearxng(finalQuery, depth);
142
+ console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to DuckDuckGo (L3)...');
143
+ try {
144
+ results = await searchDuckDuckGo(finalQuery, depth);
145
+ }
146
+ catch (e3) {
147
+ console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
148
+ results = await searchSearxng(finalQuery, depth);
149
+ }
124
150
  }
125
151
  }
126
152
  else {
127
- console.warn('[WebSearch] No backup provider found. Falling back to SearXNG Mesh...');
128
- results = await searchSearxng(finalQuery, depth);
153
+ console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
154
+ try {
155
+ results = await searchDuckDuckGo(finalQuery, depth);
156
+ }
157
+ catch (e3) {
158
+ console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
159
+ results = await searchSearxng(finalQuery, depth);
160
+ }
129
161
  }
130
162
  }
131
163
  else {
@@ -145,13 +177,25 @@ async function searchWeb(query, depth = 1) {
145
177
  results = await searchTavily(finalQuery, creds.tavily_key, depth);
146
178
  }
147
179
  catch (e2) {
148
- console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to SearXNG Mesh...');
149
- results = await searchSearxng(finalQuery, depth);
180
+ console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to DuckDuckGo (L3)...');
181
+ try {
182
+ results = await searchDuckDuckGo(finalQuery, depth);
183
+ }
184
+ catch (e3) {
185
+ console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
186
+ results = await searchSearxng(finalQuery, depth);
187
+ }
150
188
  }
151
189
  }
152
190
  else {
153
- console.warn('[WebSearch] No backup provider found. Falling back to SearXNG Mesh...');
154
- results = await searchSearxng(finalQuery, depth);
191
+ console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
192
+ try {
193
+ results = await searchDuckDuckGo(finalQuery, depth);
194
+ }
195
+ catch (e3) {
196
+ console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
197
+ results = await searchSearxng(finalQuery, depth);
198
+ }
155
199
  }
156
200
  }
157
201
  else {
@@ -159,6 +203,15 @@ async function searchWeb(query, depth = 1) {
159
203
  }
160
204
  }
161
205
  }
206
+ else if (provider === 'duckduckgo') {
207
+ try {
208
+ results = await searchDuckDuckGo(finalQuery, depth);
209
+ }
210
+ catch (e) {
211
+ console.warn('[WebSearch] Primary provider (DuckDuckGo) failed. Falling back to SearXNG Mesh...');
212
+ results = await searchSearxng(finalQuery, depth);
213
+ }
214
+ }
162
215
  else {
163
216
  results = await searchSearxng(finalQuery, depth);
164
217
  }
@@ -10,6 +10,11 @@ const path_1 = __importDefault(require("path"));
10
10
  function writeLocalFile(filePath, content) {
11
11
  try {
12
12
  const absolutePath = path_1.default.resolve(filePath);
13
+ // Security Firewall: Block modification of core configuration files
14
+ const basename = path_1.default.basename(absolutePath);
15
+ if (['config.yaml', 'rpc_key.yaml', 'policy.yaml'].includes(basename)) {
16
+ return `Error: Access Denied. You are strictly forbidden from modifying core configuration files directly. If you need to update your agent name, use the update_identity tool instead.`;
17
+ }
13
18
  const dir = path_1.default.dirname(absolutePath);
14
19
  if (!fs_1.default.existsSync(dir)) {
15
20
  fs_1.default.mkdirSync(dir, { recursive: true });
@@ -7,7 +7,7 @@ exports.getPriceToolDefinition = {
7
7
  type: "function",
8
8
  function: {
9
9
  name: "get_price",
10
- description: "Fetches the current price of a cryptocurrency. Use ONLY when the user explicitly asks for a simple price check (e.g. 'harga', 'price'). Do NOT use this for 'analysis', 'market analysis', or 'analisis pasar'.",
10
+ description: "Fetches the current price of a cryptocurrency.",
11
11
  parameters: {
12
12
  type: "object",
13
13
  properties: {