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
|
@@ -62,10 +62,6 @@ export function SpawnWizard() {
|
|
|
62
62
|
const [model, setModel] = useState('');
|
|
63
63
|
const [prompt, setPrompt] = useState('');
|
|
64
64
|
const [providers, setProviders] = useState([]);
|
|
65
|
-
const [installedSkills, setInstalledSkills] = useState([]);
|
|
66
|
-
const [selectedSkills, setSelectedSkills] = useState([]);
|
|
67
|
-
const [skillModalOpen, setSkillModalOpen] = useState(false);
|
|
68
|
-
const [skillSearch, setSkillSearch] = useState('');
|
|
69
65
|
const [installedIntegrations, setInstalledIntegrations] = useState([]);
|
|
70
66
|
const [selectedIntegrations, setSelectedIntegrations] = useState([]);
|
|
71
67
|
const [integrationModalOpen, setIntegrationModalOpen] = useState(false);
|
|
@@ -82,7 +78,6 @@ export function SpawnWizard() {
|
|
|
82
78
|
const [selectedPeerId, setSelectedPeerId] = useState('');
|
|
83
79
|
const [recommendations, setRecommendations] = useState([]);
|
|
84
80
|
const [preflightDialog, setPreflightDialog] = useState(null);
|
|
85
|
-
const [claudeAuth, setClaudeAuth] = useState(null);
|
|
86
81
|
const [ollamaInstalled, setOllamaInstalled] = useState([]);
|
|
87
82
|
const [ollamaServerRunning, setOllamaServerRunning] = useState(false);
|
|
88
83
|
const federation = useGrooveStore((s) => s.federation);
|
|
@@ -108,9 +103,6 @@ export function SpawnWizard() {
|
|
|
108
103
|
setProvider(best);
|
|
109
104
|
}
|
|
110
105
|
}).catch(() => {});
|
|
111
|
-
api.get('/skills/installed').then((data) => {
|
|
112
|
-
setInstalledSkills(Array.isArray(data) ? data : []);
|
|
113
|
-
}).catch(() => {});
|
|
114
106
|
api.get('/integrations/installed').then((data) => {
|
|
115
107
|
setInstalledIntegrations(Array.isArray(data) ? data : []);
|
|
116
108
|
}).catch(() => {});
|
|
@@ -123,7 +115,6 @@ export function SpawnWizard() {
|
|
|
123
115
|
setRole(''); setCustomRole(''); setName('');
|
|
124
116
|
setProvider(_presetProvider); setModel(_presetModel);
|
|
125
117
|
setPrompt('');
|
|
126
|
-
setSelectedSkills([]);
|
|
127
118
|
setSelectedIntegrations([]);
|
|
128
119
|
setIntegrationApproval('manual');
|
|
129
120
|
setSelectedRepos([]);
|
|
@@ -132,7 +123,6 @@ export function SpawnWizard() {
|
|
|
132
123
|
setShowAdvanced(false);
|
|
133
124
|
setRecommendations([]);
|
|
134
125
|
setPreflightDialog(null);
|
|
135
|
-
setClaudeAuth(null);
|
|
136
126
|
}
|
|
137
127
|
}, [open, fetchProviders]);
|
|
138
128
|
|
|
@@ -150,13 +140,6 @@ export function SpawnWizard() {
|
|
|
150
140
|
}).catch(() => setRecommendations([]));
|
|
151
141
|
}, [selectedRole, open]);
|
|
152
142
|
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
if (!open || provider !== 'claude-code') { setClaudeAuth(null); return; }
|
|
155
|
-
api.get('/providers/claude-code/auth').then((data) => {
|
|
156
|
-
setClaudeAuth(data);
|
|
157
|
-
}).catch(() => setClaudeAuth(null));
|
|
158
|
-
}, [open, provider]);
|
|
159
|
-
|
|
160
143
|
useEffect(() => {
|
|
161
144
|
if (!open || provider !== 'ollama') { setOllamaInstalled([]); return; }
|
|
162
145
|
api.get('/providers/ollama/models').then((data) => {
|
|
@@ -176,7 +159,6 @@ export function SpawnWizard() {
|
|
|
176
159
|
...(provider && { provider }),
|
|
177
160
|
...(model && { model }),
|
|
178
161
|
...(prompt && { prompt }),
|
|
179
|
-
...(selectedSkills.length > 0 && { skills: selectedSkills }),
|
|
180
162
|
...(selectedIntegrations.length > 0 && { integrations: selectedIntegrations }),
|
|
181
163
|
...(selectedIntegrations.length > 0 && { integrationApproval }),
|
|
182
164
|
...(selectedRepos.length > 0 && { repos: selectedRepos }),
|
|
@@ -204,11 +186,9 @@ export function SpawnWizard() {
|
|
|
204
186
|
runSpawn();
|
|
205
187
|
}
|
|
206
188
|
|
|
207
|
-
const claudeNotAuthed = provider === 'claude-code' && claudeAuth && !claudeAuth.authenticated;
|
|
208
|
-
|
|
209
189
|
return (
|
|
210
190
|
<Sheet open={open} onOpenChange={(o) => { if (!o) closeDetail(); }}>
|
|
211
|
-
<SheetContent title="Spawn Agent" width={480}>
|
|
191
|
+
<SheetContent title="Spawn Agent" width={480} onClose={() => closeDetail()}>
|
|
212
192
|
<div className="flex flex-col h-[calc(100%-57px)]">
|
|
213
193
|
{/* Scrollable content */}
|
|
214
194
|
<div className="flex-1 overflow-y-auto px-5 py-4 space-y-6">
|
|
@@ -479,119 +459,6 @@ export function SpawnWizard() {
|
|
|
479
459
|
</div>
|
|
480
460
|
)}
|
|
481
461
|
|
|
482
|
-
{/* Claude Code Auth */}
|
|
483
|
-
{claudeNotAuthed && (
|
|
484
|
-
<div className="rounded-lg border border-warning/30 bg-warning/5 px-4 py-3">
|
|
485
|
-
<div className="flex items-center gap-2 mb-2">
|
|
486
|
-
<AlertTriangle size={13} className="text-warning flex-shrink-0" />
|
|
487
|
-
<span className="text-xs font-semibold text-text-0 font-sans">Claude Code is not signed in</span>
|
|
488
|
-
</div>
|
|
489
|
-
<p className="text-2xs text-text-2 font-sans">
|
|
490
|
-
Open the terminal and run: <code className="font-mono text-accent bg-surface-4 px-1.5 py-0.5 rounded text-2xs">claude</code>
|
|
491
|
-
</p>
|
|
492
|
-
</div>
|
|
493
|
-
)}
|
|
494
|
-
{provider === 'claude-code' && claudeAuth?.authenticated && (
|
|
495
|
-
<div className="flex items-center gap-2 text-2xs text-text-2 font-sans">
|
|
496
|
-
<div className="w-2 h-2 rounded-full bg-success flex-shrink-0" />
|
|
497
|
-
Signed in as {claudeAuth.email || 'Claude user'} ({claudeAuth.subscriptionType || 'subscription'})
|
|
498
|
-
</div>
|
|
499
|
-
)}
|
|
500
|
-
|
|
501
|
-
{/* Skills */}
|
|
502
|
-
<div className="space-y-1.5">
|
|
503
|
-
<label className="text-xs font-medium text-text-2 font-sans">Skills</label>
|
|
504
|
-
<div className="flex flex-wrap items-center gap-1.5">
|
|
505
|
-
{selectedSkills.map((skillId) => {
|
|
506
|
-
const skill = installedSkills.find((s) => s.id === skillId);
|
|
507
|
-
return (
|
|
508
|
-
<span
|
|
509
|
-
key={skillId}
|
|
510
|
-
className="inline-flex items-center gap-1 px-2 py-1 rounded bg-accent/12 text-accent border border-accent/25 text-2xs font-sans"
|
|
511
|
-
>
|
|
512
|
-
<Sparkles size={9} />
|
|
513
|
-
{skill?.name || skillId}
|
|
514
|
-
<button
|
|
515
|
-
onClick={() => setSelectedSkills((prev) => prev.filter((s) => s !== skillId))}
|
|
516
|
-
className="ml-0.5 hover:text-text-0 cursor-pointer"
|
|
517
|
-
>
|
|
518
|
-
<X size={9} />
|
|
519
|
-
</button>
|
|
520
|
-
</span>
|
|
521
|
-
);
|
|
522
|
-
})}
|
|
523
|
-
<button
|
|
524
|
-
onClick={() => { setSkillModalOpen(true); setSkillSearch(''); }}
|
|
525
|
-
className={cn(
|
|
526
|
-
'inline-flex items-center gap-1.5 px-2.5 py-1 rounded text-2xs font-sans transition-colors cursor-pointer',
|
|
527
|
-
'bg-surface-0 text-text-2 border border-border-subtle hover:border-border hover:text-text-0',
|
|
528
|
-
)}
|
|
529
|
-
>
|
|
530
|
-
<Sparkles size={10} />
|
|
531
|
-
{selectedSkills.length > 0 ? 'Add skill' : 'Attach skill'}
|
|
532
|
-
</button>
|
|
533
|
-
</div>
|
|
534
|
-
</div>
|
|
535
|
-
|
|
536
|
-
{/* Skill picker modal */}
|
|
537
|
-
<Dialog open={skillModalOpen} onOpenChange={setSkillModalOpen}>
|
|
538
|
-
<DialogContent title="Select Skill" className="max-w-sm">
|
|
539
|
-
<div className="space-y-3 p-4">
|
|
540
|
-
<div className="relative">
|
|
541
|
-
<Search size={14} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-text-4" />
|
|
542
|
-
<input
|
|
543
|
-
value={skillSearch}
|
|
544
|
-
onChange={(e) => setSkillSearch(e.target.value)}
|
|
545
|
-
placeholder="Search skills..."
|
|
546
|
-
autoFocus
|
|
547
|
-
className="w-full h-8 pl-8 pr-3 text-xs rounded-md bg-surface-0 border border-border text-text-0 font-sans focus:outline-none focus:ring-1 focus:ring-accent"
|
|
548
|
-
/>
|
|
549
|
-
</div>
|
|
550
|
-
<div className="max-h-64 overflow-y-auto space-y-1">
|
|
551
|
-
{installedSkills
|
|
552
|
-
.filter((s) => {
|
|
553
|
-
if (!skillSearch) return true;
|
|
554
|
-
const q = skillSearch.toLowerCase();
|
|
555
|
-
return (s.name || s.id).toLowerCase().includes(q) || (s.description || '').toLowerCase().includes(q);
|
|
556
|
-
})
|
|
557
|
-
.map((skill) => {
|
|
558
|
-
const active = selectedSkills.includes(skill.id);
|
|
559
|
-
return (
|
|
560
|
-
<button
|
|
561
|
-
key={skill.id}
|
|
562
|
-
onClick={() => {
|
|
563
|
-
setSelectedSkills((prev) =>
|
|
564
|
-
active ? prev.filter((s) => s !== skill.id) : [...prev, skill.id]
|
|
565
|
-
);
|
|
566
|
-
}}
|
|
567
|
-
className={cn(
|
|
568
|
-
'w-full flex items-center gap-2.5 px-3 py-2 rounded-md text-left transition-colors cursor-pointer',
|
|
569
|
-
active
|
|
570
|
-
? 'bg-accent/10 border border-accent/25'
|
|
571
|
-
: 'hover:bg-surface-3 border border-transparent',
|
|
572
|
-
)}
|
|
573
|
-
>
|
|
574
|
-
<Sparkles size={12} className={active ? 'text-accent' : 'text-text-3'} />
|
|
575
|
-
<div className="flex-1 min-w-0">
|
|
576
|
-
<div className="text-xs font-semibold text-text-0 font-sans truncate">{skill.name || skill.id}</div>
|
|
577
|
-
{skill.description && (
|
|
578
|
-
<div className="text-2xs text-text-3 font-sans truncate">{skill.description}</div>
|
|
579
|
-
)}
|
|
580
|
-
</div>
|
|
581
|
-
{active && <CheckMark />}
|
|
582
|
-
</button>
|
|
583
|
-
);
|
|
584
|
-
})}
|
|
585
|
-
{installedSkills.length === 0 && (
|
|
586
|
-
<div className="text-center py-6 text-xs text-text-3 font-sans">
|
|
587
|
-
No skills installed. Visit the Marketplace to install skills.
|
|
588
|
-
</div>
|
|
589
|
-
)}
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
</DialogContent>
|
|
593
|
-
</Dialog>
|
|
594
|
-
|
|
595
462
|
{/* Integrations */}
|
|
596
463
|
<div className="space-y-1.5">
|
|
597
464
|
<label className="text-xs font-medium text-text-2 font-sans">Integrations</label>
|
|
@@ -900,7 +767,7 @@ export function SpawnWizard() {
|
|
|
900
767
|
variant="primary"
|
|
901
768
|
size="lg"
|
|
902
769
|
onClick={handleSpawn}
|
|
903
|
-
disabled={!selectedRole || spawning || installedProviders.length === 0
|
|
770
|
+
disabled={!selectedRole || spawning || installedProviders.length === 0}
|
|
904
771
|
className="w-full"
|
|
905
772
|
>
|
|
906
773
|
{spawning ? 'Spawning...' : 'Spawn Agent'}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { Badge } from '../ui/badge';
|
|
5
|
+
import { Button } from '../ui/button';
|
|
6
|
+
import { StatusDot } from '../ui/status-dot';
|
|
7
|
+
import { cronToHuman } from '../../lib/cron';
|
|
8
|
+
import { timeAgo, fmtDollar, fmtUptime } from '../../lib/format';
|
|
9
|
+
import { cn } from '../../lib/cn';
|
|
10
|
+
import {
|
|
11
|
+
Play, Pause, Clock, MoreHorizontal, Copy, Pencil, Trash2,
|
|
12
|
+
FileText, Folder, ExternalLink,
|
|
13
|
+
} from 'lucide-react';
|
|
14
|
+
|
|
15
|
+
export function AutomationCard({ automation }) {
|
|
16
|
+
const toggleAutomation = useGrooveStore((s) => s.toggleAutomation);
|
|
17
|
+
const deleteAutomation = useGrooveStore((s) => s.deleteAutomation);
|
|
18
|
+
const duplicateAutomation = useGrooveStore((s) => s.duplicateAutomation);
|
|
19
|
+
const runAutomation = useGrooveStore((s) => s.runAutomation);
|
|
20
|
+
const setEditingAutomation = useGrooveStore((s) => s.setEditingAutomation);
|
|
21
|
+
const openWizard = useGrooveStore((s) => s.openAutomationWizard);
|
|
22
|
+
const openDetail = useGrooveStore((s) => s.openDetail);
|
|
23
|
+
const agents = useGrooveStore((s) => s.agents);
|
|
24
|
+
|
|
25
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
26
|
+
const menuRef = useRef(null);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!menuOpen) return;
|
|
30
|
+
function handleClick(e) {
|
|
31
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false);
|
|
32
|
+
}
|
|
33
|
+
document.addEventListener('mousedown', handleClick);
|
|
34
|
+
return () => document.removeEventListener('mousedown', handleClick);
|
|
35
|
+
}, [menuOpen]);
|
|
36
|
+
|
|
37
|
+
const a = automation;
|
|
38
|
+
const roles = a.teamConfig || (a.agentConfig ? [a.agentConfig] : (a.role ? [{ role: a.role }] : []));
|
|
39
|
+
const instructions = a.instructionSource?.type === 'inline'
|
|
40
|
+
? a.instructionSource.content
|
|
41
|
+
: a.instructionSource?.type === 'file'
|
|
42
|
+
? null
|
|
43
|
+
: a.prompt || null;
|
|
44
|
+
const filePath = a.instructionSource?.type === 'file' ? a.instructionSource.filePath : null;
|
|
45
|
+
const gatewayIds = a.outputConfig?.gatewayIds || [];
|
|
46
|
+
const lastStatus = a.lastRunStatus || (a.lastRunAt ? 'completed' : null);
|
|
47
|
+
|
|
48
|
+
const activeAgents = (a.activeAgentIds || [])
|
|
49
|
+
.map((id) => agents.find((ag) => ag.id === id))
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
|
|
52
|
+
const lastRunAgentIds = a.lastRun?.agentId
|
|
53
|
+
? a.lastRun.agentId.split(',').filter(Boolean)
|
|
54
|
+
: [];
|
|
55
|
+
const lastRunAgents = !a.isRunning
|
|
56
|
+
? lastRunAgentIds.map((id) => agents.find((ag) => ag.id === id)).filter(Boolean)
|
|
57
|
+
: [];
|
|
58
|
+
|
|
59
|
+
function openAgentPanel(agentId) {
|
|
60
|
+
openDetail({ type: 'agent', agentId });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={cn(
|
|
65
|
+
'rounded-md border bg-surface-1 overflow-hidden transition-colors',
|
|
66
|
+
a.enabled ? 'border-border-subtle' : 'border-border-subtle/50 opacity-75',
|
|
67
|
+
)}>
|
|
68
|
+
{/* Top row */}
|
|
69
|
+
<div className="px-4 py-3 flex items-start gap-3">
|
|
70
|
+
<div className="flex-1 min-w-0">
|
|
71
|
+
<div className="flex items-center gap-2">
|
|
72
|
+
<span className="text-sm font-semibold text-text-0 font-sans truncate">{a.name}</span>
|
|
73
|
+
<Badge variant={a.enabled ? 'success' : 'default'} className="text-2xs flex-shrink-0">
|
|
74
|
+
{a.enabled ? 'Active' : 'Paused'}
|
|
75
|
+
</Badge>
|
|
76
|
+
</div>
|
|
77
|
+
{a.description && (
|
|
78
|
+
<p className="text-2xs text-text-3 font-sans truncate mt-0.5">{a.description}</p>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div className="flex items-center gap-1.5 flex-shrink-0">
|
|
83
|
+
<button
|
|
84
|
+
onClick={() => toggleAutomation(a.id, a.enabled)}
|
|
85
|
+
className={cn(
|
|
86
|
+
'p-1.5 rounded transition-colors cursor-pointer',
|
|
87
|
+
a.enabled ? 'text-success hover:text-success/80' : 'text-text-4 hover:text-text-2',
|
|
88
|
+
)}
|
|
89
|
+
title={a.enabled ? 'Pause automation' : 'Enable automation'}
|
|
90
|
+
>
|
|
91
|
+
{a.enabled ? <Pause size={13} /> : <Play size={13} />}
|
|
92
|
+
</button>
|
|
93
|
+
|
|
94
|
+
<div ref={menuRef} className="relative">
|
|
95
|
+
<button
|
|
96
|
+
onClick={() => setMenuOpen(!menuOpen)}
|
|
97
|
+
className="p-1.5 rounded text-text-4 hover:text-text-2 transition-colors cursor-pointer"
|
|
98
|
+
>
|
|
99
|
+
<MoreHorizontal size={13} />
|
|
100
|
+
</button>
|
|
101
|
+
{menuOpen && (
|
|
102
|
+
<div className="absolute right-0 top-full mt-1 z-50 min-w-[150px] bg-surface-2 border border-border rounded-md shadow-lg py-1">
|
|
103
|
+
<button
|
|
104
|
+
onClick={() => { setEditingAutomation(a.id); openWizard(); setMenuOpen(false); }}
|
|
105
|
+
className="w-full text-left px-3 py-1.5 text-xs font-sans text-text-1 hover:bg-surface-5 transition-colors cursor-pointer flex items-center gap-2"
|
|
106
|
+
>
|
|
107
|
+
<Pencil size={10} /> Edit
|
|
108
|
+
</button>
|
|
109
|
+
<button
|
|
110
|
+
onClick={() => { duplicateAutomation(a.id); setMenuOpen(false); }}
|
|
111
|
+
className="w-full text-left px-3 py-1.5 text-xs font-sans text-text-1 hover:bg-surface-5 transition-colors cursor-pointer flex items-center gap-2"
|
|
112
|
+
>
|
|
113
|
+
<Copy size={10} /> Duplicate
|
|
114
|
+
</button>
|
|
115
|
+
<button
|
|
116
|
+
onClick={() => { runAutomation(a.id); setMenuOpen(false); }}
|
|
117
|
+
className="w-full text-left px-3 py-1.5 text-xs font-sans text-text-1 hover:bg-surface-5 transition-colors cursor-pointer flex items-center gap-2"
|
|
118
|
+
>
|
|
119
|
+
<Play size={10} /> Run Now
|
|
120
|
+
</button>
|
|
121
|
+
<div className="h-px my-1 bg-border-subtle" />
|
|
122
|
+
<button
|
|
123
|
+
onClick={() => { deleteAutomation(a.id); setMenuOpen(false); }}
|
|
124
|
+
className="w-full text-left px-3 py-1.5 text-xs font-sans text-danger hover:bg-danger/5 transition-colors cursor-pointer flex items-center gap-2"
|
|
125
|
+
>
|
|
126
|
+
<Trash2 size={10} /> Delete
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{/* Team strip */}
|
|
135
|
+
{roles.length > 0 && (
|
|
136
|
+
<div className="px-4 pb-2 flex items-center gap-1.5 flex-wrap">
|
|
137
|
+
{roles.map((r, i) => (
|
|
138
|
+
<Badge key={i} variant="default" className="text-2xs">
|
|
139
|
+
{r.role}{r.phase ? ` P${r.phase}` : ''}
|
|
140
|
+
</Badge>
|
|
141
|
+
))}
|
|
142
|
+
{a.integrationIds?.length > 0 && (
|
|
143
|
+
<span className="text-2xs text-text-4 font-sans ml-1">+{a.integrationIds.length} integrations</span>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{/* Instructions preview */}
|
|
149
|
+
{(instructions || filePath) && (
|
|
150
|
+
<div className="px-4 pb-2">
|
|
151
|
+
{filePath ? (
|
|
152
|
+
<div className="flex items-center gap-1.5">
|
|
153
|
+
<Folder size={10} className="text-text-4 flex-shrink-0" />
|
|
154
|
+
<span className="text-2xs font-mono text-text-3 truncate">{filePath}</span>
|
|
155
|
+
</div>
|
|
156
|
+
) : (
|
|
157
|
+
<p className="text-2xs text-text-3 font-sans truncate">
|
|
158
|
+
<FileText size={10} className="inline mr-1 text-text-4" />
|
|
159
|
+
{instructions.slice(0, 100)}{instructions.length > 100 ? '...' : ''}
|
|
160
|
+
</p>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{/* Schedule + last run row */}
|
|
166
|
+
<div className="px-4 py-2 border-t border-border-subtle bg-surface-0 flex items-center gap-3 flex-wrap">
|
|
167
|
+
<div className="flex items-center gap-1.5">
|
|
168
|
+
<Clock size={10} className="text-text-4" />
|
|
169
|
+
<span className="text-2xs font-mono text-text-2">{cronToHuman(a.cron)}</span>
|
|
170
|
+
</div>
|
|
171
|
+
{a.nextRunAt && (
|
|
172
|
+
<span className="text-2xs font-mono text-text-3">
|
|
173
|
+
Next: {timeAgo(a.nextRunAt)}
|
|
174
|
+
</span>
|
|
175
|
+
)}
|
|
176
|
+
{a.lastRunAt && (
|
|
177
|
+
<div className="flex items-center gap-1.5">
|
|
178
|
+
<span className="text-2xs font-mono text-text-3">Last: {timeAgo(a.lastRunAt)}</span>
|
|
179
|
+
<StatusDot
|
|
180
|
+
status={lastStatus === 'error' ? 'crashed' : lastStatus === 'running' ? 'running' : 'completed'}
|
|
181
|
+
size="sm"
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
{!a.lastRunAt && (
|
|
186
|
+
<span className="text-2xs text-text-4 font-sans">Never run</span>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
{/* Output config */}
|
|
191
|
+
{(gatewayIds.length > 0 || a.outputConfig?.filePath || a.outputConfig?.customInstructions) && (
|
|
192
|
+
<div className="px-4 py-1.5 border-t border-border-subtle bg-surface-0 space-y-1">
|
|
193
|
+
{gatewayIds.length > 0 && (
|
|
194
|
+
<div className="flex items-center gap-1.5">
|
|
195
|
+
<span className="text-2xs text-text-4 font-sans">Gateways:</span>
|
|
196
|
+
<span className="text-2xs font-sans text-text-3">{gatewayIds.join(', ')}</span>
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
{a.outputConfig?.filePath && (
|
|
200
|
+
<div className="flex items-center gap-1.5">
|
|
201
|
+
<span className="text-2xs text-text-4 font-sans">File:</span>
|
|
202
|
+
<span className="text-2xs font-mono text-text-3 truncate">{a.outputConfig.filePath}</span>
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
{a.outputConfig?.customInstructions && (
|
|
206
|
+
<div className="flex items-center gap-1.5">
|
|
207
|
+
<span className="text-2xs text-text-4 font-sans">Custom:</span>
|
|
208
|
+
<span className="text-2xs font-sans text-text-3 truncate">{a.outputConfig.customInstructions.slice(0, 80)}{a.outputConfig.customInstructions.length > 80 ? '...' : ''}</span>
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{/* Active agents */}
|
|
215
|
+
{activeAgents.length > 0 && (
|
|
216
|
+
<div className="px-4 py-2 border-t border-border-subtle bg-surface-0">
|
|
217
|
+
<div className="flex items-center gap-1.5 mb-1.5">
|
|
218
|
+
<StatusDot status="running" size="sm" />
|
|
219
|
+
<span className="text-2xs font-sans text-text-2 font-medium">Running</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div className="flex items-center gap-1.5 flex-wrap">
|
|
222
|
+
{activeAgents.map((ag) => (
|
|
223
|
+
<button
|
|
224
|
+
key={ag.id}
|
|
225
|
+
onClick={() => openAgentPanel(ag.id)}
|
|
226
|
+
className="flex items-center gap-1.5 px-2 py-1 rounded bg-surface-4 hover:bg-surface-5 transition-colors cursor-pointer group"
|
|
227
|
+
>
|
|
228
|
+
<StatusDot status={ag.status} size="sm" />
|
|
229
|
+
<span className="text-2xs font-sans text-text-1 group-hover:text-text-0">{ag.name || ag.role}</span>
|
|
230
|
+
<ExternalLink size={9} className="text-text-4 group-hover:text-accent" />
|
|
231
|
+
</button>
|
|
232
|
+
))}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
|
|
237
|
+
{/* Last run agents (when not currently running) */}
|
|
238
|
+
{!a.isRunning && lastRunAgents.length > 0 && (
|
|
239
|
+
<div className="px-4 py-2 border-t border-border-subtle bg-surface-0">
|
|
240
|
+
<div className="flex items-center gap-1.5 mb-1.5">
|
|
241
|
+
<span className="text-2xs font-sans text-text-3">Last run agents</span>
|
|
242
|
+
</div>
|
|
243
|
+
<div className="flex items-center gap-1.5 flex-wrap">
|
|
244
|
+
{lastRunAgents.map((ag) => (
|
|
245
|
+
<button
|
|
246
|
+
key={ag.id}
|
|
247
|
+
onClick={() => openAgentPanel(ag.id)}
|
|
248
|
+
className="flex items-center gap-1.5 px-2 py-1 rounded bg-surface-4 hover:bg-surface-5 transition-colors cursor-pointer group"
|
|
249
|
+
>
|
|
250
|
+
<StatusDot status={ag.status} size="sm" />
|
|
251
|
+
<span className="text-2xs font-sans text-text-2 group-hover:text-text-0">{ag.name || ag.role}</span>
|
|
252
|
+
<ExternalLink size={9} className="text-text-4 group-hover:text-accent" />
|
|
253
|
+
</button>
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{/* Bottom bar */}
|
|
260
|
+
<div className="px-4 py-2 border-t border-border-subtle flex items-center gap-3">
|
|
261
|
+
<Button variant="ghost" size="sm" onClick={() => runAutomation(a.id)} className="h-6 px-2 text-2xs gap-1">
|
|
262
|
+
<Play size={10} /> Run Now
|
|
263
|
+
</Button>
|
|
264
|
+
<div className="flex-1" />
|
|
265
|
+
{a.lastRunDuration != null && (
|
|
266
|
+
<span className="text-2xs font-mono text-text-4">{fmtUptime(a.lastRunDuration / 1000)}</span>
|
|
267
|
+
)}
|
|
268
|
+
{a.lastRunCost != null && a.lastRunCost > 0 && (
|
|
269
|
+
<span className="text-2xs font-mono text-text-4">{fmtDollar(a.lastRunCost)}</span>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
}
|