groove-dev 0.27.126 → 0.27.127
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/local-models/daemon-bridge.js +87 -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 +149 -1
- package/node_modules/@groove-dev/daemon/src/index.js +2 -0
- package/node_modules/@groove-dev/daemon/src/model-lab.js +477 -0
- package/node_modules/@groove-dev/daemon/src/process.js +13 -0
- package/node_modules/@groove-dev/daemon/src/validate.js +216 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CY-CITov.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Cr5qYjcQ.js +8684 -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 +2 -0
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +245 -0
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +135 -0
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +60 -0
- package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +114 -0
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +190 -0
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +106 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +317 -0
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +208 -0
- package/node_modules/@groove-dev/gui/src/views/models.jsx +28 -3
- 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 +149 -1
- package/packages/daemon/src/index.js +2 -0
- package/packages/daemon/src/model-lab.js +477 -0
- package/packages/daemon/src/process.js +13 -0
- package/packages/daemon/src/validate.js +216 -0
- package/packages/gui/dist/assets/index-CY-CITov.css +1 -0
- package/packages/gui/dist/assets/index-Cr5qYjcQ.js +8684 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.jsx +2 -0
- package/packages/gui/src/components/lab/chat-playground.jsx +245 -0
- package/packages/gui/src/components/lab/metrics-panel.jsx +135 -0
- package/packages/gui/src/components/lab/parameter-panel.jsx +60 -0
- package/packages/gui/src/components/lab/preset-manager.jsx +114 -0
- package/packages/gui/src/components/lab/runtime-config.jsx +190 -0
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +106 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -1
- package/packages/gui/src/stores/groove.js +317 -0
- package/packages/gui/src/views/model-lab.jsx +208 -0
- package/packages/gui/src/views/models.jsx +28 -3
- package/node_modules/@groove-dev/gui/dist/assets/index-Do3uUrEW.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-oPlKeRNb.js +0 -8682
- package/packages/gui/dist/assets/index-Do3uUrEW.css +0 -1
- package/packages/gui/dist/assets/index-oPlKeRNb.js +0 -8682
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { existsSync, readdirSync } from 'fs';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { pathToFileURL } from 'url';
|
|
6
|
+
|
|
7
|
+
const port = 31415;
|
|
8
|
+
const projectDir = process.argv[2] || process.cwd();
|
|
9
|
+
|
|
10
|
+
function preflightCheck(daemonPath) {
|
|
11
|
+
if (!existsSync(daemonPath)) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Daemon entry point not found at ${daemonPath}. ` +
|
|
14
|
+
'The app may not have been packaged correctly — try reinstalling Groove.'
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const daemonDir = dirname(daemonPath);
|
|
19
|
+
const require = createRequire(daemonPath);
|
|
20
|
+
const critical = ['express', 'ws'];
|
|
21
|
+
const missing = critical.filter(dep => {
|
|
22
|
+
try { require.resolve(dep); return false; } catch { return true; }
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (missing.length) {
|
|
26
|
+
const diag = [`Missing deps: ${missing.join(', ')}`, `GROOVE_DAEMON_PATH: ${daemonPath}`];
|
|
27
|
+
let walkDir = daemonDir;
|
|
28
|
+
for (let i = 0; i < 5 && walkDir !== dirname(walkDir); i++) {
|
|
29
|
+
try {
|
|
30
|
+
const entries = readdirSync(walkDir);
|
|
31
|
+
diag.push(`${walkDir}/: [${entries.join(', ')}]`);
|
|
32
|
+
} catch { diag.push(`${walkDir}/: (unreadable)`); }
|
|
33
|
+
walkDir = dirname(walkDir);
|
|
34
|
+
}
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Daemon is missing dependencies. ` +
|
|
37
|
+
`Diagnostics:\n${diag.join('\n')}\n` +
|
|
38
|
+
'The app bundle may be incomplete — try reinstalling Groove.'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function main() {
|
|
44
|
+
let Daemon;
|
|
45
|
+
const daemonPath = process.env.GROOVE_DAEMON_PATH;
|
|
46
|
+
|
|
47
|
+
if (daemonPath) {
|
|
48
|
+
preflightCheck(daemonPath);
|
|
49
|
+
const mod = await import(pathToFileURL(daemonPath).href);
|
|
50
|
+
Daemon = mod.Daemon;
|
|
51
|
+
} else {
|
|
52
|
+
const mod = await import('@groove-dev/daemon');
|
|
53
|
+
Daemon = mod.Daemon;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const daemon = new Daemon({ port, projectDir });
|
|
57
|
+
await daemon.start();
|
|
58
|
+
|
|
59
|
+
process.send({ type: 'ready', port: daemon.port });
|
|
60
|
+
|
|
61
|
+
process.on('message', (msg) => {
|
|
62
|
+
if (msg.type === 'auth-token') {
|
|
63
|
+
(async () => {
|
|
64
|
+
try { await daemon.setAuthToken(msg.token); } catch (err) {
|
|
65
|
+
process.stderr.write(`[daemon-bridge] setAuthToken failed: ${err.message}\n`);
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
process.on('SIGTERM', async () => {
|
|
72
|
+
await daemon.stop();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
process.on('SIGINT', async () => {
|
|
77
|
+
await daemon.stop();
|
|
78
|
+
process.exit(0);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main().catch((err) => {
|
|
83
|
+
if (process.send) {
|
|
84
|
+
process.send({ type: 'error', message: err.message });
|
|
85
|
+
}
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|
|
@@ -16,7 +16,7 @@ import { OllamaProvider } from './providers/ollama.js';
|
|
|
16
16
|
import { ClaudeCodeProvider } from './providers/claude-code.js';
|
|
17
17
|
import { supportsSignalFlag, compareSemver, parseSemver } from './providers/groove-network.js';
|
|
18
18
|
import { ConsentManager } from '../../../moe-training/client/index.js';
|
|
19
|
-
import { validateAgentConfig, validateReasoningEffort, validateVerbosity, validateTeamMode } from './validate.js';
|
|
19
|
+
import { validateAgentConfig, validateReasoningEffort, validateVerbosity, validateTeamMode, validateLabRuntimeConfig, validateLabInferenceParams, validateLabPresetConfig } from './validate.js';
|
|
20
20
|
import { ROLE_INTEGRATIONS, wrapWithRoleReminder } from './process.js';
|
|
21
21
|
|
|
22
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1012,6 +1012,29 @@ export function createApi(app, daemon) {
|
|
|
1012
1012
|
}
|
|
1013
1013
|
});
|
|
1014
1014
|
|
|
1015
|
+
app.post('/api/models/:id/import-to-ollama', async (req, res) => {
|
|
1016
|
+
const model = daemon.modelManager.getModel(req.params.id);
|
|
1017
|
+
if (!model) return res.status(404).json({ error: 'Model not found' });
|
|
1018
|
+
const ggufPath = daemon.modelManager.getModelPath(req.params.id);
|
|
1019
|
+
if (!ggufPath) return res.status(404).json({ error: 'Model file not found on disk' });
|
|
1020
|
+
if (!OllamaProvider.isInstalled()) return res.status(400).json({ error: 'Ollama is not installed' });
|
|
1021
|
+
|
|
1022
|
+
const ollamaName = (model.id || model.filename.replace('.gguf', '')).toLowerCase().replace(/[^a-z0-9._-]/g, '-');
|
|
1023
|
+
const modelfilePath = resolve(ggufPath + '.Modelfile');
|
|
1024
|
+
try {
|
|
1025
|
+
writeFileSync(modelfilePath, `FROM ${ggufPath}\n`);
|
|
1026
|
+
const { execFileSync } = await import('child_process');
|
|
1027
|
+
execFileSync('ollama', ['create', ollamaName, '-f', modelfilePath], { timeout: 120000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
1028
|
+
try { unlinkSync(modelfilePath); } catch {}
|
|
1029
|
+
daemon.audit.log('model.import-ollama', { id: model.id, ollamaName });
|
|
1030
|
+
daemon.broadcast({ type: 'ollama:model:imported', model: ollamaName });
|
|
1031
|
+
res.json({ ok: true, ollamaName });
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
try { unlinkSync(modelfilePath); } catch {}
|
|
1034
|
+
res.status(500).json({ error: `Import failed: ${err.message}` });
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1015
1038
|
app.get('/api/models/recommend', (req, res) => {
|
|
1016
1039
|
const ramGb = parseInt(req.query.ram) || 16;
|
|
1017
1040
|
const quant = daemon.modelManager.recommendQuantization('7B', ramGb);
|
|
@@ -6595,6 +6618,131 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
6595
6618
|
})();
|
|
6596
6619
|
});
|
|
6597
6620
|
|
|
6621
|
+
// --- Model Lab ---
|
|
6622
|
+
|
|
6623
|
+
app.get('/api/lab/runtimes', async (req, res) => {
|
|
6624
|
+
try {
|
|
6625
|
+
const runtimes = daemon.modelLab.listRuntimes();
|
|
6626
|
+
const results = await Promise.all(runtimes.map(async (rt) => {
|
|
6627
|
+
const status = await daemon.modelLab.getRuntimeStatus(rt);
|
|
6628
|
+
return { ...rt, ...status };
|
|
6629
|
+
}));
|
|
6630
|
+
res.json(results);
|
|
6631
|
+
} catch (err) {
|
|
6632
|
+
res.status(500).json({ error: err.message });
|
|
6633
|
+
}
|
|
6634
|
+
});
|
|
6635
|
+
|
|
6636
|
+
app.post('/api/lab/runtimes', async (req, res) => {
|
|
6637
|
+
try {
|
|
6638
|
+
const config = validateLabRuntimeConfig(req.body);
|
|
6639
|
+
const runtime = await daemon.modelLab.addRuntime(config);
|
|
6640
|
+
res.status(201).json(runtime);
|
|
6641
|
+
} catch (err) {
|
|
6642
|
+
res.status(400).json({ error: err.message });
|
|
6643
|
+
}
|
|
6644
|
+
});
|
|
6645
|
+
|
|
6646
|
+
app.delete('/api/lab/runtimes/:id', (req, res) => {
|
|
6647
|
+
const removed = daemon.modelLab.removeRuntime(req.params.id);
|
|
6648
|
+
if (!removed) return res.status(404).json({ error: 'Runtime not found' });
|
|
6649
|
+
res.json({ ok: true });
|
|
6650
|
+
});
|
|
6651
|
+
|
|
6652
|
+
app.post('/api/lab/runtimes/:id/test', async (req, res) => {
|
|
6653
|
+
try {
|
|
6654
|
+
const result = await daemon.modelLab.testRuntime(req.params.id);
|
|
6655
|
+
res.json(result);
|
|
6656
|
+
} catch (err) {
|
|
6657
|
+
const status = err.message === 'Runtime not found' ? 404 : 500;
|
|
6658
|
+
res.status(status).json({ error: err.message });
|
|
6659
|
+
}
|
|
6660
|
+
});
|
|
6661
|
+
|
|
6662
|
+
app.get('/api/lab/runtimes/:id/models', async (req, res) => {
|
|
6663
|
+
try {
|
|
6664
|
+
const models = await daemon.modelLab.discoverModels(req.params.id);
|
|
6665
|
+
res.json(models);
|
|
6666
|
+
} catch (err) {
|
|
6667
|
+
const status = err.message === 'Runtime not found' ? 404 : 500;
|
|
6668
|
+
res.status(status).json({ error: err.message });
|
|
6669
|
+
}
|
|
6670
|
+
});
|
|
6671
|
+
|
|
6672
|
+
app.post('/api/lab/inference', async (req, res) => {
|
|
6673
|
+
try {
|
|
6674
|
+
const params = validateLabInferenceParams(req.body);
|
|
6675
|
+
|
|
6676
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
6677
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
6678
|
+
res.setHeader('Connection', 'keep-alive');
|
|
6679
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
6680
|
+
res.flushHeaders();
|
|
6681
|
+
|
|
6682
|
+
let closed = false;
|
|
6683
|
+
req.on('close', () => { closed = true; });
|
|
6684
|
+
|
|
6685
|
+
const stream = daemon.modelLab.streamInference(params);
|
|
6686
|
+
for await (const event of stream) {
|
|
6687
|
+
if (closed) break;
|
|
6688
|
+
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
6689
|
+
}
|
|
6690
|
+
|
|
6691
|
+
if (!closed) {
|
|
6692
|
+
res.write('data: [DONE]\n\n');
|
|
6693
|
+
res.end();
|
|
6694
|
+
}
|
|
6695
|
+
} catch (err) {
|
|
6696
|
+
if (!res.headersSent) {
|
|
6697
|
+
res.status(400).json({ error: err.message });
|
|
6698
|
+
} else {
|
|
6699
|
+
res.write(`data: ${JSON.stringify({ type: 'error', error: err.message })}\n\n`);
|
|
6700
|
+
res.end();
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
});
|
|
6704
|
+
|
|
6705
|
+
app.get('/api/lab/presets', (req, res) => {
|
|
6706
|
+
res.json(daemon.modelLab.listPresets());
|
|
6707
|
+
});
|
|
6708
|
+
|
|
6709
|
+
app.post('/api/lab/presets', (req, res) => {
|
|
6710
|
+
try {
|
|
6711
|
+
const config = validateLabPresetConfig(req.body);
|
|
6712
|
+
const preset = daemon.modelLab.createPreset(config);
|
|
6713
|
+
res.status(201).json(preset);
|
|
6714
|
+
} catch (err) {
|
|
6715
|
+
res.status(400).json({ error: err.message });
|
|
6716
|
+
}
|
|
6717
|
+
});
|
|
6718
|
+
|
|
6719
|
+
app.patch('/api/lab/presets/:id', (req, res) => {
|
|
6720
|
+
try {
|
|
6721
|
+
const updates = validateLabPresetConfig({ ...req.body, name: req.body.name || 'temp' });
|
|
6722
|
+
const preset = daemon.modelLab.updatePreset(req.params.id, updates);
|
|
6723
|
+
if (!preset) return res.status(404).json({ error: 'Preset not found' });
|
|
6724
|
+
res.json(preset);
|
|
6725
|
+
} catch (err) {
|
|
6726
|
+
res.status(400).json({ error: err.message });
|
|
6727
|
+
}
|
|
6728
|
+
});
|
|
6729
|
+
|
|
6730
|
+
app.delete('/api/lab/presets/:id', (req, res) => {
|
|
6731
|
+
const removed = daemon.modelLab.deletePreset(req.params.id);
|
|
6732
|
+
if (!removed) return res.status(404).json({ error: 'Preset not found' });
|
|
6733
|
+
res.json({ ok: true });
|
|
6734
|
+
});
|
|
6735
|
+
|
|
6736
|
+
app.get('/api/lab/sessions', (req, res) => {
|
|
6737
|
+
res.json(daemon.modelLab.listSessions());
|
|
6738
|
+
});
|
|
6739
|
+
|
|
6740
|
+
app.get('/api/lab/sessions/:id', (req, res) => {
|
|
6741
|
+
const session = daemon.modelLab.getSession(req.params.id);
|
|
6742
|
+
if (!session) return res.status(404).json({ error: 'Session not found' });
|
|
6743
|
+
res.json(session);
|
|
6744
|
+
});
|
|
6745
|
+
|
|
6598
6746
|
// --- Wallet & earnings stubs (Base L2 — wired to real data post-mainnet) ---
|
|
6599
6747
|
|
|
6600
6748
|
app.get('/api/network/wallet', networkGate, (req, res) => {
|
|
@@ -39,6 +39,7 @@ import { GatewayManager } from './gateways/manager.js';
|
|
|
39
39
|
import { McpManager } from './mcp-manager.js';
|
|
40
40
|
import { TunnelManager } from './tunnel-manager.js';
|
|
41
41
|
import { ModelManager } from './model-manager.js';
|
|
42
|
+
import { ModelLab } from './model-lab.js';
|
|
42
43
|
import { LlamaServerManager } from './llama-server.js';
|
|
43
44
|
import { RepoImporter } from './repo-import.js';
|
|
44
45
|
import { ConversationManager } from './conversations.js';
|
|
@@ -151,6 +152,7 @@ export class Daemon {
|
|
|
151
152
|
this.mcpManager = new McpManager(this);
|
|
152
153
|
this.tunnelManager = new TunnelManager(this);
|
|
153
154
|
this.repoImporter = new RepoImporter(this);
|
|
155
|
+
this.modelLab = new ModelLab(this);
|
|
154
156
|
this.toys = new Toys(this);
|
|
155
157
|
this.trajectoryCapture = null;
|
|
156
158
|
|