nyxora 26.6.20 → 26.6.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +18 -1
  2. package/bin/nyxora.mjs +32 -0
  3. package/dist/packages/core/src/agent/reasoning.js +10 -0
  4. package/dist/packages/core/src/config/parser.js +121 -7
  5. package/dist/packages/core/src/gateway/chat.js +82 -0
  6. package/dist/packages/core/src/gateway/cli.js +63 -0
  7. package/dist/packages/core/src/gateway/server.js +100 -56
  8. package/dist/packages/core/src/gateway/setup.js +39 -22
  9. package/dist/packages/core/src/utils/formatter.test.js +40 -0
  10. package/dist/packages/core/src/utils/skillManager.js +91 -0
  11. package/dist/packages/core/src/utils/userWhitelistManager.js +41 -36
  12. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +2 -2
  13. package/dist/packages/core/src/web3/aggregator/defiRouter.js +3 -0
  14. package/dist/packages/core/src/web3/skills/bridgeToken.js +4 -0
  15. package/dist/packages/core/src/web3/skills/checkRegistryStatus.js +13 -0
  16. package/dist/packages/core/src/web3/skills/customTx.js +2 -0
  17. package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
  18. package/dist/packages/core/src/web3/skills/manageCustomTokens.js +18 -32
  19. package/dist/packages/core/src/web3/skills/marketAnalysis.js +3 -1
  20. package/dist/packages/core/src/web3/skills/mintNft.js +2 -0
  21. package/dist/packages/core/src/web3/skills/provideLiquidity.js +2 -0
  22. package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
  23. package/dist/packages/core/src/web3/skills/swapToken.js +4 -2
  24. package/dist/packages/core/src/web3/skills/transfer.js +2 -0
  25. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
  26. package/dist/packages/core/src/web3/utils/tokens.js +9 -1
  27. package/package.json +2 -1
  28. package/packages/core/package.json +1 -1
  29. package/packages/core/src/agent/reasoning.ts +11 -0
  30. package/packages/core/src/config/parser.ts +119 -9
  31. package/packages/core/src/gateway/chat.ts +85 -0
  32. package/packages/core/src/gateway/cli.ts +63 -0
  33. package/packages/core/src/gateway/server.ts +115 -60
  34. package/packages/core/src/gateway/setup.ts +39 -27
  35. package/packages/core/src/utils/formatter.test.ts +41 -0
  36. package/packages/core/src/utils/skillManager.ts +98 -0
  37. package/packages/core/src/utils/userWhitelistManager.ts +48 -39
  38. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +2 -2
  39. package/packages/core/src/web3/aggregator/defiRouter.ts +4 -0
  40. package/packages/core/src/web3/skills/bridgeToken.ts +3 -0
  41. package/packages/core/src/web3/skills/checkRegistryStatus.ts +13 -0
  42. package/packages/core/src/web3/skills/customTx.ts +1 -0
  43. package/packages/core/src/web3/skills/defiLending.ts +1 -0
  44. package/packages/core/src/web3/skills/manageCustomTokens.ts +18 -29
  45. package/packages/core/src/web3/skills/marketAnalysis.ts +2 -1
  46. package/packages/core/src/web3/skills/mintNft.ts +1 -0
  47. package/packages/core/src/web3/skills/provideLiquidity.ts +1 -0
  48. package/packages/core/src/web3/skills/revokeApprovals.ts +1 -0
  49. package/packages/core/src/web3/skills/swapToken.ts +3 -2
  50. package/packages/core/src/web3/skills/transfer.ts +1 -0
  51. package/packages/core/src/web3/skills/yieldVault.ts +1 -0
  52. package/packages/core/src/web3/utils/tokens.ts +9 -1
  53. package/packages/dashboard/dist/assets/index-Di9x08yk.js +16 -0
  54. package/packages/dashboard/dist/index.html +1 -1
  55. package/packages/dashboard/package.json +1 -1
  56. package/packages/mcp-server/package.json +1 -1
  57. package/packages/policy/package.json +1 -1
  58. package/packages/signer/package.json +1 -1
  59. package/dist/packages/core/src/agent/limitOrderManager.js +0 -124
  60. package/dist/packages/core/src/system/pluginManager.js +0 -91
  61. package/dist/packages/core/src/system/skills/installSkill.js +0 -52
  62. package/dist/packages/core/src/test-all-routers.js +0 -81
  63. package/dist/packages/core/src/test-router.js +0 -38
  64. package/dist/packages/core/src/web3/skills/autonomousDefi.js +0 -191
  65. package/dist/packages/core/src/web3/skills/createWallet.js +0 -34
  66. package/dist/packages/core/src/web3/skills/limitOrder.js +0 -106
  67. package/dist/packages/core/src/web3/utils/protocolRegistry.js +0 -46
  68. package/dist/tsconfig.tsbuildinfo +0 -1
  69. package/packages/core/src/__tests__/reasoning.test.ts +0 -81
  70. package/packages/core/src/__tests__/tokens.test.ts +0 -55
  71. package/packages/core/src/__tests__/web3.test.ts +0 -50
  72. package/packages/core/src/agent/reasoning.d.ts.map +0 -1
  73. package/packages/core/src/config/parser.d.ts.map +0 -1
  74. package/packages/core/src/gateway/cli.d.ts.map +0 -1
  75. package/packages/core/src/memory/logger.d.ts.map +0 -1
  76. package/packages/core/src/test-all-routers.ts +0 -59
  77. package/packages/core/src/test-router.ts +0 -49
  78. package/packages/core/src/web3/config.d.ts.map +0 -1
  79. package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
  80. package/packages/dashboard/dist/assets/index-O2m42q4p.js +0 -16
