nyxora 1.6.6 → 1.6.7
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 +44 -0
- package/LICENSE +21 -0
- package/README.md +21 -18
- package/package.json +2 -1
- package/packages/core/package.json +7 -1
- package/packages/core/src/agent/reasoning.ts +49 -4
- package/packages/core/src/gateway/cli.ts +6 -1
- package/packages/core/src/gateway/googleAuthModule.ts +181 -0
- package/packages/core/src/gateway/server.ts +110 -6
- package/packages/core/src/gateway/setup.ts +4 -4
- package/packages/core/src/system/skills/analyzeDocument.ts +64 -0
- package/packages/core/src/system/skills/googleWorkspace.ts +250 -0
- package/packages/core/src/system/skills/searchWeb.ts +46 -0
- package/packages/core/src/utils/dynamicTokenUpdater.ts +72 -0
- package/packages/core/src/utils/skillManager.ts +44 -0
- package/packages/core/src/utils/userWhitelistManager.ts +50 -0
- package/packages/core/src/web3/config.ts +29 -2
- package/packages/core/src/web3/skills/checkPortfolio.ts +51 -2
- package/packages/core/src/web3/skills/getBalance.ts +4 -0
- package/packages/core/src/web3/skills/swapToken.ts +9 -0
- package/packages/dashboard/dist/assets/index-24OeXn-k.css +1 -0
- package/packages/dashboard/dist/assets/index-BuYfTEKE.js +295 -0
- package/packages/dashboard/dist/favicon.svg +10 -1
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/dashboard/public/favicon.svg +10 -1
- package/packages/dashboard/src/App.tsx +28 -24
- package/packages/dashboard/src/NetworkSelector.tsx +64 -0
- package/packages/dashboard/src/NyxoraLogo.tsx +25 -0
- package/packages/dashboard/src/OsSkills.tsx +352 -0
- package/packages/dashboard/src/Overview.tsx +3 -3
- package/packages/dashboard/src/PendingTransactions.tsx +2 -2
- package/packages/dashboard/src/Settings.tsx +114 -61
- package/packages/dashboard/src/Skills.tsx +135 -20
- package/packages/dashboard/src/components/PillSelect.tsx +65 -0
- package/packages/dashboard/src/index.css +205 -18
- package/packages/dashboard/src/utils/api.ts +8 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/signer/package.json +1 -1
- package/test-address.ts +11 -0
- package/test-all-chains.ts +19 -0
- package/test-portfolio.ts +14 -0
- package/IDENTITY.md +0 -17
- package/nyxora-1.5.2.tgz +0 -0
- package/packages/dashboard/dist/assets/index-CfIids2e.js +0 -170
- package/packages/dashboard/dist/assets/index-POJM-7Fd.css +0 -1
- package/security_policy.md +0 -2
- package/user.md +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,50 @@ 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.7]
|
|
9
|
+
|
|
10
|
+
### UI/UX
|
|
11
|
+
- **New Nyxora Brand Logo**: Replaced the standard dashboard `Bot` icon with a native, 100% transparent SVG component of the Nyxora Cosmic Star.
|
|
12
|
+
- **Dashboard Avatar Overhaul**: Removed the rigid box border/shadow surrounding the agent avatar and maximized the logo scale (from 28px to 48px) for a bold, premium aesthetic.
|
|
13
|
+
- **SVG Optimization**: Cropped the internal viewBox (padding) of the logo to ensure it renders with maximum density and solidity at any resolution.
|
|
14
|
+
- **Favicon Update**: Synchronized the browser tab favicon with the newly optimized Nyxora logo.
|
|
15
|
+
- **Network Sync**: Synchronized network dropdown ordering (ETH > BSC > Base > Optimism > Arbitrum > Sepolia) across Dashboard UI, Settings, and CLI setup.
|
|
16
|
+
|
|
17
|
+
### Developer Experience
|
|
18
|
+
- **Single-Command Boot**: Introduced `npm run dev` in the root workspace utilizing `concurrently` to launch the backend orchestrator (Vault/Policy/Core) and the Vite frontend simultaneously, providing a seamless "Plug & Play" dev experience.
|
|
19
|
+
|
|
20
|
+
### Agent Intelligence
|
|
21
|
+
- **Cross-Chain Context**: Upgraded the core LLM prompt to automatically detect network mentions in chat (e.g., "on BNB") and override the default chain dynamically.
|
|
22
|
+
- **Portfolio Enforcement**: Instructed the agent to prioritize the comprehensive `check_portfolio` tool when users ask for general balances, while providing polite network confirmations.
|
|
23
|
+
- **Dust Asset Precision**: Improved portfolio USD calculations to render micro-assets (< $0.01) up to 4 decimal places (e.g., ~$0.0050) preventing inaccurate $0.00 rounding.
|
|
24
|
+
- **"Lean Degen" Auto-Whitelist**: AI now automatically intercepts and permanently saves Contract Addresses (CAs) into `user_whitelist.json` whenever users execute token swaps or check specific balances.
|
|
25
|
+
- **Dynamic Portfolio Merging**: The `checkPortfolio` engine now executes a hyper-fast, 0% rate-limit risk Multicall that merges standard tokens, user-defined CAs, and CoinGecko's daily trending list into a clean, unified dashboard report.
|
|
26
|
+
|
|
27
|
+
### Dual-Engine & OS Skills (Google Workspace MVP)
|
|
28
|
+
- **Native Google Integration**: Agent can now autonomously interact with Gmail (`read_gmail_inbox`), Google Calendar (`list_calendar_events`), Google Docs (`read_google_docs`), Google Sheets (`append_row_to_sheets`), and Google Forms (`read_google_form_responses`).
|
|
29
|
+
- **Security Upgrade (OS Keyring)**: Completely migrated OAuth token storage to the OS-Native Keyring Vault. Google Refresh Tokens are now securely locked and encrypted by the host OS, preventing plaintext credential theft.
|
|
30
|
+
- **Performance Optimization**: Scrapped the heavy `googleapis` dependency in favor of lightweight Native `fetch`, resulting in zero NPM install warnings, smaller footprint, and faster execution.
|
|
31
|
+
- **Bugfix**: Resolved TypeScript compilation errors (TS2349) related to the `pdf-parse` ESM dependency.
|
|
32
|
+
## [1.6.6]
|
|
33
|
+
|
|
34
|
+
### Hotfix: Global Monorepo Dependencies
|
|
35
|
+
|
|
36
|
+
This release patches a critical bug where global installations via `npm install -g nyxora` would fail to start the daemon due to missing C++ native modules.
|
|
37
|
+
|
|
38
|
+
#### Fixes
|
|
39
|
+
- **Dependency Hoisting Fix**: Explicitly bundled essential runtime modules (isolated-vm, telegraf, @modelcontextprotocol/sdk) into the root package.json to support monolithic publishing.
|
|
40
|
+
- **Zero-Crash Boot**: Resolves the MODULE_NOT_FOUND fatal error for isolated-vm when starting the daemon after a clean global install.
|
|
41
|
+
- **Dashboard Stability**: Ensures the background API server connects flawlessly to the React Dashboard without encountering Connection Refused.
|
|
42
|
+
## [1.6.5]
|
|
43
|
+
|
|
44
|
+
### The Universal Bridge (MCP Integration)
|
|
45
|
+
|
|
46
|
+
Nyxora now natively supports the Model Context Protocol (MCP). This massive upgrade transforms Nyxora from a standalone agent into a Universal Web3 Middleware. External AI clients (like Claude Desktop or Cursor IDE) can now securely interact with the Nyxora ecosystem out-of-the-box.
|
|
47
|
+
|
|
48
|
+
#### Key Features
|
|
49
|
+
- **StdioServerTransport**: Deep integration allowing Claude Desktop to securely spawn Nyxora as a child process.
|
|
50
|
+
- **Universal Bridge**: Exposes Nyxora's core crypto actions (swap, transfer, market analysis) as standard MCP Tools.
|
|
51
|
+
- **Enterprise Security**: All external MCP commands are strictly routed through Nyxora's battle-tested Policy Engine, ensuring no unauthorized transactions occur.
|
|
8
52
|
## [1.6.4]
|
|
9
53
|
|
|
10
54
|
### Added
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nyxora AI Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Nyxora Agent 🤖
|
|
2
|
-
**
|
|
2
|
+
**Dual-Engine AI Agent: Bridging On-Chain Execution with Off-Chain Automation.**
|
|
3
|
+
|
|
3
4
|
|
|
4
|
-
[](https://github.com/perasyudha/Nyxora)
|
|
5
5
|
[](#)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](#️-advanced-security-threat-model)
|
|
8
8
|
[](#️-advanced-security-threat-model)
|
|
9
9
|
[](#️-advanced-security-threat-model)
|
|
10
10
|
|
|
11
|
-
Nyxora
|
|
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 Glassmorphism UI dashboard 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
|
|
|
@@ -24,12 +24,17 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
|
|
|
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
|
|
|
27
|
-
###
|
|
28
|
-
* **System Automation & Full OS Access**: Instruct the agent to read/write local files, run terminal commands, and browse the web natively.
|
|
27
|
+
### 🌐 Web3 Skills (On-Chain)
|
|
29
28
|
* **Anti-Rugpull & Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
|
|
30
|
-
* **Automated
|
|
29
|
+
* **Automated Take Profit (TP) & Cut Loss (CL)**: The trader's holy grail. 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.
|
|
31
30
|
* **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.
|
|
32
|
-
* **
|
|
31
|
+
* **"Lean Degen" Auto-Whitelist**: Automatically intercepts Contract Addresses (CAs) whenever you check balances or swap tokens, saving them to your localized `user_whitelist.json` for future tracking.
|
|
32
|
+
* **Dynamic Portfolio Engine**: Merges standard tokens, your custom Degen CAs, and CoinGecko's daily trending list into a single hyper-fast Multicall scan to deliver a clean, spam-free PnL portfolio report in under 1 second.
|
|
33
|
+
|
|
34
|
+
### 💻 OS & Web2 Skills (Off-Chain)
|
|
35
|
+
* **Google Workspace Automation 🚀**: Transform Nyxora into your ultimate personal assistant. The agent can read your latest Gmail inbox, check your Google Calendar, extract text from Google Docs, and even append expense/trading logs directly to your Google Sheets.
|
|
36
|
+
* **System Automation & Full OS Access**: Instruct the agent to read/write local files, run terminal commands, and browse the web natively.
|
|
37
|
+
* **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."*
|
|
33
38
|
|
|
34
39
|
### AI & UI Customization
|
|
35
40
|
* **Zero-Click Multi-Session**: Instantly create isolated chat sessions with smart auto-naming triggered by your first prompt, exactly like ChatGPT.
|
|
@@ -48,7 +53,7 @@ The following diagram illustrates Nyxora's **3-Tier Monorepo Architecture**, sho
|
|
|
48
53
|
*Nyxora separates its duties into 3 independent layers for absolute security:*
|
|
49
54
|
1. **🧠 Core (The AI Brain)**: The intelligent assistant that strategizes and plans transactions, but **never** holds your funds.
|
|
50
55
|
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.
|
|
51
|
-
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.
|
|
56
|
+
3. **🔒 Signer Vault (The Safe)**: The offline vault where your Private Keys **and highly sensitive 3rd-party tokens (e.g., Google Workspace OAuth)** 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.
|
|
52
57
|
|
|
53
58
|
*(Note: Despite the multi-layered security process appearing lengthy, the internal system validation and cryptographic signing occurs in **milliseconds**, ensuring zero latency bottlenecks).*
|
|
54
59
|
|
|
@@ -56,7 +61,7 @@ The following diagram illustrates Nyxora's **3-Tier Monorepo Architecture**, sho
|
|
|
56
61
|
|
|
57
62
|
## 🛡️ Advanced Security & Threat Model
|
|
58
63
|
|
|
59
|
-
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security Blueprint](https://
|
|
64
|
+
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security Blueprint](https://nyxoraAI.github.io/Nyxora/).
|
|
60
65
|
|
|
61
66
|
---
|
|
62
67
|
|
|
@@ -84,7 +89,7 @@ nyxora dashboard
|
|
|
84
89
|
If you wish to modify the code or run from source, you can use the Monorepo architecture.
|
|
85
90
|
|
|
86
91
|
```bash
|
|
87
|
-
git clone https://github.com/
|
|
92
|
+
git clone https://github.com/nyxoraAI/Nyxora.git
|
|
88
93
|
cd Nyxora
|
|
89
94
|
|
|
90
95
|
# 1. Install Dependencies
|
|
@@ -96,13 +101,11 @@ npm run build
|
|
|
96
101
|
# 3. Interactive Setup Wizard (API Keys, Wallet, Telegram)
|
|
97
102
|
npm run setup
|
|
98
103
|
|
|
99
|
-
# 4. Start the
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# 5. Open the Web Dashboard
|
|
103
|
-
npm run dashboard
|
|
104
|
+
# 4. Start the Development Server (Single-Command Boot)
|
|
105
|
+
# This will start the backend vault/services and the frontend Dashboard simultaneously
|
|
106
|
+
npm run dev
|
|
104
107
|
```
|
|
105
|
-
> **⚠️ IMPORTANT:** Whenever you re-run `npm run setup` or manually edit the config files, you **must restart the
|
|
108
|
+
> **⚠️ IMPORTANT:** Whenever you re-run `npm run setup` or manually edit the config files, you **must restart the dev server** for the changes to take effect.
|
|
106
109
|
|
|
107
110
|
---
|
|
108
111
|
|
|
@@ -110,7 +113,7 @@ npm run dashboard
|
|
|
110
113
|
|
|
111
114
|
For complete technical deep-dives into our Cryptographic Architecture, please visit our official VitePress Documentation Site!
|
|
112
115
|
|
|
113
|
-
> **🔗 [Read the Full Nyxora Documentation Here](https://
|
|
116
|
+
> **🔗 [Read the Full Nyxora Documentation Here](https://nyxoraAI.github.io/Nyxora/)**
|
|
114
117
|
|
|
115
118
|
*(Includes guides on Secure Wallet Imports, Architecture Blueprints, Troubleshooting, and Custom Skill Development).*
|
|
116
119
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"packages/*"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"nyxora": "bin/nyxora.mjs"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
+
"dev": "concurrently -n \"BACKEND,FRONTEND\" -c \"blue,green\" \"npx ts-node -T launcher.ts\" \"npm run dev --workspace=dashboard\"",
|
|
12
13
|
"start": "node ./bin/nyxora.mjs start",
|
|
13
14
|
"stop": "node ./bin/nyxora.mjs stop",
|
|
14
15
|
"restart": "node ./bin/nyxora.mjs restart",
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora-agent-core",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "src/gateway/server.ts",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@clack/prompts": "^1.4.0",
|
|
8
8
|
"cors": "^2.8.6",
|
|
9
|
+
"duck-duck-scrape": "^2.2.7",
|
|
9
10
|
"express": "^5.2.1",
|
|
10
11
|
"express-rate-limit": "^7.5.0",
|
|
11
12
|
"helmet": "^8.0.0",
|
|
12
13
|
"isolated-vm": "^6.1.2",
|
|
14
|
+
"mammoth": "^1.12.0",
|
|
13
15
|
"open": "^11.0.0",
|
|
14
16
|
"openai": "^6.39.0",
|
|
17
|
+
"pdf-parse": "^2.4.5",
|
|
15
18
|
"telegraf": "^4.16.3",
|
|
16
19
|
"yaml": "^2.9.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/pdf-parse": "^1.1.5"
|
|
17
23
|
}
|
|
18
24
|
}
|
|
@@ -9,6 +9,7 @@ import { transferToolDefinition, prepareTransfer } from '../web3/skills/transfer
|
|
|
9
9
|
import { getPriceToolDefinition, getPrice } from '../web3/skills/getPrice';
|
|
10
10
|
import { swapTokenToolDefinition, prepareSwapToken } from '../web3/skills/swapToken';
|
|
11
11
|
import { bridgeTokenToolDefinition, prepareBridgeToken } from '../web3/skills/bridgeToken';
|
|
12
|
+
import { isSkillActive } from '../utils/skillManager';
|
|
12
13
|
import { mintNftToolDefinition, prepareMintNft } from '../web3/skills/mintNft';
|
|
13
14
|
import { customTxToolDefinition, prepareCustomTx } from '../web3/skills/customTx';
|
|
14
15
|
import { createWalletToolDefinition, createWallet } from '../web3/skills/createWallet';
|
|
@@ -25,6 +26,18 @@ import { writeLocalFileToolDefinition, writeLocalFile } from '../system/skills/w
|
|
|
25
26
|
import { runTerminalCommandToolDefinition, runTerminalCommand } from '../system/skills/executeShell';
|
|
26
27
|
import { browseWebsiteToolDefinition, browseWebsite } from '../system/skills/browseWeb';
|
|
27
28
|
import { installExternalSkillToolDefinition, installExternalSkill } from '../system/skills/installSkill';
|
|
29
|
+
import {
|
|
30
|
+
readGmailInbox,
|
|
31
|
+
listCalendarEvents,
|
|
32
|
+
appendRowToSheets,
|
|
33
|
+
readGoogleDocs,
|
|
34
|
+
readGoogleFormResponses,
|
|
35
|
+
readGmailInboxToolDefinition,
|
|
36
|
+
listCalendarEventsToolDefinition,
|
|
37
|
+
appendRowToSheetsToolDefinition,
|
|
38
|
+
readGoogleDocsToolDefinition,
|
|
39
|
+
readGoogleFormResponsesToolDefinition
|
|
40
|
+
} from '../system/skills/googleWorkspace';
|
|
28
41
|
import { pluginManager } from '../system/pluginManager';
|
|
29
42
|
import { getPath } from '../config/paths';
|
|
30
43
|
import pc from 'picocolors';
|
|
@@ -141,9 +154,9 @@ function getSystemPrompt() {
|
|
|
141
154
|
let basePrompt = `You are an autonomous Web3 agent operating on EVM chains.
|
|
142
155
|
You are equipped with a native wallet.
|
|
143
156
|
CRITICAL RULE: You must always reply in the exact same language that the user uses to talk to you. If the user speaks Indonesian, reply in Indonesian. If they speak English, reply in English.
|
|
144
|
-
CRITICAL RULE:
|
|
145
|
-
|
|
146
|
-
If the user
|
|
157
|
+
CRITICAL RULE: When the user asks to check "my balance", "saldo saya", or anything about their own wallet generally, ALWAYS use the check_portfolio tool to show all assets on the chain that have a USD value greater than 0. LEAVE THE ADDRESS PARAMETER EMPTY. Do NOT use get_balance unless the user explicitly asks for the balance of ONE specific token (e.g., "what is my ETH balance?").
|
|
158
|
+
CRITICAL RULE: If the user doesn't specify a chain, default to: ${config.agent.default_chain}. If the user mentions a specific chain (e.g., "on BNB", "di Base"), you MUST override the default and execute the tool on that specific chain.
|
|
159
|
+
CRITICAL RULE: If you use the default chain because the user forgot to specify one, you MUST politely confirm which chain you checked in your response (e.g., "I checked your balance on the ${config.agent.default_chain} network..."). Do not issue scary warnings.`;
|
|
147
160
|
|
|
148
161
|
// Read IDENTITY.md for core AI persona
|
|
149
162
|
try {
|
|
@@ -238,8 +251,13 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
238
251
|
runTerminalCommandToolDefinition as any,
|
|
239
252
|
browseWebsiteToolDefinition as any,
|
|
240
253
|
installExternalSkillToolDefinition as any,
|
|
254
|
+
readGmailInboxToolDefinition as any,
|
|
255
|
+
listCalendarEventsToolDefinition as any,
|
|
256
|
+
appendRowToSheetsToolDefinition as any,
|
|
257
|
+
readGoogleDocsToolDefinition as any,
|
|
258
|
+
readGoogleFormResponsesToolDefinition as any,
|
|
241
259
|
...pluginManager.getToolDefinitions()
|
|
242
|
-
],
|
|
260
|
+
].filter(t => isSkillActive(t.function.name)),
|
|
243
261
|
tool_choice: "auto",
|
|
244
262
|
});
|
|
245
263
|
});
|
|
@@ -395,6 +413,26 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
395
413
|
result = await installExternalSkill(args.url);
|
|
396
414
|
break;
|
|
397
415
|
}
|
|
416
|
+
case 'read_gmail_inbox': {
|
|
417
|
+
result = await readGmailInbox(args.maxResults);
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
case 'list_calendar_events': {
|
|
421
|
+
result = await listCalendarEvents(args.maxResults);
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
case 'append_row_to_sheets': {
|
|
425
|
+
result = await appendRowToSheets(args.spreadsheetId, args.range, args.values);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case 'read_google_docs': {
|
|
429
|
+
result = await readGoogleDocs(args.documentId);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case 'read_google_form_responses': {
|
|
433
|
+
result = await readGoogleFormResponses(args.formId);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
398
436
|
default: {
|
|
399
437
|
const externalResult = await pluginManager.executeTool(toolName, args);
|
|
400
438
|
if (externalResult !== null) {
|
|
@@ -423,6 +461,13 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
423
461
|
name: toolName,
|
|
424
462
|
content: result,
|
|
425
463
|
}, sessionId);
|
|
464
|
+
|
|
465
|
+
// V2 Optimization: Zero-LLM Fast Return for data-heavy tools
|
|
466
|
+
// If the tool already returns perfectly formatted markdown, skip the second LLM call to save 5-10s latency and tokens!
|
|
467
|
+
if (toolName === 'check_portfolio' || toolName === 'check_address') {
|
|
468
|
+
logger.addEntry({ role: 'assistant', content: result }, sessionId);
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
426
471
|
}
|
|
427
472
|
|
|
428
473
|
// Second call to get the final answer after tool execution
|
|
@@ -64,8 +64,13 @@ console.log(`================================`);
|
|
|
64
64
|
|
|
65
65
|
// 4. Start the Express API Server (which also serves the static dashboard and Telegram bot)
|
|
66
66
|
startServer();
|
|
67
|
-
getSessionToken(); // Initialize token file
|
|
67
|
+
const token = getSessionToken(); // Initialize token file
|
|
68
68
|
console.log(`🌐 Nyxora API Server running on port ${process.env.PORT || 3000}`);
|
|
69
|
+
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
console.log(pc.cyan(`\n✨ Dashboard URL: http://localhost:3000/?token=${token}`));
|
|
72
|
+
console.log(pc.gray(` (Developers: Vite hot-reload available at http://localhost:5173/?token=${token})\n`));
|
|
73
|
+
}, 1500);
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
main().catch(console.error);
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getAppDir } from '../config/paths';
|
|
4
|
+
|
|
5
|
+
const CREDENTIALS_PATH = path.join(getAppDir(), 'google-credentials.json');
|
|
6
|
+
const FALLBACK_TOKEN_PATH = path.join(getAppDir(), 'google-tokens.json');
|
|
7
|
+
|
|
8
|
+
const SCOPES = [
|
|
9
|
+
'https://www.googleapis.com/auth/gmail.readonly',
|
|
10
|
+
'https://www.googleapis.com/auth/calendar.readonly',
|
|
11
|
+
'https://www.googleapis.com/auth/documents.readonly',
|
|
12
|
+
'https://www.googleapis.com/auth/spreadsheets',
|
|
13
|
+
'https://www.googleapis.com/auth/forms.responses.readonly',
|
|
14
|
+
'https://www.googleapis.com/auth/drive.file'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
interface GoogleCredentials {
|
|
18
|
+
client_id: string;
|
|
19
|
+
client_secret: string;
|
|
20
|
+
redirect_uris: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let credentials: GoogleCredentials | null = null;
|
|
24
|
+
let accessToken: string | null = null;
|
|
25
|
+
let tokenExpiry: number = 0; // Unix timestamp in ms
|
|
26
|
+
|
|
27
|
+
export async function initGoogleAuth(): Promise<boolean> {
|
|
28
|
+
if (!fs.existsSync(CREDENTIALS_PATH)) {
|
|
29
|
+
console.log(`[Google Auth] No credentials found at ${CREDENTIALS_PATH}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const content = fs.readFileSync(CREDENTIALS_PATH, 'utf8');
|
|
35
|
+
const parsed = JSON.parse(content);
|
|
36
|
+
credentials = parsed.web || parsed.installed;
|
|
37
|
+
|
|
38
|
+
// Check if we already have a refresh token saved
|
|
39
|
+
const refreshToken = await getRefreshToken();
|
|
40
|
+
if (refreshToken) {
|
|
41
|
+
console.log('[Google Auth] Refresh token found in secure storage.');
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error('[Google Auth] Error initializing:', err);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getAuthUrl(): string | null {
|
|
52
|
+
if (!credentials) return null;
|
|
53
|
+
|
|
54
|
+
const params = new URLSearchParams({
|
|
55
|
+
client_id: credentials.client_id,
|
|
56
|
+
redirect_uri: credentials.redirect_uris[0],
|
|
57
|
+
response_type: 'code',
|
|
58
|
+
scope: SCOPES.join(' '),
|
|
59
|
+
access_type: 'offline',
|
|
60
|
+
prompt: 'consent'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function processCallback(code: string): Promise<boolean> {
|
|
67
|
+
if (!credentials) return false;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch('https://oauth2.googleapis.com/token', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
73
|
+
body: new URLSearchParams({
|
|
74
|
+
code,
|
|
75
|
+
client_id: credentials.client_id,
|
|
76
|
+
client_secret: credentials.client_secret,
|
|
77
|
+
redirect_uri: credentials.redirect_uris[0],
|
|
78
|
+
grant_type: 'authorization_code'
|
|
79
|
+
}).toString()
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const data = await res.json();
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
console.error('[Google Auth] Token exchange failed:', data);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (data.access_token) {
|
|
89
|
+
accessToken = data.access_token;
|
|
90
|
+
tokenExpiry = Date.now() + (data.expires_in * 1000);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (data.refresh_token) {
|
|
94
|
+
await saveRefreshToken(data.refresh_token);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error('[Google Auth] Error processing callback:', err);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function isAuthenticated(): Promise<boolean> {
|
|
105
|
+
const rt = await getRefreshToken();
|
|
106
|
+
return !!rt;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function getAccessToken(): Promise<string | null> {
|
|
110
|
+
if (!credentials) return null;
|
|
111
|
+
|
|
112
|
+
// If token is valid for at least 5 more minutes, use it
|
|
113
|
+
if (accessToken && Date.now() < tokenExpiry - 300000) {
|
|
114
|
+
return accessToken;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Otherwise, refresh it
|
|
118
|
+
const refreshToken = await getRefreshToken();
|
|
119
|
+
if (!refreshToken) return null;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const res = await fetch('https://oauth2.googleapis.com/token', {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
125
|
+
body: new URLSearchParams({
|
|
126
|
+
refresh_token: refreshToken,
|
|
127
|
+
client_id: credentials.client_id,
|
|
128
|
+
client_secret: credentials.client_secret,
|
|
129
|
+
grant_type: 'refresh_token'
|
|
130
|
+
}).toString()
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const data = await res.json();
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
console.error('[Google Auth] Failed to refresh token:', data);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
accessToken = data.access_token;
|
|
140
|
+
tokenExpiry = Date.now() + (data.expires_in * 1000);
|
|
141
|
+
return accessToken;
|
|
142
|
+
} catch (err) {
|
|
143
|
+
console.error('[Google Auth] Error refreshing token:', err);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---- Secure Storage for Refresh Token ----
|
|
149
|
+
|
|
150
|
+
async function saveRefreshToken(token: string) {
|
|
151
|
+
try {
|
|
152
|
+
const { Entry } = require('@napi-rs/keyring');
|
|
153
|
+
const entry = new Entry('nyxora', 'google_refresh_token');
|
|
154
|
+
await entry.setPassword(token);
|
|
155
|
+
console.log('[Google Auth] Refresh token saved securely to OS Keyring.');
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.warn('[Google Auth] Keyring failed, falling back to local tokens.json');
|
|
158
|
+
fs.writeFileSync(FALLBACK_TOKEN_PATH, JSON.stringify({ refresh_token: token }), { mode: 0o600 });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function getRefreshToken(): Promise<string | null> {
|
|
163
|
+
try {
|
|
164
|
+
const { Entry } = require('@napi-rs/keyring');
|
|
165
|
+
const entry = new Entry('nyxora', 'google_refresh_token');
|
|
166
|
+
const password = await entry.getPassword();
|
|
167
|
+
if (password) return password;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
// Fallback to file
|
|
170
|
+
if (fs.existsSync(FALLBACK_TOKEN_PATH)) {
|
|
171
|
+
try {
|
|
172
|
+
const content = fs.readFileSync(FALLBACK_TOKEN_PATH, 'utf8');
|
|
173
|
+
const parsed = JSON.parse(content);
|
|
174
|
+
if (parsed.refresh_token) return parsed.refresh_token;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|