nyxora 26.6.22-1 → 26.6.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/packages/core/src/gateway/WebSocketManager.js +98 -0
- package/dist/packages/core/src/gateway/server.js +20 -0
- package/dist/packages/core/src/gateway/setup.js +9 -6
- package/dist/packages/core/src/gateway/tracker.js +5 -0
- package/dist/packages/core/src/system/skills/searchWeb.js +61 -8
- package/dist/packages/core/src/web3/utils/vaultClient.js +79 -29
- package/dist/packages/policy/src/server.js +28 -0
- package/package.json +4 -1
- package/packages/core/package.json +4 -1
- package/packages/core/src/config/parser.ts +1 -1
- package/packages/core/src/gateway/WebSocketManager.ts +114 -0
- package/packages/core/src/gateway/server.ts +22 -0
- package/packages/core/src/gateway/setup.ts +8 -5
- package/packages/core/src/gateway/tracker.ts +6 -0
- package/packages/core/src/system/skills/searchWeb.ts +56 -8
- package/packages/core/src/web3/utils/vaultClient.ts +86 -26
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +4 -3
- package/packages/policy/src/server.ts +28 -0
- package/packages/signer/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
**Your Personal Web3 Assistant.**
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
[](#)
|
|
5
6
|
[](https://arbitrum.io/)
|
|
6
7
|
[](#)
|
|
7
8
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
[](#️-advanced-security-threat-model)
|
|
9
10
|
[](#️-advanced-security-threat-model)
|
|
10
11
|
[](#️-advanced-security-threat-model)
|
|
11
12
|
|
|
@@ -90,7 +91,7 @@ Within the AI Brain, the Web3 codebase is strictly divided to prevent the LLM fr
|
|
|
90
91
|
|
|
91
92
|
## 🛡️ Advanced Security & Threat Model
|
|
92
93
|
|
|
93
|
-
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security
|
|
94
|
+
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security](https://nyxoraai.github.io/Nyxora/).
|
|
94
95
|
|
|
95
96
|
---
|
|
96
97
|
|
|
@@ -182,8 +183,6 @@ For complete technical deep-dives into our Cryptographic Architecture, please vi
|
|
|
182
183
|
|
|
183
184
|
> **🔗 [Read the Full Nyxora Documentation Here](https://nyxoraai.github.io/Nyxora/)**
|
|
184
185
|
|
|
185
|
-
*(Includes guides on Secure Wallet Imports, Architecture Blueprints, Troubleshooting, and Custom Skill Development).*
|
|
186
|
-
|
|
187
186
|
---
|
|
188
187
|
|
|
189
188
|
**❤️ Support the Project**
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wsManager = exports.WebSocketManager = void 0;
|
|
4
|
+
exports.initWebSocket = initWebSocket;
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
const state_1 = require("../utils/state");
|
|
7
|
+
class WebSocketManager {
|
|
8
|
+
wss;
|
|
9
|
+
clients = new Map();
|
|
10
|
+
ringBuffer = new Map();
|
|
11
|
+
constructor(server) {
|
|
12
|
+
this.wss = new ws_1.WebSocketServer({ noServer: true });
|
|
13
|
+
server.on('upgrade', async (request, socket, head) => {
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(request.url || '', `http://${request.headers.host}`);
|
|
16
|
+
if (url.pathname !== '/ws/stream') {
|
|
17
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
18
|
+
socket.destroy();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const traceId = url.searchParams.get('traceId');
|
|
22
|
+
const token = url.searchParams.get('token');
|
|
23
|
+
if (!traceId || !token) {
|
|
24
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
25
|
+
socket.destroy();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Validate Auth Token during Upgrade Handshake
|
|
29
|
+
if (!(0, state_1.validateToken)(token)) {
|
|
30
|
+
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
31
|
+
socket.destroy();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
35
|
+
this.wss.emit('connection', ws, request, traceId);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n');
|
|
40
|
+
socket.destroy();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
this.wss.on('connection', (ws, request, traceId) => {
|
|
44
|
+
this.clients.set(traceId, ws);
|
|
45
|
+
ws.on('close', () => {
|
|
46
|
+
this.clients.delete(traceId);
|
|
47
|
+
});
|
|
48
|
+
// Flush Ring Buffer if it exists
|
|
49
|
+
const buffer = this.ringBuffer.get(traceId);
|
|
50
|
+
if (buffer) {
|
|
51
|
+
clearTimeout(buffer.timeout);
|
|
52
|
+
for (const log of buffer.logs) {
|
|
53
|
+
ws.send(JSON.stringify(log));
|
|
54
|
+
}
|
|
55
|
+
this.ringBuffer.delete(traceId);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
broadcast(traceId, message, level = 'info') {
|
|
60
|
+
const payload = { timestamp: Date.now(), message, level };
|
|
61
|
+
const ws = this.clients.get(traceId);
|
|
62
|
+
if (ws && ws.readyState === ws_1.WebSocket.OPEN) {
|
|
63
|
+
// Client is connected, send directly
|
|
64
|
+
ws.send(JSON.stringify(payload));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Client not connected yet, buffer the log (Anti-Race Condition)
|
|
68
|
+
let buffer = this.ringBuffer.get(traceId);
|
|
69
|
+
if (!buffer) {
|
|
70
|
+
buffer = {
|
|
71
|
+
logs: [],
|
|
72
|
+
timeout: setTimeout(() => {
|
|
73
|
+
// Drop buffer after 5 seconds if client never connects
|
|
74
|
+
this.ringBuffer.delete(traceId);
|
|
75
|
+
}, 5000)
|
|
76
|
+
};
|
|
77
|
+
this.ringBuffer.set(traceId, buffer);
|
|
78
|
+
}
|
|
79
|
+
buffer.logs.push(payload);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
broadcastAll(message, level = 'info') {
|
|
83
|
+
const payload = { timestamp: Date.now(), message, level };
|
|
84
|
+
const payloadStr = JSON.stringify(payload);
|
|
85
|
+
for (const [_, ws] of this.clients) {
|
|
86
|
+
if (ws.readyState === ws_1.WebSocket.OPEN) {
|
|
87
|
+
ws.send(payloadStr);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.WebSocketManager = WebSocketManager;
|
|
93
|
+
// Global instance to be initialized in server.ts
|
|
94
|
+
exports.wsManager = null;
|
|
95
|
+
function initWebSocket(server) {
|
|
96
|
+
exports.wsManager = new WebSocketManager(server);
|
|
97
|
+
return exports.wsManager;
|
|
98
|
+
}
|
|
@@ -18,9 +18,11 @@ process.on('uncaughtException', (error) => {
|
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
19
|
const helmet_1 = __importDefault(require("helmet"));
|
|
20
20
|
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
21
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
21
22
|
const os_1 = __importDefault(require("os"));
|
|
22
23
|
const paths_1 = require("../config/paths");
|
|
23
24
|
const state_1 = require("../utils/state");
|
|
25
|
+
const WebSocketManager_1 = require("./WebSocketManager");
|
|
24
26
|
const fs_1 = __importDefault(require("fs"));
|
|
25
27
|
const yaml_1 = __importDefault(require("yaml"));
|
|
26
28
|
const reasoning_1 = require("../agent/reasoning");
|
|
@@ -850,6 +852,22 @@ function resetIdleTimer() {
|
|
|
850
852
|
reflection_1.ReflectionEngine.runReflection();
|
|
851
853
|
}, 3 * 60 * 1000); // 3 minutes idle
|
|
852
854
|
}
|
|
855
|
+
app.post('/api/v1/trade', async (req, res) => {
|
|
856
|
+
try {
|
|
857
|
+
const { message, session_id } = req.body;
|
|
858
|
+
if (!message)
|
|
859
|
+
return res.status(400).json({ error: 'Message is required' });
|
|
860
|
+
const traceId = crypto_1.default.randomBytes(8).toString('hex');
|
|
861
|
+
// Asynchronous background execution
|
|
862
|
+
(0, reasoning_1.processUserInput)(message, session_id || traceId).catch(err => {
|
|
863
|
+
console.error(`[TradeAPI] Error:`, err);
|
|
864
|
+
});
|
|
865
|
+
res.status(200).json({ status: 'processing', traceId });
|
|
866
|
+
}
|
|
867
|
+
catch (error) {
|
|
868
|
+
res.status(500).json({ error: error.message });
|
|
869
|
+
}
|
|
870
|
+
});
|
|
853
871
|
app.post('/api/chat', async (req, res) => {
|
|
854
872
|
try {
|
|
855
873
|
const { message, session_id } = req.body;
|
|
@@ -1000,6 +1018,8 @@ function startServer() {
|
|
|
1000
1018
|
const PORT = Number(process.env.PORT || 3000);
|
|
1001
1019
|
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
1002
1020
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
1021
|
+
// Initialize WebSocket Manager
|
|
1022
|
+
(0, WebSocketManager_1.initWebSocket)(server);
|
|
1003
1023
|
// Start the Telegram bot listener
|
|
1004
1024
|
(0, telegram_1.startTelegramBot)();
|
|
1005
1025
|
// Start Asynchronous Bridge Watcher
|
|
@@ -276,7 +276,7 @@ Provider: ${config.llm.provider}`;
|
|
|
276
276
|
{ value: 'gitManager', label: 'Git Operations (Commit/Push/Pull)' },
|
|
277
277
|
{ value: 'updateSecurityPolicy', label: 'Update policy.yaml rules', hint: 'safeguard' },
|
|
278
278
|
{ value: 'browseWeb', label: 'Browse & Scrape Webpages' },
|
|
279
|
-
{ value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave)', hint: '
|
|
279
|
+
{ value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave/DuckDuckGo)', hint: 'Optional API Key' },
|
|
280
280
|
{ value: 'googleWorkspace', label: 'Google Workspace (Gmail, Docs, Sheets, Forms)', hint: 'Requires OAuth' },
|
|
281
281
|
{ value: 'notionWorkspace', label: 'Notion Integration' },
|
|
282
282
|
{ value: 'xManager', label: 'X/Twitter Management' },
|
|
@@ -308,15 +308,18 @@ Provider: ${config.llm.provider}`;
|
|
|
308
308
|
options: [
|
|
309
309
|
{ value: 'tavily', label: 'Tavily Search (Built for AI - 1000 free/mo)' },
|
|
310
310
|
{ value: 'brave', label: 'Brave Search (Privacy focused - 2000 free/mo)' },
|
|
311
|
+
{ value: 'duckduckgo', label: 'DuckDuckGo (Free & Built-in)' },
|
|
311
312
|
],
|
|
312
313
|
});
|
|
313
314
|
if ((0, prompts_1.isCancel)(searchProvider))
|
|
314
315
|
return process.exit(0);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
if (searchProvider !== 'duckduckgo') {
|
|
317
|
+
searchApiKey = (await (0, prompts_1.password)({
|
|
318
|
+
message: `Enter API Key for ${searchProvider} (Get it free at ${searchProvider === 'tavily' ? 'tavily.com' : 'search.brave.com'}):`,
|
|
319
|
+
}));
|
|
320
|
+
if ((0, prompts_1.isCancel)(searchApiKey))
|
|
321
|
+
return process.exit(0);
|
|
322
|
+
}
|
|
320
323
|
}
|
|
321
324
|
const setupTelegram = activeChannels.includes('telegram');
|
|
322
325
|
let telegramToken = '';
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Tracker = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const WebSocketManager_1 = require("./WebSocketManager");
|
|
8
9
|
const paths_1 = require("../config/paths");
|
|
9
10
|
const stats = {
|
|
10
11
|
cost: 0,
|
|
@@ -97,6 +98,10 @@ exports.Tracker = {
|
|
|
97
98
|
if (gatewayLogs.length > MAX_LOGS)
|
|
98
99
|
gatewayLogs.pop();
|
|
99
100
|
saveState();
|
|
101
|
+
// Broadcast terminal logs to Dashboard via WebSocket
|
|
102
|
+
if (WebSocketManager_1.wsManager) {
|
|
103
|
+
WebSocketManager_1.wsManager.broadcastAll(`[${formatTime()}] ${message}`, meta?.level || 'info');
|
|
104
|
+
}
|
|
100
105
|
},
|
|
101
106
|
getLogs: () => {
|
|
102
107
|
return {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.searchWebToolDefinition = void 0;
|
|
4
4
|
exports.searchWeb = searchWeb;
|
|
5
5
|
const parser_1 = require("../../config/parser");
|
|
6
|
+
const duck_duck_scrape_1 = require("duck-duck-scrape");
|
|
6
7
|
const SEARXNG_INSTANCES = [
|
|
7
8
|
'https://search.mdosch.de',
|
|
8
9
|
'https://searx.tiekoetter.com',
|
|
@@ -79,6 +80,25 @@ async function searchSearxng(query, depth = 1) {
|
|
|
79
80
|
}
|
|
80
81
|
throw new Error('[SearXNG Error] All decentralized instances failed.');
|
|
81
82
|
}
|
|
83
|
+
async function searchDuckDuckGo(query, depth = 1) {
|
|
84
|
+
try {
|
|
85
|
+
const searchResults = await (0, duck_duck_scrape_1.search)(query, {
|
|
86
|
+
safeSearch: duck_duck_scrape_1.SafeSearchType.MODERATE
|
|
87
|
+
});
|
|
88
|
+
if (!searchResults.noResults && searchResults.results.length > 0) {
|
|
89
|
+
const maxResults = depth > 1 ? 15 : 8;
|
|
90
|
+
return searchResults.results.slice(0, maxResults).map(r => ({
|
|
91
|
+
title: r.title,
|
|
92
|
+
url: r.url,
|
|
93
|
+
content: r.description || r.title
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
throw new Error(`[DuckDuckGo Error] Failed to scrape: ${e.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
82
102
|
const searchCache = new Map();
|
|
83
103
|
async function searchWeb(query, depth = 1) {
|
|
84
104
|
// Auto-inject current year for time-sensitive queries
|
|
@@ -119,13 +139,25 @@ async function searchWeb(query, depth = 1) {
|
|
|
119
139
|
results = await searchBrave(finalQuery, creds.brave_key, depth);
|
|
120
140
|
}
|
|
121
141
|
catch (e2) {
|
|
122
|
-
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to
|
|
123
|
-
|
|
142
|
+
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to DuckDuckGo (L3)...');
|
|
143
|
+
try {
|
|
144
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
145
|
+
}
|
|
146
|
+
catch (e3) {
|
|
147
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
148
|
+
results = await searchSearxng(finalQuery, depth);
|
|
149
|
+
}
|
|
124
150
|
}
|
|
125
151
|
}
|
|
126
152
|
else {
|
|
127
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
128
|
-
|
|
153
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
154
|
+
try {
|
|
155
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
156
|
+
}
|
|
157
|
+
catch (e3) {
|
|
158
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
159
|
+
results = await searchSearxng(finalQuery, depth);
|
|
160
|
+
}
|
|
129
161
|
}
|
|
130
162
|
}
|
|
131
163
|
else {
|
|
@@ -145,13 +177,25 @@ async function searchWeb(query, depth = 1) {
|
|
|
145
177
|
results = await searchTavily(finalQuery, creds.tavily_key, depth);
|
|
146
178
|
}
|
|
147
179
|
catch (e2) {
|
|
148
|
-
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to
|
|
149
|
-
|
|
180
|
+
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to DuckDuckGo (L3)...');
|
|
181
|
+
try {
|
|
182
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
183
|
+
}
|
|
184
|
+
catch (e3) {
|
|
185
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
186
|
+
results = await searchSearxng(finalQuery, depth);
|
|
187
|
+
}
|
|
150
188
|
}
|
|
151
189
|
}
|
|
152
190
|
else {
|
|
153
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
154
|
-
|
|
191
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
192
|
+
try {
|
|
193
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
194
|
+
}
|
|
195
|
+
catch (e3) {
|
|
196
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
197
|
+
results = await searchSearxng(finalQuery, depth);
|
|
198
|
+
}
|
|
155
199
|
}
|
|
156
200
|
}
|
|
157
201
|
else {
|
|
@@ -159,6 +203,15 @@ async function searchWeb(query, depth = 1) {
|
|
|
159
203
|
}
|
|
160
204
|
}
|
|
161
205
|
}
|
|
206
|
+
else if (provider === 'duckduckgo') {
|
|
207
|
+
try {
|
|
208
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
console.warn('[WebSearch] Primary provider (DuckDuckGo) failed. Falling back to SearXNG Mesh...');
|
|
212
|
+
results = await searchSearxng(finalQuery, depth);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
162
215
|
else {
|
|
163
216
|
results = await searchSearxng(finalQuery, depth);
|
|
164
217
|
}
|
|
@@ -9,6 +9,8 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const os_1 = __importDefault(require("os"));
|
|
11
11
|
const crypto_1 = __importDefault(require("crypto"));
|
|
12
|
+
const http_1 = __importDefault(require("http"));
|
|
13
|
+
const msgpackr_1 = require("msgpackr");
|
|
12
14
|
function getInternalToken() {
|
|
13
15
|
try {
|
|
14
16
|
const tokenPath = path_1.default.join(os_1.default.homedir(), '.nyxora', 'auth', 'runtime.token');
|
|
@@ -21,19 +23,43 @@ function getInternalToken() {
|
|
|
21
23
|
}
|
|
22
24
|
async function getAddress() {
|
|
23
25
|
const token = getInternalToken();
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
28
|
+
const options = {
|
|
29
|
+
socketPath: fs_1.default.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
30
|
+
host: fs_1.default.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
31
|
+
port: fs_1.default.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
32
|
+
path: '/address',
|
|
33
|
+
method: 'GET',
|
|
34
|
+
headers: {
|
|
35
|
+
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const req = http_1.default.request(options, (res) => {
|
|
39
|
+
let data = '';
|
|
40
|
+
res.on('data', chunk => data += chunk);
|
|
41
|
+
res.on('end', () => {
|
|
42
|
+
try {
|
|
43
|
+
if (res.statusCode !== 200) {
|
|
44
|
+
reject(new Error(data));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const parsed = JSON.parse(data);
|
|
48
|
+
resolve(parsed.address);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
reject(new Error(`Failed to get address from vault: ${e.message}`));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
28
54
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
55
|
+
req.on('error', (error) => {
|
|
56
|
+
reject(new Error(`Failed to get address from vault: ${error.message}`));
|
|
57
|
+
});
|
|
58
|
+
req.setTimeout(30000, () => {
|
|
59
|
+
req.destroy(new Error('Timeout'));
|
|
60
|
+
});
|
|
61
|
+
req.end();
|
|
62
|
+
});
|
|
37
63
|
}
|
|
38
64
|
async function submitTransaction(txPayload) {
|
|
39
65
|
const token = getInternalToken();
|
|
@@ -45,25 +71,49 @@ async function submitTransaction(txPayload) {
|
|
|
45
71
|
const secret = getInternalToken() || '';
|
|
46
72
|
txPayload.internalSignature = crypto_1.default.createHmac('sha256', secret).update(txPayload.chainName + amountWei).digest('hex');
|
|
47
73
|
}
|
|
48
|
-
|
|
49
|
-
const
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
76
|
+
const payloadBuffer = (0, msgpackr_1.pack)(txPayload);
|
|
77
|
+
const options = {
|
|
78
|
+
socketPath: fs_1.default.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
79
|
+
host: fs_1.default.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
80
|
+
port: fs_1.default.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
81
|
+
path: '/request-tx',
|
|
50
82
|
method: 'POST',
|
|
51
83
|
headers: {
|
|
52
|
-
'Content-Type': 'application/
|
|
84
|
+
'Content-Type': 'application/msgpack',
|
|
85
|
+
'Content-Length': payloadBuffer.length,
|
|
53
86
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const req = http_1.default.request(options, (res) => {
|
|
90
|
+
let data = '';
|
|
91
|
+
res.on('data', chunk => data += chunk);
|
|
92
|
+
res.on('end', () => {
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(data);
|
|
95
|
+
if (res.statusCode !== 200) {
|
|
96
|
+
reject(new Error(parsed.error || 'Failed to submit transaction'));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (parsed.status === 'pending') {
|
|
100
|
+
resolve(`Pending (ID: ${parsed.txId})`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
resolve(parsed.signedHash || parsed.hash || parsed.txHash || parsed.status || 'Transaction executed successfully (No Hash returned)');
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
reject(new Error(`Failed to parse vault response: ${e.message}`));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
57
109
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Error(`Transaction submission failed: ${error.message}`);
|
|
68
|
-
}
|
|
110
|
+
req.on('error', (error) => {
|
|
111
|
+
reject(new Error(`Transaction submission failed: ${error.message}`));
|
|
112
|
+
});
|
|
113
|
+
req.setTimeout(30000, () => {
|
|
114
|
+
req.destroy(new Error('Timeout'));
|
|
115
|
+
});
|
|
116
|
+
req.write(payloadBuffer);
|
|
117
|
+
req.end();
|
|
118
|
+
});
|
|
69
119
|
}
|
|
@@ -15,6 +15,7 @@ const zod_1 = require("zod");
|
|
|
15
15
|
const path_1 = __importDefault(require("path"));
|
|
16
16
|
const crypto_1 = __importDefault(require("crypto"));
|
|
17
17
|
const os_1 = __importDefault(require("os"));
|
|
18
|
+
const msgpackr_1 = require("msgpackr");
|
|
18
19
|
process.on('unhandledRejection', (reason, promise) => {
|
|
19
20
|
console.error('[Anti-Crash] Unhandled Rejection at:', promise, 'reason:', reason);
|
|
20
21
|
});
|
|
@@ -35,6 +36,25 @@ catch (e) {
|
|
|
35
36
|
const SIGNER_SOCKET = process.env.SIGNER_SOCKET_PATH || '/tmp/nyxora-signer.sock';
|
|
36
37
|
const app = (0, express_1.default)();
|
|
37
38
|
app.use(express_1.default.json());
|
|
39
|
+
// MessagePack Parser Middleware for Hyper-Optimized IPC
|
|
40
|
+
app.use((req, res, next) => {
|
|
41
|
+
if (req.headers['content-type'] === 'application/msgpack') {
|
|
42
|
+
let chunks = [];
|
|
43
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
44
|
+
req.on('end', () => {
|
|
45
|
+
try {
|
|
46
|
+
req.body = (0, msgpackr_1.unpack)(Buffer.concat(chunks));
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
res.status(400).json({ error: 'Invalid MessagePack payload' });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
next();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
38
58
|
const TxRequestSchema = zod_1.z.object({
|
|
39
59
|
type: zod_1.z.enum(['transfer', 'swap', 'bridge', 'mint', 'custom']),
|
|
40
60
|
chainName: zod_1.z.string(),
|
|
@@ -237,6 +257,7 @@ app.post('/approve-tx/:id', (req, res) => {
|
|
|
237
257
|
signerReq.write(requestPayload);
|
|
238
258
|
signerReq.end();
|
|
239
259
|
});
|
|
260
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
240
261
|
const server = app.listen(Number(PORT), '127.0.0.1', () => {
|
|
241
262
|
console.log(`[Policy Engine] Listening on 127.0.0.1:${PORT} (Secured Local Loopback)`);
|
|
242
263
|
});
|
|
@@ -250,3 +271,10 @@ server.on('error', (e) => {
|
|
|
250
271
|
process.exit(1);
|
|
251
272
|
}
|
|
252
273
|
});
|
|
274
|
+
// Start UDS Server for Hyper-Optimized IPC
|
|
275
|
+
const udsServer = http_1.default.createServer(app);
|
|
276
|
+
if (fs_1.default.existsSync(POLICY_SOCKET))
|
|
277
|
+
fs_1.default.unlinkSync(POLICY_SOCKET);
|
|
278
|
+
udsServer.listen(POLICY_SOCKET, () => {
|
|
279
|
+
console.log(`[Policy Engine] Listening on UDS ${POLICY_SOCKET} (Hyper-Optimized IPC)`);
|
|
280
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.23",
|
|
4
4
|
"description": "Your Personal Web3 Assistant",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web3",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"dotenv": "^17.4.2",
|
|
61
61
|
"duck-duck-scrape": "^2.2.7",
|
|
62
62
|
"express": "^5.2.1",
|
|
63
|
+
"msgpackr": "^2.0.4",
|
|
63
64
|
"express-rate-limit": "^7.5.0",
|
|
64
65
|
"helmet": "^8.0.0",
|
|
65
66
|
"isolated-vm": "^6.1.2",
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
"typescript": "^6.0.3",
|
|
80
81
|
"viem": "^2.51.0",
|
|
81
82
|
"write-excel-file": "^4.1.1",
|
|
83
|
+
"ws": "^8.21.0",
|
|
82
84
|
"yaml": "^2.9.0",
|
|
83
85
|
"zod": "^3.23.8"
|
|
84
86
|
},
|
|
@@ -98,6 +100,7 @@
|
|
|
98
100
|
},
|
|
99
101
|
"devDependencies": {
|
|
100
102
|
"@types/jsonwebtoken": "^9.0.5",
|
|
103
|
+
"@types/ws": "^8.5.10",
|
|
101
104
|
"@types/node": "^25.9.1",
|
|
102
105
|
"@types/node-cron": "^3.0.11",
|
|
103
106
|
"concurrently": "^10.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora-agent-core",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.23",
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "src/gateway/server.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"express-rate-limit": "^7.5.0",
|
|
15
15
|
"helmet": "^8.0.0",
|
|
16
16
|
"mammoth": "^1.12.0",
|
|
17
|
+
"msgpackr": "^2.0.4",
|
|
17
18
|
"multer": "^2.2.0",
|
|
18
19
|
"node-cron": "^4.4.1",
|
|
19
20
|
"open": "^11.0.0",
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
"telegraf": "^4.16.3",
|
|
25
26
|
"twitter-api-v2": "^1.29.0",
|
|
26
27
|
"write-excel-file": "^4.1.1",
|
|
28
|
+
"ws": "^8.21.0",
|
|
27
29
|
"yaml": "^2.9.0",
|
|
28
30
|
"zod": "^3.25.76"
|
|
29
31
|
},
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"@types/node": "^25.9.2",
|
|
33
35
|
"@types/node-cron": "^3.0.11",
|
|
34
36
|
"@types/pdf-parse": "^1.1.5",
|
|
37
|
+
"@types/ws": "^8.18.1",
|
|
35
38
|
"vitest": "^4.1.8"
|
|
36
39
|
},
|
|
37
40
|
"scripts": {
|
|
@@ -115,7 +115,7 @@ export interface NyxoraConfig {
|
|
|
115
115
|
credentials?: any; // Deprecated, kept for parsing during migration
|
|
116
116
|
};
|
|
117
117
|
web_search?: {
|
|
118
|
-
provider: 'tavily' | 'brave' | 'mesh';
|
|
118
|
+
provider: 'tavily' | 'brave' | 'duckduckgo' | 'mesh';
|
|
119
119
|
enabled: boolean;
|
|
120
120
|
};
|
|
121
121
|
credentials?: {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { validateToken } from '../utils/state';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
|
|
6
|
+
interface BufferedLog {
|
|
7
|
+
timestamp: number;
|
|
8
|
+
message: string;
|
|
9
|
+
level: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class WebSocketManager {
|
|
13
|
+
private wss: WebSocketServer;
|
|
14
|
+
private clients: Map<string, WebSocket> = new Map();
|
|
15
|
+
private ringBuffer: Map<string, { logs: BufferedLog[]; timeout: NodeJS.Timeout }> = new Map();
|
|
16
|
+
|
|
17
|
+
constructor(server: http.Server) {
|
|
18
|
+
this.wss = new WebSocketServer({ noServer: true });
|
|
19
|
+
|
|
20
|
+
server.on('upgrade', async (request: IncomingMessage, socket, head) => {
|
|
21
|
+
try {
|
|
22
|
+
const url = new URL(request.url || '', `http://${request.headers.host}`);
|
|
23
|
+
if (url.pathname !== '/ws/stream') {
|
|
24
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
25
|
+
socket.destroy();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const traceId = url.searchParams.get('traceId');
|
|
30
|
+
const token = url.searchParams.get('token');
|
|
31
|
+
|
|
32
|
+
if (!traceId || !token) {
|
|
33
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
34
|
+
socket.destroy();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Validate Auth Token during Upgrade Handshake
|
|
39
|
+
if (!validateToken(token)) {
|
|
40
|
+
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
41
|
+
socket.destroy();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
46
|
+
this.wss.emit('connection', ws, request, traceId);
|
|
47
|
+
});
|
|
48
|
+
} catch (e) {
|
|
49
|
+
socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n');
|
|
50
|
+
socket.destroy();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.wss.on('connection', (ws: WebSocket, request: IncomingMessage, traceId: string) => {
|
|
55
|
+
this.clients.set(traceId, ws);
|
|
56
|
+
|
|
57
|
+
ws.on('close', () => {
|
|
58
|
+
this.clients.delete(traceId);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Flush Ring Buffer if it exists
|
|
62
|
+
const buffer = this.ringBuffer.get(traceId);
|
|
63
|
+
if (buffer) {
|
|
64
|
+
clearTimeout(buffer.timeout);
|
|
65
|
+
for (const log of buffer.logs) {
|
|
66
|
+
ws.send(JSON.stringify(log));
|
|
67
|
+
}
|
|
68
|
+
this.ringBuffer.delete(traceId);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public broadcast(traceId: string, message: string, level: string = 'info') {
|
|
74
|
+
const payload = { timestamp: Date.now(), message, level };
|
|
75
|
+
const ws = this.clients.get(traceId);
|
|
76
|
+
|
|
77
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
78
|
+
// Client is connected, send directly
|
|
79
|
+
ws.send(JSON.stringify(payload));
|
|
80
|
+
} else {
|
|
81
|
+
// Client not connected yet, buffer the log (Anti-Race Condition)
|
|
82
|
+
let buffer = this.ringBuffer.get(traceId);
|
|
83
|
+
if (!buffer) {
|
|
84
|
+
buffer = {
|
|
85
|
+
logs: [],
|
|
86
|
+
timeout: setTimeout(() => {
|
|
87
|
+
// Drop buffer after 5 seconds if client never connects
|
|
88
|
+
this.ringBuffer.delete(traceId);
|
|
89
|
+
}, 5000)
|
|
90
|
+
};
|
|
91
|
+
this.ringBuffer.set(traceId, buffer);
|
|
92
|
+
}
|
|
93
|
+
buffer.logs.push(payload);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public broadcastAll(message: string, level: string = 'info') {
|
|
98
|
+
const payload = { timestamp: Date.now(), message, level };
|
|
99
|
+
const payloadStr = JSON.stringify(payload);
|
|
100
|
+
for (const [_, ws] of this.clients) {
|
|
101
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
102
|
+
ws.send(payloadStr);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Global instance to be initialized in server.ts
|
|
109
|
+
export let wsManager: WebSocketManager | null = null;
|
|
110
|
+
|
|
111
|
+
export function initWebSocket(server: http.Server) {
|
|
112
|
+
wsManager = new WebSocketManager(server);
|
|
113
|
+
return wsManager;
|
|
114
|
+
}
|
|
@@ -21,6 +21,7 @@ import os from 'os';
|
|
|
21
21
|
import { getPath } from '../config/paths';
|
|
22
22
|
import { validateToken, getSessionToken } from '../utils/state';
|
|
23
23
|
|
|
24
|
+
import { initWebSocket } from './WebSocketManager';
|
|
24
25
|
import fs from 'fs';
|
|
25
26
|
import yaml from 'yaml';
|
|
26
27
|
import { processUserInput, logger } from '../agent/reasoning';
|
|
@@ -910,6 +911,24 @@ function resetIdleTimer() {
|
|
|
910
911
|
}, 3 * 60 * 1000); // 3 minutes idle
|
|
911
912
|
}
|
|
912
913
|
|
|
914
|
+
app.post('/api/v1/trade', async (req, res) => {
|
|
915
|
+
try {
|
|
916
|
+
const { message, session_id } = req.body;
|
|
917
|
+
if (!message) return res.status(400).json({ error: 'Message is required' });
|
|
918
|
+
|
|
919
|
+
const traceId = crypto.randomBytes(8).toString('hex');
|
|
920
|
+
|
|
921
|
+
// Asynchronous background execution
|
|
922
|
+
processUserInput(message, session_id || traceId).catch(err => {
|
|
923
|
+
console.error(`[TradeAPI] Error:`, err);
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
res.status(200).json({ status: 'processing', traceId });
|
|
927
|
+
} catch (error: any) {
|
|
928
|
+
res.status(500).json({ error: error.message });
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
913
932
|
app.post('/api/chat', async (req, res) => {
|
|
914
933
|
try {
|
|
915
934
|
const { message, session_id } = req.body;
|
|
@@ -1069,6 +1088,9 @@ export function startServer() {
|
|
|
1069
1088
|
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
1070
1089
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
1071
1090
|
|
|
1091
|
+
// Initialize WebSocket Manager
|
|
1092
|
+
initWebSocket(server);
|
|
1093
|
+
|
|
1072
1094
|
// Start the Telegram bot listener
|
|
1073
1095
|
startTelegramBot();
|
|
1074
1096
|
|
|
@@ -288,7 +288,7 @@ Provider: ${config.llm.provider}`;
|
|
|
288
288
|
{ value: 'gitManager', label: 'Git Operations (Commit/Push/Pull)' },
|
|
289
289
|
{ value: 'updateSecurityPolicy', label: 'Update policy.yaml rules', hint: 'safeguard' },
|
|
290
290
|
{ value: 'browseWeb', label: 'Browse & Scrape Webpages' },
|
|
291
|
-
{ value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave)', hint: '
|
|
291
|
+
{ value: 'searchWeb', label: 'Smart Web Search (Tavily/Brave/DuckDuckGo)', hint: 'Optional API Key' },
|
|
292
292
|
{ value: 'googleWorkspace', label: 'Google Workspace (Gmail, Docs, Sheets, Forms)', hint: 'Requires OAuth' },
|
|
293
293
|
{ value: 'notionWorkspace', label: 'Notion Integration' },
|
|
294
294
|
{ value: 'xManager', label: 'X/Twitter Management' },
|
|
@@ -320,14 +320,17 @@ Provider: ${config.llm.provider}`;
|
|
|
320
320
|
options: [
|
|
321
321
|
{ value: 'tavily', label: 'Tavily Search (Built for AI - 1000 free/mo)' },
|
|
322
322
|
{ value: 'brave', label: 'Brave Search (Privacy focused - 2000 free/mo)' },
|
|
323
|
+
{ value: 'duckduckgo', label: 'DuckDuckGo (Free & Built-in)' },
|
|
323
324
|
],
|
|
324
325
|
});
|
|
325
326
|
if (isCancel(searchProvider)) return process.exit(0);
|
|
326
327
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
if (searchProvider !== 'duckduckgo') {
|
|
329
|
+
searchApiKey = (await password({
|
|
330
|
+
message: `Enter API Key for ${searchProvider} (Get it free at ${searchProvider === 'tavily' ? 'tavily.com' : 'search.brave.com'}):`,
|
|
331
|
+
})) as string;
|
|
332
|
+
if (isCancel(searchApiKey)) return process.exit(0);
|
|
333
|
+
}
|
|
331
334
|
}
|
|
332
335
|
|
|
333
336
|
const setupTelegram = activeChannels.includes('telegram');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { wsManager } from './WebSocketManager';
|
|
3
4
|
import { getPath } from '../config/paths';
|
|
4
5
|
|
|
5
6
|
interface Stats {
|
|
@@ -114,6 +115,11 @@ export const Tracker = {
|
|
|
114
115
|
gatewayLogs.unshift({ timestamp: formatTime(), message, meta });
|
|
115
116
|
if (gatewayLogs.length > MAX_LOGS) gatewayLogs.pop();
|
|
116
117
|
saveState();
|
|
118
|
+
|
|
119
|
+
// Broadcast terminal logs to Dashboard via WebSocket
|
|
120
|
+
if (wsManager) {
|
|
121
|
+
wsManager.broadcastAll(`[${formatTime()}] ${message}`, meta?.level || 'info');
|
|
122
|
+
}
|
|
117
123
|
},
|
|
118
124
|
|
|
119
125
|
getLogs: () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadConfig, loadApiKeys } from '../../config/parser';
|
|
2
|
+
import { search, SafeSearchType } from 'duck-duck-scrape';
|
|
2
3
|
|
|
3
4
|
interface SearchQueryResult {
|
|
4
5
|
title: string;
|
|
@@ -90,6 +91,26 @@ async function searchSearxng(query: string, depth: number = 1): Promise<SearchQu
|
|
|
90
91
|
throw new Error('[SearXNG Error] All decentralized instances failed.');
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
async function searchDuckDuckGo(query: string, depth: number = 1): Promise<SearchQueryResult[]> {
|
|
95
|
+
try {
|
|
96
|
+
const searchResults = await search(query, {
|
|
97
|
+
safeSearch: SafeSearchType.MODERATE
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!searchResults.noResults && searchResults.results.length > 0) {
|
|
101
|
+
const maxResults = depth > 1 ? 15 : 8;
|
|
102
|
+
return searchResults.results.slice(0, maxResults).map(r => ({
|
|
103
|
+
title: r.title,
|
|
104
|
+
url: r.url,
|
|
105
|
+
content: r.description || r.title
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
} catch (e: any) {
|
|
110
|
+
throw new Error(`[DuckDuckGo Error] Failed to scrape: ${e.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
const searchCache = new Map<string, {data: SearchQueryResult[], timestamp: number}>();
|
|
94
115
|
|
|
95
116
|
export async function searchWeb(query: string, depth: number = 1): Promise<string> {
|
|
@@ -134,12 +155,22 @@ export async function searchWeb(query: string, depth: number = 1): Promise<strin
|
|
|
134
155
|
try {
|
|
135
156
|
results = await searchBrave(finalQuery, creds.brave_key, depth);
|
|
136
157
|
} catch (e2: any) {
|
|
137
|
-
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to
|
|
138
|
-
|
|
158
|
+
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to DuckDuckGo (L3)...');
|
|
159
|
+
try {
|
|
160
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
161
|
+
} catch (e3) {
|
|
162
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
163
|
+
results = await searchSearxng(finalQuery, depth);
|
|
164
|
+
}
|
|
139
165
|
}
|
|
140
166
|
} else {
|
|
141
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
142
|
-
|
|
167
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
168
|
+
try {
|
|
169
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
170
|
+
} catch (e3) {
|
|
171
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
172
|
+
results = await searchSearxng(finalQuery, depth);
|
|
173
|
+
}
|
|
143
174
|
}
|
|
144
175
|
} else {
|
|
145
176
|
throw e;
|
|
@@ -155,17 +186,34 @@ export async function searchWeb(query: string, depth: number = 1): Promise<strin
|
|
|
155
186
|
try {
|
|
156
187
|
results = await searchTavily(finalQuery, creds.tavily_key, depth);
|
|
157
188
|
} catch (e2: any) {
|
|
158
|
-
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to
|
|
159
|
-
|
|
189
|
+
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to DuckDuckGo (L3)...');
|
|
190
|
+
try {
|
|
191
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
192
|
+
} catch (e3) {
|
|
193
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
194
|
+
results = await searchSearxng(finalQuery, depth);
|
|
195
|
+
}
|
|
160
196
|
}
|
|
161
197
|
} else {
|
|
162
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
163
|
-
|
|
198
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
199
|
+
try {
|
|
200
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
201
|
+
} catch (e3) {
|
|
202
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
203
|
+
results = await searchSearxng(finalQuery, depth);
|
|
204
|
+
}
|
|
164
205
|
}
|
|
165
206
|
} else {
|
|
166
207
|
throw e;
|
|
167
208
|
}
|
|
168
209
|
}
|
|
210
|
+
} else if (provider === 'duckduckgo') {
|
|
211
|
+
try {
|
|
212
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
213
|
+
} catch (e: any) {
|
|
214
|
+
console.warn('[WebSearch] Primary provider (DuckDuckGo) failed. Falling back to SearXNG Mesh...');
|
|
215
|
+
results = await searchSearxng(finalQuery, depth);
|
|
216
|
+
}
|
|
169
217
|
} else {
|
|
170
218
|
results = await searchSearxng(finalQuery, depth);
|
|
171
219
|
}
|
|
@@ -2,6 +2,8 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import { pack } from 'msgpackr';
|
|
5
7
|
|
|
6
8
|
function getInternalToken(): string | undefined {
|
|
7
9
|
try {
|
|
@@ -15,17 +17,46 @@ function getInternalToken(): string | undefined {
|
|
|
15
17
|
|
|
16
18
|
export async function getAddress(): Promise<string> {
|
|
17
19
|
const token = getInternalToken();
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
22
|
+
const options = {
|
|
23
|
+
socketPath: fs.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
24
|
+
host: fs.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
25
|
+
port: fs.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
26
|
+
path: '/address',
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: {
|
|
29
|
+
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const req = http.request(options, (res) => {
|
|
34
|
+
let data = '';
|
|
35
|
+
res.on('data', chunk => data += chunk);
|
|
36
|
+
res.on('end', () => {
|
|
37
|
+
try {
|
|
38
|
+
if (res.statusCode !== 200) {
|
|
39
|
+
reject(new Error(data));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const parsed = JSON.parse(data);
|
|
43
|
+
resolve(parsed.address);
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
reject(new Error(`Failed to get address from vault: ${e.message}`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
22
48
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
|
|
50
|
+
req.on('error', (error) => {
|
|
51
|
+
reject(new Error(`Failed to get address from vault: ${error.message}`));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
req.setTimeout(30000, () => {
|
|
55
|
+
req.destroy(new Error('Timeout'));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
req.end();
|
|
59
|
+
});
|
|
29
60
|
}
|
|
30
61
|
|
|
31
62
|
export async function submitTransaction(txPayload: any): Promise<string> {
|
|
@@ -39,25 +70,54 @@ export async function submitTransaction(txPayload: any): Promise<string> {
|
|
|
39
70
|
const secret = getInternalToken() || '';
|
|
40
71
|
txPayload.internalSignature = crypto.createHmac('sha256', secret).update(txPayload.chainName + amountWei).digest('hex');
|
|
41
72
|
}
|
|
42
|
-
|
|
43
|
-
const
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
75
|
+
const payloadBuffer = pack(txPayload);
|
|
76
|
+
|
|
77
|
+
const options = {
|
|
78
|
+
socketPath: fs.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
79
|
+
host: fs.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
80
|
+
port: fs.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
81
|
+
path: '/request-tx',
|
|
44
82
|
method: 'POST',
|
|
45
83
|
headers: {
|
|
46
|
-
'Content-Type': 'application/
|
|
84
|
+
'Content-Type': 'application/msgpack',
|
|
85
|
+
'Content-Length': payloadBuffer.length,
|
|
47
86
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const req = http.request(options, (res) => {
|
|
91
|
+
let data = '';
|
|
92
|
+
res.on('data', chunk => data += chunk);
|
|
93
|
+
res.on('end', () => {
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(data);
|
|
96
|
+
if (res.statusCode !== 200) {
|
|
97
|
+
reject(new Error(parsed.error || 'Failed to submit transaction'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (parsed.status === 'pending') {
|
|
101
|
+
resolve(`Pending (ID: ${parsed.txId})`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
resolve(parsed.signedHash || parsed.hash || parsed.txHash || parsed.status || 'Transaction executed successfully (No Hash returned)');
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
reject(new Error(`Failed to parse vault response: ${e.message}`));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
51
109
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
110
|
+
|
|
111
|
+
req.on('error', (error) => {
|
|
112
|
+
reject(new Error(`Transaction submission failed: ${error.message}`));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
req.setTimeout(30000, () => {
|
|
116
|
+
req.destroy(new Error('Timeout'));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
req.write(payloadBuffer);
|
|
120
|
+
req.end();
|
|
121
|
+
});
|
|
62
122
|
}
|
|
63
123
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora-policy-engine",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.23",
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "src/server.ts",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"express": "^5.2.1",
|
|
8
|
-
"zod": "^3.23.8",
|
|
9
8
|
"jsonwebtoken": "^9.0.2",
|
|
10
|
-
"
|
|
9
|
+
"msgpackr": "^2.0.4",
|
|
10
|
+
"yaml": "^2.9.0",
|
|
11
|
+
"zod": "^3.23.8"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -11,6 +11,7 @@ import { z } from 'zod';
|
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
13
|
import os from 'os';
|
|
14
|
+
import { unpack } from 'msgpackr';
|
|
14
15
|
|
|
15
16
|
process.on('unhandledRejection', (reason, promise) => {
|
|
16
17
|
console.error('[Anti-Crash] Unhandled Rejection at:', promise, 'reason:', reason);
|
|
@@ -36,6 +37,24 @@ const SIGNER_SOCKET = process.env.SIGNER_SOCKET_PATH || '/tmp/nyxora-signer.sock
|
|
|
36
37
|
const app = express();
|
|
37
38
|
app.use(express.json());
|
|
38
39
|
|
|
40
|
+
// MessagePack Parser Middleware for Hyper-Optimized IPC
|
|
41
|
+
app.use((req, res, next) => {
|
|
42
|
+
if (req.headers['content-type'] === 'application/msgpack') {
|
|
43
|
+
let chunks: Buffer[] = [];
|
|
44
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
45
|
+
req.on('end', () => {
|
|
46
|
+
try {
|
|
47
|
+
req.body = unpack(Buffer.concat(chunks));
|
|
48
|
+
next();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
res.status(400).json({ error: 'Invalid MessagePack payload' });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
next();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
39
58
|
const TxRequestSchema = z.object({
|
|
40
59
|
type: z.enum(['transfer', 'swap', 'bridge', 'mint', 'custom']),
|
|
41
60
|
chainName: z.string(),
|
|
@@ -261,6 +280,8 @@ app.post('/approve-tx/:id', (req, res) => {
|
|
|
261
280
|
signerReq.end();
|
|
262
281
|
});
|
|
263
282
|
|
|
283
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
284
|
+
|
|
264
285
|
const server = app.listen(Number(PORT), '127.0.0.1', () => {
|
|
265
286
|
console.log(`[Policy Engine] Listening on 127.0.0.1:${PORT} (Secured Local Loopback)`);
|
|
266
287
|
});
|
|
@@ -274,3 +295,10 @@ server.on('error', (e: any) => {
|
|
|
274
295
|
process.exit(1);
|
|
275
296
|
}
|
|
276
297
|
});
|
|
298
|
+
|
|
299
|
+
// Start UDS Server for Hyper-Optimized IPC
|
|
300
|
+
const udsServer = http.createServer(app);
|
|
301
|
+
if (fs.existsSync(POLICY_SOCKET)) fs.unlinkSync(POLICY_SOCKET);
|
|
302
|
+
udsServer.listen(POLICY_SOCKET, () => {
|
|
303
|
+
console.log(`[Policy Engine] Listening on UDS ${POLICY_SOCKET} (Hyper-Optimized IPC)`);
|
|
304
|
+
});
|