package/README.md CHANGED
@@ -24,6 +24,9 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
24
24
  * **3-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (LLM Runtime), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
25
25
  * **DeFi Configuration BYOK & UI Masking**: All aggregator and provider API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into a heavily guarded `~/.nyxora/defi_keys.yaml` file. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
26
26
  * **Approval Replay Protection (Nonce Guard)**: Transactions requested by the AI are drafted as hashes and signed with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces Nonce matching to completely eliminate double-spending and Replay Attacks.
27
+ * **Native Asset Parameter Tampering Protection**: The internal cryptographic HMAC signature rigorously binds `toAddress`, `txData`, and `valueWei`, rendering the system mathematically immune to Native Token (ETH/BNB) destination or amount hijacking via Indirect Prompt Injections.
28
+ * **Human-in-the-Loop Memory Approval**: AI-extracted permanent behavioral rules are strictly quarantined in a `pending` state until explicitly authorized by the user via the Dashboard, neutralizing Persistent Memory Poisoning vectors.
29
+ * **Stateless Policy Engine & DoS Resilience**: The Policy Engine operates as a 100% stateless cryptographic HMAC gatekeeper, hardened with resilient `try...catch` IPC interceptors to withstand Signer-level Denial of Service (Crash) attacks.
27
30
  * **Immutable Policy Guardrails**: Transaction limits (e.g. `max_usd_per_tx`) are strictly enforced by the Policy Engine. The LLM has zero write-access to bypass these rules.
28
31
 
29
32
  * **Graceful SQLite WAL Shutdown**: Integrated `SIGTERM`/`SIGINT` interceptors ensure that when the daemon stops, active requests are safely terminated and SQLite Write-Ahead Logs (WAL) are securely flushed, preventing database corruption.
@@ -133,6 +136,12 @@ nyxora dashboard
133
136
  ```bash
134
137
  nyxora clear --force
135
138
  ```
139
+
140
+ ### Utility: Clean Uninstallation
141
+ To completely remove Nyxora, wipe the AI's local memory, and securely delete your Private Key from the OS Keyring before uninstalling the NPM package:
142
+ ```bash
143
+ nyxora uninstall
144
+ ```
136
145
  > **⚠️ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the daemon** by running `nyxora restart` for the changes to take effect.
137
146
 
138
147
  ### Local Development (From Source)
@@ -159,6 +168,14 @@ npm start
159
168
 
160
169
  ---
161
170
 
