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.
- package/bin/nyxora.mjs +14 -2
- package/dist/packages/core/src/agent/cronManager.js +107 -0
- package/dist/packages/core/src/agent/reasoning.js +85 -22
- package/dist/packages/core/src/agent/transactionManager.js +2 -2
- package/dist/packages/core/src/agent/updateIdentity.js +71 -0
- package/dist/packages/core/src/config/paths.js +5 -20
- package/dist/packages/core/src/gateway/chat.js +38 -0
- package/dist/packages/core/src/gateway/cli.js +20 -20
- package/dist/packages/core/src/gateway/server.js +43 -8
- package/dist/packages/core/src/gateway/telegram.js +43 -0
- package/dist/packages/core/src/gateway/tracker.js +58 -0
- package/dist/packages/core/src/memory/logger.js +1 -1
- package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
- package/dist/packages/core/src/system/skills/editFile.js +5 -0
- package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
- package/dist/packages/core/src/system/skills/writeFile.js +5 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
- package/dist/packages/policy/src/server.js +1 -1
- package/package.json +4 -1
- package/packages/core/package.json +5 -1
- package/packages/core/src/agent/cronManager.ts +87 -0
- package/packages/core/src/agent/reasoning.ts +91 -21
- package/packages/core/src/agent/transactionManager.ts +2 -1
- package/packages/core/src/agent/updateIdentity.ts +68 -0
- package/packages/core/src/config/paths.ts +5 -23
- package/packages/core/src/gateway/chat.ts +40 -1
- package/packages/core/src/gateway/cli.ts +10 -10
- package/packages/core/src/gateway/server.ts +44 -7
- package/packages/core/src/gateway/telegram.ts +49 -0
- package/packages/core/src/gateway/tracker.ts +55 -0
- package/packages/core/src/memory/logger.ts +1 -1
- package/packages/core/src/system/skills/cancelTask.ts +38 -0
- package/packages/core/src/system/skills/editFile.ts +7 -0
- package/packages/core/src/system/skills/scheduleTask.ts +38 -0
- package/packages/core/src/system/skills/writeFile.ts +7 -0
- package/packages/core/src/web3/skills/getPrice.ts +1 -1
- package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
- package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/dist/routers/0x.png +0 -0
- package/packages/dashboard/dist/routers/1inch.png +0 -0
- package/packages/dashboard/dist/routers/cmc.png +0 -0
- package/packages/dashboard/dist/routers/kyberswap.png +0 -0
- package/packages/dashboard/dist/routers/lifi.png +0 -0
- package/packages/dashboard/dist/routers/openocean.png +0 -0
- package/packages/dashboard/dist/routers/relay.png +0 -0
- package/packages/dashboard/dist/routers/zerion.png +0 -0
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +1 -1
- package/packages/signer/package.json +1 -1
- package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
- 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
|
-
|
|
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
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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 (
|
|
163
|
-
|
|
172
|
+
catch (e) {
|
|
173
|
+
isFirstTime = true;
|
|
164
174
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
14
|
-
if (
|
|
15
|
-
|
|
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
|
|
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
|
|
221
|
+
// 2. Setup boilerplate files since we enforce global mode
|
|
223
222
|
let isFirstBoot = false;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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 (
|
|
240
|
-
fs_1.default.
|
|
233
|
+
if (fs_1.default.existsSync(exampleConfigPath)) {
|
|
234
|
+
fs_1.default.copyFileSync(exampleConfigPath, globalConfigPath);
|
|
241
235
|
}
|
|
242
|
-
|
|
243
|
-
fs_1.default.writeFileSync(
|
|
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)();
|