groove-dev 0.24.9 → 0.24.10
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/node_modules/@groove-dev/daemon/src/api.js +29 -0
- package/node_modules/@groove-dev/daemon/src/providers/ollama.js +23 -0
- package/node_modules/@groove-dev/gui/dist/assets/{index-DjxlUkR9.css → index-BwmIyTkm.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-C7dW1iUw.js → index-Df8BnR7l.js} +29 -29
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/ollama-setup.jsx +103 -1
- package/package.json +1 -1
- package/packages/daemon/src/api.js +29 -0
- package/packages/daemon/src/providers/ollama.js +23 -0
- package/packages/gui/dist/assets/{index-DjxlUkR9.css → index-BwmIyTkm.css} +1 -1
- package/packages/gui/dist/assets/{index-C7dW1iUw.js → index-Df8BnR7l.js} +29 -29
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/src/components/agents/ollama-setup.jsx +103 -1
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
7
|
<title>Groove GUI</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Df8BnR7l.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BwmIyTkm.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -242,6 +242,72 @@ function ModelRow({ model, isInstalled, isRecommended, canRun, onPull, onDelete,
|
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
/* ── Server Status Bar ────────────────────────────────────────── */
|
|
246
|
+
|
|
247
|
+
function ServerStatusBar({ onStopped }) {
|
|
248
|
+
const [action, setAction] = useState(null); // 'stopping' | 'restarting'
|
|
249
|
+
const addToast = useGrooveStore((s) => s.addToast);
|
|
250
|
+
|
|
251
|
+
async function handleStop() {
|
|
252
|
+
setAction('stopping');
|
|
253
|
+
try {
|
|
254
|
+
const result = await api.post('/providers/ollama/stop');
|
|
255
|
+
if (result.ok) {
|
|
256
|
+
addToast('info', 'Ollama server stopped');
|
|
257
|
+
if (onStopped) onStopped();
|
|
258
|
+
} else {
|
|
259
|
+
addToast('error', 'Could not stop server');
|
|
260
|
+
}
|
|
261
|
+
} catch (err) {
|
|
262
|
+
addToast('error', 'Stop failed', err.message);
|
|
263
|
+
}
|
|
264
|
+
setAction(null);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function handleRestart() {
|
|
268
|
+
setAction('restarting');
|
|
269
|
+
try {
|
|
270
|
+
const result = await api.post('/providers/ollama/restart');
|
|
271
|
+
if (result.ok) {
|
|
272
|
+
addToast('success', 'Ollama server restarted');
|
|
273
|
+
} else {
|
|
274
|
+
addToast('error', 'Restart failed');
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
addToast('error', 'Restart failed', err.message);
|
|
278
|
+
}
|
|
279
|
+
setAction(null);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<div className="flex items-center gap-2 bg-success/8 border border-success/20 rounded-lg px-3 py-2">
|
|
284
|
+
<span className="relative flex-shrink-0 w-[6px] h-[6px]">
|
|
285
|
+
<span className="absolute inset-0 rounded-full bg-success" />
|
|
286
|
+
<span className="absolute inset-[-2px] rounded-full bg-success opacity-20 animate-pulse" />
|
|
287
|
+
</span>
|
|
288
|
+
<span className="text-xs font-sans text-success font-semibold">Server Running</span>
|
|
289
|
+
<span className="text-2xs font-mono text-text-4">:11434</span>
|
|
290
|
+
<div className="flex-1" />
|
|
291
|
+
<button
|
|
292
|
+
onClick={handleRestart}
|
|
293
|
+
disabled={!!action}
|
|
294
|
+
className="flex items-center gap-1 text-2xs font-sans text-text-3 hover:text-accent cursor-pointer transition-colors disabled:opacity-40"
|
|
295
|
+
>
|
|
296
|
+
<RefreshCw size={10} className={action === 'restarting' ? 'animate-spin' : ''} />
|
|
297
|
+
{action === 'restarting' ? 'Restarting...' : 'Restart'}
|
|
298
|
+
</button>
|
|
299
|
+
<button
|
|
300
|
+
onClick={handleStop}
|
|
301
|
+
disabled={!!action}
|
|
302
|
+
className="flex items-center gap-1 text-2xs font-sans text-text-3 hover:text-danger cursor-pointer transition-colors disabled:opacity-40"
|
|
303
|
+
>
|
|
304
|
+
<AlertCircle size={10} />
|
|
305
|
+
{action === 'stopping' ? 'Stopping...' : 'Stop'}
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
245
311
|
/* ── Model Browser (installed) ─────────────────────────────── */
|
|
246
312
|
|
|
247
313
|
function ModelBrowser({ onModelChange }) {
|
|
@@ -249,6 +315,7 @@ function ModelBrowser({ onModelChange }) {
|
|
|
249
315
|
const [pulling, setPulling] = useState(null);
|
|
250
316
|
const [category, setCategory] = useState('code');
|
|
251
317
|
const [showAll, setShowAll] = useState(false);
|
|
318
|
+
const [serverStopped, setServerStopped] = useState(false);
|
|
252
319
|
const addToast = useGrooveStore((s) => s.addToast);
|
|
253
320
|
|
|
254
321
|
const retried = useRef(false);
|
|
@@ -256,7 +323,6 @@ function ModelBrowser({ onModelChange }) {
|
|
|
256
323
|
function load() {
|
|
257
324
|
api.get('/providers/ollama/models').then((result) => {
|
|
258
325
|
setData(result);
|
|
259
|
-
// If no installed models on first load, retry once after 2s (server may still be warming up)
|
|
260
326
|
if (!retried.current && result.installed?.length === 0) {
|
|
261
327
|
retried.current = true;
|
|
262
328
|
setTimeout(load, 2000);
|
|
@@ -300,8 +366,44 @@ function ModelBrowser({ onModelChange }) {
|
|
|
300
366
|
const filtered = catalog.filter((m) => m.category === category);
|
|
301
367
|
const visible = showAll ? filtered : filtered.filter((m) => m.ramGb <= maxRam);
|
|
302
368
|
|
|
369
|
+
if (serverStopped) {
|
|
370
|
+
return (
|
|
371
|
+
<div className="space-y-3 p-3">
|
|
372
|
+
<div className="flex items-center gap-2 bg-warning/8 border border-warning/20 rounded-lg px-3 py-2.5">
|
|
373
|
+
<AlertCircle size={14} className="text-warning flex-shrink-0" />
|
|
374
|
+
<span className="text-xs font-sans text-text-2">
|
|
375
|
+
<span className="text-warning font-semibold">Ollama server stopped.</span>
|
|
376
|
+
{' '}Start it again to pull and use models.
|
|
377
|
+
</span>
|
|
378
|
+
</div>
|
|
379
|
+
<Button
|
|
380
|
+
variant="primary"
|
|
381
|
+
size="md"
|
|
382
|
+
onClick={async () => {
|
|
383
|
+
try {
|
|
384
|
+
const result = await api.post('/providers/ollama/serve');
|
|
385
|
+
if (result.ok) {
|
|
386
|
+
addToast('success', 'Ollama server started!');
|
|
387
|
+
setServerStopped(false);
|
|
388
|
+
retried.current = false;
|
|
389
|
+
setTimeout(load, 2000);
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
addToast('error', 'Could not start server', err.message);
|
|
393
|
+
}
|
|
394
|
+
}}
|
|
395
|
+
className="w-full gap-1.5"
|
|
396
|
+
>
|
|
397
|
+
<Zap size={12} />
|
|
398
|
+
Start Ollama Server
|
|
399
|
+
</Button>
|
|
400
|
+
</div>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
303
404
|
return (
|
|
304
405
|
<div className="space-y-2 p-3">
|
|
406
|
+
<ServerStatusBar onStopped={() => setServerStopped(true)} />
|
|
305
407
|
<HardwareBar hardware={hardware} />
|
|
306
408
|
|
|
307
409
|
{/* Installed count */}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.10",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. 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.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -199,6 +199,35 @@ export function createApi(app, daemon) {
|
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
+
app.post('/api/providers/ollama/stop', async (req, res) => {
|
|
203
|
+
if (!OllamaProvider.isInstalled()) return res.status(400).json({ error: 'Ollama is not installed' });
|
|
204
|
+
const running = await OllamaProvider.isServerRunning();
|
|
205
|
+
if (!running) return res.json({ ok: true, alreadyStopped: true });
|
|
206
|
+
const result = OllamaProvider.stopServer();
|
|
207
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
208
|
+
const stillRunning = await OllamaProvider.isServerRunning();
|
|
209
|
+
res.json({ ok: !stillRunning, method: result.method });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
app.post('/api/providers/ollama/restart', async (req, res) => {
|
|
213
|
+
if (!OllamaProvider.isInstalled()) return res.status(400).json({ error: 'Ollama is not installed' });
|
|
214
|
+
// Stop
|
|
215
|
+
const running = await OllamaProvider.isServerRunning();
|
|
216
|
+
if (running) {
|
|
217
|
+
OllamaProvider.stopServer();
|
|
218
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
219
|
+
}
|
|
220
|
+
// Start
|
|
221
|
+
const result = OllamaProvider.startServer();
|
|
222
|
+
if (result.started) {
|
|
223
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
224
|
+
const nowRunning = await OllamaProvider.isServerRunning();
|
|
225
|
+
res.json({ ok: nowRunning, method: result.method });
|
|
226
|
+
} else {
|
|
227
|
+
res.status(500).json({ error: 'Could not restart server' });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
202
231
|
// --- Credentials ---
|
|
203
232
|
|
|
204
233
|
app.get('/api/credentials', (req, res) => {
|
|
@@ -107,6 +107,29 @@ export class OllamaProvider extends Provider {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
static stopServer() {
|
|
111
|
+
const platform = process.platform;
|
|
112
|
+
if (platform === 'darwin') {
|
|
113
|
+
try {
|
|
114
|
+
execSync('brew services stop ollama', { stdio: 'ignore', timeout: 10000 });
|
|
115
|
+
return { stopped: true, method: 'brew services' };
|
|
116
|
+
} catch { /* fall through */ }
|
|
117
|
+
}
|
|
118
|
+
// Kill ollama serve process
|
|
119
|
+
try {
|
|
120
|
+
execSync('pkill -f "ollama serve"', { stdio: 'ignore', timeout: 5000 });
|
|
121
|
+
return { stopped: true, method: 'pkill' };
|
|
122
|
+
} catch {
|
|
123
|
+
// Also try killing by port
|
|
124
|
+
try {
|
|
125
|
+
execSync("lsof -ti:11434 | xargs kill", { stdio: 'ignore', timeout: 5000 });
|
|
126
|
+
return { stopped: true, method: 'port-kill' };
|
|
127
|
+
} catch {
|
|
128
|
+
return { stopped: false };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
110
133
|
static hardwareRequirements() {
|
|
111
134
|
return {
|
|
112
135
|
minRAM: 4,
|