groove-dev 0.27.14 → 0.27.17
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/README.md +37 -1
- package/developerID_application.cer +0 -0
- package/node_modules/@groove-dev/daemon/src/api.js +587 -68
- package/node_modules/@groove-dev/daemon/src/classifier.js +24 -0
- package/node_modules/@groove-dev/daemon/src/credentials.js +12 -2
- package/node_modules/@groove-dev/daemon/src/federation/ambassador.js +204 -0
- package/node_modules/@groove-dev/daemon/src/federation/connection.js +359 -0
- package/node_modules/@groove-dev/daemon/src/federation/contracts.js +112 -0
- package/node_modules/@groove-dev/daemon/src/federation/whitelist.js +190 -0
- package/node_modules/@groove-dev/daemon/src/federation.js +166 -7
- package/node_modules/@groove-dev/daemon/src/index.js +172 -19
- package/node_modules/@groove-dev/daemon/src/introducer.js +52 -7
- package/node_modules/@groove-dev/daemon/src/journalist.js +46 -1
- package/node_modules/@groove-dev/daemon/src/memory.js +36 -16
- package/node_modules/@groove-dev/daemon/src/process.js +140 -23
- package/node_modules/@groove-dev/daemon/src/providers/base.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +124 -28
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +104 -17
- package/node_modules/@groove-dev/daemon/src/providers/index.js +17 -0
- package/node_modules/@groove-dev/daemon/src/registry.js +10 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +93 -30
- package/node_modules/@groove-dev/daemon/src/skills.js +33 -3
- package/node_modules/@groove-dev/daemon/src/terminal-pty.js +9 -1
- package/node_modules/@groove-dev/daemon/src/tool-executor.js +11 -5
- package/node_modules/@groove-dev/daemon/src/toys.js +69 -0
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +24 -5
- package/node_modules/@groove-dev/daemon/templates/toys-catalog.json +242 -0
- package/node_modules/@groove-dev/daemon/test/classifier.test.js +98 -0
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +72 -1
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +117 -0
- package/node_modules/@groove-dev/daemon/test/memory.test.js +37 -1
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +183 -4
- package/node_modules/@groove-dev/gui/dist/assets/index-BglPgjlu.js +8607 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CGcwmmJv.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +3 -2
- package/node_modules/@groove-dev/gui/index.html +1 -0
- package/node_modules/@groove-dev/gui/src/app.css +7 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +37 -10
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +21 -31
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +11 -6
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/node_modules/@groove-dev/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +33 -2
- package/node_modules/@groove-dev/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/node_modules/@groove-dev/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/node_modules/@groove-dev/gui/src/components/editor/goto-line.jsx +35 -0
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +12 -6
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +13 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +0 -1
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/node_modules/@groove-dev/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +12 -5
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-activity.jsx +98 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-panel.jsx +290 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-peers.jsx +126 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +110 -67
- package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/settings/server-detail.jsx +310 -0
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +4 -1
- package/node_modules/@groove-dev/gui/src/components/settings/server-list.jsx +59 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-card.jsx +78 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-creator.jsx +144 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/lib/electron.js +15 -0
- package/node_modules/@groove-dev/gui/src/lib/format.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +373 -58
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +148 -42
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +92 -2
- package/node_modules/@groove-dev/gui/src/views/federation.jsx +37 -0
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +2 -42
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +32 -132
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +327 -0
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/toys.jsx +162 -0
- package/package.json +1 -1
- package/packages/daemon/src/api.js +587 -68
- package/packages/daemon/src/classifier.js +24 -0
- package/packages/daemon/src/credentials.js +12 -2
- package/packages/daemon/src/federation/ambassador.js +204 -0
- package/packages/daemon/src/federation/connection.js +359 -0
- package/packages/daemon/src/federation/contracts.js +112 -0
- package/packages/daemon/src/federation/whitelist.js +190 -0
- package/packages/daemon/src/federation.js +166 -7
- package/packages/daemon/src/index.js +172 -19
- package/packages/daemon/src/introducer.js +52 -7
- package/packages/daemon/src/journalist.js +46 -1
- package/packages/daemon/src/memory.js +36 -16
- package/packages/daemon/src/process.js +140 -23
- package/packages/daemon/src/providers/base.js +1 -0
- package/packages/daemon/src/providers/claude-code.js +1 -0
- package/packages/daemon/src/providers/codex.js +124 -28
- package/packages/daemon/src/providers/gemini.js +104 -17
- package/packages/daemon/src/providers/index.js +17 -0
- package/packages/daemon/src/registry.js +10 -1
- package/packages/daemon/src/rotator.js +93 -30
- package/packages/daemon/src/skills.js +33 -3
- package/packages/daemon/src/terminal-pty.js +9 -1
- package/packages/daemon/src/tool-executor.js +11 -5
- package/packages/daemon/src/toys.js +69 -0
- package/packages/daemon/src/tunnel-manager.js +24 -5
- package/packages/daemon/templates/toys-catalog.json +242 -0
- package/packages/gui/dist/assets/index-BglPgjlu.js +8607 -0
- package/packages/gui/dist/assets/index-CGcwmmJv.css +1 -0
- package/packages/gui/dist/index.html +3 -2
- package/packages/gui/index.html +1 -0
- package/packages/gui/src/app.css +7 -0
- package/packages/gui/src/app.jsx +37 -10
- package/packages/gui/src/components/agents/agent-chat.jsx +21 -31
- package/packages/gui/src/components/agents/agent-config.jsx +11 -6
- package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/packages/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/packages/gui/src/components/editor/code-editor.jsx +33 -2
- package/packages/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/packages/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/packages/gui/src/components/editor/goto-line.jsx +35 -0
- package/packages/gui/src/components/editor/terminal.jsx +12 -6
- package/packages/gui/src/components/layout/activity-bar.jsx +13 -3
- package/packages/gui/src/components/layout/app-shell.jsx +0 -1
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/packages/gui/src/components/layout/command-palette.jsx +6 -2
- package/packages/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/packages/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/packages/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/packages/gui/src/components/pro/pro-gate.jsx +12 -5
- package/packages/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/packages/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/packages/gui/src/components/settings/federation-activity.jsx +98 -0
- package/packages/gui/src/components/settings/federation-panel.jsx +290 -0
- package/packages/gui/src/components/settings/federation-peers.jsx +126 -0
- package/packages/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/packages/gui/src/components/settings/quick-connect.jsx +110 -67
- package/packages/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/packages/gui/src/components/settings/server-detail.jsx +310 -0
- package/packages/gui/src/components/settings/server-dialog.jsx +4 -1
- package/packages/gui/src/components/settings/server-list.jsx +59 -0
- package/packages/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/packages/gui/src/components/toys/toy-card.jsx +78 -0
- package/packages/gui/src/components/toys/toy-creator.jsx +144 -0
- package/packages/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/packages/gui/src/components/ui/toast.jsx +2 -2
- package/packages/gui/src/lib/electron.js +15 -0
- package/packages/gui/src/lib/format.js +1 -0
- package/packages/gui/src/stores/groove.js +373 -58
- package/packages/gui/src/views/agents.jsx +148 -42
- package/packages/gui/src/views/editor.jsx +92 -2
- package/packages/gui/src/views/federation.jsx +37 -0
- package/packages/gui/src/views/marketplace.jsx +2 -42
- package/packages/gui/src/views/settings.jsx +32 -132
- package/packages/gui/src/views/subscription-panel.jsx +327 -0
- package/packages/gui/src/views/teams.jsx +3 -3
- package/packages/gui/src/views/toys.jsx +162 -0
- package/plans/chat-persistence-refactor.md +154 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +0 -677
- package/packages/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/packages/gui/dist/assets/index-zdzOLAZM.js +0 -677
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Badge } from '../ui/badge';
|
|
4
|
+
import { StatusDot } from '../ui/status-dot';
|
|
5
|
+
import { Button } from '../ui/button';
|
|
6
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
7
|
+
import { fmtUptime } from '../../lib/format';
|
|
8
|
+
import { cn } from '../../lib/cn';
|
|
9
|
+
import {
|
|
10
|
+
Plug, PlugZap, Pencil, Trash2, Loader2, Check, X, AlertTriangle,
|
|
11
|
+
ExternalLink, Server, Clock, Activity, KeyRound, Globe, Settings,
|
|
12
|
+
} from 'lucide-react';
|
|
13
|
+
|
|
14
|
+
export function ServerDetail({ server, onEdit, onDelete, onConnect, onDisconnect, onTest }) {
|
|
15
|
+
const [testResult, setTestResult] = useState(null);
|
|
16
|
+
const [testLoading, setTestLoading] = useState(false);
|
|
17
|
+
const [connecting, setConnecting] = useState(false);
|
|
18
|
+
const [connectStep, setConnectStep] = useState(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
function handleWs(e) {
|
|
22
|
+
try {
|
|
23
|
+
const msg = JSON.parse(e.data);
|
|
24
|
+
if (msg.type === 'tunnel.status' && msg.data?.id === server.id) {
|
|
25
|
+
setConnectStep(msg.data.step);
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
}
|
|
29
|
+
const ws = useGrooveStore.getState().ws;
|
|
30
|
+
if (ws) ws.addEventListener('message', handleWs);
|
|
31
|
+
return () => { if (ws) ws.removeEventListener('message', handleWs); };
|
|
32
|
+
}, [server.id]);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setTestResult(null);
|
|
36
|
+
setConnectStep(null);
|
|
37
|
+
setConnecting(false);
|
|
38
|
+
}, [server.id]);
|
|
39
|
+
|
|
40
|
+
async function handleTest() {
|
|
41
|
+
setTestLoading(true);
|
|
42
|
+
setTestResult(null);
|
|
43
|
+
try {
|
|
44
|
+
const result = await onTest();
|
|
45
|
+
setTestResult(result);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
setTestResult({ error: err.message || 'Test failed' });
|
|
48
|
+
}
|
|
49
|
+
setTestLoading(false);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function handleConnect() {
|
|
53
|
+
setConnecting(true);
|
|
54
|
+
setConnectStep(null);
|
|
55
|
+
setTestResult(null);
|
|
56
|
+
try {
|
|
57
|
+
await onConnect();
|
|
58
|
+
setConnectStep(null);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const tr = err?.testResult || err?.body?.testResult;
|
|
61
|
+
if (tr) {
|
|
62
|
+
setTestResult(tr);
|
|
63
|
+
} else {
|
|
64
|
+
setTestResult({ error: err?.body?.error || err?.message || 'Connection failed' });
|
|
65
|
+
}
|
|
66
|
+
setConnectStep(null);
|
|
67
|
+
}
|
|
68
|
+
setConnecting(false);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function handleDisconnect() {
|
|
72
|
+
setConnecting(true);
|
|
73
|
+
try {
|
|
74
|
+
await onDisconnect();
|
|
75
|
+
} catch {}
|
|
76
|
+
setConnecting(false);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function handleOpenRemote() {
|
|
80
|
+
const port = server.localPort;
|
|
81
|
+
const name = encodeURIComponent(server.name);
|
|
82
|
+
window.open(`http://localhost:${port}?instance=${name}`, '_blank');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const connectLabel = connectStep === 'installing'
|
|
86
|
+
? 'Installing Groove...'
|
|
87
|
+
: connectStep === 'starting'
|
|
88
|
+
? 'Starting daemon...'
|
|
89
|
+
: connecting
|
|
90
|
+
? 'Connecting...'
|
|
91
|
+
: 'Connect';
|
|
92
|
+
|
|
93
|
+
const uptimeSeconds = server.active && server.startedAt
|
|
94
|
+
? Math.floor((Date.now() - new Date(server.startedAt).getTime()) / 1000)
|
|
95
|
+
: 0;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className="p-4 space-y-3">
|
|
99
|
+
{/* Header row */}
|
|
100
|
+
<div className="flex items-center gap-3 mb-1">
|
|
101
|
+
<div className="w-8 h-8 rounded-lg bg-surface-3 flex items-center justify-center">
|
|
102
|
+
<Server size={14} className="text-text-2" />
|
|
103
|
+
</div>
|
|
104
|
+
<div className="flex-1 min-w-0">
|
|
105
|
+
<h3 className="text-sm font-semibold text-text-0 font-sans truncate">{server.name}</h3>
|
|
106
|
+
<span className="text-2xs text-text-3 font-mono">
|
|
107
|
+
{server.user}@{server.host}:{server.port || 22}
|
|
108
|
+
</span>
|
|
109
|
+
</div>
|
|
110
|
+
{server.active ? (
|
|
111
|
+
<Badge variant="success" className="text-2xs gap-1">
|
|
112
|
+
<StatusDot status="running" size="sm" /> Connected
|
|
113
|
+
</Badge>
|
|
114
|
+
) : (
|
|
115
|
+
<Badge variant="default" className="text-2xs">Disconnected</Badge>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{/* Card grid */}
|
|
120
|
+
<div className="grid grid-cols-2 gap-3">
|
|
121
|
+
{/* Connection Info Card */}
|
|
122
|
+
<div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
|
|
123
|
+
<div className="flex items-center gap-2 mb-3">
|
|
124
|
+
<div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
|
|
125
|
+
<Globe size={12} className="text-accent" />
|
|
126
|
+
</div>
|
|
127
|
+
<span className="text-[13px] font-medium text-text-0 font-sans">Connection</span>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="space-y-2 text-2xs font-sans">
|
|
130
|
+
<div className="flex items-center justify-between">
|
|
131
|
+
<span className="text-text-3">Host</span>
|
|
132
|
+
<span className="text-text-0 font-mono">{server.host}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="flex items-center justify-between">
|
|
135
|
+
<span className="text-text-3">User</span>
|
|
136
|
+
<span className="text-text-0 font-mono">{server.user}</span>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="flex items-center justify-between">
|
|
139
|
+
<span className="text-text-3">SSH Port</span>
|
|
140
|
+
<span className="text-text-0 font-mono">{server.port || 22}</span>
|
|
141
|
+
</div>
|
|
142
|
+
{server.sshKeyPath && (
|
|
143
|
+
<div className="flex items-center justify-between">
|
|
144
|
+
<span className="text-text-3">SSH Key</span>
|
|
145
|
+
<span className="text-text-0 font-mono truncate max-w-36">{server.sshKeyPath}</span>
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Status / Stats Card */}
|
|
152
|
+
<div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
|
|
153
|
+
<div className="flex items-center gap-2 mb-3">
|
|
154
|
+
<div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
|
|
155
|
+
<Settings size={12} className="text-accent" />
|
|
156
|
+
</div>
|
|
157
|
+
<span className="text-[13px] font-medium text-text-0 font-sans">Settings</span>
|
|
158
|
+
</div>
|
|
159
|
+
<div className="space-y-2 text-2xs font-sans">
|
|
160
|
+
<div className="flex items-center justify-between">
|
|
161
|
+
<span className="text-text-3">Auto-start daemon</span>
|
|
162
|
+
<Badge variant={server.autoStart ? 'accent' : 'default'} className="text-2xs">
|
|
163
|
+
{server.autoStart ? 'On' : 'Off'}
|
|
164
|
+
</Badge>
|
|
165
|
+
</div>
|
|
166
|
+
<div className="flex items-center justify-between">
|
|
167
|
+
<span className="text-text-3">Auto-connect</span>
|
|
168
|
+
<Badge variant={server.autoConnect ? 'accent' : 'default'} className="text-2xs">
|
|
169
|
+
{server.autoConnect ? 'On' : 'Off'}
|
|
170
|
+
</Badge>
|
|
171
|
+
</div>
|
|
172
|
+
{server.active && uptimeSeconds > 0 && (
|
|
173
|
+
<div className="flex items-center justify-between">
|
|
174
|
+
<span className="text-text-3">Uptime</span>
|
|
175
|
+
<span className="text-text-0 font-sans">{fmtUptime(uptimeSeconds)}</span>
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
{server.active && server.latencyMs != null && (
|
|
179
|
+
<div className="flex items-center justify-between">
|
|
180
|
+
<span className="text-text-3">Latency</span>
|
|
181
|
+
<span className="text-text-0 font-mono">{server.latencyMs}ms</span>
|
|
182
|
+
</div>
|
|
183
|
+
)}
|
|
184
|
+
{server.active && server.localPort && (
|
|
185
|
+
<div className="flex items-center justify-between">
|
|
186
|
+
<span className="text-text-3">Local Port</span>
|
|
187
|
+
<span className="text-text-0 font-mono">{server.localPort}</span>
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
{/* Action buttons */}
|
|
195
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
196
|
+
{server.active ? (
|
|
197
|
+
<>
|
|
198
|
+
<Button
|
|
199
|
+
variant="primary"
|
|
200
|
+
size="sm"
|
|
201
|
+
onClick={handleOpenRemote}
|
|
202
|
+
className="h-8 text-xs gap-1.5"
|
|
203
|
+
>
|
|
204
|
+
<ExternalLink size={12} />
|
|
205
|
+
Open Remote GUI
|
|
206
|
+
</Button>
|
|
207
|
+
<Button
|
|
208
|
+
variant="ghost"
|
|
209
|
+
size="sm"
|
|
210
|
+
onClick={handleDisconnect}
|
|
211
|
+
disabled={connecting}
|
|
212
|
+
className="h-8 text-xs text-danger hover:text-danger gap-1.5"
|
|
213
|
+
>
|
|
214
|
+
<Plug size={12} />
|
|
215
|
+
{connecting ? 'Disconnecting...' : 'Disconnect'}
|
|
216
|
+
</Button>
|
|
217
|
+
</>
|
|
218
|
+
) : (
|
|
219
|
+
<>
|
|
220
|
+
<Button
|
|
221
|
+
variant="primary"
|
|
222
|
+
size="sm"
|
|
223
|
+
onClick={handleConnect}
|
|
224
|
+
disabled={connecting}
|
|
225
|
+
className="h-8 text-xs gap-1.5"
|
|
226
|
+
>
|
|
227
|
+
{connecting ? <Loader2 size={12} className="animate-spin" /> : <PlugZap size={12} />}
|
|
228
|
+
{connectLabel}
|
|
229
|
+
</Button>
|
|
230
|
+
<Button
|
|
231
|
+
variant="ghost"
|
|
232
|
+
size="sm"
|
|
233
|
+
onClick={handleTest}
|
|
234
|
+
disabled={testLoading || connecting}
|
|
235
|
+
className="h-8 text-xs text-text-3 gap-1.5"
|
|
236
|
+
>
|
|
237
|
+
{testLoading ? <Loader2 size={12} className="animate-spin" /> : <PlugZap size={12} />}
|
|
238
|
+
Test
|
|
239
|
+
</Button>
|
|
240
|
+
</>
|
|
241
|
+
)}
|
|
242
|
+
<div className="flex-1" />
|
|
243
|
+
{!server.active && (
|
|
244
|
+
<>
|
|
245
|
+
<Button
|
|
246
|
+
variant="ghost"
|
|
247
|
+
size="sm"
|
|
248
|
+
onClick={() => onEdit(server)}
|
|
249
|
+
className="h-8 text-xs text-text-3 gap-1.5"
|
|
250
|
+
>
|
|
251
|
+
<Pencil size={12} />
|
|
252
|
+
Edit
|
|
253
|
+
</Button>
|
|
254
|
+
<Button
|
|
255
|
+
variant="ghost"
|
|
256
|
+
size="sm"
|
|
257
|
+
onClick={() => onDelete(server.id)}
|
|
258
|
+
className="h-8 text-xs text-danger hover:text-danger gap-1.5"
|
|
259
|
+
>
|
|
260
|
+
<Trash2 size={12} />
|
|
261
|
+
Delete
|
|
262
|
+
</Button>
|
|
263
|
+
</>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{/* Inline test result */}
|
|
268
|
+
{testResult && !connecting && (
|
|
269
|
+
<div className={cn(
|
|
270
|
+
'px-3 py-2.5 rounded-lg text-2xs font-sans flex items-start gap-2',
|
|
271
|
+
testResult.error
|
|
272
|
+
? 'bg-danger/8 border border-danger/20 text-danger'
|
|
273
|
+
: testResult.reachable
|
|
274
|
+
? 'bg-success/8 border border-success/20 text-success'
|
|
275
|
+
: 'bg-warning/8 border border-warning/20 text-warning',
|
|
276
|
+
)}>
|
|
277
|
+
{testResult.error ? (
|
|
278
|
+
<><X size={11} className="mt-0.5 flex-shrink-0" /> {testResult.error}</>
|
|
279
|
+
) : testResult.reachable ? (
|
|
280
|
+
<>
|
|
281
|
+
<Check size={11} className="mt-0.5 flex-shrink-0" />
|
|
282
|
+
<span>
|
|
283
|
+
{testResult.daemonRunning
|
|
284
|
+
? 'Connected. Groove running.'
|
|
285
|
+
: testResult.grooveInstalled
|
|
286
|
+
? 'Connected. Groove installed but stopped.'
|
|
287
|
+
: 'Connected. Groove not installed.'}
|
|
288
|
+
{!testResult.daemonRunning && ' Click Connect to set up automatically.'}
|
|
289
|
+
</span>
|
|
290
|
+
</>
|
|
291
|
+
) : (
|
|
292
|
+
<><AlertTriangle size={11} className="mt-0.5 flex-shrink-0" /> Host unreachable</>
|
|
293
|
+
)}
|
|
294
|
+
<button
|
|
295
|
+
onClick={() => setTestResult(null)}
|
|
296
|
+
className="ml-auto text-text-4 hover:text-text-1 cursor-pointer flex-shrink-0"
|
|
297
|
+
>
|
|
298
|
+
<X size={10} />
|
|
299
|
+
</button>
|
|
300
|
+
</div>
|
|
301
|
+
)}
|
|
302
|
+
|
|
303
|
+
{server.active && (
|
|
304
|
+
<div className="text-2xs text-text-4 bg-surface-1 rounded-lg px-3 py-2 border border-border-subtle">
|
|
305
|
+
Separate Groove instance on your remote server. Local teams are not affected.
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
|
|
|
3
3
|
import { Dialog, DialogContent } from '../ui/dialog';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import { FolderBrowser } from '../agents/folder-browser';
|
|
6
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
6
7
|
import { cn } from '../../lib/cn';
|
|
7
8
|
import { FolderSearch } from 'lucide-react';
|
|
8
9
|
|
|
@@ -55,7 +56,9 @@ export function ServerDialog({ open, onOpenChange, server, onSave }) {
|
|
|
55
56
|
if (server?.id) data.id = server.id;
|
|
56
57
|
await onSave(data);
|
|
57
58
|
onOpenChange(false);
|
|
58
|
-
} catch {
|
|
59
|
+
} catch (err) {
|
|
60
|
+
useGrooveStore.getState().addToast('error', 'Failed to save server', err.message);
|
|
61
|
+
}
|
|
59
62
|
setSaving(false);
|
|
60
63
|
}
|
|
61
64
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { StatusDot } from '../ui/status-dot';
|
|
3
|
+
import { Button } from '../ui/button';
|
|
4
|
+
import { ScrollArea } from '../ui/scroll-area';
|
|
5
|
+
import { cn } from '../../lib/cn';
|
|
6
|
+
import { Plus, Radio } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export function ServerList({ servers, selectedId, onSelect, onAddNew }) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex flex-col h-full w-[220px] flex-shrink-0 border-r border-border-subtle bg-surface-1/50">
|
|
11
|
+
<div className="flex items-center justify-between px-3 py-2.5 border-b border-border-subtle">
|
|
12
|
+
<span className="text-2xs font-semibold text-text-2 font-sans uppercase tracking-wider">Servers</span>
|
|
13
|
+
<Button
|
|
14
|
+
variant="ghost"
|
|
15
|
+
size="sm"
|
|
16
|
+
onClick={onAddNew}
|
|
17
|
+
className="h-6 text-2xs gap-1 text-text-3 hover:text-accent"
|
|
18
|
+
>
|
|
19
|
+
<Plus size={11} /> Add
|
|
20
|
+
</Button>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<ScrollArea className="flex-1">
|
|
24
|
+
<div className="py-1">
|
|
25
|
+
{servers.length === 0 ? (
|
|
26
|
+
<div className="px-4 py-8 text-center">
|
|
27
|
+
<Radio size={18} className="text-text-4 mx-auto mb-2" />
|
|
28
|
+
<p className="text-2xs text-text-4 font-sans">No servers configured</p>
|
|
29
|
+
</div>
|
|
30
|
+
) : (
|
|
31
|
+
servers.map((server) => (
|
|
32
|
+
<button
|
|
33
|
+
key={server.id}
|
|
34
|
+
onClick={() => onSelect(server.id)}
|
|
35
|
+
className={cn(
|
|
36
|
+
'w-full text-left px-3 py-2.5 cursor-pointer transition-colors',
|
|
37
|
+
'hover:bg-surface-3',
|
|
38
|
+
selectedId === server.id
|
|
39
|
+
? 'bg-accent/8 border-l-2 border-accent'
|
|
40
|
+
: 'border-l-2 border-transparent',
|
|
41
|
+
)}
|
|
42
|
+
>
|
|
43
|
+
<div className="flex items-center gap-2 mb-0.5">
|
|
44
|
+
<StatusDot status={server.active ? 'running' : 'stopped'} size="sm" />
|
|
45
|
+
<span className="text-xs font-semibold text-text-0 font-sans truncate">
|
|
46
|
+
{server.name}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="text-2xs text-text-3 font-mono truncate pl-4">
|
|
50
|
+
{server.user}@{server.host}:{server.port || 22}
|
|
51
|
+
</div>
|
|
52
|
+
</button>
|
|
53
|
+
))
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
</ScrollArea>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|