groove-dev 0.27.64 → 0.27.66

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 (40) hide show
  1. package/README.md +1 -1
  2. package/node_modules/@groove-dev/cli/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/package.json +1 -1
  4. package/node_modules/@groove-dev/daemon/src/api.js +103 -31
  5. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +0 -1
  6. package/node_modules/@groove-dev/gui/dist/assets/{index-DiiEKVEo.js → index-BvvSZvQz.js} +1735 -1735
  7. package/node_modules/@groove-dev/gui/dist/assets/index-DFp5IOnd.css +1 -0
  8. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  9. package/node_modules/@groove-dev/gui/package.json +1 -1
  10. package/node_modules/@groove-dev/gui/src/components/network/activity-chart.jsx +10 -14
  11. package/node_modules/@groove-dev/gui/src/components/network/compute-header.jsx +67 -200
  12. package/node_modules/@groove-dev/gui/src/components/network/earnings-card.jsx +30 -0
  13. package/node_modules/@groove-dev/gui/src/components/network/fleet-table.jsx +114 -72
  14. package/node_modules/@groove-dev/gui/src/components/network/identity-bar.jsx +94 -0
  15. package/node_modules/@groove-dev/gui/src/components/network/node-card.jsx +88 -0
  16. package/node_modules/@groove-dev/gui/src/components/network/wallet-view.jsx +77 -0
  17. package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +1 -1
  18. package/node_modules/@groove-dev/gui/src/stores/groove.js +13 -0
  19. package/node_modules/@groove-dev/gui/src/views/network.jsx +59 -18
  20. package/package.json +1 -1
  21. package/packages/cli/package.json +1 -1
  22. package/packages/daemon/package.json +1 -1
  23. package/packages/daemon/src/api.js +103 -31
  24. package/packages/daemon/src/providers/claude-code.js +0 -1
  25. package/packages/gui/dist/assets/{index-DiiEKVEo.js → index-BvvSZvQz.js} +1735 -1735
  26. package/packages/gui/dist/assets/index-DFp5IOnd.css +1 -0
  27. package/packages/gui/dist/index.html +2 -2
  28. package/packages/gui/package.json +1 -1
  29. package/packages/gui/src/components/network/activity-chart.jsx +10 -14
  30. package/packages/gui/src/components/network/compute-header.jsx +67 -200
  31. package/packages/gui/src/components/network/earnings-card.jsx +30 -0
  32. package/packages/gui/src/components/network/fleet-table.jsx +114 -72
  33. package/packages/gui/src/components/network/identity-bar.jsx +94 -0
  34. package/packages/gui/src/components/network/node-card.jsx +88 -0
  35. package/packages/gui/src/components/network/wallet-view.jsx +77 -0
  36. package/packages/gui/src/components/onboarding/setup-wizard.jsx +1 -1
  37. package/packages/gui/src/stores/groove.js +13 -0
  38. package/packages/gui/src/views/network.jsx +59 -18
  39. package/node_modules/@groove-dev/gui/dist/assets/index-B3AqeyS4.css +0 -1
  40. package/packages/gui/dist/assets/index-B3AqeyS4.css +0 -1
