groove-dev 0.27.107 → 0.27.108

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 (53) hide show
  1. package/TRAINING_DATA.md +9 -0
  2. package/moe-training/client/envelope-builder.js +5 -0
  3. package/moe-training/client/scrubber.js +1 -1
  4. package/moe-training/client/step-classifier.js +22 -6
  5. package/moe-training/client/trajectory-capture.js +15 -4
  6. package/moe-training/shared/constants.js +2 -0
  7. package/moe-training/shared/envelope-schema.js +1 -1
  8. package/moe-training/test/client/envelope-builder.test.js +32 -0
  9. package/moe-training/test/client/scrubber.test.js +13 -0
  10. package/moe-training/test/client/step-classifier.test.js +96 -7
  11. package/moe-training/test/client/trajectory-capture.test.js +53 -6
  12. package/node_modules/@groove-dev/cli/package.json +1 -1
  13. package/node_modules/@groove-dev/daemon/package.json +1 -1
  14. package/node_modules/@groove-dev/daemon/src/api.js +29 -2
  15. package/node_modules/@groove-dev/daemon/src/process.js +5 -5
  16. package/node_modules/@groove-dev/gui/dist/assets/{index-DkAGIluW.js → index-CEgtSfbG.js} +1748 -1745
  17. package/node_modules/@groove-dev/gui/dist/assets/{index-QwgLRN8B.css → index-_3cJS_UG.css} +1 -1
  18. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  19. package/node_modules/@groove-dev/gui/package.json +1 -1
  20. package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +2 -1
  21. package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +9 -3
  22. package/node_modules/@groove-dev/gui/src/components/settings/federation-panel.jsx +2 -2
  23. package/node_modules/@groove-dev/gui/src/components/settings/federation-peers.jsx +14 -2
  24. package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +9 -0
  25. package/node_modules/@groove-dev/gui/src/stores/groove.js +10 -0
  26. package/node_modules/@groove-dev/gui/src/views/federation.jsx +56 -15
  27. package/node_modules/moe-training/client/envelope-builder.js +5 -0
  28. package/node_modules/moe-training/client/scrubber.js +1 -1
  29. package/node_modules/moe-training/client/step-classifier.js +22 -6
  30. package/node_modules/moe-training/client/trajectory-capture.js +15 -4
  31. package/node_modules/moe-training/shared/constants.js +2 -0
  32. package/node_modules/moe-training/shared/envelope-schema.js +1 -1
  33. package/node_modules/moe-training/test/client/envelope-builder.test.js +32 -0
  34. package/node_modules/moe-training/test/client/scrubber.test.js +13 -0
  35. package/node_modules/moe-training/test/client/step-classifier.test.js +96 -7
  36. package/node_modules/moe-training/test/client/trajectory-capture.test.js +53 -6
  37. package/package.json +1 -1
  38. package/packages/cli/package.json +1 -1
  39. package/packages/daemon/package.json +1 -1
  40. package/packages/daemon/src/api.js +29 -2
  41. package/packages/daemon/src/process.js +5 -5
  42. package/packages/gui/dist/assets/{index-DkAGIluW.js → index-CEgtSfbG.js} +1748 -1745
  43. package/packages/gui/dist/assets/{index-QwgLRN8B.css → index-_3cJS_UG.css} +1 -1
  44. package/packages/gui/dist/index.html +2 -2
  45. package/packages/gui/package.json +1 -1
  46. package/packages/gui/src/components/layout/command-palette.jsx +2 -1
  47. package/packages/gui/src/components/layout/status-bar.jsx +9 -3
  48. package/packages/gui/src/components/settings/federation-panel.jsx +2 -2
  49. package/packages/gui/src/components/settings/federation-peers.jsx +14 -2
  50. package/packages/gui/src/components/settings/quick-connect.jsx +9 -0
  51. package/packages/gui/src/stores/groove.js +10 -0
  52. package/packages/gui/src/views/federation.jsx +56 -15
  53. package/ssh/main.js +2253 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.107",
3
+ "version": "0.27.108",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -4,7 +4,7 @@ import { useGrooveStore } from '../../stores/groove';
4
4
  import {
5
5
  Network, Code2, ChartSpline, Puzzle, Users, Plus,
6
6
  RotateCw, Skull, MessageSquare, Terminal, Newspaper,
7
- Search, Radio, ExternalLink, FolderOpen, Globe,
7
+ Search, Radio, ExternalLink, FolderOpen, Globe, Shield,
8
8
  } from 'lucide-react';
