groove-dev 0.27.49 → 0.27.51
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.
- package/default/security-review-prompt.md +98 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +376 -29
- package/node_modules/@groove-dev/daemon/src/firstrun.js +1 -1
- package/node_modules/@groove-dev/daemon/src/index.js +7 -0
- package/node_modules/@groove-dev/daemon/src/providers/groove-network.js +57 -7
- package/node_modules/@groove-dev/gui/dist/assets/{index-B9oPxmNj.js → index-Dd4u8X70.js} +1723 -1723
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/network/network-status.jsx +12 -0
- package/node_modules/@groove-dev/gui/src/components/network/node-toggle.jsx +18 -15
- package/node_modules/@groove-dev/gui/src/stores/groove.js +128 -1
- package/node_modules/@groove-dev/gui/src/views/network.jsx +82 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +376 -29
- package/packages/daemon/src/firstrun.js +1 -1
- package/packages/daemon/src/index.js +7 -0
- package/packages/daemon/src/providers/groove-network.js +57 -7
- package/packages/gui/dist/assets/{index-B9oPxmNj.js → index-Dd4u8X70.js} +1723 -1723
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/network/network-status.jsx +12 -0
- package/packages/gui/src/components/network/node-toggle.jsx +18 -15
- package/packages/gui/src/stores/groove.js +128 -1
- package/packages/gui/src/views/network.jsx +82 -2
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Dd4u8X70.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
@@ -40,6 +40,7 @@ function KpiTile({ icon: Icon, label, value, sub }) {
|
|
|
40
40
|
export function NetworkStatus() {
|
|
41
41
|
const status = useGrooveStore((s) => s.networkStatus);
|
|
42
42
|
const ownNodeId = useGrooveStore((s) => s.networkNode.nodeId);
|
|
43
|
+
const signalReachable = useGrooveStore((s) => s.networkStatusReachable);
|
|
43
44
|
const [nodesOpen, setNodesOpen] = useState(true);
|
|
44
45
|
|
|
45
46
|
const nodes = Array.isArray(status.nodes) ? status.nodes : [];
|
|
@@ -51,6 +52,17 @@ export function NetworkStatus() {
|
|
|
51
52
|
|
|
52
53
|
return (
|
|
53
54
|
<div className="flex flex-col gap-3">
|
|
55
|
+
{/* Signal connection indicator */}
|
|
56
|
+
<div className="flex items-center gap-2 px-3 py-2 rounded-lg border border-border bg-surface-1">
|
|
57
|
+
<StatusDot status={signalReachable ? 'running' : 'crashed'} size="sm" />
|
|
58
|
+
<span className="text-2xs font-sans text-text-3">Signal:</span>
|
|
59
|
+
<span className="text-2xs font-mono text-text-1">signal.groovedev.ai</span>
|
|
60
|
+
<div className="flex-1" />
|
|
61
|
+
<span className={cn('text-2xs font-sans', signalReachable ? 'text-success' : 'text-danger')}>
|
|
62
|
+
{signalReachable ? 'Connected' : 'Unreachable'}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
54
66
|
{/* KPI strip */}
|
|
55
67
|
<div className="grid grid-cols-3 gap-2">
|
|
56
68
|
<KpiTile icon={Globe} label="Nodes" value={fmtNum(nodes.length)} sub={`${nodes.filter((n) => n.status === 'active').length} active`} />
|
|
@@ -121,22 +121,25 @@ export function NodeToggle() {
|
|
|
121
121
|
</div>
|
|
122
122
|
|
|
123
123
|
{/* Identity */}
|
|
124
|
-
|
|
125
|
-
<div className="
|
|
126
|
-
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
{node.nodeId && (
|
|
125
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2.5">
|
|
126
|
+
<div className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider mb-1.5">Your Node Identity</div>
|
|
127
|
+
<div className="flex items-center gap-2">
|
|
128
|
+
<code className="flex-1 h-8 px-2.5 flex items-center bg-surface-1 border border-border-subtle rounded-md text-xs font-mono text-text-1">
|
|
129
|
+
{shortAddress(node.nodeId)}
|
|
130
|
+
</code>
|
|
131
|
+
<Tooltip content={copied ? 'Copied' : 'Copy full address'} side="top">
|
|
132
|
+
<button
|
|
133
|
+
onClick={handleCopy}
|
|
134
|
+
className="h-8 w-8 inline-flex items-center justify-center rounded-md border border-border-subtle bg-surface-1 text-text-3 hover:text-accent hover:border-accent/40 cursor-pointer transition-colors"
|
|
135
|
+
>
|
|
136
|
+
{copied ? <Check size={12} /> : <Copy size={12} />}
|
|
137
|
+
</button>
|
|
138
|
+
</Tooltip>
|
|
139
|
+
</div>
|
|
140
|
+
<div className="mt-1.5 text-2xs font-sans text-text-4">This is your network wallet address</div>
|
|
138
141
|
</div>
|
|
139
|
-
|
|
142
|
+
)}
|
|
140
143
|
|
|
141
144
|
{/* Sessions + memory */}
|
|
142
145
|
<div className="grid grid-cols-2 gap-2">
|
|
@@ -92,7 +92,10 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
92
92
|
networkInstallProgress: { installing: false, step: null, message: null, percent: 0, error: null },
|
|
93
93
|
networkNode: { active: false, status: 'disconnected', nodeId: null, layers: null, model: null, sessions: 0, hardware: null },
|
|
94
94
|
networkStatus: { nodes: [], coverage: 0, totalLayers: 0, models: [], activeSessions: 0 },
|
|
95
|
+
networkStatusReachable: false,
|
|
95
96
|
networkEvents: [],
|
|
97
|
+
networkVersion: { installed: null, latest: null, updateAvailable: false },
|
|
98
|
+
networkUpdateProgress: { updating: false, step: null, message: null, percent: 0, error: null },
|
|
96
99
|
|
|
97
100
|
// ── Marketplace Auth ───────────────────────────────────────
|
|
98
101
|
marketplaceUser: null, // { id, displayName, avatar, ... } or null
|
|
@@ -616,6 +619,34 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
616
619
|
break;
|
|
617
620
|
}
|
|
618
621
|
|
|
622
|
+
case 'signal_connected': {
|
|
623
|
+
const ev = msg.data || msg || {};
|
|
624
|
+
set((s) => ({
|
|
625
|
+
networkEvents: [...s.networkEvents, {
|
|
626
|
+
level: 'connected',
|
|
627
|
+
msg: ev.msg || ev.message || 'Connected to signal',
|
|
628
|
+
detail: ev.detail || ev.url,
|
|
629
|
+
...ev,
|
|
630
|
+
timestamp: ev.timestamp || Date.now(),
|
|
631
|
+
}].slice(-100),
|
|
632
|
+
}));
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
case 'matched': {
|
|
637
|
+
const ev = msg.data || msg || {};
|
|
638
|
+
set((s) => ({
|
|
639
|
+
networkEvents: [...s.networkEvents, {
|
|
640
|
+
level: 'session',
|
|
641
|
+
msg: ev.msg || ev.message || 'Session matched',
|
|
642
|
+
detail: ev.detail || ev.peer || ev.nodeId,
|
|
643
|
+
...ev,
|
|
644
|
+
timestamp: ev.timestamp || Date.now(),
|
|
645
|
+
}].slice(-100),
|
|
646
|
+
}));
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
|
|
619
650
|
case 'network:status':
|
|
620
651
|
set({ networkStatus: { ...get().networkStatus, ...(msg.data || {}) } });
|
|
621
652
|
break;
|
|
@@ -652,6 +683,55 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
652
683
|
break;
|
|
653
684
|
}
|
|
654
685
|
|
|
686
|
+
case 'network:update:available': {
|
|
687
|
+
const { installed, latest, updateAvailable } = msg.data || {};
|
|
688
|
+
set({
|
|
689
|
+
networkVersion: {
|
|
690
|
+
installed: installed ?? get().networkVersion.installed,
|
|
691
|
+
latest: latest ?? get().networkVersion.latest,
|
|
692
|
+
updateAvailable: !!updateAvailable,
|
|
693
|
+
},
|
|
694
|
+
});
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
case 'network:update:progress': {
|
|
699
|
+
const { step, message, percent, version, error } = msg.data || {};
|
|
700
|
+
if (step === 'done') {
|
|
701
|
+
set({
|
|
702
|
+
networkUpdateProgress: { updating: false, step: null, message: null, percent: 0, error: null },
|
|
703
|
+
networkVersion: {
|
|
704
|
+
...get().networkVersion,
|
|
705
|
+
installed: version || get().networkVersion.latest || get().networkVersion.installed,
|
|
706
|
+
updateAvailable: false,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
get().addToast('success', 'Network package updated');
|
|
710
|
+
} else if (step === 'error') {
|
|
711
|
+
set({
|
|
712
|
+
networkUpdateProgress: {
|
|
713
|
+
updating: false,
|
|
714
|
+
step: 'error',
|
|
715
|
+
message: message || error || 'Update failed',
|
|
716
|
+
percent: 0,
|
|
717
|
+
error: message || error || 'Update failed',
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
get().addToast('error', 'Network update failed', message || error);
|
|
721
|
+
} else {
|
|
722
|
+
set({
|
|
723
|
+
networkUpdateProgress: {
|
|
724
|
+
updating: true,
|
|
725
|
+
step: step || 'progress',
|
|
726
|
+
message: message || '',
|
|
727
|
+
percent: Number.isFinite(percent) ? Math.max(0, Math.min(100, percent)) : get().networkUpdateProgress.percent,
|
|
728
|
+
error: null,
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
|
|
655
735
|
case 'config:updated':
|
|
656
736
|
get().fetchBetaStatus();
|
|
657
737
|
get().fetchNetworkInstallStatus();
|
|
@@ -1749,11 +1829,58 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1749
1829
|
async fetchNetworkStatus() {
|
|
1750
1830
|
try {
|
|
1751
1831
|
const data = await api.get('/network/status');
|
|
1752
|
-
set({
|
|
1832
|
+
set({
|
|
1833
|
+
networkStatus: { ...get().networkStatus, ...(data || {}) },
|
|
1834
|
+
networkStatusReachable: true,
|
|
1835
|
+
});
|
|
1836
|
+
return data;
|
|
1837
|
+
} catch {
|
|
1838
|
+
set({ networkStatusReachable: false });
|
|
1839
|
+
return null;
|
|
1840
|
+
}
|
|
1841
|
+
},
|
|
1842
|
+
|
|
1843
|
+
async checkNetworkUpdate() {
|
|
1844
|
+
try {
|
|
1845
|
+
const data = await api.get('/network/update/check');
|
|
1846
|
+
if (!data) return null;
|
|
1847
|
+
set({
|
|
1848
|
+
networkVersion: {
|
|
1849
|
+
installed: data.installed ?? null,
|
|
1850
|
+
latest: data.latest ?? null,
|
|
1851
|
+
updateAvailable: !!data.updateAvailable,
|
|
1852
|
+
},
|
|
1853
|
+
});
|
|
1753
1854
|
return data;
|
|
1754
1855
|
} catch { return null; }
|
|
1755
1856
|
},
|
|
1756
1857
|
|
|
1858
|
+
async updateNetworkPackage() {
|
|
1859
|
+
set({
|
|
1860
|
+
networkUpdateProgress: {
|
|
1861
|
+
updating: true,
|
|
1862
|
+
step: 'starting',
|
|
1863
|
+
message: 'Starting update…',
|
|
1864
|
+
percent: 0,
|
|
1865
|
+
error: null,
|
|
1866
|
+
},
|
|
1867
|
+
});
|
|
1868
|
+
try {
|
|
1869
|
+
await api.post('/network/update');
|
|
1870
|
+
} catch (err) {
|
|
1871
|
+
set({
|
|
1872
|
+
networkUpdateProgress: {
|
|
1873
|
+
updating: false,
|
|
1874
|
+
step: 'error',
|
|
1875
|
+
message: err.message,
|
|
1876
|
+
percent: 0,
|
|
1877
|
+
error: err.message,
|
|
1878
|
+
},
|
|
1879
|
+
});
|
|
1880
|
+
get().addToast('error', 'Update failed', err.message);
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
|
|
1757
1884
|
async startNetworkNode() {
|
|
1758
1885
|
set({ networkNode: { ...get().networkNode, status: 'connecting' } });
|
|
1759
1886
|
try {
|
|
@@ -9,7 +9,7 @@ import { Dialog, DialogContent, DialogTrigger } from '../components/ui/dialog';
|
|
|
9
9
|
import { NodeToggle } from '../components/network/node-toggle';
|
|
10
10
|
import { NodeDetails } from '../components/network/node-details';
|
|
11
11
|
import { NetworkStatus } from '../components/network/network-status';
|
|
12
|
-
import { Globe, Download, Check, AlertCircle, Loader2, Trash2 } from 'lucide-react';
|
|
12
|
+
import { Globe, Download, Check, AlertCircle, Loader2, Trash2, ArrowUpCircle } from 'lucide-react';
|
|
13
13
|
|
|
14
14
|
const REQUIREMENTS = [
|
|
15
15
|
'Python 3.10 or higher',
|
|
@@ -113,6 +113,79 @@ function InstallGate() {
|
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function UpdateProgress({ progress }) {
|
|
117
|
+
const percent = Math.max(0, Math.min(100, Number.isFinite(progress.percent) ? progress.percent : 0));
|
|
118
|
+
return (
|
|
119
|
+
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-border-subtle bg-surface-0">
|
|
120
|
+
<Loader2 size={11} className="animate-spin text-accent flex-shrink-0" />
|
|
121
|
+
<div className="flex flex-col min-w-0">
|
|
122
|
+
<span className="text-2xs font-sans text-text-1 truncate">{progress.message || 'Updating…'}</span>
|
|
123
|
+
<div className="h-1 w-32 rounded-full bg-surface-3 overflow-hidden mt-0.5">
|
|
124
|
+
<div
|
|
125
|
+
className="h-full rounded-full bg-accent transition-all duration-500 ease-out"
|
|
126
|
+
style={{ width: `${percent}%` }}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<span className="text-2xs font-mono text-text-3 tabular-nums">{percent}%</span>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function UpdateButton() {
|
|
136
|
+
const [open, setOpen] = useState(false);
|
|
137
|
+
const version = useGrooveStore((s) => s.networkVersion);
|
|
138
|
+
const progress = useGrooveStore((s) => s.networkUpdateProgress);
|
|
139
|
+
const updateNetworkPackage = useGrooveStore((s) => s.updateNetworkPackage);
|
|
140
|
+
|
|
141
|
+
if (progress.updating) {
|
|
142
|
+
return <UpdateProgress progress={progress} />;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!version.updateAvailable) return null;
|
|
146
|
+
|
|
147
|
+
const confirm = async () => {
|
|
148
|
+
try {
|
|
149
|
+
await updateNetworkPackage();
|
|
150
|
+
setOpen(false);
|
|
151
|
+
} catch { /* toast handled */ }
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
156
|
+
<DialogTrigger asChild>
|
|
157
|
+
<button
|
|
158
|
+
type="button"
|
|
159
|
+
className="inline-flex items-center gap-1"
|
|
160
|
+
title={`Update to ${version.latest}`}
|
|
161
|
+
>
|
|
162
|
+
<Badge variant="warning" className="cursor-pointer">
|
|
163
|
+
<ArrowUpCircle size={10} />
|
|
164
|
+
Update Available
|
|
165
|
+
</Badge>
|
|
166
|
+
</button>
|
|
167
|
+
</DialogTrigger>
|
|
168
|
+
<DialogContent title="Update Network Package" description="Confirm update">
|
|
169
|
+
<div className="px-5 py-4 flex flex-col gap-3">
|
|
170
|
+
<p className="text-sm text-text-1 font-sans leading-relaxed">
|
|
171
|
+
Update to <span className="font-mono text-accent">{version.latest}</span>?
|
|
172
|
+
</p>
|
|
173
|
+
<p className="text-xs text-text-3 font-sans leading-relaxed">
|
|
174
|
+
You are currently running <span className="font-mono">{version.installed || 'unknown'}</span>. Your node will be stopped during the update and restarted after.
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="flex items-center justify-end gap-2 px-5 py-3 border-t border-border-subtle bg-surface-0">
|
|
178
|
+
<Button variant="ghost" size="sm" onClick={() => setOpen(false)}>Cancel</Button>
|
|
179
|
+
<Button variant="primary" size="sm" onClick={confirm}>
|
|
180
|
+
<ArrowUpCircle size={12} />
|
|
181
|
+
Update
|
|
182
|
+
</Button>
|
|
183
|
+
</div>
|
|
184
|
+
</DialogContent>
|
|
185
|
+
</Dialog>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
116
189
|
function UninstallButton() {
|
|
117
190
|
const [open, setOpen] = useState(false);
|
|
118
191
|
const uninstallNetworkPackage = useGrooveStore((s) => s.uninstallNetworkPackage);
|
|
@@ -162,17 +235,20 @@ function UninstallButton() {
|
|
|
162
235
|
export default function NetworkView() {
|
|
163
236
|
const fetchNetworkNodeStatus = useGrooveStore((s) => s.fetchNetworkNodeStatus);
|
|
164
237
|
const fetchNetworkStatus = useGrooveStore((s) => s.fetchNetworkStatus);
|
|
238
|
+
const checkNetworkUpdate = useGrooveStore((s) => s.checkNetworkUpdate);
|
|
165
239
|
const node = useGrooveStore((s) => s.networkNode);
|
|
166
240
|
const installed = useGrooveStore((s) => s.networkInstalled);
|
|
241
|
+
const version = useGrooveStore((s) => s.networkVersion);
|
|
167
242
|
|
|
168
243
|
useEffect(() => {
|
|
169
244
|
fetchNetworkNodeStatus();
|
|
170
245
|
if (installed) {
|
|
171
246
|
fetchNetworkStatus();
|
|
247
|
+
checkNetworkUpdate();
|
|
172
248
|
const interval = setInterval(() => { fetchNetworkStatus(); }, 10000);
|
|
173
249
|
return () => clearInterval(interval);
|
|
174
250
|
}
|
|
175
|
-
}, [fetchNetworkNodeStatus, fetchNetworkStatus, installed]);
|
|
251
|
+
}, [fetchNetworkNodeStatus, fetchNetworkStatus, checkNetworkUpdate, installed]);
|
|
176
252
|
|
|
177
253
|
return (
|
|
178
254
|
<div className="flex flex-col h-full">
|
|
@@ -181,6 +257,10 @@ export default function NetworkView() {
|
|
|
181
257
|
<Globe size={14} className="text-accent" />
|
|
182
258
|
<h2 className="text-sm font-semibold text-text-0 font-sans">Groove Network</h2>
|
|
183
259
|
<Badge variant="purple">Early Access</Badge>
|
|
260
|
+
{installed && version.installed && (
|
|
261
|
+
<span className="text-2xs font-mono text-text-3 tabular-nums">v{String(version.installed).replace(/^v/, '')}</span>
|
|
262
|
+
)}
|
|
263
|
+
{installed && <UpdateButton />}
|
|
184
264
|
<div className="flex-1" />
|
|
185
265
|
{installed && (
|
|
186
266
|
<>
|