nyxora 1.6.13 → 1.7.1

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 (69) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +2 -2
  3. package/bin/nyxora.mjs +8 -0
  4. package/package.json +1 -2
  5. package/packages/core/package.json +1 -1
  6. package/packages/core/src/agent/limitOrderManager.ts +2 -2
  7. package/packages/core/src/agent/reasoning.ts +36 -14
  8. package/packages/core/src/config/parser.ts +99 -9
  9. package/packages/core/src/gateway/cli.ts +28 -2
  10. package/packages/core/src/gateway/setup.ts +45 -10
  11. package/packages/core/src/gateway/telegram.ts +53 -12
  12. package/packages/core/src/system/skills/searchWeb.ts +187 -21
  13. package/packages/core/src/utils/formatter.ts +12 -5
  14. package/packages/core/src/web3/config.ts +7 -1
  15. package/packages/core/src/web3/skills/bridgeToken.ts +4 -3
  16. package/packages/core/src/web3/skills/checkAddress.ts +2 -2
  17. package/packages/core/src/web3/skills/checkPortfolio.ts +55 -39
  18. package/packages/core/src/web3/skills/checkSecurity.ts +3 -2
  19. package/packages/core/src/web3/skills/customTx.ts +2 -2
  20. package/packages/core/src/web3/skills/getBalance.ts +3 -3
  21. package/packages/core/src/web3/skills/marketAnalysis.ts +2 -2
  22. package/packages/core/src/web3/skills/mintNft.ts +2 -2
  23. package/packages/core/src/web3/skills/swapToken.ts +4 -3
  24. package/packages/core/src/web3/skills/transfer.ts +2 -2
  25. package/packages/core/src/web3/utils/tokens.ts +8 -0
  26. package/packages/dashboard/dist/assets/{index-24OeXn-k.css → index-BSk4CLkG.css} +1 -1
  27. package/packages/dashboard/dist/assets/{index-BuYfTEKE.js → index-Dc3Tu0Te.js} +21 -21
  28. package/packages/dashboard/dist/index.html +2 -2
  29. package/packages/dashboard/package.json +1 -1
  30. package/packages/mcp-server/package.json +1 -1
  31. package/packages/policy/package.json +1 -1
  32. package/packages/signer/package.json +1 -1
  33. package/launcher.js +0 -48
  34. package/packages/core/src/agent/reasoning.d.ts.map +0 -1
  35. package/packages/core/src/config/parser.d.ts.map +0 -1
  36. package/packages/core/src/gateway/cli.d.ts.map +0 -1
  37. package/packages/core/src/memory/logger.d.ts.map +0 -1
  38. package/packages/core/src/utils/safeLogger.js +0 -59
  39. package/packages/core/src/web3/config.d.ts.map +0 -1
  40. package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
  41. package/packages/dashboard/public/favicon.svg +0 -10
  42. package/packages/dashboard/public/icons.svg +0 -24
  43. package/packages/dashboard/src/App.css +0 -184
  44. package/packages/dashboard/src/App.tsx +0 -588
  45. package/packages/dashboard/src/BalanceWidget.tsx +0 -65
  46. package/packages/dashboard/src/MarketWidget.tsx +0 -73
  47. package/packages/dashboard/src/NetworkSelector.tsx +0 -64
  48. package/packages/dashboard/src/NyxoraLogo.tsx +0 -25
  49. package/packages/dashboard/src/OsSkills.tsx +0 -352
  50. package/packages/dashboard/src/Overview.tsx +0 -157
  51. package/packages/dashboard/src/PendingTransactions.tsx +0 -75
  52. package/packages/dashboard/src/Settings.tsx +0 -338
  53. package/packages/dashboard/src/Skills.tsx +0 -200
  54. package/packages/dashboard/src/SwapWidget.tsx +0 -141
  55. package/packages/dashboard/src/TransactionWidget.tsx +0 -95
  56. package/packages/dashboard/src/assets/hero.png +0 -0
  57. package/packages/dashboard/src/assets/react.svg +0 -1
  58. package/packages/dashboard/src/assets/vite.svg +0 -1
  59. package/packages/dashboard/src/components/PillSelect.tsx +0 -65
  60. package/packages/dashboard/src/index.css +0 -807
  61. package/packages/dashboard/src/main.tsx +0 -10
  62. package/packages/dashboard/src/overview.css +0 -304
  63. package/packages/dashboard/src/utils/api.ts +0 -31
  64. package/packages/mcp-server/tsconfig.tsbuildinfo +0 -1
  65. package/test-address.ts +0 -11
  66. package/test-all-chains.ts +0 -19
  67. package/test-db.ts +0 -3
  68. package/test-portfolio.ts +0 -14
  69. package/tsconfig.tsbuildinfo +0 -1
