groove-dev 0.27.142 → 0.27.144
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/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
- package/node_modules/@groove-dev/daemon/src/index.js +3 -0
- package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
- package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
- package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
- package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +2 -2
- package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
- package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
- package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
- package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
- package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
- package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
- package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
- package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
- package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
- package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
- package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
- package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
- package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
- package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
- package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
- package/node_modules/@groove-dev/gui/src/app.css +35 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
- package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
- package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
- package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
- 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/dashboard/token-chart.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
- package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
- package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
- package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
- package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
- package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
- package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
- 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 +1086 -6532
- package/packages/daemon/src/gateways/manager.js +35 -1
- package/packages/daemon/src/index.js +3 -0
- package/packages/daemon/src/journalist.js +23 -13
- package/packages/daemon/src/mlx-server.js +365 -0
- package/packages/daemon/src/model-lab.js +308 -12
- package/packages/daemon/src/pm.js +1 -1
- package/packages/daemon/src/process.js +2 -2
- package/packages/daemon/src/providers/local.js +36 -8
- package/packages/daemon/src/registry.js +21 -5
- package/packages/daemon/src/routes/agents.js +889 -0
- package/packages/daemon/src/routes/coordination.js +318 -0
- package/packages/daemon/src/routes/files.js +751 -0
- package/packages/daemon/src/routes/integrations.js +485 -0
- package/packages/daemon/src/routes/network.js +1784 -0
- package/packages/daemon/src/routes/providers.js +755 -0
- package/packages/daemon/src/routes/schedules.js +110 -0
- package/packages/daemon/src/routes/teams.js +650 -0
- package/packages/daemon/src/scheduler.js +456 -24
- package/packages/daemon/src/teams.js +1 -1
- package/packages/daemon/src/validate.js +38 -1
- package/packages/daemon/templates/mlx-setup.json +12 -0
- package/packages/daemon/templates/tgi-setup.json +1 -1
- package/packages/daemon/templates/vllm-setup.json +1 -1
- package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
- package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
- package/packages/gui/src/app.css +35 -0
- package/packages/gui/src/components/agents/agent-config.jsx +1 -128
- package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
- package/packages/gui/src/components/agents/agent-node.jsx +8 -13
- package/packages/gui/src/components/agents/code-review.jsx +159 -122
- package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/packages/gui/src/components/automations/automation-card.jsx +274 -0
- package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
- 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/dashboard/token-chart.jsx +4 -4
- package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
- package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
- package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
- package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/packages/gui/src/components/network/network-health.jsx +2 -2
- package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/packages/gui/src/components/ui/sheet.jsx +5 -2
- package/packages/gui/src/lib/cron.js +64 -0
- package/packages/gui/src/lib/status.js +24 -24
- package/packages/gui/src/lib/theme-hex.js +1 -0
- package/packages/gui/src/stores/groove.js +34 -3144
- package/packages/gui/src/stores/helpers.js +10 -0
- package/packages/gui/src/stores/slices/agents-slice.js +452 -0
- package/packages/gui/src/stores/slices/automations-slice.js +96 -0
- package/packages/gui/src/stores/slices/chat-slice.js +227 -0
- package/packages/gui/src/stores/slices/editor-slice.js +285 -0
- package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/packages/gui/src/stores/slices/network-slice.js +361 -0
- package/packages/gui/src/stores/slices/preview-slice.js +109 -0
- package/packages/gui/src/stores/slices/providers-slice.js +897 -0
- package/packages/gui/src/stores/slices/teams-slice.js +413 -0
- package/packages/gui/src/stores/slices/ui-slice.js +98 -0
- package/packages/gui/src/views/agents.jsx +5 -5
- package/packages/gui/src/views/dashboard.jsx +12 -13
- package/packages/gui/src/views/marketplace.jsx +191 -3
- package/packages/gui/src/views/model-lab.jsx +17 -6
- package/packages/gui/src/views/models.jsx +410 -509
- package/packages/gui/src/views/network.jsx +3 -3
- package/packages/gui/src/views/settings.jsx +81 -94
- package/packages/gui/src/views/teams.jsx +40 -483
- package/SECURITY_SWEEP.md +0 -228
- package/TRAINING_DATA_v4.md +0 -6
- package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
- package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
- package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
- package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
- package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
- package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
- package/packages/gui/src/views/preview.jsx +0 -6
- package/packages/gui/src/views/subscription-panel.jsx +0 -327
- package/test.py +0 -571
|
@@ -1,502 +1,59 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
3
|
import { useGrooveStore } from '../stores/groove';
|
|
4
|
-
import { Tabs, TabsList, TabsTrigger, TabsContent } from '../components/ui/tabs';
|
|
5
4
|
import { Button } from '../components/ui/button';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { useToast } from '../lib/hooks/use-toast';
|
|
10
|
-
import { fmtNum, fmtDollar, timeAgo, fmtUptime } from '../lib/format';
|
|
11
|
-
import { cn } from '../lib/cn';
|
|
12
|
-
import { Dialog, DialogContent } from '../components/ui/dialog';
|
|
13
|
-
import {
|
|
14
|
-
Clock, CheckCircle, XCircle, AlertTriangle, ShieldCheck, ShieldX,
|
|
15
|
-
Users, Folder, Cpu, Trash2, Play, Pause, LayoutDashboard, ListChecks, Calendar,
|
|
16
|
-
Archive, RotateCcw, ChevronRight, ArrowUpCircle,
|
|
17
|
-
} from 'lucide-react';
|
|
18
|
-
import { TeamRemovalDialog, PurgeConfirmDialog } from '../components/teams/team-removal-dialog';
|
|
5
|
+
import { AutomationCard } from '../components/automations/automation-card';
|
|
6
|
+
import { AutomationWizard } from '../components/automations/automation-wizard';
|
|
7
|
+
import { Plus, Calendar } from 'lucide-react';
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const deleteTeamPermanently = useGrooveStore((s) => s.deleteTeamPermanently);
|
|
27
|
-
const addToast = useGrooveStore((s) => s.addToast);
|
|
28
|
-
const archivedTeams = useGrooveStore((s) => s.archivedTeams);
|
|
29
|
-
const fetchArchivedTeams = useGrooveStore((s) => s.fetchArchivedTeams);
|
|
30
|
-
const restoreTeam = useGrooveStore((s) => s.restoreTeam);
|
|
31
|
-
const purgeTeam = useGrooveStore((s) => s.purgeTeam);
|
|
32
|
-
const promoteTeam = useGrooveStore((s) => s.promoteTeam);
|
|
33
|
-
|
|
34
|
-
const [archiveConfirm, setArchiveConfirm] = useState(null);
|
|
35
|
-
const [purgeConfirm, setPurgeConfirm] = useState(null);
|
|
36
|
-
const [promoteConfirm, setPromoteConfirm] = useState(null);
|
|
37
|
-
const [archivedOpen, setArchivedOpen] = useState(false);
|
|
38
|
-
|
|
39
|
-
useEffect(() => { fetchArchivedTeams(); }, []);
|
|
40
|
-
|
|
41
|
-
if (teams.length === 0 && archivedTeams.length === 0) {
|
|
42
|
-
return (
|
|
43
|
-
<div className="flex-1 flex items-center justify-center">
|
|
44
|
-
<div className="text-center space-y-2">
|
|
45
|
-
<Users size={28} className="mx-auto text-text-4" />
|
|
46
|
-
<p className="text-xs font-sans text-text-3">No teams yet</p>
|
|
47
|
-
<p className="text-2xs font-sans text-text-4">Teams are created when you spawn agents or launch a planner</p>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className="flex-1 overflow-y-auto">
|
|
55
|
-
<div className="p-4 space-y-3">
|
|
56
|
-
{teams.map((team) => {
|
|
57
|
-
const teamAgents = agents.filter((a) => a.teamId === team.id);
|
|
58
|
-
const running = teamAgents.filter((a) => a.status === 'running' || a.status === 'starting');
|
|
59
|
-
const completed = teamAgents.filter((a) => a.status === 'completed');
|
|
60
|
-
const crashed = teamAgents.filter((a) => a.status === 'crashed');
|
|
61
|
-
const totalTokens = teamAgents.reduce((s, a) => s + (a.tokensUsed || 0), 0);
|
|
62
|
-
const totalCost = teamAgents.reduce((s, a) => s + (a.costUsd || 0), 0);
|
|
63
|
-
const isActive = team.id === activeTeamId;
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div
|
|
67
|
-
key={team.id}
|
|
68
|
-
className={cn(
|
|
69
|
-
'rounded-md border bg-surface-1 overflow-hidden transition-colors',
|
|
70
|
-
isActive ? 'border-accent/30' : 'border-border-subtle',
|
|
71
|
-
)}
|
|
72
|
-
>
|
|
73
|
-
{/* Header */}
|
|
74
|
-
<div className="px-4 py-3 flex items-center gap-3">
|
|
75
|
-
<div className="flex-1 min-w-0">
|
|
76
|
-
<div className="flex items-center gap-2">
|
|
77
|
-
<span className="text-sm font-semibold text-text-0 font-sans">{team.name}</span>
|
|
78
|
-
{isActive && <Badge variant="accent" className="text-2xs">Active</Badge>}
|
|
79
|
-
<Badge variant={team.mode === 'production' ? 'success' : 'default'} className="text-2xs">
|
|
80
|
-
{team.mode === 'production' ? 'Production' : 'Sandbox'}
|
|
81
|
-
</Badge>
|
|
82
|
-
</div>
|
|
83
|
-
{team.workingDir && (
|
|
84
|
-
<div className="flex items-center gap-1 mt-0.5">
|
|
85
|
-
<Folder size={10} className="text-text-4" />
|
|
86
|
-
<span className="text-2xs font-mono text-text-3 truncate">{team.workingDir}</span>
|
|
87
|
-
</div>
|
|
88
|
-
)}
|
|
89
|
-
</div>
|
|
90
|
-
{team.mode !== 'production' && (
|
|
91
|
-
<button
|
|
92
|
-
onClick={() => setPromoteConfirm(team)}
|
|
93
|
-
className="p-1.5 text-text-4 hover:text-success rounded transition-colors cursor-pointer"
|
|
94
|
-
title="Promote to production"
|
|
95
|
-
>
|
|
96
|
-
<ArrowUpCircle size={13} />
|
|
97
|
-
</button>
|
|
98
|
-
)}
|
|
99
|
-
<button
|
|
100
|
-
onClick={() => {
|
|
101
|
-
if (teamAgents.some((a) => a.status === 'running' || a.status === 'starting')) {
|
|
102
|
-
addToast('error', 'Stop running agents first');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
setArchiveConfirm(team);
|
|
106
|
-
}}
|
|
107
|
-
className="p-1.5 text-text-4 hover:text-danger rounded transition-colors cursor-pointer"
|
|
108
|
-
title="Archive team"
|
|
109
|
-
>
|
|
110
|
-
<Trash2 size={13} />
|
|
111
|
-
</button>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
{/* Stats row */}
|
|
115
|
-
<div className="px-4 py-2.5 border-t border-border-subtle bg-surface-0 flex items-center gap-4">
|
|
116
|
-
<Stat label="Agents" value={teamAgents.length} />
|
|
117
|
-
<Stat label="Running" value={running.length} color={running.length > 0 ? 'text-success' : undefined} />
|
|
118
|
-
<Stat label="Done" value={completed.length} />
|
|
119
|
-
<Stat label="Crashed" value={crashed.length} color={crashed.length > 0 ? 'text-danger' : undefined} />
|
|
120
|
-
<div className="flex-1" />
|
|
121
|
-
<Stat label="Tokens" value={fmtNum(totalTokens)} />
|
|
122
|
-
{totalCost > 0 && <Stat label="Cost" value={fmtDollar(totalCost)} />}
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Agent list */}
|
|
126
|
-
{teamAgents.length > 0 && (
|
|
127
|
-
<div className="border-t border-border-subtle">
|
|
128
|
-
{teamAgents.map((a) => (
|
|
129
|
-
<div key={a.id} className="flex items-center gap-2 px-4 py-1.5 border-b border-border-subtle last:border-b-0">
|
|
130
|
-
<StatusDot status={a.status} size="sm" />
|
|
131
|
-
<span className="text-xs font-semibold text-text-0 font-sans truncate">{a.name}</span>
|
|
132
|
-
<span className="text-2xs font-mono text-text-3 uppercase">{a.role}</span>
|
|
133
|
-
<div className="flex-1" />
|
|
134
|
-
<span className="text-2xs font-mono text-text-2 tabular-nums">{fmtNum(a.tokensUsed || 0)}</span>
|
|
135
|
-
</div>
|
|
136
|
-
))}
|
|
137
|
-
</div>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
})}
|
|
142
|
-
</div>
|
|
143
|
-
|
|
144
|
-
{/* Archived Teams */}
|
|
145
|
-
{archivedTeams.length > 0 && (
|
|
146
|
-
<div className="border-t border-border-subtle">
|
|
147
|
-
<button
|
|
148
|
-
onClick={() => setArchivedOpen(!archivedOpen)}
|
|
149
|
-
className="w-full flex items-center gap-2 px-5 py-3 text-left cursor-pointer hover:bg-surface-5/30 transition-colors"
|
|
150
|
-
>
|
|
151
|
-
<ChevronRight
|
|
152
|
-
size={12}
|
|
153
|
-
className={cn('text-text-4 transition-transform duration-200', archivedOpen && 'rotate-90')}
|
|
154
|
-
/>
|
|
155
|
-
<Archive size={13} className="text-text-3" />
|
|
156
|
-
<span className="text-xs font-semibold text-text-2 font-sans uppercase tracking-wider flex-1">
|
|
157
|
-
Archived Teams
|
|
158
|
-
</span>
|
|
159
|
-
<span className="text-2xs font-mono text-text-4 bg-surface-4 px-1.5 py-0.5 rounded">
|
|
160
|
-
{archivedTeams.length}
|
|
161
|
-
</span>
|
|
162
|
-
</button>
|
|
163
|
-
{archivedOpen && (
|
|
164
|
-
<div className="px-4 pb-4 space-y-2">
|
|
165
|
-
{archivedTeams.map((at) => (
|
|
166
|
-
<div key={at.id} className="flex items-center gap-3 px-3 py-2.5 rounded-md bg-surface-0 border border-border-subtle">
|
|
167
|
-
<Archive size={13} className="text-text-4 flex-shrink-0" />
|
|
168
|
-
<div className="flex-1 min-w-0">
|
|
169
|
-
<span className="text-xs font-semibold text-text-1 font-sans">{at.originalName || at.name}</span>
|
|
170
|
-
{(at.deletedAt || at.archivedAt) && (
|
|
171
|
-
<div className="text-2xs text-text-4 font-mono mt-0.5">Archived {timeAgo(at.deletedAt || at.archivedAt)}</div>
|
|
172
|
-
)}
|
|
173
|
-
</div>
|
|
174
|
-
<button
|
|
175
|
-
onClick={() => restoreTeam(at.id)}
|
|
176
|
-
className="p-1.5 text-text-3 hover:text-accent rounded transition-colors cursor-pointer"
|
|
177
|
-
title="Restore team"
|
|
178
|
-
>
|
|
179
|
-
<RotateCcw size={13} />
|
|
180
|
-
</button>
|
|
181
|
-
<button
|
|
182
|
-
onClick={() => setPurgeConfirm(at)}
|
|
183
|
-
className="p-1.5 text-text-4 hover:text-danger rounded transition-colors cursor-pointer"
|
|
184
|
-
title="Permanently delete"
|
|
185
|
-
>
|
|
186
|
-
<Trash2 size={13} />
|
|
187
|
-
</button>
|
|
188
|
-
</div>
|
|
189
|
-
))}
|
|
190
|
-
</div>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
)}
|
|
194
|
-
|
|
195
|
-
<TeamRemovalDialog
|
|
196
|
-
team={archiveConfirm}
|
|
197
|
-
open={!!archiveConfirm}
|
|
198
|
-
onOpenChange={(open) => !open && setArchiveConfirm(null)}
|
|
199
|
-
onArchive={archiveTeam}
|
|
200
|
-
onDeletePermanently={deleteTeamPermanently}
|
|
201
|
-
onPromote={promoteTeam}
|
|
202
|
-
mode={archiveConfirm?.mode || 'sandbox'}
|
|
203
|
-
/>
|
|
204
|
-
|
|
205
|
-
<PurgeConfirmDialog
|
|
206
|
-
team={purgeConfirm}
|
|
207
|
-
open={!!purgeConfirm}
|
|
208
|
-
onOpenChange={(open) => !open && setPurgeConfirm(null)}
|
|
209
|
-
onPurge={purgeTeam}
|
|
210
|
-
/>
|
|
211
|
-
|
|
212
|
-
<PromoteConfirmDialog
|
|
213
|
-
team={promoteConfirm}
|
|
214
|
-
open={!!promoteConfirm}
|
|
215
|
-
onOpenChange={(open) => !open && setPromoteConfirm(null)}
|
|
216
|
-
onPromote={promoteTeam}
|
|
217
|
-
/>
|
|
218
|
-
</div>
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function PromoteConfirmDialog({ team, open, onOpenChange, onPromote }) {
|
|
223
|
-
const [promoting, setPromoting] = useState(false);
|
|
224
|
-
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
if (!open) setPromoting(false);
|
|
227
|
-
}, [open]);
|
|
228
|
-
|
|
229
|
-
async function handleConfirm() {
|
|
230
|
-
setPromoting(true);
|
|
231
|
-
try {
|
|
232
|
-
await onPromote(team?.id);
|
|
233
|
-
onOpenChange(false);
|
|
234
|
-
} catch {
|
|
235
|
-
setPromoting(false);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return (
|
|
240
|
-
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
241
|
-
<DialogContent title="Promote to Production" description="Promote this team to production mode">
|
|
242
|
-
<div className="px-5 py-4 space-y-3">
|
|
243
|
-
<p className="text-sm text-text-1 font-sans">
|
|
244
|
-
Promote <span className="font-semibold text-text-0">{team?.name}</span> to production?
|
|
245
|
-
</p>
|
|
246
|
-
<p className="text-xs text-text-3 font-sans">
|
|
247
|
-
This will move files from the team directory into the project directory.
|
|
248
|
-
The team will be removed but your work stays in the project permanently.
|
|
249
|
-
</p>
|
|
250
|
-
</div>
|
|
251
|
-
<div className="px-5 py-3 border-t border-border-subtle flex justify-end gap-2">
|
|
252
|
-
<Button variant="ghost" size="sm" onClick={() => onOpenChange(false)}>Cancel</Button>
|
|
253
|
-
<Button variant="primary" size="sm" disabled={promoting} onClick={handleConfirm} className="gap-1.5">
|
|
254
|
-
<ArrowUpCircle size={12} />
|
|
255
|
-
{promoting ? 'Promoting...' : 'Promote'}
|
|
256
|
-
</Button>
|
|
257
|
-
</div>
|
|
258
|
-
</DialogContent>
|
|
259
|
-
</Dialog>
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function Stat({ label, value, color }) {
|
|
264
|
-
return (
|
|
265
|
-
<div className="text-center">
|
|
266
|
-
<div className={cn('text-xs font-mono tabular-nums', color || 'text-text-1')}>{value}</div>
|
|
267
|
-
<div className="text-2xs font-mono text-text-4 uppercase tracking-wider">{label}</div>
|
|
268
|
-
</div>
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ── Approvals ─────────────────────────────────────────────────
|
|
273
|
-
function PendingApprovals() {
|
|
274
|
-
const pending = useGrooveStore((s) => s.pendingApprovals);
|
|
275
|
-
const approveRequest = useGrooveStore((s) => s.approveRequest);
|
|
276
|
-
const rejectRequest = useGrooveStore((s) => s.rejectRequest);
|
|
277
|
-
|
|
278
|
-
if (pending.length === 0) return null;
|
|
279
|
-
|
|
280
|
-
return (
|
|
281
|
-
<div className="px-4 pt-4 space-y-2">
|
|
282
|
-
<div className="flex items-center gap-2 mb-1">
|
|
283
|
-
<AlertTriangle size={12} className="text-warning" />
|
|
284
|
-
<span className="text-2xs font-mono text-warning uppercase tracking-wider">Pending ({pending.length})</span>
|
|
285
|
-
</div>
|
|
286
|
-
{pending.map((item) => (
|
|
287
|
-
<div key={item.id} className="flex items-center gap-3 px-3 py-2 rounded-md bg-warning/5 border border-warning/20">
|
|
288
|
-
<div className="flex-1 min-w-0">
|
|
289
|
-
<div className="text-xs text-text-0 font-sans font-medium truncate">
|
|
290
|
-
{item.agentName}: {item.action?.description || item.action?.type || 'action'}
|
|
291
|
-
</div>
|
|
292
|
-
{item.action?.filePath && <div className="text-2xs font-mono text-text-3 truncate mt-0.5">{item.action.filePath}</div>}
|
|
293
|
-
<div className="text-2xs text-text-4 font-mono mt-0.5">{timeAgo(item.requestedAt)}</div>
|
|
294
|
-
</div>
|
|
295
|
-
<div className="flex gap-1.5 flex-shrink-0">
|
|
296
|
-
<Button variant="primary" size="sm" onClick={() => approveRequest(item.id)} className="h-7 px-2.5 gap-1 text-2xs">
|
|
297
|
-
<ShieldCheck size={10} /> Approve
|
|
298
|
-
</Button>
|
|
299
|
-
<Button variant="danger" size="sm" onClick={() => rejectRequest(item.id)} className="h-7 px-2.5 gap-1 text-2xs">
|
|
300
|
-
<ShieldX size={10} /> Reject
|
|
301
|
-
</Button>
|
|
302
|
-
</div>
|
|
303
|
-
</div>
|
|
304
|
-
))}
|
|
305
|
-
</div>
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function ApprovalsTab() {
|
|
310
|
-
const resolved = useGrooveStore((s) => s.resolvedApprovals);
|
|
311
|
-
const [pmHistory, setPmHistory] = useState([]);
|
|
312
|
-
const [loading, setLoading] = useState(true);
|
|
9
|
+
export default function TeamsView() {
|
|
10
|
+
const automations = useGrooveStore((s) => s.automations);
|
|
11
|
+
const fetchAutomations = useGrooveStore((s) => s.fetchAutomations);
|
|
12
|
+
const fetchGateways = useGrooveStore((s) => s.fetchGateways);
|
|
13
|
+
const fetchInstalledIntegrations = useGrooveStore((s) => s.fetchInstalledIntegrations);
|
|
14
|
+
const openWizard = useGrooveStore((s) => s.openAutomationWizard);
|
|
313
15
|
|
|
314
16
|
useEffect(() => {
|
|
315
|
-
|
|
316
|
-
|
|
17
|
+
fetchAutomations();
|
|
18
|
+
fetchGateways();
|
|
19
|
+
fetchInstalledIntegrations();
|
|
20
|
+
const interval = setInterval(fetchAutomations, 10000);
|
|
317
21
|
return () => clearInterval(interval);
|
|
318
22
|
}, []);
|
|
319
23
|
|
|
320
|
-
async function fetchHistory() {
|
|
321
|
-
try {
|
|
322
|
-
const data = await api.get('/pm/history');
|
|
323
|
-
setPmHistory(data.history || data || []);
|
|
324
|
-
} catch { /* ignore */ }
|
|
325
|
-
setLoading(false);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const seen = new Set();
|
|
329
|
-
const allHistory = [...resolved, ...pmHistory].filter((item) => {
|
|
330
|
-
const key = item.id || `${item.agentName}-${item.timestamp}`;
|
|
331
|
-
if (seen.has(key)) return false;
|
|
332
|
-
seen.add(key);
|
|
333
|
-
return true;
|
|
334
|
-
});
|
|
335
|
-
|
|
336
24
|
return (
|
|
337
|
-
<div className="flex-
|
|
338
|
-
|
|
339
|
-
<div className="
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
<div className="text-center py-12">
|
|
345
|
-
<CheckCircle size={24} className="mx-auto mb-2 text-text-4" />
|
|
346
|
-
<p className="text-xs font-sans text-text-3">No approval history</p>
|
|
347
|
-
<p className="text-2xs text-text-4 font-sans mt-1">Approvals appear when agents use Auto permission mode</p>
|
|
348
|
-
</div>
|
|
349
|
-
)}
|
|
350
|
-
{allHistory.map((item, i) => {
|
|
351
|
-
const approved = item.status === 'approved' || item.verdict === 'approved';
|
|
352
|
-
return (
|
|
353
|
-
<div key={item.id || i} className="flex items-center gap-2.5 px-3 py-2 rounded-md bg-surface-0 border border-border-subtle">
|
|
354
|
-
{approved ? (
|
|
355
|
-
<CheckCircle size={12} className="text-success flex-shrink-0" />
|
|
356
|
-
) : (
|
|
357
|
-
<XCircle size={12} className="text-danger flex-shrink-0" />
|
|
358
|
-
)}
|
|
359
|
-
<div className="flex-1 min-w-0">
|
|
360
|
-
<div className="text-xs text-text-1 font-sans truncate">
|
|
361
|
-
<span className="font-medium text-text-0">{item.agentName}</span>
|
|
362
|
-
<span className="text-text-3 mx-1">·</span>
|
|
363
|
-
<span>{item.action?.description || item.action || 'action'}</span>
|
|
364
|
-
</div>
|
|
365
|
-
{item.reason && <div className="text-2xs text-text-3 font-sans truncate mt-0.5">{item.reason}</div>}
|
|
366
|
-
</div>
|
|
367
|
-
<span className="text-2xs font-mono text-text-4 flex-shrink-0">
|
|
368
|
-
{timeAgo(item.resolvedAt || item.timestamp)}
|
|
369
|
-
</span>
|
|
370
|
-
</div>
|
|
371
|
-
);
|
|
372
|
-
})}
|
|
25
|
+
<div className="flex flex-col h-full">
|
|
26
|
+
{/* Header */}
|
|
27
|
+
<div className="flex items-center justify-between px-5 py-4 border-b border-border-subtle bg-surface-1">
|
|
28
|
+
<h2 className="text-sm font-semibold text-text-0 font-sans uppercase tracking-wide">Automations</h2>
|
|
29
|
+
<Button variant="primary" size="sm" onClick={openWizard} className="gap-1.5">
|
|
30
|
+
<Plus size={12} /> New Automation
|
|
31
|
+
</Button>
|
|
373
32
|
</div>
|
|
374
|
-
</div>
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ── Schedules ─────────────────────────────────────────────────
|
|
379
|
-
function SchedulesTab() {
|
|
380
|
-
const [schedules, setSchedules] = useState([]);
|
|
381
|
-
const [loading, setLoading] = useState(true);
|
|
382
|
-
const toast = useToast();
|
|
383
33
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
} catch { /* ignore */ }
|
|
395
|
-
setLoading(false);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async function toggleSchedule(id, enabled) {
|
|
399
|
-
try {
|
|
400
|
-
await api.post(`/schedules/${id}/${enabled ? 'disable' : 'enable'}`);
|
|
401
|
-
fetchSchedules();
|
|
402
|
-
} catch (err) {
|
|
403
|
-
toast.error('Failed to toggle schedule', err.message);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return (
|
|
408
|
-
<div className="flex-1 overflow-y-auto">
|
|
409
|
-
<div className="p-4 space-y-2">
|
|
410
|
-
{loading && schedules.length === 0 && (
|
|
411
|
-
<div className="text-center py-12 text-text-4 font-mono text-xs">Loading...</div>
|
|
412
|
-
)}
|
|
413
|
-
{!loading && schedules.length === 0 && (
|
|
414
|
-
<div className="text-center py-12">
|
|
415
|
-
<Calendar size={24} className="mx-auto mb-2 text-text-4" />
|
|
416
|
-
<p className="text-xs font-sans text-text-3">No schedules configured</p>
|
|
417
|
-
<p className="text-2xs text-text-4 font-sans mt-1">Use the CLI to create agent schedules</p>
|
|
418
|
-
</div>
|
|
419
|
-
)}
|
|
420
|
-
{schedules.map((s) => (
|
|
421
|
-
<div key={s.id} className="rounded-md border border-border-subtle bg-surface-0 overflow-hidden">
|
|
422
|
-
<div className="flex items-center gap-3 px-4 py-3">
|
|
423
|
-
<div className="flex-1 min-w-0">
|
|
424
|
-
<div className="flex items-center gap-2">
|
|
425
|
-
<span className="text-xs font-semibold text-text-0 font-sans">{s.name}</span>
|
|
426
|
-
<Badge variant={s.enabled ? 'success' : 'default'} className="text-2xs">
|
|
427
|
-
{s.enabled ? 'Active' : 'Paused'}
|
|
428
|
-
</Badge>
|
|
429
|
-
</div>
|
|
430
|
-
<div className="flex items-center gap-2 mt-1">
|
|
431
|
-
<span className="text-2xs font-mono text-text-2">{s.cron}</span>
|
|
432
|
-
<span className="text-2xs text-text-4">·</span>
|
|
433
|
-
<span className="text-2xs font-mono text-text-3 uppercase">{s.role}</span>
|
|
434
|
-
{s.teamId && (
|
|
435
|
-
<>
|
|
436
|
-
<span className="text-2xs text-text-4">·</span>
|
|
437
|
-
<span className="text-2xs font-sans text-text-3">{s.teamName || s.teamId}</span>
|
|
438
|
-
</>
|
|
439
|
-
)}
|
|
440
|
-
</div>
|
|
441
|
-
{s.prompt && (
|
|
442
|
-
<div className="text-2xs font-sans text-text-4 mt-1 truncate">{s.prompt}</div>
|
|
443
|
-
)}
|
|
444
|
-
</div>
|
|
445
|
-
<Button
|
|
446
|
-
variant="secondary"
|
|
447
|
-
size="sm"
|
|
448
|
-
onClick={() => toggleSchedule(s.id, s.enabled)}
|
|
449
|
-
className="h-7 px-2.5 gap-1 text-2xs"
|
|
450
|
-
>
|
|
451
|
-
{s.enabled ? <><Pause size={10} /> Pause</> : <><Play size={10} /> Enable</>}
|
|
34
|
+
{/* Content */}
|
|
35
|
+
<div className="flex-1 overflow-y-auto">
|
|
36
|
+
{automations.length === 0 ? (
|
|
37
|
+
<div className="flex-1 flex items-center justify-center h-full">
|
|
38
|
+
<div className="text-center space-y-3 py-20">
|
|
39
|
+
<Calendar size={28} className="mx-auto text-text-4" />
|
|
40
|
+
<p className="text-xs font-sans text-text-3">No automations yet</p>
|
|
41
|
+
<p className="text-2xs font-sans text-text-4">Schedule agent teams to automate your workflows</p>
|
|
42
|
+
<Button variant="primary" size="sm" onClick={openWizard} className="gap-1.5 mt-2">
|
|
43
|
+
<Plus size={12} /> Create Automation
|
|
452
44
|
</Button>
|
|
453
45
|
</div>
|
|
454
|
-
{s.lastRunAt && (
|
|
455
|
-
<div className="px-4 py-1.5 border-t border-border-subtle bg-surface-1 text-2xs font-mono text-text-4">
|
|
456
|
-
Last run: {timeAgo(s.lastRunAt)}
|
|
457
|
-
{s.nextRunAt && <span className="ml-3">Next: {timeAgo(s.nextRunAt)}</span>}
|
|
458
|
-
</div>
|
|
459
|
-
)}
|
|
460
46
|
</div>
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
export default function TeamsView() {
|
|
469
|
-
return (
|
|
470
|
-
<Tabs defaultValue="dashboard" className="flex flex-col h-full">
|
|
471
|
-
<div className="px-4 pt-3 bg-surface-1 border-b border-border">
|
|
472
|
-
<div className="flex items-center gap-4 mb-0">
|
|
473
|
-
<h2 className="text-xs font-semibold text-text-0 font-sans tracking-wide uppercase">Management</h2>
|
|
474
|
-
</div>
|
|
475
|
-
<TabsList className="border-b-0">
|
|
476
|
-
<TabsTrigger value="dashboard" className="inline-flex items-center gap-1.5">
|
|
477
|
-
<LayoutDashboard size={12} />
|
|
478
|
-
Teams
|
|
479
|
-
</TabsTrigger>
|
|
480
|
-
<TabsTrigger value="approvals" className="inline-flex items-center gap-1.5">
|
|
481
|
-
<ListChecks size={12} />
|
|
482
|
-
Approvals
|
|
483
|
-
</TabsTrigger>
|
|
484
|
-
<TabsTrigger value="schedules" className="inline-flex items-center gap-1.5">
|
|
485
|
-
<Calendar size={12} />
|
|
486
|
-
Schedules
|
|
487
|
-
</TabsTrigger>
|
|
488
|
-
</TabsList>
|
|
47
|
+
) : (
|
|
48
|
+
<div className="p-4 space-y-3">
|
|
49
|
+
{automations.map((a) => (
|
|
50
|
+
<AutomationCard key={a.id} automation={a} />
|
|
51
|
+
))}
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
489
54
|
</div>
|
|
490
55
|
|
|
491
|
-
<
|
|
492
|
-
|
|
493
|
-
</TabsContent>
|
|
494
|
-
<TabsContent value="approvals" className="flex flex-col min-h-0">
|
|
495
|
-
<ApprovalsTab />
|
|
496
|
-
</TabsContent>
|
|
497
|
-
<TabsContent value="schedules" className="flex flex-col min-h-0">
|
|
498
|
-
<SchedulesTab />
|
|
499
|
-
</TabsContent>
|
|
500
|
-
</Tabs>
|
|
56
|
+
<AutomationWizard />
|
|
57
|
+
</div>
|
|
501
58
|
);
|
|
502
59
|
}
|