groove-dev 0.27.119 → 0.27.121
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/moe-training/client/trajectory-capture.js +55 -0
- package/moe-training/test/client/trajectory-capture.test.js +63 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/cli/src/commands/start.js +2 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +30 -10
- package/node_modules/@groove-dev/daemon/src/conversations.js +54 -32
- package/node_modules/@groove-dev/daemon/src/introducer.js +45 -20
- package/node_modules/@groove-dev/daemon/src/process.js +47 -1
- package/node_modules/@groove-dev/daemon/src/teams.js +33 -0
- package/node_modules/@groove-dev/gui/dist/assets/{index-DT6Jbf_q.css → index-BLd3MON8.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-BxPCaxlC.js → index-bmkBX18f.js} +1721 -1721
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +3 -41
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +4 -43
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +8 -10
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +8 -23
- package/node_modules/@groove-dev/gui/src/components/settings/ProviderSetupWizard.jsx +54 -143
- package/node_modules/@groove-dev/gui/src/components/ui/data-sharing-modal.jsx +7 -57
- package/node_modules/@groove-dev/gui/src/stores/groove.js +13 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +50 -84
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +61 -1
- package/node_modules/moe-training/client/trajectory-capture.js +55 -0
- package/node_modules/moe-training/test/client/trajectory-capture.test.js +63 -0
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/commands/start.js +2 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +30 -10
- package/packages/daemon/src/conversations.js +54 -32
- package/packages/daemon/src/introducer.js +45 -20
- package/packages/daemon/src/process.js +47 -1
- package/packages/daemon/src/teams.js +33 -0
- package/packages/gui/dist/assets/{index-DT6Jbf_q.css → index-BLd3MON8.css} +1 -1
- package/packages/gui/dist/assets/{index-BxPCaxlC.js → index-bmkBX18f.js} +1721 -1721
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-config.jsx +3 -41
- package/packages/gui/src/components/agents/spawn-wizard.jsx +4 -43
- package/packages/gui/src/components/layout/status-bar.jsx +8 -10
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +8 -23
- package/packages/gui/src/components/settings/ProviderSetupWizard.jsx +54 -143
- package/packages/gui/src/components/ui/data-sharing-modal.jsx +7 -57
- package/packages/gui/src/stores/groove.js +13 -0
- package/packages/gui/src/views/settings.jsx +50 -84
- package/packages/gui/src/views/teams.jsx +61 -1
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-bmkBX18f.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-26L3JoZv.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-DoBZjiHE.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-CFF1Lrnz.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BLd3MON8.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
AlertCircle, Layers, Activity,
|
|
7
7
|
RotateCw, Skull, Copy, Trash2,
|
|
8
8
|
Sparkles, Calendar, Plug, MessageCircle, Save, GitBranch,
|
|
9
|
-
ExternalLink, Loader2,
|
|
10
9
|
} from 'lucide-react';
|
|
11
10
|
import { useGrooveStore } from '../../stores/groove';
|
|
12
11
|
import { Badge } from '../ui/badge';
|
|
@@ -169,8 +168,6 @@ export function AgentConfig({ agent }) {
|
|
|
169
168
|
const [savingPersonality, setSavingPersonality] = useState(false);
|
|
170
169
|
const [installedIntegrations, setInstalledIntegrations] = useState([]);
|
|
171
170
|
const [claudeAuth, setClaudeAuth] = useState(null);
|
|
172
|
-
const [claudeAuthLoading, setClaudeAuthLoading] = useState(false);
|
|
173
|
-
const [claudeAuthPolling, setClaudeAuthPolling] = useState(false);
|
|
174
171
|
|
|
175
172
|
const isAlive = agent.status === 'running' || agent.status === 'starting';
|
|
176
173
|
|
|
@@ -217,23 +214,6 @@ export function AgentConfig({ agent }) {
|
|
|
217
214
|
}).catch(() => {});
|
|
218
215
|
}, [agent.id, agent.name]);
|
|
219
216
|
|
|
220
|
-
useEffect(() => {
|
|
221
|
-
if (!claudeAuthPolling) return;
|
|
222
|
-
const start = Date.now();
|
|
223
|
-
const interval = setInterval(() => {
|
|
224
|
-
if (Date.now() - start > 300000) { setClaudeAuthPolling(false); clearInterval(interval); return; }
|
|
225
|
-
api.get('/providers/claude-code/auth').then((data) => {
|
|
226
|
-
if (data?.authenticated) {
|
|
227
|
-
setClaudeAuth(data);
|
|
228
|
-
setClaudeAuthPolling(false);
|
|
229
|
-
setClaudeAuthLoading(false);
|
|
230
|
-
clearInterval(interval);
|
|
231
|
-
}
|
|
232
|
-
}).catch(() => {});
|
|
233
|
-
}, 2000);
|
|
234
|
-
return () => clearInterval(interval);
|
|
235
|
-
}, [claudeAuthPolling]);
|
|
236
|
-
|
|
237
217
|
const currentProvider = providers.find((p) => p.id === agent.provider);
|
|
238
218
|
|
|
239
219
|
async function handleModelSwap(providerId, modelId) {
|
|
@@ -304,16 +284,6 @@ export function AgentConfig({ agent }) {
|
|
|
304
284
|
}
|
|
305
285
|
}
|
|
306
286
|
|
|
307
|
-
async function handleClaudeLogin() {
|
|
308
|
-
setClaudeAuthLoading(true);
|
|
309
|
-
try {
|
|
310
|
-
await api.post('/providers/claude-code/login');
|
|
311
|
-
setClaudeAuthPolling(true);
|
|
312
|
-
} catch {
|
|
313
|
-
setClaudeAuthLoading(false);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
287
|
const spawned = agent.spawnedAt || agent.createdAt;
|
|
318
288
|
|
|
319
289
|
return (
|
|
@@ -475,17 +445,9 @@ export function AgentConfig({ agent }) {
|
|
|
475
445
|
<AlertCircle size={13} className="text-warning flex-shrink-0" />
|
|
476
446
|
<span className="text-xs font-semibold text-text-0 font-sans">Claude Code is not signed in</span>
|
|
477
447
|
</div>
|
|
478
|
-
|
|
479
|
-
<
|
|
480
|
-
|
|
481
|
-
Waiting for browser authentication...
|
|
482
|
-
</div>
|
|
483
|
-
) : (
|
|
484
|
-
<Button variant="primary" size="sm" onClick={handleClaudeLogin} className="text-2xs gap-1.5">
|
|
485
|
-
<ExternalLink size={10} />
|
|
486
|
-
Sign in to Claude
|
|
487
|
-
</Button>
|
|
488
|
-
)}
|
|
448
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
449
|
+
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>
|
|
450
|
+
</p>
|
|
489
451
|
</div>
|
|
490
452
|
)}
|
|
491
453
|
{agent.provider === 'claude-code' && claudeAuth?.authenticated && (
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
Shield, Database, Megaphone, Calculator, UserCheck,
|
|
13
13
|
Headphones, BarChart3, Rocket, ChevronDown, Pen, Presentation,
|
|
14
14
|
Sparkles, X, Search, AlertTriangle, Plug, MessageCircle, GitBranch, Globe,
|
|
15
|
-
Check,
|
|
15
|
+
Check,
|
|
16
16
|
} from 'lucide-react';
|
|
17
17
|
import { api } from '../../lib/api';
|
|
18
18
|
import { Dialog, DialogContent } from '../ui/dialog';
|
|
@@ -83,8 +83,6 @@ export function SpawnWizard() {
|
|
|
83
83
|
const [recommendations, setRecommendations] = useState([]);
|
|
84
84
|
const [preflightDialog, setPreflightDialog] = useState(null);
|
|
85
85
|
const [claudeAuth, setClaudeAuth] = useState(null);
|
|
86
|
-
const [claudeAuthLoading, setClaudeAuthLoading] = useState(false);
|
|
87
|
-
const [claudeAuthPolling, setClaudeAuthPolling] = useState(false);
|
|
88
86
|
const federation = useGrooveStore((s) => s.federation);
|
|
89
87
|
|
|
90
88
|
const selectedRole = role || customRole;
|
|
@@ -127,8 +125,6 @@ export function SpawnWizard() {
|
|
|
127
125
|
setRecommendations([]);
|
|
128
126
|
setPreflightDialog(null);
|
|
129
127
|
setClaudeAuth(null);
|
|
130
|
-
setClaudeAuthLoading(false);
|
|
131
|
-
setClaudeAuthPolling(false);
|
|
132
128
|
}
|
|
133
129
|
}, [open, fetchProviders]);
|
|
134
130
|
|
|
@@ -153,23 +149,6 @@ export function SpawnWizard() {
|
|
|
153
149
|
}).catch(() => setClaudeAuth(null));
|
|
154
150
|
}, [open, provider]);
|
|
155
151
|
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
if (!claudeAuthPolling) return;
|
|
158
|
-
const start = Date.now();
|
|
159
|
-
const interval = setInterval(() => {
|
|
160
|
-
if (Date.now() - start > 300000) { setClaudeAuthPolling(false); clearInterval(interval); return; }
|
|
161
|
-
api.get('/providers/claude-code/auth').then((data) => {
|
|
162
|
-
if (data?.authenticated) {
|
|
163
|
-
setClaudeAuth(data);
|
|
164
|
-
setClaudeAuthPolling(false);
|
|
165
|
-
setClaudeAuthLoading(false);
|
|
166
|
-
clearInterval(interval);
|
|
167
|
-
}
|
|
168
|
-
}).catch(() => {});
|
|
169
|
-
}, 2000);
|
|
170
|
-
return () => clearInterval(interval);
|
|
171
|
-
}, [claudeAuthPolling]);
|
|
172
|
-
|
|
173
152
|
async function runSpawn() {
|
|
174
153
|
setSpawning(true);
|
|
175
154
|
try {
|
|
@@ -207,16 +186,6 @@ export function SpawnWizard() {
|
|
|
207
186
|
runSpawn();
|
|
208
187
|
}
|
|
209
188
|
|
|
210
|
-
async function handleClaudeLogin() {
|
|
211
|
-
setClaudeAuthLoading(true);
|
|
212
|
-
try {
|
|
213
|
-
await api.post('/providers/claude-code/login');
|
|
214
|
-
setClaudeAuthPolling(true);
|
|
215
|
-
} catch {
|
|
216
|
-
setClaudeAuthLoading(false);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
189
|
const claudeNotAuthed = provider === 'claude-code' && claudeAuth && !claudeAuth.authenticated;
|
|
221
190
|
|
|
222
191
|
return (
|
|
@@ -457,17 +426,9 @@ export function SpawnWizard() {
|
|
|
457
426
|
<AlertTriangle size={13} className="text-warning flex-shrink-0" />
|
|
458
427
|
<span className="text-xs font-semibold text-text-0 font-sans">Claude Code is not signed in</span>
|
|
459
428
|
</div>
|
|
460
|
-
|
|
461
|
-
<
|
|
462
|
-
|
|
463
|
-
Waiting for browser authentication...
|
|
464
|
-
</div>
|
|
465
|
-
) : (
|
|
466
|
-
<Button variant="primary" size="sm" onClick={handleClaudeLogin} className="text-2xs gap-1.5">
|
|
467
|
-
<ExternalLink size={10} />
|
|
468
|
-
Sign in to Claude
|
|
469
|
-
</Button>
|
|
470
|
-
)}
|
|
429
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
430
|
+
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>
|
|
431
|
+
</p>
|
|
471
432
|
</div>
|
|
472
433
|
)}
|
|
473
434
|
{provider === 'claude-code' && claudeAuth?.authenticated && (
|
|
@@ -90,16 +90,14 @@ export function StatusBar({
|
|
|
90
90
|
</button>
|
|
91
91
|
</div>
|
|
92
92
|
))}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
</button>
|
|
102
|
-
)}
|
|
93
|
+
<button
|
|
94
|
+
onClick={() => useGrooveStore.getState().toggleQuickConnect()}
|
|
95
|
+
className="flex items-center gap-1.5 text-text-4 hover:text-text-1 cursor-pointer transition-colors"
|
|
96
|
+
title="Quick Connect to remote server"
|
|
97
|
+
>
|
|
98
|
+
<Plug size={10} />
|
|
99
|
+
<span>Connect</span>
|
|
100
|
+
</button>
|
|
103
101
|
</>
|
|
104
102
|
)}
|
|
105
103
|
{connected && (
|
|
@@ -242,7 +242,7 @@ function InstallStep({ providerStatus, selected, onInstall, installing, statusCh
|
|
|
242
242
|
|
|
243
243
|
// ── Step 4: Authentication ──────────────────────────────────
|
|
244
244
|
|
|
245
|
-
function AuthCard({ provider, providerStatus, onSaveKey
|
|
245
|
+
function AuthCard({ provider, providerStatus, onSaveKey }) {
|
|
246
246
|
const [authMode, setAuthMode] = useState(
|
|
247
247
|
provider.authModes.includes('subscription') ? 'subscription' : 'apikey',
|
|
248
248
|
);
|
|
@@ -307,14 +307,12 @@ function AuthCard({ provider, providerStatus, onSaveKey, onSubscriptionLogin })
|
|
|
307
307
|
|
|
308
308
|
{authMode === 'subscription' && (
|
|
309
309
|
<div className="space-y-3">
|
|
310
|
-
<p className="text-xs text-text-2">Sign in with your Claude subscription
|
|
311
|
-
<
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
>
|
|
316
|
-
Sign In
|
|
317
|
-
</button>
|
|
310
|
+
<p className="text-xs text-text-2">Sign in via the terminal with your Claude subscription.</p>
|
|
311
|
+
<div className="space-y-1.5">
|
|
312
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
313
|
+
Open a terminal and run: <code className="font-mono text-accent bg-surface-4 px-1.5 py-0.5 rounded text-2xs">claude</code> — then follow the prompts to sign in.
|
|
314
|
+
</p>
|
|
315
|
+
</div>
|
|
318
316
|
{providerStatus?.authenticated && (
|
|
319
317
|
<p className="text-xs text-success flex items-center gap-1.5">
|
|
320
318
|
<Check className="w-3.5 h-3.5" /> Connected
|
|
@@ -388,7 +386,7 @@ function AuthCard({ provider, providerStatus, onSaveKey, onSubscriptionLogin })
|
|
|
388
386
|
);
|
|
389
387
|
}
|
|
390
388
|
|
|
391
|
-
function AuthStep({ providerStatus, installedIds, onSaveKey
|
|
389
|
+
function AuthStep({ providerStatus, installedIds, onSaveKey }) {
|
|
392
390
|
const installed = PROVIDERS.filter((p) => installedIds.includes(p.id) || providerStatus[p.id]?.installed);
|
|
393
391
|
const hasAuthenticated = installed.some((p) => providerStatus[p.id]?.authenticated);
|
|
394
392
|
|
|
@@ -406,7 +404,6 @@ function AuthStep({ providerStatus, installedIds, onSaveKey, onSubscriptionLogin
|
|
|
406
404
|
provider={p}
|
|
407
405
|
providerStatus={providerStatus[p.id] || {}}
|
|
408
406
|
onSaveKey={onSaveKey}
|
|
409
|
-
onSubscriptionLogin={onSubscriptionLogin}
|
|
410
407
|
/>
|
|
411
408
|
))}
|
|
412
409
|
</div>
|
|
@@ -687,17 +684,6 @@ export function SetupWizard() {
|
|
|
687
684
|
}
|
|
688
685
|
}, [addToast]);
|
|
689
686
|
|
|
690
|
-
const handleSubscriptionLogin = useCallback((providerId) => {
|
|
691
|
-
if (isElectron() && window.groove?.auth?.login) {
|
|
692
|
-
window.groove.auth.login();
|
|
693
|
-
} else {
|
|
694
|
-
fetch('/api/auth/login-url')
|
|
695
|
-
.then((r) => r.json())
|
|
696
|
-
.then((d) => { if (d.url) window.open(d.url, '_blank'); })
|
|
697
|
-
.catch(() => addToast('error', 'Failed to start login'));
|
|
698
|
-
}
|
|
699
|
-
}, [addToast]);
|
|
700
|
-
|
|
701
687
|
const handleSetDefault = useCallback(async (provider, model) => {
|
|
702
688
|
setDefaultProv(provider);
|
|
703
689
|
setDefaultMod(model);
|
|
@@ -741,7 +727,6 @@ export function SetupWizard() {
|
|
|
741
727
|
providerStatus={providerStatus}
|
|
742
728
|
installedIds={selected}
|
|
743
729
|
onSaveKey={handleSaveKey}
|
|
744
|
-
onSubscriptionLogin={handleSubscriptionLogin}
|
|
745
730
|
/>,
|
|
746
731
|
<DefaultModelStep
|
|
747
732
|
key="default"
|
|
@@ -9,7 +9,7 @@ import { cn } from '../../lib/cn';
|
|
|
9
9
|
import { api } from '../../lib/api';
|
|
10
10
|
import {
|
|
11
11
|
Download, Loader2, Check, ChevronDown, ChevronUp,
|
|
12
|
-
Eye, EyeOff, Key, RotateCcw,
|
|
12
|
+
Eye, EyeOff, Key, RotateCcw, Sparkles,
|
|
13
13
|
} from 'lucide-react';
|
|
14
14
|
|
|
15
15
|
const PROVIDER_META = {
|
|
@@ -166,15 +166,15 @@ function InstallStep({ providerId, meta }) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
function AuthenticateStep({ providerId, meta, onSaveKey }) {
|
|
169
|
-
const loginProvider = useGrooveStore((s) => s.loginProvider);
|
|
170
169
|
const addToast = useGrooveStore((s) => s.addToast);
|
|
171
170
|
const [key, setKey] = useState('');
|
|
172
171
|
const [showKey, setShowKey] = useState(false);
|
|
173
172
|
const [saving, setSaving] = useState(false);
|
|
174
|
-
const [
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
const [authMode, setAuthMode] = useState(
|
|
174
|
+
providerId === 'claude-code' ? 'terminal' :
|
|
175
|
+
providerId === 'codex' ? 'terminal' :
|
|
176
|
+
'apikey',
|
|
177
|
+
);
|
|
178
178
|
|
|
179
179
|
async function handleSaveKey() {
|
|
180
180
|
if (!key.trim()) return;
|
|
@@ -190,158 +190,69 @@ function AuthenticateStep({ providerId, meta, onSaveKey }) {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
async function handleLogin() {
|
|
194
|
-
const body = authMode === 'chatgpt-plus' ? { method: 'chatgpt-plus' } : undefined;
|
|
195
|
-
try {
|
|
196
|
-
await loginProvider(providerId, body);
|
|
197
|
-
setLoginStarted(true);
|
|
198
|
-
} catch { /* handled in store */ }
|
|
199
|
-
}
|
|
200
|
-
|
|
201
193
|
return (
|
|
202
194
|
<div className="space-y-4">
|
|
203
195
|
<p className="text-sm font-medium text-text-0 font-sans">{meta.authLabel}</p>
|
|
204
196
|
|
|
205
197
|
{providerId === 'claude-code' && (
|
|
206
|
-
<div className="
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
className=
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
198
|
+
<div className="space-y-3">
|
|
199
|
+
<p className="text-xs text-text-2 font-sans">Sign in via the terminal with your Claude subscription.</p>
|
|
200
|
+
<div className="space-y-1.5">
|
|
201
|
+
<div className="flex items-start gap-2">
|
|
202
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">1</span>
|
|
203
|
+
<p className="text-2xs text-text-2 font-sans">Open the Groove terminal below</p>
|
|
204
|
+
</div>
|
|
205
|
+
<div className="flex items-start gap-2">
|
|
206
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">2</span>
|
|
207
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
208
|
+
Run: <code className="font-mono text-accent bg-surface-4 px-1.5 py-0.5 rounded text-2xs">claude</code>
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
<div className="flex items-start gap-2">
|
|
212
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">3</span>
|
|
213
|
+
<p className="text-2xs text-text-2 font-sans">Follow the prompts to sign in with your Anthropic account</p>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
216
|
<button
|
|
217
|
-
onClick={() =>
|
|
218
|
-
className=
|
|
219
|
-
'flex-1 h-7 rounded text-xs font-medium transition-colors cursor-pointer font-sans',
|
|
220
|
-
authMode === 'apikey' ? 'bg-surface-5 text-text-0' : 'text-text-3 hover:text-text-1',
|
|
221
|
-
)}
|
|
217
|
+
onClick={() => setAuthMode(authMode === 'apikey' ? 'terminal' : 'apikey')}
|
|
218
|
+
className="text-2xs text-text-4 hover:text-accent cursor-pointer font-sans"
|
|
222
219
|
>
|
|
223
|
-
API
|
|
220
|
+
{authMode === 'apikey' ? 'Use terminal login instead' : 'I have an API key instead'}
|
|
224
221
|
</button>
|
|
225
222
|
</div>
|
|
226
223
|
)}
|
|
227
224
|
|
|
228
225
|
{providerId === 'codex' && (
|
|
229
|
-
<div className="flex gap-1 bg-surface-3 p-0.5 rounded-md mb-4">
|
|
230
|
-
<button
|
|
231
|
-
onClick={() => { setAuthMode('apikey'); setLoginStarted(false); }}
|
|
232
|
-
className={cn(
|
|
233
|
-
'flex-1 h-7 rounded text-xs font-medium transition-colors cursor-pointer font-sans',
|
|
234
|
-
authMode === 'apikey' ? 'bg-surface-5 text-text-0' : 'text-text-3 hover:text-text-1',
|
|
235
|
-
)}
|
|
236
|
-
>
|
|
237
|
-
API Key
|
|
238
|
-
</button>
|
|
239
|
-
<button
|
|
240
|
-
onClick={() => { setAuthMode('chatgpt-plus'); setLoginStarted(false); }}
|
|
241
|
-
className={cn(
|
|
242
|
-
'flex-1 h-7 rounded text-xs font-medium transition-colors cursor-pointer font-sans',
|
|
243
|
-
authMode === 'chatgpt-plus' ? 'bg-surface-5 text-text-0' : 'text-text-3 hover:text-text-1',
|
|
244
|
-
)}
|
|
245
|
-
>
|
|
246
|
-
ChatGPT Plus
|
|
247
|
-
</button>
|
|
248
|
-
</div>
|
|
249
|
-
)}
|
|
250
|
-
|
|
251
|
-
{authMode === 'subscription' && (
|
|
252
226
|
<div className="space-y-3">
|
|
253
|
-
<p className="text-xs text-text-2 font-sans">
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<ExternalLink size={12} /> {meta.loginLabel}
|
|
259
|
-
</Button>
|
|
260
|
-
) : (
|
|
261
|
-
<div className="space-y-3">
|
|
262
|
-
<div className="flex items-center gap-2 p-3 bg-accent/5 border border-accent/15 rounded-md">
|
|
263
|
-
<ExternalLink size={12} className="text-accent" />
|
|
264
|
-
<span className="text-xs text-accent font-sans">Sign-in opened in your browser</span>
|
|
265
|
-
</div>
|
|
266
|
-
<Button
|
|
267
|
-
variant="primary"
|
|
268
|
-
size="sm"
|
|
269
|
-
disabled={verifying}
|
|
270
|
-
onClick={async () => {
|
|
271
|
-
setVerifying(true);
|
|
272
|
-
setVerifyError('');
|
|
273
|
-
try {
|
|
274
|
-
const res = await api.post(`/providers/${providerId}/verify`);
|
|
275
|
-
if (res.authenticated) {
|
|
276
|
-
onSaveKey();
|
|
277
|
-
} else {
|
|
278
|
-
setVerifyError('Authentication not detected yet. Please complete sign-in in your browser and try again.');
|
|
279
|
-
}
|
|
280
|
-
} catch {
|
|
281
|
-
setVerifyError('Authentication not detected yet. Please complete sign-in in your browser and try again.');
|
|
282
|
-
} finally {
|
|
283
|
-
setVerifying(false);
|
|
284
|
-
}
|
|
285
|
-
}}
|
|
286
|
-
className="gap-1.5"
|
|
287
|
-
>
|
|
288
|
-
{verifying ? <Loader2 size={11} className="animate-spin" /> : <Check size={11} />} I've signed in
|
|
289
|
-
</Button>
|
|
290
|
-
{verifyError && (
|
|
291
|
-
<p className="text-2xs text-danger font-sans">{verifyError}</p>
|
|
292
|
-
)}
|
|
227
|
+
<p className="text-xs text-text-2 font-sans">Sign in via the terminal with your OpenAI account.</p>
|
|
228
|
+
<div className="space-y-1.5">
|
|
229
|
+
<div className="flex items-start gap-2">
|
|
230
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">1</span>
|
|
231
|
+
<p className="text-2xs text-text-2 font-sans">Open the Groove terminal below</p>
|
|
293
232
|
</div>
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
)}
|
|
300
|
-
|
|
301
|
-
{authMode === 'chatgpt-plus' && (
|
|
302
|
-
<div className="space-y-3">
|
|
303
|
-
<p className="text-xs text-text-2 font-sans">
|
|
304
|
-
Click below to sign in with your ChatGPT Plus subscription.
|
|
305
|
-
</p>
|
|
306
|
-
{!loginStarted ? (
|
|
307
|
-
<Button variant="primary" size="md" onClick={handleLogin} className="gap-1.5">
|
|
308
|
-
<ExternalLink size={12} /> {meta.loginLabel}
|
|
309
|
-
</Button>
|
|
310
|
-
) : (
|
|
311
|
-
<div className="space-y-3">
|
|
312
|
-
<div className="flex items-center gap-2 p-3 bg-accent/5 border border-accent/15 rounded-md">
|
|
313
|
-
<ExternalLink size={12} className="text-accent" />
|
|
314
|
-
<span className="text-xs text-accent font-sans">Sign-in opened in your browser</span>
|
|
315
|
-
</div>
|
|
316
|
-
<Button
|
|
317
|
-
variant="primary"
|
|
318
|
-
size="sm"
|
|
319
|
-
disabled={verifying}
|
|
320
|
-
onClick={async () => {
|
|
321
|
-
setVerifying(true);
|
|
322
|
-
setVerifyError('');
|
|
323
|
-
try {
|
|
324
|
-
const res = await api.post(`/providers/codex/verify`);
|
|
325
|
-
if (res.authenticated) {
|
|
326
|
-
onSaveKey();
|
|
327
|
-
} else {
|
|
328
|
-
setVerifyError('Authentication not detected yet. Please complete sign-in in your browser and try again.');
|
|
329
|
-
}
|
|
330
|
-
} catch {
|
|
331
|
-
setVerifyError('Authentication not detected yet. Please complete sign-in in your browser and try again.');
|
|
332
|
-
} finally {
|
|
333
|
-
setVerifying(false);
|
|
334
|
-
}
|
|
335
|
-
}}
|
|
336
|
-
className="gap-1.5"
|
|
337
|
-
>
|
|
338
|
-
{verifying ? <Loader2 size={11} className="animate-spin" /> : <Check size={11} />} I've signed in
|
|
339
|
-
</Button>
|
|
340
|
-
{verifyError && (
|
|
341
|
-
<p className="text-2xs text-danger font-sans">{verifyError}</p>
|
|
342
|
-
)}
|
|
233
|
+
<div className="flex items-start gap-2">
|
|
234
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">2</span>
|
|
235
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
236
|
+
Run: <code className="font-mono text-accent bg-surface-4 px-1.5 py-0.5 rounded text-2xs">npm i -g @openai/codex</code> (if not installed)
|
|
237
|
+
</p>
|
|
343
238
|
</div>
|
|
344
|
-
|
|
239
|
+
<div className="flex items-start gap-2">
|
|
240
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">3</span>
|
|
241
|
+
<p className="text-2xs text-text-2 font-sans">
|
|
242
|
+
Run: <code className="font-mono text-accent bg-surface-4 px-1.5 py-0.5 rounded text-2xs">codex login</code>
|
|
243
|
+
</p>
|
|
244
|
+
</div>
|
|
245
|
+
<div className="flex items-start gap-2">
|
|
246
|
+
<span className="text-2xs font-bold text-accent font-mono mt-0.5">4</span>
|
|
247
|
+
<p className="text-2xs text-text-2 font-sans">Follow the prompts to authenticate</p>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
<button
|
|
251
|
+
onClick={() => setAuthMode(authMode === 'apikey' ? 'terminal' : 'apikey')}
|
|
252
|
+
className="text-2xs text-text-4 hover:text-accent cursor-pointer font-sans"
|
|
253
|
+
>
|
|
254
|
+
{authMode === 'apikey' ? 'Use terminal login instead' : 'I have an API key instead'}
|
|
255
|
+
</button>
|
|
345
256
|
</div>
|
|
346
257
|
)}
|
|
347
258
|
|
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { Dialog, DialogContent } from './dialog';
|
|
4
4
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
5
|
import { useGrooveStore } from '../../stores/groove';
|
|
6
|
-
import { Sparkles,
|
|
6
|
+
import { Sparkles, Shield, X } from 'lucide-react';
|
|
7
7
|
|
|
8
8
|
export function DataSharingModal() {
|
|
9
9
|
const open = useGrooveStore((s) => s.dataSharingModalOpen);
|
|
@@ -14,8 +14,8 @@ export function DataSharingModal() {
|
|
|
14
14
|
return (
|
|
15
15
|
<Dialog open={open}>
|
|
16
16
|
<DialogContent
|
|
17
|
-
className="max-w-
|
|
18
|
-
description="
|
|
17
|
+
className="max-w-md"
|
|
18
|
+
description="Help improve Groove by sharing usage data"
|
|
19
19
|
onInteractOutside={(e) => e.preventDefault()}
|
|
20
20
|
onEscapeKeyDown={(e) => e.preventDefault()}
|
|
21
21
|
>
|
|
@@ -27,51 +27,10 @@ export function DataSharingModal() {
|
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
<DialogPrimitive.Title className="text-xl font-bold text-text-0 font-sans">
|
|
30
|
-
Help Build
|
|
30
|
+
Help Build a Better Groove
|
|
31
31
|
</DialogPrimitive.Title>
|
|
32
32
|
<p className="text-sm text-text-2 font-sans mt-2 max-w-md mx-auto">
|
|
33
|
-
|
|
34
|
-
</p>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
{/* Value Proposition */}
|
|
38
|
-
<div className="px-6 pt-5 pb-1">
|
|
39
|
-
<div className="grid grid-cols-3 gap-3">
|
|
40
|
-
{[
|
|
41
|
-
{ icon: Share2, title: 'You Share', desc: 'Anonymized agent session data: tool calls, error patterns, task flows' },
|
|
42
|
-
{ icon: Cpu, title: 'We Train', desc: 'A Groove-specific Mixture of Experts model built on real multi-agent workflows' },
|
|
43
|
-
{ icon: Gift, title: 'Everyone Wins', desc: 'Free, local, open source model for all Groove users. More data = smarter agents' },
|
|
44
|
-
].map(({ icon: Icon, title, desc }) => (
|
|
45
|
-
<div key={title} className="rounded-lg border border-border-subtle bg-surface-2/50 p-3 text-center">
|
|
46
|
-
<div className="flex justify-center mb-2">
|
|
47
|
-
<Icon size={18} className="text-accent" />
|
|
48
|
-
</div>
|
|
49
|
-
<div className="text-xs font-semibold text-text-0 font-sans mb-1">{title}</div>
|
|
50
|
-
<div className="text-2xs text-text-2 font-sans leading-relaxed">{desc}</div>
|
|
51
|
-
</div>
|
|
52
|
-
))}
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
{/* What We Collect */}
|
|
57
|
-
<div className="px-6 pt-4">
|
|
58
|
-
<div className="text-xs font-semibold uppercase text-text-3 tracking-wider font-sans mb-2.5">What We Collect</div>
|
|
59
|
-
<div className="space-y-1.5">
|
|
60
|
-
{[
|
|
61
|
-
'Agent tool calling patterns',
|
|
62
|
-
'Error and recovery sequences',
|
|
63
|
-
'Task complexity and coordination events',
|
|
64
|
-
'Model and provider usage metadata',
|
|
65
|
-
'Session duration and outcomes',
|
|
66
|
-
].map((item) => (
|
|
67
|
-
<div key={item} className="flex items-center gap-2">
|
|
68
|
-
<Check size={12} className="text-accent flex-shrink-0" />
|
|
69
|
-
<span className="text-xs text-text-1 font-sans">{item}</span>
|
|
70
|
-
</div>
|
|
71
|
-
))}
|
|
72
|
-
</div>
|
|
73
|
-
<p className="text-xs text-text-0 font-sans font-medium mt-3">
|
|
74
|
-
That's it. Groove orchestration data only.
|
|
33
|
+
We collect errors and usage reports to improve the quality of Groove for everyone.
|
|
75
34
|
</p>
|
|
76
35
|
</div>
|
|
77
36
|
|
|
@@ -87,7 +46,7 @@ export function DataSharingModal() {
|
|
|
87
46
|
'Your source code or file contents',
|
|
88
47
|
'API keys, passwords, or credentials',
|
|
89
48
|
'Personal information — emails, names, file paths',
|
|
90
|
-
'Anything that could identify your IP or projects',
|
|
49
|
+
'Anything that could identify you, your IP or projects',
|
|
91
50
|
].map((item) => (
|
|
92
51
|
<div key={item} className="flex items-center gap-2">
|
|
93
52
|
<X size={12} className="text-danger flex-shrink-0" />
|
|
@@ -96,16 +55,7 @@ export function DataSharingModal() {
|
|
|
96
55
|
))}
|
|
97
56
|
</div>
|
|
98
57
|
<p className="text-xs text-text-0 font-sans font-medium mt-3">
|
|
99
|
-
|
|
100
|
-
</p>
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
{/* Mission Statement */}
|
|
105
|
-
<div className="px-6 pt-4">
|
|
106
|
-
<div className="border-l-2 border-accent/30 pl-3">
|
|
107
|
-
<p className="text-xs text-text-3 italic leading-relaxed font-sans">
|
|
108
|
-
We believe in open source, decentralized intelligence. Not walled gardens. Not data hoarding. Every contribution makes the model better for everyone. We need your help to get there.
|
|
58
|
+
PII is automatically scrubbed before any data leaves your machine.
|
|
109
59
|
</p>
|
|
110
60
|
</div>
|
|
111
61
|
</div>
|
|
@@ -1274,6 +1274,19 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1274
1274
|
throw err;
|
|
1275
1275
|
}
|
|
1276
1276
|
},
|
|
1277
|
+
|
|
1278
|
+
async promoteTeam(id) {
|
|
1279
|
+
try {
|
|
1280
|
+
const team = await api.post(`/teams/${encodeURIComponent(id)}/promote`);
|
|
1281
|
+
set((s) => ({ teams: s.teams.map((t) => (t.id === id ? { ...t, ...team } : t)) }));
|
|
1282
|
+
get().addToast('success', 'Team promoted to production');
|
|
1283
|
+
return team;
|
|
1284
|
+
} catch (err) {
|
|
1285
|
+
get().addToast('error', 'Failed to promote team', err.message);
|
|
1286
|
+
throw err;
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1289
|
+
|
|
1277
1290
|
openDetail(descriptor) {
|
|
1278
1291
|
const tid = get().activeTeamId;
|
|
1279
1292
|
set((s) => ({ detailPanel: descriptor, teamDetailPanels: { ...s.teamDetailPanels, [tid]: descriptor } }));
|