groove-dev 0.27.197 → 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_5.25.30_PM.png +0 -0
- package/axom-integration/Screenshot_2026-07-25_at_5.33.26_PM.png +0 -0
- package/axom-integration/Screenshot_2026-07-25_at_6.11.53_PM.png +0 -0
- 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/api.js +2 -0
- package/node_modules/@groove-dev/daemon/src/axom-connector.js +368 -0
- package/node_modules/@groove-dev/daemon/src/axom-install.js +162 -0
- package/node_modules/@groove-dev/daemon/src/axom-remote.js +230 -0
- package/node_modules/@groove-dev/daemon/src/axom-server.js +249 -0
- package/node_modules/@groove-dev/daemon/src/federation.js +6 -0
- package/node_modules/@groove-dev/daemon/src/index.js +14 -0
- package/node_modules/@groove-dev/daemon/src/innerchat-docs.js +8 -1
- package/node_modules/@groove-dev/daemon/src/innerchat-relay.js +89 -0
- package/node_modules/@groove-dev/daemon/src/innerchat.js +261 -1
- package/node_modules/@groove-dev/daemon/src/introducer.js +1 -1
- package/node_modules/@groove-dev/daemon/src/network-guard.js +56 -0
- package/node_modules/@groove-dev/daemon/src/process.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/axom.js +66 -0
- package/node_modules/@groove-dev/daemon/src/providers/index.js +2 -0
- package/node_modules/@groove-dev/daemon/src/routes/axom.js +267 -0
- package/node_modules/@groove-dev/daemon/src/routes/innerchat.js +334 -20
- package/node_modules/@groove-dev/daemon/test/axom-connector.test.js +455 -0
- package/node_modules/@groove-dev/daemon/test/axom-remote.test.js +115 -0
- package/node_modules/@groove-dev/daemon/test/axom-server.test.js +228 -0
- package/node_modules/@groove-dev/daemon/test/innerchat-relay.test.js +251 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BbA-X4CE.js +1084 -0
- 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/App.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/app.css +53 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +3 -1
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +22 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/axom-slice.js +279 -0
- package/node_modules/@groove-dev/gui/src/views/axom.jsx +1897 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +232 -4
- package/package.json +2 -2
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +2 -0
- package/packages/daemon/src/axom-connector.js +368 -0
- package/packages/daemon/src/axom-install.js +162 -0
- package/packages/daemon/src/axom-remote.js +230 -0
- package/packages/daemon/src/axom-server.js +249 -0
- package/packages/daemon/src/federation.js +6 -0
- package/packages/daemon/src/index.js +14 -0
- package/packages/daemon/src/innerchat-docs.js +8 -1
- package/packages/daemon/src/innerchat-relay.js +89 -0
- package/packages/daemon/src/innerchat.js +261 -1
- package/packages/daemon/src/introducer.js +1 -1
- package/packages/daemon/src/network-guard.js +56 -0
- package/packages/daemon/src/process.js +1 -1
- package/packages/daemon/src/providers/axom.js +66 -0
- package/packages/daemon/src/providers/index.js +2 -0
- package/packages/daemon/src/routes/axom.js +267 -0
- package/packages/daemon/src/routes/innerchat.js +334 -20
- package/packages/gui/dist/assets/index-BbA-X4CE.js +1084 -0
- 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/App.jsx +2 -0
- package/packages/gui/src/app.css +53 -0
- package/packages/gui/src/components/agents/agent-feed.jsx +3 -1
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -1
- package/packages/gui/src/stores/groove.js +22 -0
- package/packages/gui/src/stores/slices/axom-slice.js +279 -0
- package/packages/gui/src/views/axom.jsx +1897 -0
- package/packages/gui/src/views/settings.jsx +232 -4
- package/node_modules/@groove-dev/gui/dist/assets/index-Da2nbd6M.js +0 -1043
- package/node_modules/@groove-dev/gui/dist/assets/index-zmrIbwNm.css +0 -1
- package/packages/gui/dist/assets/index-Da2nbd6M.js +0 -1043
- package/packages/gui/dist/assets/index-zmrIbwNm.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 ────────────────────────────────────────────────── */
|
|
@@ -52,6 +52,15 @@ const KEY_PLACEHOLDERS = {
|
|
|
52
52
|
grok: 'xai-...',
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
// installCommand is a string for most providers, but some (Ollama) return
|
|
56
|
+
// {command, alt, platform} — normalize so an object can never reach JSX as a
|
|
57
|
+
// child (React #31 white-screens the whole tab).
|
|
58
|
+
function installCommandText(installCommand) {
|
|
59
|
+
if (!installCommand) return null;
|
|
60
|
+
if (typeof installCommand === 'string') return installCommand;
|
|
61
|
+
return installCommand.command || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
function ProviderCard({ provider, onKeyChange }) {
|
|
56
65
|
const [settingKey, setSettingKey] = useState(false);
|
|
57
66
|
const [keyInput, setKeyInput] = useState('');
|
|
@@ -182,15 +191,15 @@ function ProviderCard({ provider, onKeyChange }) {
|
|
|
182
191
|
</Button>
|
|
183
192
|
|
|
184
193
|
{/* Manual install command */}
|
|
185
|
-
{provider.installCommand && (
|
|
194
|
+
{installCommandText(provider.installCommand) && (
|
|
186
195
|
<div className="space-y-1">
|
|
187
196
|
<p className="text-2xs text-text-4 font-sans">Or install manually in your terminal:</p>
|
|
188
197
|
<div className="flex items-center gap-1">
|
|
189
198
|
<code className="flex-1 px-2 py-1.5 bg-surface-0 border border-border-subtle rounded text-2xs font-mono text-text-2 select-all">
|
|
190
|
-
{provider.installCommand}
|
|
199
|
+
{installCommandText(provider.installCommand)}
|
|
191
200
|
</code>
|
|
192
201
|
<button
|
|
193
|
-
onClick={() => { navigator.clipboard.writeText(provider.installCommand); addToast('success', 'Copied'); }}
|
|
202
|
+
onClick={() => { navigator.clipboard.writeText(installCommandText(provider.installCommand)); addToast('success', 'Copied'); }}
|
|
194
203
|
className="p-1.5 text-text-4 hover:text-text-2 cursor-pointer"
|
|
195
204
|
>
|
|
196
205
|
<Copy size={10} />
|
|
@@ -1491,6 +1500,222 @@ function TrainingDataSection() {
|
|
|
1491
1500
|
);
|
|
1492
1501
|
}
|
|
1493
1502
|
|
|
1503
|
+
/* ── InnerChat Peers Section ──────────────────────────────── */
|
|
1504
|
+
|
|
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.
|
|
1510
|
+
function InnerChatPeersSection() {
|
|
1511
|
+
const addToast = useGrooveStore((s) => s.addToast);
|
|
1512
|
+
const [peers, setPeers] = useState([]);
|
|
1513
|
+
const [identity, setIdentity] = useState(null);
|
|
1514
|
+
const [loading, setLoading] = useState(true);
|
|
1515
|
+
const [adding, setAdding] = useState(false);
|
|
1516
|
+
const [form, setForm] = useState({ alias: '', url: '' });
|
|
1517
|
+
const [busy, setBusy] = useState(false);
|
|
1518
|
+
const [tests, setTests] = useState({});
|
|
1519
|
+
|
|
1520
|
+
const loadPeers = () => api.get('/innerchat/peers')
|
|
1521
|
+
.then((d) => setPeers(Array.isArray(d?.peers) ? d.peers : []))
|
|
1522
|
+
.catch(() => {});
|
|
1523
|
+
|
|
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();
|
|
1532
|
+
|
|
1533
|
+
async function addPeer() {
|
|
1534
|
+
if (!canSubmit) return;
|
|
1535
|
+
setBusy(true);
|
|
1536
|
+
try {
|
|
1537
|
+
const d = await api.post('/innerchat/peers', { alias: form.alias.trim(), url: form.url.trim() });
|
|
1538
|
+
setPeers(Array.isArray(d?.peers) ? d.peers : []);
|
|
1539
|
+
setForm({ alias: '', url: '' });
|
|
1540
|
+
setAdding(false);
|
|
1541
|
+
addToast(d.keyPushed ? 'success' : 'warning', `Connected to ${form.alias.trim()}`, d.note);
|
|
1542
|
+
testPeer(form.alias.trim());
|
|
1543
|
+
} catch (err) {
|
|
1544
|
+
addToast('error', 'Could not connect', err.message);
|
|
1545
|
+
} finally {
|
|
1546
|
+
setBusy(false);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
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
|
+
|
|
1560
|
+
async function removePeer(alias) {
|
|
1561
|
+
try {
|
|
1562
|
+
const d = await api.delete(`/innerchat/peers/${encodeURIComponent(alias)}`);
|
|
1563
|
+
setPeers(Array.isArray(d?.peers) ? d.peers : []);
|
|
1564
|
+
addToast('info', `Removed peer "${alias}"`);
|
|
1565
|
+
} catch (err) {
|
|
1566
|
+
addToast('error', 'Remove failed', err.message);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
return (
|
|
1571
|
+
<div>
|
|
1572
|
+
<div className="flex items-center gap-2 mb-2.5 px-0.5">
|
|
1573
|
+
<span className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">InnerChat Peers</span>
|
|
1574
|
+
<div className="flex-1 h-px bg-border-subtle" />
|
|
1575
|
+
<span className="text-2xs text-text-4 font-sans">
|
|
1576
|
+
{peers.length} peer{peers.length !== 1 ? 's' : ''} · agents address them as <code className="text-text-3">name@peer</code>
|
|
1577
|
+
</span>
|
|
1578
|
+
</div>
|
|
1579
|
+
|
|
1580
|
+
<div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5 flex flex-col gap-3">
|
|
1581
|
+
<div className="flex items-start gap-2.5">
|
|
1582
|
+
<div className="w-6 h-6 rounded bg-indigo/10 flex items-center justify-center flex-shrink-0 mt-0.5">
|
|
1583
|
+
<Share2 size={12} className="text-indigo" />
|
|
1584
|
+
</div>
|
|
1585
|
+
<div className="flex-1 min-w-0">
|
|
1586
|
+
<div className="text-[13px] font-medium text-text-0 font-sans leading-tight">Let agents talk across machines</div>
|
|
1587
|
+
<div className="text-2xs text-text-4 font-sans leading-relaxed mt-0.5">
|
|
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.
|
|
1590
|
+
</div>
|
|
1591
|
+
</div>
|
|
1592
|
+
</div>
|
|
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
|
+
|
|
1620
|
+
{/* Peer list */}
|
|
1621
|
+
{loading ? (
|
|
1622
|
+
<Skeleton className="h-10 rounded-md" />
|
|
1623
|
+
) : peers.length > 0 ? (
|
|
1624
|
+
<div className="flex flex-col gap-1.5">
|
|
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
|
+
})}
|
|
1671
|
+
</div>
|
|
1672
|
+
) : (
|
|
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>
|
|
1674
|
+
)}
|
|
1675
|
+
|
|
1676
|
+
{/* Add form — name + URL only; id and keys are handled for you */}
|
|
1677
|
+
{adding ? (
|
|
1678
|
+
<div className="flex flex-col gap-2 pt-1 border-t border-border-subtle">
|
|
1679
|
+
<div className="grid grid-cols-[1fr_2fr] gap-2">
|
|
1680
|
+
<input
|
|
1681
|
+
value={form.alias}
|
|
1682
|
+
onChange={(e) => setForm((f) => ({ ...f, alias: e.target.value }))}
|
|
1683
|
+
placeholder="name (e.g. spark)"
|
|
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"
|
|
1685
|
+
/>
|
|
1686
|
+
<input
|
|
1687
|
+
value={form.url}
|
|
1688
|
+
onChange={(e) => setForm((f) => ({ ...f, url: e.target.value }))}
|
|
1689
|
+
onKeyDown={(e) => { if (e.key === 'Enter' && canSubmit && !busy) addPeer(); }}
|
|
1690
|
+
placeholder="http://127.0.0.1:31416"
|
|
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"
|
|
1692
|
+
/>
|
|
1693
|
+
</div>
|
|
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>
|
|
1698
|
+
<div className="flex items-center justify-end gap-2">
|
|
1699
|
+
<Button variant="ghost" size="sm" onClick={() => { setAdding(false); setForm({ alias: '', url: '' }); }} disabled={busy}>Cancel</Button>
|
|
1700
|
+
<Button variant="primary" size="sm" onClick={addPeer} disabled={!canSubmit || busy}>
|
|
1701
|
+
{busy ? <Loader2 size={12} className="animate-spin" /> : <Check size={12} />}
|
|
1702
|
+
Connect
|
|
1703
|
+
</Button>
|
|
1704
|
+
</div>
|
|
1705
|
+
</div>
|
|
1706
|
+
) : (
|
|
1707
|
+
<button
|
|
1708
|
+
onClick={() => setAdding(true)}
|
|
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"
|
|
1710
|
+
>
|
|
1711
|
+
<Plus size={12} />
|
|
1712
|
+
Add Peer Machine
|
|
1713
|
+
</button>
|
|
1714
|
+
)}
|
|
1715
|
+
</div>
|
|
1716
|
+
</div>
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1494
1719
|
/* ── Early Access Section ─────────────────────────────────── */
|
|
1495
1720
|
|
|
1496
1721
|
function EarlyAccessSection() {
|
|
@@ -1791,6 +2016,9 @@ export default function SettingsView() {
|
|
|
1791
2016
|
</div>
|
|
1792
2017
|
)}
|
|
1793
2018
|
|
|
2019
|
+
{/* ═══════ INNERCHAT PEERS ═══════ */}
|
|
2020
|
+
<InnerChatPeersSection />
|
|
2021
|
+
|
|
1794
2022
|
{/* ═══════ EARLY ACCESS ═══════ */}
|
|
1795
2023
|
<EarlyAccessSection />
|
|
1796
2024
|
|