nyxora 26.6.21 → 26.6.22-1

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 (54) hide show
  1. package/bin/nyxora.mjs +14 -2
  2. package/dist/packages/core/src/agent/cronManager.js +107 -0
  3. package/dist/packages/core/src/agent/reasoning.js +85 -22
  4. package/dist/packages/core/src/agent/transactionManager.js +2 -2
  5. package/dist/packages/core/src/agent/updateIdentity.js +71 -0
  6. package/dist/packages/core/src/config/paths.js +5 -20
  7. package/dist/packages/core/src/gateway/chat.js +38 -0
  8. package/dist/packages/core/src/gateway/cli.js +20 -20
  9. package/dist/packages/core/src/gateway/server.js +43 -8
  10. package/dist/packages/core/src/gateway/telegram.js +43 -0
  11. package/dist/packages/core/src/gateway/tracker.js +58 -0
  12. package/dist/packages/core/src/memory/logger.js +1 -1
  13. package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
  14. package/dist/packages/core/src/system/skills/editFile.js +5 -0
  15. package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
  16. package/dist/packages/core/src/system/skills/writeFile.js +5 -0
  17. package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
  18. package/dist/packages/policy/src/server.js +1 -1
  19. package/package.json +4 -1
  20. package/packages/core/package.json +5 -1
  21. package/packages/core/src/agent/cronManager.ts +87 -0
  22. package/packages/core/src/agent/reasoning.ts +91 -21
  23. package/packages/core/src/agent/transactionManager.ts +2 -1
  24. package/packages/core/src/agent/updateIdentity.ts +68 -0
  25. package/packages/core/src/config/paths.ts +5 -23
  26. package/packages/core/src/gateway/chat.ts +40 -1
  27. package/packages/core/src/gateway/cli.ts +10 -10
  28. package/packages/core/src/gateway/server.ts +44 -7
  29. package/packages/core/src/gateway/telegram.ts +49 -0
  30. package/packages/core/src/gateway/tracker.ts +55 -0
  31. package/packages/core/src/memory/logger.ts +1 -1
  32. package/packages/core/src/system/skills/cancelTask.ts +38 -0
  33. package/packages/core/src/system/skills/editFile.ts +7 -0
  34. package/packages/core/src/system/skills/scheduleTask.ts +38 -0
  35. package/packages/core/src/system/skills/writeFile.ts +7 -0
  36. package/packages/core/src/web3/skills/getPrice.ts +1 -1
  37. package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
  38. package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
  39. package/packages/dashboard/dist/index.html +2 -2
  40. package/packages/dashboard/dist/routers/0x.png +0 -0
  41. package/packages/dashboard/dist/routers/1inch.png +0 -0
  42. package/packages/dashboard/dist/routers/cmc.png +0 -0
  43. package/packages/dashboard/dist/routers/kyberswap.png +0 -0
  44. package/packages/dashboard/dist/routers/lifi.png +0 -0
  45. package/packages/dashboard/dist/routers/openocean.png +0 -0
  46. package/packages/dashboard/dist/routers/relay.png +0 -0
  47. package/packages/dashboard/dist/routers/zerion.png +0 -0
  48. package/packages/dashboard/package.json +1 -1
  49. package/packages/mcp-server/package.json +1 -1
  50. package/packages/policy/package.json +1 -1
  51. package/packages/policy/src/server.ts +1 -1
  52. package/packages/signer/package.json +1 -1
  53. package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
  54. package/packages/dashboard/dist/assets/index-Di9x08yk.js +0 -16
package/bin/nyxora.mjs CHANGED
@@ -86,18 +86,30 @@ async function start() {
86
86
  }
87
87
  }
88
88
 
