nyxora 26.6.13 → 26.6.18
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/README.md +4 -3
- package/bin/nyxora.mjs +3 -4
- package/dist/launcher.js +18 -7
- package/dist/packages/core/src/agent/reasoning.js +21 -52
- package/dist/packages/core/src/config/parser.js +48 -11
- package/dist/packages/core/src/gateway/server.js +71 -9
- package/dist/packages/core/src/gateway/setup.js +5 -4
- package/dist/packages/core/src/gateway/telegram.js +9 -19
- package/dist/packages/core/src/system/skills/updateSecurityPolicy.js +17 -11
- package/dist/packages/core/src/utils/formatter.js +13 -18
- package/dist/packages/core/src/web3/skills/createMarketWatchAgent.js +51 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
- package/dist/packages/core/src/web3/skills/marketAnalysis.js +208 -47
- package/dist/packages/core/src/web3/utils/riskIntelligence.js +110 -0
- package/dist/packages/policy/src/server.js +38 -3
- package/dist/packages/signer/src/server.js +2 -2
- package/launcher.ts +18 -7
- package/package.json +5 -7
- package/packages/core/package.json +1 -2
- package/packages/core/src/agent/reasoning.ts +24 -50
- package/packages/core/src/config/parser.ts +49 -22
- package/packages/core/src/gateway/server.ts +76 -10
- package/packages/core/src/gateway/setup.ts +6 -5
- package/packages/core/src/gateway/telegram.ts +9 -19
- package/packages/core/src/system/skills/updateSecurityPolicy.ts +18 -11
- package/packages/core/src/utils/formatter.ts +13 -17
- package/packages/core/src/web3/skills/createMarketWatchAgent.ts +59 -0
- package/packages/core/src/web3/skills/getPrice.ts +1 -1
- package/packages/core/src/web3/skills/marketAnalysis.ts +209 -49
- package/packages/core/src/web3/utils/riskIntelligence.ts +118 -0
- package/packages/dashboard/dist/assets/index-BAXifdMN.js +16 -0
- package/packages/dashboard/dist/index.html +1 -1
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/dist/server.js +110 -0
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +41 -4
- package/packages/signer/package.json +1 -1
- package/packages/signer/src/server.ts +2 -2
- package/packages/core/src/system/pluginManager.ts +0 -106
- package/packages/core/src/system/skills/installSkill.ts +0 -51
- package/packages/dashboard/dist/assets/index-BhKhEfi_.js +0 -13
|
@@ -171,7 +171,7 @@ app.post('/sign-transaction', async (req, res) => {
|
|
|
171
171
|
const rpcNonce = await client.getTransactionCount({ address: account.address, blockTag: 'pending' });
|
|
172
172
|
let nextNonce = Math.max(rpcNonce, nonceCache[chainId] || 0);
|
|
173
173
|
const txRequest = txPayload.details?.txRequest || txPayload.details?.txData || txPayload;
|
|
174
|
-
// Phase 2: Transaction Simulation (Dry-Run / Anti-
|
|
174
|
+
// Phase 2: Transaction Simulation (Dry-Run / Anti-Fail)
|
|
175
175
|
try {
|
|
176
176
|
await client.estimateGas({
|
|
177
177
|
account,
|
|
@@ -225,7 +225,7 @@ app.listen(SOCKET_PATH, () => {
|
|
|
225
225
|
console.error('Failed to chmod socket:', err);
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
|
-
// Phase 3: Graceful Shutdown (
|
|
228
|
+
// Phase 3: Graceful Shutdown (Local Keyring Security)
|
|
229
229
|
const gracefulShutdown = () => {
|
|
230
230
|
console.log('[Signer Vault] Received shutdown signal. Locking vault...');
|
|
231
231
|
// @ts-ignore
|
package/launcher.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-ignore
|
|
1
2
|
import { initSafeLogger } from './packages/core/src/utils/safeLogger';
|
|
2
3
|
initSafeLogger();
|
|
3
4
|
|
|
@@ -38,7 +39,14 @@ const spawnService = (name: string, command: string, args: string[], env: any, i
|
|
|
38
39
|
|
|
39
40
|
if (!inheritStdio) {
|
|
40
41
|
child.stdout?.on('data', (data) => process.stdout.write(`[${name}] ${data}`));
|
|
41
|
-
child.stderr?.on('data', (data) =>
|
|
42
|
+
child.stderr?.on('data', (data) => {
|
|
43
|
+
const msg = data.toString();
|
|
44
|
+
if (msg.toLowerCase().includes('warn')) {
|
|
45
|
+
process.stderr.write(`[${name}] ${msg}`);
|
|
46
|
+
} else {
|
|
47
|
+
process.stderr.write(`[${name}] ERROR: ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
child.on('close', async (code) => {
|
|
@@ -109,22 +117,25 @@ if (fs.existsSync(socketPath)) {
|
|
|
109
117
|
|
|
110
118
|
const children: { kill: () => void }[] = [];
|
|
111
119
|
|
|
112
|
-
const
|
|
120
|
+
const __filenameResolved = __filename;
|
|
121
|
+
const __dirnameResolved = __dirname;
|
|
122
|
+
|
|
123
|
+
const isCompiled = __filenameResolved.endsWith('.js');
|
|
113
124
|
const ext = isCompiled ? '.js' : '.ts';
|
|
114
|
-
const cmd = isCompiled ? 'node' : '
|
|
115
|
-
const baseArgs = isCompiled ? [] : ['
|
|
125
|
+
const cmd = isCompiled ? 'node' : path.join(__dirnameResolved, 'node_modules', '.bin', 'ts-node');
|
|
126
|
+
const baseArgs = isCompiled ? [] : ['-T'];
|
|
116
127
|
|
|
117
|
-
const signerPath = path.join(
|
|
128
|
+
const signerPath = path.join(__dirnameResolved, `packages/signer/src/server${ext}`);
|
|
118
129
|
const signer = spawnService('Signer', cmd, [...baseArgs, signerPath], env);
|
|
119
130
|
children.push(signer);
|
|
120
131
|
|
|
121
132
|
setTimeout(() => {
|
|
122
|
-
const policyPath = path.join(
|
|
133
|
+
const policyPath = path.join(__dirnameResolved, `packages/policy/src/server${ext}`);
|
|
123
134
|
const policy = spawnService('Policy', cmd, [...baseArgs, policyPath], env);
|
|
124
135
|
children.push(policy);
|
|
125
136
|
|
|
126
137
|
setTimeout(() => {
|
|
127
|
-
const corePath = path.join(
|
|
138
|
+
const corePath = path.join(__dirnameResolved, `packages/core/src/gateway/cli${ext}`);
|
|
128
139
|
const args = process.argv.slice(2);
|
|
129
140
|
const core = spawnService('Core', cmd, [...baseArgs, corePath, ...args], env, true);
|
|
130
141
|
children.push(core);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.18",
|
|
4
4
|
"description": "Your Personal Web3 Assistant",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web3",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"packages/signer/src",
|
|
28
28
|
"packages/signer/package.json",
|
|
29
29
|
"packages/mcp-server/src",
|
|
30
|
+
"packages/mcp-server/dist",
|
|
30
31
|
"packages/mcp-server/package.json",
|
|
31
32
|
"packages/dashboard/dist",
|
|
32
33
|
"packages/dashboard/package.json"
|
|
@@ -35,8 +36,8 @@
|
|
|
35
36
|
"nyxora": "bin/nyxora.mjs"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
|
-
"prepare": "npm run build
|
|
39
|
-
"dev": "concurrently -n \"BACKEND,FRONTEND\" -c \"blue,green\" \"
|
|
39
|
+
"prepare": "npm run build",
|
|
40
|
+
"dev": "concurrently -n \"BACKEND,FRONTEND\" -c \"blue,green\" \"NODE_NO_WARNINGS=1 ./node_modules/.bin/ts-node -T launcher.ts\" \"npm run dev --workspace=nyxora-dashboard\"",
|
|
40
41
|
"start": "node ./bin/nyxora.mjs start",
|
|
41
42
|
"stop": "node ./bin/nyxora.mjs stop",
|
|
42
43
|
"restart": "node ./bin/nyxora.mjs restart",
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"setup": "node ./bin/nyxora.mjs setup",
|
|
45
46
|
"set-key": "node ./bin/nyxora.mjs set-key",
|
|
46
47
|
"doctor": "node ./bin/nyxora.mjs doctor",
|
|
47
|
-
"build": "tsc && npm run build --workspace=nyxora-dashboard",
|
|
48
|
+
"build": "tsc && npm run build --workspace=nyxora-mcp-server && npm run build --workspace=nyxora-dashboard",
|
|
48
49
|
"test": "npm run test --workspace=packages/core"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
@@ -98,8 +99,5 @@
|
|
|
98
99
|
"concurrently": "^10.0.3",
|
|
99
100
|
"oxc-minify": "^0.135.0",
|
|
100
101
|
"vitepress": "^2.0.0-alpha.17"
|
|
101
|
-
},
|
|
102
|
-
"allowScripts": {
|
|
103
|
-
"isolated-vm": "true"
|
|
104
102
|
}
|
|
105
103
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora-agent-core",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.17",
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "src/gateway/server.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"express": "^5.2.1",
|
|
14
14
|
"express-rate-limit": "^7.5.0",
|
|
15
15
|
"helmet": "^8.0.0",
|
|
16
|
-
"isolated-vm": "^6.1.2",
|
|
17
16
|
"mammoth": "^1.12.0",
|
|
18
17
|
"open": "^11.0.0",
|
|
19
18
|
"openai": "^6.39.0",
|
|
@@ -16,6 +16,7 @@ import { customTxToolDefinition, prepareCustomTx } from '../web3/skills/customTx
|
|
|
16
16
|
|
|
17
17
|
import { checkSecurityToolDefinition, checkTokenSecurity } from '../web3/skills/checkSecurity';
|
|
18
18
|
import { marketAnalysisToolDefinition, analyzeMarket } from '../web3/skills/marketAnalysis';
|
|
19
|
+
import { createMarketWatchAgentToolDefinition, createMarketWatchAgent } from '../web3/skills/createMarketWatchAgent';
|
|
19
20
|
import { checkPortfolioToolDefinition, checkPortfolio } from '../web3/skills/checkPortfolio';
|
|
20
21
|
import { checkAddressToolDefinition, checkAddress } from '../web3/skills/checkAddress';
|
|
21
22
|
import { getMyAddressToolDefinition, getMyAddress } from '../web3/skills/getMyAddress';
|
|
@@ -36,7 +37,7 @@ import { generateExcelToolDefinition, generateExcelFile } from '../system/skills
|
|
|
36
37
|
import { runTerminalCommandToolDefinition, runTerminalCommand } from '../system/skills/executeShell';
|
|
37
38
|
import { browseWebsiteToolDefinition, browseWebsite } from '../system/skills/browseWeb';
|
|
38
39
|
import { searchWebToolDefinition, searchWeb } from '../system/skills/searchWeb';
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
import { editLocalFileToolDefinition, editLocalFile } from '../system/skills/editFile';
|
|
41
42
|
import { gitManagerToolDefinition, executeGitCommand } from '../system/skills/gitManager';
|
|
42
43
|
import { xManagerToolDefinition, manageTwitter } from '../system/skills/xManager';
|
|
@@ -55,7 +56,7 @@ import {
|
|
|
55
56
|
readGoogleDocsToolDefinition,
|
|
56
57
|
readGoogleFormResponsesToolDefinition
|
|
57
58
|
} from '../system/skills/googleWorkspace';
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
import { getPath } from '../config/paths';
|
|
60
61
|
import pc from 'picocolors';
|
|
61
62
|
|
|
@@ -156,7 +157,7 @@ IMPORTANT: The <think> block is strictly for your internal hidden monologue. NEV
|
|
|
156
157
|
|
|
157
158
|
[EXECUTION WORKFLOW]
|
|
158
159
|
CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
|
|
159
|
-
CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt.
|
|
160
|
+
CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt. Render all output, metric labels, and suggested actions entirely in the language the user initiated the prompt with, while strictly preserving the visual structure of the progress bars.
|
|
160
161
|
CRITICAL RULE 3: FORMATTING & CONCISENESS. Provide concise analytical summaries of data rather than just dumping raw markdown tables. Be analytical but brief. Use commas for thousands.
|
|
161
162
|
CRITICAL RULE 4: TOOL PRIORITIZATION. Web3 tasks must use Web3 Skills exclusively. OS Skills (search, browse) are fallbacks only. Use get_my_address to show wallet address, and check_portfolio to show balances.
|
|
162
163
|
CRITICAL RULE 5: DEFAULT CHAIN HANDLING. Default to: ${config.agent.default_chain} unless specified. If overridden, confirm the chain politely. For 2-chain txs (bridge), default source to ${config.agent.default_chain}.
|
|
@@ -197,15 +198,19 @@ CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token sec
|
|
|
197
198
|
console.error('Failed to read user.md:', error);
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
// Read
|
|
201
|
+
// Read policy.yaml for NLP security constraints
|
|
201
202
|
try {
|
|
202
|
-
const policyPath = getPath('
|
|
203
|
+
const policyPath = getPath('policy.yaml');
|
|
203
204
|
if (fs.existsSync(policyPath)) {
|
|
204
|
-
const
|
|
205
|
-
|
|
205
|
+
const yaml = require('yaml'); // lazily import if not imported
|
|
206
|
+
const file = fs.readFileSync(policyPath, 'utf8');
|
|
207
|
+
const parsed = yaml.parse(file) || {};
|
|
208
|
+
if (parsed.custom_llm_rules && Array.isArray(parsed.custom_llm_rules) && parsed.custom_llm_rules.length > 0) {
|
|
209
|
+
basePrompt += `\n\n--- SECURITY POLICY (MANDATORY RULES) ---\n${parsed.custom_llm_rules.map((r: string) => `* ${r}`).join('\n')}\n\nCRITICAL: If the user asks you to perform an action that violates the Security Policy above, YOU MUST NOT EXECUTE IT DIRECTLY. Instead, ask for their explicit permission first.`;
|
|
210
|
+
}
|
|
206
211
|
}
|
|
207
212
|
} catch (error) {
|
|
208
|
-
console.error('Failed to read
|
|
213
|
+
console.error('Failed to read policy.yaml:', error);
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
// Inject Episodic Memories (Smart Suggestions Context)
|
|
@@ -285,6 +290,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
285
290
|
customTxToolDefinition,
|
|
286
291
|
checkSecurityToolDefinition,
|
|
287
292
|
marketAnalysisToolDefinition,
|
|
293
|
+
createMarketWatchAgentToolDefinition,
|
|
288
294
|
checkPortfolioToolDefinition,
|
|
289
295
|
checkAddressToolDefinition,
|
|
290
296
|
getMyAddressToolDefinition,
|
|
@@ -297,16 +303,16 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
297
303
|
createLimitOrderToolDefinition
|
|
298
304
|
);
|
|
299
305
|
}
|
|
300
|
-
const SYSTEM_TOOLS = [updateProfileToolDefinition, updateSecurityPolicyToolDefinition, analyzeDocumentToolDefinition, readLocalFileToolDefinition, writeLocalFileToolDefinition, generateExcelToolDefinition, runTerminalCommandToolDefinition, browseWebsiteToolDefinition, searchWebToolDefinition,
|
|
306
|
+
const SYSTEM_TOOLS = [updateProfileToolDefinition, updateSecurityPolicyToolDefinition, analyzeDocumentToolDefinition, readLocalFileToolDefinition, writeLocalFileToolDefinition, generateExcelToolDefinition, runTerminalCommandToolDefinition, browseWebsiteToolDefinition, searchWebToolDefinition, editLocalFileToolDefinition, gitManagerToolDefinition, xManagerToolDefinition, notionWorkspaceToolDefinition, audioTranscribeToolDefinition, summarizeTextToolDefinition];
|
|
301
307
|
const GOOGLE_TOOLS = [readGmailInboxToolDefinition, listCalendarEventsToolDefinition, appendRowToSheetsToolDefinition, readGoogleDocsToolDefinition, readGoogleFormResponsesToolDefinition];
|
|
302
308
|
|
|
303
309
|
let activeTools: any[] = [];
|
|
304
310
|
if (hasGoogleKeyword && !hasWeb3Keyword) {
|
|
305
|
-
activeTools = [...GOOGLE_TOOLS, ...SYSTEM_TOOLS
|
|
311
|
+
activeTools = [...GOOGLE_TOOLS, ...SYSTEM_TOOLS];
|
|
306
312
|
} else if (hasWeb3Keyword && !hasGoogleKeyword) {
|
|
307
|
-
activeTools = [...tools, ...SYSTEM_TOOLS
|
|
313
|
+
activeTools = [...tools, ...SYSTEM_TOOLS];
|
|
308
314
|
} else {
|
|
309
|
-
activeTools = [...tools, ...SYSTEM_TOOLS, ...GOOGLE_TOOLS
|
|
315
|
+
activeTools = [...tools, ...SYSTEM_TOOLS, ...GOOGLE_TOOLS];
|
|
310
316
|
}
|
|
311
317
|
activeTools = activeTools.filter(t => isSkillActive(t.function.name));
|
|
312
318
|
|
|
@@ -376,10 +382,6 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
376
382
|
}
|
|
377
383
|
case 'transfer_token':
|
|
378
384
|
case 'transfer_native': {
|
|
379
|
-
if (config.permissions?.web3?.allow_transfer === false) {
|
|
380
|
-
result = `[Security Blocked] Runtime Permission Denied: Web3 transfers are disabled. Update config.yaml to allow.`;
|
|
381
|
-
break;
|
|
382
|
-
}
|
|
383
385
|
result = await prepareTransfer(args.chainName, args.toAddress, args.amountStr || args.amountEth, args.token);
|
|
384
386
|
break;
|
|
385
387
|
}
|
|
@@ -388,18 +390,10 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
388
390
|
break;
|
|
389
391
|
}
|
|
390
392
|
case 'swap_token': {
|
|
391
|
-
if (config.permissions?.web3?.allow_swap === false) {
|
|
392
|
-
result = `[Security Blocked] Runtime Permission Denied: Web3 swaps are disabled. Update config.yaml to allow.`;
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
395
393
|
result = await prepareSwapToken(args.chainName, args.fromToken, args.toToken, args.amountStr || args.amount, args.mode, args.providerName);
|
|
396
394
|
break;
|
|
397
395
|
}
|
|
398
396
|
case 'bridge_token': {
|
|
399
|
-
if (config.permissions?.web3?.allow_transfer === false) {
|
|
400
|
-
result = `[Security Blocked] Runtime Permission Denied: Web3 bridging (transfer) is disabled. Update config.yaml to allow.`;
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
397
|
result = await prepareBridgeToken(args.fromChain, args.toChain, args.tokenSymbol, args.amountStr, args.mode, args.providerName);
|
|
404
398
|
break;
|
|
405
399
|
}
|
|
@@ -408,10 +402,6 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
408
402
|
break;
|
|
409
403
|
}
|
|
410
404
|
case 'custom_tx': {
|
|
411
|
-
if (config.permissions?.web3?.allow_transfer === false) {
|
|
412
|
-
result = `[Security Blocked] Runtime Permission Denied: Custom transactions are blocked because transfers are disabled.`;
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
405
|
result = await prepareCustomTx(args.chainName, args.toAddress, args.dataHex, args.valueEth, args.gasLimitStr);
|
|
416
406
|
break;
|
|
417
407
|
}
|
|
@@ -423,6 +413,10 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
423
413
|
result = await analyzeMarket(args.chainName, args.tokenAddressOrSymbol);
|
|
424
414
|
break;
|
|
425
415
|
}
|
|
416
|
+
case 'create_market_watch_agent': {
|
|
417
|
+
result = await createMarketWatchAgent(args.chainName, args.contractAddress, args.rules, args.durationDays);
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
426
420
|
case 'check_portfolio': {
|
|
427
421
|
result = await checkPortfolio(args.chainName, args.address);
|
|
428
422
|
break;
|
|
@@ -535,26 +529,14 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
535
529
|
break;
|
|
536
530
|
}
|
|
537
531
|
case 'write_local_file': {
|
|
538
|
-
if (config.permissions?.system?.allow_file_write === false) {
|
|
539
|
-
result = `[Security Blocked] Runtime Permission Denied: File writing is disabled. Update config.yaml to allow.`;
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
532
|
result = writeLocalFile(args.filePath, args.content);
|
|
543
533
|
break;
|
|
544
534
|
}
|
|
545
535
|
case 'generate_excel_file': {
|
|
546
|
-
if (config.permissions?.system?.allow_file_write === false) {
|
|
547
|
-
result = `[Security Blocked] Runtime Permission Denied: File writing is disabled. Update config.yaml to allow.`;
|
|
548
|
-
break;
|
|
549
|
-
}
|
|
550
536
|
result = await generateExcelFile(args.data, args.filePath);
|
|
551
537
|
break;
|
|
552
538
|
}
|
|
553
539
|
case 'run_terminal_command': {
|
|
554
|
-
if (config.permissions?.system?.allow_shell_execution === false) {
|
|
555
|
-
result = `[Security Blocked] Runtime Permission Denied: Shell execution is disabled. Update config.yaml to allow.`;
|
|
556
|
-
break;
|
|
557
|
-
}
|
|
558
540
|
result = await runTerminalCommand(args.command);
|
|
559
541
|
break;
|
|
560
542
|
}
|
|
@@ -566,10 +548,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
566
548
|
result = await searchWeb(args.query, args.depth);
|
|
567
549
|
break;
|
|
568
550
|
}
|
|
569
|
-
|
|
570
|
-
result = await installExternalSkill(args.url);
|
|
571
|
-
break;
|
|
572
|
-
}
|
|
551
|
+
|
|
573
552
|
case 'read_gmail_inbox': {
|
|
574
553
|
result = await readGmailInbox(args.maxResults);
|
|
575
554
|
break;
|
|
@@ -591,12 +570,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
591
570
|
break;
|
|
592
571
|
}
|
|
593
572
|
default: {
|
|
594
|
-
|
|
595
|
-
if (externalResult !== null) {
|
|
596
|
-
result = externalResult;
|
|
597
|
-
} else {
|
|
598
|
-
result = `Error: Tool ${toolName} is not implemented.`;
|
|
599
|
-
}
|
|
573
|
+
result = `Error: Tool ${toolName} is not implemented.`;
|
|
600
574
|
break;
|
|
601
575
|
}
|
|
602
576
|
}
|
|
@@ -3,6 +3,27 @@ import yaml from 'yaml';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { getPath } from './paths';
|
|
5
5
|
|
|
6
|
+
export function loadRpcConfig(): Record<string, string | string[]> {
|
|
7
|
+
const rpcPath = getPath('rpc_key.yaml');
|
|
8
|
+
if (fs.existsSync(rpcPath)) {
|
|
9
|
+
try {
|
|
10
|
+
return yaml.parse(fs.readFileSync(rpcPath, 'utf8')) || {};
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error('[Config] Failed to parse rpc_key.yaml', e);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function saveRpcConfig(rpcUrls: Record<string, string | string[]>): void {
|
|
19
|
+
const rpcPath = getPath('rpc_key.yaml');
|
|
20
|
+
try {
|
|
21
|
+
fs.writeFileSync(rpcPath, yaml.stringify(rpcUrls), 'utf8');
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Failed to save rpc_key.yaml', error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
6
27
|
export async function loadApiKeys(): Promise<Record<string, string>> {
|
|
7
28
|
const config = loadConfig();
|
|
8
29
|
return config.credentials || {};
|
|
@@ -64,21 +85,13 @@ export interface NyxoraConfig {
|
|
|
64
85
|
channels?: {
|
|
65
86
|
active: string[];
|
|
66
87
|
};
|
|
67
|
-
permissions?: {
|
|
68
|
-
web3?: {
|
|
69
|
-
allow_transfer?: boolean;
|
|
70
|
-
allow_swap?: boolean;
|
|
71
|
-
max_usd_per_tx?: number;
|
|
72
|
-
};
|
|
73
|
-
system?: {
|
|
74
|
-
allow_shell_execution?: boolean;
|
|
75
|
-
allow_file_write?: boolean;
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
export function loadConfig(): NyxoraConfig {
|
|
81
91
|
const configPath = getPath('config.yaml');
|
|
92
|
+
const rpcPath = getPath('rpc_key.yaml');
|
|
93
|
+
let rpcUrls = loadRpcConfig();
|
|
94
|
+
|
|
82
95
|
try {
|
|
83
96
|
const file = fs.readFileSync(configPath, 'utf8');
|
|
84
97
|
const parsed = yaml.parse(file) as Partial<NyxoraConfig>;
|
|
@@ -99,6 +112,24 @@ export function loadConfig(): NyxoraConfig {
|
|
|
99
112
|
needsSave = true;
|
|
100
113
|
}
|
|
101
114
|
|
|
115
|
+
// Ensure we don't accidentally overwrite rpc_key.yaml with old config.yaml data.
|
|
116
|
+
if (parsed.web3 && parsed.web3.rpc_urls) {
|
|
117
|
+
delete parsed.web3.rpc_urls;
|
|
118
|
+
needsSave = true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Auto-migration logic: move permissions to policy.yaml
|
|
122
|
+
const policyPath = getPath('policy.yaml');
|
|
123
|
+
if (!fs.existsSync(policyPath)) {
|
|
124
|
+
const defaultPolicy = `max_usd_per_tx: ${(parsed as any).permissions?.web3?.max_usd_per_tx || 999999999}\nwhitelist_only: false\nrequire_approval: true\n`;
|
|
125
|
+
fs.writeFileSync(policyPath, defaultPolicy, 'utf8');
|
|
126
|
+
console.log('[Config] Created default policy.yaml.');
|
|
127
|
+
}
|
|
128
|
+
if ((parsed as any).permissions) {
|
|
129
|
+
delete (parsed as any).permissions;
|
|
130
|
+
needsSave = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
102
133
|
if (needsSave) {
|
|
103
134
|
try {
|
|
104
135
|
const yamlStr = yaml.stringify(parsed);
|
|
@@ -125,13 +156,9 @@ export function loadConfig(): NyxoraConfig {
|
|
|
125
156
|
},
|
|
126
157
|
credentials: parsed.credentials || {},
|
|
127
158
|
memory: parsed.memory || { type: 'file', path: './memory.json' },
|
|
128
|
-
web3: parsed.web3
|
|
159
|
+
web3: { ...parsed.web3, rpc_urls: rpcUrls },
|
|
129
160
|
integrations: parsed.integrations || {
|
|
130
161
|
telegram: { enabled: false }
|
|
131
|
-
},
|
|
132
|
-
permissions: parsed.permissions || {
|
|
133
|
-
web3: { allow_transfer: true, allow_swap: true, max_usd_per_tx: 999999999 },
|
|
134
|
-
system: { allow_shell_execution: true, allow_file_write: true }
|
|
135
162
|
}
|
|
136
163
|
} as NyxoraConfig;
|
|
137
164
|
} catch (error: any) {
|
|
@@ -160,13 +187,9 @@ export function loadConfig(): NyxoraConfig {
|
|
|
160
187
|
},
|
|
161
188
|
credentials: {},
|
|
162
189
|
memory: { type: 'file', path: './memory.json' },
|
|
163
|
-
web3: { rpc_urls:
|
|
190
|
+
web3: { rpc_urls: rpcUrls },
|
|
164
191
|
integrations: {
|
|
165
192
|
telegram: { enabled: false }
|
|
166
|
-
},
|
|
167
|
-
permissions: {
|
|
168
|
-
web3: { allow_transfer: true, allow_swap: true, max_usd_per_tx: 999999999 },
|
|
169
|
-
system: { allow_shell_execution: true, allow_file_write: true }
|
|
170
193
|
}
|
|
171
194
|
};
|
|
172
195
|
}
|
|
@@ -175,7 +198,11 @@ export function loadConfig(): NyxoraConfig {
|
|
|
175
198
|
export function saveConfig(newConfig: NyxoraConfig): void {
|
|
176
199
|
const configPath = getPath('config.yaml');
|
|
177
200
|
try {
|
|
178
|
-
const
|
|
201
|
+
const configToSave = JSON.parse(JSON.stringify(newConfig));
|
|
202
|
+
if (configToSave.web3 && configToSave.web3.rpc_urls) {
|
|
203
|
+
delete configToSave.web3.rpc_urls;
|
|
204
|
+
}
|
|
205
|
+
const yamlStr = yaml.stringify(configToSave);
|
|
179
206
|
fs.writeFileSync(configPath, yamlStr, 'utf8');
|
|
180
207
|
} catch (error) {
|
|
181
208
|
console.error('Failed to save config.yaml', error);
|
|
@@ -21,14 +21,15 @@ import { getPath } from '../config/paths';
|
|
|
21
21
|
import { validateToken, getSessionToken } from '../utils/state';
|
|
22
22
|
|
|
23
23
|
import fs from 'fs';
|
|
24
|
+
import yaml from 'yaml';
|
|
24
25
|
import { processUserInput, logger } from '../agent/reasoning';
|
|
25
|
-
import { loadConfig, saveConfig } from '../config/parser';
|
|
26
|
+
import { loadConfig, saveConfig, loadRpcConfig, saveRpcConfig } from '../config/parser';
|
|
26
27
|
import { loadDefiKeys, saveDefiKeys } from '../config/defiConfigManager';
|
|
27
28
|
import { getPublicClient, SUPPORTED_CHAIN_NAMES, getAddress } from '../web3/config';
|
|
28
29
|
import { TOKEN_MAP, ERC20_ABI } from '../web3/utils/tokens';
|
|
29
30
|
import { Tracker } from './tracker';
|
|
30
31
|
import { txManager } from '../agent/transactionManager';
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
import { executeTransfer, transferToolDefinition } from '../web3/skills/transfer';
|
|
33
34
|
import { executeSwap, swapTokenToolDefinition } from '../web3/skills/swapToken';
|
|
34
35
|
import { getBalanceToolDefinition } from '../web3/skills/getBalance';
|
|
@@ -56,7 +57,7 @@ import { createLimitOrderToolDefinition } from '../web3/skills/createLimitOrder'
|
|
|
56
57
|
// System Skills
|
|
57
58
|
import { browseWebsiteToolDefinition } from '../system/skills/browseWeb';
|
|
58
59
|
import { runTerminalCommandToolDefinition } from '../system/skills/executeShell';
|
|
59
|
-
|
|
60
|
+
|
|
60
61
|
import { readLocalFileToolDefinition } from '../system/skills/readFile';
|
|
61
62
|
import { editLocalFileToolDefinition } from '../system/skills/editFile';
|
|
62
63
|
import { gitManagerToolDefinition } from '../system/skills/gitManager';
|
|
@@ -287,6 +288,26 @@ app.post('/api/config', (req, res) => {
|
|
|
287
288
|
}
|
|
288
289
|
});
|
|
289
290
|
|
|
291
|
+
app.get('/api/rpc', (req, res) => {
|
|
292
|
+
try {
|
|
293
|
+
const rpcConfig = loadRpcConfig();
|
|
294
|
+
res.json(rpcConfig);
|
|
295
|
+
} catch (error: any) {
|
|
296
|
+
res.status(500).json({ error: error.message });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
app.post('/api/rpc', (req, res) => {
|
|
301
|
+
try {
|
|
302
|
+
const currentRpc = loadRpcConfig();
|
|
303
|
+
const newRpc = { ...currentRpc, ...req.body };
|
|
304
|
+
saveRpcConfig(newRpc);
|
|
305
|
+
res.json({ success: true });
|
|
306
|
+
} catch (error: any) {
|
|
307
|
+
res.status(500).json({ error: error.message });
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
290
311
|
app.get('/api/defi-keys', (req, res) => {
|
|
291
312
|
try {
|
|
292
313
|
const keys = loadDefiKeys();
|
|
@@ -356,7 +377,7 @@ app.get('/api/skills/system', (req, res) => {
|
|
|
356
377
|
generateExcelToolDefinition,
|
|
357
378
|
browseWebsiteToolDefinition,
|
|
358
379
|
updateSecurityPolicyToolDefinition,
|
|
359
|
-
|
|
380
|
+
|
|
360
381
|
analyzeDocumentToolDefinition,
|
|
361
382
|
searchWebToolDefinition,
|
|
362
383
|
readGmailInboxToolDefinition,
|
|
@@ -829,6 +850,45 @@ app.delete('/api/memory/:id', (req, res) => {
|
|
|
829
850
|
}
|
|
830
851
|
});
|
|
831
852
|
|
|
853
|
+
// --- Policy Engine Endpoints ---
|
|
854
|
+
app.get('/api/policy', (req, res) => {
|
|
855
|
+
try {
|
|
856
|
+
const policyPath = getPath('policy.yaml');
|
|
857
|
+
if (!fs.existsSync(policyPath)) {
|
|
858
|
+
return res.json({
|
|
859
|
+
max_usd_per_tx: 999999999,
|
|
860
|
+
whitelist_only: false,
|
|
861
|
+
require_approval: true,
|
|
862
|
+
custom_llm_rules: []
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
const file = fs.readFileSync(policyPath, 'utf8');
|
|
866
|
+
const parsed = yaml.parse(file) || {};
|
|
867
|
+
res.json({
|
|
868
|
+
max_usd_per_tx: parsed.max_usd_per_tx ?? 999999999,
|
|
869
|
+
whitelist_only: parsed.whitelist_only ?? false,
|
|
870
|
+
require_approval: parsed.require_approval ?? true,
|
|
871
|
+
custom_llm_rules: parsed.custom_llm_rules || []
|
|
872
|
+
});
|
|
873
|
+
} catch (error: any) {
|
|
874
|
+
res.status(500).json({ error: error.message });
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
app.post('/api/policy', (req, res) => {
|
|
879
|
+
try {
|
|
880
|
+
const policyPath = getPath('policy.yaml');
|
|
881
|
+
let current = {};
|
|
882
|
+
if (fs.existsSync(policyPath)) {
|
|
883
|
+
current = yaml.parse(fs.readFileSync(policyPath, 'utf8')) || {};
|
|
884
|
+
}
|
|
885
|
+
const updated = { ...current, ...req.body };
|
|
886
|
+
fs.writeFileSync(policyPath, yaml.stringify(updated), 'utf8');
|
|
887
|
+
res.json({ success: true });
|
|
888
|
+
} catch (error: any) {
|
|
889
|
+
res.status(500).json({ error: error.message });
|
|
890
|
+
}
|
|
891
|
+
});
|
|
832
892
|
|
|
833
893
|
// --- User Persona / Risk Profile Endpoints (V3) ---
|
|
834
894
|
app.get('/api/profile', (req, res) => {
|
|
@@ -893,9 +953,7 @@ export async function autoMigrateKeys() {
|
|
|
893
953
|
export function startServer() {
|
|
894
954
|
autoMigrateKeys().catch(e => console.error('[Auto-Migrate] Error:', e));
|
|
895
955
|
|
|
896
|
-
|
|
897
|
-
console.log(`[PluginManager] Finished loading external skills.`);
|
|
898
|
-
});
|
|
956
|
+
|
|
899
957
|
|
|
900
958
|
const PORT = Number(process.env.PORT || 3000);
|
|
901
959
|
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
@@ -921,19 +979,27 @@ export function startServer() {
|
|
|
921
979
|
}
|
|
922
980
|
});
|
|
923
981
|
|
|
982
|
+
let isShuttingDown = false;
|
|
924
983
|
const gracefulShutdown = () => {
|
|
984
|
+
if (isShuttingDown) return;
|
|
985
|
+
isShuttingDown = true;
|
|
925
986
|
console.log('[Nyxora Gateway] Received shutdown signal. Closing server...');
|
|
987
|
+
|
|
988
|
+
if (server.closeAllConnections) {
|
|
989
|
+
server.closeAllConnections();
|
|
990
|
+
}
|
|
991
|
+
|
|
926
992
|
server.close(() => {
|
|
927
993
|
console.log('[Nyxora Gateway] HTTP server closed.');
|
|
928
994
|
logger.close();
|
|
929
995
|
process.exit(0);
|
|
930
996
|
});
|
|
931
997
|
|
|
932
|
-
// Force exit after
|
|
998
|
+
// Force exit after 3s if stuck
|
|
933
999
|
setTimeout(() => {
|
|
934
|
-
console.error('[Nyxora Gateway] Forced shutdown
|
|
1000
|
+
console.error('[Nyxora Gateway] Forced shutdown.');
|
|
935
1001
|
process.exit(1);
|
|
936
|
-
},
|
|
1002
|
+
}, 3000).unref();
|
|
937
1003
|
};
|
|
938
1004
|
|
|
939
1005
|
process.on('SIGTERM', gracefulShutdown);
|
|
@@ -4,7 +4,7 @@ import pc from 'picocolors';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { getAppDir, getPath } from '../config/paths';
|
|
7
|
-
import { loadConfig, saveConfig, saveApiKeys } from '../config/parser';
|
|
7
|
+
import { loadConfig, saveConfig, saveApiKeys, saveRpcConfig } from '../config/parser';
|
|
8
8
|
import crypto from 'crypto';
|
|
9
9
|
|
|
10
10
|
function encryptKey(privateKey: string, password: string) {
|
|
@@ -302,9 +302,8 @@ Provider: ${config.llm.provider}`;
|
|
|
302
302
|
{ value: 'generateExcel', label: 'Generate Excel Reports' },
|
|
303
303
|
{ value: 'analyzeDocument', label: 'Analyze Docs (PDF/Word)' },
|
|
304
304
|
{ value: 'run_terminal', label: 'Run Terminal Command', hint: '⚠️ UNSAFE' },
|
|
305
|
-
{ value: 'installSkill', label: 'Install External Skills (Plugins)' },
|
|
306
305
|
{ value: 'gitManager', label: 'Git Operations (Commit/Push/Pull)' },
|
|
307
|
-
{ value: 'updateSecurityPolicy', label: 'Update
|
|
306
|
+
{ value: 'updateSecurityPolicy', label: 'Update policy.yaml rules', hint: 'safeguard' },
|
|
308
307
|
{ value: 'browseWeb', label: 'Browse & Scrape Webpages' },
|
|
309
308
|
{ value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave)', hint: 'Requires API Key' },
|
|
310
309
|
{ value: 'googleWorkspace', label: 'Google Workspace (Gmail, Docs, Sheets, Forms)', hint: 'Requires OAuth' },
|
|
@@ -429,7 +428,8 @@ Provider: ${config.llm.provider}`;
|
|
|
429
428
|
}
|
|
430
429
|
|
|
431
430
|
if (Object.keys(newApiKeys).length > 0) {
|
|
432
|
-
|
|
431
|
+
if (!config.credentials) config.credentials = {};
|
|
432
|
+
config.credentials = { ...config.credentials, ...newApiKeys };
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
if (!config.integrations) config.integrations = {};
|
|
@@ -446,6 +446,7 @@ Provider: ${config.llm.provider}`;
|
|
|
446
446
|
|
|
447
447
|
saveConfig(config);
|
|
448
448
|
|
|
449
|
+
|
|
449
450
|
// Sync disabled_skills.json based on user selection
|
|
450
451
|
const allWeb3Skills = [
|
|
451
452
|
'transfer', 'swapToken', 'bridgeToken', 'customTx', 'mintNft',
|
|
@@ -456,7 +457,7 @@ Provider: ${config.llm.provider}`;
|
|
|
456
457
|
|
|
457
458
|
const allOsSkills = [
|
|
458
459
|
'readFile', 'writeFile', 'editFile', 'generateExcel', 'analyzeDocument',
|
|
459
|
-
'run_terminal', '
|
|
460
|
+
'run_terminal', 'gitManager', 'updateSecurityPolicy',
|
|
460
461
|
'browseWeb', 'searchWeb', 'googleWorkspace', 'notionWorkspace', 'xManager',
|
|
461
462
|
'audioTranscribe', 'summarizeText'
|
|
462
463
|
];
|