nyxora 26.6.6 → 26.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 +22 -0
- package/README.md +16 -2
- package/bin/nyxora.mjs +32 -0
- package/dist/packages/core/src/agent/reasoning.js +11 -6
- package/dist/packages/core/src/gateway/cli.js +75 -0
- package/dist/packages/core/src/gateway/doctor.js +131 -0
- package/dist/packages/core/src/gateway/googleAuthModule.js +1 -1
- package/dist/packages/core/src/gateway/server.js +171 -1
- package/dist/packages/core/src/gateway/setup.js +8 -3
- package/dist/packages/core/src/web3/config.js +3 -3
- package/dist/packages/core/src/web3/skills/bridgeToken.js +3 -1
- package/dist/packages/core/src/web3/skills/manageCustomTokens.js +82 -0
- package/dist/packages/core/src/web3/skills/swapToken.js +5 -2
- package/dist/packages/core/src/web3/utils/tokens.js +1 -1
- package/package.json +13 -2
- package/packages/core/package.json +2 -1
- package/packages/core/src/agent/reasoning.ts +11 -6
- package/packages/core/src/gateway/cli.ts +42 -1
- package/packages/core/src/gateway/doctor.ts +126 -0
- package/packages/core/src/gateway/googleAuthModule.ts +1 -1
- package/packages/core/src/gateway/server.ts +179 -1
- package/packages/core/src/gateway/setup.ts +10 -5
- package/packages/core/src/web3/config.ts +4 -3
- package/packages/core/src/web3/skills/bridgeToken.ts +3 -1
- package/packages/core/src/web3/skills/manageCustomTokens.ts +81 -0
- package/packages/core/src/web3/skills/swapToken.ts +5 -2
- package/packages/core/src/web3/utils/tokens.ts +1 -1
- package/packages/dashboard/dist/assets/index-whRRjJKK.js +306 -0
- package/packages/dashboard/dist/index.html +1 -1
- package/packages/dashboard/package.json +2 -2
- package/packages/dashboard/dist/assets/index-DWxWzOS7.js +0 -306
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepashangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [26.6.7] - Unreleased
|
|
9
|
+
### Enterprise Features & Web3 Enhancements
|
|
10
|
+
- **Enterprise Portfolio Scanner**: Integrated a fully decentralized, real-time Dashboard UI (Nord Theme) to scan all native and ERC-20 token balances across 8 EVM chains natively, without relying on centralized third-party APIs.
|
|
11
|
+
- **Real-Time USD Valuation**: Integrated DexScreener API into the Portfolio Scanner backend to actively compute and display USD portfolio values in real-time. Features an adaptive 2-minute memory cache system to ensure complete immunity against API rate-limits and eliminate LLM token consumption.
|
|
12
|
+
- **Official Web3 Branding**: Integrated TrustWallet and CovalentHQ CDNs to automatically resolve and render official Native Chain icons and ERC-20 Token logos with dynamic address casing, delivering an authentic Tier-1 exchange aesthetic.
|
|
13
|
+
- **Custom Token Management (AI Skill)**: Deployed the new `manage_custom_tokens` Web3 skill. The AI agent can now autonomously recognize, store, and manage user-specified custom token addresses (e.g., obscure/degen tokens) to `~/.nyxora/custom_tokens.json`. These are instantly synced with the Portfolio Scanner.
|
|
14
|
+
- **MEV-Blocker Integration**: Upgraded the Ethereum mainnet routing core in `config.ts` to strictly prioritize `rpc.mevblocker.io` and `rpc.flashbots.net` as primary transports. All user transactions are now forcefully routed through Private Mempools, establishing complete immunity against sandwich attacks and front-running bots.
|
|
15
|
+
- **System Diagnosis (`nyxora doctor`)**: Added a new CLI tool `nyxora doctor` to automatically verify OS requirements, node versions, filesystem permissions, SQLite database r/w access, Keyring vault security, and network port availability for seamless troubleshooting.
|
|
16
|
+
|
|
17
|
+
### Security & UX Updates
|
|
18
|
+
- **CLI Wallet Management (`nyxora wallet update`)**: Added a highly requested sub-command to allow users to securely overwrite their OS Keyring Web3 wallet directly via the CLI without having to re-run the full LLM setup wizard. Features an aggressive visual confirmation step to prevent accidental Private Key destruction.
|
|
19
|
+
- **Terminal UI Resilience**: Replaced dynamic `note()` text rendering with static linear console logs in the `nyxora setup` wizard. This completely eliminates a UI truncation bug where the `clack` prompter would swallow the 12-word mnemonic phrase on small terminal windows.
|
|
20
|
+
- **Helmet CSP Optimization**: Adjusted the Gateway Server's Content Security Policy (CSP) to securely whitelist decentralized image repositories (GitHub raw, CovalentHQ) without compromising strict anti-XSS protection protocols.
|
|
21
|
+
- **BIP-39 Mnemonic Generation**: Upgraded the `nyxora setup` CLI wizard. When auto-generating a new wallet, the system now provides a standard 12-word Seed Phrase (Mnemonic) instead of a raw hex Private Key, vastly improving user security and cross-wallet compatibility (e.g., MetaMask). The private key is still autonomously extracted and locked in the OS Keyring.
|
|
22
|
+
- **One-Liner Install Script**: Added a new hacker-style `curl | bash` installation method at `https://nyxoraai.github.io/Nyxora/install.sh` for Linux/macOS, and a native PowerShell script `install.ps1` for Windows, providing an instant, frictionless setup experience across all major operating systems.
|
|
23
|
+
- **Global Localization Standardization**: Swept and translated the entire AI reasoning log engine (`reasoning.ts`) from Indonesian to standardized English to maintain strict international professional standards across console output and UI feedback.
|
|
24
|
+
|
|
25
|
+
### Bug Fixes & Optimizations
|
|
26
|
+
- **ERC-20 Decimals Resolution**: Completely eradicated a critical math bug where all custom tokens were assumed to have 18 decimals. The backend now executes parallel `decimals()` on-chain queries alongside `balanceOf()`, guaranteeing 100% mathematical precision for tokens like USDC/USDT (6 decimals).
|
|
27
|
+
- **NPM Monorepo Build Fix**: Fixed the `packages/core` workspace `package.json` to correctly include the `"build": "tsc"` script and aligned its internal versioning (`v26.6.7`). This resolves the NPM workspace lifecycle crash during global build triggers.
|
|
28
|
+
- **NPM Optimization**: Added official keywords (`web3`, `ai`, `agent`, `crypto`, `mcp`, `automation`, `defi`, `zero-trust`) to the root `package.json` to significantly improve Nyxora's discoverability and SEO on the NPM Registry.
|
|
29
|
+
|
|
8
30
|
## [26.6.6] - 2026-06-05
|
|
9
31
|
### Enterprise Stability Upgrades
|
|
10
32
|
- **Strict LLM Output Validation**: Added robust try-catch parsing for LLM tool arguments in `reasoning.ts`. If the AI outputs malformed JSON, the error is fed back into the reasoning loop, allowing the model to autonomously self-correct without crashing the agent pipeline.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Nyxora Agent
|
|
1
|
+
# Nyxora Agent <img src="./packages/dashboard/public/favicon.svg" width="36" align="top" />
|
|
2
2
|
**Your Personal Web3 Assistant.**
|
|
3
3
|
|
|
4
4
|
|
|
@@ -75,9 +75,23 @@ To dive deeper into the technical details of our Zero-Knowledge security archite
|
|
|
75
75
|
### Global Installation via NPM (Recommended)
|
|
76
76
|
The easiest and fastest way to use Nyxora is to install it globally via NPM. This ensures you get the latest version and can run Nyxora from anywhere on your machine.
|
|
77
77
|
|
|
78
|
+
The fastest way to install Nyxora is via our automated installation script:
|
|
79
|
+
|
|
80
|
+
**For Linux & macOS (Bash):**
|
|
81
|
+
```bash
|
|
82
|
+
curl -fsSL https://nyxoraai.github.io/Nyxora/install.sh | bash
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**For Windows (PowerShell):**
|
|
86
|
+
```powershell
|
|
87
|
+
iwr https://nyxoraai.github.io/Nyxora/install.ps1 -useb | iex
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Alternatively, you can install it manually on any operating system using NPM:
|
|
91
|
+
|
|
78
92
|
```bash
|
|
79
|
-
# 1. Install Nyxora globally
|
|
80
93
|
npm install -g nyxora@latest
|
|
94
|
+
```
|
|
81
95
|
|
|
82
96
|
# 2. Run the Interactive Setup Wizard (API Keys, Wallet, Telegram)
|
|
83
97
|
nyxora setup
|
package/bin/nyxora.mjs
CHANGED
|
@@ -247,11 +247,41 @@ async function setKey(cliArgs) {
|
|
|
247
247
|
await new Promise(resolve => child.on('close', resolve));
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
async function wallet(cliArgs) {
|
|
251
|
+
const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/cli.js');
|
|
252
|
+
const useCompiled = fs.existsSync(compiledCli);
|
|
253
|
+
const cmd = useCompiled ? 'node' : 'npx';
|
|
254
|
+
const args = useCompiled ? [compiledCli, 'wallet', ...cliArgs] : ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'wallet', ...cliArgs];
|
|
255
|
+
const child = spawn(cmd, args, {
|
|
256
|
+
cwd: projectRoot,
|
|
257
|
+
stdio: 'inherit',
|
|
258
|
+
env: { ...process.env, TS_NODE_CACHE: 'false' }
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await new Promise(resolve => child.on('close', resolve));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function runDoctor() {
|
|
265
|
+
const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/doctor.js');
|
|
266
|
+
const useCompiled = fs.existsSync(compiledCli);
|
|
267
|
+
const cmd = useCompiled ? 'node' : 'npx';
|
|
268
|
+
const args = useCompiled ? [compiledCli] : ['ts-node', '-T', 'packages/core/src/gateway/doctor.ts'];
|
|
269
|
+
const child = spawn(cmd, args, {
|
|
270
|
+
cwd: projectRoot,
|
|
271
|
+
stdio: 'inherit',
|
|
272
|
+
env: { ...process.env, TS_NODE_CACHE: 'false' }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
await new Promise(resolve => child.on('close', resolve));
|
|
276
|
+
}
|
|
277
|
+
|
|
250
278
|
async function main() {
|
|
251
279
|
switch (command) {
|
|
280
|
+
case 'doctor': await runDoctor(); break;
|
|
252
281
|
case 'setup': await setup(); break;
|
|
253
282
|
case 'clear': await clearMemory(process.argv.slice(3)); break;
|
|
254
283
|
case 'set-key': await setKey(process.argv.slice(3)); break;
|
|
284
|
+
case 'wallet': await wallet(process.argv.slice(3)); break;
|
|
255
285
|
case 'start': await start(); break;
|
|
256
286
|
case 'stop': await stop(); break;
|
|
257
287
|
case 'restart': await restart(); break;
|
|
@@ -277,10 +307,12 @@ Commands:
|
|
|
277
307
|
restart Restart the daemon
|
|
278
308
|
setup Run the interactive Setup Wizard
|
|
279
309
|
dashboard Open the dashboard in your browser
|
|
310
|
+
doctor Run system diagnostics and check requirements
|
|
280
311
|
clear Atomically clear the AI's short/long-term memory SQLite database
|
|
281
312
|
clean-logs Clear the daemon logs
|
|
282
313
|
autostart Enable/disable autostart on boot (usage: nyxora autostart enable)
|
|
283
314
|
set-key Securely save API Key (usage: nyxora set-key <provider> <key>)
|
|
315
|
+
wallet Manage your Web3 Wallet (usage: nyxora wallet update)
|
|
284
316
|
|
|
285
317
|
Options:
|
|
286
318
|
-v, --version Show current version
|
|
@@ -24,6 +24,7 @@ const marketAnalysis_1 = require("../web3/skills/marketAnalysis");
|
|
|
24
24
|
const checkPortfolio_1 = require("../web3/skills/checkPortfolio");
|
|
25
25
|
const checkAddress_1 = require("../web3/skills/checkAddress");
|
|
26
26
|
const getMyAddress_1 = require("../web3/skills/getMyAddress");
|
|
27
|
+
const manageCustomTokens_1 = require("../web3/skills/manageCustomTokens");
|
|
27
28
|
const limitOrderManager_1 = require("./limitOrderManager");
|
|
28
29
|
const updateProfile_1 = require("./updateProfile");
|
|
29
30
|
const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
|
|
@@ -207,7 +208,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
207
208
|
const lowerInput = input.toLowerCase();
|
|
208
209
|
const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
|
|
209
210
|
const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
|
|
210
|
-
const WEB3_TOOLS = [getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, createWallet_1.createWalletToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, limitOrderManager_1.createLimitOrderToolDefinition, limitOrderManager_1.listLimitOrdersToolDefinition, limitOrderManager_1.cancelLimitOrderToolDefinition];
|
|
211
|
+
const WEB3_TOOLS = [getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, createWallet_1.createWalletToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, limitOrderManager_1.createLimitOrderToolDefinition, limitOrderManager_1.listLimitOrdersToolDefinition, limitOrderManager_1.cancelLimitOrderToolDefinition];
|
|
211
212
|
const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, installSkill_1.installExternalSkillToolDefinition];
|
|
212
213
|
const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
|
|
213
214
|
let activeTools = [];
|
|
@@ -251,9 +252,9 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
251
252
|
let result = "";
|
|
252
253
|
let args = {};
|
|
253
254
|
const toolName = toolCall.function.name;
|
|
254
|
-
console.log(picocolors_1.default.yellow(`[⚡
|
|
255
|
+
console.log(picocolors_1.default.yellow(`[⚡ Tool Execution] AI is calling ${toolName}...`));
|
|
255
256
|
if (onProgress)
|
|
256
|
-
onProgress(`_⚡
|
|
257
|
+
onProgress(`_⚡ Running tool: ${toolName}..._`);
|
|
257
258
|
// Phase 1: LLM Output Validation (Anti-Halusinasi)
|
|
258
259
|
try {
|
|
259
260
|
args = JSON.parse(toolCall.function.arguments);
|
|
@@ -342,6 +343,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
342
343
|
result = await (0, getMyAddress_1.getMyAddress)();
|
|
343
344
|
break;
|
|
344
345
|
}
|
|
346
|
+
case 'manage_custom_tokens': {
|
|
347
|
+
result = await (0, manageCustomTokens_1.executeManageCustomTokens)(args);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
345
350
|
case 'create_limit_order': {
|
|
346
351
|
if (config.permissions?.web3?.allow_swap === false) {
|
|
347
352
|
result = `[Security Blocked] Runtime Permission Denied: Limit orders require swap permissions. Update config.yaml to allow.`;
|
|
@@ -434,15 +439,15 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
434
439
|
}
|
|
435
440
|
}
|
|
436
441
|
if (result.includes('[Security Blocked]') || result.startsWith('Error:')) {
|
|
437
|
-
console.log(picocolors_1.default.red(`[❌
|
|
442
|
+
console.log(picocolors_1.default.red(`[❌ Failed] Tool ${toolName} returned an error or was blocked.`));
|
|
438
443
|
}
|
|
439
444
|
else {
|
|
440
|
-
console.log(picocolors_1.default.green(`[✅
|
|
445
|
+
console.log(picocolors_1.default.green(`[✅ Success] Tool ${toolName} executed successfully.`));
|
|
441
446
|
}
|
|
442
447
|
}
|
|
443
448
|
catch (toolError) {
|
|
444
449
|
result = `Error executing ${toolName}: ${toolError.message}`;
|
|
445
|
-
console.
|
|
450
|
+
console.error(picocolors_1.default.red(`[❌ Error Crash] Execution of ${toolName} failed completely: ${toolError.message}`));
|
|
446
451
|
}
|
|
447
452
|
exports.logger.addEntry({
|
|
448
453
|
role: 'tool',
|
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
38
|
};
|
|
@@ -8,9 +41,11 @@ const safeLogger_1 = require("../utils/safeLogger");
|
|
|
8
41
|
(0, safeLogger_1.initSafeLogger)();
|
|
9
42
|
const fs_1 = __importDefault(require("fs"));
|
|
10
43
|
const path_1 = __importDefault(require("path"));
|
|
44
|
+
const os_1 = __importDefault(require("os"));
|
|
11
45
|
const paths_1 = require("../config/paths");
|
|
12
46
|
const server_1 = require("./server");
|
|
13
47
|
const setup_1 = require("./setup");
|
|
48
|
+
const prompts_1 = require("@clack/prompts");
|
|
14
49
|
const state_1 = require("../utils/state");
|
|
15
50
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
16
51
|
const parser_1 = require("../config/parser");
|
|
@@ -27,6 +62,12 @@ async function main() {
|
|
|
27
62
|
await (0, setup_1.runSetupWizard)();
|
|
28
63
|
process.exit(0);
|
|
29
64
|
}
|
|
65
|
+
// Check for doctor command
|
|
66
|
+
if (process.argv.includes('doctor')) {
|
|
67
|
+
const { runDoctor } = await Promise.resolve().then(() => __importStar(require('./doctor')));
|
|
68
|
+
await runDoctor();
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
30
71
|
// Check for memory clear command
|
|
31
72
|
if (process.argv.includes('clear')) {
|
|
32
73
|
if (process.argv.includes('--force') || process.argv.includes('-y')) {
|
|
@@ -68,6 +109,40 @@ async function main() {
|
|
|
68
109
|
console.log(picocolors_1.default.green(`✅ API Key for ${provider} saved securely to vault.`));
|
|
69
110
|
process.exit(0);
|
|
70
111
|
}
|
|
112
|
+
// Check for wallet command
|
|
113
|
+
if (process.argv.includes('wallet')) {
|
|
114
|
+
if (process.argv.includes('update')) {
|
|
115
|
+
console.log(picocolors_1.default.cyan('\n🔄 Wallet Update Wizard'));
|
|
116
|
+
const proceed = await (0, prompts_1.confirm)({
|
|
117
|
+
message: picocolors_1.default.bgRed(picocolors_1.default.white(' ⚠️ WARNING ')) + picocolors_1.default.yellow(' This will immediately OVERWRITE your existing wallet in the OS Vault.\nIf you have not backed up your current Private Key, you will lose access to its funds forever.\n\nAre you absolutely sure you want to proceed?'),
|
|
118
|
+
});
|
|
119
|
+
if ((0, prompts_1.isCancel)(proceed) || !proceed)
|
|
120
|
+
process.exit(0);
|
|
121
|
+
const pk = await (0, prompts_1.password)({
|
|
122
|
+
message: 'Enter your new Private Key (0x...):',
|
|
123
|
+
});
|
|
124
|
+
if ((0, prompts_1.isCancel)(pk))
|
|
125
|
+
process.exit(0);
|
|
126
|
+
try {
|
|
127
|
+
const { Entry } = await Promise.resolve().then(() => __importStar(require('@napi-rs/keyring')));
|
|
128
|
+
const entry = new Entry('nyxora', 'wallet');
|
|
129
|
+
await entry.setPassword(pk);
|
|
130
|
+
console.log(picocolors_1.default.green('✅ Wallet updated securely in OS Native Vault.'));
|
|
131
|
+
console.log(picocolors_1.default.yellow('⚠️ Please restart your Nyxora agent for the new wallet to take effect.\n'));
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
const vaultPath = path_1.default.join(os_1.default.homedir(), '.nyxora', 'vault.key');
|
|
135
|
+
fs_1.default.writeFileSync(vaultPath, `PRIVATE_KEY=${pk}\n`, { mode: 0o600 });
|
|
136
|
+
console.log(picocolors_1.default.green('✅ Wallet updated securely in fallback vault.key.'));
|
|
137
|
+
console.log(picocolors_1.default.yellow('⚠️ Please restart your Nyxora agent for the new wallet to take effect.\n'));
|
|
138
|
+
}
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.error(picocolors_1.default.red('Usage: nyxora wallet update'));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
71
146
|
// 2. Setup boilerplate files if in global mode and they don't exist
|
|
72
147
|
let isFirstBoot = false;
|
|
73
148
|
if (isGlobalMode) {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runDoctor = runDoctor;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const net_1 = __importDefault(require("net"));
|
|
10
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
11
|
+
const paths_1 = require("../config/paths");
|
|
12
|
+
const parser_1 = require("../config/parser");
|
|
13
|
+
async function runDoctor() {
|
|
14
|
+
console.log(picocolors_1.default.cyan('\n🔍 Nyxora System Doctor\n'));
|
|
15
|
+
let allGood = true;
|
|
16
|
+
const printStatus = (name, status, errorMsg) => {
|
|
17
|
+
if (status) {
|
|
18
|
+
console.log(`${picocolors_1.default.green('✓')} ${name}`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
console.log(`${picocolors_1.default.red('✗')} ${name}`);
|
|
22
|
+
if (errorMsg)
|
|
23
|
+
console.log(` ${picocolors_1.default.gray(errorMsg)}`);
|
|
24
|
+
allGood = false;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
// 1. Check Node Version
|
|
28
|
+
const nodeVer = process.versions.node;
|
|
29
|
+
const majorVer = parseInt(nodeVer.split('.')[0], 10);
|
|
30
|
+
printStatus(`Node.js Version (${nodeVer})`, majorVer >= 22, `Please upgrade to Node.js v22 or higher.`);
|
|
31
|
+
// 2. Check App Directory & Config
|
|
32
|
+
const appDir = (0, paths_1.getAppDir)();
|
|
33
|
+
const configPath = path_1.default.join(appDir, 'config.yaml');
|
|
34
|
+
let configOk = false;
|
|
35
|
+
let configErr = '';
|
|
36
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
37
|
+
try {
|
|
38
|
+
(0, parser_1.loadConfig)();
|
|
39
|
+
configOk = true;
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
configErr = `Invalid YAML syntax: ${e.message}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
configErr = `config.yaml not found at ${configPath}`;
|
|
47
|
+
}
|
|
48
|
+
printStatus(`Configuration File`, configOk, configErr);
|
|
49
|
+
// 3. Check SQLite DB access
|
|
50
|
+
const dbPath = path_1.default.join(appDir, 'memory.db');
|
|
51
|
+
let dbOk = false;
|
|
52
|
+
let dbErr = '';
|
|
53
|
+
try {
|
|
54
|
+
if (fs_1.default.existsSync(dbPath)) {
|
|
55
|
+
fs_1.default.accessSync(dbPath, fs_1.default.constants.R_OK | fs_1.default.constants.W_OK);
|
|
56
|
+
dbOk = true;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
dbOk = true; // Not created yet, which is fine
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
dbErr = `Cannot read/write memory.db: ${e.message}`;
|
|
64
|
+
}
|
|
65
|
+
printStatus(`SQLite Database Permissions`, dbOk, dbErr);
|
|
66
|
+
// 4. Check OS Keyring
|
|
67
|
+
let keyringOk = false;
|
|
68
|
+
let keyringErr = '';
|
|
69
|
+
try {
|
|
70
|
+
const { getPassword } = require('@napi-rs/keyring');
|
|
71
|
+
// Just try to access it. If it throws native error, keyring is inaccessible
|
|
72
|
+
try {
|
|
73
|
+
getPassword('nyxora', 'test_ping_doctor');
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
// It's normal to throw "The specified item could not be found in the keychain"
|
|
77
|
+
// But if it crashes or throws permission error, it's bad.
|
|
78
|
+
}
|
|
79
|
+
keyringOk = true;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
keyringErr = `OS Vault inaccessible: ${e.message}. Ensure libsecret is installed on Linux.`;
|
|
83
|
+
}
|
|
84
|
+
printStatus(`OS Native Vault (Keyring)`, keyringOk, keyringErr);
|
|
85
|
+
// 5. Check Ports
|
|
86
|
+
const checkPort = (port) => {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const server = net_1.default.createServer();
|
|
89
|
+
server.unref();
|
|
90
|
+
server.on('error', () => resolve(false)); // Port in use
|
|
91
|
+
server.listen(port, () => {
|
|
92
|
+
server.close(() => resolve(true)); // Port free
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const port3000Free = await checkPort(3000);
|
|
97
|
+
const port3001Free = await checkPort(3001);
|
|
98
|
+
let isDaemonRunning = false;
|
|
99
|
+
const pidPath = path_1.default.join(appDir, 'daemon.pid');
|
|
100
|
+
if (fs_1.default.existsSync(pidPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const pidStr = fs_1.default.readFileSync(pidPath, 'utf8').trim();
|
|
103
|
+
process.kill(parseInt(pidStr, 10), 0);
|
|
104
|
+
isDaemonRunning = true;
|
|
105
|
+
}
|
|
106
|
+
catch (e) { }
|
|
107
|
+
}
|
|
108
|
+
if (isDaemonRunning && !port3000Free) {
|
|
109
|
+
console.log(`${picocolors_1.default.green('✓')} Port 3000 (Dashboard) ${picocolors_1.default.cyan('[In Use by Nyxora]')}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
printStatus(`Port 3000 (Dashboard)`, port3000Free, `Port is already in use by another application.`);
|
|
113
|
+
}
|
|
114
|
+
if (isDaemonRunning && !port3001Free) {
|
|
115
|
+
console.log(`${picocolors_1.default.green('✓')} Port 3001 (Gateway API) ${picocolors_1.default.cyan('[In Use by Nyxora]')}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
printStatus(`Port 3001 (Gateway API)`, port3001Free, `Port is already in use by another application.`);
|
|
119
|
+
}
|
|
120
|
+
console.log('\n================================');
|
|
121
|
+
if (allGood) {
|
|
122
|
+
console.log(picocolors_1.default.green('🚀 All systems are completely healthy! Nyxora is ready to fly.'));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(picocolors_1.default.yellow('⚠️ Some checks failed. Please fix the issues above for optimal performance.'));
|
|
126
|
+
}
|
|
127
|
+
console.log('================================\n');
|
|
128
|
+
}
|
|
129
|
+
if (require.main === module) {
|
|
130
|
+
runDoctor();
|
|
131
|
+
}
|
|
@@ -37,7 +37,7 @@ async function initGoogleAuth() {
|
|
|
37
37
|
// Check if we already have a refresh token saved
|
|
38
38
|
const refreshToken = await getRefreshToken();
|
|
39
39
|
if (refreshToken) {
|
|
40
|
-
console.log('[Google Auth] Refresh token found in secure storage.');
|
|
40
|
+
// console.log('[Google Auth] Refresh token found in secure storage.'); // Suppressed to avoid CLI prompt disruption
|
|
41
41
|
return true;
|
|
42
42
|
}
|
|
43
43
|
return false;
|
|
@@ -9,9 +9,13 @@ const cors_1 = __importDefault(require("cors"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const helmet_1 = __importDefault(require("helmet"));
|
|
11
11
|
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
12
|
+
const paths_1 = require("../config/paths");
|
|
12
13
|
const state_1 = require("../utils/state");
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
15
|
const reasoning_1 = require("../agent/reasoning");
|
|
14
16
|
const parser_1 = require("../config/parser");
|
|
17
|
+
const config_1 = require("../web3/config");
|
|
18
|
+
const tokens_1 = require("../web3/utils/tokens");
|
|
15
19
|
const tracker_1 = require("./tracker");
|
|
16
20
|
const transactionManager_1 = require("../agent/transactionManager");
|
|
17
21
|
const limitOrderManager_1 = require("../agent/limitOrderManager");
|
|
@@ -21,6 +25,7 @@ const swapToken_1 = require("../web3/skills/swapToken");
|
|
|
21
25
|
const getBalance_1 = require("../web3/skills/getBalance");
|
|
22
26
|
const checkAddress_1 = require("../web3/skills/checkAddress");
|
|
23
27
|
const getMyAddress_1 = require("../web3/skills/getMyAddress");
|
|
28
|
+
const manageCustomTokens_1 = require("../web3/skills/manageCustomTokens");
|
|
24
29
|
const getPrice_1 = require("../web3/skills/getPrice");
|
|
25
30
|
const checkSecurity_1 = require("../web3/skills/checkSecurity");
|
|
26
31
|
const checkPortfolio_1 = require("../web3/skills/checkPortfolio");
|
|
@@ -59,7 +64,17 @@ console.error = function (...args) {
|
|
|
59
64
|
originalError.apply(console, args);
|
|
60
65
|
};
|
|
61
66
|
const app = (0, express_1.default)();
|
|
62
|
-
app.use((0, helmet_1.default)(
|
|
67
|
+
app.use((0, helmet_1.default)({
|
|
68
|
+
contentSecurityPolicy: {
|
|
69
|
+
directives: {
|
|
70
|
+
defaultSrc: ["'self'"],
|
|
71
|
+
imgSrc: ["'self'", 'data:', 'https://raw.githubusercontent.com', 'https://logos.covalenthq.com'],
|
|
72
|
+
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
73
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
74
|
+
connectSrc: ["'self'", 'https://*']
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
63
78
|
app.use((0, cors_1.default)({
|
|
64
79
|
origin: function (origin, callback) {
|
|
65
80
|
if (!origin || /^(http:\/\/(localhost|127\.0\.0\.1):\d+)$/.test(origin)) {
|
|
@@ -208,6 +223,7 @@ app.get('/api/skills', (req, res) => {
|
|
|
208
223
|
checkPortfolio_1.checkPortfolioToolDefinition,
|
|
209
224
|
marketAnalysis_1.marketAnalysisToolDefinition,
|
|
210
225
|
createWallet_1.createWalletToolDefinition,
|
|
226
|
+
manageCustomTokens_1.manageCustomTokensDefinition,
|
|
211
227
|
limitOrderManager_2.createLimitOrderToolDefinition,
|
|
212
228
|
limitOrderManager_2.listLimitOrdersToolDefinition,
|
|
213
229
|
limitOrderManager_2.cancelLimitOrderToolDefinition
|
|
@@ -361,6 +377,8 @@ app.post('/api/transactions/:id/reject', async (req, res) => {
|
|
|
361
377
|
});
|
|
362
378
|
let cachedTrending = null;
|
|
363
379
|
let lastTrendingFetch = 0;
|
|
380
|
+
let cachedPrices = {};
|
|
381
|
+
let lastPricesFetch = 0;
|
|
364
382
|
app.get('/api/trending', async (req, res) => {
|
|
365
383
|
const now = Date.now();
|
|
366
384
|
if (cachedTrending && now - lastTrendingFetch < 5 * 60 * 1000) {
|
|
@@ -388,6 +406,158 @@ app.get('/api/trending', async (req, res) => {
|
|
|
388
406
|
res.status(500).json({ error: err.message });
|
|
389
407
|
}
|
|
390
408
|
});
|
|
409
|
+
app.get('/api/portfolio', async (req, res) => {
|
|
410
|
+
try {
|
|
411
|
+
const userAddress = await (0, config_1.getAddress)();
|
|
412
|
+
const customTokensPath = path_1.default.join((0, paths_1.getPath)('custom_tokens.json'));
|
|
413
|
+
let customTokens = {};
|
|
414
|
+
if (fs_1.default.existsSync(customTokensPath)) {
|
|
415
|
+
try {
|
|
416
|
+
customTokens = JSON.parse(fs_1.default.readFileSync(customTokensPath, 'utf8'));
|
|
417
|
+
}
|
|
418
|
+
catch (e) { }
|
|
419
|
+
}
|
|
420
|
+
const portfolio = {};
|
|
421
|
+
await Promise.all(config_1.SUPPORTED_CHAIN_NAMES.map(async (chainName) => {
|
|
422
|
+
portfolio[chainName] = [];
|
|
423
|
+
try {
|
|
424
|
+
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
425
|
+
// 1. Get Native Balance
|
|
426
|
+
const nativeBal = await publicClient.getBalance({ address: userAddress });
|
|
427
|
+
if (nativeBal > 0n) {
|
|
428
|
+
portfolio[chainName].push({
|
|
429
|
+
symbol: chainName === 'bsc' ? 'BNB' : chainName === 'polygon' ? 'POL' : 'ETH',
|
|
430
|
+
address: 'native',
|
|
431
|
+
balanceRaw: nativeBal.toString(),
|
|
432
|
+
decimals: 18,
|
|
433
|
+
isNative: true
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
// 2. Combine TOKEN_MAP and customTokens for this chain
|
|
437
|
+
const tokensToQuery = { ...(tokens_1.TOKEN_MAP[chainName] || {}) };
|
|
438
|
+
if (customTokens[chainName]) {
|
|
439
|
+
Object.assign(tokensToQuery, customTokens[chainName]);
|
|
440
|
+
}
|
|
441
|
+
// 3. Query all ERC-20 balances in parallel
|
|
442
|
+
await Promise.all(Object.entries(tokensToQuery).map(async ([symbol, address]) => {
|
|
443
|
+
if (address === '0x0000000000000000000000000000000000000000')
|
|
444
|
+
return; // Skip native placeholder
|
|
445
|
+
try {
|
|
446
|
+
const balPromise = publicClient.readContract({
|
|
447
|
+
address: address,
|
|
448
|
+
abi: tokens_1.ERC20_ABI,
|
|
449
|
+
functionName: 'balanceOf',
|
|
450
|
+
args: [userAddress]
|
|
451
|
+
});
|
|
452
|
+
const decPromise = publicClient.readContract({
|
|
453
|
+
address: address,
|
|
454
|
+
abi: tokens_1.ERC20_ABI,
|
|
455
|
+
functionName: 'decimals'
|
|
456
|
+
});
|
|
457
|
+
const [bal, decimals] = await Promise.all([balPromise, decPromise]);
|
|
458
|
+
if (bal > 0n) {
|
|
459
|
+
portfolio[chainName].push({
|
|
460
|
+
symbol,
|
|
461
|
+
address,
|
|
462
|
+
balanceRaw: bal.toString(),
|
|
463
|
+
decimals: decimals, // Now using actual on-chain decimals
|
|
464
|
+
isNative: false
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
catch (e) {
|
|
469
|
+
// Ignore read errors
|
|
470
|
+
}
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
catch (e) {
|
|
474
|
+
console.error(`Portfolio error on ${chainName}:`, e);
|
|
475
|
+
}
|
|
476
|
+
}));
|
|
477
|
+
// --- DexScreener Price Fetching ---
|
|
478
|
+
const addressesToFetch = new Set();
|
|
479
|
+
const wrapMap = {
|
|
480
|
+
ethereum: 'WETH', arbitrum: 'WETH', base: 'WETH', optimism: 'WETH', sepolia: 'WETH', base_sepolia: 'WETH',
|
|
481
|
+
bsc: 'WBNB', polygon: 'WMATIC'
|
|
482
|
+
};
|
|
483
|
+
for (const chain of Object.keys(portfolio)) {
|
|
484
|
+
for (const t of portfolio[chain]) {
|
|
485
|
+
if (t.isNative) {
|
|
486
|
+
const wToken = wrapMap[chain] || 'WETH';
|
|
487
|
+
const wAddr = (tokens_1.TOKEN_MAP[chain]?.[wToken]) || '';
|
|
488
|
+
if (wAddr)
|
|
489
|
+
addressesToFetch.add(wAddr.toLowerCase());
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
addressesToFetch.add(t.address.toLowerCase());
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const uniqueAddrs = Array.from(addressesToFetch);
|
|
497
|
+
const now = Date.now();
|
|
498
|
+
let priceMap = cachedPrices;
|
|
499
|
+
if (uniqueAddrs.length > 0 && now - lastPricesFetch > 2 * 60 * 1000) {
|
|
500
|
+
try {
|
|
501
|
+
const newPrices = {};
|
|
502
|
+
await Promise.all(uniqueAddrs.map(async (addr) => {
|
|
503
|
+
try {
|
|
504
|
+
const res = await fetch(`https://api.dexscreener.com/latest/dex/tokens/${addr}`);
|
|
505
|
+
if (res.ok) {
|
|
506
|
+
const data = await res.json();
|
|
507
|
+
if (data.pairs && data.pairs.length > 0) {
|
|
508
|
+
// Find the pair with highest liquidity
|
|
509
|
+
let bestPair = data.pairs[0];
|
|
510
|
+
let maxLiq = 0;
|
|
511
|
+
for (const p of data.pairs) {
|
|
512
|
+
const liq = p.liquidity?.usd || 0;
|
|
513
|
+
if (liq > maxLiq) {
|
|
514
|
+
maxLiq = liq;
|
|
515
|
+
bestPair = p;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Calculate price from bestPair
|
|
519
|
+
if (bestPair.priceUsd) {
|
|
520
|
+
const baseAddr = bestPair.baseToken.address.toLowerCase();
|
|
521
|
+
const quoteAddr = bestPair.quoteToken?.address?.toLowerCase();
|
|
522
|
+
if (baseAddr === addr) {
|
|
523
|
+
newPrices[addr] = parseFloat(bestPair.priceUsd);
|
|
524
|
+
}
|
|
525
|
+
else if (quoteAddr === addr && bestPair.priceNative && parseFloat(bestPair.priceNative) > 0) {
|
|
526
|
+
newPrices[addr] = parseFloat(bestPair.priceUsd) / parseFloat(bestPair.priceNative);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch (e) {
|
|
533
|
+
console.error(`DexScreener error for ${addr}:`, e);
|
|
534
|
+
}
|
|
535
|
+
}));
|
|
536
|
+
console.log('DexScreener Fetched Prices:', newPrices);
|
|
537
|
+
cachedPrices = { ...cachedPrices, ...newPrices };
|
|
538
|
+
priceMap = cachedPrices;
|
|
539
|
+
lastPricesFetch = now;
|
|
540
|
+
}
|
|
541
|
+
catch (e) {
|
|
542
|
+
console.error('DexScreener fetch error:', e);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
for (const chain of Object.keys(portfolio)) {
|
|
546
|
+
for (const t of portfolio[chain]) {
|
|
547
|
+
let lookupAddr = t.address.toLowerCase();
|
|
548
|
+
if (t.isNative) {
|
|
549
|
+
const wToken = wrapMap[chain] || 'WETH';
|
|
550
|
+
lookupAddr = ((tokens_1.TOKEN_MAP[chain]?.[wToken]) || '').toLowerCase();
|
|
551
|
+
}
|
|
552
|
+
t.priceUsd = priceMap[lookupAddr] || 0;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
res.json(portfolio);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
res.status(500).json({ error: err.message });
|
|
559
|
+
}
|
|
560
|
+
});
|
|
391
561
|
app.post('/api/chat', async (req, res) => {
|
|
392
562
|
try {
|
|
393
563
|
const { message, session_id } = req.body;
|
|
@@ -304,9 +304,14 @@ Provider: ${config.llm.provider}`;
|
|
|
304
304
|
return process.exit(0);
|
|
305
305
|
}
|
|
306
306
|
else if (walletSetupType === 'generate') {
|
|
307
|
-
|
|
308
|
-
const account = (0, accounts_1.
|
|
309
|
-
|
|
307
|
+
const seedPhrase = (0, accounts_1.generateMnemonic)(accounts_1.english);
|
|
308
|
+
const account = (0, accounts_1.mnemonicToAccount)(seedPhrase);
|
|
309
|
+
privateKey = '0x' + Buffer.from(account.getHdKey().privateKey).toString('hex');
|
|
310
|
+
prompts_1.log.success('New Wallet Generated!');
|
|
311
|
+
prompts_1.log.info(`Address: ${account.address}`);
|
|
312
|
+
prompts_1.log.info(`Private Key: ${privateKey}`);
|
|
313
|
+
prompts_1.log.info(`Seed Phrase (Mnemonic): ${seedPhrase}`);
|
|
314
|
+
prompts_1.log.warn('IMPORTANT: Write down these 12 words (or the Private Key) NOW! This is your ONLY backup. The credentials have been securely injected into your local OS vault.');
|
|
310
315
|
}
|
|
311
316
|
// --- SAVING ---
|
|
312
317
|
// Update Config.yaml
|