nyxora 1.6.2 → 1.6.3

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 (41) hide show
  1. package/README.md +22 -12
  2. package/SECURITY.md +25 -21
  3. package/assets/raw-diagram.png +0 -0
  4. package/assets/security-flow.png +0 -0
  5. package/bin/nyxora.mjs +236 -0
  6. package/launcher.js +8 -3
  7. package/launcher.ts +28 -1
  8. package/package.json +11 -7
  9. package/packages/core/package.json +4 -3
  10. package/packages/core/src/agent/reasoning.ts +10 -8
  11. package/packages/core/src/config/parser.ts +2 -1
  12. package/packages/core/src/gateway/cli.ts +2 -64
  13. package/packages/core/src/gateway/server.ts +89 -8
  14. package/packages/core/src/gateway/setup-cli.ts +7 -0
  15. package/packages/core/src/gateway/setup.ts +51 -28
  16. package/packages/core/src/gateway/telegram.ts +147 -89
  17. package/packages/core/src/memory/logger.ts +63 -7
  18. package/packages/core/src/system/pluginManager.ts +48 -34
  19. package/packages/core/src/utils/state.ts +15 -2
  20. package/packages/core/src/web3/config.ts +18 -3
  21. package/packages/core/src/web3/skills/marketAnalysis.ts +43 -17
  22. package/packages/core/src/web3/skills/swapToken.ts +9 -1
  23. package/packages/dashboard/dist/assets/index-BTP1WrFj.js +194 -0
  24. package/packages/dashboard/dist/assets/index-POJM-7Fd.css +1 -0
  25. package/packages/dashboard/dist/favicon.svg +1 -1
  26. package/packages/dashboard/dist/index.html +2 -2
  27. package/packages/dashboard/package-lock.json +2 -2
  28. package/packages/dashboard/package.json +1 -1
  29. package/packages/dashboard/public/favicon.svg +1 -1
  30. package/packages/dashboard/src/App.tsx +224 -167
  31. package/packages/dashboard/src/Settings.tsx +55 -0
  32. package/packages/dashboard/src/Skills.tsx +8 -1
  33. package/packages/dashboard/src/index.css +146 -35
  34. package/packages/policy/package.json +1 -1
  35. package/packages/policy/src/server.ts +21 -28
  36. package/packages/signer/package.json +1 -1
  37. package/packages/signer/src/server.ts +39 -13
  38. package/bin/nyxora.js +0 -13
  39. package/packages/dashboard/dist/assets/index-BK4qmIy6.js +0 -200
  40. package/packages/dashboard/dist/assets/index-C1m4ohce.css +0 -1
  41. package/packages/dashboard/src/Memory.tsx +0 -110
@@ -1,16 +1,20 @@
1
1
  :root {
2
- --bg-color: #0f172a;
3
- --bg-secondary: #1e293b;
4
- --bg-sidebar: #0b1120;
5
- --text-primary: #f8fafc;
6
- --text-secondary: #94a3b8;
7
- --accent: #3b82f6;
8
- --accent-hover: #2563eb;
9
- --glass-bg: rgba(30, 41, 59, 0.7);
10
- --glass-border: rgba(255, 255, 255, 0.1);
11
- --chat-user: #3b82f6;
12
- --chat-agent: #1e293b;
13
- --tool-bg: #0f172a;
2
+ /* Nord Theme - Dark Default */
3
+ --bg-color: #3b4252; /* Main content background */
4
+ --bg-secondary: #434c5e;
5
+ --bg-sidebar: #2e3440; /* Sidebar darker background */
6
+ --text-primary: #eceff4;
7
+ --text-secondary: #d8dee9;
8
+ --accent: #88c0d0; /* Frost Light Blue/Cyan */
9
+ --accent-hover: #81a1c1;
10
+ --glass-bg: rgba(46, 52, 64, 0.7);
11
+ --glass-border: rgba(216, 222, 233, 0.1);
12
+ --chat-user: #88c0d0;
13
+ --chat-user-text: #2e3440; /* Dark text for contrast against cyan bubble */
14
+ --chat-agent: #2e3440;
15
+ --tool-bg: #4c566a;
16
+ --success: #a3be8c;
17
+ --danger: #bf616a;
14
18
  }