89
- async function stop() {
89
+ async function stop(preserveTracker = false) {
90
90
  const pid = await getDaemonPid();
91
91
  if (pid) {
92
92
  console.log(`Stopping Nyxora daemon (PID: ${pid})...`);
93
93
  try {
94
94
  process.kill(-pid, 'SIGTERM');
95
+
96
+ // Wait for process to exit to avoid race condition with flushState
97
+ let attempts = 0;
98
+ while (isDaemonRunning(pid.toString()) && attempts < 20) {
99
+ await new Promise(r => setTimeout(r, 100));
100
+ attempts++;
101
+ }
102
+
95
103
  console.log('Nyxora stopped gracefully.');
96
104
  } catch (e) {
97
105
  console.error('Failed to kill process:', e.message);
98
106
  }
99
107
  try {
100
108
  fs.unlinkSync(pidFile);
109
+ if (!preserveTracker) {
110
+ const trackerFile = path.join(appDir, 'run', 'tracker.json');
111
+ if (fs.existsSync(trackerFile)) fs.unlinkSync(trackerFile);
112
+ }
101
113
  } catch(e) {}
102
114
  } else {
103
115
  console.log('Nyxora is not running.');
@@ -105,7 +117,7 @@ async function stop() {
105
117
  }
106
118
 
107
119
  async function restart() {
108
- await stop();
120
+ await stop(true);
109
121
  setTimeout(start, 1000);
110
122
  }
111
123
 
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.cronManager = void 0;
40
+ const cron = __importStar(require("node-cron"));
41
+ const parser_1 = require("../config/parser");
42
+ const telegram_1 = require("../gateway/telegram");
43
+ const crypto_1 = require("crypto");
44
+ const picocolors_1 = __importDefault(require("picocolors"));
45
+ class CronManager {
46
+ jobs = new Map();
47
+ addJob(expression, prompt) {
48
+ const id = (0, crypto_1.randomUUID)();
49
+ // Validate expression
50
+ if (!cron.validate(expression)) {
51
+ throw new Error(`Invalid cron expression: ${expression}`);
52
+ }
53
+ const task = cron.schedule(expression, async () => {
54
+ console.log(picocolors_1.default.cyan(`[Cron] Executing job ${id}: "${prompt}"`));
55
+ try {
56
+ // Dynamically import processUserInput to avoid circular dependencies
57
+ const { processUserInput } = await Promise.resolve().then(() => __importStar(require('./reasoning')));
58
+ // Execute the prompt as a background system task
59
+ const response = await processUserInput(prompt, 'system', undefined, `cron-${id}`);
60
+ // Push notification to Telegram if configured
61
+ const config = (0, parser_1.loadConfig)();
62
+ if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
63
+ const message = `🤖 *AI Scheduled Report*\n\n${response}`;
64
+ await (0, telegram_1.sendPushNotification)(config.integrations.telegram.authorized_chat_id, message);
65
+ }
66
+ }
67
+ catch (err) {
68
+ console.error(picocolors_1.default.red(`[Cron] Failed to execute job ${id}:`), err);
69
+ const config = (0, parser_1.loadConfig)();
70
+ if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
71
+ await (0, telegram_1.sendPushNotification)(config.integrations.telegram.authorized_chat_id, `⚠️ *Cron Job Error*\n\nPrompt: ${prompt}\nError: ${err.message}`);
72
+ }
73
+ }
74
+ });
75
+ this.jobs.set(id, {
76
+ id,
77
+ expression,
78
+ prompt,
79
+ task,
80
+ createdAt: Date.now()
81
+ });
82
+ console.log(picocolors_1.default.green(`[Cron] Scheduled new job ${id} with expression '${expression}'`));
83
+ return id;
84
+ }
85
+ removeJob(id) {
86
+ const job = this.jobs.get(id);
87
+ if (job) {
88
+ job.task.stop();
89
+ this.jobs.delete(id);
90
+ console.log(picocolors_1.default.yellow(`[Cron] Removed job ${id}`));
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+ getJobs() {
96
+ return Array.from(this.jobs.values()).map(job => ({
97
+ id: job.id,
98
+ expression: job.expression,
99
+ prompt: job.prompt,
100
+ createdAt: job.createdAt
101
+ }));
102
+ }
103
+ getActiveJobsCount() {
104
+ return this.jobs.size;
105
+ }
106
+ }
107
+ exports.cronManager = new CronManager();
@@ -34,6 +34,7 @@ const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
34
34
  const getTxHistory_1 = require("../web3/skills/getTxHistory");
35
35
  const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
36
36
  const updateProfile_1 = require("./updateProfile");
37
+ const updateIdentity_1 = require("./updateIdentity");
37
38
  const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
38
39
  const analyzeDocument_1 = require("../system/skills/analyzeDocument");
39
40
  const readFile_1 = require("../system/skills/readFile");
@@ -48,6 +49,8 @@ const xManager_1 = require("../system/skills/xManager");
48
49
  const notionWorkspace_1 = require("../system/skills/notionWorkspace");
49
50
  const audioTranscribe_1 = require("../system/skills/audioTranscribe");
50
51
  const summarizeText_1 = require("../system/skills/summarizeText");
52
+ const scheduleTask_1 = require("../system/skills/scheduleTask");
53
+ const cancelTask_1 = require("../system/skills/cancelTask");
51
54
  const googleWorkspace_1 = require("../system/skills/googleWorkspace");
52
55
  const paths_1 = require("../config/paths");
53
56
  const picocolors_1 = __importDefault(require("picocolors"));
@@ -143,35 +146,62 @@ CRITICAL RULE 11: ADAPTIVE RESPONSE RULE. You must process Web3 data (portfolio,
143
146
  CRITICAL RULE 13: WALLET CONTEXT CACHING. Portfolio data in chat history is potentially stale. Do not use cached data for transactional planning; refresh the balance via tools first.
144
147
  CRITICAL RULE 14: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer, stake), do NOT ask for verbal confirmation. Execute the tool IMMEDIATELY. The tool itself will trigger a secure popup in the user's dashboard UI for final approval.
145
148
  CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answer exists in recent verified context and freshness is not strictly required. Use history to save latency.
149
+ CRITICAL RULE 19: GET_PRICE USAGE. Use get_price 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'.
146
150
 
147
151
  [ANTI-HALLUCINATION PROTOCOL]
148
152
  CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
149
153
  CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
154
+ CRITICAL RULE 18: AMOUNT PRECISION. When displaying crypto amounts, use exactly 6 decimal places for precision, or round to 2 decimals only if the value is significantly large (>$10,000). Never abbreviate unless the number is >$1,000,000.
150
155
  CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
151
156
  CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
152
157
  CRITICAL RULE 16: CAPABILITY HONESTY. NEVER claim a capability not available through installed tools. If asked for an unsupported action, state honestly that the skill is missing.
153
- CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response.`;
154
- // Read IDENTITY.md for core AI persona
158
+ CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response.
159
+ CRITICAL RULE 20: CRON JOBS VS LIMIT ORDERS. STRICT RULE: Do NOT use schedule_task for price-based trading triggers or buying/selling at a specific price level. Use create_limit_order. STRICT RULE: Do NOT use create_limit_order for time-based recurring tasks (e.g. 'every 5 minutes', 'every Monday'). Use schedule_task.
160
+ CRITICAL RULE 21: CONFIGURATION SECURITY. You are STRICTLY FORBIDDEN from modifying config.yaml, rpc_key.yaml, or policy.yaml using OS skills or terminal commands (like sed, echo, nano). If you need to change your name, use the update_identity tool.`;
161
+ const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
162
+ const userMdPath = (0, paths_1.getPath)('user.md');
163
+ let isFirstTime = false;
155
164
  try {
156
- const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
157
- if (fs_1.default.existsSync(identityMdPath)) {
158
- const identityInstructions = fs_1.default.readFileSync(identityMdPath, 'utf8');
159
- basePrompt += `\n\n--- CORE IDENTITY & PERSONA ---\n${identityInstructions}`;
160
- }
165
+ const identityContent = fs_1.default.existsSync(identityMdPath) ? fs_1.default.readFileSync(identityMdPath, 'utf8').trim() : '';
166
+ const userContent = fs_1.default.existsSync(userMdPath) ? fs_1.default.readFileSync(userMdPath, 'utf8').trim() : '';
167
+ // Check if files are empty or contain the default installation text
168
+ const isIdentityDefault = !identityContent || identityContent.includes('You are a Web3 AI assistant named Nyxora.');
169
+ const isUserDefault = !userContent || userContent.includes('Write custom instructions, special rules, user profiles');
170
+ isFirstTime = isIdentityDefault && isUserDefault;
161
171
  }
162
- catch (error) {
163
- console.error('Failed to read IDENTITY.md:', error);
172
+ catch (e) {
173
+ isFirstTime = true;
164
174
  }
165
- // Read user.md for custom instructions
166
- try {
167
- const userMdPath = (0, paths_1.getPath)('user.md');
168
- if (fs_1.default.existsSync(userMdPath)) {
169
- const customInstructions = fs_1.default.readFileSync(userMdPath, 'utf8');
170
- basePrompt += `\n\n--- CUSTOM USER INSTRUCTIONS ---\n${customInstructions}`;
171
- }
175
+ if (isFirstTime) {
176
+ basePrompt += `\n\n[ONBOARDING MODE]
177
+ This is your VERY FIRST interaction with the user. You MUST warmly welcome them to Nyxora and ask for 4 things to initialize your setup:
178
+ 1. Their Name
179
+ 2. What they want to name YOU (the AI Agent)
180
+ 3. Their Hobbies or Job (so you can tailor your conversation context)
181
+ 4. Your Persona/Character (e.g., professional, sarcastic, JARVIS, anime waifu)
182
+ Do NOT perform any web3 tasks or generic answers until they provide all 4 details. Once they answer, use 'update_profile' to save their name and hobbies/job to user.md, and use 'update_identity' (making sure to provide the 'agentName' parameter!) to save your new name and persona to IDENTITY.md.`;
172
183
  }
173
- catch (error) {
174
- console.error('Failed to read user.md:', error);
184
+ else {
185
+ // Read IDENTITY.md for core AI persona
186
+ try {
187
+ if (fs_1.default.existsSync(identityMdPath)) {
188
+ const identityInstructions = fs_1.default.readFileSync(identityMdPath, 'utf8');
189
+ basePrompt += `\n\n--- CORE IDENTITY & PERSONA ---\n${identityInstructions}`;
190
+ }
191
+ }
192
+ catch (error) {
193
+ console.error('Failed to read IDENTITY.md:', error);
194
+ }
195
+ // Read user.md for custom instructions
196
+ try {
197
+ if (fs_1.default.existsSync(userMdPath)) {
198
+ const customInstructions = fs_1.default.readFileSync(userMdPath, 'utf8');
199
+ basePrompt += `\n\n--- CUSTOM USER INSTRUCTIONS ---\n${customInstructions}`;
200
+ }
201
+ }
202
+ catch (error) {
203
+ console.error('Failed to read user.md:', error);
204
+ }
175
205
  }
176
206
  // Read policy.yaml for NLP security constraints
177
207
  try {
@@ -226,7 +256,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
226
256
  exports.logger.addEntry({ role, content: input }, sessionId);
227
257
  const history = exports.logger.getHistory(sessionId);
228
258
  // Format messages for OpenAI
229
- const messages = [
259
+ let messages = [
230
260
  { role: 'system', content: getSystemPrompt() },
231
261
  ...history
232
262
  .filter(m => !(m.role === 'tool' && !m.tool_call_id))
@@ -244,6 +274,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
244
274
  return msg;
245
275
  })
246
276
  ];
277
+ // Remove orphaned tool responses (truncated by the 40-message limit) at the start of the history window
278
+ while (messages.length > 1 && messages[1].role === 'tool') {
279
+ messages.splice(1, 1);
280
+ }
247
281
  try {
248
282
  const lowerInput = input.toLowerCase();
249
283
  const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
@@ -252,7 +286,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
252
286
  if ((0, skillManager_1.isSkillActive)('web3')) {
253
287
  tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, createMarketWatchAgent_1.createMarketWatchAgentToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition, createLimitOrder_1.createLimitOrderToolDefinition);
254
288
  }
255
- const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition];
289
+ const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateIdentity_1.updateIdentityToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition, scheduleTask_1.scheduleTaskDefinition, cancelTask_1.cancelTaskDefinition];
256
290
  const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
257
291
  let activeTools = [];
258
292
  if (hasGoogleKeyword && !hasWeb3Keyword) {
@@ -412,6 +446,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
412
446
  result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
413
447
  break;
414
448
  }
449
+ case 'update_identity': {
450
+ result = (0, updateIdentity_1.updateIdentity)(args.content, args.mode);
451
+ break;
452
+ }
415
453
  case 'update_security_policy': {
416
454
  result = await (0, updateSecurityPolicy_1.updateSecurityPolicy)(args.policy, args.action || 'add');
417
455
  break;
@@ -468,6 +506,14 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
468
506
  result = await (0, searchWeb_1.searchWeb)(args.query, args.depth);
469
507
  break;
470
508
  }
509
+ case 'schedule_task': {
510
+ result = await (0, scheduleTask_1.executeScheduleTask)(args);
511
+ break;
512
+ }
513
+ case 'cancel_task': {
514
+ result = await (0, cancelTask_1.executeCancelTask)(args);
515
+ break;
516
+ }
471
517
  case 'read_gmail_inbox': {
472
518
  result = await (0, googleWorkspace_1.readGmailInbox)(args.maxResults);
473
519
  break;
@@ -551,11 +597,28 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
551
597
  tracker_1.Tracker.addTokens(secondResponse.usage.total_tokens, config.llm.provider);
552
598
  }
553
599
  tracker_1.Tracker.addEvent('llm.final_response', { provider: config.llm.provider });
554
- const finalContent = secondResponse.choices[0].message.content || "";
600
+ let finalContent = secondResponse.choices[0].message.content || "";
601
+ // Clean up orphaned <think> blocks that forgot to output </think>
602
+ finalContent = finalContent.replace(/<thought>[\s\S]*?<\/thought>\n?/gi, '');
603
+ finalContent = finalContent.replace(/<think>[\s\S]*?<\/think>\n?/gi, '');
604
+ if (finalContent.includes('<think>')) {
605
+ finalContent = finalContent.replace(/<think>[\s\S]*?\n\n/i, '');
606
+ finalContent = finalContent.replace(/<think>[\s\S]*$/i, '');
607
+ }
608
+ finalContent = finalContent.trim();
555
609
  exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
556
610
  return finalContent;
557
611
  }
558
- return responseMessage.content || "No response generated.";
612
+ let finalContent = responseMessage.content || "No response generated.";
613
+ // Clean up orphaned <think> blocks that forgot to output </think>
614
+ finalContent = finalContent.replace(/<thought>[\s\S]*?<\/thought>\n?/gi, '');
615
+ finalContent = finalContent.replace(/<think>[\s\S]*?<\/think>\n?/gi, '');
616
+ if (finalContent.includes('<think>')) {
617
+ finalContent = finalContent.replace(/<think>[\s\S]*?\n\n/i, '');
618
+ finalContent = finalContent.replace(/<think>[\s\S]*$/i, '');
619
+ }
620
+ finalContent = finalContent.trim();
621
+ return finalContent;
559
622
  }
560
623
  catch (error) {
561
624
  console.error("LLM Error:", error);
@@ -6,13 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.txManager = void 0;
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
9
+ const paths_1 = require("../config/paths");
10
10
  class TransactionManager {
11
11
  transactions = new Map();
12
12
  withdrawals = new Map();
13
13
  dbPath;
14
14
  constructor() {
15
- this.dbPath = path_1.default.join(process.cwd(), '.nyxora_withdrawals.json');
15
+ this.dbPath = (0, paths_1.getPath)('.nyxora_withdrawals.json');
16
16
  this.loadWithdrawals();
17
17
  }
18
18
  loadWithdrawals() {
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.updateIdentityToolDefinition = void 0;
7
+ exports.updateIdentity = updateIdentity;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const paths_1 = require("../config/paths");
10
+ const parser_1 = require("../config/parser");
11
+ function updateIdentity(content, mode, agentName) {
12
+ try {
13
+ const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
14
+ if (mode === 'replace') {
15
+ fs_1.default.writeFileSync(identityMdPath, content, 'utf8');
16
+ let msg = "Identity replaced successfully. New IDENTITY.md has been saved.";
17
+ if (agentName) {
18
+ const config = (0, parser_1.loadConfig)();
19
+ config.agent.name = agentName;
20
+ (0, parser_1.saveConfig)(config);
21
+ msg += ` Agent Name updated to '${agentName}' in config.`;
22
+ }
23
+ return msg;
24
+ }
25
+ else {
26
+ let existingContent = "";
27
+ if (fs_1.default.existsSync(identityMdPath)) {
28
+ existingContent = fs_1.default.readFileSync(identityMdPath, 'utf8');
29
+ }
30
+ const newContent = existingContent + "\n" + content;
31
+ fs_1.default.writeFileSync(identityMdPath, newContent, 'utf8');
32
+ let msg = "Identity appended successfully. New instructions added to IDENTITY.md.";
33
+ if (agentName) {
34
+ const config = (0, parser_1.loadConfig)();
35
+ config.agent.name = agentName;
36
+ (0, parser_1.saveConfig)(config);
37
+ msg += ` Agent Name updated to '${agentName}' in config.`;
38
+ }
39
+ return msg;
40
+ }
41
+ }
42
+ catch (error) {
43
+ return `Failed to update identity: ${error.message}`;
44
+ }
45
+ }
46
+ exports.updateIdentityToolDefinition = {
47
+ type: "function",
48
+ function: {
49
+ name: "update_identity",
50
+ description: "Updates or rewrites the IDENTITY.md file. Use this when the user sets or changes your AI name, persona, or core character instructions.",
51
+ parameters: {
52
+ type: "object",
53
+ properties: {
54
+ content: {
55
+ type: "string",
56
+ description: "The content to write or append to IDENTITY.md",
57
+ },
58
+ mode: {
59
+ type: "string",
60
+ enum: ["append", "replace"],
61
+ description: "Whether to append the content to the existing file or replace the entire file.",
62
+ },
63
+ agentName: {
64
+ type: "string",
65
+ description: "The short display name of the AI agent (e.g. Hinata, Nyxora). MUST be provided if the user assigns you a new name.",
66
+ }
67
+ },
68
+ required: ["content", "mode"],
69
+ },
70
+ },
71
+ };
@@ -8,27 +8,12 @@ exports.getPath = getPath;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const os_1 = __importDefault(require("os"));
11
- let isGlobalModeCache = null;
12
11
  function getAppDir() {
13
- // Check if .env or config.yaml exists in current working directory
14
- if (isGlobalModeCache === null) {
15
- const localEnv = path_1.default.join(process.cwd(), '.env');
16
- const localConfig = path_1.default.join(process.cwd(), 'config.yaml');
17
- if (fs_1.default.existsSync(localEnv) || fs_1.default.existsSync(localConfig)) {
18
- isGlobalModeCache = false; // Local manual mode
19
- }
20
- else {
21
- isGlobalModeCache = true; // Global CLI mode
22
- }
23
- }
24
- if (isGlobalModeCache) {
25
- const globalDir = path_1.default.join(os_1.default.homedir(), '.nyxora');
26
- if (!fs_1.default.existsSync(globalDir)) {
27
- fs_1.default.mkdirSync(globalDir, { recursive: true });
28
- }
29
- return globalDir;
12
+ const globalDir = path_1.default.join(os_1.default.homedir(), '.nyxora');
13
+ if (!fs_1.default.existsSync(globalDir)) {
14
+ fs_1.default.mkdirSync(globalDir, { recursive: true });
30
15
  }
31
- return process.cwd();
16
+ return globalDir;
32
17
  }
33
18
  function ensureDir(dir) {
34
19
  if (!fs_1.default.existsSync(dir)) {
@@ -49,7 +34,7 @@ function getPath(filename) {
49
34
  else if (lowerFile.endsWith('.token') || lowerFile.includes('vault') || lowerFile.includes('credentials')) {
50
35
  subDir = 'auth';
51
36
  }
52
- else if (lowerFile.endsWith('.log') || lowerFile.includes('pid')) {
37
+ else if (lowerFile.endsWith('.log') || lowerFile.includes('pid') || lowerFile.includes('tracker')) {
53
38
  subDir = 'run';
54
39
  }
55
40
  const targetDir = path_1.default.join(baseDir, subDir);
@@ -73,6 +73,44 @@ async function chatInteractive() {
73
73
  // Strip <think> tags for clean UI
74
74
  finalReply = finalReply.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
75
75
  console.log(finalReply + '\n');
76
+ // Check for pending transactions
77
+ try {
78
+ const txRes = await fetch('http://localhost:3000/api/transactions', {
79
+ headers: { 'x-nyxora-token': token }
80
+ });
81
+ if (txRes.ok) {
82
+ const txs = await txRes.json();
83
+ for (const tx of txs) {
84
+ const isApproved = await (0, prompts_1.confirm)({
85
+ message: picocolors_1.default.yellow(`Approve Transaction [${tx.type.toUpperCase()}] on ${tx.chainName.toUpperCase()}?`),
86
+ });
87
+ if ((0, prompts_1.isCancel)(isApproved) || !isApproved) {
88
+ await fetch(`http://localhost:3000/api/transactions/${tx.id}/reject`, {
89
+ method: 'POST',
90
+ headers: { 'Content-Type': 'application/json', 'x-nyxora-token': token },
91
+ body: JSON.stringify({ nonce: tx.nonce, sessionId: 'cli-chat' })
92
+ });
93
+ console.log(picocolors_1.default.red(`Transaction rejected.\n`));
94
+ continue;
95
+ }
96
+ const appRes = await fetch(`http://localhost:3000/api/transactions/${tx.id}/approve`, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json', 'x-nyxora-token': token },
99
+ body: JSON.stringify({ nonce: tx.nonce, sessionId: 'cli-chat' })
100
+ });
101
+ const appData = await appRes.json();
102
+ if (appData.success) {
103
+ console.log(picocolors_1.default.green(`Transaction approved! Processing in background...\n`));
104
+ }
105
+ else {
106
+ console.log(picocolors_1.default.red(`Failed to approve: ${appData.error}\n`));
107
+ }
108
+ }
109
+ }
110
+ }
111
+ catch (e) {
112
+ // silently ignore fetch errors for tx polling
113
+ }
76
114
  }
77
115
  catch (error) {
78
116
  s.stop(picocolors_1.default.red('Connection failed.'));
@@ -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)();