groove-dev 0.27.34 → 0.27.36
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/.groove-staging/state.json +3 -0
- package/.groove-staging/timeline.json +13 -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 +12 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-BoU6IhQI.js → index-Df4O6yJI.js} +62 -62
- package/node_modules/@groove-dev/gui/dist/assets/{index-BnLiWvrh.css → index-Dx7i-7_K.css} +1 -1
- 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/components/layout/status-bar.jsx +33 -14
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +41 -16
- package/node_modules/@groove-dev/gui/src/stores/groove.js +11 -0
- 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 +12 -3
- package/packages/gui/dist/assets/{index-BoU6IhQI.js → index-Df4O6yJI.js} +62 -62
- package/packages/gui/dist/assets/{index-BnLiWvrh.css → index-Dx7i-7_K.css} +1 -1
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/layout/status-bar.jsx +33 -14
- package/packages/gui/src/components/settings/quick-connect.jsx +41 -16
- package/packages/gui/src/stores/groove.js +11 -0
|
@@ -4,7 +4,7 @@ import { useGrooveStore } from '../../stores/groove';
|
|
|
4
4
|
import { cn } from '../../lib/cn';
|
|
5
5
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
6
6
|
import {
|
|
7
|
-
Server, Radio, ExternalLink, Loader2, X, Plus, ArrowLeft,
|
|
7
|
+
Server, Radio, ExternalLink, Loader2, X, Plus, ArrowLeft, Unplug,
|
|
8
8
|
} from 'lucide-react';
|
|
9
9
|
import { StatusDot } from '../ui/status-dot';
|
|
10
10
|
import { Button } from '../ui/button';
|
|
@@ -33,9 +33,12 @@ export function QuickConnect() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function handleOpenRemote(server) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (window.groove?.remote?.openWindow) {
|
|
37
|
+
window.groove.remote.openWindow(server.localPort, server.name);
|
|
38
|
+
} else {
|
|
39
|
+
const name = encodeURIComponent(server.name);
|
|
40
|
+
window.open(`http://localhost:${server.localPort}?instance=${name}`, '_blank');
|
|
41
|
+
}
|
|
39
42
|
toggle();
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -127,36 +130,58 @@ export function QuickConnect() {
|
|
|
127
130
|
</div>
|
|
128
131
|
) : (
|
|
129
132
|
savedTunnels.map((server) => (
|
|
130
|
-
<
|
|
133
|
+
<div
|
|
131
134
|
key={server.id}
|
|
132
|
-
onClick={() => server.active ? handleOpenRemote(server) : handleConnect(server.id)}
|
|
133
|
-
disabled={connectingId === server.id}
|
|
134
135
|
className={cn(
|
|
135
|
-
'w-full flex items-center gap-3 px-4 py-2.5
|
|
136
|
+
'w-full flex items-center gap-3 px-4 py-2.5 transition-colors',
|
|
136
137
|
'hover:bg-surface-5',
|
|
137
138
|
connectingId === server.id && 'opacity-60 pointer-events-none',
|
|
138
139
|
)}
|
|
139
140
|
>
|
|
140
141
|
<Server size={15} className={server.active ? 'text-success' : 'text-text-4'} />
|
|
141
|
-
<
|
|
142
|
+
<button
|
|
143
|
+
onClick={() => server.active ? handleOpenRemote(server) : handleConnect(server.id)}
|
|
144
|
+
disabled={connectingId === server.id}
|
|
145
|
+
className="flex-1 min-w-0 text-left cursor-pointer"
|
|
146
|
+
>
|
|
142
147
|
<div className="flex items-center gap-2">
|
|
143
148
|
<span className="text-sm font-medium text-text-0 font-sans truncate">{server.name}</span>
|
|
144
149
|
{server.active && <StatusDot status="running" size="sm" />}
|
|
145
150
|
</div>
|
|
146
151
|
<span className="text-2xs text-text-4 font-mono">{server.user}@{server.host}</span>
|
|
147
|
-
</
|
|
148
|
-
<div className="flex-shrink-0">
|
|
152
|
+
</button>
|
|
153
|
+
<div className="flex items-center gap-1.5 flex-shrink-0">
|
|
149
154
|
{connectingId === server.id ? (
|
|
150
155
|
<Loader2 size={14} className="text-text-3 animate-spin" />
|
|
151
156
|
) : server.active ? (
|
|
152
|
-
|
|
153
|
-
<
|
|
154
|
-
|
|
157
|
+
<>
|
|
158
|
+
<button
|
|
159
|
+
onClick={() => handleOpenRemote(server)}
|
|
160
|
+
className="flex items-center gap-1 text-2xs text-success font-sans hover:text-success/80 cursor-pointer transition-colors"
|
|
161
|
+
>
|
|
162
|
+
<ExternalLink size={11} /> Open
|
|
163
|
+
</button>
|
|
164
|
+
<button
|
|
165
|
+
onClick={async () => {
|
|
166
|
+
await useGrooveStore.getState().disconnectTunnel(server.id);
|
|
167
|
+
addToast('info', 'Disconnected', server.name);
|
|
168
|
+
}}
|
|
169
|
+
className="p-1 text-text-4 hover:text-danger cursor-pointer transition-colors rounded"
|
|
170
|
+
title="Disconnect"
|
|
171
|
+
>
|
|
172
|
+
<Unplug size={12} />
|
|
173
|
+
</button>
|
|
174
|
+
</>
|
|
155
175
|
) : (
|
|
156
|
-
<
|
|
176
|
+
<button
|
|
177
|
+
onClick={() => handleConnect(server.id)}
|
|
178
|
+
className="text-2xs text-text-3 font-sans hover:text-text-1 cursor-pointer transition-colors"
|
|
179
|
+
>
|
|
180
|
+
Connect
|
|
181
|
+
</button>
|
|
157
182
|
)}
|
|
158
183
|
</div>
|
|
159
|
-
</
|
|
184
|
+
</div>
|
|
160
185
|
))
|
|
161
186
|
)}
|
|
162
187
|
</div>
|
|
@@ -1106,13 +1106,24 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1106
1106
|
const result = await api.post(`/tunnels/${encodeURIComponent(id)}/connect`);
|
|
1107
1107
|
set({ activeTunnelId: id });
|
|
1108
1108
|
get().fetchTunnels();
|
|
1109
|
+
if (result.localPort && result.name) {
|
|
1110
|
+
if (window.groove?.remote?.openWindow) {
|
|
1111
|
+
window.groove.remote.openWindow(result.localPort, result.name);
|
|
1112
|
+
} else {
|
|
1113
|
+
window.open(`http://localhost:${result.localPort}?instance=${encodeURIComponent(result.name)}`, '_blank');
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1109
1116
|
return result;
|
|
1110
1117
|
},
|
|
1111
1118
|
|
|
1112
1119
|
async disconnectTunnel(id) {
|
|
1120
|
+
const tunnel = get().savedTunnels.find(t => t.id === id);
|
|
1113
1121
|
await api.post(`/tunnels/${encodeURIComponent(id)}/disconnect`);
|
|
1114
1122
|
set({ activeTunnelId: null });
|
|
1115
1123
|
get().fetchTunnels();
|
|
1124
|
+
if (tunnel?.localPort && window.groove?.remote?.closeByPort) {
|
|
1125
|
+
window.groove.remote.closeByPort(tunnel.localPort);
|
|
1126
|
+
}
|
|
1116
1127
|
},
|
|
1117
1128
|
|
|
1118
1129
|
async installTunnel(id) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.36",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -2206,10 +2206,19 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2206
2206
|
const entries = [];
|
|
2207
2207
|
|
|
2208
2208
|
// Dirs first (sorted), then files (sorted)
|
|
2209
|
-
const
|
|
2210
|
-
.
|
|
2211
|
-
|
|
2209
|
+
const isDir = (e) => {
|
|
2210
|
+
if (e.isDirectory()) return true;
|
|
2211
|
+
if (e.isSymbolicLink()) { try { return statSync(resolve(fullPath, e.name)).isDirectory(); } catch { return false; } }
|
|
2212
|
+
return false;
|
|
2213
|
+
};
|
|
2214
|
+
const dirs = raw.filter((e) => isDir(e) && !IGNORED_NAMES.has(e.name) && !e.name.startsWith('.'))
|
|
2212
2215
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2216
|
+
const files = raw.filter((e) => {
|
|
2217
|
+
if (e.name.startsWith('.')) return false;
|
|
2218
|
+
if (e.isFile()) return true;
|
|
2219
|
+
if (e.isSymbolicLink()) { try { return statSync(resolve(fullPath, e.name)).isFile(); } catch { return false; } }
|
|
2220
|
+
return false;
|
|
2221
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2213
2222
|
|
|
2214
2223
|
for (const d of dirs) {
|
|
2215
2224
|
const childPath = relPath ? `${relPath}/${d.name}` : d.name;
|