groove-dev 0.27.198 → 0.27.199
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/axom-integration/Screenshot_2026-07-25_at_6.44.56_PM.png +0 -0
- package/axom-integration/Screenshot_2026-07-25_at_6.45.35_PM.png +0 -0
- 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/axom-connector.js +30 -2
- package/node_modules/@groove-dev/daemon/src/axom-install.js +24 -2
- package/node_modules/@groove-dev/daemon/src/axom-remote.js +230 -0
- package/node_modules/@groove-dev/daemon/src/axom-server.js +22 -2
- package/node_modules/@groove-dev/daemon/src/index.js +3 -0
- package/node_modules/@groove-dev/daemon/src/routes/axom.js +73 -2
- package/node_modules/@groove-dev/daemon/src/routes/innerchat.js +190 -8
- package/node_modules/@groove-dev/daemon/test/axom-connector.test.js +44 -1
- package/node_modules/@groove-dev/daemon/test/axom-remote.test.js +115 -0
- package/node_modules/@groove-dev/daemon/test/axom-server.test.js +50 -9
- package/node_modules/@groove-dev/gui/dist/assets/{index-b9dKN6cq.js → index-BbA-X4CE.js} +243 -233
- package/node_modules/@groove-dev/gui/dist/assets/index-BbE3qX83.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/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +3 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +4 -1
- package/node_modules/@groove-dev/gui/src/stores/slices/axom-slice.js +83 -1
- package/node_modules/@groove-dev/gui/src/views/axom.jsx +1147 -310
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +123 -70
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/axom-connector.js +30 -2
- package/packages/daemon/src/axom-install.js +24 -2
- package/packages/daemon/src/axom-remote.js +230 -0
- package/packages/daemon/src/axom-server.js +22 -2
- package/packages/daemon/src/index.js +3 -0
- package/packages/daemon/src/routes/axom.js +73 -2
- package/packages/daemon/src/routes/innerchat.js +190 -8
- package/packages/gui/dist/assets/{index-b9dKN6cq.js → index-BbA-X4CE.js} +243 -233
- package/packages/gui/dist/assets/index-BbE3qX83.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +3 -1
- package/packages/gui/src/stores/groove.js +4 -1
- package/packages/gui/src/stores/slices/axom-slice.js +83 -1
- package/packages/gui/src/views/axom.jsx +1147 -310
- package/packages/gui/src/views/settings.jsx +123 -70
- package/node_modules/@groove-dev/gui/dist/assets/index-RbtaI6l7.css +0 -1
- package/packages/gui/dist/assets/index-RbtaI6l7.css +0 -1
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
ShieldCheck, Settings, Lock, Database, Shield,
|
|
22
22
|
Newspaper, Radio, Send, MessageSquare, MessageCircle,
|
|
23
23
|
Plus, Trash2, Plug, PlugZap, TestTube, X, HelpCircle, ExternalLink,
|
|
24
|
-
Sparkles, Share2, Gift,
|
|
24
|
+
Sparkles, Share2, Gift, AlertCircle,
|
|
25
25
|
} from 'lucide-react';
|
|
26
26
|
|
|
27
27
|
/* ── Toggle ────────────────────────────────────────────────── */
|
|
@@ -1502,52 +1502,61 @@ function TrainingDataSection() {
|
|
|
1502
1502
|
|
|
1503
1503
|
/* ── InnerChat Peers Section ──────────────────────────────── */
|
|
1504
1504
|
|
|
1505
|
-
// Cross-daemon InnerChat: agents
|
|
1506
|
-
//
|
|
1507
|
-
//
|
|
1508
|
-
//
|
|
1509
|
-
//
|
|
1510
|
-
// to the other side.
|
|
1505
|
+
// Cross-daemon InnerChat: agents here reach a peer's agents as `name@alias`.
|
|
1506
|
+
// A peer is another Groove daemon at any URL you can already reach (a
|
|
1507
|
+
// tunnel-forwarded local port, a Tailscale address). Adding one discovers its
|
|
1508
|
+
// id and trades public keys automatically, so the user supplies only a name
|
|
1509
|
+
// and a URL — everything else this panel shows rather than asks for.
|
|
1511
1510
|
function InnerChatPeersSection() {
|
|
1512
1511
|
const addToast = useGrooveStore((s) => s.addToast);
|
|
1513
1512
|
const [peers, setPeers] = useState([]);
|
|
1514
|
-
const [
|
|
1513
|
+
const [identity, setIdentity] = useState(null);
|
|
1515
1514
|
const [loading, setLoading] = useState(true);
|
|
1516
1515
|
const [adding, setAdding] = useState(false);
|
|
1517
|
-
const [form, setForm] = useState({ alias: '', url: ''
|
|
1516
|
+
const [form, setForm] = useState({ alias: '', url: '' });
|
|
1518
1517
|
const [busy, setBusy] = useState(false);
|
|
1518
|
+
const [tests, setTests] = useState({});
|
|
1519
1519
|
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
.catch(() => {})
|
|
1524
|
-
.finally(() => setLoading(false));
|
|
1525
|
-
api.get('/federation').then((d) => setOwnId(d?.id || null)).catch(() => {});
|
|
1526
|
-
};
|
|
1527
|
-
useEffect(() => { load(); }, []);
|
|
1520
|
+
const loadPeers = () => api.get('/innerchat/peers')
|
|
1521
|
+
.then((d) => setPeers(Array.isArray(d?.peers) ? d.peers : []))
|
|
1522
|
+
.catch(() => {});
|
|
1528
1523
|
|
|
1529
|
-
|
|
1524
|
+
useEffect(() => {
|
|
1525
|
+
Promise.all([
|
|
1526
|
+
loadPeers(),
|
|
1527
|
+
api.get('/innerchat/identity').then(setIdentity).catch(() => {}),
|
|
1528
|
+
]).finally(() => setLoading(false));
|
|
1529
|
+
}, []);
|
|
1530
|
+
|
|
1531
|
+
const canSubmit = form.alias.trim() && form.url.trim();
|
|
1530
1532
|
|
|
1531
1533
|
async function addPeer() {
|
|
1532
1534
|
if (!canSubmit) return;
|
|
1533
1535
|
setBusy(true);
|
|
1534
1536
|
try {
|
|
1535
|
-
const d = await api.post('/innerchat/peers', {
|
|
1536
|
-
alias: form.alias.trim(),
|
|
1537
|
-
url: form.url.trim(),
|
|
1538
|
-
daemonId: form.daemonId.trim().toLowerCase(),
|
|
1539
|
-
});
|
|
1537
|
+
const d = await api.post('/innerchat/peers', { alias: form.alias.trim(), url: form.url.trim() });
|
|
1540
1538
|
setPeers(Array.isArray(d?.peers) ? d.peers : []);
|
|
1541
|
-
setForm({ alias: '', url: ''
|
|
1539
|
+
setForm({ alias: '', url: '' });
|
|
1542
1540
|
setAdding(false);
|
|
1543
|
-
addToast('success', `
|
|
1541
|
+
addToast(d.keyPushed ? 'success' : 'warning', `Connected to ${form.alias.trim()}`, d.note);
|
|
1542
|
+
testPeer(form.alias.trim());
|
|
1544
1543
|
} catch (err) {
|
|
1545
|
-
addToast('error', 'Could not
|
|
1544
|
+
addToast('error', 'Could not connect', err.message);
|
|
1546
1545
|
} finally {
|
|
1547
1546
|
setBusy(false);
|
|
1548
1547
|
}
|
|
1549
1548
|
}
|
|
1550
1549
|
|
|
1550
|
+
async function testPeer(alias) {
|
|
1551
|
+
setTests((t) => ({ ...t, [alias]: { testing: true } }));
|
|
1552
|
+
try {
|
|
1553
|
+
const r = await api.get(`/innerchat/peers/${encodeURIComponent(alias)}/test`);
|
|
1554
|
+
setTests((t) => ({ ...t, [alias]: r }));
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
setTests((t) => ({ ...t, [alias]: { ok: false, error: err.message } }));
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1551
1560
|
async function removePeer(alias) {
|
|
1552
1561
|
try {
|
|
1553
1562
|
const d = await api.delete(`/innerchat/peers/${encodeURIComponent(alias)}`);
|
|
@@ -1564,88 +1573,133 @@ function InnerChatPeersSection() {
|
|
|
1564
1573
|
<span className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">InnerChat Peers</span>
|
|
1565
1574
|
<div className="flex-1 h-px bg-border-subtle" />
|
|
1566
1575
|
<span className="text-2xs text-text-4 font-sans">
|
|
1567
|
-
{peers.length} peer{peers.length !== 1 ? 's' : ''} · address as <code className="text-text-3">name@
|
|
1576
|
+
{peers.length} peer{peers.length !== 1 ? 's' : ''} · agents address them as <code className="text-text-3">name@peer</code>
|
|
1568
1577
|
</span>
|
|
1569
1578
|
</div>
|
|
1570
1579
|
|
|
1571
1580
|
<div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5 flex flex-col gap-3">
|
|
1572
|
-
{/* This daemon's identity — to hand to the peer machine */}
|
|
1573
1581
|
<div className="flex items-start gap-2.5">
|
|
1574
1582
|
<div className="w-6 h-6 rounded bg-indigo/10 flex items-center justify-center flex-shrink-0 mt-0.5">
|
|
1575
1583
|
<Share2 size={12} className="text-indigo" />
|
|
1576
1584
|
</div>
|
|
1577
1585
|
<div className="flex-1 min-w-0">
|
|
1578
|
-
<div className="text-[13px] font-medium text-text-0 font-sans leading-tight">
|
|
1586
|
+
<div className="text-[13px] font-medium text-text-0 font-sans leading-tight">Let agents talk across machines</div>
|
|
1579
1587
|
<div className="text-2xs text-text-4 font-sans leading-relaxed mt-0.5">
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
</div>
|
|
1583
|
-
<div className="flex items-center gap-1.5 mt-1.5">
|
|
1584
|
-
<code className="h-7 px-2 flex items-center bg-surface-0 border border-border-subtle rounded-md text-2xs font-mono text-text-2 truncate min-w-0">
|
|
1585
|
-
{ownId || '—'}
|
|
1586
|
-
</code>
|
|
1587
|
-
{ownId && (
|
|
1588
|
-
<Button variant="secondary" size="sm" onClick={() => { navigator.clipboard.writeText(ownId); addToast('success', 'Copied daemon id'); }} className="h-7 px-2 flex-shrink-0">
|
|
1589
|
-
<Copy size={11} />
|
|
1590
|
-
</Button>
|
|
1591
|
-
)}
|
|
1588
|
+
Add another Groove machine below and your agents can consult its agents directly as{' '}
|
|
1589
|
+
<code className="text-text-3">name@peer</code>. Keys are exchanged for you — nothing else to set up.
|
|
1592
1590
|
</div>
|
|
1593
1591
|
</div>
|
|
1594
1592
|
</div>
|
|
1595
1593
|
|
|
1594
|
+
{/* This machine — what the OTHER side needs */}
|
|
1595
|
+
<div className="rounded-md bg-surface-0 border border-border-subtle px-3 py-2.5 flex flex-col gap-2">
|
|
1596
|
+
<div className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">This machine</div>
|
|
1597
|
+
<div className="flex items-center gap-2">
|
|
1598
|
+
<span className="text-2xs text-text-4 font-sans w-16 flex-shrink-0">Daemon ID</span>
|
|
1599
|
+
<code className="flex-1 text-2xs font-mono text-text-2 truncate min-w-0">{identity?.daemonId || '—'}</code>
|
|
1600
|
+
{identity?.daemonId && (
|
|
1601
|
+
<button
|
|
1602
|
+
onClick={() => { navigator.clipboard.writeText(identity.daemonId); addToast('success', 'Copied'); }}
|
|
1603
|
+
className="text-text-4 hover:text-accent transition-colors flex-shrink-0 cursor-pointer"
|
|
1604
|
+
title="Copy daemon id"
|
|
1605
|
+
>
|
|
1606
|
+
<Copy size={11} />
|
|
1607
|
+
</button>
|
|
1608
|
+
)}
|
|
1609
|
+
</div>
|
|
1610
|
+
<div className="flex items-start gap-2">
|
|
1611
|
+
<span className="text-2xs text-text-4 font-sans w-16 flex-shrink-0 pt-px">Reach me</span>
|
|
1612
|
+
<span className="flex-1 text-2xs text-text-3 font-sans leading-relaxed min-w-0">
|
|
1613
|
+
On this machine: <code className="text-text-2">http://127.0.0.1:{identity?.port || 31415}</code>.
|
|
1614
|
+
From another machine, use whatever address forwards here — an SSH tunnel's local port on that side,
|
|
1615
|
+
or this host's Tailscale address.
|
|
1616
|
+
</span>
|
|
1617
|
+
</div>
|
|
1618
|
+
</div>
|
|
1619
|
+
|
|
1596
1620
|
{/* Peer list */}
|
|
1597
1621
|
{loading ? (
|
|
1598
1622
|
<Skeleton className="h-10 rounded-md" />
|
|
1599
1623
|
) : peers.length > 0 ? (
|
|
1600
1624
|
<div className="flex flex-col gap-1.5">
|
|
1601
|
-
{peers.map((p) =>
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
<
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1625
|
+
{peers.map((p) => {
|
|
1626
|
+
const t = tests[p.alias];
|
|
1627
|
+
return (
|
|
1628
|
+
<div key={p.alias} className="rounded-md bg-surface-0 border border-border-subtle px-2.5 py-2 flex flex-col gap-1.5">
|
|
1629
|
+
<div className="flex items-center gap-2.5">
|
|
1630
|
+
<MessageCircle size={12} className="text-indigo flex-shrink-0" />
|
|
1631
|
+
<Badge variant="default" className="font-mono flex-shrink-0 normal-case">@{p.alias}</Badge>
|
|
1632
|
+
<code className="text-2xs font-mono text-text-2 truncate min-w-0 flex-1">{p.url}</code>
|
|
1633
|
+
{t?.testing ? (
|
|
1634
|
+
<Loader2 size={11} className="animate-spin text-text-3 flex-shrink-0" />
|
|
1635
|
+
) : t ? (
|
|
1636
|
+
<Badge variant={t.ok ? 'success' : 'danger'} className="flex-shrink-0">
|
|
1637
|
+
{t.ok ? <Check size={9} /> : <AlertCircle size={9} />}
|
|
1638
|
+
{t.ok ? 'Connected' : 'Problem'}
|
|
1639
|
+
</Badge>
|
|
1640
|
+
) : null}
|
|
1641
|
+
<button
|
|
1642
|
+
onClick={() => testPeer(p.alias)}
|
|
1643
|
+
className="text-text-4 hover:text-accent transition-colors flex-shrink-0 cursor-pointer"
|
|
1644
|
+
title="Test connection"
|
|
1645
|
+
>
|
|
1646
|
+
<TestTube size={12} />
|
|
1647
|
+
</button>
|
|
1648
|
+
<button
|
|
1649
|
+
onClick={() => removePeer(p.alias)}
|
|
1650
|
+
className="text-text-4 hover:text-danger transition-colors flex-shrink-0 cursor-pointer"
|
|
1651
|
+
title={`Remove ${p.alias}`}
|
|
1652
|
+
>
|
|
1653
|
+
<Trash2 size={12} />
|
|
1654
|
+
</button>
|
|
1655
|
+
</div>
|
|
1656
|
+
{t && !t.testing && (
|
|
1657
|
+
<div className="text-2xs font-sans pl-6 leading-relaxed">
|
|
1658
|
+
{t.ok ? (
|
|
1659
|
+
<span className="text-text-4">
|
|
1660
|
+
v{t.peerVersion} · {t.agents?.length || 0} agent{t.agents?.length !== 1 ? 's' : ''}
|
|
1661
|
+
{t.agents?.length ? <> — try <code className="text-text-3">{t.agents[0]}@{p.alias}</code></> : null}
|
|
1662
|
+
</span>
|
|
1663
|
+
) : (
|
|
1664
|
+
<span className="text-danger">{t.error}</span>
|
|
1665
|
+
)}
|
|
1666
|
+
</div>
|
|
1667
|
+
)}
|
|
1668
|
+
</div>
|
|
1669
|
+
);
|
|
1670
|
+
})}
|
|
1616
1671
|
</div>
|
|
1617
1672
|
) : (
|
|
1618
|
-
<div className="text-2xs text-text-4 font-sans px-1 py-1">No peers
|
|
1673
|
+
<div className="text-2xs text-text-4 font-sans px-1 py-1">No peers yet — add one to let agents talk across machines.</div>
|
|
1619
1674
|
)}
|
|
1620
1675
|
|
|
1621
|
-
{/* Add form */}
|
|
1676
|
+
{/* Add form — name + URL only; id and keys are handled for you */}
|
|
1622
1677
|
{adding ? (
|
|
1623
1678
|
<div className="flex flex-col gap-2 pt-1 border-t border-border-subtle">
|
|
1624
1679
|
<div className="grid grid-cols-[1fr_2fr] gap-2">
|
|
1625
1680
|
<input
|
|
1626
1681
|
value={form.alias}
|
|
1627
1682
|
onChange={(e) => setForm((f) => ({ ...f, alias: e.target.value }))}
|
|
1628
|
-
placeholder="
|
|
1683
|
+
placeholder="name (e.g. spark)"
|
|
1629
1684
|
className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
1630
1685
|
/>
|
|
1631
1686
|
<input
|
|
1632
1687
|
value={form.url}
|
|
1633
1688
|
onChange={(e) => setForm((f) => ({ ...f, url: e.target.value }))}
|
|
1634
|
-
|
|
1689
|
+
onKeyDown={(e) => { if (e.key === 'Enter' && canSubmit && !busy) addPeer(); }}
|
|
1690
|
+
placeholder="http://127.0.0.1:31416"
|
|
1635
1691
|
className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
1636
1692
|
/>
|
|
1637
1693
|
</div>
|
|
1638
|
-
<
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
1643
|
-
/>
|
|
1694
|
+
<div className="text-2xs text-text-4 font-sans px-0.5 leading-relaxed">
|
|
1695
|
+
The URL is where that machine is reachable <em>from here</em> — if you connect over an SSH tunnel that is
|
|
1696
|
+
the forwarded local port (not 31415, which is this machine).
|
|
1697
|
+
</div>
|
|
1644
1698
|
<div className="flex items-center justify-end gap-2">
|
|
1645
|
-
<Button variant="ghost" size="sm" onClick={() => { setAdding(false); setForm({ alias: '', url: ''
|
|
1699
|
+
<Button variant="ghost" size="sm" onClick={() => { setAdding(false); setForm({ alias: '', url: '' }); }} disabled={busy}>Cancel</Button>
|
|
1646
1700
|
<Button variant="primary" size="sm" onClick={addPeer} disabled={!canSubmit || busy}>
|
|
1647
1701
|
{busy ? <Loader2 size={12} className="animate-spin" /> : <Check size={12} />}
|
|
1648
|
-
|
|
1702
|
+
Connect
|
|
1649
1703
|
</Button>
|
|
1650
1704
|
</div>
|
|
1651
1705
|
</div>
|
|
@@ -1655,14 +1709,13 @@ function InnerChatPeersSection() {
|
|
|
1655
1709
|
className="flex items-center justify-center gap-1.5 h-8 rounded-md border border-dashed border-border-subtle text-2xs font-sans text-text-3 hover:text-accent hover:border-accent/40 transition-colors cursor-pointer"
|
|
1656
1710
|
>
|
|
1657
1711
|
<Plus size={12} />
|
|
1658
|
-
Add Peer
|
|
1712
|
+
Add Peer Machine
|
|
1659
1713
|
</button>
|
|
1660
1714
|
)}
|
|
1661
1715
|
</div>
|
|
1662
1716
|
</div>
|
|
1663
1717
|
);
|
|
1664
1718
|
}
|
|
1665
|
-
|
|
1666
1719
|
/* ── Early Access Section ─────────────────────────────────── */
|
|
1667
1720
|
|
|
1668
1721
|
function EarlyAccessSection() {
|