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.
Files changed (187) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  4. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  5. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  6. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  7. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  8. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  9. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  10. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  11. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  12. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  13. package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
  14. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  15. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  21. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  22. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  23. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  24. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  25. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  26. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  27. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  28. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  29. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  30. package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  31. package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  32. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  33. package/node_modules/@groove-dev/gui/package.json +1 -1
  34. package/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
  35. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  36. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  37. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  39. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  40. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  41. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  42. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  43. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  44. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  45. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  46. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  47. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  48. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  49. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  50. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  54. package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
  55. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  56. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  57. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
  58. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
  59. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  60. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  61. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  62. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  63. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  64. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  65. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  66. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  67. package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
  68. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  69. package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
  70. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  71. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
  72. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  73. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
  74. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  75. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  76. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  77. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  78. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  79. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  81. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  82. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  83. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  84. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
  85. package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
  86. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  87. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  88. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  89. package/package.json +1 -1
  90. package/packages/cli/package.json +1 -1
  91. package/packages/daemon/package.json +1 -1
  92. package/packages/daemon/src/api.js +1086 -6532
  93. package/packages/daemon/src/gateways/manager.js +35 -1
  94. package/packages/daemon/src/index.js +3 -0
  95. package/packages/daemon/src/journalist.js +23 -13
  96. package/packages/daemon/src/mlx-server.js +365 -0
  97. package/packages/daemon/src/model-lab.js +308 -12
  98. package/packages/daemon/src/pm.js +1 -1
  99. package/packages/daemon/src/process.js +2 -2
  100. package/packages/daemon/src/providers/local.js +36 -8
  101. package/packages/daemon/src/registry.js +21 -5
  102. package/packages/daemon/src/routes/agents.js +889 -0
  103. package/packages/daemon/src/routes/coordination.js +318 -0
  104. package/packages/daemon/src/routes/files.js +751 -0
  105. package/packages/daemon/src/routes/integrations.js +485 -0
  106. package/packages/daemon/src/routes/network.js +1784 -0
  107. package/packages/daemon/src/routes/providers.js +755 -0
  108. package/packages/daemon/src/routes/schedules.js +110 -0
  109. package/packages/daemon/src/routes/teams.js +650 -0
  110. package/packages/daemon/src/scheduler.js +456 -24
  111. package/packages/daemon/src/teams.js +1 -1
  112. package/packages/daemon/src/validate.js +38 -1
  113. package/packages/daemon/templates/mlx-setup.json +12 -0
  114. package/packages/daemon/templates/tgi-setup.json +1 -1
  115. package/packages/daemon/templates/vllm-setup.json +1 -1
  116. package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  117. package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  118. package/packages/gui/dist/index.html +2 -2
  119. package/packages/gui/package.json +1 -1
  120. package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
  121. package/packages/gui/src/app.css +35 -0
  122. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  123. package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
  124. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  125. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  126. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  127. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  128. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  129. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  130. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  131. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  132. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  133. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  134. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  135. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  136. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  137. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  138. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  139. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  140. package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
  141. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  142. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  143. package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
  144. package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
  145. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  146. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  147. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  148. package/packages/gui/src/components/network/network-health.jsx +2 -2
  149. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  150. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  151. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  152. package/packages/gui/src/lib/cron.js +64 -0
  153. package/packages/gui/src/lib/status.js +24 -24
  154. package/packages/gui/src/lib/theme-hex.js +1 -0
  155. package/packages/gui/src/stores/groove.js +34 -3144
  156. package/packages/gui/src/stores/helpers.js +10 -0
  157. package/packages/gui/src/stores/slices/agents-slice.js +452 -0
  158. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  159. package/packages/gui/src/stores/slices/chat-slice.js +227 -0
  160. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  161. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  162. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  163. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  164. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  165. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  166. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  167. package/packages/gui/src/views/agents.jsx +5 -5
  168. package/packages/gui/src/views/dashboard.jsx +12 -13
  169. package/packages/gui/src/views/marketplace.jsx +191 -3
  170. package/packages/gui/src/views/model-lab.jsx +17 -6
  171. package/packages/gui/src/views/models.jsx +410 -509
  172. package/packages/gui/src/views/network.jsx +3 -3
  173. package/packages/gui/src/views/settings.jsx +81 -94
  174. package/packages/gui/src/views/teams.jsx +40 -483
  175. package/SECURITY_SWEEP.md +0 -228
  176. package/TRAINING_DATA_v4.md +0 -6
  177. package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
  178. package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
  179. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
  180. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  181. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  182. package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
  183. package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
  184. package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
  185. package/packages/gui/src/views/preview.jsx +0 -6
  186. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  187. package/test.py +0 -571
@@ -1,502 +1,59 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
- import { useState, useEffect } from 'react';
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 { Badge } from '../components/ui/badge';
7
- import { StatusDot } from '../components/ui/status-dot';
8
- import { api } from '../lib/api';
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
- // ── Team Dashboard ────────────────────────────────────────────
21
- function TeamsDashboard() {
22
- const teams = useGrooveStore((s) => s.teams);
23
- const agents = useGrooveStore((s) => s.agents);
24
- const activeTeamId = useGrooveStore((s) => s.activeTeamId);
25
- const archiveTeam = useGrooveStore((s) => s.archiveTeam);
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
- const interval = setInterval(fetchHistory, 4000);
316
- fetchHistory();
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-1 overflow-y-auto">
338
- <PendingApprovals />
339
- <div className="p-4 space-y-1.5">
340
- {loading && allHistory.length === 0 && (
341
- <div className="text-center py-12 text-text-4 font-mono text-xs">Loading...</div>
342
- )}
343
- {!loading && allHistory.length === 0 && (
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
- useEffect(() => {
385
- const interval = setInterval(fetchSchedules, 10000);
386
- fetchSchedules();
387
- return () => clearInterval(interval);
388
- }, []);
389
-
390
- async function fetchSchedules() {
391
- try {
392
- const data = await api.get('/schedules');
393
- setSchedules(data.schedules || data || []);
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
- </div>
463
- </div>
464
- );
465
- }
466
-
467
- // ── Main View ─────────────────────────────────────────────────
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
- <TabsContent value="dashboard" className="flex flex-col min-h-0">
492
- <TeamsDashboard />
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.27.142",
3
+ "version": "0.27.144",
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)",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.142",
3
+ "version": "0.27.144",
4
4
  "description": "GROOVE CLI — manage AI coding agents from your terminal",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/daemon",
3
- "version": "0.27.142",
3
+ "version": "0.27.144",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",