groove-dev 0.27.7 → 0.27.11
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/CLAUDE.md +0 -7
- package/node_modules/@groove-dev/daemon/src/api.js +496 -44
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +25 -12
- package/node_modules/@groove-dev/daemon/src/index.js +7 -0
- package/node_modules/@groove-dev/daemon/src/introducer.js +72 -4
- package/node_modules/@groove-dev/daemon/src/journalist.js +66 -11
- package/node_modules/@groove-dev/daemon/src/process.js +128 -104
- package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
- package/node_modules/@groove-dev/daemon/src/repo-import.js +541 -0
- package/node_modules/@groove-dev/daemon/src/rotator.js +28 -1
- package/node_modules/@groove-dev/daemon/src/supervisor.js +2 -1
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +504 -0
- package/node_modules/@groove-dev/daemon/src/validate.js +13 -0
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +5 -4
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +4 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +677 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/src/app.css +14 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +13 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +130 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/agent-mdfiles.jsx +43 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +16 -17
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +141 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +8 -8
- package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +7 -1
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +14 -4
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +46 -11
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-card.jsx +64 -0
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +363 -0
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
- package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +22 -0
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +48 -0
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +129 -0
- package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +243 -0
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +192 -0
- package/node_modules/@groove-dev/gui/src/components/ui/approval-modal.jsx +63 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/lib/edition.js +4 -0
- package/node_modules/@groove-dev/gui/src/lib/electron.js +17 -0
- package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +150 -6
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +39 -40
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +82 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +66 -0
- package/node_modules/@groove-dev/gui/vite.config.js +3 -0
- package/package.json +7 -2
- package/packages/daemon/src/api.js +496 -44
- package/packages/daemon/src/gateways/manager.js +25 -12
- package/packages/daemon/src/index.js +7 -0
- package/packages/daemon/src/introducer.js +72 -4
- package/packages/daemon/src/journalist.js +66 -11
- package/packages/daemon/src/process.js +128 -104
- package/packages/daemon/src/registry.js +1 -1
- package/packages/daemon/src/repo-import.js +541 -0
- package/packages/daemon/src/rotator.js +28 -1
- package/packages/daemon/src/supervisor.js +2 -1
- package/packages/daemon/src/tunnel-manager.js +504 -0
- package/packages/daemon/src/validate.js +13 -0
- package/packages/gui/dist/assets/index-BE6lYcd7.css +1 -0
- package/packages/gui/dist/assets/index-zdzOLAZM.js +677 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js +2 -2
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js +5 -5
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-dialog.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-scroll-area.js +1 -1
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tabs.js +5 -5
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tooltip.js +3 -3
- package/packages/gui/node_modules/.vite/deps/_metadata.json +53 -53
- package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js → chunk-DH7AESXW.js} +2 -2
- package/packages/gui/node_modules/.vite/deps/{chunk-KXLIKZFX.js → chunk-GFE3G4IN.js} +133 -133
- package/packages/gui/node_modules/.vite/deps/chunk-GFE3G4IN.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js → chunk-LKZVMLRH.js} +6 -6
- package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js → chunk-MCVDVNE5.js} +2 -2
- package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js → chunk-SPKVQGZX.js} +6 -6
- package/packages/gui/src/app.css +14 -0
- package/packages/gui/src/app.jsx +13 -0
- package/packages/gui/src/components/agents/agent-config.jsx +130 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
- package/packages/gui/src/components/agents/agent-mdfiles.jsx +43 -1
- package/packages/gui/src/components/agents/agent-node.jsx +16 -17
- package/packages/gui/src/components/agents/spawn-wizard.jsx +141 -1
- package/packages/gui/src/components/dashboard/fleet-panel.jsx +3 -3
- package/packages/gui/src/components/dashboard/intel-panel.jsx +8 -8
- package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/packages/gui/src/components/layout/activity-bar.jsx +4 -4
- package/packages/gui/src/components/layout/app-shell.jsx +7 -1
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
- package/packages/gui/src/components/layout/command-palette.jsx +14 -4
- package/packages/gui/src/components/layout/status-bar.jsx +46 -11
- package/packages/gui/src/components/marketplace/repo-card.jsx +64 -0
- package/packages/gui/src/components/marketplace/repo-import.jsx +363 -0
- package/packages/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
- package/packages/gui/src/components/pro/pro-gate.jsx +22 -0
- package/packages/gui/src/components/pro/upgrade-card.jsx +48 -0
- package/packages/gui/src/components/settings/quick-connect.jsx +129 -0
- package/packages/gui/src/components/settings/remote-server-card.jsx +243 -0
- package/packages/gui/src/components/settings/server-dialog.jsx +192 -0
- package/packages/gui/src/components/ui/approval-modal.jsx +63 -0
- package/packages/gui/src/components/ui/toast.jsx +1 -1
- package/packages/gui/src/lib/edition.js +4 -0
- package/packages/gui/src/lib/electron.js +17 -0
- package/packages/gui/src/lib/status.js +1 -0
- package/packages/gui/src/stores/groove.js +150 -6
- package/packages/gui/src/views/dashboard.jsx +39 -40
- package/packages/gui/src/views/marketplace.jsx +82 -0
- package/packages/gui/src/views/settings.jsx +66 -0
- package/packages/gui/vite.config.js +3 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Bl1_J0sN.js +0 -652
- package/node_modules/@groove-dev/gui/dist/assets/index-DjORRpF0.css +0 -1
- package/packages/gui/dist/assets/index-Bl1_J0sN.js +0 -652
- package/packages/gui/dist/assets/index-DjORRpF0.css +0 -1
- package/packages/gui/node_modules/.vite/deps/chunk-KXLIKZFX.js.map +0 -7
- package/test-slack.mjs +0 -28
- /package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js.map → chunk-DH7AESXW.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js.map → chunk-LKZVMLRH.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js.map → chunk-MCVDVNE5.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js.map → chunk-SPKVQGZX.js.map} +0 -0
|
@@ -7,6 +7,7 @@ import { api } from '../lib/api';
|
|
|
7
7
|
const WS_URL = `ws://${window.location.hostname}:${window.location.port || 31415}`;
|
|
8
8
|
|
|
9
9
|
let toastCounter = 0;
|
|
10
|
+
let plannerPollInterval = null;
|
|
10
11
|
|
|
11
12
|
function loadJSON(key, fallback = {}) {
|
|
12
13
|
try { return JSON.parse(localStorage.getItem(key) || JSON.stringify(fallback)); }
|
|
@@ -46,6 +47,10 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
46
47
|
detailPanel: null, // null | { type: 'agent', agentId } | { type: 'spawn' } | { type: 'journalist' }
|
|
47
48
|
teamDetailPanels: {}, // { [teamId]: detailPanel } — persists panel state per team
|
|
48
49
|
commandPaletteOpen: false,
|
|
50
|
+
quickConnectOpen: false,
|
|
51
|
+
|
|
52
|
+
// ── Node expansion (click-to-open persistent panels) ───────
|
|
53
|
+
expandedNodes: loadJSON('groove:expandedNodes'),
|
|
49
54
|
|
|
50
55
|
// ── Layout persistence ────────────────────────────────────
|
|
51
56
|
detailPanelWidth: Number(localStorage.getItem('groove:detailWidth')) || 480,
|
|
@@ -76,6 +81,14 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
76
81
|
// ── Toasts ────────────────────────────────────────────────
|
|
77
82
|
toasts: [],
|
|
78
83
|
|
|
84
|
+
// ── Tunnels ────────────────────────────────────────────────
|
|
85
|
+
savedTunnels: [],
|
|
86
|
+
activeTunnelId: null,
|
|
87
|
+
|
|
88
|
+
// ── GitHub Repo Import ────────────────────────────────────
|
|
89
|
+
importedRepos: [],
|
|
90
|
+
importInProgress: false,
|
|
91
|
+
|
|
79
92
|
// ── Editor state ──────────────────────────────────────────
|
|
80
93
|
editorFiles: {},
|
|
81
94
|
editorActiveFile: null,
|
|
@@ -129,6 +142,22 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
129
142
|
|| p.contextUsage !== a.contextUsage || p.name !== a.name || p.model !== a.model;
|
|
130
143
|
});
|
|
131
144
|
set({ agents: changed ? msg.data : prev, tokenTimeline: timeline, hydrated: true });
|
|
145
|
+
|
|
146
|
+
// Poll for recommended-team.json while a planner is running
|
|
147
|
+
const hasRunningPlanner = msg.data.some((a) => a.role === 'planner' && a.status === 'running');
|
|
148
|
+
if (hasRunningPlanner && !plannerPollInterval && !get().recommendedTeam) {
|
|
149
|
+
plannerPollInterval = setInterval(() => {
|
|
150
|
+
if (get().recommendedTeam) {
|
|
151
|
+
clearInterval(plannerPollInterval);
|
|
152
|
+
plannerPollInterval = null;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
get().checkRecommendedTeam();
|
|
156
|
+
}, 3000);
|
|
157
|
+
} else if ((!hasRunningPlanner || get().recommendedTeam) && plannerPollInterval) {
|
|
158
|
+
clearInterval(plannerPollInterval);
|
|
159
|
+
plannerPollInterval = null;
|
|
160
|
+
}
|
|
132
161
|
break;
|
|
133
162
|
}
|
|
134
163
|
|
|
@@ -317,7 +346,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
317
346
|
|
|
318
347
|
case 'approval:request':
|
|
319
348
|
set((s) => ({ pendingApprovals: [...s.pendingApprovals, msg.data] }));
|
|
320
|
-
get().addToast('warning', `Approval needed: ${msg.data?.agentName || 'agent'}`, msg.data?.action?.description);
|
|
321
349
|
break;
|
|
322
350
|
|
|
323
351
|
case 'approval:resolved': {
|
|
@@ -348,10 +376,32 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
348
376
|
case 'gateway:status':
|
|
349
377
|
set({ gateways: msg.data || [] });
|
|
350
378
|
break;
|
|
379
|
+
|
|
380
|
+
case 'tunnel.connected':
|
|
381
|
+
set({ activeTunnelId: msg.data?.id || null });
|
|
382
|
+
get().fetchTunnels();
|
|
383
|
+
break;
|
|
384
|
+
|
|
385
|
+
case 'tunnel.disconnected':
|
|
386
|
+
set({ activeTunnelId: null });
|
|
387
|
+
get().fetchTunnels();
|
|
388
|
+
break;
|
|
389
|
+
|
|
390
|
+
case 'tunnel.health': {
|
|
391
|
+
const tunnels = get().savedTunnels.map((t) =>
|
|
392
|
+
t.id === msg.data?.id ? { ...t, latencyMs: msg.data.latencyMs, healthy: msg.data.healthy } : t,
|
|
393
|
+
);
|
|
394
|
+
set({ savedTunnels: tunnels });
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
351
397
|
}
|
|
352
398
|
};
|
|
353
399
|
|
|
354
400
|
ws.onclose = () => {
|
|
401
|
+
if (plannerPollInterval) {
|
|
402
|
+
clearInterval(plannerPollInterval);
|
|
403
|
+
plannerPollInterval = null;
|
|
404
|
+
}
|
|
355
405
|
set({ connected: false, hydrated: false, ws: null, daemonHost: null, tunneled: false });
|
|
356
406
|
setTimeout(() => get().connect(), 2000);
|
|
357
407
|
};
|
|
@@ -459,6 +509,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
459
509
|
set((s) => ({ detailPanel: null, teamDetailPanels: { ...s.teamDetailPanels, [tid]: null } }));
|
|
460
510
|
},
|
|
461
511
|
toggleCommandPalette() { set((s) => ({ commandPaletteOpen: !s.commandPaletteOpen })); },
|
|
512
|
+
toggleQuickConnect() { set((s) => ({ quickConnectOpen: !s.quickConnectOpen })); },
|
|
462
513
|
|
|
463
514
|
setDetailPanelWidth(w) {
|
|
464
515
|
set({ detailPanelWidth: w });
|
|
@@ -474,6 +525,14 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
474
525
|
},
|
|
475
526
|
setTerminalFullHeight(v) { set({ terminalFullHeight: v }); },
|
|
476
527
|
|
|
528
|
+
toggleNodeExpanded(id) {
|
|
529
|
+
const expanded = { ...get().expandedNodes };
|
|
530
|
+
expanded[id] = !expanded[id];
|
|
531
|
+
if (!expanded[id]) delete expanded[id];
|
|
532
|
+
set({ expandedNodes: expanded });
|
|
533
|
+
persistJSON('groove:expandedNodes', expanded);
|
|
534
|
+
},
|
|
535
|
+
|
|
477
536
|
// ── Toasts ────────────────────────────────────────────────
|
|
478
537
|
|
|
479
538
|
addToast(type, message, detail) {
|
|
@@ -585,9 +644,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
585
644
|
|
|
586
645
|
// Check if all recommended roles already exist in the planner's team.
|
|
587
646
|
// If so, auto-delegate instead of showing the "Launch Team" modal.
|
|
588
|
-
const
|
|
589
|
-
const planner = planners.sort((a, b) => (b.lastActivity || '').localeCompare(a.lastActivity || ''))[0];
|
|
590
|
-
const teamId = planner?.teamId;
|
|
647
|
+
const teamId = data.teamId || null;
|
|
591
648
|
|
|
592
649
|
if (teamId) {
|
|
593
650
|
const teamAgents = get().agents.filter((a) => a.teamId === teamId && a.role !== 'planner');
|
|
@@ -613,7 +670,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
613
670
|
}
|
|
614
671
|
|
|
615
672
|
// New agents needed — show the modal for approval
|
|
616
|
-
set({ recommendedTeam: data });
|
|
673
|
+
set({ recommendedTeam: { ...data, teamId: data.teamId || null } });
|
|
617
674
|
} catch {
|
|
618
675
|
set({ recommendedTeam: null });
|
|
619
676
|
}
|
|
@@ -621,9 +678,10 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
621
678
|
|
|
622
679
|
async launchRecommendedTeam(modifiedAgents) {
|
|
623
680
|
try {
|
|
681
|
+
const teamId = get().recommendedTeam?.teamId || null;
|
|
624
682
|
set({ recommendedTeam: null }); // Dismiss modal immediately
|
|
625
683
|
get().addToast('info', 'Launching team...');
|
|
626
|
-
const body = modifiedAgents
|
|
684
|
+
const body = { ...(modifiedAgents && { agents: modifiedAgents }), ...(teamId && { teamId }) };
|
|
627
685
|
const result = await api.post('/recommended-team/launch', body);
|
|
628
686
|
const sub = [
|
|
629
687
|
result.phase2Pending ? `${result.phase2Pending} QC queued` : '',
|
|
@@ -646,6 +704,92 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
646
704
|
}
|
|
647
705
|
},
|
|
648
706
|
|
|
707
|
+
// ── GitHub Repo Import ────────────────────────────────────
|
|
708
|
+
|
|
709
|
+
async fetchImportedRepos() {
|
|
710
|
+
try {
|
|
711
|
+
const repos = await api.get('/repos/imported');
|
|
712
|
+
set({ importedRepos: repos });
|
|
713
|
+
} catch { /* ignore */ }
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
async previewRepo(repoUrl) {
|
|
717
|
+
return api.post('/repos/preview', { repoUrl });
|
|
718
|
+
},
|
|
719
|
+
|
|
720
|
+
async importRepo(repoUrl, targetPath, createTeam, teamName) {
|
|
721
|
+
set({ importInProgress: true });
|
|
722
|
+
try {
|
|
723
|
+
const result = await api.post('/repos/import', { repoUrl, targetPath, createTeam, teamName });
|
|
724
|
+
get().fetchImportedRepos();
|
|
725
|
+
return result;
|
|
726
|
+
} finally {
|
|
727
|
+
set({ importInProgress: false });
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
async softRemoveRepo(importId) {
|
|
732
|
+
await api.delete('/repos/' + importId + '/remove');
|
|
733
|
+
get().fetchImportedRepos();
|
|
734
|
+
},
|
|
735
|
+
|
|
736
|
+
async hardNukeRepo(importId, deleteFiles = true) {
|
|
737
|
+
await api.delete('/repos/' + importId + '/nuke?deleteFiles=' + deleteFiles);
|
|
738
|
+
get().fetchImportedRepos();
|
|
739
|
+
},
|
|
740
|
+
|
|
741
|
+
// ── Tunnels ──────────────────────────────────────────────
|
|
742
|
+
|
|
743
|
+
async fetchTunnels() {
|
|
744
|
+
try {
|
|
745
|
+
const tunnels = await api.get('/tunnels');
|
|
746
|
+
set({ savedTunnels: Array.isArray(tunnels) ? tunnels : [] });
|
|
747
|
+
} catch {}
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
async saveTunnel(config) {
|
|
751
|
+
const result = await api.post('/tunnels', config);
|
|
752
|
+
get().fetchTunnels();
|
|
753
|
+
return result;
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
async updateTunnel(id, config) {
|
|
757
|
+
const result = await api.patch('/tunnels/' + id, config);
|
|
758
|
+
get().fetchTunnels();
|
|
759
|
+
return result;
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
async deleteTunnel(id) {
|
|
763
|
+
await api.delete('/tunnels/' + id);
|
|
764
|
+
get().fetchTunnels();
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
async testTunnel(id) {
|
|
768
|
+
return api.post('/tunnels/' + id + '/test');
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
async connectTunnel(id) {
|
|
772
|
+
const result = await api.post('/tunnels/' + id + '/connect');
|
|
773
|
+
set({ activeTunnelId: id });
|
|
774
|
+
get().fetchTunnels();
|
|
775
|
+
if (result.url) window.open(result.url, '_blank');
|
|
776
|
+
return result;
|
|
777
|
+
},
|
|
778
|
+
|
|
779
|
+
async disconnectTunnel(id) {
|
|
780
|
+
await api.post('/tunnels/' + id + '/disconnect');
|
|
781
|
+
set({ activeTunnelId: null });
|
|
782
|
+
get().fetchTunnels();
|
|
783
|
+
},
|
|
784
|
+
|
|
785
|
+
async installTunnel(id) {
|
|
786
|
+
return api.post('/tunnels/' + id + '/install');
|
|
787
|
+
},
|
|
788
|
+
|
|
789
|
+
async startTunnel(id) {
|
|
790
|
+
return api.post('/tunnels/' + id + '/start');
|
|
791
|
+
},
|
|
792
|
+
|
|
649
793
|
// ── Journalist ────────────────────────────────────────────
|
|
650
794
|
|
|
651
795
|
async fetchJournalist() {
|
|
@@ -19,7 +19,7 @@ function DashboardSkeleton() {
|
|
|
19
19
|
return (
|
|
20
20
|
<div className="flex-1 grid gap-px p-0" style={{
|
|
21
21
|
gridTemplateRows: 'auto minmax(0, 1fr) minmax(0, 1fr)',
|
|
22
|
-
gridTemplateColumns: '
|
|
22
|
+
gridTemplateColumns: '2fr 2.5fr 1.5fr',
|
|
23
23
|
background: '#282c34',
|
|
24
24
|
}}>
|
|
25
25
|
<div className="col-span-3"><Skeleton className="h-[72px] rounded-none" /></div>
|
|
@@ -112,54 +112,53 @@ export default function DashboardView() {
|
|
|
112
112
|
|
|
113
113
|
<KpiStrip kpis={kpis} />
|
|
114
114
|
|
|
115
|
-
<div className="flex-1 min-h-0
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}}>
|
|
121
|
-
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 relative">
|
|
122
|
-
<TokenChart data={snapshots} />
|
|
123
|
-
</div>
|
|
115
|
+
<div className="flex-1 min-h-0 flex flex-col" style={{ background: '#282c34', gap: '1px' }}>
|
|
116
|
+
<div className="min-h-0 flex-1 grid" style={{ gridTemplateColumns: '3fr 1.5fr 1.5fr', gap: '0 1px' }}>
|
|
117
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 relative">
|
|
118
|
+
<TokenChart data={snapshots} />
|
|
119
|
+
</div>
|
|
124
120
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 flex flex-col border-l border-border">
|
|
122
|
+
<div className="px-3 pt-2.5 pb-1">
|
|
123
|
+
<span className="text-2xs font-mono text-text-3 uppercase tracking-widest">Cache Performance</span>
|
|
124
|
+
</div>
|
|
125
|
+
<CacheRing
|
|
126
|
+
cacheRead={tokens.cacheReadTokens}
|
|
127
|
+
cacheCreation={tokens.cacheCreationTokens}
|
|
128
|
+
totalInput={tokens.totalInputTokens}
|
|
129
|
+
/>
|
|
128
130
|
</div>
|
|
129
|
-
<CacheRing
|
|
130
|
-
cacheRead={tokens.cacheReadTokens}
|
|
131
|
-
cacheCreation={tokens.cacheCreationTokens}
|
|
132
|
-
totalInput={tokens.totalInputTokens}
|
|
133
|
-
/>
|
|
134
|
-
</div>
|
|
135
131
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 flex flex-col border-l border-border">
|
|
133
|
+
<div className="px-3 pt-2.5 pb-1">
|
|
134
|
+
<span className="text-2xs font-mono text-text-3 uppercase tracking-widest">Model Routing</span>
|
|
135
|
+
</div>
|
|
136
|
+
<RoutingChart routing={routing} agentBreakdown={agentBreakdown} />
|
|
139
137
|
</div>
|
|
140
|
-
<RoutingChart routing={routing} agentBreakdown={agentBreakdown} />
|
|
141
138
|
</div>
|
|
142
139
|
|
|
143
|
-
<div className="min-
|
|
144
|
-
<div className="
|
|
145
|
-
<
|
|
140
|
+
<div className="min-h-0 flex-1 grid" style={{ gridTemplateColumns: '2fr 2.5fr 1.5fr', gap: '0 1px' }}>
|
|
141
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 flex flex-col border-t border-border">
|
|
142
|
+
<div className="px-3 pt-2.5 pb-1 flex-shrink-0">
|
|
143
|
+
<span className="text-2xs font-mono text-text-3 uppercase tracking-widest">Agent Fleet</span>
|
|
144
|
+
</div>
|
|
145
|
+
<FleetPanel agentBreakdown={agentBreakdown} rotating={rotating} teams={teams} />
|
|
146
146
|
</div>
|
|
147
|
-
<FleetPanel agentBreakdown={agentBreakdown} rotating={rotating} teams={teams} />
|
|
148
|
-
</div>
|
|
149
147
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
148
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 flex flex-col border-t border-l border-border">
|
|
149
|
+
<IntelPanel
|
|
150
|
+
tokens={tokens}
|
|
151
|
+
rotation={rotation}
|
|
152
|
+
adaptive={adaptive}
|
|
153
|
+
journalist={journalist}
|
|
154
|
+
agentBreakdown={agentBreakdown}
|
|
155
|
+
memory={memory}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
160
158
|
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1 flex flex-col border-t border-l border-border">
|
|
160
|
+
<TeamBurnPanel teams={teamBurn} />
|
|
161
|
+
</div>
|
|
163
162
|
</div>
|
|
164
163
|
</div>
|
|
165
164
|
|
|
@@ -18,9 +18,13 @@ import { useToast } from '../lib/hooks/use-toast';
|
|
|
18
18
|
import { fmtNum, timeAgo } from '../lib/format';
|
|
19
19
|
import { useGrooveStore } from '../stores/groove';
|
|
20
20
|
import { IntegrationWizard, GoogleWorkspaceWizard } from '../components/marketplace/integration-wizard';
|
|
21
|
+
import { RepoImport } from '../components/marketplace/repo-import';
|
|
22
|
+
import { RepoCard } from '../components/marketplace/repo-card';
|
|
23
|
+
import { RepoNukeDialog } from '../components/marketplace/repo-nuke-dialog';
|
|
21
24
|
import {
|
|
22
25
|
ChevronLeft, ChevronDown, Sparkles, Plug, LogIn, LogOut,
|
|
23
26
|
User, Upload, Package, Download, ShoppingBag, RefreshCw, Trash2,
|
|
27
|
+
GitBranch,
|
|
24
28
|
} from 'lucide-react';
|
|
25
29
|
|
|
26
30
|
// ── Skill Detail ─────────────────────────────────────────
|
|
@@ -623,6 +627,82 @@ function AuthArea() {
|
|
|
623
627
|
);
|
|
624
628
|
}
|
|
625
629
|
|
|
630
|
+
// ── GitHub Browse ───────────────────────────────────────
|
|
631
|
+
function GitHubBrowse() {
|
|
632
|
+
const importedRepos = useGrooveStore((s) => s.importedRepos);
|
|
633
|
+
const fetchImportedRepos = useGrooveStore((s) => s.fetchImportedRepos);
|
|
634
|
+
const softRemoveRepo = useGrooveStore((s) => s.softRemoveRepo);
|
|
635
|
+
const hardNukeRepo = useGrooveStore((s) => s.hardNukeRepo);
|
|
636
|
+
const toast = useToast();
|
|
637
|
+
|
|
638
|
+
const [nukeTarget, setNukeTarget] = useState(null);
|
|
639
|
+
|
|
640
|
+
useEffect(() => { fetchImportedRepos(); }, []);
|
|
641
|
+
|
|
642
|
+
async function handleRemove(repo) {
|
|
643
|
+
try {
|
|
644
|
+
await softRemoveRepo(repo.id);
|
|
645
|
+
toast.success(`Removed ${repo.repoName || repo.name}`);
|
|
646
|
+
} catch (err) {
|
|
647
|
+
toast.error('Remove failed', err.message);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async function handleNukeConfirm(deleteFiles) {
|
|
652
|
+
if (!nukeTarget) return;
|
|
653
|
+
try {
|
|
654
|
+
if (deleteFiles) {
|
|
655
|
+
await hardNukeRepo(nukeTarget.id);
|
|
656
|
+
} else {
|
|
657
|
+
await softRemoveRepo(nukeTarget.id);
|
|
658
|
+
}
|
|
659
|
+
toast.success(`${deleteFiles ? 'Nuked' : 'Removed'} ${nukeTarget.name || nukeTarget.repo}`);
|
|
660
|
+
} catch (err) {
|
|
661
|
+
toast.error('Nuke failed', err.message);
|
|
662
|
+
}
|
|
663
|
+
setNukeTarget(null);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return (
|
|
667
|
+
<ScrollArea className="h-full">
|
|
668
|
+
<div className="px-5 py-4 space-y-5">
|
|
669
|
+
<RepoImport />
|
|
670
|
+
|
|
671
|
+
{importedRepos.length > 0 && (
|
|
672
|
+
<div>
|
|
673
|
+
<h3 className="text-xs font-semibold text-text-2 font-sans uppercase tracking-wider mb-3">
|
|
674
|
+
Recently Imported
|
|
675
|
+
</h3>
|
|
676
|
+
<div className="space-y-2">
|
|
677
|
+
{(Array.isArray(importedRepos) ? importedRepos : []).map((repo) => (
|
|
678
|
+
<RepoCard
|
|
679
|
+
key={repo.id}
|
|
680
|
+
repo={repo}
|
|
681
|
+
onRemove={() => handleRemove(repo)}
|
|
682
|
+
onNuke={() => setNukeTarget(repo)}
|
|
683
|
+
/>
|
|
684
|
+
))}
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
)}
|
|
688
|
+
|
|
689
|
+
{importedRepos.length === 0 && (
|
|
690
|
+
<div className="text-center py-16 text-text-4 font-sans text-sm">
|
|
691
|
+
No repos imported yet. Paste a GitHub URL above to get started.
|
|
692
|
+
</div>
|
|
693
|
+
)}
|
|
694
|
+
</div>
|
|
695
|
+
|
|
696
|
+
<RepoNukeDialog
|
|
697
|
+
repo={nukeTarget}
|
|
698
|
+
open={!!nukeTarget}
|
|
699
|
+
onClose={() => setNukeTarget(null)}
|
|
700
|
+
onConfirm={handleNukeConfirm}
|
|
701
|
+
/>
|
|
702
|
+
</ScrollArea>
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
626
706
|
// ── Main ─────────────────────────────────────────────────
|
|
627
707
|
export default function MarketplaceView() {
|
|
628
708
|
const [tab, setTab] = useState('skills');
|
|
@@ -630,6 +710,7 @@ export default function MarketplaceView() {
|
|
|
630
710
|
const tabs = [
|
|
631
711
|
{ id: 'skills', label: 'Skills', icon: Sparkles },
|
|
632
712
|
{ id: 'integrations', label: 'Integrations', icon: Plug },
|
|
713
|
+
{ id: 'github', label: 'GitHub', icon: GitBranch },
|
|
633
714
|
{ id: 'library', label: 'My Library', icon: Package },
|
|
634
715
|
];
|
|
635
716
|
|
|
@@ -662,6 +743,7 @@ export default function MarketplaceView() {
|
|
|
662
743
|
<div className="flex-1 min-h-0">
|
|
663
744
|
{tab === 'skills' && <SkillsBrowse />}
|
|
664
745
|
{tab === 'integrations' && <IntegrationsBrowse />}
|
|
746
|
+
{tab === 'github' && <GitHubBrowse />}
|
|
665
747
|
{tab === 'library' && <MyLibrary />}
|
|
666
748
|
</div>
|
|
667
749
|
</div>
|
|
@@ -12,6 +12,9 @@ import { Sheet, SheetContent } from '../components/ui/sheet';
|
|
|
12
12
|
import { api } from '../lib/api';
|
|
13
13
|
import { cn } from '../lib/cn';
|
|
14
14
|
import { fmtUptime } from '../lib/format';
|
|
15
|
+
import { RemoteServerCard } from '../components/settings/remote-server-card';
|
|
16
|
+
import { ServerDialog } from '../components/settings/server-dialog';
|
|
17
|
+
import { ProGate } from '../components/pro/pro-gate';
|
|
15
18
|
import {
|
|
16
19
|
Key, Eye, EyeOff, Check, Cpu, ChevronDown,
|
|
17
20
|
FolderOpen, FolderSearch, RotateCw, Users, Gauge, Zap,
|
|
@@ -974,6 +977,9 @@ export default function SettingsView() {
|
|
|
974
977
|
const [gwList, setGwList] = useState([]);
|
|
975
978
|
const [loading, setLoading] = useState(true);
|
|
976
979
|
const [folderBrowserOpen, setFolderBrowserOpen] = useState(false);
|
|
980
|
+
const [serverDialogOpen, setServerDialogOpen] = useState(false);
|
|
981
|
+
const [editingServer, setEditingServer] = useState(null);
|
|
982
|
+
const savedTunnels = useGrooveStore((s) => s.savedTunnels);
|
|
977
983
|
const addToast = useGrooveStore((s) => s.addToast);
|
|
978
984
|
const marketplaceUser = useGrooveStore((s) => s.marketplaceUser);
|
|
979
985
|
const marketplaceAuthenticated = useGrooveStore((s) => s.marketplaceAuthenticated);
|
|
@@ -992,6 +998,7 @@ export default function SettingsView() {
|
|
|
992
998
|
Promise.all([api.get('/providers'), api.get('/config'), api.get('/status'), api.get('/gateways')])
|
|
993
999
|
.then(([p, c, s, g]) => { setProviders(Array.isArray(p) ? p : []); setConfig(c); setDaemonInfo(s); setGwList(Array.isArray(g) ? g : []); setLoading(false); })
|
|
994
1000
|
.catch(() => setLoading(false));
|
|
1001
|
+
useGrooveStore.getState().fetchTunnels();
|
|
995
1002
|
}, []);
|
|
996
1003
|
|
|
997
1004
|
async function addGateway(type) {
|
|
@@ -1221,9 +1228,68 @@ export default function SettingsView() {
|
|
|
1221
1228
|
</div>
|
|
1222
1229
|
</div>
|
|
1223
1230
|
)}
|
|
1231
|
+
|
|
1232
|
+
{/* ═══════ REMOTE SERVERS ═══════ */}
|
|
1233
|
+
<div>
|
|
1234
|
+
<div className="flex items-center gap-2 mb-2.5 px-0.5">
|
|
1235
|
+
<span className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">Remote Servers</span>
|
|
1236
|
+
<div className="flex-1 h-px bg-border-subtle" />
|
|
1237
|
+
</div>
|
|
1238
|
+
<ProGate feature="Remote Access" description="Connect to remote servers via SSH tunnel and manage agents across machines">
|
|
1239
|
+
<div>
|
|
1240
|
+
<div className="flex justify-end mb-2.5">
|
|
1241
|
+
<Button
|
|
1242
|
+
variant="ghost"
|
|
1243
|
+
size="sm"
|
|
1244
|
+
onClick={() => { setEditingServer(null); setServerDialogOpen(true); }}
|
|
1245
|
+
className="h-6 text-2xs gap-1 text-text-3 hover:text-accent"
|
|
1246
|
+
>
|
|
1247
|
+
<Plus size={11} /> Add Server
|
|
1248
|
+
</Button>
|
|
1249
|
+
</div>
|
|
1250
|
+
{savedTunnels.length === 0 ? (
|
|
1251
|
+
<div className="rounded-lg border border-dashed border-border-subtle bg-surface-1/50 px-4 py-6 text-center">
|
|
1252
|
+
<Radio size={20} className="text-text-4 mx-auto mb-2" />
|
|
1253
|
+
<p className="text-xs text-text-3 font-sans">No remote servers configured.</p>
|
|
1254
|
+
<p className="text-2xs text-text-4 font-sans mt-1">Add one to connect to a VPS or remote machine.</p>
|
|
1255
|
+
</div>
|
|
1256
|
+
) : (
|
|
1257
|
+
<div className="grid grid-cols-2 gap-3">
|
|
1258
|
+
{savedTunnels.map((server) => (
|
|
1259
|
+
<RemoteServerCard
|
|
1260
|
+
key={server.id}
|
|
1261
|
+
server={server}
|
|
1262
|
+
onConnect={() => useGrooveStore.getState().connectTunnel(server.id)}
|
|
1263
|
+
onDisconnect={() => useGrooveStore.getState().disconnectTunnel(server.id)}
|
|
1264
|
+
onTest={() => useGrooveStore.getState().testTunnel(server.id)}
|
|
1265
|
+
onEdit={(s) => { setEditingServer(s); setServerDialogOpen(true); }}
|
|
1266
|
+
onDelete={(id) => useGrooveStore.getState().deleteTunnel(id)}
|
|
1267
|
+
/>
|
|
1268
|
+
))}
|
|
1269
|
+
</div>
|
|
1270
|
+
)}
|
|
1271
|
+
</div>
|
|
1272
|
+
</ProGate>
|
|
1273
|
+
</div>
|
|
1274
|
+
|
|
1224
1275
|
</div>
|
|
1225
1276
|
</ScrollArea>
|
|
1226
1277
|
|
|
1278
|
+
{/* Server Dialog */}
|
|
1279
|
+
<ServerDialog
|
|
1280
|
+
open={serverDialogOpen}
|
|
1281
|
+
onOpenChange={setServerDialogOpen}
|
|
1282
|
+
server={editingServer}
|
|
1283
|
+
onSave={async (data) => {
|
|
1284
|
+
if (data.id) {
|
|
1285
|
+
await useGrooveStore.getState().updateTunnel(data.id, data);
|
|
1286
|
+
} else {
|
|
1287
|
+
await useGrooveStore.getState().saveTunnel(data);
|
|
1288
|
+
}
|
|
1289
|
+
addToast('success', data.id ? 'Server updated' : 'Server added');
|
|
1290
|
+
}}
|
|
1291
|
+
/>
|
|
1292
|
+
|
|
1227
1293
|
{/* Folder Browser Modal */}
|
|
1228
1294
|
<FolderBrowser
|
|
1229
1295
|
open={folderBrowserOpen}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.11",
|
|
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)",
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"workspaces": [
|
|
44
44
|
"packages/daemon",
|
|
45
45
|
"packages/cli",
|
|
46
|
-
"packages/gui"
|
|
46
|
+
"packages/gui",
|
|
47
|
+
"packages/desktop"
|
|
47
48
|
],
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=20.0.0"
|
|
@@ -52,6 +53,10 @@
|
|
|
52
53
|
"dev:daemon": "npm run dev -w packages/daemon",
|
|
53
54
|
"dev:gui": "npm run dev -w packages/gui",
|
|
54
55
|
"build": "npm run build -w packages/gui",
|
|
56
|
+
"build:pro": "GROOVE_EDITION=pro npm run build -w packages/gui",
|
|
57
|
+
"start:desktop": "npm run start -w packages/desktop",
|
|
58
|
+
"build:desktop": "npm run build -w packages/desktop",
|
|
59
|
+
"dist:desktop": "npm run build:pro && npm run dist -w packages/desktop",
|
|
55
60
|
"test": "node --test packages/daemon/test/*.test.js",
|
|
56
61
|
"prepublishOnly": "npm run build"
|
|
57
62
|
},
|