nyxora 26.6.7 → 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/.dockerignore +9 -0
- package/CHANGELOG.md +27 -3
- package/README.md +18 -10
- package/SECURITY.md +16 -4
- package/bin/nyxora.mjs +43 -5
- package/dist/launcher.js +137 -0
- package/dist/packages/core/src/agent/reasoning.js +5 -21
- package/dist/packages/core/src/agent/transactionManager.js +2 -0
- package/dist/packages/core/src/config/parser.js +7 -45
- package/dist/packages/core/src/config/paths.js +39 -1
- package/dist/packages/core/src/gateway/cli.js +5 -5
- package/dist/packages/core/src/gateway/doctor.js +3 -5
- package/dist/packages/core/src/gateway/googleAuthModule.js +13 -32
- package/dist/packages/core/src/gateway/legalGenerator.js +88 -0
- package/dist/packages/core/src/gateway/server.js +115 -5
- package/dist/packages/core/src/gateway/setup.js +4 -2
- 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 +24 -7
- 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/manageCustomTokens.js +1 -2
- 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 +21 -4
- package/dist/packages/signer/src/server.js +7 -0
- package/package.json +4 -3
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/reasoning.ts +7 -24
- package/packages/core/src/agent/transactionManager.ts +3 -0
- package/packages/core/src/config/parser.ts +7 -41
- package/packages/core/src/config/paths.ts +42 -1
- package/packages/core/src/gateway/cli.ts +21 -21
- package/packages/core/src/gateway/doctor.ts +4 -5
- package/packages/core/src/gateway/googleAuthModule.ts +12 -27
- package/packages/core/src/gateway/legalGenerator.ts +85 -0
- package/packages/core/src/gateway/server.ts +138 -7
- package/packages/core/src/gateway/setup.ts +5 -2
- 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 +23 -6
- 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/manageCustomTokens.ts +2 -2
- 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 +23 -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/packages/signer/src/server.ts +9 -0
- package/test_state.mjs +1 -0
- package/test_state.ts +20 -0
- package/test_updates.mjs +76 -0
- package/DOCKER.md +0 -68
- package/Dockerfile +0 -43
- package/assets/architecture.png +0 -0
- package/assets/architecture.svg +0 -1
- package/assets/raw-diagram.png +0 -0
- package/assets/security-flow.png +0 -0
- package/launcher.ts +0 -98
- package/packages/dashboard/dist/assets/index-whRRjJKK.js +0 -306
package/.dockerignore
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,32 @@ 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
|
|
21
|
+
### Enterprise Features & Web3 Enhancements
|
|
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.
|
|
23
|
+
### Security & UX Updates
|
|
24
|
+
- **Proactive Anti-Crash Engine**: Implemented `Max Retry` & `Exponential Backoff` auto-respawn logic inside the Monorepo Launcher. The daemon now features robust global `unhandledRejection` and `uncaughtException` guards across the Core, Policy, and Signer subsystems, ensuring sporadic Web3 network timeouts can no longer crash the main reasoning engine.
|
|
25
|
+
- **Death Loop Telegram Alerts**: Integrated a critical emergency monitoring system into the Launcher. If a core microservice crashes catastrophically (5 times within 1 minute), the daemon will autonomously initiate an emergency lock-down to protect the system state and immediately broadcast a high-priority alert directly to the administrator's Telegram.
|
|
26
|
+
- **Unix Socket Anti-EADDRINUSE Guard**: Implemented aggressive pre-flight cleanup routines for the `nyxora-signer.sock` IPC channel. The system now guarantees secure file unlinking before rebinding, eliminating recurring `EADDRINUSE` daemon failures upon rapid restart commands.
|
|
27
|
+
|
|
28
|
+
### Bug Fixes & Optimizations
|
|
29
|
+
- **NPM Package Optimization**: Added `assets/` to `.npmignore` to exclude large architectural diagrams and images from the NPM registry tarball, reducing the total package download size by over 11 MB.
|
|
30
|
+
- **Docker Multi-Stage Build**: Radically refactored `Dockerfile` to a Multi-Stage architecture. The production image now exclusively installs runtime dependencies (`--omit=dev`) and leaves behind heavy build tools (`python3`, `make`, `g++`), dramatically shrinking the final container image size.
|
|
31
|
+
- **Docker Security Patch**: Hardened `.dockerignore` to explicitly block local keystores (`keystore.json`), persistent memory (`memory.db`), and local credentials from accidentally leaking into Docker image layers during local builds.
|
|
32
|
+
|
|
33
|
+
## [26.6.7] - 2026-06-07
|
|
9
34
|
### Enterprise Features & Web3 Enhancements
|
|
10
35
|
- **Enterprise Portfolio Scanner**: Integrated a fully decentralized, real-time Dashboard UI (Nord Theme) to scan all native and ERC-20 token balances across 8 EVM chains natively, without relying on centralized third-party APIs.
|
|
11
36
|
- **Real-Time USD Valuation**: Integrated DexScreener API into the Portfolio Scanner backend to actively compute and display USD portfolio values in real-time. Features an adaptive 2-minute memory cache system to ensure complete immunity against API rate-limits and eliminate LLM token consumption.
|
|
@@ -121,8 +146,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
121
146
|
- **Fast CLI Shortcuts**: Added the `nyxora set-key <provider> <key>` global command shortcut allowing developers to quickly inject or override any API Key (OpenAI, Gemini, OpenRouter, Tavily, Brave) directly into the secure vault without traversing the wizard.
|
|
122
147
|
|
|
123
148
|
### AI Engine Optimizations
|
|
124
|
-
|
|
125
|
-
- **Root-Level Config Auto-Migration**: Restructured `config.yaml` to move all API keys out of the nested `llm.credentials` into a logical, root-level `credentials` object. Implemented a silent auto-migration routine in `parser.ts` that safely upgrades legacy config files on boot without breaking existing setups.
|
|
149
|
+
|
|
126
150
|
- **Web Search Smart Memory Cache**: Embedded a local Memory Cache (`Map`) into the `searchWeb` skill with a 5-minute (300,000ms) TTL. Exact duplicate queries now execute in 0ms and consume 0 API quota, dramatically improving conversation flow.
|
|
127
151
|
- **Deep Research Mode**: The `search_web` tool definition now accepts a dynamic `depth` parameter (1 to 3). If users instruct the AI to conduct comprehensive research, Nyxora will automatically trigger `advanced` API payloads and extract up to 15 top web snippets simultaneously.
|
|
128
152
|
- **Strict Skill Prioritization**: Added CRITICAL RULE 7 to the core NLP System Prompt. The AI is now hard-coded to prioritize native Web3 Skills (e.g. `get_price`, `analyze_market`, `check_security`) for all crypto-related queries, using `search_web` exclusively as a fallback mechanism.
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Nyxora is a **secure, non-custodial runtime infrastructure for autonomous onchain agents** built with a robust Monorepo architecture (Node.js & React). Designed for autonomous workflows with a premium Utility-Centric dark-themed UI and strict client-side key isolation.
|
|
12
12
|
|
|
13
|
-
**Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://
|
|
13
|
+
**Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://nyxoraai.github.io/Nyxora/guide/mcp-integration)
|
|
14
14
|
|
|
15
15
|
It operates under an institutional-grade **Cryptographically Bound Human-in-the-Loop** execution model, ensuring that Remote AIs (LLMs) never have unilateral access to your funds.
|
|
16
16
|
|
|
@@ -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.
|
|
@@ -66,7 +67,7 @@ The following diagram illustrates Nyxora's **3-Tier Monorepo Architecture**, sho
|
|
|
66
67
|
|
|
67
68
|
## 🛡️ Advanced Security & Threat Model
|
|
68
69
|
|
|
69
|
-
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security Blueprint](https://
|
|
70
|
+
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security Blueprint](https://nyxoraai.github.io/Nyxora/).
|
|
70
71
|
|
|
71
72
|
---
|
|
72
73
|
|
|
@@ -93,16 +94,23 @@ Alternatively, you can install it manually on any operating system using NPM:
|
|
|
93
94
|
npm install -g nyxora@latest
|
|
94
95
|
```
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
### 2. Run the Interactive Setup Wizard (API Keys, Wallet, Telegram)
|
|
98
|
+
```bash
|
|
97
99
|
nyxora setup
|
|
100
|
+
```
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
### 3. Start the Nyxora background daemon
|
|
103
|
+
```bash
|
|
100
104
|
nyxora start
|
|
105
|
+
```
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
### 4. Open the Web Dashboard
|
|
108
|
+
```bash
|
|
103
109
|
nyxora dashboard
|
|
110
|
+
```
|
|
104
111
|
|
|
105
|
-
|
|
112
|
+
### Utility: Atomically clear the AI's short-term and long-term memory
|
|
113
|
+
```bash
|
|
106
114
|
nyxora clear --force
|
|
107
115
|
```
|
|
108
116
|
> **⚠️ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the daemon** by running `nyxora restart` for the changes to take effect.
|
|
@@ -135,7 +143,7 @@ npm start
|
|
|
135
143
|
|
|
136
144
|
For complete technical deep-dives into our Cryptographic Architecture, please visit our official VitePress Documentation Site!
|
|
137
145
|
|
|
138
|
-
> **🔗 [Read the Full Nyxora Documentation Here](https://
|
|
146
|
+
> **🔗 [Read the Full Nyxora Documentation Here](https://nyxoraai.github.io/Nyxora/)**
|
|
139
147
|
|
|
140
148
|
*(Includes guides on Secure Wallet Imports, Architecture Blueprints, Troubleshooting, and Custom Skill Development).*
|
|
141
149
|
|
package/SECURITY.md
CHANGED
|
@@ -38,7 +38,9 @@ Nyxora completely eliminates the need for manual "Master Passwords" or custom AE
|
|
|
38
38
|
When the background daemon boots via `nyxora start`, the Signer Vault process reads the Private Key directly from the OS Keyring without requiring human intervention. This ensures the daemon can safely persist across reboots while maintaining institutional-grade encryption at rest.
|
|
39
39
|
|
|
40
40
|
### Secure Fallback Storage
|
|
41
|
-
In headless server environments (e.g., VPS, Docker) where a GUI Keyring is unavailable, Nyxora gracefully falls back to a strictly permissioned
|
|
41
|
+
In headless server environments (e.g., VPS, Docker) where a GUI Keyring is unavailable, Nyxora gracefully falls back to a strictly permissioned `vault.key` file mechanism. This file is programmatically enforced with `chmod 0600` permissions (Read/Write for owner only), preventing access by other system users.
|
|
42
|
+
|
|
43
|
+
> **Note:** This OS-level keyring protection is strictly reserved for your Web3 Wallet Private Keys. Standard integration credentials (like LLM API Keys or Telegram tokens) are managed separately via the `config.yaml` file for transparent developer access.
|
|
42
44
|
|
|
43
45
|
---
|
|
44
46
|
|
|
@@ -64,12 +66,22 @@ approval_hash = sha256(policy_diff + timestamp + user_id)
|
|
|
64
66
|
```
|
|
65
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.
|
|
66
68
|
|
|
67
|
-
### Anti-Replay Challenge Nonce
|
|
68
|
-
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
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
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.
|
|
69
81
|
|
|
70
82
|
---
|
|
71
83
|
|
|
72
|
-
##
|
|
84
|
+
## 5. Plugin Sandboxing (Node.js VM Isolation)
|
|
73
85
|
|
|
74
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.
|
|
75
87
|
|
package/bin/nyxora.mjs
CHANGED
|
@@ -11,9 +11,9 @@ const __dirname = path.dirname(__filename);
|
|
|
11
11
|
const projectRoot = path.join(__dirname, '..');
|
|
12
12
|
|
|
13
13
|
const appDir = path.join(os.homedir(), '.nyxora');
|
|
14
|
-
const pidFile = path.join(appDir, 'daemon.pid');
|
|
15
|
-
const logFile = path.join(appDir, 'gateway.log');
|
|
16
|
-
const tokenFile = path.join(appDir, 'auth.token');
|
|
14
|
+
const pidFile = path.join(appDir, 'run', 'daemon.pid');
|
|
15
|
+
const logFile = path.join(appDir, 'run', 'gateway.log');
|
|
16
|
+
const tokenFile = path.join(appDir, 'auth', 'auth.token');
|
|
17
17
|
|
|
18
18
|
if (!fs.existsSync(appDir)) {
|
|
19
19
|
fs.mkdirSync(appDir, { recursive: true });
|
|
@@ -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
|
package/dist/launcher.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const safeLogger_1 = require("./packages/core/src/utils/safeLogger");
|
|
7
|
+
(0, safeLogger_1.initSafeLogger)();
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const INTERNAL_AUTH_TOKEN = crypto_1.default.randomBytes(64).toString('hex');
|
|
13
|
+
console.log(`[Launcher] Generated Internal Auth Token: ${INTERNAL_AUTH_TOKEN.substring(0, 8)}...`);
|
|
14
|
+
const nyxoraDir = path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.nyxora');
|
|
15
|
+
const authDir = path_1.default.join(nyxoraDir, 'auth');
|
|
16
|
+
if (!fs_1.default.existsSync(authDir))
|
|
17
|
+
fs_1.default.mkdirSync(authDir, { recursive: true, mode: 0o700 });
|
|
18
|
+
const tokenPath = path_1.default.join(authDir, 'runtime.token');
|
|
19
|
+
fs_1.default.writeFileSync(tokenPath, INTERNAL_AUTH_TOKEN, { mode: 0o600 });
|
|
20
|
+
console.log(`[Launcher] Secured runtime token at ${tokenPath} (0600)`);
|
|
21
|
+
const env = {
|
|
22
|
+
...process.env,
|
|
23
|
+
INTERNAL_AUTH_TOKEN,
|
|
24
|
+
SIGNER_SOCKET_PATH: '/tmp/nyxora-signer.sock',
|
|
25
|
+
TS_NODE_CACHE: 'false'
|
|
26
|
+
};
|
|
27
|
+
const spawnService = (name, command, args, env, inheritStdio = false) => {
|
|
28
|
+
let child;
|
|
29
|
+
let crashCount = 0;
|
|
30
|
+
let crashWindowStart = Date.now();
|
|
31
|
+
let isShuttingDown = false;
|
|
32
|
+
const startProcess = () => {
|
|
33
|
+
child = (0, child_process_1.spawn)(command, args, { env, stdio: inheritStdio ? 'inherit' : 'pipe' });
|
|
34
|
+
if (!inheritStdio) {
|
|
35
|
+
child.stdout?.on('data', (data) => process.stdout.write(`[${name}] ${data}`));
|
|
36
|
+
child.stderr?.on('data', (data) => process.stderr.write(`[${name}] ERROR: ${data}`));
|
|
37
|
+
}
|
|
38
|
+
child.on('close', async (code) => {
|
|
39
|
+
console.log(`[${name}] Exited with code ${code}`);
|
|
40
|
+
if (isShuttingDown)
|
|
41
|
+
return;
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
if (now - crashWindowStart > 60000) {
|
|
44
|
+
crashCount = 0;
|
|
45
|
+
crashWindowStart = now;
|
|
46
|
+
}
|
|
47
|
+
crashCount++;
|
|
48
|
+
if (crashCount > 5) {
|
|
49
|
+
console.error(`[Launcher] FATAL: ${name} crashed 5 times in 1 minute. Initiating emergency shutdown.`);
|
|
50
|
+
isShuttingDown = true;
|
|
51
|
+
try {
|
|
52
|
+
const yaml = require('yaml');
|
|
53
|
+
const configPath = path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.nyxora', 'config', 'config.yaml');
|
|
54
|
+
// Fallback check just in case it hasn't migrated yet
|
|
55
|
+
const actualConfigPath = fs_1.default.existsSync(configPath) ? configPath : path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.nyxora', 'config.yaml');
|
|
56
|
+
if (fs_1.default.existsSync(actualConfigPath)) {
|
|
57
|
+
const configStr = fs_1.default.readFileSync(actualConfigPath, 'utf8');
|
|
58
|
+
const config = yaml.parse(configStr);
|
|
59
|
+
const tgToken = config?.telegram?.bot_token;
|
|
60
|
+
const tgChatId = config?.telegram?.admin_chat_id;
|
|
61
|
+
if (tgToken && tgChatId) {
|
|
62
|
+
const alertText = config?.alerts?.emergency_text || "🚨 FATAL ERROR: A critical process has crashed repeatedly. Nyxora is executing an emergency auto-shutdown to protect your system. Please check the server logs.";
|
|
63
|
+
await fetch(`https://api.telegram.org/bot${tgToken}/sendMessage`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: { 'Content-Type': 'application/json' },
|
|
66
|
+
body: JSON.stringify({ chat_id: tgChatId, text: alertText })
|
|
67
|
+
}).catch(() => { });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) { }
|
|
72
|
+
process.exit(1);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`[Launcher] Restarting ${name} in 3 seconds... (Attempt ${crashCount}/5)`);
|
|
76
|
+
setTimeout(startProcess, 3000);
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
startProcess();
|
|
80
|
+
return {
|
|
81
|
+
kill: () => {
|
|
82
|
+
isShuttingDown = true;
|
|
83
|
+
if (child && !child.killed && child.pid) {
|
|
84
|
+
try {
|
|
85
|
+
process.kill(child.pid, 'SIGTERM');
|
|
86
|
+
}
|
|
87
|
+
catch (e) { }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
console.log('[Launcher] Starting Monorepo Services...');
|
|
93
|
+
const socketPath = env.SIGNER_SOCKET_PATH;
|
|
94
|
+
if (fs_1.default.existsSync(socketPath)) {
|
|
95
|
+
console.log(`[Launcher] Removing stale unix socket at ${socketPath}`);
|
|
96
|
+
fs_1.default.unlinkSync(socketPath);
|
|
97
|
+
}
|
|
98
|
+
const children = [];
|
|
99
|
+
const isCompiled = __filename.endsWith('.js');
|
|
100
|
+
const ext = isCompiled ? '.js' : '.ts';
|
|
101
|
+
const cmd = isCompiled ? 'node' : 'npx';
|
|
102
|
+
const baseArgs = isCompiled ? [] : ['ts-node', '-T'];
|
|
103
|
+
const signerPath = path_1.default.join(__dirname, `packages/signer/src/server${ext}`);
|
|
104
|
+
const signer = spawnService('Signer', cmd, [...baseArgs, signerPath], env);
|
|
105
|
+
children.push(signer);
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
const policyPath = path_1.default.join(__dirname, `packages/policy/src/server${ext}`);
|
|
108
|
+
const policy = spawnService('Policy', cmd, [...baseArgs, policyPath], env);
|
|
109
|
+
children.push(policy);
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
const corePath = path_1.default.join(__dirname, `packages/core/src/gateway/cli${ext}`);
|
|
112
|
+
const args = process.argv.slice(2);
|
|
113
|
+
const core = spawnService('Core', cmd, [...baseArgs, corePath, ...args], env, true);
|
|
114
|
+
children.push(core);
|
|
115
|
+
}, 1000);
|
|
116
|
+
}, 1000);
|
|
117
|
+
// Ensure all child processes are killed when launcher exits
|
|
118
|
+
let isCleaningUp = false;
|
|
119
|
+
const cleanup = () => {
|
|
120
|
+
if (isCleaningUp)
|
|
121
|
+
return;
|
|
122
|
+
isCleaningUp = true;
|
|
123
|
+
console.log('\n[Launcher] Shutting down all services...');
|
|
124
|
+
children.forEach(c => c.kill());
|
|
125
|
+
// Give them a moment to cleanup
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
try {
|
|
128
|
+
require('child_process').execSync('pkill -f ts-node');
|
|
129
|
+
}
|
|
130
|
+
catch (e) { }
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}, 1000);
|
|
133
|
+
};
|
|
134
|
+
process.on('SIGINT', cleanup);
|
|
135
|
+
process.on('SIGTERM', cleanup);
|
|
136
|
+
process.on('SIGTERM', cleanup);
|
|
137
|
+
process.on('exit', cleanup);
|
|
@@ -40,7 +40,6 @@ const pluginManager_1 = require("../system/pluginManager");
|
|
|
40
40
|
const paths_1 = require("../config/paths");
|
|
41
41
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
42
42
|
exports.logger = new logger_1.Logger();
|
|
43
|
-
let currentKeyIndex = 0;
|
|
44
43
|
const PROVIDER_CONFIGS = {
|
|
45
44
|
ollama: { baseURL: process.env.OLLAMA_BASE_URL ? `${process.env.OLLAMA_BASE_URL}/v1` : 'http://localhost:11434/v1', requiresApiKey: false },
|
|
46
45
|
gemini: { baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/', requiresApiKey: true },
|
|
@@ -59,27 +58,12 @@ async function getOpenAI() {
|
|
|
59
58
|
let apiKey = 'local';
|
|
60
59
|
if (providerConf.requiresApiKey) {
|
|
61
60
|
apiKey = '';
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
configuredKeys = [configuredKeys];
|
|
65
|
-
}
|
|
66
|
-
if (Array.isArray(configuredKeys) && configuredKeys.length > 0) {
|
|
67
|
-
const keys = configuredKeys.filter(k => typeof k === 'string' && k.trim() !== '');
|
|
68
|
-
if (keys.length > 0) {
|
|
69
|
-
currentKeyIndex = currentKeyIndex % keys.length;
|
|
70
|
-
apiKey = keys[currentKeyIndex];
|
|
71
|
-
console.log(`[LLM] Using rotated API Key (${currentKeyIndex + 1}/${keys.length}): ${apiKey.substring(0, 4)}...`);
|
|
72
|
-
currentKeyIndex++;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
61
|
+
const keyName = `${providerName}_key`;
|
|
62
|
+
apiKey = vaultKeys[keyName] || config.credentials?.[keyName] || '';
|
|
75
63
|
if (!apiKey) {
|
|
76
|
-
|
|
77
|
-
apiKey = vaultKeys[fallbackKeyName] || config.credentials?.[fallbackKeyName] || '';
|
|
78
|
-
if (!apiKey) {
|
|
79
|
-
throw new Error(`No API Key found for ${providerName}. Please run 'nyxora setup' to configure it.`);
|
|
80
|
-
}
|
|
81
|
-
console.log(`[LLM] Using API Key from secure vault`);
|
|
64
|
+
throw new Error(`[Security] No API Key found for ${providerName} in OS Keyring. Please run 'nyxora set-key ${providerName} <key>' or 'nyxora setup'.`);
|
|
82
65
|
}
|
|
66
|
+
console.log(`[LLM] Using API Key securely unlocked from OS Keyring vault.`);
|
|
83
67
|
}
|
|
84
68
|
return new openai_1.OpenAI({
|
|
85
69
|
baseURL: providerConf.baseURL,
|
|
@@ -130,7 +114,7 @@ function getSystemPrompt() {
|
|
|
130
114
|
You are equipped with a native wallet.
|
|
131
115
|
The current real-world date and time is: ${currentDateTime}. Use this for any time-related questions.
|
|
132
116
|
|
|
133
|
-
CRITICAL RULE 1:
|
|
117
|
+
CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
|
|
134
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.
|
|
135
119
|
CRITICAL RULE 3: FORMATTING & CONCISENESS.
|
|
136
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;
|
|
@@ -11,38 +11,15 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
11
11
|
const yaml_1 = __importDefault(require("yaml"));
|
|
12
12
|
const paths_1 = require("./paths");
|
|
13
13
|
async function loadApiKeys() {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const { Entry } = require('@napi-rs/keyring');
|
|
17
|
-
const entry = new Entry('nyxora', 'api_keys');
|
|
18
|
-
const data = await entry.getPassword();
|
|
19
|
-
if (data)
|
|
20
|
-
return JSON.parse(data);
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
if (fs_1.default.existsSync(vaultPath)) {
|
|
24
|
-
try {
|
|
25
|
-
const file = fs_1.default.readFileSync(vaultPath, 'utf8');
|
|
26
|
-
return JSON.parse(file);
|
|
27
|
-
}
|
|
28
|
-
catch (err) { }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {};
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
return config.credentials || {};
|
|
32
16
|
}
|
|
33
17
|
async function saveApiKeys(newKeys) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const { Entry } = require('@napi-rs/keyring');
|
|
40
|
-
const entry = new Entry('nyxora', 'api_keys');
|
|
41
|
-
await entry.setPassword(dataString);
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
fs_1.default.writeFileSync(vaultPath, dataString, { mode: 0o600 });
|
|
45
|
-
}
|
|
18
|
+
const config = loadConfig();
|
|
19
|
+
if (!config.credentials)
|
|
20
|
+
config.credentials = {};
|
|
21
|
+
config.credentials = { ...config.credentials, ...newKeys };
|
|
22
|
+
saveConfig(config);
|
|
46
23
|
}
|
|
47
24
|
function loadConfig() {
|
|
48
25
|
const configPath = (0, paths_1.getPath)('config.yaml');
|
|
@@ -74,21 +51,6 @@ function loadConfig() {
|
|
|
74
51
|
console.error('[Config] Failed to auto-migrate config file', e);
|
|
75
52
|
}
|
|
76
53
|
}
|
|
77
|
-
// Auto-migrate from config.yaml to Keyring/Vault
|
|
78
|
-
if (parsed.credentials && Object.keys(parsed.credentials).length > 0) {
|
|
79
|
-
const credsToMigrate = { ...parsed.credentials };
|
|
80
|
-
saveApiKeys(credsToMigrate).then(() => {
|
|
81
|
-
console.log('[Config] Auto-migrated API keys to secure vault.');
|
|
82
|
-
delete parsed.credentials;
|
|
83
|
-
try {
|
|
84
|
-
const yamlStr = yaml_1.default.stringify(parsed);
|
|
85
|
-
fs_1.default.writeFileSync(configPath, yamlStr, 'utf8');
|
|
86
|
-
}
|
|
87
|
-
catch (e) { }
|
|
88
|
-
}).catch(e => {
|
|
89
|
-
console.error('[Config] Failed to migrate API keys to secure vault', e);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
54
|
return {
|
|
93
55
|
agent: parsed.agent || { name: 'Nyxora-Default', description: 'Your Personal Web3 Assistant.', default_chain: 'base', default_router: 'auto', default_slippage: 0.5 },
|
|
94
56
|
llm: parsed.llm || {
|
|
@@ -30,6 +30,44 @@ function getAppDir() {
|
|
|
30
30
|
}
|
|
31
31
|
return process.cwd();
|
|
32
32
|
}
|
|
33
|
+
function ensureDir(dir) {
|
|
34
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
35
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
33
38
|
function getPath(filename) {
|
|
34
|
-
|
|
39
|
+
const baseDir = getAppDir();
|
|
40
|
+
// Determine subdirectory based on filename
|
|
41
|
+
let subDir = '';
|
|
42
|
+
const lowerFile = filename.toLowerCase();
|
|
43
|
+
if (lowerFile.endsWith('.db') || lowerFile.endsWith('.db-wal') || lowerFile.endsWith('.db-shm') || lowerFile.endsWith('.json') && lowerFile.includes('memory') || lowerFile.endsWith('.md') || lowerFile.includes('orders')) {
|
|
44
|
+
subDir = 'data';
|
|
45
|
+
}
|
|
46
|
+
else if (lowerFile.endsWith('.yaml') || lowerFile.includes('config') || lowerFile.includes('skills') || lowerFile.includes('whitelist') || lowerFile.includes('tokens')) {
|
|
47
|
+
subDir = 'config';
|
|
48
|
+
}
|
|
49
|
+
else if (lowerFile.endsWith('.token') || lowerFile.includes('vault') || lowerFile.includes('credentials')) {
|
|
50
|
+
subDir = 'auth';
|
|
51
|
+
}
|
|
52
|
+
else if (lowerFile.endsWith('.log') || lowerFile.includes('pid')) {
|
|
53
|
+
subDir = 'run';
|
|
54
|
+
}
|
|
55
|
+
const targetDir = path_1.default.join(baseDir, subDir);
|
|
56
|
+
ensureDir(targetDir);
|
|
57
|
+
const fullPath = path_1.default.join(targetDir, filename);
|
|
58
|
+
// AUTO-MIGRATION: If file exists in root but not in subdir, move it
|
|
59
|
+
const oldRootPath = path_1.default.join(baseDir, filename);
|
|
60
|
+
if (subDir !== '') {
|
|
61
|
+
if (fs_1.default.existsSync(oldRootPath) && !fs_1.default.existsSync(fullPath)) {
|
|
62
|
+
try {
|
|
63
|
+
fs_1.default.renameSync(oldRootPath, fullPath);
|
|
64
|
+
console.log(`[Migration] Moved ${filename} to ${subDir}/ directory.`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.warn(`[Migration] Failed to move ${filename} to ${subDir}/`, err);
|
|
68
|
+
return oldRootPath; // fallback to root if migration fails
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return fullPath;
|
|
35
73
|
}
|
|
@@ -106,7 +106,7 @@ async function main() {
|
|
|
106
106
|
};
|
|
107
107
|
const mappedKey = keyMap[provider.toLowerCase()] || `${provider.toLowerCase()}_key`;
|
|
108
108
|
await (0, parser_1.saveApiKeys)({ [mappedKey]: key });
|
|
109
|
-
console.log(picocolors_1.default.green(`✅ API Key for ${provider} saved
|
|
109
|
+
console.log(picocolors_1.default.green(`✅ API Key for ${provider} saved successfully.`));
|
|
110
110
|
process.exit(0);
|
|
111
111
|
}
|
|
112
112
|
// Check for wallet command
|
|
@@ -146,9 +146,9 @@ async function main() {
|
|
|
146
146
|
// 2. Setup boilerplate files if in global mode and they don't exist
|
|
147
147
|
let isFirstBoot = false;
|
|
148
148
|
if (isGlobalMode) {
|
|
149
|
-
const globalConfigPath =
|
|
150
|
-
const globalUserMdPath =
|
|
151
|
-
const globalIdentityMdPath =
|
|
149
|
+
const globalConfigPath = (0, paths_1.getPath)('config.yaml');
|
|
150
|
+
const globalUserMdPath = (0, paths_1.getPath)('user.md');
|
|
151
|
+
const globalIdentityMdPath = (0, paths_1.getPath)('IDENTITY.md');
|
|
152
152
|
// Copy default config.yaml
|
|
153
153
|
if (!fs_1.default.existsSync(globalConfigPath)) {
|
|
154
154
|
isFirstBoot = true;
|
|
@@ -157,7 +157,7 @@ async function main() {
|
|
|
157
157
|
fs_1.default.copyFileSync(exampleConfigPath, globalConfigPath);
|
|
158
158
|
}
|
|
159
159
|
else {
|
|
160
|
-
fs_1.default.writeFileSync(globalConfigPath, 'agent:\n name: Nyxora-Agent\n default_chain: base\nllm:\n provider: openai\n model: gpt-
|
|
160
|
+
fs_1.default.writeFileSync(globalConfigPath, 'agent:\n name: Nyxora-Agent\n default_chain: base\nllm:\n provider: openai\n model: gpt-4\n temperature: 0.7\nmemory:\n type: file\n path: memory.json\n');
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
if (!fs_1.default.existsSync(globalUserMdPath)) {
|