@@ -0,0 +1,94 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { memo, useState, useEffect, useCallback } from 'react';
3
+ import { useGrooveStore } from '../../stores/groove';
4
+ import { cn } from '../../lib/cn';
5
+ import { StatusDot } from '../ui/status-dot';
6
+ import { Button } from '../ui/button';
7
+ import { Tooltip } from '../ui/tooltip';
8
+ import { fmtUptime } from '../../lib/format';
9
+ import { Copy, Check, Wallet } from 'lucide-react';
10
+
11
+ function shortAddr(addr) {
12
+ if (!addr || typeof addr !== 'string') return '—';
13
+ if (addr.length < 14) return addr;
14
+ return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
15
+ }
16
+
17
+ function nodeStatusMap(node) {
18
+ if (node.active && node.status === 'connected') return 'running';
19
+ if (node.status === 'connecting') return 'starting';
20
+ return 'stopped';
21
+ }
22
+
23
+ function statusColor(status) {
24
+ if (status === 'connected') return 'text-success';
25
+ if (status === 'connecting') return 'text-warning';
26
+ return 'text-danger';
27
+ }
28
+
29
+ export const IdentityBar = memo(function IdentityBar() {
30
+ const node = useGrooveStore((s) => s.networkNode);
31
+ const wallet = useGrooveStore((s) => s.networkWallet);
32
+ const [copied, setCopied] = useState(false);
33
+ const [uptime, setUptime] = useState(0);
34
+
35
+ useEffect(() => {
36
+ if (!node.startedAt) return;
37
+ const tick = () => setUptime(Math.floor((Date.now() - node.startedAt) / 1000));
38
+ tick();
39
+ const id = setInterval(tick, 1000);
40
+ return () => clearInterval(id);
41
+ }, [node.startedAt]);
42
+
43
+ const handleCopy = useCallback(async () => {
44
+ if (!node.nodeId) return;
45
+ try {
46
+ await navigator.clipboard.writeText(node.nodeId);
47
+ setCopied(true);
48
+ setTimeout(() => setCopied(false), 1500);
49
+ } catch { /* clipboard blocked */ }
50
+ }, [node.nodeId]);
51
+
52
+ const connStatus = node.status || 'disconnected';
53
+
54
+ return (
55
+ <div className="flex items-center h-11 px-4 bg-surface-0 border-b border-border-subtle gap-3">
56
+ {/* Left: node identity */}
57
+ <div className="flex items-center gap-2">
58
+ <StatusDot status={nodeStatusMap(node)} size="sm" />
59
+ <code className="text-xs font-mono text-text-0">{shortAddr(node.nodeId)}</code>
60
+ <Tooltip content={copied ? 'Copied' : 'Copy address'} side="bottom">
61
+ <button
62
+ onClick={handleCopy}
63
+ className="h-6 w-6 inline-flex items-center justify-center rounded border border-border-subtle text-text-3 hover:text-accent hover:border-accent/40 cursor-pointer transition-colors"
64
+ >
65
+ {copied ? <Check size={10} /> : <Copy size={10} />}
66
+ </button>
67
+ </Tooltip>
68
+ </div>
69
+
70
+ {/* Center: status + uptime */}
71
+ <div className="flex-1 flex items-center justify-center gap-3">
72
+ <span className={cn('text-2xs uppercase tracking-widest font-sans', statusColor(connStatus))}>
73
+ {connStatus === 'connected' ? 'Connected' : connStatus === 'connecting' ? 'Connecting' : 'Disconnected'}
74
+ </span>
75
+ <span className="w-1 h-1 rounded-full bg-text-4" />
76
+ <span className="text-2xs font-mono text-text-3 tabular-nums">
77
+ {fmtUptime(uptime)}
78
+ </span>
79
+ </div>
80
+
81
+ {/* Right: token balance + wallet */}
82
+ <div className="flex items-center gap-3">
83
+ <span className="text-xs font-mono text-text-2">
84
+ <span className="text-text-0">{wallet.balance}</span>
85
+ <span className="text-text-3"> GROOVE</span>
86
+ </span>
87
+ <Button variant="outline" size="sm" className="text-2xs gap-1.5" disabled>
88
+ <Wallet size={12} />
89
+ Connect Wallet
90
+ </Button>
91
+ </div>
92
+ </div>
93
+ );
94
+ });
@@ -0,0 +1,88 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { memo } from 'react';
3
+ import { useGrooveStore } from '../../stores/groove';
4
+ import { cn } from '../../lib/cn';
5
+ import { StatusDot } from '../ui/status-dot';
6
+
7
+ function shortAddr(addr) {
8
+ if (!addr || typeof addr !== 'string') return '—';
9
+ if (addr.length < 14) return addr;
10
+ return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
11
+ }
12
+
13
+ function nodeStatusMap(node) {
14
+ if (node.active && node.status === 'connected') return 'running';
15
+ if (node.status === 'connecting') return 'starting';
16
+ return 'stopped';
17
+ }
18
+
19
+ function modelShort(model) {
20
+ if (!model) return '—';
21
+ const parts = model.split('/');
22
+ return parts[parts.length - 1];
23
+ }
24
+
25
+ export const NodeCard = memo(function NodeCard() {
26
+ const node = useGrooveStore((s) => s.networkNode);
27
+ const hardware = node.hardware || {};
28
+ const memPct = Number.isFinite(node.memoryPct) ? node.memoryPct : null;
29
+ const layersLabel = Array.isArray(node.layers) ? `${node.layers[0]}-${node.layers[1]}` : '—';
30
+
31
+ const metrics = [
32
+ { label: 'DEVICE', value: hardware.device || 'auto' },
33
+ { label: 'GPU', value: hardware.gpu || 'None' },
34
+ { label: 'LAYERS', value: layersLabel },
35
+ { label: 'MODEL', value: modelShort(node.model) },
36
+ { label: 'SESSIONS', value: node.sessions ?? 0 },
37
+ { label: 'VRAM', value: hardware.memory || '—' },
38
+ ];
39
+
40
+ return (
41
+ <div className="flex flex-col h-full">
42
+ <div className="px-3 pt-2.5 pb-1">
43
+ <span className="text-2xs font-mono text-text-3 uppercase tracking-widest">Your Node</span>
44
+ </div>
45
+
46
+ <div className="px-3 py-2 space-y-2">
47
+ <div>
48
+ <div className="text-2xs text-text-4 uppercase tracking-wider">ADDRESS</div>
49
+ <div className="text-xs font-mono text-accent truncate">{shortAddr(node.nodeId)}</div>
50
+ </div>
51
+
52
+ <div className="flex items-center gap-1.5">
53
+ <StatusDot status={nodeStatusMap(node)} size="sm" />
54
+ <span className="text-2xs capitalize text-text-2">{node.status || 'disconnected'}</span>
55
+ </div>
56
+
57
+ <div className="border-t border-border-subtle my-1" />
58
+
59
+ <div className="grid grid-cols-2 gap-x-3 gap-y-1.5">
60
+ {metrics.map((m) => (
61
+ <div key={m.label}>
62
+ <div className="text-2xs text-text-4 uppercase tracking-wider">{m.label}</div>
63
+ <div className="text-xs font-mono text-text-1 tabular-nums truncate">{m.value}</div>
64
+ </div>
65
+ ))}
66
+ </div>
67
+
68
+ {memPct != null && (
69
+ <div className="mt-1">
70
+ <div className="flex items-center justify-between mb-0.5">
71
+ <span className="text-2xs text-text-4 uppercase tracking-wider">MEM</span>
72
+ <span className="text-2xs font-mono text-text-3 tabular-nums">{Math.round(memPct)}%</span>
73
+ </div>
74
+ <div className="h-1 w-full rounded-full bg-surface-3 overflow-hidden">
75
+ <div
76
+ className={cn(
77
+ 'h-full rounded-full transition-all',
78
+ memPct > 90 ? 'bg-danger' : memPct > 70 ? 'bg-warning' : 'bg-accent',
79
+ )}
80
+ style={{ width: `${Math.min(100, Math.max(0, memPct))}%` }}
81
+ />
82
+ </div>
83
+ </div>
84
+ )}
85
+ </div>
86
+ </div>
87
+ );
88
+ });
@@ -0,0 +1,77 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { memo } from 'react';
3
+ import { Badge } from '../ui/badge';
4
+ import { Button } from '../ui/button';
5
+ import { Wallet, Cpu, Activity, Clock, Zap } from 'lucide-react';
6
+
7
+ const REWARD_CARDS = [
8
+ { icon: Cpu, label: 'Compute Hours', value: '—' },
9
+ { icon: Activity, label: 'Sessions Served', value: '—' },
10
+ { icon: Clock, label: 'Uptime Score', value: '—' },
11
+ { icon: Zap, label: 'Network Score', value: '—' },
12
+ ];
13
+
14
+ const PAYOUT_COLS = ['Date', 'Amount', 'Status', 'TX'];
15
+
16
+ export const WalletView = memo(function WalletView() {
17
+ return (
18
+ <div className="flex flex-col h-full overflow-auto">
19
+ {/* Hero */}
20
+ <div className="px-6 py-8 text-center border-b border-border-subtle bg-surface-0">
21
+ <div className="text-3xl font-mono font-semibold text-text-0 tabular-nums">0.00</div>
22
+ <div className="text-sm font-mono text-text-3 mt-1">GROOVE</div>
23
+ <Badge variant="purple" className="mt-3">Base L2</Badge>
24
+ <div className="mt-4">
25
+ <Button variant="primary" size="md" className="gap-2" disabled>
26
+ <Wallet size={14} />
27
+ Connect Wallet
28
+ </Button>
29
+ </div>
30
+ <div className="text-2xs text-text-4 mt-2">Connect your Base wallet to claim rewards</div>
31
+ </div>
32
+
33
+ {/* Reward Metrics */}
34
+ <div className="px-4 py-4">
35
+ <div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">EARNING POTENTIAL</div>
36
+ <div className="grid grid-cols-4 gap-2">
37
+ {REWARD_CARDS.map((card) => (
38
+ <div key={card.label} className="rounded-md border border-border-subtle bg-surface-1 px-3 py-3 text-center">
39
+ <card.icon size={16} className="text-text-3 mx-auto mb-1.5" />
40
+ <div className="text-lg font-mono text-text-0 tabular-nums">{card.value}</div>
41
+ <div className="text-2xs text-text-4 mt-0.5">{card.label}</div>
42
+ </div>
43
+ ))}
44
+ </div>
45
+ </div>
46
+
47
+ {/* Earnings Timeline */}
48
+ <div className="px-4 py-4 border-t border-border-subtle">
49
+ <div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">EARNINGS HISTORY</div>
50
+ <div className="h-40 rounded-md border border-border-subtle bg-surface-0 flex items-center justify-center">
51
+ <span className="text-xs font-mono text-text-4">Earnings data will appear here</span>
52
+ </div>
53
+ </div>
54
+
55
+ {/* Payouts Table */}
56
+ <div className="px-4 py-4 border-t border-border-subtle">
57
+ <div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-3">PAYOUT HISTORY</div>
58
+ <div className="grid grid-cols-4 px-3 py-1.5 text-2xs font-mono text-text-4 uppercase tracking-wider">
59
+ {PAYOUT_COLS.map((col) => (
60
+ <span key={col}>{col}</span>
61
+ ))}
62
+ </div>
63
+ <div className="px-3 py-6 text-center text-xs font-mono text-text-4">No payouts yet</div>
64
+ </div>
65
+
66
+ {/* Banner */}
67
+ <div className="px-4 pb-6">
68
+ <div className="rounded-md border border-purple/20 bg-purple/5 px-4 py-3 flex items-center gap-3">
69
+ <Zap size={16} className="text-purple flex-shrink-0" />
70
+ <span className="text-xs font-sans text-text-2">
71
+ Rewards go live when the GROOVE token launches on Base. Keep your node running to accumulate compute credits.
72
+ </span>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ );
77
+ });
@@ -19,7 +19,7 @@ const PROVIDERS = [
19
19
  id: 'claude-code',
20
20
  name: 'Claude Code',
21
21
  subtitle: 'by Anthropic',
22
- models: ['Opus 4.7', 'Opus 4.6', 'Sonnet 4.6', 'Haiku 4.5'],
22
+ models: ['Opus 4.6', 'Sonnet 4.6', 'Haiku 4.5'],
23
23
  authType: 'Subscription or API key',
24
24
  authModes: ['subscription', 'apikey'],
25
25
  recommended: true,
@@ -105,6 +105,8 @@ export const useGrooveStore = create((set, get) => ({
105
105
  networkUpdateProgress: { updating: false, step: null, message: null, percent: 0, error: null },
106
106
  networkCompute: { totalRamMb: 0, totalVramMb: 0, totalCpuCores: 0, totalBandwidthMbps: 0, activeNodes: 0, totalNodes: 0, avgLoad: 0 },
107
107
  networkSnapshots: [],
108
+ networkWallet: { connected: false, address: null, balance: '0.00', token: 'GROOVE', chain: 'base-l2' },
109
+ networkEarnings: { today: 0, thisWeek: 0, allTime: 0, history: [] },
108
110
 
109
111
  // ── Marketplace Auth ───────────────────────────────────────
110
112
  marketplaceUser: null, // { id, displayName, avatar, ... } or null
@@ -732,6 +734,8 @@ export const useGrooveStore = create((set, get) => ({
732
734
  nodeCount: wsActive.length,
733
735
  avgLoad: wsActive.length > 0 ? wsActive.reduce((s, n) => s + (n.load || 0), 0) / wsActive.length : 0,
734
736
  myLoad: wsOwn?.load ?? 0,
737
+ totalVramMb: wsNodes.reduce((s, n) => s + (n.vram_mb || 0), 0),
738
+ totalRamMb: wsNodes.reduce((s, n) => s + (n.ram_mb || 0), 0),
735
739
  };
736
740
  let wsSnapshots = [...get().networkSnapshots, wsSnap];
737
741
  if (wsSnapshots.length > 100) wsSnapshots = wsSnapshots.slice(-100);
@@ -2179,6 +2183,8 @@ export const useGrooveStore = create((set, get) => ({
2179
2183
  nodeCount: activeNodes.length,
2180
2184
  avgLoad: activeNodes.length > 0 ? activeNodes.reduce((s, n) => s + (n.load || 0), 0) / activeNodes.length : 0,
2181
2185
  myLoad: ownNode?.load ?? 0,
2186
+ totalVramMb: nodes.reduce((s, n) => s + (n.vram_mb || 0), 0),
2187
+ totalRamMb: nodes.reduce((s, n) => s + (n.ram_mb || 0), 0),
2182
2188
  };
2183
2189
  let snapshots = [...get().networkSnapshots, snap];
2184
2190
  if (snapshots.length > 100) snapshots = snapshots.slice(-100);
@@ -2258,6 +2264,13 @@ export const useGrooveStore = create((set, get) => ({
2258
2264
  }
2259
2265
  },
2260
2266
 
2267
+ async fetchNetworkWallet() {
2268
+ return get().networkWallet;
2269
+ },
2270
+ async fetchNetworkEarnings() {
2271
+ return get().networkEarnings;
2272
+ },
2273
+
2261
2274
  async renameFile(oldPath, newPath) {
2262
2275
  try {
2263
2276
  await api.post('/files/rename', { oldPath, newPath });
@@ -6,12 +6,17 @@ import { Badge } from '../components/ui/badge';
6
6
  import { Button } from '../components/ui/button';
7
7
  import { Dialog, DialogContent, DialogTrigger } from '../components/ui/dialog';
8
8
  import { ScrollArea } from '../components/ui/scroll-area';
9
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '../components/ui/tabs';
9
10
  import { cn } from '../lib/cn';
10
- import { NodeToggle } from '../components/network/node-toggle';
11
+ import { IdentityBar } from '../components/network/identity-bar';
11
12
  import { ComputeHeader } from '../components/network/compute-header';
12
13
  import { ActivityChart } from '../components/network/activity-chart';
13
14
  import { ActivityStream } from '../components/network/activity-stream';
14
15
  import { NetworkHealth } from '../components/network/network-health';
16
+ import { NodeCard } from '../components/network/node-card';
17
+ import { EarningsCard } from '../components/network/earnings-card';
18
+ import { FleetTable } from '../components/network/fleet-table';
19
+ import { WalletView } from '../components/network/wallet-view';
15
20
  import { HEX, hexAlpha } from '../lib/theme-hex';
16
21
  import { Globe, Download, Check, AlertCircle, Loader2, Trash2, ArrowUpCircle, Zap } from 'lucide-react';
17
22
 
@@ -405,12 +410,44 @@ function IdleHero() {
405
410
  );
406
411
  }
407
412
 
413
+ function NetworkOverview() {
414
+ return (
415
+ <div className="flex flex-col h-full">
416
+ <ComputeHeader />
417
+ <div className="flex-1 min-h-0 grid" style={{ gridTemplateColumns: '2.5fr 1fr', gap: '1px', background: HEX.surface3 }}>
418
+ {/* Left column */}
419
+ <div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
420
+ <div className="flex-1 min-h-0 overflow-hidden bg-surface-1">
421
+ <ActivityChart />
422
+ </div>
423
+ <div className="flex-[0.5] min-h-0 overflow-hidden bg-surface-1">
424
+ <ActivityStream />
425
+ </div>
426
+ </div>
427
+ {/* Right column */}
428
+ <div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
429
+ <div className="min-h-0 overflow-hidden bg-surface-1">
430
+ <NodeCard />
431
+ </div>
432
+ <div className="flex-1 min-h-0 overflow-hidden bg-surface-1">
433
+ <NetworkHealth />
434
+ </div>
435
+ <div className="min-h-0 overflow-hidden bg-surface-1">
436
+ <EarningsCard />
437
+ </div>
438
+ </div>
439
+ </div>
440
+ </div>
441
+ );
442
+ }
443
+
408
444
  export default function NetworkView() {
409
445
  const fetchNetworkNodeStatus = useGrooveStore((s) => s.fetchNetworkNodeStatus);
410
446
  const fetchNetworkStatus = useGrooveStore((s) => s.fetchNetworkStatus);
411
447
  const checkNetworkUpdate = useGrooveStore((s) => s.checkNetworkUpdate);
412
448
  const installed = useGrooveStore((s) => s.networkInstalled);
413
449
  const nodeActive = useGrooveStore((s) => s.networkNode.active);
450
+ const [activeTab, setActiveTab] = useState('overview');
414
451
 
415
452
  useEffect(() => {
416
453
  fetchNetworkNodeStatus();
@@ -440,23 +477,27 @@ export default function NetworkView() {
440
477
  return (
441
478
  <div className="flex flex-col h-full">
442
479
  <NetworkHeader />
443
-
444
- <ComputeHeader />
445
-
446
- <div className="flex-1 min-h-0 flex flex-col" style={{ background: HEX.surface3, gap: '1px' }}>
447
- <div className="min-h-0 flex-1 grid" style={{ gridTemplateColumns: '3fr 1.5fr', gap: '0 1px' }}>
448
- <div className="min-w-0 min-h-0 overflow-hidden bg-surface-1">
449
- <ActivityChart />
450
- </div>
451
- <div className="min-w-0 min-h-0 overflow-hidden bg-surface-1">
452
- <NetworkHealth />
453
- </div>
454
- </div>
455
-
456
- <div className="min-h-0 flex-[0.6] bg-surface-1">
457
- <ActivityStream />
458
- </div>
459
- </div>
480
+ <IdentityBar />
481
+
482
+ <Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 min-h-0 flex flex-col">
483
+ <TabsList className="bg-surface-0 border-b border-border-subtle px-4 flex-shrink-0">
484
+ <TabsTrigger value="overview" className="text-xs">Overview</TabsTrigger>
485
+ <TabsTrigger value="fleet" className="text-xs">Fleet</TabsTrigger>
486
+ <TabsTrigger value="wallet" className="text-xs">Wallet</TabsTrigger>
487
+ </TabsList>
488
+
489
+ <TabsContent value="overview" className="flex-1 min-h-0 overflow-hidden">
490
+ <NetworkOverview />
491
+ </TabsContent>
492
+
493
+ <TabsContent value="fleet" className="flex-1 min-h-0 overflow-hidden bg-surface-1">
494
+ <FleetTable />
495
+ </TabsContent>
496
+
497
+ <TabsContent value="wallet" className="flex-1 min-h-0 overflow-hidden">
498
+ <WalletView />
499
+ </TabsContent>
500
+ </Tabs>
460
501
  </div>
461
502
  );
462
503
  }