nyxora 1.0.8 → 1.1.2

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.
@@ -1,45 +1,10 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.logger = void 0;
40
7
  exports.processUserInput = processUserInput;
41
- const dotenv = __importStar(require("dotenv"));
42
- dotenv.config();
43
8
  const fs_1 = __importDefault(require("fs"));
44
9
  const openai_1 = require("openai");
45
10
  const parser_1 = require("../config/parser");
@@ -72,21 +37,21 @@ function getOpenAI() {
72
37
  currentKeyIndex++; // Increment for next request
73
38
  }
74
39
  }
75
- // Fallbacks if no valid keys found in config
40
+ // Fallbacks if no valid keys found in config.llm.api_keys
76
41
  if (!apiKey) {
77
42
  if (config.llm.provider === 'gemini') {
78
- apiKey = process.env.GEMINI_API_KEY || '';
43
+ apiKey = config.llm.credentials?.gemini_key || '';
79
44
  }
80
45
  else if (config.llm.provider === 'openrouter') {
81
- apiKey = process.env.OPENROUTER_API_KEY || '';
46
+ apiKey = config.llm.credentials?.openrouter_key || '';
82
47
  }
83
48
  else {
84
- apiKey = process.env.OPENAI_API_KEY || '';
49
+ apiKey = config.llm.credentials?.openai_key || '';
85
50
  }
86
51
  if (!apiKey) {
87
- throw new Error(`No API Key found for ${config.llm.provider} in config or .env`);
52
+ throw new Error(`No API Key found for ${config.llm.provider} in config.yaml. Please run 'nyxora setup' to configure it.`);
88
53
  }
89
- console.log(`[LLM] Using default API Key from .env`);
54
+ console.log(`[LLM] Using default API Key from config.yaml`);
90
55
  }