15
19
 
16
20
  * {
@@ -36,8 +40,7 @@ body {
36
40
  /* SIDEBAR */
37
41
  .sidebar {
38
42
  width: 280px;
39
- background-color: rgba(11, 17, 32, 0.85);
40
- backdrop-filter: blur(20px);
43
+ background-color: var(--bg-sidebar);
41
44
  border-right: 1px solid var(--glass-border);
42
45
  display: flex;
43
46
  flex-direction: column;
@@ -56,11 +59,11 @@ body {
56
59
  }
57
60
 
58
61
  .agent-avatar {
59
- background: rgba(59, 130, 246, 0.15);
62
+ background: rgba(136, 192, 208, 0.15);
60
63
  padding: 12px;
61
64
  border-radius: 16px;
62
- border: 1px solid rgba(59, 130, 246, 0.3);
63
- box-shadow: 0 0 20px 0 rgba(59, 130, 246, 0.2);
65
+ border: 1px solid rgba(136, 192, 208, 0.3);
66
+ box-shadow: 0 0 20px 0 rgba(136, 192, 208, 0.2);
64
67
  display: flex;
65
68
  justify-content: center;
66
69
  align-items: center;
@@ -118,12 +121,10 @@ body {
118
121
  }
119
122
 
120
123
  .sidebar-section {
121
- padding: 24px 24px 12px;
122
- font-size: 0.7rem;
123
- text-transform: uppercase;
124
- letter-spacing: 0.15em;
125
- color: #64748b;
126
- font-weight: 700;
124
+ padding: 24px 24px 8px;
125
+ font-size: 0.75rem;
126
+ color: #8b9bb4;
127
+ font-weight: 500;
127
128
  }
128
129
 
129
130
  .sidebar-nav {
@@ -136,11 +137,11 @@ body {
136
137
  .nav-item {
137
138
  display: flex;
138
139
  align-items: center;
139
- gap: 14px;
140
- padding: 12px 16px;
141
- border-radius: 12px;
140
+ gap: 10px;
141
+ padding: 8px 12px;
142
+ border-radius: 8px;
142
143
  color: #94a3b8;
143
- font-size: 0.95rem;
144
+ font-size: 0.8rem;
144
145
  font-weight: 500;
145
146
  cursor: pointer;
146
147
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
@@ -159,7 +160,47 @@ body {
159
160
 
160
161
  .nav-item:hover .nav-icon {
161
162
  transform: scale(1.1);
162
- color: var(--accent);
163
+ color: #fff;
164
+ }
165
+
166
+ .sessions-list {
167
+ gap: 0px !important;
168
+ justify-content: flex-start !important;
169
+ flex: none !important;
170
+ height: max-content !important;
171
+ }
172
+
173
+ .session-item {
174
+ justify-content: space-between;
175
+ padding: 4px 10px !important;
176
+ margin-bottom: 2px !important;
177
+ margin-top: 0px !important;
178
+ min-height: 28px !important;
179
+ height: max-content !important;
180
+ flex: none !important;
181
+ }
182
+
183
+ .delete-session-btn {
184
+ background: transparent;
185
+ border: none;
186
+ color: #bf616a;
187
+ cursor: pointer;
188
+ opacity: 0;
189
+ transition: opacity 0.2s ease, transform 0.2s ease;
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ padding: 4px;
194
+ border-radius: 6px;
195
+ }
196
+
197
+ .delete-session-btn:hover {
198
+ background: rgba(191, 97, 106, 0.2);
199
+ transform: scale(1.1);
200
+ }
201
+
202
+ .session-item:hover .delete-session-btn {
203
+ opacity: 1;
163
204
  }
164
205
 
165
206
  .nav-item.active {
@@ -192,8 +233,7 @@ body {
192
233
  align-items: center;
193
234
  justify-content: space-between;
194
235
  padding: 0 24px;
195
- background: rgba(15, 23, 42, 0.6);
196
- backdrop-filter: blur(12px);
236
+ background: var(--bg-color);
197
237
  }
198
238
 
199
239
  .topbar-left {
@@ -324,12 +364,38 @@ body {
324
364
  animation: fadeIn 0.3s ease-out forwards;
325
365
  }
326
366
 
367
+ .message-wrapper.agent {
368
+ align-self: flex-start;
369
+ flex-direction: row;
370
+ align-items: flex-end;
371
+ gap: 8px;
372
+ }
327
373
  .message-wrapper.user {
328
374
  align-self: flex-end;
375
+ flex-direction: row-reverse;
376
+ align-items: flex-end;
377
+ gap: 8px;
329
378
  }
330
379
 
331
- .message-wrapper.agent {
332
- align-self: flex-start;
380
+ .copy-btn {
381
+ background: transparent;
382
+ border: none;
383
+ color: var(--text-secondary);
384
+ cursor: pointer;
385
+ padding: 6px;
386
+ border-radius: 6px;
387
+ opacity: 0;
388
+ transition: opacity 0.2s, background 0.2s;
389
+ display: flex;
390
+ align-items: center;
391
+ justify-content: center;
392
+ }
393
+ .copy-btn:hover {
394
+ background: var(--bg-secondary);
395
+ color: var(--text-primary);
396
+ }
397
+ .message-wrapper:hover .copy-btn {
398
+ opacity: 1;
333
399
  }
334
400
 
335
401
  .message-bubble {
@@ -343,7 +409,7 @@ body {
343
409
 
344
410
  .user .message-bubble {
345
411
  background-color: var(--chat-user);
346
- color: white;
412
+ color: var(--chat-user-text);
347
413
  border-bottom-right-radius: 4px;
348
414
  }
349
415
 
@@ -351,6 +417,7 @@ body {
351
417
  background-color: var(--chat-agent);
352
418
  border: 1px solid var(--glass-border);
353
419
  border-bottom-left-radius: 4px;
420
+ color: var(--text-primary);
354
421
  }
355
422
 
356
423
  .tool-call {
@@ -360,14 +427,14 @@ body {
360
427
  padding: 10px 14px;
361
428
  border-radius: 12px;
362
429
  margin-top: 10px;
363
- border: 1px solid rgba(255, 255, 255, 0.05);
430
+ border: 1px solid var(--glass-border);
364
431
  display: flex;
365
432
  align-items: center;
366
433
  gap: 8px;
367
434
  }
368
435
 
369
436
  .tool-call code {
370
- color: #a78bfa;
437
+ color: var(--accent);
371
438
  font-family: monospace;
372
439
  }
373
440
 
@@ -507,3 +574,47 @@ body {
507
574
  from { opacity: 0; transform: translateY(10px); }
508
575
  to { opacity: 1; transform: translateY(0); }
509
576
  }
577
+
578
+ .prompt-suggestions {
579
+ display: flex;
580
+ gap: 12px;
581
+ margin-bottom: 12px;
582
+ overflow-x: auto;
583
+ }
584
+ .prompt-suggestions button {
585
+ background: var(--bg-secondary);
586
+ color: var(--text-secondary);
587
+ border: 1px solid var(--glass-border);
588
+ padding: 8px 16px;
589
+ border-radius: 20px;
590
+ font-size: 0.85rem;
591
+ cursor: pointer;
592
+ white-space: nowrap;
593
+ transition: all 0.2s;
594
+ }
595
+ .prompt-suggestions button:hover {
596
+ border-color: var(--accent);
597
+ color: var(--text-primary);
598
+ }
599
+ .trending-tokens {
600
+ display: flex;
601
+ align-items: center;
602
+ gap: 12px;
603
+ margin-top: 12px;
604
+ font-size: 0.8rem;
605
+ color: var(--text-secondary);
606
+ }
607
+ .token-tag {
608
+ background: var(--accent);
609
+ color: var(--bg-color);
610
+ padding: 4px 10px;
611
+ border-radius: 12px;
612
+ font-weight: 600;
613
+ cursor: pointer;
614
+ transition: transform 0.2s, filter 0.2s;
615
+ }
616
+
617
+ .token-tag:hover {
618
+ transform: translateY(-2px);
619
+ filter: brightness(1.2);
620
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyxora/policy",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "private": true,
5
5
  "main": "src/server.ts",
6
6
  "dependencies": {
@@ -5,6 +5,7 @@ import yaml from 'yaml';
5
5
  import http from 'http';
6
6
  import { z } from 'zod';
7
7
  import path from 'path';
8
+ import crypto from 'crypto';
8
9
 
9
10
  const PORT = 3001;
10
11
  const JWT_SECRET = process.env.INTERNAL_AUTH_TOKEN;
@@ -77,33 +78,6 @@ app.get('/address', (req, res) => {
77
78
  signerReq.end();
78
79
  });
79
80
 
80
- // Proxy POST /unlock to Signer
81
- app.post('/unlock', (req, res) => {
82
- const { keystore, password } = req.body;
83
- const requestPayload = JSON.stringify({ keystore, password });
84
-
85
- const options = {
86
- socketPath: SIGNER_SOCKET,
87
- path: '/unlock',
88
- method: 'POST',
89
- headers: {
90
- 'Content-Type': 'application/json',
91
- 'Authorization': `Bearer ${jwt.sign({ service: 'policy' }, JWT_SECRET, { expiresIn: '1m' })}`,
92
- 'Content-Length': Buffer.byteLength(requestPayload)
93
- }
94
- };
95
-
96
- const signerReq = http.request(options, (signerRes) => {
97
- let data = '';
98
- signerRes.on('data', chunk => data += chunk);
99
- signerRes.on('end', () => res.status(signerRes.statusCode || 200).json(JSON.parse(data)));
100
- });
101
-
102
- signerReq.on('error', (e) => res.status(500).json({ error: 'Failed to unlock vault: ' + e.message }));
103
- signerReq.write(requestPayload);
104
- signerReq.end();
105
- });
106
-
107
81
  app.post('/request-tx', (req, res) => {
108
82
  try {
109
83
  const payload = TxRequestSchema.parse(req.body);
@@ -111,6 +85,17 @@ app.post('/request-tx', (req, res) => {
111
85
 
112
86
  // Auto-approve bypass for internal trusted features like CL/TP
113
87
  if (payload.autoApprove) {
88
+ const providedSig = req.body.internalSignature;
89
+ if (!providedSig) return res.status(403).json({ error: 'Missing internal signature for autoApprove' });
90
+
91
+ // Ensure amountWei exists
92
+ const amountWei = payload.details?.amountWei || '';
93
+ const expectedSig = crypto.createHmac('sha256', JWT_SECRET).update(payload.chainName + amountWei).digest('hex');
94
+
95
+ if (providedSig !== expectedSig) {
96
+ return res.status(403).json({ error: 'Invalid internal signature for autoApprove' });
97
+ }
98
+
114
99
  const requestPayload = JSON.stringify({ txPayload: payload });
115
100
  const options = {
116
101
  socketPath: SIGNER_SOCKET,
@@ -153,11 +138,19 @@ app.get('/pending-tx', (req, res) => {
153
138
 
154
139
  app.post('/approve-tx/:id', (req, res) => {
155
140
  const txId = req.params.id;
141
+ const { nonce, approvalHash } = req.body;
156
142
  const tx = pendingTransactions[txId];
157
143
 
158
144
  if (!tx) return res.status(404).json({ error: 'Transaction not found' });
159
145
  if (tx.status !== 'pending') return res.status(400).json({ error: 'Transaction not pending' });
160
-
146
+ if (!nonce || !approvalHash) return res.status(400).json({ error: 'Missing cryptographic approval parameters' });
147
+
148
+ // Cryptographically Bound Approval verification
149
+ const expectedHash = crypto.createHash('sha256').update(txId + nonce + JWT_SECRET).digest('hex');
150
+ if (approvalHash !== expectedHash) {
151
+ return res.status(403).json({ error: 'Invalid Challenge Nonce Hash. Cryptographic approval failed.' });
152
+ }
153
+
161
154
  const requestPayload = JSON.stringify({
162
155
  txPayload: tx
163
156
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyxora/signer",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "private": true,
5
5
  "main": "src/server.ts",
6
6
  "dependencies": {
@@ -17,9 +17,48 @@ if (!JWT_SECRET) {
17
17
  const app = express();
18
18
  app.use(express.json());
19
19
 
20
+ import keytar from 'keytar';
21
+ import path from 'path';
22
+ import os from 'os';
23
+
20
24
  let vaultPrivateKey: `0x${string}` | null = null;
21
25
  let vaultAddress: string | null = null;
22
26
 
27
+ // Auto-unlock from OS Keyring or fallback .env
28
+ async function loadPrivateKey() {
29
+ try {
30
+ const pk = await keytar.getPassword('nyxora', 'wallet');
31
+ if (pk) {
32
+ vaultPrivateKey = pk.startsWith('0x') ? (pk as `0x${string}`) : (`0x${pk}` as `0x${string}`);
33
+ const account = privateKeyToAccount(vaultPrivateKey);
34
+ vaultAddress = account.address;
35
+ console.log(`✅ [Signer] Vault unlocked securely from OS Keyring. Agent Address: ${vaultAddress}`);
36
+ return;
37
+ }
38
+ } catch (e) {
39
+ console.warn(`⚠️ [Signer] OS Keyring failed (module mismatch or headless). Using fallback.`);
40
+ }
41
+
42
+ // Fallback to vault.key
43
+ const vaultPath = path.join(os.homedir(), '.nyxora', 'vault.key');
44
+ if (fs.existsSync(vaultPath)) {
45
+ const content = fs.readFileSync(vaultPath, 'utf8');
46
+ const match = content.match(/PRIVATE_KEY=(.+)/);
47
+ if (match && match[1]) {
48
+ const pk = match[1].trim();
49
+ vaultPrivateKey = pk.startsWith('0x') ? (pk as `0x${string}`) : (`0x${pk}` as `0x${string}`);
50
+ const account = privateKeyToAccount(vaultPrivateKey);
51
+ vaultAddress = account.address;
52
+ console.log(`✅ [Signer] Vault unlocked from vault.key fallback. Agent Address: ${vaultAddress}`);
53
+ }
54
+ } else {
55
+ console.log(`❌ [Signer] No Private Key found in OS Keyring or vault.key. Web3 features will fail.`);
56
+ }
57
+ }
58
+
59
+ // Load it immediately
60
+ loadPrivateKey();
61
+
23
62
  // Nonce Management
24
63
  const nonceLocks: Record<number, Promise<void>> = {};
25
64
  const nonceCache: Record<number, number> = {};
@@ -42,19 +81,6 @@ app.use((req, res, next) => {
42
81
  }
43
82
  });
44
83
 
45
- app.post('/unlock', (req, res) => {
46
- const { keystore, password } = req.body;
47
- try {
48
- const pk = decryptKey(keystore, password);
49
- vaultPrivateKey = pk.startsWith('0x') ? pk as `0x${string}` : `0x${pk}` as `0x${string}`;
50
- const account = privateKeyToAccount(vaultPrivateKey);
51
- vaultAddress = account.address;
52
- res.json({ success: true, address: vaultAddress });
53
- } catch (err: any) {
54
- res.status(401).json({ error: 'Invalid password or keystore' });
55
- }
56
- });
57
-
58
84
  app.get('/address', (req, res) => {
59
85
  if (!vaultAddress) return res.status(403).json({ error: 'Vault is locked' });
60
86
  res.json({ address: vaultAddress });
package/bin/nyxora.js DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env node
2
- const path = require('path');
3
- process.env.TS_NODE_SKIP_IGNORE = "true";
4
- process.env.TS_NODE_TRANSPILE_ONLY = "true";
5
- process.env.TS_NODE_PROJECT = path.join(__dirname, '../tsconfig.json');
6
- require('ts-node/register');
7
-
8
- const args = process.argv.slice(2);
9
- if (args[0] === 'setup') {
10
- require('../packages/core/src/gateway/cli.ts');
11
- } else {
12
- require('../launcher.ts');
13
- }