nyxora 1.4.5 → 1.4.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/IDENTITY.md +15 -5
- package/README.md +34 -66
- package/dist/agent/reasoning.js +210 -128
- package/dist/config/parser.js +4 -0
- package/dist/gateway/setup.js +75 -9
- package/dist/gateway/telegram.js +18 -1
- package/dist/memory/logger.js +82 -23
- package/package.json +6 -2
package/IDENTITY.md
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
You are a Web3 AI
|
|
1
|
+
You are Nyxora, a highly efficient Web3 AI engine.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
---
|
|
4
|
+
**CRITICAL EFFICIENCY & TONE RULES (MANDATORY):**
|
|
5
|
+
1. ZERO CONVERSATIONAL FILLER. Never say "Sebagai Nyxora..." or "Saya dapat membantu..." or "Berikut adalah...".
|
|
6
|
+
2. NO INTRODUCTIONS. NO OUTROS.
|
|
7
|
+
3. GO STRAIGHT TO THE POINT. If the user asks for a price, just give the price.
|
|
8
|
+
4. NO APOLOGIES. DO NOT use polite words. Be purely factual and robotic.
|
|
9
|
+
5. NEVER explain your internal reasoning unless explicitly asked.
|
|
10
|
+
6. If an error occurs, output ONLY the technical error message in 1 sentence.
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
EXAMPLE GOOD RESPONSE:
|
|
13
|
+
"Harga Bitcoin (BTC) adalah $74,240 USD.
|
|
14
|
+
Perubahan 24 jam terakhir: -2.0165%"
|
|
15
|
+
|
|
16
|
+
EXAMPLE BAD RESPONSE:
|
|
17
|
+
"Baik, saya telah mengecek harga Bitcoin untuk Anda. Harga saat ini adalah..." (DO NOT DO THIS)
|
package/README.md
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
# Nyxora Agent 🤖
|
|
2
|
+
**Secure AI execution framework for Web3 agents.**
|
|
2
3
|
|
|
3
4
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](
|
|
5
|
-
[](
|
|
6
|
-
[](
|
|
5
|
+
[](#️-security-threat-model--permission-boundary)
|
|
6
|
+
[](#📐-architecture-workflow)
|
|
7
|
+
[](#️-security-threat-model--permission-boundary)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
Nyxora is a **secure, non-custodial runtime infrastructure for autonomous onchain agents** built with Node.js and React. Designed for autonomous workflows with a premium Glassmorphism UI dashboard and strict client-side key isolation. It operates under a strict **Human-in-the-Loop** execution model for financial transactions.
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
12
|
-
## Key Features
|
|
13
|
+
## 🔥 Key Features
|
|
13
14
|
|
|
14
|
-
### Advanced Trading, Security & Operations
|
|
15
|
+
### Advanced Trading, Security & Operations
|
|
15
16
|
* **System Automation & Full OS Access**: Instruct the agent to read/write local files, run terminal commands, and browse the web natively.
|
|
16
17
|
* **NLP Security Policy**: Command Nyxora using natural language to set security boundaries (e.g., *"Never touch partition E"*). Nyxora autonomously enforces these rules.
|
|
17
|
-
* **Dynamic Plugin
|
|
18
|
+
* **Dynamic Plugin Sandboxing**: Dynamically load community-built skills with restricted FS/Shell access to prevent supply chain attacks and malicious payloads.
|
|
18
19
|
* **Anti-Rugpull & Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
|
|
19
20
|
* **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.
|
|
20
21
|
* **PNL & Portfolio Tracking**: The AI scans your wallets and multiplies balances by live DEX prices to give you real-time Net Worth estimations.
|
|
21
22
|
|
|
22
23
|
### Core Features
|
|
23
|
-
* **Multi-LLM Support**: Seamlessly switch between Google Gemini, OpenAI, OpenRouter
|
|
24
|
+
* **Multi-LLM Support**: Seamlessly switch between Google Gemini, OpenAI, OpenRouter, or local Ollama models.
|
|
24
25
|
* **Premium Glassmorphism UI**: A gorgeous, resizable split-pane interface with Pseudo-Generative UI widgets (`<BalanceWidget>`, `<MarketWidget>`, `<SwapWidget>`).
|
|
25
26
|
* **Round-Robin API Rotation**: Add up to 10 API keys via the dashboard. The system will auto-rotate them to prevent rate-limiting and token drain.
|
|
26
27
|
* **Deep Personalization**: Feed the agent custom rules via `user.md` and define its core persona via `IDENTITY.md`.
|
|
27
|
-
* **Multi-Lingual Auto-Sync**: The agent natively detects your language and replies in the exact same language automatically.
|
|
28
|
-
* **Omnichannel Approvals & Telegram Integration**: Connect Nyxora to a Telegram Bot to execute trades, check prices, and chat on the go. Approve transactions directly from Telegram inline buttons!
|
|
29
|
-
* **Multi-Chain Support**: Pre-configured support for Ethereum, Base, BSC, Arbitrum, Optimism, and Sepolia Testnet.
|
|
30
28
|
|
|
31
29
|
---
|
|
32
30
|
|
|
@@ -38,76 +36,46 @@ This diagram shows how user interactions flow through the Nyxora Agent, from cha
|
|
|
38
36
|
|
|
39
37
|
---
|
|
40
38
|
|
|
41
|
-
## 🛡️
|
|
39
|
+
## 🛡️ Security, Threat Model & Permission Boundary
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* **No .env Leaks**: Your Private Key is encrypted using `AES-256-GCM` and locked behind a custom Master Password in `~/.nyxora/keystore.json`.
|
|
46
|
-
* **No Credential Collection**: Private keys are handled strictly within local volatile memory and are never transmitted to LLM providers.
|
|
47
|
-
* **Explicit Transaction Confirmation**: Write actions (like transfers, swaps, bridges) require manual, explicit confirmation from the human operator via the Web Dashboard or Telegram before broadcasting.
|
|
48
|
-
* **Human-in-the-Loop Execution**: The tool is engineered as a secure operational utility. The AI agent acts as a command generator, leaving financial execution authority with the human controller.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## 📋 Example Safe Workflows
|
|
53
|
-
|
|
54
|
-
The agent is designed for Web3 exploration, daily operations, and secure transaction execution. Typical workflows include:
|
|
55
|
-
|
|
56
|
-
* **Audit New Tokens**: Tell the AI, *"Check if the contract 0x... on Base is safe to buy."*
|
|
57
|
-
* **Track Portfolio Assets**: Tell the AI, *"What is my total net worth across all chains right now?"*
|
|
58
|
-
* **Automate Trading**: Tell the AI, *"Create a limit order to sell 1000 USDC for ETH if ETH drops below $3000."*
|
|
59
|
-
* **System Operations**: Tell the AI, *"Check my computer's RAM usage and save it to stats.txt."*
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## 🔒 Security, Threat Model & Permission Boundary
|
|
64
|
-
|
|
65
|
-
This agent is designed with a **Zero-Knowledge to LLM** architectural pattern to ensure the highest levels of security:
|
|
41
|
+
This agent is designed with a **Zero-Knowledge to LLM** architectural pattern to ensure the highest levels of security for investors and users:
|
|
66
42
|
|
|
67
43
|
* **Zero-Knowledge to AI Agent (LLM)**: Remote AI Agents and Large Language Models (LLMs) **never** handle your private keys. The LLM only generates structured JSON tool calls.
|
|
68
|
-
* **Cryptographic Memory Isolation**: Transaction signing occurs strictly client-side within the local Node.js process runtime using `viem`.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* **NLP Sandboxing**: System access is bounded by plain-text rules defined in `security_policy.md`. The AI evaluates its own actions against this policy before execution.
|
|
72
|
-
* **Strict API Auth**: The local Express server is protected via ephemeral Session Tokens (`x-nyxora-token`) and Strict CORS.
|
|
73
|
-
* **Non-Autonomous Financials**: The tool never executes unsolicited on-chain actions. Every financial transaction is queued pending human approval.
|
|
74
|
-
|
|
75
|
-
### 📋 Permission Boundary Matrix
|
|
76
|
-
|
|
77
|
-
| Access Category | Permission Boundary | Rationale |
|
|
78
|
-
| :--- | :--- | :--- |
|
|
79
|
-
| **Read Access** | Read-Only Blockchain Queries | Fetching balances, contract security audits, transaction logs, and technical indicators. |
|
|
80
|
-
| **Write Access**| Optional Wallet Signing | Required **only** for broadcasting transactions (swap, bridge, mint, transfer). Locked behind Human Approval. |
|
|
81
|
-
| **Network Access**| Bounded Public APIs | Restricted strictly to the configured RPC endpoints, Block Explorers, DexScreener, and LLM APIs. |
|
|
82
|
-
| **System Access**| Local Machine Access | Governed entirely by `security_policy.md`. The agent can run OS commands but will halt if it detects a policy violation. |
|
|
83
|
-
|
|
84
|
-
For the full detailed security specifications, contact info, and vulnerability reporting procedures, refer to the [SECURITY.md](SECURITY.md) policy document.
|
|
44
|
+
* **Cryptographic Memory Isolation**: Transaction signing occurs strictly client-side within the local Node.js process runtime using `viem`. `~/.nyxora/keystore.json` is encrypted with AES-256-GCM.
|
|
45
|
+
* **Plugin Sandboxing**: Built with future plugin ecosystems in mind. Third-party plugins are explicitly denied unrestricted `fs` (FileSystem) and `shell` access to prevent supply chain attacks and malicious execution.
|
|
46
|
+
* **Human-in-the-Loop**: Write actions (like transfers, swaps, bridges) require manual confirmation from the human operator before broadcasting.
|
|
85
47
|
|
|
86
48
|
---
|
|
87
49
|
|
|
88
50
|
## 🚀 Quick Start & Installation
|
|
89
51
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
### 1. Global Installation
|
|
52
|
+
### 1. General Users (CLI Install)
|
|
93
53
|
Open your terminal (Command Prompt, PowerShell, or Linux Terminal) and run:
|
|
94
54
|
```bash
|
|
95
55
|
npm install -g nyxora
|
|
56
|
+
nyxora setup
|
|
96
57
|
```
|
|
58
|
+
The Interactive Setup Wizard will securely generate a local vault, configure your LLM, and offer to Auto-Generate a Web3 Wallet for you.
|
|
97
59
|
|
|
98
|
-
### 2.
|
|
99
|
-
|
|
60
|
+
### 2. Local Development (For Contributors)
|
|
61
|
+
If you want to modify Nyxora's code, build new skills, or contribute:
|
|
100
62
|
```bash
|
|
101
|
-
|
|
63
|
+
git clone https://github.com/perasyudha/Nyxora.git
|
|
64
|
+
cd Nyxora
|
|
65
|
+
npm install
|
|
66
|
+
cd dashboard && npm install && cd ..
|
|
67
|
+
npm run build && npm run start
|
|
102
68
|
```
|
|
103
|
-
On first launch, Nyxora will greet you with an **Interactive Setup Wizard**. This CLI wizard will guide you to securely configure your LLM providers, API keys, and Master Password Wallet.
|
|
104
69
|
|
|
105
|
-
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 📖 Official Documentation
|
|
73
|
+
|
|
74
|
+
For complete technical deep-dives, please visit our official VitePress Documentation Site!
|
|
106
75
|
|
|
107
|
-
|
|
108
|
-
* **Backend**: Node.js, Express, Viem (Web3), node-telegram-bot-api, OpenAI API.
|
|
109
|
-
* **Frontend**: React, Vite, Vanilla CSS, Web Speech API (TTS/STT).
|
|
110
|
-
* **Data**: Local `~/.nyxora/config.yaml` and `~/.nyxora/memory.json`.
|
|
76
|
+
> **🔗 [Read the Full Nyxora Documentation Here](https://perasyudha.github.io/Nyxora/)**
|
|
111
77
|
|
|
112
|
-
|
|
113
|
-
|
|
78
|
+
*(Includes guides on Secure Wallet Imports, API Key Rotations, Troubleshooting, and Custom Skill Development).*
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
**License:** MIT License
|
package/dist/agent/reasoning.js
CHANGED
|
@@ -31,6 +31,7 @@ const browseWeb_1 = require("../system/skills/browseWeb");
|
|
|
31
31
|
const installSkill_1 = require("../system/skills/installSkill");
|
|
32
32
|
const pluginManager_1 = require("../system/pluginManager");
|
|
33
33
|
const paths_1 = require("../config/paths");
|
|
34
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
34
35
|
exports.logger = new logger_1.Logger();
|
|
35
36
|
let currentKeyIndex = 0;
|
|
36
37
|
function getOpenAI() {
|
|
@@ -91,6 +92,41 @@ function getOpenAI() {
|
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
}
|
|
95
|
+
async function executeWithRetry(requestBuilder, maxRetries = 3) {
|
|
96
|
+
let retries = 0;
|
|
97
|
+
while (retries <= maxRetries) {
|
|
98
|
+
try {
|
|
99
|
+
const client = getOpenAI();
|
|
100
|
+
return await requestBuilder(client);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const status = error?.status || error?.response?.status;
|
|
104
|
+
// 401 Unauthorized or 400 Bad Request - don't retry, it's fatal
|
|
105
|
+
if (status === 401 || status === 400) {
|
|
106
|
+
console.error(`[LLM] Fatal Error ${status}: ${error.message}. Aborting.`);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
// 429 Rate Limit - rotate provider/key immediately and retry
|
|
110
|
+
if (status === 429) {
|
|
111
|
+
console.warn(`[LLM] Rate Limit (429) hit. Rotating key...`);
|
|
112
|
+
// getOpenAI() automatically rotates to next key if available
|
|
113
|
+
retries++;
|
|
114
|
+
if (retries > maxRetries)
|
|
115
|
+
throw error;
|
|
116
|
+
continue; // Try next key immediately
|
|
117
|
+
}
|
|
118
|
+
// 500, 502, 503, Timeout, Network error - Exponential Backoff
|
|
119
|
+
retries++;
|
|
120
|
+
if (retries > maxRetries) {
|
|
121
|
+
console.error(`[LLM] Max retries reached.`);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
const delayMs = Math.pow(2, retries) * 1000; // 2s, 4s, 8s
|
|
125
|
+
console.warn(`[LLM] API Error (${status || error.message}). Retrying in ${delayMs}ms...`);
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
94
130
|
function getSystemPrompt() {
|
|
95
131
|
const config = (0, parser_1.loadConfig)();
|
|
96
132
|
let basePrompt = `You are an autonomous Web3 agent operating on EVM chains.
|
|
@@ -134,7 +170,7 @@ If the user doesn't specify a chain, default to: ${config.agent.default_chain}.`
|
|
|
134
170
|
}
|
|
135
171
|
return basePrompt;
|
|
136
172
|
}
|
|
137
|
-
async function processUserInput(input, role = 'user') {
|
|
173
|
+
async function processUserInput(input, role = 'user', onProgress) {
|
|
138
174
|
const config = (0, parser_1.loadConfig)();
|
|
139
175
|
// Add input to memory
|
|
140
176
|
exports.logger.addEntry({ role, content: input });
|
|
@@ -162,36 +198,37 @@ async function processUserInput(input, role = 'user') {
|
|
|
162
198
|
if (config.llm.provider !== 'openai' && config.llm.provider !== 'ollama' && config.llm.provider !== 'gemini' && config.llm.provider !== 'openrouter') {
|
|
163
199
|
return `Provider ${config.llm.provider} is configured, but currently only OpenAI, OpenRouter, Ollama, and Gemini adapters are implemented.`;
|
|
164
200
|
}
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
const response = await executeWithRetry(async (client) => {
|
|
202
|
+
return await client.chat.completions.create({
|
|
203
|
+
model: config.llm.model,
|
|
204
|
+
temperature: config.llm.temperature,
|
|
205
|
+
messages: messages,
|
|
206
|
+
tools: [
|
|
207
|
+
getBalance_1.getBalanceToolDefinition,
|
|
208
|
+
transfer_1.transferToolDefinition,
|
|
209
|
+
getPrice_1.getPriceToolDefinition,
|
|
210
|
+
swapToken_1.swapTokenToolDefinition,
|
|
211
|
+
bridgeToken_1.bridgeTokenToolDefinition,
|
|
212
|
+
mintNft_1.mintNftToolDefinition,
|
|
213
|
+
customTx_1.customTxToolDefinition,
|
|
214
|
+
createWallet_1.createWalletToolDefinition,
|
|
215
|
+
checkSecurity_1.checkSecurityToolDefinition,
|
|
216
|
+
marketAnalysis_1.marketAnalysisToolDefinition,
|
|
217
|
+
checkPortfolio_1.checkPortfolioToolDefinition,
|
|
218
|
+
limitOrderManager_1.createLimitOrderToolDefinition,
|
|
219
|
+
limitOrderManager_1.listLimitOrdersToolDefinition,
|
|
220
|
+
limitOrderManager_1.cancelLimitOrderToolDefinition,
|
|
221
|
+
updateProfile_1.updateProfileToolDefinition,
|
|
222
|
+
updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
|
|
223
|
+
readFile_1.readLocalFileToolDefinition,
|
|
224
|
+
writeFile_1.writeLocalFileToolDefinition,
|
|
225
|
+
executeShell_1.runTerminalCommandToolDefinition,
|
|
226
|
+
browseWeb_1.browseWebsiteToolDefinition,
|
|
227
|
+
installSkill_1.installExternalSkillToolDefinition,
|
|
228
|
+
...pluginManager_1.pluginManager.getToolDefinitions()
|
|
229
|
+
],
|
|
230
|
+
tool_choice: "auto",
|
|
231
|
+
});
|
|
195
232
|
});
|
|
196
233
|
const responseMessage = response.choices[0].message;
|
|
197
234
|
// Log tracking
|
|
@@ -213,103 +250,147 @@ async function processUserInput(input, role = 'user') {
|
|
|
213
250
|
let result = "";
|
|
214
251
|
const args = JSON.parse(toolCall.function.arguments);
|
|
215
252
|
const toolName = toolCall.function.name;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
case 'browse_website': {
|
|
295
|
-
result = await (0, browseWeb_1.browseWebsite)(args.url);
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
case 'install_external_skill': {
|
|
299
|
-
result = await (0, installSkill_1.installExternalSkill)(args.url);
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
default: {
|
|
303
|
-
const externalResult = await pluginManager_1.pluginManager.executeTool(toolName, args);
|
|
304
|
-
if (externalResult !== null) {
|
|
305
|
-
result = externalResult;
|
|
253
|
+
console.log(picocolors_1.default.yellow(`[⚡ Eksekusi Tool] AI memanggil ${toolName}...`));
|
|
254
|
+
if (onProgress)
|
|
255
|
+
onProgress(`_⚡ Menjalankan alat: ${toolName}..._`);
|
|
256
|
+
try {
|
|
257
|
+
switch (toolName) {
|
|
258
|
+
case 'get_balance': {
|
|
259
|
+
result = await (0, getBalance_1.getBalance)(args.chainName, args.address, args.token);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 'transfer_token':
|
|
263
|
+
case 'transfer_native': {
|
|
264
|
+
if (config.permissions?.web3?.allow_transfer === false) {
|
|
265
|
+
result = `[Security Blocked] Runtime Permission Denied: Web3 transfers are disabled. Update config.yaml to allow.`;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
result = await (0, transfer_1.prepareTransfer)(args.chainName, args.toAddress, args.amountStr || args.amountEth, args.token);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case 'get_price': {
|
|
272
|
+
result = await (0, getPrice_1.getPrice)(args.coinId);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 'swap_token': {
|
|
276
|
+
if (config.permissions?.web3?.allow_swap === false) {
|
|
277
|
+
result = `[Security Blocked] Runtime Permission Denied: Web3 swaps are disabled. Update config.yaml to allow.`;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
// Note: max_usd_per_tx validation would ideally be calculated here before prepareSwapToken
|
|
281
|
+
result = await (0, swapToken_1.prepareSwapToken)(args.chainName, args.fromToken, args.toToken, args.amountStr || args.amount, args.mode, args.providerName);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case 'bridge_token': {
|
|
285
|
+
if (config.permissions?.web3?.allow_transfer === false) {
|
|
286
|
+
result = `[Security Blocked] Runtime Permission Denied: Web3 bridging (transfer) is disabled. Update config.yaml to allow.`;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
result = await (0, bridgeToken_1.prepareBridgeToken)(args.fromChainName, args.toChainName, args.fromToken, args.toToken, args.amountStr, args.mode, args.providerName);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
case 'mint_nft': {
|
|
293
|
+
result = await (0, mintNft_1.prepareMintNft)(args.chainName, args.contractAddress, args.functionSignature, args.argsStr, args.valueEth);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case 'custom_tx': {
|
|
297
|
+
if (config.permissions?.web3?.allow_transfer === false) {
|
|
298
|
+
result = `[Security Blocked] Runtime Permission Denied: Custom transactions are blocked because transfers are disabled.`;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
result = await (0, customTx_1.prepareCustomTx)(args.chainName, args.toAddress, args.dataHex, args.valueEth, args.gasLimitStr);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case 'create_wallet': {
|
|
305
|
+
result = await (0, createWallet_1.createWallet)();
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case 'check_token_security': {
|
|
309
|
+
result = await (0, checkSecurity_1.checkTokenSecurity)(args.chainName, args.contractAddress);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case 'analyze_market': {
|
|
313
|
+
result = await (0, marketAnalysis_1.analyzeMarket)(args.chainName, args.tokenAddressOrSymbol);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case 'check_portfolio': {
|
|
317
|
+
result = await (0, checkPortfolio_1.checkPortfolio)(args.chainName, args.address);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
case 'create_limit_order': {
|
|
321
|
+
if (config.permissions?.web3?.allow_swap === false) {
|
|
322
|
+
result = `[Security Blocked] Runtime Permission Denied: Limit orders require swap permissions. Update config.yaml to allow.`;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
result = limitOrderManager_1.limitOrderManager.createOrder(args.chainName, args.fromToken, args.toToken, args.amountStr, args.targetPriceUsd, args.condition);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case 'list_limit_orders': {
|
|
329
|
+
result = limitOrderManager_1.limitOrderManager.listOrders();
|
|
330
|
+
break;
|
|
306
331
|
}
|
|
307
|
-
|
|
308
|
-
result =
|
|
332
|
+
case 'cancel_limit_order': {
|
|
333
|
+
result = limitOrderManager_1.limitOrderManager.cancelOrder(args.id);
|
|
334
|
+
break;
|
|
309
335
|
}
|
|
310
|
-
|
|
336
|
+
case 'update_profile': {
|
|
337
|
+
result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case 'update_security_policy': {
|
|
341
|
+
result = (0, updateSecurityPolicy_1.updateSecurityPolicy)(args.rule, args.action);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case 'read_local_file': {
|
|
345
|
+
result = (0, readFile_1.readLocalFile)(args.filePath);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case 'write_local_file': {
|
|
349
|
+
if (config.permissions?.system?.allow_file_write === false) {
|
|
350
|
+
result = `[Security Blocked] Runtime Permission Denied: File writing is disabled. Update config.yaml to allow.`;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
result = (0, writeFile_1.writeLocalFile)(args.filePath, args.content);
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case 'run_terminal_command': {
|
|
357
|
+
if (config.permissions?.system?.allow_shell_execution === false) {
|
|
358
|
+
result = `[Security Blocked] Runtime Permission Denied: Shell execution is disabled. Update config.yaml to allow.`;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
result = await (0, executeShell_1.runTerminalCommand)(args.command);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case 'browse_website': {
|
|
365
|
+
result = await (0, browseWeb_1.browseWebsite)(args.url);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
case 'install_external_skill': {
|
|
369
|
+
result = await (0, installSkill_1.installExternalSkill)(args.url);
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
default: {
|
|
373
|
+
const externalResult = await pluginManager_1.pluginManager.executeTool(toolName, args);
|
|
374
|
+
if (externalResult !== null) {
|
|
375
|
+
result = externalResult;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
result = `Error: Tool ${toolName} is not implemented.`;
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (result.includes('[Security Blocked]') || result.startsWith('Error:')) {
|
|
384
|
+
console.log(picocolors_1.default.red(`[❌ Gagal] Tool ${toolName} mengembalikan error atau diblokir.`));
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
console.log(picocolors_1.default.green(`[✅ Sukses] Tool ${toolName} berhasil dieksekusi.`));
|
|
311
388
|
}
|
|
312
389
|
}
|
|
390
|
+
catch (toolError) {
|
|
391
|
+
result = `Error executing ${toolName}: ${toolError.message}`;
|
|
392
|
+
console.log(picocolors_1.default.red(`[❌ Error Crash] Eksekusi ${toolName} gagal total: ${toolError.message}`));
|
|
393
|
+
}
|
|
313
394
|
exports.logger.addEntry({
|
|
314
395
|
role: 'tool',
|
|
315
396
|
tool_call_id: toolCall.id,
|
|
@@ -336,10 +417,11 @@ async function processUserInput(input, role = 'user') {
|
|
|
336
417
|
return msg;
|
|
337
418
|
})
|
|
338
419
|
];
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
420
|
+
const secondResponse = await executeWithRetry(async (client) => {
|
|
421
|
+
return await client.chat.completions.create({
|
|
422
|
+
model: config.llm.model,
|
|
423
|
+
messages: secondMessages,
|
|
424
|
+
});
|
|
343
425
|
});
|
|
344
426
|
if (secondResponse.usage?.total_tokens) {
|
|
345
427
|
tracker_1.Tracker.addTokens(secondResponse.usage.total_tokens, config.llm.provider);
|
package/dist/config/parser.js
CHANGED
|
@@ -30,6 +30,10 @@ function loadConfig() {
|
|
|
30
30
|
web3: { rpc_urls: {} },
|
|
31
31
|
integrations: {
|
|
32
32
|
telegram: { enabled: false }
|
|
33
|
+
},
|
|
34
|
+
permissions: {
|
|
35
|
+
web3: { allow_transfer: false, allow_swap: true, max_usd_per_tx: 50 },
|
|
36
|
+
system: { allow_shell_execution: false, allow_file_write: false }
|
|
33
37
|
}
|
|
34
38
|
};
|
|
35
39
|
}
|
package/dist/gateway/setup.js
CHANGED
|
@@ -11,6 +11,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
11
11
|
const paths_1 = require("../config/paths");
|
|
12
12
|
const parser_1 = require("../config/parser");
|
|
13
13
|
const crypto_1 = require("../utils/crypto");
|
|
14
|
+
const accounts_1 = require("viem/accounts");
|
|
14
15
|
async function runSetupWizard() {
|
|
15
16
|
console.clear();
|
|
16
17
|
const logo = `
|
|
@@ -76,12 +77,52 @@ Provider: ${config.llm.provider}`;
|
|
|
76
77
|
if ((0, prompts_1.isCancel)(provider))
|
|
77
78
|
return process.exit(0);
|
|
78
79
|
// 2. Model Name
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
let modelOptions = [];
|
|
81
|
+
if (provider === 'gemini') {
|
|
82
|
+
modelOptions = [
|
|
83
|
+
{ value: 'gemini-2.5-flash', label: 'gemini-2.5-flash (Fast & Cheap)' },
|
|
84
|
+
{ value: 'gemini-2.5-pro', label: 'gemini-2.5-pro (Advanced Reasoning)' },
|
|
85
|
+
{ value: 'gemini-1.5-pro', label: 'gemini-1.5-pro' },
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
else if (provider === 'openai') {
|
|
89
|
+
modelOptions = [
|
|
90
|
+
{ value: 'gpt-4o', label: 'gpt-4o (Powerful)' },
|
|
91
|
+
{ value: 'gpt-4o-mini', label: 'gpt-4o-mini (Fast)' },
|
|
92
|
+
{ value: 'o1-preview', label: 'o1-preview (Reasoning)' },
|
|
93
|
+
{ value: 'o1-mini', label: 'o1-mini' },
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
else if (provider === 'openrouter') {
|
|
97
|
+
modelOptions = [
|
|
98
|
+
{ value: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet' },
|
|
99
|
+
{ value: 'meta-llama/llama-3.1-70b-instruct', label: 'Llama 3.1 70B' },
|
|
100
|
+
{ value: 'liquid/lfm-40b', label: 'Liquid LFM 40B' },
|
|
101
|
+
{ value: 'google/gemini-pro-1.5', label: 'Gemini Pro 1.5' },
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
modelOptions = [
|
|
106
|
+
{ value: 'llama3', label: 'Llama 3 (8B)' },
|
|
107
|
+
{ value: 'qwen2', label: 'Qwen 2' },
|
|
108
|
+
{ value: 'phi3', label: 'Phi-3' },
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
modelOptions.push({ value: 'custom', label: 'Type manually (Custom Model)' });
|
|
112
|
+
let model = (await (0, prompts_1.select)({
|
|
113
|
+
message: 'Select AI Model:',
|
|
114
|
+
options: modelOptions,
|
|
115
|
+
}));
|
|
83
116
|
if ((0, prompts_1.isCancel)(model))
|
|
84
117
|
return process.exit(0);
|
|
118
|
+
if (model === 'custom') {
|
|
119
|
+
model = (await (0, prompts_1.text)({
|
|
120
|
+
message: 'Enter custom model name (e.g., deepseek-coder, llama-3-8b-instruct):',
|
|
121
|
+
initialValue: config.llm.model,
|
|
122
|
+
}));
|
|
123
|
+
if ((0, prompts_1.isCancel)(model))
|
|
124
|
+
return process.exit(0);
|
|
125
|
+
}
|
|
85
126
|
// 3. API Key for LLM (Saved to config.yaml)
|
|
86
127
|
let apiKey = '';
|
|
87
128
|
if (provider !== 'ollama') {
|
|
@@ -121,19 +162,44 @@ Provider: ${config.llm.provider}`;
|
|
|
121
162
|
if ((0, prompts_1.isCancel)(telegramToken))
|
|
122
163
|
return process.exit(0);
|
|
123
164
|
}
|
|
124
|
-
// 6. Wallet
|
|
125
|
-
const
|
|
126
|
-
message: '
|
|
165
|
+
// 6. Wallet Setup
|
|
166
|
+
const walletSetupType = await (0, prompts_1.select)({
|
|
167
|
+
message: 'Web3 Wallet Setup:',
|
|
168
|
+
options: [
|
|
169
|
+
{ value: 'skip', label: 'Skip for now (No Web3 execution)' },
|
|
170
|
+
{ value: 'generate', label: 'Auto-Generate New Wallet (Recommended for testing)' },
|
|
171
|
+
{ value: 'manual', label: 'Input Manual Private Key' },
|
|
172
|
+
],
|
|
127
173
|
});
|
|
128
|
-
if ((0, prompts_1.isCancel)(
|
|
174
|
+
if ((0, prompts_1.isCancel)(walletSetupType))
|
|
129
175
|
return process.exit(0);
|
|
176
|
+
let privateKey = '';
|
|
177
|
+
if (walletSetupType === 'manual') {
|
|
178
|
+
privateKey = (await (0, prompts_1.password)({
|
|
179
|
+
message: 'Enter Wallet Private Key (0x...)\n (Will be AES-256-GCM encrypted. See documentation for import guides):',
|
|
180
|
+
}));
|
|
181
|
+
if ((0, prompts_1.isCancel)(privateKey))
|
|
182
|
+
return process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
else if (walletSetupType === 'generate') {
|
|
185
|
+
privateKey = (0, accounts_1.generatePrivateKey)();
|
|
186
|
+
const account = (0, accounts_1.privateKeyToAccount)(privateKey);
|
|
187
|
+
(0, prompts_1.note)(`New Wallet Generated!\nAddress: ${account.address}\n\nIMPORTANT: Backup this address. The Private Key is securely injected into your local vault.`, 'Wallet Created');
|
|
188
|
+
}
|
|
130
189
|
let masterPassword = '';
|
|
131
190
|
if (privateKey) {
|
|
132
191
|
masterPassword = (await (0, prompts_1.password)({
|
|
133
|
-
message: 'Enter MASTER PASSWORD to encrypt your key vault:',
|
|
192
|
+
message: 'Enter a strong MASTER PASSWORD to encrypt your key vault:',
|
|
134
193
|
}));
|
|
135
194
|
if ((0, prompts_1.isCancel)(masterPassword) || !masterPassword)
|
|
136
195
|
return process.exit(0);
|
|
196
|
+
const masterPasswordConfirm = (await (0, prompts_1.password)({
|
|
197
|
+
message: 'Confirm MASTER PASSWORD:',
|
|
198
|
+
}));
|
|
199
|
+
if ((0, prompts_1.isCancel)(masterPasswordConfirm) || masterPassword !== masterPasswordConfirm) {
|
|
200
|
+
console.log(picocolors_1.default.red('❌ Passwords do not match. Setup cancelled.'));
|
|
201
|
+
return process.exit(1);
|
|
202
|
+
}
|
|
137
203
|
}
|
|
138
204
|
// --- SAVING ---
|
|
139
205
|
// Update Config.yaml
|
package/dist/gateway/telegram.js
CHANGED
|
@@ -38,8 +38,25 @@ function startTelegramBot() {
|
|
|
38
38
|
// Send typing action to Telegram
|
|
39
39
|
bot.sendChatAction(chatId, 'typing');
|
|
40
40
|
try {
|
|
41
|
+
let progressMsgId = null;
|
|
42
|
+
const onProgress = async (progressText) => {
|
|
43
|
+
try {
|
|
44
|
+
if (!progressMsgId) {
|
|
45
|
+
const sent = await bot.sendMessage(chatId, progressText, { parse_mode: 'Markdown' });
|
|
46
|
+
progressMsgId = sent.message_id;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
await bot.editMessageText(progressText, { chat_id: chatId, message_id: progressMsgId, parse_mode: 'Markdown' });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e) { }
|
|
53
|
+
};
|
|
41
54
|
// Feed the message to the AI agent
|
|
42
|
-
const response = await (0, reasoning_1.processUserInput)(text);
|
|
55
|
+
const response = await (0, reasoning_1.processUserInput)(text, 'user', onProgress);
|
|
56
|
+
if (progressMsgId) {
|
|
57
|
+
// Clean up the progress message
|
|
58
|
+
bot.deleteMessage(chatId, progressMsgId).catch(() => { });
|
|
59
|
+
}
|
|
43
60
|
// Send the AI's response back to Telegram
|
|
44
61
|
// Check for newly created pending transactions
|
|
45
62
|
const pendingTxs = transactionManager_1.txManager.getPending();
|
package/dist/memory/logger.js
CHANGED
|
@@ -5,46 +5,105 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Logger = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
10
|
const parser_1 = require("../config/parser");
|
|
9
11
|
const paths_1 = require("../config/paths");
|
|
10
12
|
class Logger {
|
|
11
|
-
|
|
12
|
-
memory = [];
|
|
13
|
+
db;
|
|
13
14
|
constructor() {
|
|
14
15
|
const config = (0, parser_1.loadConfig)();
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
let dbPath = config.memory?.path || 'memory.db';
|
|
17
|
+
if (dbPath.endsWith('.json')) {
|
|
18
|
+
dbPath = dbPath.replace('.json', '.db');
|
|
19
|
+
}
|
|
20
|
+
const fullPath = (0, paths_1.getPath)(dbPath);
|
|
21
|
+
// Ensure directory exists
|
|
22
|
+
const dir = path_1.default.dirname(fullPath);
|
|
23
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
24
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
this.db = new better_sqlite3_1.default(fullPath);
|
|
27
|
+
this.initDb();
|
|
17
28
|
}
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
initDb() {
|
|
30
|
+
this.db.exec(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
32
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
33
|
+
role TEXT NOT NULL,
|
|
34
|
+
content TEXT NOT NULL,
|
|
35
|
+
name TEXT,
|
|
36
|
+
tool_call_id TEXT,
|
|
37
|
+
tool_calls TEXT,
|
|
38
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
39
|
+
)
|
|
40
|
+
`);
|
|
41
|
+
// Migration logic from old memory.json to SQLite
|
|
42
|
+
const config = (0, parser_1.loadConfig)();
|
|
43
|
+
const oldJsonPath = (0, paths_1.getPath)(config.memory?.path || 'memory.json');
|
|
44
|
+
const countRow = this.db.prepare('SELECT COUNT(*) as count FROM messages').get();
|
|
45
|
+
if (countRow.count === 0 && fs_1.default.existsSync(oldJsonPath)) {
|
|
20
46
|
try {
|
|
21
|
-
const data = fs_1.default.readFileSync(
|
|
22
|
-
|
|
47
|
+
const data = fs_1.default.readFileSync(oldJsonPath, 'utf-8');
|
|
48
|
+
const oldMemory = JSON.parse(data);
|
|
49
|
+
if (Array.isArray(oldMemory) && oldMemory.length > 0) {
|
|
50
|
+
const insert = this.db.prepare(`
|
|
51
|
+
INSERT INTO messages (role, content, name, tool_call_id, tool_calls)
|
|
52
|
+
VALUES (@role, @content, @name, @tool_call_id, @tool_calls)
|
|
53
|
+
`);
|
|
54
|
+
const insertMany = this.db.transaction((entries) => {
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
insert.run({
|
|
57
|
+
role: entry.role,
|
|
58
|
+
content: entry.content || '',
|
|
59
|
+
name: entry.name || null,
|
|
60
|
+
tool_call_id: entry.tool_call_id || null,
|
|
61
|
+
tool_calls: entry.tool_calls ? JSON.stringify(entry.tool_calls) : null
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
insertMany(oldMemory);
|
|
66
|
+
console.log('[Nyxora Memory] Successfully migrated memory.json to SQLite database (Atomic Storage).');
|
|
67
|
+
// Rename old file to prevent re-migration issues and keep as backup
|
|
68
|
+
fs_1.default.renameSync(oldJsonPath, oldJsonPath + '.bak');
|
|
69
|
+
}
|
|
23
70
|
}
|
|
24
71
|
catch (error) {
|
|
25
|
-
console.error('Failed to
|
|
26
|
-
this.memory = [];
|
|
72
|
+
console.error('[Nyxora Memory] Failed to migrate old memory.json:', error);
|
|
27
73
|
}
|
|
28
74
|
}
|
|
29
75
|
}
|
|
30
|
-
saveMemory() {
|
|
31
|
-
try {
|
|
32
|
-
fs_1.default.writeFileSync(this.logFilePath, JSON.stringify(this.memory, null, 2));
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.error('Failed to write memory file.');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
76
|
getHistory() {
|
|
39
|
-
|
|
77
|
+
const rows = this.db.prepare('SELECT role, content, name, tool_call_id, tool_calls FROM messages ORDER BY id ASC').all();
|
|
78
|
+
return rows.map((row) => {
|
|
79
|
+
const entry = {
|
|
80
|
+
role: row.role,
|
|
81
|
+
content: row.content,
|
|
82
|
+
};
|
|
83
|
+
if (row.name)
|
|
84
|
+
entry.name = row.name;
|
|
85
|
+
if (row.tool_call_id)
|
|
86
|
+
entry.tool_call_id = row.tool_call_id;
|
|
87
|
+
if (row.tool_calls)
|
|
88
|
+
entry.tool_calls = JSON.parse(row.tool_calls);
|
|
89
|
+
return entry;
|
|
90
|
+
});
|
|
40
91
|
}
|
|
41
92
|
addEntry(entry) {
|
|
42
|
-
this.
|
|
43
|
-
|
|
93
|
+
const insert = this.db.prepare(`
|
|
94
|
+
INSERT INTO messages (role, content, name, tool_call_id, tool_calls)
|
|
95
|
+
VALUES (@role, @content, @name, @tool_call_id, @tool_calls)
|
|
96
|
+
`);
|
|
97
|
+
insert.run({
|
|
98
|
+
role: entry.role,
|
|
99
|
+
content: entry.content || '',
|
|
100
|
+
name: entry.name || null,
|
|
101
|
+
tool_call_id: entry.tool_call_id || null,
|
|
102
|
+
tool_calls: entry.tool_calls ? JSON.stringify(entry.tool_calls) : null
|
|
103
|
+
});
|
|
44
104
|
}
|
|
45
105
|
clear() {
|
|
46
|
-
this.
|
|
47
|
-
this.saveMemory();
|
|
106
|
+
this.db.prepare('DELETE FROM messages').run();
|
|
48
107
|
}
|
|
49
108
|
}
|
|
50
109
|
exports.Logger = Logger;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/gateway/cli.js",
|
|
6
6
|
"files": [
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"type": "commonjs",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@clack/prompts": "^1.4.0",
|
|
35
|
+
"better-sqlite3": "^12.10.0",
|
|
35
36
|
"concurrently": "^9.2.1",
|
|
36
37
|
"cors": "^2.8.6",
|
|
37
38
|
"express": "^5.2.1",
|
|
@@ -43,11 +44,14 @@
|
|
|
43
44
|
"yaml": "^2.9.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
47
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
46
48
|
"@types/cors": "^2.8.19",
|
|
47
49
|
"@types/express": "^5.0.6",
|
|
48
50
|
"@types/node": "^25.9.1",
|
|
49
51
|
"@types/node-telegram-bot-api": "^0.64.14",
|
|
50
52
|
"ts-node": "^10.9.2",
|
|
51
|
-
"typescript": "^6.0.3"
|
|
53
|
+
"typescript": "^6.0.3",
|
|
54
|
+
"vitepress": "^1.6.4",
|
|
55
|
+
"vue": "^3.5.35"
|
|
52
56
|
}
|
|
53
57
|
}
|