nyxora 1.6.2 → 1.6.4

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +22 -12
  3. package/SECURITY.md +25 -21
  4. package/assets/raw-diagram.png +0 -0
  5. package/assets/security-flow.png +0 -0
  6. package/bin/nyxora.mjs +236 -0
  7. package/launcher.js +8 -3
  8. package/launcher.ts +28 -1
  9. package/package.json +11 -8
  10. package/packages/core/package.json +4 -4
  11. package/packages/core/src/agent/reasoning.ts +10 -8
  12. package/packages/core/src/config/parser.ts +2 -1
  13. package/packages/core/src/gateway/cli.ts +2 -64
  14. package/packages/core/src/gateway/server.ts +89 -8
  15. package/packages/core/src/gateway/setup-cli.ts +7 -0
  16. package/packages/core/src/gateway/setup.ts +52 -28
  17. package/packages/core/src/gateway/telegram.ts +147 -89
  18. package/packages/core/src/memory/logger.ts +83 -20
  19. package/packages/core/src/system/pluginManager.ts +48 -34
  20. package/packages/core/src/utils/state.ts +15 -2
  21. package/packages/core/src/web3/config.ts +18 -3
  22. package/packages/core/src/web3/skills/marketAnalysis.ts +43 -17
  23. package/packages/core/src/web3/skills/swapToken.ts +9 -1
  24. package/packages/dashboard/dist/assets/index-CfIids2e.js +170 -0
  25. package/packages/dashboard/dist/assets/index-POJM-7Fd.css +1 -0
  26. package/packages/dashboard/dist/favicon.svg +1 -1
  27. package/packages/dashboard/dist/index.html +2 -2
  28. package/packages/dashboard/package.json +7 -7
  29. package/packages/dashboard/public/favicon.svg +1 -1
  30. package/packages/dashboard/src/App.tsx +224 -167
  31. package/packages/dashboard/src/Settings.tsx +55 -0
  32. package/packages/dashboard/src/Skills.tsx +8 -1
  33. package/packages/dashboard/src/index.css +146 -35
  34. package/packages/policy/package.json +1 -1
  35. package/packages/policy/src/server.ts +21 -28
  36. package/packages/signer/package.json +1 -1
  37. package/packages/signer/src/server.ts +40 -13
  38. package/test-db.ts +3 -0
  39. package/bin/nyxora.js +0 -13
  40. package/packages/dashboard/dist/assets/index-BK4qmIy6.js +0 -200
  41. package/packages/dashboard/dist/assets/index-C1m4ohce.css +0 -1
  42. package/packages/dashboard/package-lock.json +0 -2748
  43. package/packages/dashboard/src/Memory.tsx +0 -110
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.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
+ ## [1.6.4]
9
+
10
+ ### Added
11
+ - **Node.js Native Database Engine**: Migrated the core `logger.ts` memory subsystem to use the built-in `node:sqlite` engine (Node 22+), maintaining ultra-fast 100% synchronous operations while dramatically reducing dependency bloat.
12
+ - **Next-Gen OS Keyring (N-API)**: Migrated `keytar` to `@napi-rs/keyring`. This replaces legacy C++ bindings with modern Rust/NAPI-RS, retaining identical OS-level security (Mac Keychain, Windows Credential Manager) while eliminating the `prebuild-install` deprecation warning and streamlining global installation.
13
+
14
+ ### Removed
15
+ - `better-sqlite3` and `keytar` dependencies entirely removed from the monorepo architecture.
16
+
17
+ ## [1.6.3]
18
+
19
+ ### Added
20
+ - Implemented **Zero-Click Multi-Session** for instantaneous chat creation and switching.
21
+ - Introduced **Smart Auto-Naming** for automatic contextual session titles.
22
+
23
+ ### Changed
24
+ - **Redesigned Sidebar Architecture**: enhanced utility-centric design, significantly reducing gaps for a compact, elegant look.
25
+ - Integrated **OS-Native Keyring**, replacing legacy AES-256-GCM and Master Password mechanics.
26
+ - Updated and cleaned up legacy cryptography references in VitePress guides and README.
27
+
28
+ ### Fixed
29
+ - Resolved deeply-nested monorepo CI/CD deployment failures by isolating `package-lock.json` and mitigating peer-dependency conflicts.
30
+
8
31
  ## [1.4.5]