171
+ ## ⚖️ Terms of Service
172
+
173
+ By downloading, installing, or using the Nyxora AI Agent, you agree to our assumption of risk and liability limitations. Please ensure you review our legal policies before deploying the agent.
174
+
175
+ > **🔗 [Read the Full Terms of Service Here](https://nyxoraai.github.io/Nyxora/terms)**
176
+
177
+ ---
178
+
162
179
  ## 📖 Official Documentation
163
180
 
164
181
  For complete technical deep-dives into our Cryptographic Architecture, please visit our official VitePress Documentation Site!
@@ -172,7 +189,7 @@ For complete technical deep-dives into our Cryptographic Architecture, please vi
172
189
  **❤️ Support the Project**
173
190
 
174
191
  Building and maintaining a highly secure, zero-trust architecture takes significant time and resources. If you love what we are building, you can help us keep Nyxora open, secure, and constantly evolving by sending a coffee our way:
175
- - **EVM (Multi-Sig Safe):** `0x490717E50D6434C348AA0D2bD5fe682392823708`
192
+ - **EVM :** `0x490717E50D6434C348AA0D2bD5fe682392823708`
176
193
 
177
194
  ---
178
195
  **License:** MIT License
package/bin/nyxora.mjs CHANGED
@@ -267,6 +267,34 @@ async function wallet(cliArgs) {
267
267
  await new Promise(resolve => child.on('close', resolve));
268
268
  }
269
269
 
270
+ async function uninstall(cliArgs) {
271
+ const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/cli.js');
272
+ const useCompiled = fs.existsSync(compiledCli);
273
+ const cmd = useCompiled ? 'node' : 'npx';
274
+ const args = useCompiled ? [compiledCli, 'uninstall', ...cliArgs] : ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'uninstall', ...cliArgs];
275
+ const child = spawn(cmd, args, {
276
+ cwd: projectRoot,
277
+ stdio: 'inherit',
278
+ env: { ...process.env, TS_NODE_CACHE: 'false' }
279
+ });
280
+
281
+ await new Promise(resolve => child.on('close', resolve));
282
+ }
283
+
284
+ async function chat(cliArgs) {
285
+ const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/cli.js');
286
+ const useCompiled = fs.existsSync(compiledCli);
287
+ const cmd = useCompiled ? 'node' : 'npx';
288
+ const args = useCompiled ? [compiledCli, 'chat', ...cliArgs] : ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'chat', ...cliArgs];
289
+ const child = spawn(cmd, args, {
290
+ cwd: projectRoot,
291
+ stdio: 'inherit',
292
+ env: { ...process.env, TS_NODE_CACHE: 'false' }
293
+ });
294
+
295
+ await new Promise(resolve => child.on('close', resolve));
296
+ }
297
+
270
298
  async function runDoctor() {
271
299
  const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/doctor.js');
272
300
  const useCompiled = fs.existsSync(compiledCli);
@@ -317,6 +345,8 @@ async function main() {
317
345
  case 'clear': await clearMemory(process.argv.slice(3)); break;
318
346
  case 'set-key': await setKey(process.argv.slice(3)); break;
319
347
  case 'wallet': await wallet(process.argv.slice(3)); break;
348
+ case 'chat': await chat(process.argv.slice(3)); break;
349
+ case 'uninstall': await uninstall(process.argv.slice(3)); break;
320
350
  case 'start': await start(); break;
321
351
  case 'stop': await stop(); break;
322
352
  case 'restart': await restart(); break;
@@ -341,6 +371,7 @@ Commands:
341
371
  start Start the Nyxora background daemon
342
372
  stop Stop the running daemon
343
373
  restart Restart the daemon
374
+ chat Chat interactively with the AI in terminal
344
375
  setup Run the interactive Setup Wizard
345
376
  dashboard Open the dashboard in your browser
346
377
  unlock Unlock an inactive dashboard session
@@ -350,6 +381,7 @@ Commands:
350
381
  autostart Enable/disable autostart on boot (usage: nyxora autostart enable)
351
382
  set-key Securely save API Key (usage: nyxora set-key <provider> <key>)
352
383
  wallet Manage your Web3 Wallet (usage: nyxora wallet update)
384
+ uninstall Wipe AI memory, securely delete keys, and remove configuration
353
385
 
354
386
  Options:
355
387
  -v, --version Show current version
@@ -315,6 +315,16 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
315
315
  }, sessionId);
316
316
  continue;
317
317
  }
