nyxora 26.6.8-2 → 26.6.9
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/CHANGELOG.md +13 -1
- package/README.md +4 -3
- package/SECURITY.md +13 -3
- package/bin/nyxora.mjs +40 -2
- package/dist/packages/core/src/agent/reasoning.js +1 -1
- package/dist/packages/core/src/agent/transactionManager.js +2 -0
- package/dist/packages/core/src/gateway/server.js +50 -5
- package/dist/packages/core/src/gateway/setup.js +3 -0
- package/dist/packages/core/src/memory/logger.js +15 -3
- package/dist/packages/core/src/system/pluginManager.js +2 -1
- package/dist/packages/core/src/system/skills/installSkill.js +2 -1
- package/dist/packages/core/src/utils/state.js +22 -4
- package/dist/packages/core/src/web3/config.js +11 -1
- package/dist/packages/core/src/web3/skills/bridgeToken.js +30 -2
- package/dist/packages/core/src/web3/skills/checkSecurity.js +2 -0
- package/dist/packages/core/src/web3/skills/customTx.js +1 -1
- package/dist/packages/core/src/web3/skills/mintNft.js +1 -1
- package/dist/packages/core/src/web3/skills/swapToken.js +1 -1
- package/dist/packages/core/src/web3/skills/transfer.js +1 -1
- package/dist/packages/core/src/web3/utils/tokens.js +6 -0
- package/dist/packages/policy/src/server.js +14 -4
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/reasoning.ts +1 -1
- package/packages/core/src/agent/transactionManager.ts +3 -0
- package/packages/core/src/gateway/server.ts +61 -7
- package/packages/core/src/gateway/setup.ts +3 -0
- package/packages/core/src/memory/logger.ts +15 -2
- package/packages/core/src/system/pluginManager.ts +2 -1
- package/packages/core/src/system/skills/installSkill.ts +2 -2
- package/packages/core/src/utils/state.ts +22 -7
- package/packages/core/src/web3/config.ts +10 -2
- package/packages/core/src/web3/skills/bridgeToken.ts +29 -3
- package/packages/core/src/web3/skills/checkSecurity.ts +2 -0
- package/packages/core/src/web3/skills/customTx.ts +1 -1
- package/packages/core/src/web3/skills/mintNft.ts +1 -1
- package/packages/core/src/web3/skills/swapToken.ts +1 -1
- package/packages/core/src/web3/skills/transfer.ts +1 -1
- package/packages/core/src/web3/utils/tokens.ts +6 -0
- package/packages/dashboard/dist/assets/index-BT9WzHpr.js +326 -0
- package/packages/dashboard/dist/index.html +1 -1
- 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 +14 -3
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.dbg.json +4 -0
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.json +85 -0
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json +4 -0
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.json +10 -0
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/utils/Pausable.sol/Pausable.dbg.json +4 -0
- package/packages/registry-contract/artifacts/@openzeppelin/contracts/utils/Pausable.sol/Pausable.json +60 -0
- package/packages/registry-contract/artifacts/build-info/1a74d547ba64d2f3b7adbff726f3d048.json +1 -0
- package/packages/registry-contract/artifacts/contracts/NyxoraAgentRegistry.sol/NyxoraAgentRegistry.dbg.json +4 -0
- package/packages/registry-contract/artifacts/contracts/NyxoraAgentRegistry.sol/NyxoraAgentRegistry.json +316 -0
- package/packages/registry-contract/cache/solidity-files-cache.json +156 -0
- package/packages/registry-contract/contracts/NyxoraAgentRegistry.sol +93 -0
- package/packages/registry-contract/hardhat.config.ts +32 -0
- package/packages/registry-contract/ignition/deployments/chain-421614/artifacts/RegistryModule#NyxoraAgentRegistry.dbg.json +4 -0
- package/packages/registry-contract/ignition/deployments/chain-421614/artifacts/RegistryModule#NyxoraAgentRegistry.json +316 -0
- package/packages/registry-contract/ignition/deployments/chain-421614/build-info/1a74d547ba64d2f3b7adbff726f3d048.json +12064 -0
- package/packages/registry-contract/ignition/deployments/chain-421614/deployed_addresses.json +3 -0
- package/packages/registry-contract/ignition/deployments/chain-421614/journal.jsonl +8 -0
- package/packages/registry-contract/ignition/modules/Registry.ts +9 -0
- package/packages/registry-contract/package.json +23 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/contracts/access/index.ts +4 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/contracts/index.ts +7 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/contracts/utils/Pausable.ts +150 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/contracts/utils/index.ts +4 -0
- package/packages/registry-contract/typechain-types/@openzeppelin/index.ts +5 -0
- package/packages/registry-contract/typechain-types/common.ts +131 -0
- package/packages/registry-contract/typechain-types/contracts/NyxoraAgentRegistry.ts +416 -0
- package/packages/registry-contract/typechain-types/contracts/index.ts +4 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +96 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/contracts/access/index.ts +4 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/contracts/index.ts +5 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/contracts/utils/Pausable__factory.ts +71 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/contracts/utils/index.ts +4 -0
- package/packages/registry-contract/typechain-types/factories/@openzeppelin/index.ts +4 -0
- package/packages/registry-contract/typechain-types/factories/contracts/NyxoraAgentRegistry__factory.ts +378 -0
- package/packages/registry-contract/typechain-types/factories/contracts/index.ts +4 -0
- package/packages/registry-contract/typechain-types/factories/index.ts +5 -0
- package/packages/registry-contract/typechain-types/hardhat.d.ts +99 -0
- package/packages/registry-contract/typechain-types/index.ts +14 -0
- package/packages/signer/package.json +1 -1
- package/test_state.mjs +1 -0
- package/test_state.ts +20 -0
- package/test_updates.mjs +76 -0
- package/packages/dashboard/dist/assets/index-D50q-_33.js +0 -311
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepashangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [26.6.
|
|
8
|
+
## [26.6.9] - 2026-06-08
|
|
9
|
+
### Security & UX Hardening
|
|
10
|
+
- **Zero-Trust Auto-Lock (Passwordless)**: Implemented a robust idle timeout mechanism in the React Dashboard with an elegant glassmorphism blur overlay. The dashboard securely locks after periods of inactivity, requiring the user to authorize unlock directly via the CLI (`nyxora unlock`) to prevent unauthorized local access.
|
|
11
|
+
- **Approval Replay Protection (Nonce Guard)**: Hardened the `transactionManager` to cryptographically sign all pending transaction payloads with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint now strictly enforces Nonce matching and immediately marks it as `used_` upon first validation, completely eliminating double-spending and Replay Attack vectors.
|
|
12
|
+
- **Graceful Shutdown (SQLite WAL Guard)**: Integrated deep `SIGTERM` and `SIGINT` signal listeners within the Gateway server. When the daemon is halted, the system now safely terminates active incoming requests and explicitly invokes `logger.close()` to securely flush SQLite Write-Ahead Logs (WAL) before exiting, completely eliminating the risk of database corruption.
|
|
13
|
+
- **Resilient UI (Reconnect Overlay)**: Engineered a global network interceptor inside the Dashboard's React `apiFetch` utility. If the daemon goes offline unexpectedly or is restarting, the UI instantly pauses and deploys a transparent, pulsing "Nyxora Daemon Offline" screen. Once the daemon is revived, the overlay automatically lifts, preserving the user's workflow seamlessly.
|
|
14
|
+
|
|
15
|
+
### Architecture & Production Readiness
|
|
16
|
+
- **Dynamic Port Anti-Collision**: Replaced the hardcoded `3001` Policy Server port with a dynamic `process.env.POLICY_PORT` fallback. All Web3 Agents (Bridge, Swap, Transfer, etc.) are now dynamically linked to this environment variable, completely eliminating `ECONNREFUSED` crashes when port 3001 is occupied by other local developer applications.
|
|
17
|
+
- **Production-Ready Path Resolution**: Eliminated hardcoded `process.cwd()` dependencies across the Gateway, Dashboard, and Plugin Manager. The CLI now utilizes robust absolute `__dirname` and `getAppDir()` traversal, guaranteeing the Dashboard UI and External Skills load flawlessly regardless of where the global CLI command is executed from.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [26.6.8] - 2026-06-08
|
|
9
21
|
### Enterprise Features & Web3 Enhancements
|
|
10
22
|
- **Zero-Downtime Directory Migration**: Restructured the root `~/.nyxora` local data directory into a strict `config/`, `data/`, `auth/`, and `run/` subdirectory architecture. Implemented a Lazy Auto-Migration Engine (`getPath()`) that seamlessly relocates legacy files to their new secure zones instantly upon access, ensuring zero-downtime and zero-data-loss upgrades for existing users.
|
|
11
23
|
### Security & UX Updates
|
package/README.md
CHANGED
|
@@ -20,10 +20,10 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
|
|
|
20
20
|
|
|
21
21
|
### Advanced Security Architecture
|
|
22
22
|
* **3-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (LLM Runtime), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
|
|
23
|
-
* **
|
|
23
|
+
* **Approval Replay Protection (Nonce Guard)**: Transactions requested by the AI are drafted as hashes and signed with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces Nonce matching to completely eliminate double-spending and Replay Attacks.
|
|
24
24
|
* **Immutable Policy Guardrails**: Transaction limits (e.g. `max_usd_per_tx`) are strictly enforced by the Policy Engine. The LLM has zero write-access to bypass these rules.
|
|
25
25
|
* **Plugin Sandbox VM**: Execute community-built external skills securely inside an airtight Node.js `vm` chamber with zero access to your file system or terminal processes.
|
|
26
|
-
* **
|
|
26
|
+
* **Graceful SQLite WAL Shutdown**: Integrated `SIGTERM`/`SIGINT` interceptors ensure that when the daemon stops, active requests are safely terminated and SQLite Write-Ahead Logs (WAL) are securely flushed, preventing database corruption.
|
|
27
27
|
|
|
28
28
|
### 🌐 Web3 Skills (On-Chain)
|
|
29
29
|
* **Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
|
|
@@ -39,8 +39,9 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
|
|
|
39
39
|
* **Unstoppable Synergy**: Combine both engines with a single prompt. Example: *"Read the latest presale token email from my Gmail, automatically set a Take Profit limit order on Uniswap, and log the execution result to my Google Sheets."*
|
|
40
40
|
|
|
41
41
|
### AI & UI Customization
|
|
42
|
+
* **Zero-Trust Auto-Lock (Passwordless)**: A sleek glassmorphism blur overlay automatically locks the dashboard during inactivity. Unlocking requires physical local execution via the CLI (`nyxora unlock`), preventing unauthorized local access.
|
|
43
|
+
* **Resilient UI (Reconnect Overlay)**: Built-in global network interceptors ensure that if the daemon restarts or crashes, the UI immediately pauses with a transparent "Offline" overlay and seamlessly resumes your workflow once revived.
|
|
42
44
|
* **Zero-Click Multi-Session**: Instantly create isolated chat sessions with smart auto-naming triggered by your first prompt, exactly like ChatGPT.
|
|
43
|
-
* **Dynamic Trending Tokens**: Live top 5 crypto assets feed directly injected into the dashboard, completely clickable for instant AI market analysis.
|
|
44
45
|
* **Premium Utility-Centric UI**: A sleek, dark-themed dashboard built for high readability and professional Web3 execution, featuring Pseudo-Generative UI widgets (`<BalanceWidget>`, `<MarketWidget>`, `<SwapWidget>`).
|
|
45
46
|
* **Massive 2026 Model Roster**: Out-of-the-box support for cutting-edge models via Google Gemini, OpenAI, Groq, Mistral, xAI, DeepSeek, OpenRouter, and local Ollama, equipped with a searchable CLI prompt to instantly find your favorite model.
|
|
46
47
|
* **Strict NLP Exactness (Rule 8)**: The AI is rigorously instructed never to hallucinate or guess missing transaction parameters (like destination chains or swap amounts). It halts and requests human clarification, guaranteeing 100% precision.
|
package/SECURITY.md
CHANGED
|
@@ -66,12 +66,22 @@ approval_hash = sha256(policy_diff + timestamp + user_id)
|
|
|
66
66
|
```
|
|
67
67
|
This ensures that what the human saw on the UI matches exactly what is being executed, preventing the LLM from secretly modifying the payload in transit.
|
|
68
68
|
|
|
69
|
-
### Anti-Replay Challenge Nonce
|
|
70
|
-
Every approval UI prompt utilizes a **Single-Use Challenge Nonce** with
|
|
69
|
+
### Anti-Replay Challenge Nonce (Nonce Guard)
|
|
70
|
+
Every approval UI prompt utilizes a **Single-Use Challenge Nonce** (a randomized 16-byte cryptographic string). The `transactionManager` signs all pending payloads with this Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces matching and immediately marks the Nonce as `used_` upon first validation. This completely eliminates *XSS Token Leaks*, *Double-Spending*, and *Replay Attacks*, ensuring that an old approval token cannot be stolen and reused for a malicious transaction later.
|
|
71
71
|
|
|
72
72
|
---
|
|
73
73
|
|
|
74
|
-
## 4.
|
|
74
|
+
## 4. Physical Access & Data Integrity
|
|
75
|
+
|
|
76
|
+
### Zero-Trust Auto-Lock (Physical Protection)
|
|
77
|
+
To protect against unauthorized physical access (e.g., leaving a laptop unattended), the Dashboard implements a **Zero-Trust Auto-Lock** mechanism. After a period of inactivity, the UI aggressively blurs and locks all state. Unlocking the interface requires the user to execute `nyxora unlock` directly from the host operating system's CLI. This guarantees that anyone physically sitting at the unlocked dashboard cannot execute transactions without also possessing SSH or direct terminal access to the host machine.
|
|
78
|
+
|
|
79
|
+
### SQLite WAL Graceful Shutdown (Data Integrity)
|
|
80
|
+
To prevent database corruption during abrupt terminations, the Gateway daemon employs deep `SIGTERM` and `SIGINT` interceptors. When a halt is requested, the system safely terminates active incoming API requests and explicitly flushes the SQLite Write-Ahead Logs (WAL) before fully exiting, ensuring enterprise-grade state stability.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 5. Plugin Sandboxing (Node.js VM Isolation)
|
|
75
85
|
|
|
76
86
|
Community plugins and custom skills are NEVER executed directly at the OS level. Instead, Nyxora creates an airtight **Virtual Machine (VM) Sandbox** in memory.
|
|
77
87
|
|
package/bin/nyxora.mjs
CHANGED
|
@@ -117,7 +117,13 @@ async function dashboard() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (fs.existsSync(tokenFile)) {
|
|
120
|
-
|
|
120
|
+
let token = fs.readFileSync(tokenFile, 'utf8').trim();
|
|
121
|
+
if (token.startsWith('{')) {
|
|
122
|
+
try {
|
|
123
|
+
const parsed = JSON.parse(token);
|
|
124
|
+
token = parsed.token;
|
|
125
|
+
} catch (e) {}
|
|
126
|
+
}
|
|
121
127
|
const url = `http://localhost:3000?token=${token}`;
|
|
122
128
|
console.log(`Opening Dashboard at ${url}`);
|
|
123
129
|
try {
|
|
@@ -275,8 +281,38 @@ async function runDoctor() {
|
|
|
275
281
|
await new Promise(resolve => child.on('close', resolve));
|
|
276
282
|
}
|
|
277
283
|
|
|
284
|
+
async function unlock() {
|
|
285
|
+
if (fs.existsSync(tokenFile)) {
|
|
286
|
+
let token = fs.readFileSync(tokenFile, 'utf8').trim();
|
|
287
|
+
if (token.startsWith('{')) {
|
|
288
|
+
try {
|
|
289
|
+
const parsed = JSON.parse(token);
|
|
290
|
+
token = parsed.token;
|
|
291
|
+
} catch (e) {}
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const fetch = (await import('node-fetch')).default;
|
|
295
|
+
const res = await fetch('http://localhost:3000/api/status/unlock', {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: {
|
|
298
|
+
'x-nyxora-token': token
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
if (res.ok) {
|
|
302
|
+
console.log('✅ Dashboard unlocked successfully.');
|
|
303
|
+
} else {
|
|
304
|
+
console.log('❌ Failed to unlock dashboard. Is the daemon running?');
|
|
305
|
+
}
|
|
306
|
+
} catch (e) {
|
|
307
|
+
console.log('❌ Failed to communicate with the daemon. Is it running?');
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
console.log('❌ Authentication token not found.');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
278
314
|
async function main() {
|
|
279
|
-
switch
|
|
315
|
+
switch(command) {
|
|
280
316
|
case 'doctor': await runDoctor(); break;
|
|
281
317
|
case 'setup': await setup(); break;
|
|
282
318
|
case 'clear': await clearMemory(process.argv.slice(3)); break;
|
|
@@ -286,6 +322,7 @@ async function main() {
|
|
|
286
322
|
case 'stop': await stop(); break;
|
|
287
323
|
case 'restart': await restart(); break;
|
|
288
324
|
case 'dashboard': await dashboard(); break;
|
|
325
|
+
case 'unlock': await unlock(); break;
|
|
289
326
|
case 'clean-logs': await cleanLogs(); break;
|
|
290
327
|
case 'autostart': await autostart(process.argv[3]); break;
|
|
291
328
|
case '-v':
|
|
@@ -307,6 +344,7 @@ Commands:
|
|
|
307
344
|
restart Restart the daemon
|
|
308
345
|
setup Run the interactive Setup Wizard
|
|
309
346
|
dashboard Open the dashboard in your browser
|
|
347
|
+
unlock Unlock an inactive dashboard session
|
|
310
348
|
doctor Run system diagnostics and check requirements
|
|
311
349
|
clear Atomically clear the AI's short/long-term memory SQLite database
|
|
312
350
|
clean-logs Clear the daemon logs
|
|
@@ -114,7 +114,7 @@ function getSystemPrompt() {
|
|
|
114
114
|
You are equipped with a native wallet.
|
|
115
115
|
The current real-world date and time is: ${currentDateTime}. Use this for any time-related questions.
|
|
116
116
|
|
|
117
|
-
CRITICAL RULE 1:
|
|
117
|
+
CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
|
|
118
118
|
CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt. If the user's latest prompt is in English, you MUST reply entirely in English, completely ignoring the language of previous messages. If their latest prompt is in Indonesian, reply in Indonesian.
|
|
119
119
|
CRITICAL RULE 3: FORMATTING & CONCISENESS.
|
|
120
120
|
- Your responses MUST be concise and to the point. Do not add unnecessary fluff or overly long explanations unless explicitly asked.
|
|
@@ -9,6 +9,7 @@ class TransactionManager {
|
|
|
9
9
|
transactions = new Map();
|
|
10
10
|
createPendingTransaction(type, chainName, details) {
|
|
11
11
|
const id = crypto_1.default.randomUUID();
|
|
12
|
+
const nonce = crypto_1.default.randomBytes(16).toString('hex');
|
|
12
13
|
const tx = {
|
|
13
14
|
id,
|
|
14
15
|
type,
|
|
@@ -16,6 +17,7 @@ class TransactionManager {
|
|
|
16
17
|
details,
|
|
17
18
|
status: 'pending',
|
|
18
19
|
createdAt: Date.now(),
|
|
20
|
+
nonce,
|
|
19
21
|
};
|
|
20
22
|
this.transactions.set(id, tx);
|
|
21
23
|
return tx;
|
|
@@ -109,14 +109,21 @@ app.use('/api', (req, res, next) => {
|
|
|
109
109
|
return next();
|
|
110
110
|
}
|
|
111
111
|
const token = req.headers['x-nyxora-token'];
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
const validation = (0, state_1.validateToken)(token);
|
|
113
|
+
if (!validation.valid) {
|
|
114
|
+
console.error(`[Auth] Rejected ${req.method} ${req.originalUrl} - Received invalid token.`);
|
|
114
115
|
return res.status(401).json({ error: `Unauthorized: Invalid or missing token.` });
|
|
115
116
|
}
|
|
116
117
|
next();
|
|
117
118
|
});
|
|
118
119
|
// Serve Static Dashboard
|
|
119
|
-
|
|
120
|
+
let rootDir = __dirname;
|
|
121
|
+
while (!fs_1.default.existsSync(path_1.default.join(rootDir, 'package.json'))) {
|
|
122
|
+
rootDir = path_1.default.dirname(rootDir);
|
|
123
|
+
if (rootDir === '/' || rootDir === 'C:\\')
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
const dashboardPath = path_1.default.join(rootDir, 'packages', 'dashboard', 'dist');
|
|
120
127
|
app.use(express_1.default.static(dashboardPath));
|
|
121
128
|
app.get('/', (req, res) => {
|
|
122
129
|
res.sendFile(path_1.default.join(dashboardPath, 'index.html'));
|
|
@@ -333,16 +340,29 @@ app.delete('/api/auth/google', async (req, res) => {
|
|
|
333
340
|
const success = await (0, googleAuthModule_1.logoutGoogle)();
|
|
334
341
|
res.json({ success });
|
|
335
342
|
});
|
|
343
|
+
let lastUnlockRequest = 0;
|
|
344
|
+
app.post('/api/status/unlock', (req, res) => {
|
|
345
|
+
lastUnlockRequest = Date.now();
|
|
346
|
+
res.json({ success: true });
|
|
347
|
+
});
|
|
348
|
+
app.get('/api/status/lock', (req, res) => {
|
|
349
|
+
res.json({ lastUnlockRequest });
|
|
350
|
+
});
|
|
336
351
|
app.get('/api/transactions', (req, res) => {
|
|
337
352
|
res.json(transactionManager_1.txManager.getPending());
|
|
338
353
|
});
|
|
339
354
|
app.post('/api/transactions/:id/approve', async (req, res) => {
|
|
340
355
|
try {
|
|
341
356
|
const id = req.params.id;
|
|
342
|
-
const { sessionId } = req.body || {};
|
|
357
|
+
const { sessionId, nonce } = req.body || {};
|
|
343
358
|
const tx = transactionManager_1.txManager.getTransaction(id);
|
|
344
359
|
if (!tx || tx.status !== 'pending')
|
|
345
360
|
return res.status(404).json({ error: 'Transaction not found or not pending' });
|
|
361
|
+
if (tx.nonce !== nonce) {
|
|
362
|
+
return res.status(403).json({ error: 'Invalid or missing nonce. Replay attack detected.' });
|
|
363
|
+
}
|
|
364
|
+
// Invalidate the nonce immediately to prevent replay
|
|
365
|
+
tx.nonce = 'used_' + Date.now();
|
|
346
366
|
transactionManager_1.txManager.updateStatus(id, 'approved', 'Executing on-chain...');
|
|
347
367
|
res.json({ success: true, status: 'processing', message: 'Transaction submitted to background processing.' });
|
|
348
368
|
// Execute in background
|
|
@@ -652,11 +672,36 @@ function startServer() {
|
|
|
652
672
|
});
|
|
653
673
|
limitOrderManager_1.limitOrderManager.startMonitor();
|
|
654
674
|
const PORT = Number(process.env.PORT || 3000);
|
|
655
|
-
app.listen(PORT, '0.0.0.0', () => {
|
|
675
|
+
const server = app.listen(PORT, '0.0.0.0', () => {
|
|
656
676
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
657
677
|
// Start the Telegram bot listener
|
|
658
678
|
(0, telegram_1.startTelegramBot)();
|
|
659
679
|
});
|
|
680
|
+
server.on('error', (e) => {
|
|
681
|
+
if (e.code === 'EADDRINUSE') {
|
|
682
|
+
console.error(`[Nyxora Gateway] Port ${PORT} is already in use. Is Nyxora already running?`);
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
console.error(`[Nyxora Gateway] Server error:`, e);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
const gracefulShutdown = () => {
|
|
691
|
+
console.log('[Nyxora Gateway] Received shutdown signal. Closing server...');
|
|
692
|
+
server.close(() => {
|
|
693
|
+
console.log('[Nyxora Gateway] HTTP server closed.');
|
|
694
|
+
reasoning_1.logger.close();
|
|
695
|
+
process.exit(0);
|
|
696
|
+
});
|
|
697
|
+
// Force exit after 10s if stuck
|
|
698
|
+
setTimeout(() => {
|
|
699
|
+
console.error('[Nyxora Gateway] Forced shutdown after 10s.');
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}, 10000);
|
|
702
|
+
};
|
|
703
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
704
|
+
process.on('SIGINT', gracefulShutdown);
|
|
660
705
|
}
|
|
661
706
|
// Start server if this file is run directly
|
|
662
707
|
if (require.main === module) {
|
|
@@ -231,6 +231,9 @@ Provider: ${config.llm.provider}`;
|
|
|
231
231
|
{ value: 'optimism', label: 'OP Mainnet' },
|
|
232
232
|
{ value: 'polygon', label: 'Polygon (Matic)' },
|
|
233
233
|
{ value: 'sepolia', label: 'Sepolia (Testnet)' },
|
|
234
|
+
{ value: 'base_sepolia', label: 'Base Sepolia (Testnet)' },
|
|
235
|
+
{ value: 'arbitrum_sepolia', label: 'Arbitrum Sepolia (Testnet)' },
|
|
236
|
+
{ value: 'optimism_sepolia', label: 'OP Sepolia (Testnet)' },
|
|
234
237
|
],
|
|
235
238
|
});
|
|
236
239
|
if ((0, prompts_1.isCancel)(defaultChain))
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Logger = void 0;
|
|
6
|
+
exports.logger = exports.Logger = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
@@ -114,8 +114,10 @@ class Logger {
|
|
|
114
114
|
return id;
|
|
115
115
|
}
|
|
116
116
|
deleteSession(sessionId) {
|
|
117
|
-
this.db.prepare('DELETE FROM
|
|
118
|
-
|
|
117
|
+
const stmt = this.db.prepare('DELETE FROM sessions WHERE id = ?');
|
|
118
|
+
stmt.run(sessionId);
|
|
119
|
+
const stmt2 = this.db.prepare('DELETE FROM messages WHERE session_id = ?');
|
|
120
|
+
stmt2.run(sessionId);
|
|
119
121
|
}
|
|
120
122
|
renameSession(sessionId, newTitle) {
|
|
121
123
|
this.db.prepare('UPDATE sessions SET title = ? WHERE id = ?').run(newTitle, sessionId);
|
|
@@ -182,5 +184,15 @@ class Logger {
|
|
|
182
184
|
this.db.prepare('DELETE FROM messages WHERE session_id IS NULL').run();
|
|
183
185
|
}
|
|
184
186
|
}
|
|
187
|
+
close() {
|
|
188
|
+
try {
|
|
189
|
+
this.db.close();
|
|
190
|
+
console.log('[Nyxora Memory] SQLite database closed gracefully.');
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
console.error('[Nyxora Memory] Error closing database:', e);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
185
196
|
}
|
|
186
197
|
exports.Logger = Logger;
|
|
198
|
+
exports.logger = new Logger();
|
|
@@ -7,10 +7,11 @@ exports.pluginManager = exports.PluginManager = void 0;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const isolated_vm_1 = __importDefault(require("isolated-vm"));
|
|
10
|
+
const paths_1 = require("../config/paths");
|
|
10
11
|
class PluginManager {
|
|
11
12
|
skills = new Map();
|
|
12
13
|
async loadPlugins() {
|
|
13
|
-
const pluginsDir = path_1.default.join(
|
|
14
|
+
const pluginsDir = path_1.default.join((0, paths_1.getAppDir)(), 'plugins');
|
|
14
15
|
if (!fs_1.default.existsSync(pluginsDir)) {
|
|
15
16
|
fs_1.default.mkdirSync(pluginsDir, { recursive: true });
|
|
16
17
|
return;
|
|
@@ -7,6 +7,7 @@ exports.installExternalSkillToolDefinition = void 0;
|
|
|
7
7
|
exports.installExternalSkill = installExternalSkill;
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const paths_1 = require("../../config/paths");
|
|
10
11
|
async function installExternalSkill(url) {
|
|
11
12
|
try {
|
|
12
13
|
const response = await fetch(url);
|
|
@@ -20,7 +21,7 @@ async function installExternalSkill(url) {
|
|
|
20
21
|
filename = `skill_${Date.now()}.ts`;
|
|
21
22
|
}
|
|
22
23
|
// Ensure external_skills directory exists
|
|
23
|
-
const pluginsDir = path_1.default.join(
|
|
24
|
+
const pluginsDir = path_1.default.join((0, paths_1.getAppDir)(), 'plugins');
|
|
24
25
|
if (!fs_1.default.existsSync(pluginsDir)) {
|
|
25
26
|
fs_1.default.mkdirSync(pluginsDir, { recursive: true });
|
|
26
27
|
}
|
|
@@ -4,16 +4,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getSessionToken = getSessionToken;
|
|
7
|
+
exports.validateToken = validateToken;
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
let sessionToken = null;
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
10
|
const paths_1 = require("../config/paths");
|
|
11
|
+
let sessionToken = null;
|
|
11
12
|
function getSessionToken() {
|
|
12
13
|
if (!sessionToken) {
|
|
13
14
|
const tokenFile = (0, paths_1.getPath)('auth.token');
|
|
14
15
|
try {
|
|
15
16
|
if (fs_1.default.existsSync(tokenFile)) {
|
|
16
|
-
|
|
17
|
+
const raw = fs_1.default.readFileSync(tokenFile, 'utf8').trim();
|
|
18
|
+
// Support migrating from the temporary JSON format back to raw string
|
|
19
|
+
if (raw.startsWith('{')) {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
sessionToken = parsed.token;
|
|
23
|
+
// Overwrite file to restore clean string format
|
|
24
|
+
fs_1.default.writeFileSync(tokenFile, sessionToken, { mode: 0o600 });
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
sessionToken = raw;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
sessionToken = raw;
|
|
32
|
+
}
|
|
17
33
|
}
|
|
18
34
|
else {
|
|
19
35
|
sessionToken = crypto_1.default.randomBytes(32).toString('hex');
|
|
@@ -21,9 +37,11 @@ function getSessionToken() {
|
|
|
21
37
|
}
|
|
22
38
|
}
|
|
23
39
|
catch (e) {
|
|
24
|
-
|
|
25
|
-
sessionToken = crypto_1.default.randomBytes(32).toString('hex');
|
|
40
|
+
sessionToken = crypto_1.default.randomBytes(32).toString('hex');
|
|
26
41
|
}
|
|
27
42
|
}
|
|
28
43
|
return sessionToken;
|
|
29
44
|
}
|
|
45
|
+
function validateToken(incomingToken) {
|
|
46
|
+
return { valid: incomingToken === getSessionToken() };
|
|
47
|
+
}
|
|
@@ -15,6 +15,8 @@ exports.supportedChains = {
|
|
|
15
15
|
sepolia: chains_1.sepolia,
|
|
16
16
|
polygon: chains_1.polygon,
|
|
17
17
|
base_sepolia: chains_1.baseSepolia,
|
|
18
|
+
arbitrum_sepolia: chains_1.arbitrumSepolia,
|
|
19
|
+
optimism_sepolia: chains_1.optimismSepolia,
|
|
18
20
|
};
|
|
19
21
|
exports.SUPPORTED_CHAIN_NAMES = Object.keys(exports.supportedChains);
|
|
20
22
|
function getPublicClient(chainName) {
|
|
@@ -67,6 +69,14 @@ function getPublicClient(chainName) {
|
|
|
67
69
|
transports.push((0, viem_1.http)('https://polygon.llamarpc.com', { timeout: 5000 }));
|
|
68
70
|
transports.push((0, viem_1.http)('https://polygon-rpc.com', { timeout: 5000 }));
|
|
69
71
|
}
|
|
72
|
+
else if (chainName === 'arbitrum_sepolia') {
|
|
73
|
+
transports.push((0, viem_1.http)('https://arbitrum-sepolia-rpc.publicnode.com', { timeout: 5000 }));
|
|
74
|
+
transports.push((0, viem_1.http)('https://sepolia-rollup.arbitrum.io/rpc', { timeout: 5000 }));
|
|
75
|
+
}
|
|
76
|
+
else if (chainName === 'optimism_sepolia') {
|
|
77
|
+
transports.push((0, viem_1.http)('https://optimism-sepolia-rpc.publicnode.com', { timeout: 5000 }));
|
|
78
|
+
transports.push((0, viem_1.http)('https://sepolia.optimism.io', { timeout: 5000 }));
|
|
79
|
+
}
|
|
70
80
|
}
|
|
71
81
|
// Always append the default public RPC (like cloudflare) as the last resort
|
|
72
82
|
transports.push((0, viem_1.http)(undefined, { timeout: 5000 }));
|
|
@@ -83,7 +93,7 @@ function getPublicClient(chainName) {
|
|
|
83
93
|
async function getAddress() {
|
|
84
94
|
const token = process.env.INTERNAL_AUTH_TOKEN;
|
|
85
95
|
try {
|
|
86
|
-
const res = await fetch(
|
|
96
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/address`, {
|
|
87
97
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
88
98
|
});
|
|
89
99
|
if (!res.ok)
|
|
@@ -50,6 +50,8 @@ const CHAIN_IDS = {
|
|
|
50
50
|
sepolia: 11155111,
|
|
51
51
|
polygon: 137,
|
|
52
52
|
base_sepolia: 84532,
|
|
53
|
+
arbitrum_sepolia: 421614,
|
|
54
|
+
optimism_sepolia: 11155420,
|
|
53
55
|
};
|
|
54
56
|
async function getLifiQuote(fromChainId, toChainId, fromToken, toToken, amountWei, userAddress, slippage) {
|
|
55
57
|
const url = new URL('https://li.quest/v1/quote');
|
|
@@ -140,7 +142,33 @@ async function prepareBridgeToken(fromChainName, toChainName, fromToken, toToken
|
|
|
140
142
|
}
|
|
141
143
|
const isTestnet = fromChainId === 11155111 || toChainId === 11155111 || fromChainId === 84532 || toChainId === 84532;
|
|
142
144
|
let actualProvider = mode === "auto" ? (isTestnet ? "relay" : "lifi") : providerName;
|
|
143
|
-
if (
|
|
145
|
+
if (fromChainId === 11155111 && toChainId === 421614 && isNativeIn) {
|
|
146
|
+
const { encodeFunctionData } = await Promise.resolve().then(() => __importStar(require('viem')));
|
|
147
|
+
txRequest = {
|
|
148
|
+
to: "0xaae29b0366299461418f5324a79afc425be5ae21",
|
|
149
|
+
value: amountWei,
|
|
150
|
+
data: encodeFunctionData({
|
|
151
|
+
abi: [{ type: 'function', name: 'depositEth', inputs: [], outputs: [{ type: 'uint256' }], stateMutability: 'payable' }],
|
|
152
|
+
args: []
|
|
153
|
+
})
|
|
154
|
+
};
|
|
155
|
+
expectedOutputStr = amountStr;
|
|
156
|
+
actualProvider = "arbitrum-canonical-inbox";
|
|
157
|
+
}
|
|
158
|
+
else if (fromChainId === 421614 && toChainId === 11155111 && isNativeIn) {
|
|
159
|
+
const { encodeFunctionData } = await Promise.resolve().then(() => __importStar(require('viem')));
|
|
160
|
+
txRequest = {
|
|
161
|
+
to: "0x0000000000000000000000000000000000000064",
|
|
162
|
+
value: amountWei,
|
|
163
|
+
data: encodeFunctionData({
|
|
164
|
+
abi: [{ type: 'function', name: 'withdrawEth', inputs: [{ name: 'destination', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'payable' }],
|
|
165
|
+
args: [userAddress]
|
|
166
|
+
})
|
|
167
|
+
};
|
|
168
|
+
expectedOutputStr = amountStr;
|
|
169
|
+
actualProvider = "arbsys-canonical-withdraw";
|
|
170
|
+
}
|
|
171
|
+
else if (actualProvider === "lifi") {
|
|
144
172
|
const quote = await getLifiQuote(fromChainId, toChainId, fromTokenAddress, toTokenAddress, amountWei, userAddress, actualSlippage / 100);
|
|
145
173
|
txRequest = quote.transactionRequest;
|
|
146
174
|
approvalAddress = quote.estimate.approvalAddress;
|
|
@@ -203,7 +231,7 @@ async function executeBridge(chainName, params, autoApprove = false) {
|
|
|
203
231
|
const crypto = require('crypto');
|
|
204
232
|
payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + amountWei).digest('hex');
|
|
205
233
|
}
|
|
206
|
-
const res = await fetch(
|
|
234
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/request-tx`, {
|
|
207
235
|
method: 'POST',
|
|
208
236
|
headers: {
|
|
209
237
|
'Content-Type': 'application/json',
|
|
@@ -56,7 +56,7 @@ async function executeCustomTx(chainName, params, autoApprove = false) {
|
|
|
56
56
|
const signAmount = valueWei || "0";
|
|
57
57
|
payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + signAmount).digest('hex');
|
|
58
58
|
}
|
|
59
|
-
const res = await fetch(
|
|
59
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/request-tx`, {
|
|
60
60
|
method: 'POST',
|
|
61
61
|
headers: {
|
|
62
62
|
'Content-Type': 'application/json',
|
|
@@ -84,7 +84,7 @@ async function executeMintNft(chainName, params, autoApprove = false) {
|
|
|
84
84
|
const signAmount = valueWei || "0";
|
|
85
85
|
payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + signAmount).digest('hex');
|
|
86
86
|
}
|
|
87
|
-
const res = await fetch(
|
|
87
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/request-tx`, {
|
|
88
88
|
method: 'POST',
|
|
89
89
|
headers: {
|
|
90
90
|
'Content-Type': 'application/json',
|
|
@@ -247,7 +247,7 @@ async function executeSwap(chainName, params, autoApprove = false) {
|
|
|
247
247
|
// In a real scenario, use a specific txId or nonce.
|
|
248
248
|
payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + amountWei).digest('hex');
|
|
249
249
|
}
|
|
250
|
-
const res = await fetch(
|
|
250
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/request-tx`, {
|
|
251
251
|
method: 'POST',
|
|
252
252
|
headers: {
|
|
253
253
|
'Content-Type': 'application/json',
|
|
@@ -78,7 +78,7 @@ async function executeTransfer(chainName, params, autoApprove = false) {
|
|
|
78
78
|
const crypto = require('crypto');
|
|
79
79
|
payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + amountWei).digest('hex');
|
|
80
80
|
}
|
|
81
|
-
const res = await fetch(
|
|
81
|
+
const res = await fetch(`http://127.0.0.1:${process.env.POLICY_PORT || 3001}/request-tx`, {
|
|
82
82
|
method: 'POST',
|
|
83
83
|
headers: {
|
|
84
84
|
'Content-Type': 'application/json',
|
|
@@ -104,6 +104,12 @@ exports.TOKEN_MAP = {
|
|
|
104
104
|
},
|
|
105
105
|
base_sepolia: {
|
|
106
106
|
ETH: "0x0000000000000000000000000000000000000000",
|
|
107
|
+
},
|
|
108
|
+
arbitrum_sepolia: {
|
|
109
|
+
ETH: "0x0000000000000000000000000000000000000000",
|
|
110
|
+
},
|
|
111
|
+
optimism_sepolia: {
|
|
112
|
+
ETH: "0x0000000000000000000000000000000000000000",
|
|
107
113
|
}
|
|
108
114
|
};
|
|
109
115
|
function resolveToken(tokenSymbolOrAddress, chainName) {
|
|
@@ -5,13 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const safeLogger_1 = require("../../core/src/utils/safeLogger");
|
|
7
7
|
(0, safeLogger_1.initSafeLogger)();
|
|
8
|
+
const paths_1 = require("../../core/src/config/paths");
|
|
8
9
|
const express_1 = __importDefault(require("express"));
|
|
9
10
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
|
11
12
|
const yaml_1 = __importDefault(require("yaml"));
|
|
12
13
|
const http_1 = __importDefault(require("http"));
|
|
13
14
|
const zod_1 = require("zod");
|
|
14
|
-
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const crypto_1 = __importDefault(require("crypto"));
|
|
16
16
|
process.on('unhandledRejection', (reason, promise) => {
|
|
17
17
|
console.error('[Anti-Crash] Unhandled Rejection at:', promise, 'reason:', reason);
|
|
@@ -20,7 +20,7 @@ process.on('uncaughtException', (error) => {
|
|
|
20
20
|
console.error('[Anti-Crash] Uncaught Exception:', error);
|
|
21
21
|
process.exit(1);
|
|
22
22
|
});
|
|
23
|
-
const PORT = 3001;
|
|
23
|
+
const PORT = process.env.POLICY_PORT || 3001;
|
|
24
24
|
const JWT_SECRET = process.env.INTERNAL_AUTH_TOKEN;
|
|
25
25
|
const SIGNER_SOCKET = process.env.SIGNER_SOCKET_PATH || '/tmp/nyxora-signer.sock';
|
|
26
26
|
if (!JWT_SECRET) {
|
|
@@ -37,7 +37,7 @@ const TxRequestSchema = zod_1.z.object({
|
|
|
37
37
|
});
|
|
38
38
|
let policyRules = {};
|
|
39
39
|
try {
|
|
40
|
-
const policyPath =
|
|
40
|
+
const policyPath = (0, paths_1.getPath)('policy.yaml');
|
|
41
41
|
const file = fs_1.default.readFileSync(policyPath, 'utf8');
|
|
42
42
|
policyRules = yaml_1.default.parse(file);
|
|
43
43
|
}
|
|
@@ -175,6 +175,16 @@ app.post('/approve-tx/:id', (req, res) => {
|
|
|
175
175
|
signerReq.write(requestPayload);
|
|
176
176
|
signerReq.end();
|
|
177
177
|
});
|
|
178
|
-
app.listen(PORT, '127.0.0.1', () => {
|
|
178
|
+
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
179
179
|
console.log(`[Policy Engine] Listening on 127.0.0.1:${PORT} (Secured Local Loopback)`);
|
|
180
180
|
});
|
|
181
|
+
server.on('error', (e) => {
|
|
182
|
+
if (e.code === 'EADDRINUSE') {
|
|
183
|
+
console.error(`[Policy Engine] Port ${PORT} is already in use. Is Nyxora already running?`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.error(`[Policy Engine] Server error:`, e);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
});
|
package/package.json
CHANGED
|
@@ -135,7 +135,7 @@ function getSystemPrompt() {
|
|
|
135
135
|
You are equipped with a native wallet.
|
|
136
136
|
The current real-world date and time is: ${currentDateTime}. Use this for any time-related questions.
|
|
137
137
|
|
|
138
|
-
CRITICAL RULE 1:
|
|
138
|
+
CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
|
|
139
139
|
CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt. If the user's latest prompt is in English, you MUST reply entirely in English, completely ignoring the language of previous messages. If their latest prompt is in Indonesian, reply in Indonesian.
|
|
140
140
|
CRITICAL RULE 3: FORMATTING & CONCISENESS.
|
|
141
141
|
- Your responses MUST be concise and to the point. Do not add unnecessary fluff or overly long explanations unless explicitly asked.
|