9
32
 
10
33
  ### Fixed
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Nyxora Agent 🤖
2
2
  **Production-Grade Secure AI Execution Framework for Web3 Agents.**
3
3
 
4
- [![Version](https://img.shields.io/badge/version-1.6.2-blue.svg)](https://github.com/perasyudha/Nyxora)
4
+ [![Version](https://img.shields.io/badge/version-1.6.3-blue.svg)](https://github.com/perasyudha/Nyxora)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Security: Production-Grade](https://img.shields.io/badge/Security-Production--Grade-blue.svg)](#️-advanced-security-threat-model)
7
7
  [![Execution: Cryptographic Approval](https://img.shields.io/badge/Execution-Cryptographic--Approval-orange.svg)](#️-advanced-security-threat-model)
8
8
  [![Privacy: Local-Only Keys](https://img.shields.io/badge/Privacy-Local--Only--Keys-success.svg)](#️-advanced-security-threat-model)
9
9
 
10
- Nyxora (v1.6.2) 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 Glassmorphism UI dashboard and strict client-side key isolation.
10
+ Nyxora (v1.6.3) 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 Glassmorphism UI dashboard and strict client-side key isolation.
11
11
 
12
12
  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.
13
13
 
@@ -15,20 +15,23 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
15
15
 
16
16
  ## 🔥 Key Features
17
17
 
18
- ### Advanced Security Architecture (v1.6.2)
18
+ ### Advanced Security Architecture (v1.6.3)
19
19
  * **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).
20
20
  * **Cryptographically Bound Approval**: Policy changes and transactions requested by the AI are drafted as hashes (`sha256`). Approval via the UI requires a challenge nonce, preventing Man-in-the-Middle (MITM) attacks.
21
21
  * **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.
22
+ * **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.
22
23
 
23
24
  ### Core Operations & Web3 Execution
24
25
  * **System Automation & Full OS Access**: Instruct the agent to read/write local files, run terminal commands, and browse the web natively.
25
26
  * **Anti-Rugpull & Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
26
27
  * **Automated Limit Orders**: Set natural language rules (e.g., "Sell my PEPE if price drops below $0.001"). Nyxora runs a background cron monitor and executes the swap while you sleep (Auto-Approve Bypass configured safely).
28
+ * **Cross-Chain Hybrid Market Scanner**: Real-time asset tracking combining CoinGecko global data with DexScreener on-chain metrics across Ethereum, Base, Solana, BSC, and more.
27
29
  * **PNL & Portfolio Tracking**: The AI scans your wallets and multiplies balances by live DEX prices to give you real-time Net Worth estimations.
28
30
 
29
31
  ### AI & UI Customization
30
- * **Multi-LLM Support**: Seamlessly switch between Google Gemini, OpenAI, OpenRouter, or local Ollama models.
31
- * **Premium Glassmorphism UI**: A gorgeous, resizable split-pane interface with Pseudo-Generative UI widgets (`<BalanceWidget>`, `<MarketWidget>`, `<SwapWidget>`).
32
+ * **Zero-Click Multi-Session**: Instantly create isolated chat sessions with smart auto-naming triggered by your first prompt, exactly like ChatGPT.
33
+ * **Dynamic Trending Tokens**: Live top 5 crypto assets feed directly injected into the dashboard, completely clickable for instant AI market analysis.
34
+ * **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>`).
32
35
  * **Deep Personalization**: Feed the agent custom rules via `user.md` and define its core persona via `IDENTITY.md`.
33
36
 
34
37
  ---
@@ -42,7 +45,7 @@ The following diagram illustrates Nyxora's **3-Tier Monorepo Architecture**, sho
42
45
  *Nyxora separates its duties into 3 independent layers for absolute security:*
43
46
  1. **🧠 Core (The AI Brain)**: The intelligent assistant that strategizes and plans transactions, but **never** holds your funds.
44
47
  2. **🛡️ Policy Engine (The Guard)**: The security guard that verifies the Brain's plans. If the AI attempts to send funds exceeding your set limits, this engine automatically blocks it.
45
- 3. **🔒 Signer Vault (The Safe)**: The offline vault where your Private Keys are securely locked. It only signs transactions after they pass all rigorous security checks.
48
+ 3. **🔒 Signer Vault (The Safe)**: The offline vault where your Private Keys are securely locked natively in your OS Keyring (GNOME Keyring / macOS Keychain / Windows Credential Manager). It only signs transactions after they pass all rigorous security checks.
46
49
 
47
50
  *(Note: Despite the multi-layered security process appearing lengthy, the internal system validation and cryptographic signing occurs in **milliseconds**, ensuring zero latency bottlenecks).*
48
51
 
@@ -63,12 +66,16 @@ The easiest and fastest way to use Nyxora is to install it globally via NPM. Thi
63
66
  # 1. Install Nyxora globally
64
67
  npm install -g nyxora@latest
65
68
 
66
- # 2. Run the Interactive Setup Wizard (API Keys, Wallet, Model Selection)
69
+ # 2. Run the Interactive Setup Wizard (API Keys, Wallet, Telegram)
67
70
  nyxora setup
68
71
 
69
- # 3. Start the Nyxora Orchestrator and Dashboard
70
- nyxora
72
+ # 3. Start the Nyxora background daemon
73
+ nyxora start
74
+
75
+ # 4. Open the Web Dashboard
76
+ nyxora dashboard
71
77
  ```
78
+ > **⚠️ 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.
72
79
 
73
80
  ### Local Development (From Source)
74
81
  If you wish to modify the code or run from source, you can use the Monorepo architecture.
@@ -83,13 +90,16 @@ npm install
83
90
  # 2. Build the Dashboard UI
84
91
  npm run build
85
92
 
86
- # 3. Interactive Setup Wizard (API Keys, Wallet, Model Selection)
93
+ # 3. Interactive Setup Wizard (API Keys, Wallet, Telegram)
87
94
  npm run setup
88
95
 
89
- # 4. Start the Nyxora Orchestrator
96
+ # 4. Start the Nyxora background daemon
90
97
  npm start
98
+
99
+ # 5. Open the Web Dashboard
100
+ npm run dashboard
91
101
  ```
92
- *`npm start` will automatically boot the Core, Policy Engine, Signer Vault, and Local Dashboard UI.*
102
+ > **⚠️ IMPORTANT:** Whenever you re-run `npm run setup` or manually edit the config files, you **must restart the daemon** by running `npm run restart` for the changes to take effect.
93
103
 
94
104
  ---
95
105
 
package/SECURITY.md CHANGED
@@ -19,21 +19,7 @@ To achieve this, Nyxora uses a **3-Tier Monorepo IPC (Inter-Process Communicatio
19
19
  ### The Security Flow
20
20
  When the LLM processes a transaction instruction (e.g., swapping tokens), the lifecycle is as follows:
21
21
 
22
- ```text
23
- [1] User (Dashboard/Telegram) ──> Sends prompt "Please swap ETH to USDC"
24
-
25
- [2] Core Runtime (LLM) <── Understands context & generates JSON Tool Call
26
-
27
- [3] Policy Engine <── Receives payload, evaluates rules & limits
28
-
29
- [4] User (Dashboard/Telegram) <── (If Auth required) Requests Approval (Challenge Nonce)
30
-
31
- [5] Signer Vault <── Receives certified instruction from Policy
32
-
33
- [6] Blockchain RPC <── Signer Vault signs & broadcasts to RPC
34
-
35
- [7] User (Dashboard/Telegram) <── Success status returned to chat interface
36
- ```
22
+ ![Nyxora Security Flow](https://raw.githubusercontent.com/perasyudha/Nyxora/main/assets/security-flow.png)
37
23
 
38
24
  The diagram above illustrates the lifecycle of a transaction initiated from the user interface. Due to Nyxora's layered architecture, the LLM in the Core Runtime acts solely as a planner generating transaction data structures. The actual cryptographic execution and signing are strictly locked and fully controlled by the Policy Engine and Signer Vault after you provide authorization.
39
25
 
@@ -41,7 +27,22 @@ The diagram above illustrates the lifecycle of a transaction initiated from the
41
27
 
42
28
  ---
43
29
 
44
- ## 2. Advanced Cryptographic Security
30
+ ## 2. OS-Native Keyring Integration
31
+
32
+ Nyxora completely eliminates the need for manual "Master Passwords" or custom AES-GCM keystore files by delegating Private Key encryption directly to your Operating System's trusted Keyring.
33
+
34
+ * **Linux:** Integrates seamlessly with `Secret Service API / GNOME Keyring` via `libsecret`.
35
+ * **macOS:** Utilizes the native `Keychain Access`.
36
+ * **Windows:** Uses `Windows Credential Manager`.
37
+
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
+
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 `.env` / `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
+ ---
44
+
45
+ ## 3. Advanced Cryptographic Security
45
46
 
46
47
  To prevent advanced Man-in-the-Middle (MITM) attacks and UI compromises, Nyxora is adopting the following cryptographic standards:
47
48
 
@@ -68,13 +69,16 @@ Every approval UI prompt utilizes a **Single-Use Challenge Nonce** with a strict
68
69
 
69
70
  ---
70
71
 
71
- ## 3. Plugin Sandboxing
72
+ ## 4. Plugin Sandboxing (Node.js VM Isolation)
73
+
74
+ Community plugins and custom skills are NEVER executed directly at the OS level. Instead, Nyxora creates an airtight **Virtual Machine (VM) Sandbox** in memory.
72
75
 
73
- Community plugins and custom skills are executed inside a sandboxed environment.
74
- * **Restricted FS Access:** Plugins cannot arbitrarily read your `~/.nyxora` keystore directory.
75
- * **Restricted Shell Exec:** Arbitrary shell commands are disabled for third-party skills to prevent malicious `curl | bash` supply chain payloads.
76
+ To prevent Supply Chain Attacks, the sandbox **permanently blacklists** critical native modules:
77
+ * `fs` (File System): Plugins cannot read or steal local keystore files.
78
+ * `child_process`: Plugins cannot spawn silent background terminals or malicious `curl | bash` supply chain payloads.
79
+ * `os`, `net`, `cluster`: Blocked to prevent network-level exploitation.
76
80
 
77
- ## 4. Reporting Vulnerabilities
81
+ ## 5. Reporting Vulnerabilities
78
82
 
79
83
  If you discover a vulnerability in the Nyxora architecture, please DO NOT open a public issue.
80
84
  Instead, email the core maintainer directly at **ainyxor@gmail.com**.
Binary file
Binary file
package/bin/nyxora.mjs ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import os from 'os';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const projectRoot = path.join(__dirname, '..');
12
+
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');
17
+
18
+ if (!fs.existsSync(appDir)) {
19
+ fs.mkdirSync(appDir, { recursive: true });
20
+ }
21
+
22
+ const command = process.argv[2];
23
+
24
+ function isDaemonRunning(pidStr) {
25
+ if (!pidStr) return false;
26
+ try {
27
+ process.kill(parseInt(pidStr, 10), 0);
28
+ return true;
29
+ } catch (e) {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ async function getDaemonPid() {
35
+ if (fs.existsSync(pidFile)) {
36
+ const pidStr = fs.readFileSync(pidFile, 'utf8').trim();
37
+ if (isDaemonRunning(pidStr)) {
38
+ return parseInt(pidStr, 10);
39
+ } else {
40
+ fs.unlinkSync(pidFile);
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+
46
+ async function start() {
47
+ const pid = await getDaemonPid();
48
+ if (pid) {
49
+ console.log(`Nyxora is already running (PID: ${pid}). Use 'nyxora restart' to restart.`);
50
+ return;
51
+ }
52
+
53
+ console.log('Starting Nyxora daemon...');
54
+
55
+ const out = fs.openSync(logFile, 'a');
56
+ const err = fs.openSync(logFile, 'a');
57
+
58
+ // Clean up any stale sockets first
59
+ const socketPath = '/tmp/nyxora-signer.sock';
60
+ if (fs.existsSync(socketPath)) {
61
+ try {
62
+ fs.unlinkSync(socketPath);
63
+ } catch(e) {}
64
+ }
65
+
66
+ const child = spawn('npx', ['ts-node', '-T', 'launcher.ts'], {
67
+ cwd: projectRoot,
68
+ detached: true,
69
+ stdio: ['ignore', out, err],
70
+ env: { ...process.env, TS_NODE_CACHE: 'false' }
71
+ });
72
+
73
+ child.unref();
74
+
75
+ if (child.pid) {
76
+ fs.writeFileSync(pidFile, child.pid.toString());
77
+ console.log(`Nyxora daemon started (PID: ${child.pid}).`);
78
+ console.log(`Logs are available at: ${logFile}`);
79
+ }
80
+ }
81
+
82
+ async function stop() {
83
+ const pid = await getDaemonPid();
84
+ if (pid) {
85
+ console.log(`Stopping Nyxora daemon (PID: ${pid})...`);
86
+ try {
87
+ process.kill(-pid, 'SIGTERM');
88
+ console.log('Nyxora stopped gracefully.');
89
+ } catch (e) {
90
+ console.error('Failed to kill process:', e.message);
91
+ }
92
+ try {
93
+ fs.unlinkSync(pidFile);
94
+ } catch(e) {}
95
+ } else {
96
+ console.log('Nyxora is not running.');
97
+ }
98
+ }
99
+
100
+ async function restart() {
101
+ await stop();
102
+ setTimeout(start, 1000);
103
+ }
104
+
105
+ async function dashboard() {
106
+ const pid = await getDaemonPid();
107
+ if (!pid) {
108
+ console.log("Nyxora is not running. Start it first with 'nyxora start'.");
109
+ return;
110
+ }
111
+
112
+ if (fs.existsSync(tokenFile)) {
113
+ const token = fs.readFileSync(tokenFile, 'utf8').trim();
114
+ const url = `http://localhost:3000?token=${token}`;
115
+ console.log(`Opening Dashboard at ${url}`);
116
+ try {
117
+ const { default: open } = await import('open');
118
+ await open(url);
119
+ } catch(e) {
120
+ console.error("Failed to open browser. Please manually open:", url);
121
+ }
122
+ } else {
123
+ console.log("Dashboard token not found. The daemon might still be starting.");
124
+ }
125
+ }
126
+
127
+ async function cleanLogs() {
128
+ if (fs.existsSync(logFile)) {
129
+ fs.writeFileSync(logFile, '');
130
+ console.log('Logs have been cleared.');
131
+ } else {
132
+ console.log('No logs found to clear.');
133
+ }
134
+ }
135
+
136
+ async function autostart(action) {
137
+ const isLinux = process.platform === 'linux';
138
+ const isMac = process.platform === 'darwin';
139
+ const isWin = process.platform === 'win32';
140
+
141
+ if (isLinux) {
142
+ const autostartDir = path.join(os.homedir(), '.config', 'autostart');
143
+ const desktopFile = path.join(autostartDir, 'nyxora.desktop');
144
+
145
+ if (action === 'enable') {
146
+ if (!fs.existsSync(autostartDir)) fs.mkdirSync(autostartDir, { recursive: true });
147
+ const binPath = path.resolve(projectRoot, 'bin', 'nyxora.mjs');
148
+ const desktopContent = `[Desktop Entry]
149
+ Type=Application
150
+ Exec=node ${binPath} start
151
+ Hidden=false
152
+ NoDisplay=false
153
+ X-GNOME-Autostart-enabled=true
154
+ Name=Nyxora Daemon
155
+ Comment=Start Nyxora in the background
156
+ `;
157
+ fs.writeFileSync(desktopFile, desktopContent);
158
+ console.log('Autostart enabled for Linux (XDG Autostart).');
159
+ } else if (action === 'disable') {
160
+ if (fs.existsSync(desktopFile)) fs.unlinkSync(desktopFile);
161
+ console.log('Autostart disabled.');
162
+ } else {
163
+ console.log("Usage: nyxora autostart [enable|disable]");
164
+ }
165
+ } else if (isMac) {
166
+ const plistDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
167
+ const plistFile = path.join(plistDir, 'com.nyxora.gateway.plist');
168
+ if (action === 'enable') {
169
+ if (!fs.existsSync(plistDir)) fs.mkdirSync(plistDir, { recursive: true });
170
+ const binPath = path.resolve(projectRoot, 'bin', 'nyxora.mjs');
171
+ const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
172
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
173
+ <plist version="1.0">
174
+ <dict>
175
+ <key>Label</key>
176
+ <string>com.nyxora.gateway</string>
177
+ <key>ProgramArguments</key>
178
+ <array>
179
+ <string>${process.execPath}</string>
180
+ <string>${binPath}</string>
181
+ <string>start</string>
182
+ </array>
183
+ <key>RunAtLoad</key>
184
+ <true/>
185
+ </dict>
186
+ </plist>`;
187
+ fs.writeFileSync(plistFile, plistContent);
188
+ console.log('Autostart enabled for macOS (LaunchAgent).');
189
+ } else if (action === 'disable') {
190
+ if (fs.existsSync(plistFile)) fs.unlinkSync(plistFile);
191
+ console.log('Autostart disabled.');
192
+ }
193
+ } else if (isWin) {
194
+ console.log("For Windows, please use Task Scheduler or add a shortcut to the Startup folder.");
195
+ } else {
196
+ console.log("Unsupported OS for autostart script.");
197
+ }
198
+ }
199
+
200
+ async function setup() {
201
+ console.log("Running Nyxora Setup Wizard...");
202
+ const child = spawn('npx', ['ts-node', '-T', 'packages/core/src/gateway/setup-cli.ts'], {
203
+ cwd: projectRoot,
204
+ stdio: 'inherit',
205
+ env: { ...process.env, TS_NODE_CACHE: 'false' }
206
+ });
207
+
208
+ await new Promise(resolve => child.on('close', resolve));
209
+ }
210
+
211
+ async function main() {
212
+ switch (command) {
213
+ case 'setup': await setup(); break;
214
+ case 'start': await start(); break;
215
+ case 'stop': await stop(); break;
216
+ case 'restart': await restart(); break;
217
+ case 'dashboard': await dashboard(); break;
218
+ case 'clean-logs': await cleanLogs(); break;
219
+ case 'autostart': await autostart(process.argv[3]); break;
220
+ default:
221
+ console.log(`
222
+ Nyxora CLI Manager
223
+ Usage: nyxora <command>
224
+
225
+ Commands:
226
+ start Start the Nyxora background daemon
227
+ stop Stop the running daemon
228
+ restart Restart the daemon
229
+ dashboard Open the dashboard in your browser
230
+ clean-logs Clear the daemon logs
231
+ autostart Enable/disable autostart on boot (usage: nyxora autostart enable)
232
+ `);
233
+ }
234
+ }
235
+
236
+ main().catch(console.error);
package/launcher.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const child_process_1 = require("child_process");
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
9
10
  const INTERNAL_AUTH_TOKEN = crypto_1.default.randomBytes(64).toString('hex');
10
11
  console.log(`[Launcher] Generated Internal Auth Token: ${INTERNAL_AUTH_TOKEN.substring(0, 8)}...`);
11
12
  const env = {
@@ -34,10 +35,14 @@ if (fs_1.default.existsSync(socketPath)) {
34
35
  console.log(`[Launcher] Removing stale unix socket at ${socketPath}`);
35
36
  fs_1.default.unlinkSync(socketPath);
36
37
  }
37
- const signer = spawnService('Signer', 'npx', ['ts-node', '-T', 'packages/signer/src/server.ts'], env);
38
+ const signerPath = path_1.default.join(__dirname, 'packages/signer/src/server.ts');
39
+ const signer = spawnService('Signer', 'npx', ['ts-node', '-T', signerPath], env);
38
40
  setTimeout(() => {
39
- const policy = spawnService('Policy', 'npx', ['ts-node', '-T', 'packages/policy/src/server.ts'], env);
41
+ const policyPath = path_1.default.join(__dirname, 'packages/policy/src/server.ts');
42
+ const policy = spawnService('Policy', 'npx', ['ts-node', '-T', policyPath], env);
40
43
  setTimeout(() => {
41
- const core = spawnService('Core', 'npx', ['ts-node', '-T', 'packages/core/src/gateway/cli.ts'], env, true);
44
+ const corePath = path_1.default.join(__dirname, 'packages/core/src/gateway/cli.ts');
45
+ const args = process.argv.slice(2);
46
+ const core = spawnService('Core', 'npx', ['ts-node', '-T', corePath, ...args], env, true);
42
47
  }, 1000);
43
48
  }, 1000);
package/launcher.ts CHANGED
@@ -9,7 +9,8 @@ console.log(`[Launcher] Generated Internal Auth Token: ${INTERNAL_AUTH_TOKEN.sub
9
9
  const env = {
10
10
  ...process.env,
11
11
  INTERNAL_AUTH_TOKEN,
12
- SIGNER_SOCKET_PATH: '/tmp/nyxora-signer.sock'
12
+ SIGNER_SOCKET_PATH: '/tmp/nyxora-signer.sock',
13
+ TS_NODE_CACHE: 'false'
13
14
  };
14
15
 
15
16
  const spawnService = (name: string, command: string, args: string[], env: any, inheritStdio: boolean = false) => {
@@ -40,16 +41,42 @@ if (fs.existsSync(socketPath)) {
40
41
  fs.unlinkSync(socketPath);
41
42
  }
42
43
 
44
+ const children: ReturnType<typeof spawn>[] = [];
45
+
43
46
  const signerPath = path.join(__dirname, 'packages/signer/src/server.ts');
44
47
  const signer = spawnService('Signer', 'npx', ['ts-node', '-T', signerPath], env);
48
+ children.push(signer);
45
49
 
46
50
  setTimeout(() => {
47
51
  const policyPath = path.join(__dirname, 'packages/policy/src/server.ts');
48
52
  const policy = spawnService('Policy', 'npx', ['ts-node', '-T', policyPath], env);
53
+ children.push(policy);
49
54
 
50
55
  setTimeout(() => {
51
56
  const corePath = path.join(__dirname, 'packages/core/src/gateway/cli.ts');
52
57
  const args = process.argv.slice(2);
53
58
  const core = spawnService('Core', 'npx', ['ts-node', '-T', corePath, ...args], env, true);
59
+ children.push(core);
54
60
  }, 1000);
55
61
  }, 1000);
62
+
63
+ // Ensure all child processes are killed when launcher exits
64
+ const cleanup = () => {
65
+ console.log('\n[Launcher] Shutting down all services...');
66
+ children.forEach(child => {
67
+ if (!child.killed && child.pid) {
68
+ try {
69
+ process.kill(child.pid, 'SIGKILL');
70
+ } catch (e) {}
71
+ }
72
+ });
73
+ // Kill any stray ts-node processes left behind by npx
74
+ try {
75
+ require('child_process').execSync('pkill -f ts-node');
76
+ } catch (e) {}
77
+ process.exit(0);
78
+ };
79
+
80
+ process.on('SIGINT', cleanup);
81
+ process.on('SIGTERM', cleanup);
82
+ process.on('exit', cleanup);
package/package.json CHANGED
@@ -1,18 +1,24 @@
1
1
  {
2
2
  "name": "nyxora",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
+ "license": "MIT",
4
5
  "workspaces": [
5
6
  "packages/*"
6
7
  ],
8
+ "bin": {
9
+ "nyxora": "bin/nyxora.mjs"
10
+ },
7
11
  "scripts": {
8
- "start": "ts-node -T launcher.ts",
12
+ "start": "node ./bin/nyxora.mjs start",
13
+ "stop": "node ./bin/nyxora.mjs stop",
14
+ "restart": "node ./bin/nyxora.mjs restart",
15
+ "dashboard": "node ./bin/nyxora.mjs dashboard",
16
+ "setup": "node ./bin/nyxora.mjs setup",
9
17
  "build": "npm run build --workspace=dashboard",
10
- "setup": "ts-node -T packages/core/src/gateway/cli.ts setup",
11
18
  "test": "echo \"Error: no test specified\" && exit 1"
12
19
  },
13
20
  "dependencies": {
14
21
  "@clack/prompts": "^1.4.0",
15
- "better-sqlite3": "^12.10.0",
16
22
  "concurrently": "^9.2.1",
17
23
  "cors": "^2.8.6",
18
24
  "dotenv": "^17.4.2",
@@ -20,7 +26,7 @@
20
26
  "express-rate-limit": "^7.5.0",
21
27
  "helmet": "^8.0.0",
22
28
  "jsonwebtoken": "^9.0.2",
23
- "node-telegram-bot-api": "^0.67.0",
29
+ "@napi-rs/keyring": "^1.3.0",
24
30
  "open": "^11.0.0",
25
31
  "openai": "^6.39.0",
26
32
  "picocolors": "^1.1.1",
@@ -34,8 +40,5 @@
34
40
  "@types/jsonwebtoken": "^9.0.5",
35
41
  "@types/node": "^25.9.1",
36
42
  "vitepress": "^1.6.4"
37
- },
38
- "bin": {
39
- "nyxora": "./bin/nyxora.js"
40
43
  }
41
44
  }
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@nyxora/core",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "private": true,
5
5
  "main": "src/gateway/server.ts",
6
6
  "dependencies": {
7
7
  "@clack/prompts": "^1.4.0",
8
- "better-sqlite3": "^12.10.0",
9
8
  "cors": "^2.8.6",
10
9
  "express": "^5.2.1",
11
- "helmet": "^8.0.0",
12
10
  "express-rate-limit": "^7.5.0",
13
- "node-telegram-bot-api": "^0.67.0",
11
+ "helmet": "^8.0.0",
12
+ "isolated-vm": "^6.1.2",
14
13
  "open": "^11.0.0",
15
14
  "openai": "^6.39.0",
15
+ "telegraf": "^4.16.3",
16
16
  "yaml": "^2.9.0"
17
17
  }
18
18
  }
@@ -181,12 +181,12 @@ If the user doesn't specify a chain, default to: ${config.agent.default_chain}.`
181
181
  return basePrompt;
182
182
  }
183
183
 
184
- export async function processUserInput(input: string, role: 'user' | 'system' = 'user', onProgress?: (msg: string) => void): Promise<string> {
184
+ export async function processUserInput(input: string, role: 'user' | 'system' = 'user', onProgress?: (msg: string) => void, sessionId?: string): Promise<string> {
185
185
  const config = loadConfig();
186
186
  // Add input to memory
187
- logger.addEntry({ role, content: input });
187
+ logger.addEntry({ role, content: input }, sessionId);
188
188
 
189
- const history = logger.getHistory();
189
+ const history = logger.getHistory(sessionId);
190
190
 
191
191
  // Format messages for OpenAI
192
192
  const messages: any[] = [
@@ -258,7 +258,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
258
258
  role: 'assistant',
259
259
  content: responseMessage.content || "",
260
260
  tool_calls: responseMessage.tool_calls,
261
- });
261
+ }, sessionId);
262
262
 
263
263
  // Check if the model wants to call a tool
264
264
  if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
@@ -422,13 +422,13 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
422
422
  tool_call_id: toolCall.id,
423
423
  name: toolName,
424
424
  content: result,
425
- });
425
+ }, sessionId);
426
426
  }
427
427
 
428
428
  // Second call to get the final answer after tool execution
429
429
  const secondMessages = [
430
430
  { role: 'system', content: getSystemPrompt() },
431
- ...logger.getHistory()
431
+ ...logger.getHistory(sessionId)
432
432
  .filter(m => !(m.role === 'tool' && !m.tool_call_id))
433
433
  .map(m => {
434
434
  let role = m.role;
@@ -454,13 +454,15 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
454
454
  Tracker.addEvent('llm.final_response', { provider: config.llm.provider });
455
455
 
456
456
  const finalContent = secondResponse.choices[0].message.content || "";
457
- logger.addEntry({ role: 'assistant', content: finalContent });
457
+ logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
458
458
  return finalContent;
459
459
  }
460
460
 
461
461
  return responseMessage.content || "No response generated.";
462
462
  } catch (error: any) {
463
463
  console.error("LLM Error:", error);
464
- return `Error connecting to AI Provider: ${error.message}`;
464
+ const errorMsg = '⚠️ All models are temporarily rate-limited. Please try again in a few minutes.';
465
+ logger.addEntry({ role: 'assistant', content: errorMsg }, sessionId);
466
+ return errorMsg;
465
467
  }
466
468
  }
@@ -24,12 +24,13 @@ export interface NyxoraConfig {
24
24
  path: string;
25
25
  };
26
26
  web3?: {
27
- rpc_urls?: Record<string, string>;
27
+ rpc_urls?: Record<string, string | string[]>;
28
28
  };
29
29
  integrations?: {
30
30
  telegram?: {
31
31
  enabled: boolean;
32
32
  bot_token?: string;
33
+ authorized_chat_id?: number;
33
34
  };
34
35
  };
35
36
  permissions?: {