groove-dev 0.27.42 → 0.27.45
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/groovedev-beta-auth-endpoint.md +166 -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 +619 -0
- package/node_modules/@groove-dev/daemon/src/firstrun.js +11 -0
- package/node_modules/@groove-dev/daemon/src/index.js +28 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/groove-network.js +114 -0
- package/node_modules/@groove-dev/daemon/src/providers/index.js +2 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BoIbnaqa.js +8607 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CyVj0fHl.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/app.jsx +3 -0
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +5 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +7 -3
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +12 -0
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +25 -7
- package/node_modules/@groove-dev/gui/src/components/network/network-status.jsx +164 -0
- package/node_modules/@groove-dev/gui/src/components/network/node-details.jsx +66 -0
- package/node_modules/@groove-dev/gui/src/components/network/node-toggle.jsx +172 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +191 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/views/network.jsx +227 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +88 -1
- 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 +619 -0
- package/packages/daemon/src/firstrun.js +11 -0
- package/packages/daemon/src/index.js +28 -0
- package/packages/daemon/src/providers/claude-code.js +1 -1
- package/packages/daemon/src/providers/groove-network.js +114 -0
- package/packages/daemon/src/providers/index.js +2 -0
- package/packages/gui/dist/assets/index-BoIbnaqa.js +8607 -0
- package/packages/gui/dist/assets/index-CyVj0fHl.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.jsx +3 -0
- package/packages/gui/src/components/editor/terminal.jsx +5 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +7 -3
- package/packages/gui/src/components/layout/status-bar.jsx +12 -0
- package/packages/gui/src/components/layout/terminal-panel.jsx +25 -7
- package/packages/gui/src/components/network/network-status.jsx +164 -0
- package/packages/gui/src/components/network/node-details.jsx +66 -0
- package/packages/gui/src/components/network/node-toggle.jsx +172 -0
- package/packages/gui/src/stores/groove.js +191 -0
- package/packages/gui/src/views/agents.jsx +1 -1
- package/packages/gui/src/views/network.jsx +227 -0
- package/packages/gui/src/views/settings.jsx +88 -1
- package/analyist/groove-security-audit.md +0 -323
- package/node_modules/@groove-dev/gui/dist/assets/index-C1C2biHU.js +0 -8607
- package/node_modules/@groove-dev/gui/dist/assets/index-Dx7i-7_K.css +0 -1
- package/packages/gui/dist/assets/index-C1C2biHU.js +0 -8607
- package/packages/gui/dist/assets/index-Dx7i-7_K.css +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
3
|
+
import { ScrollArea } from '../ui/scroll-area';
|
|
4
|
+
import { timeAgo } from '../../lib/format';
|
|
5
|
+
import { cn } from '../../lib/cn';
|
|
6
|
+
import { Activity, Zap, AlertTriangle, CheckCircle } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
const EVENT_ICON = {
|
|
9
|
+
info: Activity,
|
|
10
|
+
success: CheckCircle,
|
|
11
|
+
warning: AlertTriangle,
|
|
12
|
+
error: AlertTriangle,
|
|
13
|
+
connected: CheckCircle,
|
|
14
|
+
disconnected: AlertTriangle,
|
|
15
|
+
session: Zap,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const EVENT_COLOR = {
|
|
19
|
+
info: 'text-text-3',
|
|
20
|
+
success: 'text-success',
|
|
21
|
+
warning: 'text-warning',
|
|
22
|
+
error: 'text-danger',
|
|
23
|
+
connected: 'text-success',
|
|
24
|
+
disconnected: 'text-warning',
|
|
25
|
+
session: 'text-accent',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function NodeDetails() {
|
|
29
|
+
const events = useGrooveStore((s) => s.networkEvents);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex flex-col rounded-lg border border-border bg-surface-1 overflow-hidden min-h-0">
|
|
33
|
+
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-border-subtle">
|
|
34
|
+
<Activity size={12} className="text-text-3" />
|
|
35
|
+
<span className="text-xs font-semibold text-text-1 font-sans">Node Activity</span>
|
|
36
|
+
<div className="flex-1" />
|
|
37
|
+
<span className="text-2xs font-mono text-text-4">{events.length} events</span>
|
|
38
|
+
</div>
|
|
39
|
+
<ScrollArea className="flex-1 min-h-[160px] max-h-[320px]">
|
|
40
|
+
{events.length === 0 ? (
|
|
41
|
+
<div className="px-4 py-8 text-center text-2xs text-text-4 font-sans">
|
|
42
|
+
Toggle on to start contributing — events will appear here.
|
|
43
|
+
</div>
|
|
44
|
+
) : (
|
|
45
|
+
<ul className="divide-y divide-border-subtle">
|
|
46
|
+
{[...events].reverse().map((ev, i) => {
|
|
47
|
+
const level = ev.level || ev.type || 'info';
|
|
48
|
+
const Icon = EVENT_ICON[level] || Activity;
|
|
49
|
+
const color = EVENT_COLOR[level] || 'text-text-3';
|
|
50
|
+
return (
|
|
51
|
+
<li key={i} className="flex items-start gap-2.5 px-4 py-2">
|
|
52
|
+
<Icon size={11} className={cn('flex-shrink-0 mt-0.5', color)} />
|
|
53
|
+
<div className="flex-1 min-w-0">
|
|
54
|
+
<div className="text-xs font-sans text-text-1 break-words">{ev.msg || ev.message || ev.text || 'event'}</div>
|
|
55
|
+
{ev.detail && <div className="text-2xs text-text-4 font-mono mt-0.5">{ev.detail}</div>}
|
|
56
|
+
</div>
|
|
57
|
+
<span className="text-2xs font-mono text-text-4 flex-shrink-0">{timeAgo(ev.timestamp || ev.ts)}</span>
|
|
58
|
+
</li>
|
|
59
|
+
);
|
|
60
|
+
})}
|
|
61
|
+
</ul>
|
|
62
|
+
)}
|
|
63
|
+
</ScrollArea>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
5
|
+
import { Badge } from '../ui/badge';
|
|
6
|
+
import { StatusDot } from '../ui/status-dot';
|
|
7
|
+
import { Tooltip } from '../ui/tooltip';
|
|
8
|
+
import { Copy, Check, Cpu } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
function shortAddress(addr) {
|
|
11
|
+
if (!addr || typeof addr !== 'string') return '—';
|
|
12
|
+
if (addr.length < 14) return addr;
|
|
13
|
+
return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function nodeStatus(node) {
|
|
17
|
+
if (node.active && node.status === 'connected') return 'running';
|
|
18
|
+
if (node.status === 'connecting') return 'starting';
|
|
19
|
+
return 'crashed';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function Toggle({ value, onChange, disabled }) {
|
|
23
|
+
return (
|
|
24
|
+
<button
|
|
25
|
+
onClick={() => !disabled && onChange(!value)}
|
|
26
|
+
disabled={disabled}
|
|
27
|
+
className={cn(
|
|
28
|
+
'relative inline-flex h-7 w-12 rounded-full p-0.5 transition-colors',
|
|
29
|
+
disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',
|
|
30
|
+
value ? 'bg-accent' : 'bg-surface-5',
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
<span
|
|
34
|
+
className={cn(
|
|
35
|
+
'inline-block h-6 w-6 rounded-full bg-white shadow-sm transition-transform',
|
|
36
|
+
value ? 'translate-x-5' : 'translate-x-0',
|
|
37
|
+
)}
|
|
38
|
+
/>
|
|
39
|
+
</button>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function NodeToggle() {
|
|
44
|
+
const node = useGrooveStore((s) => s.networkNode);
|
|
45
|
+
const startNetworkNode = useGrooveStore((s) => s.startNetworkNode);
|
|
46
|
+
const stopNetworkNode = useGrooveStore((s) => s.stopNetworkNode);
|
|
47
|
+
const [pending, setPending] = useState(false);
|
|
48
|
+
const [copied, setCopied] = useState(false);
|
|
49
|
+
|
|
50
|
+
async function handleToggle(next) {
|
|
51
|
+
setPending(true);
|
|
52
|
+
try {
|
|
53
|
+
if (next) await startNetworkNode();
|
|
54
|
+
else await stopNetworkNode();
|
|
55
|
+
} catch { /* toasted in store */ }
|
|
56
|
+
setPending(false);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function handleCopy() {
|
|
60
|
+
if (!node.nodeId) return;
|
|
61
|
+
try {
|
|
62
|
+
await navigator.clipboard.writeText(node.nodeId);
|
|
63
|
+
setCopied(true);
|
|
64
|
+
setTimeout(() => setCopied(false), 1500);
|
|
65
|
+
} catch { /* clipboard blocked */ }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const active = !!node.active;
|
|
69
|
+
const hardware = node.hardware || {};
|
|
70
|
+
const layersLabel = Array.isArray(node.layers)
|
|
71
|
+
? `layers ${node.layers[0]}-${node.layers[1]}`
|
|
72
|
+
: (node.layers && node.layers.start != null)
|
|
73
|
+
? `layers ${node.layers.start}-${node.layers.end}`
|
|
74
|
+
: 'unassigned';
|
|
75
|
+
const modelLabel = node.model || 'No model assigned';
|
|
76
|
+
const memPct = Number.isFinite(node.memoryPct) ? node.memoryPct : null;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="rounded-lg border border-border bg-surface-1 overflow-hidden">
|
|
80
|
+
{/* Hero toggle */}
|
|
81
|
+
<div className="flex items-center gap-4 px-5 py-4 border-b border-border-subtle">
|
|
82
|
+
<div className="flex-1 min-w-0">
|
|
83
|
+
<div className="text-sm font-semibold text-text-0 font-sans">Lend Compute</div>
|
|
84
|
+
<div className="text-2xs text-text-3 font-sans mt-0.5">
|
|
85
|
+
{active ? 'Contributing to the Groove network' : 'Your machine can contribute to the Groove network'}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<Toggle value={active} onChange={handleToggle} disabled={pending} />
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Body */}
|
|
92
|
+
<div className="px-5 py-4 space-y-4">
|
|
93
|
+
{!active ? (
|
|
94
|
+
<div>
|
|
95
|
+
<div className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider mb-2">Detected hardware</div>
|
|
96
|
+
<div className="grid grid-cols-3 gap-2">
|
|
97
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2">
|
|
98
|
+
<div className="text-2xs text-text-4 font-sans">Device</div>
|
|
99
|
+
<div className="text-xs font-mono text-text-1 truncate">{hardware.device || 'auto'}</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2">
|
|
102
|
+
<div className="text-2xs text-text-4 font-sans">Memory</div>
|
|
103
|
+
<div className="text-xs font-mono text-text-1 truncate">{hardware.memory || '—'}</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2">
|
|
106
|
+
<div className="text-2xs text-text-4 font-sans">GPU</div>
|
|
107
|
+
<div className="text-xs font-mono text-text-1 truncate">{hardware.gpu || 'None'}</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
) : (
|
|
112
|
+
<>
|
|
113
|
+
{/* Status row */}
|
|
114
|
+
<div className="flex items-center gap-3">
|
|
115
|
+
<StatusDot status={nodeStatus(node)} />
|
|
116
|
+
<div className="text-xs font-sans text-text-1 capitalize">{node.status || 'disconnected'}</div>
|
|
117
|
+
<div className="flex-1" />
|
|
118
|
+
<Badge variant="accent" className="font-mono">
|
|
119
|
+
<Cpu size={9} /> {modelLabel} · {layersLabel}
|
|
120
|
+
</Badge>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Identity */}
|
|
124
|
+
<div>
|
|
125
|
+
<div className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider mb-1.5">Node Identity</div>
|
|
126
|
+
<div className="flex items-center gap-2">
|
|
127
|
+
<code className="flex-1 h-8 px-2.5 flex items-center bg-surface-0 border border-border-subtle rounded-md text-xs font-mono text-text-1">
|
|
128
|
+
{shortAddress(node.nodeId)}
|
|
129
|
+
</code>
|
|
130
|
+
<Tooltip content={copied ? 'Copied' : 'Copy full address'} side="top">
|
|
131
|
+
<button
|
|
132
|
+
onClick={handleCopy}
|
|
133
|
+
className="h-8 w-8 inline-flex items-center justify-center rounded-md border border-border-subtle bg-surface-0 text-text-3 hover:text-accent hover:border-accent/40 cursor-pointer transition-colors"
|
|
134
|
+
>
|
|
135
|
+
{copied ? <Check size={12} /> : <Copy size={12} />}
|
|
136
|
+
</button>
|
|
137
|
+
</Tooltip>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{/* Sessions + memory */}
|
|
142
|
+
<div className="grid grid-cols-2 gap-2">
|
|
143
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2">
|
|
144
|
+
<div className="text-2xs text-text-4 font-sans uppercase tracking-wider">Active Sessions</div>
|
|
145
|
+
<div className="text-lg font-mono text-text-0 tabular-nums leading-tight">{node.sessions || 0}</div>
|
|
146
|
+
</div>
|
|
147
|
+
<div className="rounded-md border border-border-subtle bg-surface-0 px-3 py-2">
|
|
148
|
+
<div className="text-2xs text-text-4 font-sans uppercase tracking-wider mb-1">Memory</div>
|
|
149
|
+
{memPct != null ? (
|
|
150
|
+
<>
|
|
151
|
+
<div className="h-1.5 w-full rounded-full bg-surface-3 overflow-hidden">
|
|
152
|
+
<div
|
|
153
|
+
className={cn(
|
|
154
|
+
'h-full rounded-full transition-all',
|
|
155
|
+
memPct >= 90 ? 'bg-danger' : memPct >= 70 ? 'bg-warning' : 'bg-accent',
|
|
156
|
+
)}
|
|
157
|
+
style={{ width: `${Math.min(100, Math.max(0, memPct))}%` }}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
<div className="mt-1 text-2xs font-mono text-text-3 tabular-nums">{Math.round(memPct)}%</div>
|
|
161
|
+
</>
|
|
162
|
+
) : (
|
|
163
|
+
<div className="text-xs font-mono text-text-3">—</div>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
@@ -86,6 +86,14 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
86
86
|
// ── Journalist ────────────────────────────────────────────
|
|
87
87
|
journalistStatus: null, // { cycleCount, lastCycleTime, history, lastSynthesis }
|
|
88
88
|
|
|
89
|
+
// ── Network (Early Access) ────────────────────────────────
|
|
90
|
+
networkUnlocked: false,
|
|
91
|
+
networkInstalled: false,
|
|
92
|
+
networkInstallProgress: { installing: false, step: null, message: null, percent: 0, error: null },
|
|
93
|
+
networkNode: { active: false, status: 'disconnected', nodeId: null, layers: null, model: null, sessions: 0, hardware: null },
|
|
94
|
+
networkStatus: { nodes: [], coverage: 0, totalLayers: 0, models: [], activeSessions: 0 },
|
|
95
|
+
networkEvents: [],
|
|
96
|
+
|
|
89
97
|
// ── Marketplace Auth ───────────────────────────────────────
|
|
90
98
|
marketplaceUser: null, // { id, displayName, avatar, ... } or null
|
|
91
99
|
marketplaceAuthenticated: false,
|
|
@@ -155,6 +163,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
155
163
|
get().fetchApprovals();
|
|
156
164
|
get().checkMarketplaceAuth();
|
|
157
165
|
get().fetchTunnels();
|
|
166
|
+
get().fetchBetaStatus();
|
|
167
|
+
get().fetchNetworkInstallStatus();
|
|
158
168
|
if (!get().onboardingComplete) get().fetchOnboardingStatus();
|
|
159
169
|
if (window.groove?.auth?.onSubscriptionStatus) {
|
|
160
170
|
window.groove.auth.onSubscriptionStatus((data) => {
|
|
@@ -585,6 +595,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
585
595
|
},
|
|
586
596
|
});
|
|
587
597
|
}).catch(() => {});
|
|
598
|
+
get().fetchTunnels();
|
|
588
599
|
break;
|
|
589
600
|
}
|
|
590
601
|
|
|
@@ -592,6 +603,59 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
592
603
|
set({ marketplaceAuthenticated: false, marketplaceUser: null });
|
|
593
604
|
get().addToast('warning', 'Session expired', 'Please sign in again');
|
|
594
605
|
break;
|
|
606
|
+
|
|
607
|
+
case 'network:node:status':
|
|
608
|
+
set({ networkNode: { ...get().networkNode, ...(msg.data || {}) } });
|
|
609
|
+
break;
|
|
610
|
+
|
|
611
|
+
case 'network:node:event': {
|
|
612
|
+
const ev = msg.data || {};
|
|
613
|
+
set((s) => ({
|
|
614
|
+
networkEvents: [...s.networkEvents, { ...ev, timestamp: ev.timestamp || Date.now() }].slice(-100),
|
|
615
|
+
}));
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
case 'network:status':
|
|
620
|
+
set({ networkStatus: { ...get().networkStatus, ...(msg.data || {}) } });
|
|
621
|
+
break;
|
|
622
|
+
|
|
623
|
+
case 'network:install:progress': {
|
|
624
|
+
const { step, message, percent } = msg.data || {};
|
|
625
|
+
if (step === 'done') {
|
|
626
|
+
set({
|
|
627
|
+
networkInstalled: true,
|
|
628
|
+
networkInstallProgress: { installing: false, step: null, message: null, percent: 0, error: null },
|
|
629
|
+
});
|
|
630
|
+
get().addToast('success', 'Network package installed');
|
|
631
|
+
} else if (step === 'error') {
|
|
632
|
+
set({
|
|
633
|
+
networkInstallProgress: {
|
|
634
|
+
installing: false,
|
|
635
|
+
step: 'error',
|
|
636
|
+
message: message || 'Install failed',
|
|
637
|
+
percent: 0,
|
|
638
|
+
error: message || 'Install failed',
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
} else {
|
|
642
|
+
set({
|
|
643
|
+
networkInstallProgress: {
|
|
644
|
+
installing: true,
|
|
645
|
+
step: step || 'progress',
|
|
646
|
+
message: message || '',
|
|
647
|
+
percent: Number.isFinite(percent) ? Math.max(0, Math.min(100, percent)) : get().networkInstallProgress.percent,
|
|
648
|
+
error: null,
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
case 'config:updated':
|
|
656
|
+
get().fetchBetaStatus();
|
|
657
|
+
get().fetchNetworkInstallStatus();
|
|
658
|
+
break;
|
|
595
659
|
}
|
|
596
660
|
};
|
|
597
661
|
|
|
@@ -1588,6 +1652,133 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1588
1652
|
}
|
|
1589
1653
|
},
|
|
1590
1654
|
|
|
1655
|
+
// ── Network (Early Access) ────────────────────────────────
|
|
1656
|
+
|
|
1657
|
+
async fetchBetaStatus() {
|
|
1658
|
+
try {
|
|
1659
|
+
const data = await api.get('/beta/status');
|
|
1660
|
+
set({ networkUnlocked: !!data?.unlocked });
|
|
1661
|
+
} catch { /* endpoint may not exist yet */ }
|
|
1662
|
+
},
|
|
1663
|
+
|
|
1664
|
+
async activateBeta(code) {
|
|
1665
|
+
const data = await api.post('/beta/activate', { code });
|
|
1666
|
+
if (!data?.unlocked) {
|
|
1667
|
+
throw new Error(data?.message || 'Invalid invite code');
|
|
1668
|
+
}
|
|
1669
|
+
set({ networkUnlocked: true });
|
|
1670
|
+
return data;
|
|
1671
|
+
},
|
|
1672
|
+
|
|
1673
|
+
async deactivateBeta() {
|
|
1674
|
+
try {
|
|
1675
|
+
await api.post('/beta/deactivate');
|
|
1676
|
+
set({
|
|
1677
|
+
networkUnlocked: false,
|
|
1678
|
+
activeView: get().activeView === 'network' ? 'agents' : get().activeView,
|
|
1679
|
+
});
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
get().addToast('error', 'Deactivate failed', err.message);
|
|
1682
|
+
throw err;
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
|
|
1686
|
+
async fetchNetworkNodeStatus() {
|
|
1687
|
+
try {
|
|
1688
|
+
const data = await api.get('/network/node/status');
|
|
1689
|
+
const update = { networkNode: { ...get().networkNode, ...(data || {}) } };
|
|
1690
|
+
if (data && typeof data.installed === 'boolean') {
|
|
1691
|
+
update.networkInstalled = data.installed;
|
|
1692
|
+
}
|
|
1693
|
+
set(update);
|
|
1694
|
+
return data;
|
|
1695
|
+
} catch { return null; }
|
|
1696
|
+
},
|
|
1697
|
+
|
|
1698
|
+
async fetchNetworkInstallStatus() {
|
|
1699
|
+
try {
|
|
1700
|
+
const data = await api.get('/network/install/status');
|
|
1701
|
+
if (data && typeof data.installed === 'boolean') {
|
|
1702
|
+
set({ networkInstalled: data.installed });
|
|
1703
|
+
}
|
|
1704
|
+
return data;
|
|
1705
|
+
} catch { return null; }
|
|
1706
|
+
},
|
|
1707
|
+
|
|
1708
|
+
async installNetworkPackage() {
|
|
1709
|
+
set({
|
|
1710
|
+
networkInstallProgress: {
|
|
1711
|
+
installing: true,
|
|
1712
|
+
step: 'starting',
|
|
1713
|
+
message: 'Starting install…',
|
|
1714
|
+
percent: 0,
|
|
1715
|
+
error: null,
|
|
1716
|
+
},
|
|
1717
|
+
});
|
|
1718
|
+
try {
|
|
1719
|
+
await api.post('/network/install');
|
|
1720
|
+
} catch (err) {
|
|
1721
|
+
set({
|
|
1722
|
+
networkInstallProgress: {
|
|
1723
|
+
installing: false,
|
|
1724
|
+
step: 'error',
|
|
1725
|
+
message: err.message,
|
|
1726
|
+
percent: 0,
|
|
1727
|
+
error: err.message,
|
|
1728
|
+
},
|
|
1729
|
+
});
|
|
1730
|
+
get().addToast('error', 'Install failed', err.message);
|
|
1731
|
+
}
|
|
1732
|
+
},
|
|
1733
|
+
|
|
1734
|
+
async uninstallNetworkPackage() {
|
|
1735
|
+
try {
|
|
1736
|
+
await api.post('/network/uninstall');
|
|
1737
|
+
set({
|
|
1738
|
+
networkInstalled: false,
|
|
1739
|
+
networkNode: { active: false, status: 'disconnected', nodeId: null, layers: null, model: null, sessions: 0, hardware: null },
|
|
1740
|
+
networkInstallProgress: { installing: false, step: null, message: null, percent: 0, error: null },
|
|
1741
|
+
});
|
|
1742
|
+
get().addToast('success', 'Network package uninstalled');
|
|
1743
|
+
} catch (err) {
|
|
1744
|
+
get().addToast('error', 'Uninstall failed', err.message);
|
|
1745
|
+
throw err;
|
|
1746
|
+
}
|
|
1747
|
+
},
|
|
1748
|
+
|
|
1749
|
+
async fetchNetworkStatus() {
|
|
1750
|
+
try {
|
|
1751
|
+
const data = await api.get('/network/status');
|
|
1752
|
+
set({ networkStatus: { ...get().networkStatus, ...(data || {}) } });
|
|
1753
|
+
return data;
|
|
1754
|
+
} catch { return null; }
|
|
1755
|
+
},
|
|
1756
|
+
|
|
1757
|
+
async startNetworkNode() {
|
|
1758
|
+
set({ networkNode: { ...get().networkNode, status: 'connecting' } });
|
|
1759
|
+
try {
|
|
1760
|
+
const data = await api.post('/network/node/start');
|
|
1761
|
+
set({ networkNode: { ...get().networkNode, active: true, ...(data || {}) } });
|
|
1762
|
+
get().addToast('success', 'Node started', 'Connecting to the Groove network');
|
|
1763
|
+
return data;
|
|
1764
|
+
} catch (err) {
|
|
1765
|
+
set({ networkNode: { ...get().networkNode, status: 'disconnected', active: false } });
|
|
1766
|
+
get().addToast('error', 'Node start failed', err.message);
|
|
1767
|
+
throw err;
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
|
|
1771
|
+
async stopNetworkNode() {
|
|
1772
|
+
try {
|
|
1773
|
+
await api.post('/network/node/stop');
|
|
1774
|
+
set({ networkNode: { ...get().networkNode, active: false, status: 'disconnected' } });
|
|
1775
|
+
get().addToast('info', 'Node stopped');
|
|
1776
|
+
} catch (err) {
|
|
1777
|
+
get().addToast('error', 'Node stop failed', err.message);
|
|
1778
|
+
throw err;
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
|
|
1591
1782
|
async renameFile(oldPath, newPath) {
|
|
1592
1783
|
try {
|
|
1593
1784
|
await api.post('/files/rename', { oldPath, newPath });
|
|
@@ -181,7 +181,7 @@ export function TeamTabBar() {
|
|
|
181
181
|
className={cn(
|
|
182
182
|
'relative flex items-center gap-2 px-3 h-9 text-xs font-sans cursor-pointer select-none transition-colors flex-shrink-0',
|
|
183
183
|
isActive
|
|
184
|
-
? 'text-text-0 font-semibold border-x border-x-
|
|
184
|
+
? 'text-text-0 font-semibold border-x border-x-[#242830] bg-[#242830]'
|
|
185
185
|
: 'text-text-3 hover:text-text-1 hover:bg-surface-3/50',
|
|
186
186
|
dragId === team.id && 'opacity-40',
|
|
187
187
|
dragOverId === team.id && dragId !== team.id && 'border-l-2 !border-l-accent',
|