@@ -1,73 +0,0 @@
1
- import React from 'react';
2
- import { TrendingUp, TrendingDown, Activity } from 'lucide-react';
3
-
4
- interface MarketWidgetProps {
5
- data: string;
6
- }
7
-
8
- const MarketWidget: React.FC<MarketWidgetProps> = ({ data }) => {
9
- let parsedData = null;
10
- try {
11
- parsedData = JSON.parse(data);
12
- } catch (e) {
13
- return null;
14
- }
15
-
16
- if (parsedData.error) {
17
- return (
18
- <div style={{ background: 'rgba(239, 68, 68, 0.1)', border: '1px solid rgba(239, 68, 68, 0.3)', padding: '16px', borderRadius: '16px' }}>
19
- <p style={{ color: '#ef4444' }}>{parsedData.error}</p>
20
- </div>
21
- );
22
- }
23
-
24
- const { coin, priceUsd, change24h } = parsedData;
25
- const isPositive = change24h >= 0;
26
- const color = isPositive ? '#4ade80' : '#ef4444';
27
- const bgColor = isPositive ? 'rgba(74, 222, 128, 0.1)' : 'rgba(239, 68, 68, 0.1)';
28
- const Icon = isPositive ? TrendingUp : TrendingDown;
29
-
30
- return (
31
- <div style={{
32
- background: 'linear-gradient(145deg, rgba(30, 41, 59, 0.8) 0%, rgba(15, 23, 42, 0.8) 100%)',
33
- border: '1px solid rgba(255, 255, 255, 0.1)',
34
- borderRadius: '24px',
35
- padding: '24px',
36
- boxShadow: '0 10px 40px -10px rgba(0,0,0,0.5)'
37
- }}>
38
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '24px' }}>
39
- <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
40
- <div style={{ background: 'rgba(59, 130, 246, 0.2)', padding: '12px', borderRadius: '16px', border: '1px solid rgba(59, 130, 246, 0.3)' }}>
41
- <Activity size={24} color="#3b82f6" />
42
- </div>
43
- <div>
44
- <h3 style={{ margin: 0, fontSize: '1.2rem', color: 'white', textTransform: 'capitalize' }}>{coin}</h3>
45
- <span style={{ fontSize: '0.85rem', color: '#94a3b8' }}>Live Price (USD)</span>
46
- </div>
47
- </div>
48
- <div style={{
49
- background: bgColor,
50
- color: color,
51
- padding: '6px 12px',
52
- borderRadius: '12px',
53
- display: 'flex',
54
- alignItems: 'center',
55
- gap: '6px',
56
- fontWeight: 600,
57
- fontSize: '0.9rem'
58
- }}>
59
- <Icon size={16} />
60
- {isPositive ? '+' : ''}{change24h.toFixed(2)}%
61
- </div>
62
- </div>
63
-
64
- <div style={{ display: 'flex', alignItems: 'baseline', gap: '8px', overflow: 'hidden' }}>
65
- <span style={{ fontSize: '3rem', fontWeight: 800, color: 'white', letterSpacing: '-1px', wordBreak: 'break-all' }}>
66
- ${priceUsd.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 6 })}
67
- </span>
68
- </div>
69
- </div>
70
- );
71
- };
72
-
73
- export default MarketWidget;
@@ -1,64 +0,0 @@
1
- import React, { useState, useRef, useEffect } from 'react';
2
- import { Network, ChevronDown } from 'lucide-react';
3
-
4
- const NETWORKS = [
5
- { id: 'ethereum', label: 'Ethereum' },
6
- { id: 'bsc', label: 'BNB Smart Chain' },
7
- { id: 'base', label: 'Base' },
8
- { id: 'optimism', label: 'Optimism' },
9
- { id: 'arbitrum', label: 'Arbitrum' },
10
- { id: 'sepolia', label: 'Sepolia (Testnet)' }
11
- ];
12
-
13
- interface NetworkSelectorProps {
14
- value: string;
15
- onChange: (network: string) => void;
16
- }
17
-
18
- export const NetworkSelector: React.FC<NetworkSelectorProps> = ({ value, onChange }) => {
19
- const [isOpen, setIsOpen] = useState(false);
20
- const dropdownRef = useRef<HTMLDivElement>(null);
21
-
22
- useEffect(() => {
23
- const handleClickOutside = (event: MouseEvent) => {
24
- if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
25
- setIsOpen(false);
26
- }
27
- };
28
- document.addEventListener('mousedown', handleClickOutside);
29
- return () => document.removeEventListener('mousedown', handleClickOutside);
30
- }, []);
31
-
32
- const currentNetwork = NETWORKS.find(n => n.id === value) || NETWORKS[0];
33
-
34
- return (
35
- <div className="custom-network-selector" ref={dropdownRef}>
36
- <button
37
- className="network-selector-pill"
38
- onClick={() => setIsOpen(!isOpen)}
39
- aria-expanded={isOpen}
40
- >
41
- <Network size={16} className="network-icon" />
42
- <span className="network-label">{currentNetwork.label}</span>
43
- <ChevronDown size={14} className="network-chevron" />
44
- </button>
45
-
46
- {isOpen && (
47
- <ul className="network-dropdown-menu">
48
- {NETWORKS.map(net => (
49
- <li
50
- key={net.id}
51
- className={`network-dropdown-item ${net.id === value ? 'active' : ''}`}
52
- onClick={() => {
53
- onChange(net.id);
54
- setIsOpen(false);
55
- }}
56
- >
57
- {net.label}
58
- </li>
59
- ))}
60
- </ul>
61
- )}
62
- </div>
63
- );
64
- };
@@ -1,25 +0,0 @@
1
- import React from 'react';
2
-
3
- const NyxoraLogo = ({ size = 28, className = "" }) => {
4
- return (
5
- <svg
6
- version="1.1"
7
- xmlns="http://www.w3.org/2000/svg"
8
- viewBox="210 210 835 835"
9
- width={size}
10
- height={size}
11
- className={className}
12
- >
13
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M391.558502,864.562012 C414.770752,831.424988 437.898010,798.228088 461.224091,765.171326 C477.741455,741.763550 494.530212,718.547302 511.188538,695.238953 C512.325684,693.647888 513.384827,692.001099 514.827759,689.865967 C416.111115,669.324341 317.944061,648.897095 219.777023,628.469910 C219.745956,628.100342 219.714890,627.730774 219.683807,627.361206 C317.965973,607.048096 416.248169,586.734985 515.386597,566.244934 C489.683167,530.326538 464.441345,495.053223 438.641846,459.000580 C437.294037,462.427734 436.131836,465.046661 435.221252,467.750305 C426.203400,494.526520 419.706848,521.833374 418.307770,550.164917 C418.161621,553.124207 417.398041,554.463074 414.247314,555.063660 C403.349243,557.141052 392.525635,559.609436 380.672791,562.142944 C381.955139,549.287842 382.795654,537.188110 384.428314,525.196289 C389.018463,491.482178 398.703979,459.225067 412.769470,428.242523 C413.487701,426.660400 412.872864,423.774323 411.822296,422.200531 C404.613678,411.401794 397.123077,400.791260 389.753174,389.651367 C389.905334,389.067993 390.027649,388.953430 390.149933,388.838837 C400.609253,396.183838 411.068604,403.528839 421.732483,411.017487 C448.953796,360.083954 488.595703,321.332672 537.556702,291.163635 C536.430664,291.357513 535.276550,291.451691 534.182617,291.759583 C473.404877,308.867706 421.375061,340.392334 377.824371,386.130768 C332.064423,434.189392 302.581238,490.723297 288.788391,555.519043 C287.160980,563.164368 286.307709,570.972107 284.888123,578.666321 C284.643646,579.991333 283.544373,581.987549 282.517700,582.214172 C268.590088,585.289429 254.605560,588.106750 239.666290,591.187622 C249.186905,504.342407 281.266876,428.187378 339.867310,364.210388 C407.528534,290.341309 491.608215,248.142334 592.349182,237.331238 C590.731506,245.370377 589.307007,252.614029 587.812500,259.843231 C584.407288,276.314758 580.834412,292.753448 577.645447,309.266479 C576.937866,312.930328 574.568787,313.922150 571.855469,315.234436 C518.939209,340.827637 479.413025,380.264313 451.142212,432.059692 C489.395081,459.635254 527.531799,487.127106 566.385315,515.135681 C586.988159,416.540558 607.394653,318.885071 627.801086,221.229568 C628.032654,221.259720 628.264160,221.289856 628.495667,221.320007 C648.896179,318.988525 669.296631,416.657074 689.808777,514.860291 C749.231628,472.484833 808.059265,430.533875 867.341919,388.626953 C867.900818,388.812653 868.004700,388.954315 868.108582,389.096008 C849.003479,415.756317 829.905701,442.421814 810.791443,469.075500 C788.267273,500.484131 765.727478,531.881470 743.204163,563.290710 C742.647949,564.066345 742.205200,564.923340 741.467285,566.142639 C840.231323,586.608215 938.625000,606.997070 1037.018677,627.385925 C1037.040283,627.731873 1037.062012,628.077881 1037.083618,628.423828 C938.778198,648.880981 840.472778,669.338074 741.484985,689.937195 C780.034912,744.140015 818.287720,797.925110 856.739441,851.989746 C857.870544,851.388916 859.027100,850.855286 860.102966,850.191101 C891.392395,830.875427 918.577148,806.818420 939.357849,776.533569 C947.590210,764.536194 953.158813,750.218750 957.569092,736.211121 C963.550171,717.214111 967.202209,697.487122 972.020630,678.114868 C972.446899,676.400879 973.864685,673.918213 975.231079,673.610779 C988.701538,670.579590 1002.258545,667.933167 1015.850098,665.165710 C1006.960327,831.437073 859.668823,998.586304 665.964600,1014.599609 C666.161560,1012.942749 666.229980,1011.378174 666.546021,1009.865417 C669.031982,997.967957 671.659729,986.098999 674.013733,974.175842 C674.563416,971.391418 675.734192,970.367493 678.503540,969.994568 C730.376587,963.009094 778.243225,945.312073 821.729614,916.054871 C827.915039,911.893494 833.889709,907.418823 839.719055,902.229187 C789.737976,922.594238 738.281250,931.719971 684.053101,924.868225 C686.495605,912.886902 688.875671,901.211975 691.358215,889.034485 C719.122437,891.573181 746.474426,890.263916 773.529236,884.649719 C800.425781,879.068298 825.987671,869.833374 850.707886,855.726074 C796.893921,817.308716 743.624573,779.280273 689.712219,740.792725 C669.298584,840.439758 649.062561,939.219666 628.826538,1037.999634 C628.337769,1038.062988 627.848999,1038.126343 627.360229,1038.189697 C607.031616,939.372681 586.703064,840.555603 566.225220,741.013123 C508.181274,782.548218 450.532928,823.800232 392.387512,864.971436 C391.890411,864.890564 391.558502,864.562012 391.558502,864.562012 M671.054382,685.592590 C683.651733,669.250977 700.426758,659.979492 720.473022,655.958557 C760.160095,647.997925 799.813110,639.867310 839.477173,631.792114 C844.905396,630.686890 850.316223,629.496033 856.934082,628.090088 C855.240906,627.423645 854.855042,627.202271 854.441284,627.118713 C809.391235,618.015625 764.324524,608.994141 719.292053,599.805237 C687.129700,593.242493 666.357849,573.794189 658.085388,542.310303 C652.000183,519.150879 647.862915,495.475311 642.997131,472.000824 C638.094788,448.350525 633.364502,424.664551 628.366150,400.044769 C627.805237,401.719086 627.470642,402.460938 627.308472,403.238739 C618.164917,447.087311 609.088989,490.950134 599.887695,534.786621 C592.838074,568.371887 574.333862,591.798584 539.707825,599.260986 C497.549194,608.346741 455.186249,616.484192 412.913910,625.042847 C409.059052,625.823303 405.220459,626.683960 400.609985,627.671082 C401.828217,628.367615 402.088959,628.612732 402.387390,628.673767 C446.942963,637.790649 491.531921,646.747131 536.049377,656.046265 C569.849854,663.106689 591.605042,683.306885 598.985901,717.262268 C608.430969,760.713928 616.988403,804.358521 625.937500,847.918152 C626.558655,850.941589 627.255554,853.949463 628.215210,858.322327 C637.018921,814.944580 645.550049,773.068237 654.003601,731.176331 C657.208618,715.293640 660.428406,699.446167 671.054382,685.592590 z"/>
14
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M386.726410,932.261108 C301.744720,862.964966 252.860352,774.276062 239.571213,664.782349 C243.640167,665.623230 246.658188,666.259705 249.681610,666.869385 C260.132385,668.976929 270.570435,671.152649 281.047028,673.123108 C283.713837,673.624695 284.803009,674.577637 285.264160,677.483826 C299.943695,769.993530 344.846649,844.861694 418.412781,902.306030 C465.297638,938.916199 518.736572,961.112610 577.599609,970.152466 C579.135315,970.388245 581.405090,971.909729 581.702209,973.205444 C584.604919,985.865601 587.168518,998.603699 589.797119,1011.326111 C589.988586,1012.252930 589.933960,1013.230591 590.033508,1014.819641 C514.202637,1006.650635 446.673218,979.319702 386.726410,932.261108 z"/>
15
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M717.095459,286.762451 C702.518005,285.946136 688.403137,285.164001 673.635620,284.345703 C670.496094,269.397308 667.259644,253.987091 663.812012,237.571594 C699.948914,240.829361 734.403198,248.649704 767.807129,261.272522 C801.262024,273.914642 832.392090,290.666199 860.278870,313.266907 C813.130981,301.801331 765.908508,291.274475 717.095459,286.762451 z"/>
16
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M508.385742,845.601562 C503.292084,841.116760 498.462646,836.867615 493.256165,832.286682 C503.293274,824.734863 512.820435,817.566772 522.083801,810.597107 C532.372131,818.453186 542.291443,825.997009 552.154602,833.613647 C552.996399,834.263733 553.846802,835.322083 554.047241,836.320496 C557.123535,851.643250 560.092407,866.987488 563.338562,883.619385 C542.903259,872.819641 525.360107,859.943787 508.385742,845.601562 z"/>
17
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M443.739258,707.133484 C445.894348,709.609070 447.759094,711.954102 449.403809,714.444519 C453.212250,720.211304 452.803345,725.074036 447.955048,730.693237 C441.574707,738.088135 436.480682,746.592834 429.788666,756.105713 C420.586670,736.813904 411.856506,718.511169 403.126312,700.208496 C403.546173,699.856079 403.966003,699.503662 404.385864,699.151184 C417.373566,701.790466 430.361298,704.429749 443.739258,707.133484 z"/>
18
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M389.592651,389.346680 C389.291229,389.122070 389.180267,388.733337 389.069275,388.344666 C389.338684,388.340118 389.608063,388.335571 390.013702,388.584961 C390.027649,388.953430 389.905334,389.067993 389.592651,389.346680 z"/>
19
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M867.646118,388.454865 C867.856812,388.036652 868.218323,387.834564 868.579834,387.632507 C868.601868,388.016388 868.623962,388.400299 868.377319,388.940094 C868.004700,388.954315 867.900818,388.812653 867.646118,388.454865 z"/>
20
- <path fill="#88c0d0" opacity="1.0" stroke="none" d="M392.055237,865.056030 C391.863586,865.412903 391.507172,865.604370 391.150757,865.795837 C391.133575,865.521484 391.116425,865.247192 391.328857,864.767456 C391.558502,864.562012 391.890411,864.890564 392.055237,865.056030 z"/>
21
- </svg>
22
- );
23
- };
24
-
25
- export default NyxoraLogo;
@@ -1,352 +0,0 @@
1
- import { apiFetch } from './utils/api';
2
- import React, { useState, useEffect } from 'react';
3
- import { Compass, Search, Terminal, FileText, FileEdit, Globe, ShieldAlert, DownloadCloud, AlertTriangle, FileSearch, Search as SearchIcon, Mail, Calendar } from 'lucide-react';
4
-
5
- interface SkillParam {
6
- type: string;
7
- description?: string;
8
- enum?: string[];
9
- }
10
-
11
- interface SkillDefinition {
12
- type: string;
13
- isActive?: boolean;
14
- function: {
15
- name: string;
16
- description: string;
17
- parameters: {
18
- type: string;
19
- properties: Record<string, SkillParam>;
20
- required: string[];
21
- };
22
- };
23
- }
24
-
25
- const formatSkillName = (name: string) => {
26
- return name
27
- .split('_')
28
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
29
- .join(' ');
30
- };
31
-
32
- const getSkillIcon = (skillName: string, isActive: boolean) => {
33
- const color = isActive !== false ? "#88c0d0" : "#4c566a";
34
- const size = 20;
35
- switch (formatSkillName(skillName).toLowerCase()) {
36
- case 'run terminal command': return <Terminal size={size} color={color} />;
37
- case 'read local file': return <FileText size={size} color={color} />;
38
- case 'write local file': return <FileEdit size={size} color={color} />;
39
- case 'browse website': return <Globe size={size} color={color} />;
40
- case 'update security policy': return <ShieldAlert size={size} color={color} />;
41
- case 'install external skill': return <DownloadCloud size={size} color={color} />;
42
- case 'analyze document': return <FileSearch size={size} color={color} />;
43
- case 'search web': return <SearchIcon size={size} color={color} />;
44
- case 'read gmail inbox': return <Mail size={size} color={color} />;
45
- case 'list calendar events': return <Calendar size={size} color={color} />;
46
- default: return <Compass size={size} color={color} />;
47
- }
48
- };
49
-
50
- const OsSkills: React.FC = () => {
51
- const [skills, setSkills] = useState<SkillDefinition[]>([]);
52
- const [searchQuery, setSearchQuery] = useState('');
53
- const [isLoading, setIsLoading] = useState(true);
54
- const [pendingToggle, setPendingToggle] = useState<{skillName: string, currentStatus: boolean} | null>(null);
55
- const [googleConnected, setGoogleConnected] = useState(false);
56
-
57
- useEffect(() => {
58
- const fetchSkills = async () => {
59
- try {
60
- const res = await apiFetch('/api/skills/system');
61
- if (res.ok) setSkills(await res.json());
62
- } catch (e) {
63
- console.error(e);
64
- } finally {
65
- setIsLoading(false);
66
- }
67
- };
68
- const fetchGoogleStatus = async () => {
69
- try {
70
- const res = await apiFetch('/api/auth/google/status');
71
- if (res.ok) {
72
- const data = await res.json();
73
- setGoogleConnected(data.connected);
74
- }
75
- } catch (e) {
76
- console.error('Failed to fetch Google Auth status', e);
77
- }
78
- };
79
- fetchSkills();
80
- fetchGoogleStatus();
81
- }, []);
82
-
83
- const handleToggle = async (skillName: string, currentStatus: boolean) => {
84
- if (skillName === 'run_terminal_command' && !currentStatus) {
85
- setPendingToggle({ skillName, currentStatus });
86
- return;
87
- }
88
- await executeToggle(skillName, currentStatus);
89
- };
90
-
91
- const executeToggle = async (skillName: string, currentStatus: boolean) => {
92
- const newStatus = !currentStatus;
93
-
94
- // Optimistic UI update
95
- setSkills(prev => prev.map(s =>
96
- s.function.name === skillName ? { ...s, isActive: newStatus } : s
97
- ));
98
-
99
- try {
100
- await apiFetch('/api/skills/toggle', {
101
- method: 'POST',
102
- headers: { 'Content-Type': 'application/json' },
103
- body: JSON.stringify({ skillName, active: newStatus })
104
- });
105
- } catch (e) {
106
- // Revert on error
107
- setSkills(prev => prev.map(s =>
108
- s.function.name === skillName ? { ...s, isActive: currentStatus } : s
109
- ));
110
- console.error('Failed to toggle OS skill', e);
111
- }
112
- };
113
-
114
- if (isLoading) return <div className="overview-container">Loading OS skills...</div>;
115
-
116
- const filteredSkills = skills.filter(skill =>
117
- skill.function.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
118
- skill.function.description.toLowerCase().includes(searchQuery.toLowerCase())
119
- );
120
-
121
- return (
122
- <div className="overview-container" style={{ maxWidth: '1000px', margin: '0 auto', width: '100%' }}>
123
- <div className="overview-header" style={{ marginBottom: '24px' }}>
124
- <h1 style={{ color: '#eceff4' }}>OS Skills</h1>
125
- <p style={{ color: '#d8dee9' }}>System-level capabilities for the agent OS.</p>
126
- </div>
127
-
128
- {/* Account Linking Panel */}
129
- <div style={{
130
- background: '#3b4252',
131
- border: '1px solid rgba(216, 222, 233, 0.1)',
132
- borderRadius: '8px',
133
- padding: '24px',
134
- marginBottom: '24px',
135
- display: 'flex',
136
- alignItems: 'center',
137
- justifyContent: 'space-between'
138
- }}>
139
- <div>
140
- <h3 style={{ margin: '0 0 8px 0', color: '#eceff4', fontSize: '1.1rem' }}>Account Linking</h3>
141
- <p style={{ margin: 0, color: '#d8dee9', fontSize: '0.9rem' }}>
142
- Connect your Google Workspace to unlock Gmail and Calendar skills.
143
- </p>
144
- </div>
145
- <div>
146
- {googleConnected ? (
147
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px', background: 'rgba(163, 190, 140, 0.1)', color: '#a3be8c', padding: '8px 16px', borderRadius: '6px', fontWeight: 600 }}>
148
- <span style={{ width: '8px', height: '8px', background: '#a3be8c', borderRadius: '50%', display: 'inline-block' }}></span>
149
- Connected to Google
150
- </div>
151
- ) : (
152
- <button
153
- onClick={async () => {
154
- try {
155
- const res = await apiFetch('/api/auth/google/url');
156
- const data = await res.json();
157
- if (res.ok) {
158
- window.open(data.url, '_blank', 'width=600,height=700');
159
- } else {
160
- alert('Setup Required: ' + (data.error || 'Please add google-credentials.json to ~/.nyxora/'));
161
- }
162
- } catch (e) {
163
- alert('Failed to initiate Google Auth. Is the backend running?');
164
- }
165
- }}
166
- style={{
167
- background: '#eceff4',
168
- color: '#2e3440',
169
- border: 'none',
170
- padding: '10px 20px',
171
- borderRadius: '6px',
172
- fontWeight: 600,
173
- cursor: 'pointer',
174
- display: 'flex',
175
- alignItems: 'center',
176
- gap: '8px'
177
- }}
178
- >
179
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
180
- <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
181
- <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
182
- <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
183
- <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
184
- </svg>
185
- Sign in with Google
186
- </button>
187
- )}
188
- </div>
189
- </div>
190
-
191
- <div style={{
192
- display: 'flex',
193
- alignItems: 'center',
194
- background: 'rgba(46, 52, 64, 0.4)',
195
- border: '1px solid rgba(216, 222, 233, 0.1)',
196
- borderRadius: '8px',
197
- padding: '0 16px',
198
- marginBottom: '24px'
199
- }}>
200
- <Search size={18} color="#9ca3af" />
201
- <input
202
- type="text"
203
- placeholder="Filter OS skills..."
204
- value={searchQuery}
205
- onChange={(e) => setSearchQuery(e.target.value)}
206
- style={{
207
- background: 'transparent',
208
- border: 'none',
209
- outline: 'none',
210
- color: '#eceff4',
211
- padding: '16px',
212
- width: '100%',
213
- fontSize: '0.95rem'
214
- }}
215
- />
216
- <span style={{ color: '#6b7280', fontSize: '0.85rem' }}>{filteredSkills.length} shown</span>
217
- </div>
218
-
219
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
220
- {filteredSkills.map((skill, idx) => (
221
- <div key={idx} style={{
222
- display: 'flex',
223
- alignItems: 'center',
224
- justifyContent: 'space-between',
225
- background: '#3b4252',
226
- padding: '20px 24px',
227
- borderRadius: '8px',
228
- border: '1px solid rgba(216, 222, 233, 0.05)'
229
- }}>
230
- <div style={{ display: 'flex', alignItems: 'flex-start', gap: '16px', flex: 1 }}>
231
- <div style={{ marginTop: '2px' }}>
232
- {getSkillIcon(skill.function.name, skill.isActive !== false)}
233
- </div>
234
- <div>
235
- <h3 style={{ margin: '0 0 6px 0', fontSize: '1rem', color: skill.isActive !== false ? '#eceff4' : '#d8dee9', fontWeight: 600 }}>
236
- {formatSkillName(skill.function.name)}
237
- </h3>
238
- <p style={{ margin: 0, color: '#9ca3af', fontSize: '0.85rem', lineHeight: '1.5', maxWidth: '800px' }}>
239
- {skill.function.description}
240
- </p>
241
- </div>
242
- </div>
243
-
244
- <div style={{ marginLeft: '24px' }}>
245
- <button
246
- onClick={() => handleToggle(skill.function.name, skill.isActive !== false)}
247
- style={{
248
- position: 'relative',
249
- width: '40px',
250
- height: '22px',
251
- borderRadius: '11px',
252
- background: skill.isActive !== false ? '#88c0d0' : '#4c566a',
253
- border: 'none',
254
- cursor: 'pointer',
255
- transition: 'background 0.3s ease',
256
- padding: 0
257
- }}
258
- >
259
- <div style={{
260
- position: 'absolute',
261
- top: '2px',
262
- left: skill.isActive !== false ? '20px' : '2px',
263
- width: '18px',
264
- height: '18px',
265
- borderRadius: '50%',
266
- background: '#fff',
267
- transition: 'left 0.3s cubic-bezier(0.4, 0.0, 0.2, 1)',
268
- boxShadow: '0 1px 3px rgba(0,0,0,0.3)'
269
- }} />
270
- </button>
271
- </div>
272
- </div>
273
- ))}
274
- </div>
275
-
276
- {/* Warning Modal */}
277
- {pendingToggle && (
278
- <div style={{
279
- position: 'fixed',
280
- top: 0, left: 0, right: 0, bottom: 0,
281
- background: 'rgba(0, 0, 0, 0.6)',
282
- backdropFilter: 'blur(4px)',
283
- display: 'flex',
284
- alignItems: 'center',
285
- justifyContent: 'center',
286
- zIndex: 1000
287
- }}>
288
- <div style={{
289
- background: '#2e3440',
290
- border: '1px solid rgba(191, 97, 106, 0.4)',
291
- borderRadius: '12px',
292
- padding: '32px',
293
- maxWidth: '500px',
294
- width: '100%',
295
- boxShadow: '0 20px 40px rgba(0,0,0,0.4)'
296
- }}>
297
- <div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginBottom: '16px' }}>
298
- <div style={{ background: 'rgba(191, 97, 106, 0.1)', padding: '12px', borderRadius: '50%' }}>
299
- <AlertTriangle size={32} color="#bf616a" />
300
- </div>
301
- <h2 style={{ margin: 0, color: '#eceff4', fontSize: '1.25rem' }}>DANGER ZONE: System Access</h2>
302
- </div>
303
-
304
- <p style={{ color: '#d8dee9', lineHeight: '1.6', marginBottom: '24px' }}>
305
- You are about to enable <strong>Terminal Execution</strong>. This grants the AI agent
306
- <strong> full, unrestrained access</strong> to execute arbitrary shell scripts on your host operating system.
307
- </p>
308
- <p style={{ color: '#bf616a', fontWeight: 'bold', fontSize: '0.9rem', marginBottom: '32px' }}>
309
- ⚠️ The agent could read sensitive files, modify your system, or execute harmful commands if prompted maliciously.
310
- </p>
311
-
312
- <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '12px' }}>
313
- <button
314
- onClick={() => setPendingToggle(null)}
315
- style={{
316
- background: 'transparent',
317
- border: '1px solid #4c566a',
318
- color: '#d8dee9',
319
- padding: '10px 20px',
320
- borderRadius: '6px',
321
- cursor: 'pointer',
322
- fontWeight: 600
323
- }}
324
- >
325
- Cancel
326
- </button>
327
- <button
328
- onClick={() => {
329
- executeToggle(pendingToggle.skillName, pendingToggle.currentStatus);
330
- setPendingToggle(null);
331
- }}
332
- style={{
333
- background: '#bf616a',
334
- border: 'none',
335
- color: '#eceff4',
336
- padding: '10px 20px',
337
- borderRadius: '6px',
338
- cursor: 'pointer',
339
- fontWeight: 600
340
- }}
341
- >
342
- I Understand the Risks, Enable
343
- </button>
344
- </div>
345
- </div>
346
- </div>
347
- )}
348
- </div>
349
- );
350
- };
351
-
352
- export default OsSkills;