91
56
  if (config.llm.provider === 'gemini') {
92
57
  return new openai_1.OpenAI({
@@ -138,10 +103,10 @@ If the user doesn't specify a chain, default to: ${config.agent.default_chain}.`
138
103
  }
139
104
  return basePrompt;
140
105
  }
141
- async function processUserInput(input) {
106
+ async function processUserInput(input, role = 'user') {
142
107
  const config = (0, parser_1.loadConfig)();
143
- // Add user input to memory
144
- exports.logger.addEntry({ role: 'user', content: input });
108
+ // Add input to memory
109
+ exports.logger.addEntry({ role, content: input });
145
110
  const history = exports.logger.getHistory();
146
111
  // Format messages for OpenAI
147
112
  const messages = [
@@ -0,0 +1,38 @@
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.txManager = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ class TransactionManager {
9
+ transactions = new Map();
10
+ createPendingTransaction(type, chainName, details) {
11
+ const id = crypto_1.default.randomUUID();
12
+ const tx = {
13
+ id,
14
+ type,
15
+ chainName,
16
+ details,
17
+ status: 'pending',
18
+ createdAt: Date.now(),
19
+ };
20
+ this.transactions.set(id, tx);
21
+ return tx;
22
+ }
23
+ getPending() {
24
+ return Array.from(this.transactions.values()).filter(t => t.status === 'pending');
25
+ }
26
+ getTransaction(id) {
27
+ return this.transactions.get(id);
28
+ }
29
+ updateStatus(id, status, result) {
30
+ const tx = this.transactions.get(id);
31
+ if (tx) {
32
+ tx.status = status;
33
+ if (result)
34
+ tx.result = result;
35
+ }
36
+ }
37
+ }
38
+ exports.txManager = new TransactionManager();
@@ -1,49 +1,19 @@
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
- })();
36
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
5
  };
39
6
  Object.defineProperty(exports, "__esModule", { value: true });
40
7
  const fs_1 = __importDefault(require("fs"));
41
8
  const path_1 = __importDefault(require("path"));
42
- const dotenv = __importStar(require("dotenv"));
43
9
  const open_1 = __importDefault(require("open"));
44
10
  const paths_1 = require("../config/paths");
45
11
  const server_1 = require("./server");
46
12
  const setup_1 = require("./setup");
13
+ const prompts_1 = require("@clack/prompts");
14
+ const crypto_1 = require("../utils/crypto");
15
+ const state_1 = require("../utils/state");
16
+ const picocolors_1 = __importDefault(require("picocolors"));
47
17
  async function main() {
48
18
  // 1. Determine configuration directory
49
19
  const appDir = (0, paths_1.getAppDir)();
@@ -60,23 +30,12 @@ async function main() {
60
30
  // 2. Setup boilerplate files if in global mode and they don't exist
61
31
  let isFirstBoot = false;
62
32
  if (isGlobalMode) {
63
- const globalEnvPath = path_1.default.join(appDir, '.env');
64
33
  const globalConfigPath = path_1.default.join(appDir, 'config.yaml');
65
34
  const globalUserMdPath = path_1.default.join(appDir, 'user.md');
66
35
  const globalIdentityMdPath = path_1.default.join(appDir, 'IDENTITY.md');
67
- // Copy .env.example to ~/.nyxora/.env if it doesn't exist
68
- if (!fs_1.default.existsSync(globalEnvPath)) {
69
- isFirstBoot = true;
70
- const exampleEnvPath = path_1.default.resolve(__dirname, '../../../.env.example');
71
- if (fs_1.default.existsSync(exampleEnvPath)) {
72
- fs_1.default.copyFileSync(exampleEnvPath, globalEnvPath);
73
- }
74
- else {
75
- fs_1.default.writeFileSync(globalEnvPath, '# Nyxora Environment Variables\nPRIVATE_KEY=\n');
76
- }
77
- }
78
36
  // Copy default config.yaml
79
37
  if (!fs_1.default.existsSync(globalConfigPath)) {
38
+ isFirstBoot = true;
80
39
  const exampleConfigPath = path_1.default.resolve(__dirname, '../../../config.yaml');
81
40
  if (fs_1.default.existsSync(exampleConfigPath)) {
82
41
  fs_1.default.copyFileSync(exampleConfigPath, globalConfigPath);
@@ -86,24 +45,47 @@ async function main() {
86
45
  }
87
46
  }
88
47
  if (!fs_1.default.existsSync(globalUserMdPath)) {
89
- fs_1.default.writeFileSync(globalUserMdPath, 'Tuliskan instruksi kustom, aturan khusus, profil pengguna, atau persona yang Anda inginkan untuk Nyxora AI di file ini.\n');
48
+ fs_1.default.writeFileSync(globalUserMdPath, 'Write custom instructions, special rules, user profiles, or the persona you want for Nyxora AI in this file.\n');
90
49
  }
91
50
  if (!fs_1.default.existsSync(globalIdentityMdPath)) {
92
- fs_1.default.writeFileSync(globalIdentityMdPath, 'Kamu adalah Nyxora, asisten Web3 pintar.\n');
51
+ fs_1.default.writeFileSync(globalIdentityMdPath, 'You are a Web3 AI assistant named Nyxora.\n');
93
52
  }
94
53
  }
95
54
  if (isFirstBoot) {
96
- console.log('[Setup] Instalasi baru terdeteksi. Memulai Setup Wizard...');
55
+ console.log('[Setup] New installation detected. Starting Setup Wizard...');
97
56
  await (0, setup_1.runSetupWizard)();
98
57
  }
99
- // 3. Load Environment Variables from the determined directory
100
- dotenv.config({ path: path_1.default.join(appDir, '.env') });
58
+ // 3. Load Private Key into Memory
59
+ const keystorePath = path_1.default.join(appDir, 'keystore.json');
60
+ if (fs_1.default.existsSync(keystorePath)) {
61
+ const masterPassword = await (0, prompts_1.password)({
62
+ message: '🔒 Vault locked! Enter Master Password to access Nyxora:',
63
+ });
64
+ if ((0, prompts_1.isCancel)(masterPassword) || !masterPassword) {
65
+ console.log(picocolors_1.default.red('Access denied. Exiting Nyxora.'));
66
+ return process.exit(0);
67
+ }
68
+ try {
69
+ const keystore = JSON.parse(fs_1.default.readFileSync(keystorePath, 'utf8'));
70
+ const privateKey = (0, crypto_1.decryptKey)(keystore, masterPassword);
71
+ (0, state_1.setPrivateKey)(privateKey);
72
+ console.log(picocolors_1.default.green('✅ Private Key successfully decrypted into memory.'));
73
+ }
74
+ catch (error) {
75
+ console.log(picocolors_1.default.red('❌ Invalid Master Password or corrupted keystore. Exiting Nyxora.'));
76
+ return process.exit(1);
77
+ }
78
+ }
79
+ else {
80
+ console.log(picocolors_1.default.yellow('⚠️ Keystore not found. Web3 features will be disabled unless you run `nyxora setup`.'));
81
+ }
101
82
  // 4. Start the Express API Server (which also serves the static dashboard and Telegram bot)
102
83
  (0, server_1.startServer)();
103
84
  // 5. Open the Dashboard in the default browser
104
85
  const PORT = process.env.PORT || 3000;
86
+ const token = (0, state_1.getSessionToken)();
105
87
  setTimeout(() => {
106
- const url = `http://localhost:${PORT}`;
88
+ const url = `http://localhost:${PORT}?token=${token}`;
107
89
  console.log(`🌐 Opening Dashboard at ${url}`);
108
90
  (0, open_1.default)(url);
109
91
  }, 1500);
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -40,17 +7,19 @@ exports.startServer = startServer;
40
7
  const express_1 = __importDefault(require("express"));
41
8
  const cors_1 = __importDefault(require("cors"));
42
9
  const path_1 = __importDefault(require("path"));
43
- const dotenv = __importStar(require("dotenv"));
44
- const paths_1 = require("../config/paths");
45
- dotenv.config({ path: (0, paths_1.getPath)('.env') });
10
+ const state_1 = require("../utils/state");
46
11
  const reasoning_1 = require("../agent/reasoning");
47
12
  const parser_1 = require("../config/parser");
48
13
  const tracker_1 = require("./tracker");
49
- const getBalance_1 = require("../web3/skills/getBalance");
14
+ const transactionManager_1 = require("../agent/transactionManager");
50
15
  const transfer_1 = require("../web3/skills/transfer");
51
- const getPrice_1 = require("../web3/skills/getPrice");
52
16
  const swapToken_1 = require("../web3/skills/swapToken");
17
+ const getBalance_1 = require("../web3/skills/getBalance");
18
+ const transfer_2 = require("../web3/skills/transfer");
19
+ const getPrice_1 = require("../web3/skills/getPrice");
20
+ const swapToken_2 = require("../web3/skills/swapToken");
53
21
  const telegram_1 = require("./telegram");
22
+ const formatter_1 = require("../utils/formatter");
54
23
  // Intercept console.log and console.error
55
24
  const originalLog = console.log;
56
25
  const originalError = console.error;
@@ -63,8 +32,16 @@ console.error = function (...args) {
63
32
  originalError.apply(console, args);
64
33
  };
65
34
  const app = (0, express_1.default)();
66
- app.use((0, cors_1.default)());
35
+ app.use((0, cors_1.default)({ origin: ['http://localhost:3000', 'http://localhost:5173'] }));
67
36
  app.use(express_1.default.json());
37
+ // API Auth Middleware
38
+ app.use('/api', (req, res, next) => {
39
+ const token = req.headers['x-nyxora-token'];
40
+ if (token !== (0, state_1.getSessionToken)()) {
41
+ return res.status(401).json({ error: 'Unauthorized: Invalid or missing token' });
42
+ }
43
+ next();
44
+ });
68
45
  // Serve static frontend from dashboard/dist
69
46
  app.use(express_1.default.static(path_1.default.join(__dirname, '../../dashboard/dist')));
70
47
  app.get('/api/history', (req, res) => {
@@ -117,11 +94,53 @@ app.get('/api/logs', (req, res) => {
117
94
  app.get('/api/skills', (req, res) => {
118
95
  res.json([
119
96
  getBalance_1.getBalanceToolDefinition,
120
- transfer_1.transferToolDefinition,
97
+ transfer_2.transferToolDefinition,
121
98
  getPrice_1.getPriceToolDefinition,
122
- swapToken_1.swapTokenToolDefinition
99
+ swapToken_2.swapTokenToolDefinition
123
100
  ]);
124
101
  });
102
+ app.get('/api/transactions', (req, res) => {
103
+ res.json(transactionManager_1.txManager.getPending());
104
+ });
105
+ app.post('/api/transactions/:id/approve', async (req, res) => {
106
+ const id = req.params.id;
107
+ const tx = transactionManager_1.txManager.getTransaction(id);
108
+ if (!tx || tx.status !== 'pending')
109
+ return res.status(404).json({ error: 'Transaction not found or not pending' });
110
+ try {
111
+ let result = '';
112
+ if (tx.type === 'transfer') {
113
+ result = await (0, transfer_1.executeTransfer)(tx.chainName, tx.details.toAddress, tx.details.amountEth);
114
+ }
115
+ else if (tx.type === 'swap') {
116
+ result = await (0, swapToken_1.executeSwap)(tx.chainName, tx.details.fromToken, tx.details.toToken, tx.details.amount);
117
+ }
118
+ transactionManager_1.txManager.updateStatus(id, 'executed', result);
119
+ // Add programmatic beautiful message directly to chat
120
+ const prettyMsg = (0, formatter_1.formatTransactionSuccess)(tx, result);
121
+ reasoning_1.logger.addEntry({ role: 'assistant', content: `✅ Transaction processed:\n\n${prettyMsg}` });
122
+ // Background update to LLM
123
+ (0, reasoning_1.processUserInput)(`Transaction ${id} was APPROVED and EXECUTED by the user via Dashboard. Result: ${result}`, 'system').catch(() => { });
124
+ res.json({ success: true, result });
125
+ }
126
+ catch (err) {
127
+ transactionManager_1.txManager.updateStatus(id, 'failed', err.message);
128
+ // Add programmatic beautiful error message directly to chat
129
+ const prettyError = (0, formatter_1.formatTransactionError)(tx, err.message);
130
+ reasoning_1.logger.addEntry({ role: 'assistant', content: prettyError });
131
+ (0, reasoning_1.processUserInput)(`Transaction ${id} was APPROVED but FAILED to execute. Error: ${err.message}`, 'system').catch(() => { });
132
+ res.status(500).json({ error: err.message });
133
+ }
134
+ });
135
+ app.post('/api/transactions/:id/reject', (req, res) => {
136
+ const id = req.params.id;
137
+ const tx = transactionManager_1.txManager.getTransaction(id);
138
+ if (!tx || tx.status !== 'pending')
139
+ return res.status(404).json({ error: 'Transaction not found or not pending' });
140
+ transactionManager_1.txManager.updateStatus(id, 'rejected');
141
+ (0, reasoning_1.processUserInput)(`Transaction ${id} was REJECTED by the user via Dashboard. Acknowledge this briefly.`, 'system').catch(() => { });
142
+ res.json({ success: true });
143
+ });
125
144
  app.post('/api/chat', async (req, res) => {
126
145
  try {
127
146
  const { message } = req.body;
@@ -10,6 +10,7 @@ const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const paths_1 = require("../config/paths");
12
12
  const parser_1 = require("../config/parser");
13
+ const crypto_1 = require("../utils/crypto");
13
14
  async function runSetupWizard() {
14
15
  console.clear();
15
16
  const logo = `
@@ -21,62 +22,62 @@ async function runSetupWizard() {
21
22
  ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
22
23
  `;
23
24
  console.log(picocolors_1.default.cyan(logo));
24
- (0, prompts_1.intro)(picocolors_1.default.inverse(' Pengaturan Nyxora CLI '));
25
+ (0, prompts_1.intro)(picocolors_1.default.inverse(' Nyxora CLI Setup '));
25
26
  const appDir = (0, paths_1.getAppDir)();
26
27
  const config = (0, parser_1.loadConfig)();
27
- const disclaimer = `Nyxora adalah Asisten Web3 yang beroperasi dengan akses penuh atas kendali anda.
28
+ const disclaimer = `Nyxora is a Web3 Assistant that operates with full access under your control.
28
29
 
29
- Tindakan Pencegahan Kritis:
30
- - Private Key Anda adalah nyawa aset Anda. JANGAN PERNAH menyalin atau membagikan file .env.
31
- - Segala instruksi yang Anda berikan via Telegram atau Dashboard dapat memicu transaksi on-chain.
32
- - Disarankan menggunakan model AI yang cerdas untuk akurasi maksimal.
30
+ Critical Precautions:
31
+ - Your Private Key is the lifeblood of your assets. NEVER copy or share the keystore.json file.
32
+ - Any instructions you provide via Telegram or Dashboard can trigger on-chain transactions.
33
+ - It is recommended to use a smart AI model for maximum accuracy.
33
34
 
34
- Dengan menggunakan Nyxora, Anda sepenuhnya memegang kendali atas kunci Anda sendiri.`;
35
- (0, prompts_1.note)(disclaimer, 'Peringatan Keamanan');
35
+ By using Nyxora, you retain full control over your own keys.`;
36
+ (0, prompts_1.note)(disclaimer, 'Security Warning');
36
37
  const understand = await (0, prompts_1.confirm)({
37
- message: 'Saya mengerti bahwa keamanan Private Key adalah tanggung jawab saya. Lanjutkan?',
38
+ message: 'I understand that Private Key security is my responsibility. Continue?',
38
39
  initialValue: true,
39
40
  });
40
41
  if ((0, prompts_1.isCancel)(understand) || !understand) {
41
- (0, prompts_1.cancel)('Pengaturan dibatalkan.');
42
+ (0, prompts_1.cancel)('Setup cancelled.');
42
43
  return process.exit(0);
43
44
  }
44
45
  const existingConfigNote = `Workspace: ${appDir}
45
- Model Saat Ini: ${config.llm.model}
46
+ Current Model: ${config.llm.model}
46
47
  Provider: ${config.llm.provider}`;
47
- (0, prompts_1.note)(existingConfigNote, 'Konfigurasi Terdeteksi');
48
+ (0, prompts_1.note)(existingConfigNote, 'Configuration Detected');
48
49
  const action = await (0, prompts_1.select)({
49
- message: 'Apa yang ingin Anda lakukan?',
50
+ message: 'What would you like to do?',
50
51
  options: [
51
- { value: 'keep', label: 'Pertahankan nilai saat ini' },
52
- { value: 'update', label: 'Tinjau dan perbarui pengaturan' },
52
+ { value: 'keep', label: 'Keep current values' },
53
+ { value: 'update', label: 'Review and update settings' },
53
54
  ],
54
55
  });
55
56
  if ((0, prompts_1.isCancel)(action)) {
56
- (0, prompts_1.cancel)('Pengaturan dibatalkan.');
57
+ (0, prompts_1.cancel)('Setup cancelled.');
57
58
  return process.exit(0);
58
59
  }
59
60
  if (action === 'keep') {
60
- (0, prompts_1.outro)(picocolors_1.default.green('Selesai! Konfigurasi tidak diubah. Menjalankan Nyxora...'));
61
+ (0, prompts_1.outro)(picocolors_1.default.green('Done! Configuration unchanged. Starting Nyxora...'));
61
62
  return;
62
63
  }
63
64
  // --- WIZARD FORM ---
64
65
  // 1. LLM Provider
65
66
  const provider = await (0, prompts_1.select)({
66
- message: 'Pilih Mesin AI (Provider):',
67
+ message: 'Select AI Engine (Provider):',
67
68
  initialValue: config.llm.provider,
68
69
  options: [
69
- { value: 'openai', label: 'OpenAI (Rekomendasi)' },
70
+ { value: 'openai', label: 'OpenAI (Recommended)' },
70
71
  { value: 'gemini', label: 'Google Gemini' },
71
- { value: 'openrouter', label: 'OpenRouter (Banyak Model)' },
72
- { value: 'ollama', label: 'Ollama (Lokal)' },
72
+ { value: 'openrouter', label: 'OpenRouter (Many Models)' },
73
+ { value: 'ollama', label: 'Ollama (Local)' },
73
74
  ],
74
75
  });
75
76
  if ((0, prompts_1.isCancel)(provider))
76
77
  return process.exit(0);
77
78
  // 2. Model Name
78
79
  const model = await (0, prompts_1.text)({
79
- message: 'Masukkan nama model AI (contoh: gpt-4o, gemini-2.5-flash):',
80
+ message: 'Enter AI model name (e.g. gpt-4o, gemini-2.5-flash):',
80
81
  initialValue: config.llm.model,
81
82
  });
82
83
  if ((0, prompts_1.isCancel)(model))
@@ -85,14 +86,14 @@ Provider: ${config.llm.provider}`;
85
86
  let apiKey = '';
86
87
  if (provider !== 'ollama') {
87
88
  apiKey = (await (0, prompts_1.password)({
88
- message: `Masukkan API Key untuk ${provider} (Biarkan kosong jika sudah ada):`,
89
+ message: `Enter API Key for ${provider} (Leave empty if already set):`,
89
90
  }));
90
91
  if ((0, prompts_1.isCancel)(apiKey))
91
92
  return process.exit(0);
92
93
  }
93
94
  // 4. Default Chain
94
95
  const defaultChain = await (0, prompts_1.select)({
95
- message: 'Pilih Jaringan Utama (Default Chain):',
96
+ message: 'Select Default Chain:',
96
97
  initialValue: config.agent.default_chain,
97
98
  options: [
98
99
  { value: 'sepolia', label: 'Sepolia (Testnet)' },
@@ -107,7 +108,7 @@ Provider: ${config.llm.provider}`;
107
108
  return process.exit(0);
108
109
  // 5. Telegram Bot
109
110
  const setupTelegram = await (0, prompts_1.confirm)({
110
- message: 'Apakah Anda ingin memasang Bot Telegram?',
111
+ message: 'Do you want to setup the Telegram Bot?',
111
112
  initialValue: config.integrations?.telegram?.enabled || false,
112
113
  });
113
114
  if ((0, prompts_1.isCancel)(setupTelegram))
@@ -115,17 +116,25 @@ Provider: ${config.llm.provider}`;
115
116
  let telegramToken = '';
116
117
  if (setupTelegram) {
117
118
  telegramToken = (await (0, prompts_1.password)({
118
- message: 'Masukkan Telegram Bot Token dari @BotFather (Biarkan kosong jika sudah ada):',
119
+ message: 'Enter Telegram Bot Token from @BotFather (Leave empty if already set):',
119
120
  }));
120
121
  if ((0, prompts_1.isCancel)(telegramToken))
121
122
  return process.exit(0);
122
123
  }
123
- // 6. Wallet Private Key (.env)
124
+ // 6. Wallet Private Key (keystore.json)
124
125
  const privateKey = await (0, prompts_1.password)({
125
- message: 'Masukkan Wallet Private Key (0x...)\n (Super Rahasia! Akan disimpan eksklusif di .env. Biarkan kosong jika tidak ingin mengubah):',
126
+ message: 'Enter Wallet Private Key (0x...)\n (Will be AES-256-GCM encrypted. Leave empty to keep current):',
126
127
  });
127
128
  if ((0, prompts_1.isCancel)(privateKey))
128
129
  return process.exit(0);
130
+ let masterPassword = '';
131
+ if (privateKey) {
132
+ masterPassword = (await (0, prompts_1.password)({
133
+ message: 'Enter MASTER PASSWORD to encrypt your key vault:',
134
+ }));
135
+ if ((0, prompts_1.isCancel)(masterPassword) || !masterPassword)
136
+ return process.exit(0);
137
+ }
129
138
  // --- SAVING ---
130
139
  // Update Config.yaml
131
140
  config.llm.provider = provider;
@@ -150,21 +159,22 @@ Provider: ${config.llm.provider}`;
150
159
  config.integrations.telegram.bot_token = telegramToken;
151
160
  }
152
161
  (0, parser_1.saveConfig)(config);
153
- // Update .env exclusively for Private Key
154
- if (privateKey) {
155
- const envPath = path_1.default.join(appDir, '.env');
156
- let envContent = '';
157
- if (fs_1.default.existsSync(envPath)) {
158
- envContent = fs_1.default.readFileSync(envPath, 'utf8');
159
- }
160
- // Replace or append WALLET_PRIVATE_KEY
161
- if (envContent.includes('PRIVATE_KEY=')) {
162
- envContent = envContent.replace(/PRIVATE_KEY=.*/g, `PRIVATE_KEY="${privateKey}"`);
162
+ // Update keystore.json exclusively for Private Key
163
+ if (privateKey && masterPassword) {
164
+ const keystorePath = path_1.default.join(appDir, 'keystore.json');
165
+ try {
166
+ const encryptedData = (0, crypto_1.encryptKey)(privateKey, masterPassword);
167
+ fs_1.default.writeFileSync(keystorePath, JSON.stringify(encryptedData, null, 2), 'utf8');
168
+ // Cleanup old .env if it existed
169
+ const envPath = path_1.default.join(appDir, '.env');
170
+ if (fs_1.default.existsSync(envPath)) {
171
+ fs_1.default.unlinkSync(envPath);
172
+ console.log(picocolors_1.default.yellow('Legacy .env file has been deleted for security.'));
173
+ }
163
174
  }
164
- else {
165
- envContent += `\nPRIVATE_KEY="${privateKey}"`;
175
+ catch (error) {
176
+ console.error('Failed to save keystore.json:', error);
166
177
  }
167
- fs_1.default.writeFileSync(envPath, envContent);
168
178
  }
169
- (0, prompts_1.outro)(picocolors_1.default.green('✨ Setup Berhasil! Semua konfigurasi telah disimpan dengan aman.'));
179
+ (0, prompts_1.outro)(picocolors_1.default.green('✨ Setup Successful! All configurations have been securely saved.'));
170
180
  }