318
+ if (!(0, skillManager_1.isSkillActive)(toolName)) {
319
+ console.warn(picocolors_1.default.red(`[Security] Blocked illegal execution of disabled skill: ${toolName}`));
320
+ result = `[System Error] Access denied: Skill '${toolName}' is currently disabled by the user.`;
321
+ exports.logger.addEntry({
322
+ role: "tool",
323
+ tool_call_id: toolCall.id,
324
+ content: result
325
+ }, sessionId);
326
+ continue;
327
+ }
318
328
  try {
319
329
  switch (toolName) {
320
330
  case 'get_balance': {
@@ -3,15 +3,92 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.encryptDataSync = encryptDataSync;
7
+ exports.decryptDataSync = decryptDataSync;
6
8
  exports.loadRpcConfig = loadRpcConfig;
7
9
  exports.saveRpcConfig = saveRpcConfig;
8
10
  exports.loadApiKeys = loadApiKeys;
9
11
  exports.saveApiKeys = saveApiKeys;
10
12
  exports.loadConfig = loadConfig;
11
13
  exports.saveConfig = saveConfig;
14
+ exports.loadPolicyConfig = loadPolicyConfig;
12
15
  const fs_1 = __importDefault(require("fs"));
13
16
  const yaml_1 = __importDefault(require("yaml"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const os_1 = __importDefault(require("os"));
19
+ const crypto_1 = __importDefault(require("crypto"));
14
20
  const paths_1 = require("./paths");
21
+ let cachedEncryptionKey = null;
22
+ function getEncryptionKeySync() {
23
+ if (cachedEncryptionKey)
24
+ return cachedEncryptionKey;
25
+ let masterKeyRaw = process.env.NYXORA_MASTER_KEY;
26
+ if (!masterKeyRaw) {
27
+ try {
28
+ const { execSync } = require('child_process');
29
+ const output = execSync(`node -e "require('@napi-rs/keyring').Entry.prototype.getPassword.call(new (require('@napi-rs/keyring').Entry)('nyxora', 'config_master')).then(console.log).catch(()=>console.log(''))"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
30
+ const pk = output?.trim();
31
+ if (pk)
32
+ masterKeyRaw = pk;
33
+ }
34
+ catch (e) {
35
+ // Ignore
36
+ }
37
+ }
38
+ if (!masterKeyRaw) {
39
+ try {
40
+ const masterKeyPath = path_1.default.join(os_1.default.homedir(), '.nyxora', 'auth', 'master.key');
41
+ if (fs_1.default.existsSync(masterKeyPath)) {
42
+ masterKeyRaw = fs_1.default.readFileSync(masterKeyPath, 'utf8').trim();
43
+ }
44
+ else {
45
+ masterKeyRaw = crypto_1.default.randomBytes(32).toString('hex');
46
+ try {
47
+ fs_1.default.writeFileSync(masterKeyPath, masterKeyRaw, { mode: 0o600 });
48
+ }
49
+ catch (e) { }
50
+ }
51
+ }
52
+ catch (e) {
53
+ masterKeyRaw = 'default_fallback_nyxora_key';
54
+ }
55
+ }
56
+ cachedEncryptionKey = crypto_1.default.createHash('sha256').update(masterKeyRaw).digest();
57
+ return cachedEncryptionKey;
58
+ }
59
+ function encryptDataSync(text) {
60
+ if (!text || text.startsWith('ENC:'))
61
+ return text;
62
+ const iv = crypto_1.default.randomBytes(12);
63
+ const cipher = crypto_1.default.createCipheriv('aes-256-gcm', getEncryptionKeySync(), iv);
64
+ let encrypted = cipher.update(text, 'utf8', 'hex');
65
+ encrypted += cipher.final('hex');
66
+ const authTag = cipher.getAuthTag().toString('hex');
67
+ return `ENC:${iv.toString('hex')}:${authTag}:${encrypted}`;
68
+ }
69
+ function decryptDataSync(encryptedText) {
70
+ if (!encryptedText)
71
+ return encryptedText;
72
+ if (!encryptedText.startsWith('ENC:')) {
73
+ return encryptedText;
74
+ }
75
+ try {
76
+ const parts = encryptedText.split(':');
77
+ if (parts.length < 4)
78
+ return encryptedText;
79
+ const iv = Buffer.from(parts[1], 'hex');
80
+ const authTag = Buffer.from(parts[2], 'hex');
81
+ const encrypted = parts.slice(3).join(':');
82
+ const decipher = crypto_1.default.createDecipheriv('aes-256-gcm', getEncryptionKeySync(), iv);
83
+ decipher.setAuthTag(authTag);
84
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
85
+ decrypted += decipher.final('utf8');
86
+ return decrypted;
87
+ }
88
+ catch (e) {
89
+ return encryptedText; // return raw if decryption fails
90
+ }
91
+ }
15
92
  function loadRpcConfig() {
16
93
  const rpcPath = (0, paths_1.getPath)('rpc_key.yaml');
17
94
  if (fs_1.default.existsSync(rpcPath)) {
@@ -51,8 +128,28 @@ function loadConfig() {
51
128
  try {
52
129
  const file = fs_1.default.readFileSync(configPath, 'utf8');
53
130
  const parsed = yaml_1.default.parse(file);
54
- // Auto-migration logic: move llm.credentials to root credentials
55
131
  let needsSave = false;
132
+ // Decrypt credentials
133
+ if (parsed.credentials) {
134
+ for (const key in parsed.credentials) {
135
+ if (parsed.credentials[key]) {
136
+ if (parsed.credentials[key].startsWith('ENC:'))
137
+ needsSave = true;
138
+ parsed.credentials[key] = decryptDataSync(parsed.credentials[key]);
139
+ }
140
+ }
141
+ }
142
+ if (parsed.integrations?.telegram?.bot_token) {
143
+ if (parsed.integrations.telegram.bot_token.startsWith('ENC:'))
144
+ needsSave = true;
145
+ parsed.integrations.telegram.bot_token = decryptDataSync(parsed.integrations.telegram.bot_token);
146
+ }
147
+ if (parsed.web3?.explorer_api_key) {
148
+ if (parsed.web3.explorer_api_key.startsWith('ENC:'))
149
+ needsSave = true;
150
+ parsed.web3.explorer_api_key = decryptDataSync(parsed.web3.explorer_api_key);
151
+ }
152
+ // Auto-migration logic: move llm.credentials to root credentials
56
153
  if (parsed.llm && parsed.llm.credentials) {
57
154
  if (!parsed.credentials) {
58
155
  parsed.credentials = {};
@@ -74,7 +171,7 @@ function loadConfig() {
74
171
  // Auto-migration logic: move permissions to policy.yaml
75
172
  const policyPath = (0, paths_1.getPath)('policy.yaml');
76
173
  if (!fs_1.default.existsSync(policyPath)) {
77
- const defaultPolicy = `max_usd_per_tx: ${parsed.permissions?.web3?.max_usd_per_tx || 999999999}\nwhitelist_only: false\nrequire_approval: true\n`;
174
+ const defaultPolicy = `auto_approve_limit_usd: 0\ncustom_llm_rules: []\n`;
78
175
  fs_1.default.writeFileSync(policyPath, defaultPolicy, 'utf8');
79
176
  console.log('[Config] Created default policy.yaml.');
80
177
  }
@@ -84,15 +181,14 @@ function loadConfig() {
84
181
  }
85
182
  if (needsSave) {
86
183
  try {
87
- const yamlStr = yaml_1.default.stringify(parsed);
88
- fs_1.default.writeFileSync(configPath, yamlStr, 'utf8');
89
- console.log('[Config] Auto-migrated llm.credentials to root credentials.');
184
+ saveConfig(parsed);
185
+ console.log('[Config] Auto-migrated config file safely.');
90
186
  }
91
187
  catch (e) {
92
188
  console.error('[Config] Failed to auto-migrate config file', e);
93
189
  }
94
190
  }
95
- return {
191
+ const validatedConfig = {
96
192
  agent: parsed.agent || { name: 'Nyxora-Default', description: 'Your Personal Web3 Assistant.', default_chain: 'base', default_router: 'auto', default_slippage: 'auto' },
97
193
  llm: parsed.llm || {
98
194
  provider: 'openai',
@@ -109,13 +205,18 @@ function loadConfig() {
109
205
  web3: { ...parsed.web3, rpc_urls: rpcUrls },
110
206
  integrations: parsed.integrations || {
111
207
  telegram: { enabled: false }
112
- }
208
+ },
209
+ skills: parsed.skills
113
210
  };
211
+ return validatedConfig;
114
212
  }
115
213
  catch (error) {
116
214
  if (error.code === 'ENOENT') {
117
215
  console.log('[Config] No config.yaml found. Using default configuration.');
118
216
  }
217
+ else if (error.name === 'YAMLError' || error.message?.includes('YAML')) {
218
+ console.warn('[Parser] YAML Parse Error:', error.message);
219
+ }
119
220
  else {
120
221
  console.error('[Config] Failed to load config.yaml. Using default configuration.', error);
121
222
  }
@@ -153,6 +254,7 @@ function saveConfig(newConfig) {
153
254
  if (configToSave.web3 && configToSave.web3.rpc_urls) {
154
255
  delete configToSave.web3.rpc_urls;
155
256
  }
257
+ // Keys are no longer encrypted before saving. They are stored in plain text.
156
258
  const yamlStr = yaml_1.default.stringify(configToSave);
157
259
  fs_1.default.writeFileSync(configPath, yamlStr, 'utf8');
158
260
  }
@@ -160,3 +262,15 @@ function saveConfig(newConfig) {
160
262
  console.error('Failed to save config.yaml', error);
161
263
  }
162
264
  }
265
+ function loadPolicyConfig() {
266
+ const policyPath = (0, paths_1.getPath)('policy.yaml');
267
+ if (fs_1.default.existsSync(policyPath)) {
268
+ try {
269
+ return yaml_1.default.parse(fs_1.default.readFileSync(policyPath, 'utf8')) || {};
270
+ }
271
+ catch (e) {
272
+ console.error('[Config] Failed to parse policy.yaml', e);
273
+ }
274
+ }
275
+ return {};
276
+ }
@@ -0,0 +1,82 @@
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.chatInteractive = chatInteractive;
7
+ const prompts_1 = require("@clack/prompts");
8
+ const picocolors_1 = __importDefault(require("picocolors"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const paths_1 = require("../config/paths");
11
+ async function chatInteractive() {
12
+ const tokenFile = (0, paths_1.getPath)('auth.token');
13
+ if (!fs_1.default.existsSync(tokenFile)) {
14
+ console.log(picocolors_1.default.red('❌ Nyxora daemon is not running. Please start it with `nyxora start`.'));
15
+ process.exit(1);
16
+ }
17
+ let token = fs_1.default.readFileSync(tokenFile, 'utf8').trim();
18
+ if (token.startsWith('{')) {
19
+ try {
20
+ const parsed = JSON.parse(token);
21
+ token = parsed.token;
22
+ }
23
+ catch (e) { }
24
+ }
25
+ const logo = `
26
+ ███╗ ██╗██╗ ██╗██╗ ██╗ ██████╗ ██████╗ █████╗
27
+ ████╗ ██║╚██╗ ██╔╝╚██╗██╔╝██╔═══██╗██╔══██╗██╔══██╗
28
+ ██╔██╗ ██║ ╚████╔╝ ╚███╔╝ ██║ ██║██████╔╝███████║
29
+ ██║╚██╗██║ ╚██╔╝ ██╔██╗ ██║ ██║██╔══██╗██╔══██║
30
+ ██║ ╚████║ ██║ ██╔╝ ██╗╚██████╔╝██║ ██║██║ ██║
31
+ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
32
+ `;
33
+ console.log(picocolors_1.default.cyan(logo));
34
+ (0, prompts_1.intro)(picocolors_1.default.inverse(' Nyxora Interactive Shell '));
35
+ console.log(picocolors_1.default.gray('Type your message and press Enter. Type "exit" or press Ctrl+C to quit.\n'));
36
+ while (true) {
37
+ const input = await (0, prompts_1.text)({
38
+ message: picocolors_1.default.cyan('You:'),
39
+ placeholder: 'Send a message...',
40
+ });
41
+ if ((0, prompts_1.isCancel)(input) || input.toString().trim().toLowerCase() === 'exit' || input.toString().trim().toLowerCase() === 'quit') {
42
+ (0, prompts_1.cancel)('Chat session ended.');
43
+ process.exit(0);
44
+ }
45
+ const messageStr = input.toString().trim();
46
+ if (!messageStr)
47
+ continue;
48
+ const s = (0, prompts_1.spinner)();
49
+ s.start('Thinking...');
50
+ try {
51
+ const response = await fetch('http://localhost:3000/api/chat', {
52
+ method: 'POST',
53
+ headers: {
54
+ 'Content-Type': 'application/json',
55
+ 'x-nyxora-token': token,
56
+ },
57
+ body: JSON.stringify({ message: messageStr, session_id: 'cli-chat' })
58
+ });
59
+ if (!response.ok) {
60
+ s.stop(picocolors_1.default.red('API Error.'));
61
+ if (response.status === 401) {
62
+ console.log(picocolors_1.default.red('Unauthorized: Token is invalid. Please restart the daemon.'));
63
+ process.exit(1);
64
+ }
65
+ else {
66
+ console.log(picocolors_1.default.red(`Gateway returned status ${response.status}`));
67
+ }
68
+ continue;
69
+ }
70
+ const data = await response.json();
71
+ s.stop(picocolors_1.default.green('Nyxora:'));
72
+ let finalReply = data.response || '';
73
+ // Strip <think> tags for clean UI
74
+ finalReply = finalReply.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
75
+ console.log(finalReply + '\n');
76
+ }
77
+ catch (error) {
78
+ s.stop(picocolors_1.default.red('Connection failed.'));
79
+ console.log(picocolors_1.default.red(`Is the daemon running? (http://localhost:3000)`));
80
+ }
81
+ }
82
+ }
@@ -156,6 +156,69 @@ async function main() {
156
156
  process.exit(1);
157
157
  }
158
158
  }
159
+ // Check for chat command
160
+ if (process.argv.includes('chat')) {
161
+ const { chatInteractive } = await Promise.resolve().then(() => __importStar(require('./chat')));
162
+ await chatInteractive();
163
+ process.exit(0);
164
+ }
165
+ // Check for uninstall command
166
+ if (process.argv.includes('uninstall')) {
167
+ console.log(picocolors_1.default.cyan('\n🗑️ Nyxora Uninstallation Wizard'));
168
+ let shouldProceed = process.argv.includes('--force') || process.argv.includes('-y');
169
+ if (!shouldProceed) {
170
+ const proceed = await (0, prompts_1.confirm)({
171
+ message: picocolors_1.default.bgRed(picocolors_1.default.white(' ⚠️ WARNING ')) + picocolors_1.default.yellow(' This will PERMANENTLY WIPE the AI\'s local memory, securely delete your Private Key and Master Key from the OS Keyring, and remove all configuration.\n\nAre you absolutely sure you want to proceed?'),
172
+ });
173
+ if ((0, prompts_1.isCancel)(proceed) || !proceed)
174
+ process.exit(0);
175
+ }
176
+ console.log(picocolors_1.default.gray('\nStarting cleanup process...'));
177
+ // 1. Wipe AI Memory
178
+ try {
179
+ const { Logger } = require('../memory/logger');
180
+ const logger = new Logger();
181
+ logger.clear();
182
+ console.log(picocolors_1.default.green('✅ AI memory wiped successfully.'));
183
+ }
184
+ catch (e) {
185
+ console.log(picocolors_1.default.gray('⚠️ Could not clear AI memory (may not exist).'));
186
+ }
187
+ // 2. Delete OS Keyring entries
188
+ try {
189
+ const { Entry } = await Promise.resolve().then(() => __importStar(require('@napi-rs/keyring')));
190
+ const walletEntry = new Entry('nyxora', 'wallet');
191
+ try {
192
+ await walletEntry.deletePassword();
193
+ }
194
+ catch (e) { }
195
+ console.log(picocolors_1.default.green('✅ Wallet key removed from OS Keyring.'));
196
+ const masterEntry = new Entry('nyxora', 'config_master');
197
+ try {
198
+ await masterEntry.deletePassword();
199
+ }
200
+ catch (e) { }
201
+ console.log(picocolors_1.default.green('✅ Master key removed from OS Keyring.'));
202
+ }
203
+ catch (e) {
204
+ console.log(picocolors_1.default.gray('⚠️ Could not access OS Keyring.'));
205
+ }
206
+ // 3. Delete ~/.nyxora directory
207
+ try {
208
+ const targetDir = path_1.default.join(os_1.default.homedir(), '.nyxora');
209
+ if (fs_1.default.existsSync(targetDir)) {
210
+ fs_1.default.rmSync(targetDir, { recursive: true, force: true });
211
+ console.log(picocolors_1.default.green('✅ Configuration directory (~/.nyxora) deleted.'));
212
+ }
213
+ }
214
+ catch (e) {
215
+ console.log(picocolors_1.default.red(`❌ Failed to delete ~/.nyxora: ${e.message}`));
216
+ }
217
+ console.log(picocolors_1.default.cyan('\n✨ Nyxora data has been completely removed.'));
218
+ console.log(picocolors_1.default.white('To complete the uninstallation, run:'));
219
+ console.log(picocolors_1.default.green(' npm uninstall -g nyxora\n'));
220
+ process.exit(0);
221
+ }
159
222
  // 2. Setup boilerplate files if in global mode and they don't exist
160
223
  let isFirstBoot = false;
161
224
  if (isGlobalMode) {