9
9
  import { cn } from '../../lib/cn';
10
10
  import { AnimatePresence, motion } from 'framer-motion';
@@ -16,6 +16,7 @@ const STATIC_COMMANDS = [
16
16
  { id: 'nav:marketplace', label: 'Go to Marketplace', icon: Puzzle, category: 'Navigation', action: (s) => { s.setActiveView('marketplace'); } },
17
17
  { id: 'nav:teams', label: 'Go to Teams', icon: Users, category: 'Navigation', action: (s) => { s.setActiveView('teams'); } },
18
18
  { id: 'nav:federation', label: 'Go to Federation', icon: Globe, category: 'Navigation', action: (s) => { s.setActiveView('federation'); } },
19
+ { id: 'action:whitelist', label: 'Add to Federation Whitelist', icon: Shield, category: 'Federation', action: (s) => { s.setActiveView('federation'); } },
19
20
  { id: 'action:spawn', label: 'Spawn Agent', icon: Plus, category: 'Actions', action: (s) => { s.openDetail({ type: 'spawn' }); } },
20
21
  { id: 'action:terminal', label: 'Toggle Terminal', icon: Terminal, category: 'Actions', action: (s) => { s.setTerminalVisible(!s.terminalVisible); }, shortcut: 'Cmd+J' },
21
22
  { id: 'action:journalist', label: 'Toggle Journalist', icon: Newspaper, category: 'Actions', action: (s) => {
@@ -1,5 +1,5 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
- import { Terminal, BookOpen, Radio, Plug, Globe, ArrowUpCircle, X, Unplug } from 'lucide-react';
2
+ import { Terminal, BookOpen, Radio, Plug, Globe, ArrowUpCircle, X, Unplug, Shield } from 'lucide-react';
3
3
  import { cn } from '../../lib/cn';
4
4
  import { StatusDot } from '../ui/status-dot';
5
5
  import { Badge } from '../ui/badge';
@@ -22,7 +22,6 @@ export function StatusBar({
22
22
  const updateReady = useGrooveStore((s) => s.updateReady);
23
23
  const updateProgress = useGrooveStore((s) => s.updateProgress);
24
24
  const setUpdateModalOpen = useGrooveStore((s) => s.setUpdateModalOpen);
25
- const subscription = useGrooveStore((s) => s.subscription);
26
25
  const navigate = useGrooveStore((s) => s.setActiveView);
27
26
  const activeTunnel = savedTunnels.find((t) => t.active);
28
27
  const electron = isElectron();
@@ -71,6 +70,13 @@ export function StatusBar({
71
70
  <span className="text-text-4">{activeTunnel.latencyMs}ms</span>
72
71
  )}
73
72
  </button>
73
+ <button
74
+ onClick={() => useGrooveStore.getState().addToWhitelist(activeTunnel.host)}
75
+ className="p-0.5 text-text-4 hover:text-accent cursor-pointer transition-colors rounded"
76
+ title="Add to Federation Whitelist"
77
+ >
78
+ <Shield size={10} />
79
+ </button>
74
80
  <button
75
81
  onClick={() => useGrooveStore.getState().disconnectTunnel(activeTunnel.id)}
76
82
  className="p-0.5 text-text-4 hover:text-danger cursor-pointer transition-colors rounded"
@@ -98,7 +104,7 @@ export function StatusBar({
98
104
  <span>Connect</span>
99
105
  </button>
100
106
  )}
101
- {subscription?.active && (subscription.plan === 'pro' || subscription.plan === 'team') && (
107
+ {connected && (
102
108
  <button
103
109
  onClick={() => navigate('federation')}
104
110
  className="flex items-center gap-1.5 text-text-4 hover:text-text-1 cursor-pointer transition-colors"
@@ -22,7 +22,7 @@ function statusBadge(status) {
22
22
  }
23
23
  }
24
24
 
25
- function WhitelistTab() {
25
+ export function WhitelistTab() {
26
26
  const whitelist = useGrooveStore((s) => s.federation.whitelist);
27
27
  const addToWhitelist = useGrooveStore((s) => s.addToWhitelist);
28
28
  const removeFromWhitelist = useGrooveStore((s) => s.removeFromWhitelist);
@@ -198,7 +198,7 @@ function WhitelistTab() {
198
198
  );
199
199
  }
200
200
 
201
- function AmbassadorsTab() {
201
+ export function AmbassadorsTab() {
202
202
  const ambassadors = useGrooveStore((s) => s.federation.ambassadors);
203
203
 
204
204
  return (
@@ -22,6 +22,8 @@ export function FederationPeers({ onOpenWizard }) {
22
22
  const connections = useGrooveStore((s) => s.federation.connections);
23
23
  const peers = useGrooveStore((s) => s.federation.peers);
24
24
  const sendPouch = useGrooveStore((s) => s.sendPouch);
25
+ const disconnectPeer = useGrooveStore((s) => s.disconnectPeer);
26
+ const addToast = useGrooveStore((s) => s.addToast);
25
27
  const [sendingTo, setSendingTo] = useState(null);
26
28
 
27
29
  const allPeers = peers.length > 0 ? peers : connections;
@@ -97,7 +99,12 @@ export function FederationPeers({ onOpenWizard }) {
97
99
  </div>
98
100
 
99
101
  <div className="flex items-center gap-1.5 mt-3 pt-2.5 border-t border-border-subtle">
100
- <Button size="sm" variant="ghost" className="h-6 text-2xs gap-1 text-text-2">
102
+ <Button
103
+ size="sm"
104
+ variant="ghost"
105
+ className="h-6 text-2xs gap-1 text-text-2"
106
+ onClick={() => addToast('info', name, `ID: ${id}\nIP: ${ip}${peer.port ? `:${peer.port}` : ''}\nLatency: ${latency != null ? `${latency}ms` : 'N/A'}\nState: ${state}`)}
107
+ >
101
108
  <Eye size={10} />
102
109
  Details
103
110
  </Button>
@@ -111,7 +118,12 @@ export function FederationPeers({ onOpenWizard }) {
111
118
  <Send size={10} />
112
119
  Send Pouch
113
120
  </Button>
114
- <Button size="sm" variant="ghost" className="h-6 text-2xs gap-1 text-danger/70 hover:text-danger ml-auto">
121
+ <Button
122
+ size="sm"
123
+ variant="ghost"
124
+ className="h-6 text-2xs gap-1 text-danger/70 hover:text-danger ml-auto"
125
+ onClick={() => disconnectPeer(id)}
126
+ >
115
127
  <Unplug size={10} />
116
128
  Disconnect
117
129
  </Button>
@@ -25,6 +25,15 @@ export function QuickConnect() {
25
25
  setConnectingId(id);
26
26
  try {
27
27
  await useGrooveStore.getState().connectTunnel(id);
28
+ const tunnel = savedTunnels.find((t) => t.id === id);
29
+ if (tunnel?.host) {
30
+ addToast('info', `Add ${tunnel.host} to Federation Whitelist?`, '', {
31
+ action: {
32
+ label: 'Add',
33
+ onClick: () => useGrooveStore.getState().addToWhitelist(tunnel.host),
34
+ },
35
+ });
36
+ }
28
37
  toggle();
29
38
  } catch (err) {
30
39
  let detail = err?.message || 'Unknown error';
@@ -2660,6 +2660,16 @@ export const useGrooveStore = create((set, get) => ({
2660
2660
  }
2661
2661
  },
2662
2662
 
2663
+ async disconnectPeer(peerId) {
2664
+ try {
2665
+ await api.delete(`/federation/peers/${encodeURIComponent(peerId)}`);
2666
+ get().addToast('info', 'Peer disconnected');
2667
+ get().fetchFederationStatus();
2668
+ } catch (err) {
2669
+ get().addToast('error', 'Disconnect failed', err.message);
2670
+ }
2671
+ },
2672
+
2663
2673
  // ── Training Data ─────────────────────────────────────────
2664
2674
 
2665
2675
  async setTrainingOptIn(enabled) {
@@ -1,37 +1,78 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
- import { useEffect } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { useGrooveStore } from '../stores/groove';
4
- import { FederationPanel } from '../components/settings/federation-panel';
5
- import { ProGate } from '../components/pro/pro-gate';
4
+ import { FederationPeers } from '../components/settings/federation-peers';
5
+ import { FederationActivity } from '../components/settings/federation-activity';
6
+ import { FederationWizard } from '../components/settings/federation-wizard';
7
+ import { WhitelistTab, AmbassadorsTab } from '../components/settings/federation-panel';
8
+ import { Button } from '../components/ui/button';
6
9
  import { ScrollArea } from '../components/ui/scroll-area';
7
- import { Globe } from 'lucide-react';
10
+ import { Globe, Plus } from 'lucide-react';
8
11
 
9
12
  export default function FederationView() {
10
13
  const fetchFederationStatus = useGrooveStore((s) => s.fetchFederationStatus);
14
+ const fetchPouchLog = useGrooveStore((s) => s.fetchPouchLog);
15
+ const peers = useGrooveStore((s) => s.federation.peers);
16
+ const connections = useGrooveStore((s) => s.federation.connections);
17
+ const whitelist = useGrooveStore((s) => s.federation.whitelist);
18
+ const [wizardOpen, setWizardOpen] = useState(false);
19
+
20
+ const peerCount = (peers.length > 0 ? peers : connections).length;
11
21
 
12
22
  useEffect(() => {
13
23
  fetchFederationStatus();
24
+ fetchPouchLog();
14
25
  }, []);
15
26
 
16
27
  return (
17
28
  <div className="flex flex-col h-full bg-surface-0">
18
- <div className="flex items-center gap-3 px-6 py-4 border-b border-border">
19
- <div className="w-8 h-8 rounded-md bg-accent/10 flex items-center justify-center">
20
- <Globe size={16} className="text-accent" />
21
- </div>
22
- <div>
23
- <h1 className="text-sm font-semibold text-text-0 font-sans">Federation</h1>
24
- <p className="text-2xs text-text-3 font-sans">Connect to remote Groove daemons</p>
29
+ {/* Hero strip */}
30
+ <div className="flex items-center justify-between px-6 py-4 border-b border-border">
31
+ <div className="flex items-center gap-3">
32
+ <div className="w-8 h-8 rounded-md bg-accent/10 flex items-center justify-center">
33
+ <Globe size={16} className="text-accent" />
34
+ </div>
35
+ <div>
36
+ <h1 className="text-sm font-semibold text-text-0 font-sans">Federation</h1>
37
+ <p className="text-2xs text-text-3 font-sans">
38
+ {peerCount} peer{peerCount !== 1 ? 's' : ''} &middot; {whitelist.length} whitelisted
39
+ </p>
40
+ </div>
25
41
  </div>
42
+ <Button size="sm" variant="primary" onClick={() => setWizardOpen(true)} className="h-8 text-xs gap-1.5">
43
+ <Plus size={12} />
44
+ Add Peer
45
+ </Button>
26
46
  </div>
27
47
 
48
+ {/* Content */}
28
49
  <ScrollArea className="flex-1">
29
- <div className="max-w-2xl mx-auto px-6 py-5">
30
- <ProGate feature="Federation" featureKey="federation" description="Daemon-to-daemon federation over Tailscale mesh for multi-machine agent coordination">
31
- <FederationPanel />
32
- </ProGate>
50
+ <div className="p-6">
51
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
52
+ {/* Left: Peers + Activity (stacked) */}
53
+ <div className="lg:col-span-2 space-y-4">
54
+ <div className="rounded-lg border border-border-subtle bg-surface-1 p-4">
55
+ <FederationPeers onOpenWizard={() => setWizardOpen(true)} />
56
+ </div>
57
+ <div className="rounded-lg border border-border-subtle bg-surface-1 p-4">
58
+ <FederationActivity />
59
+ </div>
60
+ </div>
61
+
62
+ {/* Right: Whitelist + Ambassadors (stacked) */}
63
+ <div className="space-y-4">
64
+ <div className="rounded-lg border border-border-subtle bg-surface-1 p-4">
65
+ <WhitelistTab />
66
+ </div>
67
+ <div className="rounded-lg border border-border-subtle bg-surface-1 p-4">
68
+ <AmbassadorsTab />
69
+ </div>
70
+ </div>
71
+ </div>
33
72
  </div>
34
73
  </ScrollArea>
74
+
75
+ <FederationWizard open={wizardOpen} onOpenChange={setWizardOpen} />
35
76
  </div>
36
77
  );
37
78
  }