@sleep2agi/agent-network-dashboard 0.4.5-preview.7 → 0.4.5-preview.71
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +49 -49
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +4 -4
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page.js.nft.json +1 -1
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +4 -4
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +4 -4
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page.js.nft.json +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +4 -4
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page.js.nft.json +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +4 -4
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page.js.nft.json +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +4 -4
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page.js.nft.json +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +4 -4
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +4 -4
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page.js.nft.json +1 -1
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +4 -4
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +4 -4
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +4 -4
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page.js.nft.json +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +4 -4
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__02ee4km._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02ee4km._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n4l2is._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n4l2is._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js.map +1 -1
- package/.next/server/chunks/ssr/_0nne7mz._.js +3 -0
- package/.next/server/chunks/ssr/_0nne7mz._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0.zbtn5._.js +9 -0
- package/.next/server/chunks/ssr/app_0.zbtn5._.js.map +1 -0
- package/.next/server/chunks/ssr/app_01y-~mp._.js +3 -0
- package/.next/server/chunks/ssr/app_01y-~mp._.js.map +1 -0
- package/.next/server/chunks/ssr/app_04n78pr._.js +3 -0
- package/.next/server/chunks/ssr/app_04n78pr._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0_agk0r._.js +3 -0
- package/.next/server/chunks/ssr/app_0_agk0r._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0aum_g5._.js +3 -0
- package/.next/server/chunks/ssr/app_0aum_g5._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0esuljp._.js +3 -0
- package/.next/server/chunks/ssr/app_0esuljp._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0izi28j._.js +1 -1
- package/.next/server/chunks/ssr/app_0izi28j._.js.map +1 -1
- package/.next/server/chunks/ssr/app_0py1sns._.js +3 -0
- package/.next/server/chunks/ssr/app_0py1sns._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0tncr1_._.js +3 -0
- package/.next/server/chunks/ssr/app_0tncr1_._.js.map +1 -0
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js +5 -5
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -1
- package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js +1 -1
- package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js.map +1 -1
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +4 -4
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/013_kw~b7_tqy.js +1 -0
- package/.next/static/chunks/04dbz0h45njew.js +1 -0
- package/.next/static/chunks/06uku.~mtk9zz.js +1 -0
- package/.next/static/chunks/07gtk5bhf8gn9.js +1 -0
- package/.next/static/chunks/08scyy1.s_5my.js +1 -0
- package/.next/static/chunks/09oxaqu0qn3c9.js +1 -0
- package/.next/static/chunks/0b8qzoyn-7e~f.js +1 -0
- package/.next/static/chunks/{07okhs5561y5y.js → 0c1bp~g7.8ru..js} +1 -1
- package/.next/static/chunks/0dj5~edkdwh1n.js +1 -0
- package/.next/static/chunks/0glm3gl882al6.js +1 -0
- package/.next/static/chunks/0gnf.w4c8z.l~.js +1 -0
- package/.next/static/chunks/0iwn.7hiigyrz.js +7 -0
- package/.next/static/chunks/0l8112gmzl31s.js +1 -0
- package/.next/static/chunks/0rk5zmyp19fv8.js +7 -0
- package/.next/static/chunks/0uh.dd0_.y_dh.js +1 -0
- package/.next/static/chunks/0vn.u7tcybvik.js +1 -0
- package/.next/static/chunks/13u~uqoww_0ib.css +2 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/admin/page.tsx +90 -26
- package/app/components/AgentCard.tsx +19 -5
- package/app/components/BroadcastBar.tsx +22 -4
- package/app/components/DispatchPanel.tsx +9 -5
- package/app/components/EmptyState.tsx +77 -4
- package/app/components/HealthBanner.tsx +11 -2
- package/app/components/HelpOverlay.tsx +50 -10
- package/app/components/InboxPanel.tsx +7 -4
- package/app/components/Sidebar.tsx +49 -41
- package/app/components/StatsBar.tsx +59 -36
- package/app/components/TaskChatPanel.tsx +8 -10
- package/app/components/TaskDrawer.tsx +72 -28
- package/app/components/TopoGraph.tsx +118 -24
- package/app/components/utils.ts +13 -0
- package/app/globals.css +90 -0
- package/app/lib/status.ts +63 -0
- package/app/lib/version.ts +7 -0
- package/app/login/page.tsx +40 -7
- package/app/logs/page.tsx +70 -21
- package/app/messages/page.tsx +24 -7
- package/app/node/page.tsx +48 -25
- package/app/nodes/page.tsx +37 -9
- package/app/page.tsx +107 -36
- package/app/server-logs/page.tsx +97 -25
- package/app/settings/networks/page.tsx +37 -13
- package/app/settings/page.tsx +39 -11
- package/app/tasks/[id]/page.tsx +2 -13
- package/app/tasks/page.tsx +66 -73
- package/package.json +1 -1
- package/scripts/fold-only.mjs +44 -0
- package/scripts/loop-review.mjs +226 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__06pu~4t._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__06pu~4t._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js.map +0 -1
- package/.next/server/chunks/ssr/_0uw5cjt._.js +0 -3
- package/.next/server/chunks/ssr/_0uw5cjt._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0.0wpa~._.js +0 -3
- package/.next/server/chunks/ssr/app_0.0wpa~._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0ap_oiq._.js +0 -3
- package/.next/server/chunks/ssr/app_0ap_oiq._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0dk5wao._.js +0 -3
- package/.next/server/chunks/ssr/app_0dk5wao._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0et5yua._.js +0 -3
- package/.next/server/chunks/ssr/app_0et5yua._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0k8xbx~._.js +0 -9
- package/.next/server/chunks/ssr/app_0k8xbx~._.js.map +0 -1
- package/.next/server/chunks/ssr/app_11vl967._.js +0 -3
- package/.next/server/chunks/ssr/app_11vl967._.js.map +0 -1
- package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +0 -3
- package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +0 -1
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +0 -3
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +0 -1
- package/.next/static/chunks/02flrewxlf_m_.js +0 -1
- package/.next/static/chunks/051gslfjka1kj.js +0 -1
- package/.next/static/chunks/06t_vzei8s9_h.js +0 -1
- package/.next/static/chunks/07oobuohrpigy.js +0 -1
- package/.next/static/chunks/09al3qm76q7nd.js +0 -1
- package/.next/static/chunks/0e7c40p~wq665.js +0 -1
- package/.next/static/chunks/0g7m7zk.v7g6h.js +0 -1
- package/.next/static/chunks/0ogeals5ufhho.js +0 -1
- package/.next/static/chunks/0v~9m62eciynd.js +0 -1
- package/.next/static/chunks/0xh3wr5rwvifr.css +0 -2
- package/.next/static/chunks/0z.pzy8zpa~js.js +0 -7
- package/.next/static/chunks/1031v6nw33ud3.js +0 -1
- package/.next/static/chunks/10ir.g.n~hre0.js +0 -7
- package/.next/static/chunks/10njj7kn9ylkf.js +0 -1
- package/.next/static/chunks/11v5tec4s1168.js +0 -1
- package/.next/static/chunks/13.dst7liziwo.js +0 -1
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- /package/.next/static/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_buildManifest.js +0 -0
- /package/.next/static/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_ssgManifest.js +0 -0
package/app/page.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
|
-
import { formatUptime } from './components/utils';
|
|
5
|
+
import { formatUptime, previewContent } from './components/utils';
|
|
6
6
|
import { StatsBar } from './components/StatsBar';
|
|
7
7
|
import { BroadcastBar } from './components/BroadcastBar';
|
|
8
8
|
import { TopoGraph } from './components/TopoGraph';
|
|
@@ -10,6 +10,8 @@ import { AgentCard } from './components/AgentCard';
|
|
|
10
10
|
import { InboxPanel } from './components/InboxPanel';
|
|
11
11
|
import { LoadingSkeleton } from './components/LoadingSkeleton';
|
|
12
12
|
import { NodesEmptyState as EmptyState } from './components/EmptyState';
|
|
13
|
+
import { AliasAvatar } from './components/AliasAvatar';
|
|
14
|
+
import { STATUS_DOT_HEX, STATUS_CHIP_CLASS } from './lib/status';
|
|
13
15
|
import { UserBar } from './components/UserBar';
|
|
14
16
|
import { CommandCenter, useCommandCenter } from './components/CommandCenter';
|
|
15
17
|
import { DispatchPanel } from './components/DispatchPanel';
|
|
@@ -40,6 +42,7 @@ export default function Dashboard() {
|
|
|
40
42
|
const cmd = useCommandCenter();
|
|
41
43
|
const [showDispatch, setShowDispatch] = useState(false);
|
|
42
44
|
const [inbox, setInbox] = useState<InboxMessage[]>([]);
|
|
45
|
+
const [agentFilter, setAgentFilter] = useState<'all' | 'working' | 'idle' | 'offline'>('all');
|
|
43
46
|
const { mutate } = useSWRConfig();
|
|
44
47
|
|
|
45
48
|
// SSE: instant revalidation on CommHub events
|
|
@@ -113,18 +116,27 @@ export default function Dashboard() {
|
|
|
113
116
|
return bWorking - aWorking;
|
|
114
117
|
});
|
|
115
118
|
|
|
119
|
+
/** Round 70: when the fleet is empty, the Overview reorders to lead with
|
|
120
|
+
* the "Spin up your first agent" CTA and hides the Quick Navigation /
|
|
121
|
+
* Nav rail / Broadcast bar that would otherwise occupy prime real estate
|
|
122
|
+
* with zeros and dead-end links. Computed once, used as a gate below. */
|
|
123
|
+
const fleetEmpty = sessions.length === 0 && !sessError;
|
|
124
|
+
|
|
116
125
|
return (
|
|
117
126
|
<div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
|
|
118
127
|
<div className="lg:ml-0 ml-10">
|
|
119
128
|
<StatsBar online={online} working={working} total={total} version={version} uptime={uptime} />
|
|
120
129
|
</div>
|
|
121
130
|
|
|
122
|
-
{/* Dispatch + User Bar
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
{/* Dispatch + User Bar — Dispatch hidden when fleet empty (nothing to
|
|
132
|
+
dispatch to); UserBar still useful (account/sign-out menu). */}
|
|
133
|
+
<div className="flex items-center gap-3 mb-4">
|
|
134
|
+
{!fleetEmpty && (
|
|
135
|
+
<button onClick={() => setShowDispatch(true)}
|
|
136
|
+
className="px-4 py-2 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 text-white text-sm font-medium rounded-xl shadow-lg shadow-cyan-500/10 transition-all active:scale-95 shrink-0">
|
|
137
|
+
⚡ Dispatch
|
|
138
|
+
</button>
|
|
139
|
+
)}
|
|
128
140
|
<div className="flex-1"><UserBar /></div>
|
|
129
141
|
</div>
|
|
130
142
|
|
|
@@ -167,18 +179,15 @@ export default function Dashboard() {
|
|
|
167
179
|
<Link href="/tasks" prefetch={false} className="text-xs text-cyan-400 hover:text-cyan-300">View all →</Link>
|
|
168
180
|
</div>
|
|
169
181
|
<div className="flex flex-wrap gap-2">
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
].filter(({ key }) => taskStats[key])
|
|
180
|
-
.map(({ key, color }) => (
|
|
181
|
-
<Link key={key} href={`/tasks?status=${key}`} prefetch={false} className={`px-2.5 py-1 rounded-md border text-xs ${color} hover:opacity-80 transition-opacity`}>
|
|
182
|
+
{/* Round 69: order is "hot first" — active flow before terminal
|
|
183
|
+
states — intentionally different from the lifecycle order on
|
|
184
|
+
/tasks. Colors come from shared STATUS_CHIP_CLASS so a
|
|
185
|
+
palette tweak in app/lib/status.ts updates here too.
|
|
186
|
+
'created' added in r69 (was missing — enum-coverage bug). */}
|
|
187
|
+
{(['running', 'delivered', 'acked', 'replied', 'created', 'failed', 'cancelled', 'expired', 'closed'] as const)
|
|
188
|
+
.filter((key) => taskStats[key])
|
|
189
|
+
.map((key) => (
|
|
190
|
+
<Link key={key} href={`/tasks?status=${key}`} prefetch={false} className={`px-2.5 py-1 rounded-md border text-xs ${STATUS_CHIP_CLASS[key]} hover:opacity-80 transition-opacity`}>
|
|
182
191
|
{key}: {taskStats[key]}
|
|
183
192
|
</Link>
|
|
184
193
|
))}
|
|
@@ -189,9 +198,13 @@ export default function Dashboard() {
|
|
|
189
198
|
{/* Quick Actions — split into two distinct intents:
|
|
190
199
|
(1) Top: live stat cards (carry data, drill-in on click)
|
|
191
200
|
(2) Bottom: pure nav rail (no number, icon + label)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
Round 24 — wrap both in a labelled block so the rhythm reads as
|
|
202
|
+
"here are the main jumps" instead of two disconnected strips.
|
|
203
|
+
Round 70 — entire Quick Nav + Nav rail + Broadcast + Recent
|
|
204
|
+
Activity block is hidden when the fleet is empty so the
|
|
205
|
+
onboarding CTA gets the page above the fold. */}
|
|
206
|
+
{!fleetEmpty && <>
|
|
207
|
+
<div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 mb-2">Quick navigation</div>
|
|
195
208
|
<section className="mb-3 grid grid-cols-3 gap-2 sm:gap-3">
|
|
196
209
|
{(() => {
|
|
197
210
|
// Build breakdown popover content per card. Pure data — pure CSS
|
|
@@ -311,21 +324,25 @@ export default function Dashboard() {
|
|
|
311
324
|
<div className="space-y-2 max-h-40 overflow-y-auto">
|
|
312
325
|
{tasks.slice(0, 5).map((t: { task_id: string; from_name: string; to_name: string; status: string; content: string; created_at: string }) => (
|
|
313
326
|
<div key={t.task_id} className="flex items-center gap-2 text-xs">
|
|
314
|
-
<span
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
327
|
+
<span
|
|
328
|
+
className="w-1.5 h-1.5 rounded-full shrink-0"
|
|
329
|
+
style={{ backgroundColor: STATUS_DOT_HEX[t.status] || '#6b7280' }}
|
|
330
|
+
/>
|
|
331
|
+
{t.from_name && <AliasAvatar alias={t.from_name} size={14} />}
|
|
332
|
+
<span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.from_name || '?'}</span>
|
|
318
333
|
<span className="text-gray-600">→</span>
|
|
319
|
-
<
|
|
320
|
-
<span className="text-gray-
|
|
334
|
+
{t.to_name && <AliasAvatar alias={t.to_name} size={14} />}
|
|
335
|
+
<span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.to_name || '?'}</span>
|
|
336
|
+
<span className="text-gray-500 truncate flex-1" title={t.content || ''}>{previewContent(t.content).slice(0, 60)}</span>
|
|
321
337
|
<span className={`shrink-0 px-1.5 py-0.5 rounded text-[10px] border ${
|
|
322
|
-
t.status
|
|
338
|
+
STATUS_CHIP_CLASS[t.status] || 'text-gray-500 border-gray-700/30'
|
|
323
339
|
}`}>{t.status}</span>
|
|
324
340
|
</div>
|
|
325
341
|
))}
|
|
326
342
|
</div>
|
|
327
343
|
</section>
|
|
328
344
|
)}
|
|
345
|
+
</>}
|
|
329
346
|
|
|
330
347
|
{sessError && (
|
|
331
348
|
<div className="bg-red-900/20 border border-red-800/40 text-red-300 px-4 py-3 rounded-lg mb-6 text-sm flex items-center justify-between" role="alert">
|
|
@@ -356,13 +373,67 @@ export default function Dashboard() {
|
|
|
356
373
|
|
|
357
374
|
{sessions.length === 0 && !sessError ? (
|
|
358
375
|
<EmptyState hint={sessHint} />
|
|
359
|
-
) : (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
))
|
|
364
|
-
|
|
365
|
-
|
|
376
|
+
) : (() => {
|
|
377
|
+
const counts = {
|
|
378
|
+
all: sortedSessions.length,
|
|
379
|
+
working: sortedSessions.filter(s => isOnline(s) && s.status === 'working').length,
|
|
380
|
+
idle: sortedSessions.filter(s => isOnline(s) && s.status !== 'working').length,
|
|
381
|
+
offline: sortedSessions.filter(s => !isOnline(s)).length,
|
|
382
|
+
};
|
|
383
|
+
const filtered = sortedSessions.filter(s => {
|
|
384
|
+
if (agentFilter === 'all') return true;
|
|
385
|
+
if (agentFilter === 'offline') return !isOnline(s);
|
|
386
|
+
if (agentFilter === 'working') return isOnline(s) && s.status === 'working';
|
|
387
|
+
if (agentFilter === 'idle') return isOnline(s) && s.status !== 'working';
|
|
388
|
+
return true;
|
|
389
|
+
});
|
|
390
|
+
// Filter chip color keyed to status (round 34): working=green, idle=cyan,
|
|
391
|
+
// offline=gray, all=neutral. Inline hex dots avoid Tailwind v4 purge.
|
|
392
|
+
const chips: { key: typeof agentFilter; label: string; dot?: string }[] = [
|
|
393
|
+
{ key: 'all', label: 'All' },
|
|
394
|
+
{ key: 'working', label: 'Working', dot: '#4ade80' },
|
|
395
|
+
{ key: 'idle', label: 'Idle', dot: '#22d3ee' },
|
|
396
|
+
{ key: 'offline', label: 'Offline', dot: '#6b7280' },
|
|
397
|
+
];
|
|
398
|
+
return (
|
|
399
|
+
<>
|
|
400
|
+
<div className="mb-3 flex flex-wrap items-center gap-1">
|
|
401
|
+
{chips.map(c => (
|
|
402
|
+
<button
|
|
403
|
+
key={c.key}
|
|
404
|
+
type="button"
|
|
405
|
+
onClick={() => setAgentFilter(c.key)}
|
|
406
|
+
disabled={counts[c.key] === 0 && c.key !== 'all'}
|
|
407
|
+
className={`flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs transition-colors disabled:opacity-30 disabled:cursor-not-allowed ${
|
|
408
|
+
agentFilter === c.key
|
|
409
|
+
? 'bg-cyan-500/10 text-cyan-300 border border-cyan-500/30'
|
|
410
|
+
: 'text-gray-500 hover:text-gray-200 hover:bg-[#1a1a2a] border border-transparent'
|
|
411
|
+
}`}
|
|
412
|
+
>
|
|
413
|
+
{c.dot && <span aria-hidden className="inline-block w-1.5 h-1.5 rounded-full" style={{ backgroundColor: c.dot }} />}
|
|
414
|
+
<span>{c.label}</span>
|
|
415
|
+
<span className={`text-[10px] tabular-nums ${agentFilter === c.key ? 'text-cyan-400' : 'text-gray-600'}`}>{counts[c.key]}</span>
|
|
416
|
+
</button>
|
|
417
|
+
))}
|
|
418
|
+
</div>
|
|
419
|
+
{/* Round 48: previous breakpoints had `lg:grid-cols-2 xl:grid-cols-3`
|
|
420
|
+
which kept lg (1024-1279px) at only 2 columns even though
|
|
421
|
+
each AgentCard is fine ≥260px wide. With the sidebar (208px),
|
|
422
|
+
main area at lg is ~816px so 3 cols at ~272px each fits.
|
|
423
|
+
xl breakpoint auto-inherits lg=3 cols; 2xl bumps to 4. */}
|
|
424
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-3 sm:gap-4">
|
|
425
|
+
{filtered.map(s => (
|
|
426
|
+
<AgentCard key={s.alias} session={s} hasSse={isOnline(s)} sseCount={sseLookup(s) || 0} onChat={cmd.openTab} />
|
|
427
|
+
))}
|
|
428
|
+
</div>
|
|
429
|
+
{filtered.length === 0 && (
|
|
430
|
+
<div className="mt-4 text-center text-xs text-gray-600">
|
|
431
|
+
No agents match "{agentFilter}" — <button onClick={() => setAgentFilter('all')} className="underline hover:text-gray-400">Show all</button>
|
|
432
|
+
</div>
|
|
433
|
+
)}
|
|
434
|
+
</>
|
|
435
|
+
);
|
|
436
|
+
})()}
|
|
366
437
|
|
|
367
438
|
<InboxPanel messages={inbox} />
|
|
368
439
|
|
package/app/server-logs/page.tsx
CHANGED
|
@@ -23,6 +23,30 @@ const LEVEL_BADGE: Record<LogLine['level'], string> = {
|
|
|
23
23
|
error: 'bg-red-500/10 text-red-300 border-red-500/30',
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
/** Left-rail strip color per log level — gives the eye a vertical guide
|
|
27
|
+
* when scanning long log walls. Plain hex so Tailwind purge can't drop it. */
|
|
28
|
+
const LEVEL_STRIPE: Record<LogLine['level'], string> = {
|
|
29
|
+
log: 'transparent',
|
|
30
|
+
info: '#22d3ee',
|
|
31
|
+
warn: '#f59e0b',
|
|
32
|
+
error: '#ef4444',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Highlight search-matched substring inside a log line. Case-insensitive
|
|
36
|
+
* first-match wrap; mark inherits color so log-level coloring stays. */
|
|
37
|
+
function highlightSearch(line: string, q: string) {
|
|
38
|
+
if (!q) return line;
|
|
39
|
+
const idx = line.toLowerCase().indexOf(q.toLowerCase());
|
|
40
|
+
if (idx < 0) return line;
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
{line.slice(0, idx)}
|
|
44
|
+
<mark className="bg-yellow-500/30 text-yellow-100 rounded-sm px-0.5">{line.slice(idx, idx + q.length)}</mark>
|
|
45
|
+
{line.slice(idx + q.length)}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
function shortTime(iso: string) {
|
|
27
51
|
try {
|
|
28
52
|
const d = new Date(iso);
|
|
@@ -93,43 +117,76 @@ export default function ServerLogsPage() {
|
|
|
93
117
|
|
|
94
118
|
return (
|
|
95
119
|
<div className="p-6 max-w-[1600px] mx-auto">
|
|
96
|
-
<div className="flex items-
|
|
97
|
-
<div className="flex items-
|
|
120
|
+
<div className="flex items-center justify-between mb-4 gap-3">
|
|
121
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
98
122
|
<h1 className="text-2xl font-bold text-white">Server Logs</h1>
|
|
99
|
-
|
|
123
|
+
{/* Round 86: dropped the {logs.length} header chip — r84 added
|
|
124
|
+
`all <count>` to the filter strip just below, so this duplicated
|
|
125
|
+
the value within 40px of itself. */}
|
|
126
|
+
<span className="text-xs text-gray-500 hidden sm:inline">CommHub stdout/stderr</span>
|
|
100
127
|
</div>
|
|
101
|
-
<div className="flex items-center gap-2 text-xs">
|
|
128
|
+
<div className="flex items-center gap-2 text-xs shrink-0">
|
|
102
129
|
<button
|
|
103
130
|
onClick={() => setPaused(p => !p)}
|
|
104
|
-
className={`px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
|
|
131
|
+
className={`flex items-center gap-1.5 px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
|
|
105
132
|
>
|
|
106
|
-
{paused ?
|
|
133
|
+
{paused ? (
|
|
134
|
+
<>
|
|
135
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden><path d="M8 5v14l11-7z" /></svg>
|
|
136
|
+
Resume
|
|
137
|
+
</>
|
|
138
|
+
) : (
|
|
139
|
+
<>
|
|
140
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
|
|
141
|
+
<rect x="6" y="5" width="4" height="14" rx="0.5" />
|
|
142
|
+
<rect x="14" y="5" width="4" height="14" rx="0.5" />
|
|
143
|
+
</svg>
|
|
144
|
+
Pause
|
|
145
|
+
</>
|
|
146
|
+
)}
|
|
107
147
|
</button>
|
|
108
148
|
<label className="flex items-center gap-1 text-gray-400 select-none">
|
|
109
149
|
<input type="checkbox" checked={autoRefresh} onChange={e => setAutoRefresh(e.target.checked)} className="accent-cyan-500" />
|
|
110
|
-
auto-refresh
|
|
150
|
+
<span className="hidden sm:inline">auto-refresh</span>
|
|
111
151
|
</label>
|
|
112
|
-
<button onClick={() => fetchLogs(true)} className="px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
|
|
113
|
-
|
|
152
|
+
<button onClick={() => fetchLogs(true)} className="flex items-center gap-1.5 px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
|
|
153
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
|
154
|
+
<path d="M3 12a9 9 0 0 1 15.5-6.4L21 8" />
|
|
155
|
+
<path d="M21 3v5h-5" />
|
|
156
|
+
<path d="M21 12a9 9 0 0 1-15.5 6.4L3 16" />
|
|
157
|
+
<path d="M3 21v-5h5" />
|
|
158
|
+
</svg>
|
|
159
|
+
<span className="hidden sm:inline">Reload</span>
|
|
114
160
|
</button>
|
|
115
161
|
</div>
|
|
116
162
|
</div>
|
|
117
163
|
|
|
118
164
|
<div className="flex items-center gap-3 mb-3">
|
|
119
165
|
<div className="flex items-center gap-1 text-xs">
|
|
120
|
-
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
166
|
+
{/* Round 84: every chip shows its count (matches Tasks r56 +
|
|
167
|
+
Audit Log r43); zero-count non-active chips are disabled
|
|
168
|
+
+ muted so the eye lands on the levels that actually have
|
|
169
|
+
entries. */}
|
|
170
|
+
{(['all', 'log', 'info', 'warn', 'error'] as const).map(lv => {
|
|
171
|
+
const count = lv === 'all' ? logs.length : counts[lv as LogLine['level']];
|
|
172
|
+
const isActive = filterLevel === lv;
|
|
173
|
+
const isEmpty = lv !== 'all' && count === 0 && !isActive;
|
|
174
|
+
return (
|
|
175
|
+
<button
|
|
176
|
+
key={lv}
|
|
177
|
+
onClick={() => setFilterLevel(lv)}
|
|
178
|
+
disabled={isEmpty}
|
|
179
|
+
className={`px-2.5 py-1 rounded border flex items-center gap-1 disabled:opacity-30 disabled:cursor-not-allowed ${
|
|
180
|
+
isActive
|
|
181
|
+
? 'bg-cyan-500/15 text-cyan-200 border-cyan-500/40'
|
|
182
|
+
: 'bg-[#11111c] text-gray-400 border-[#2a2a4a] hover:bg-[#1a1a2a]'
|
|
183
|
+
}`}
|
|
184
|
+
>
|
|
185
|
+
<span>{lv}</span>
|
|
186
|
+
<span className={`text-[10px] tabular-nums ${isActive ? 'opacity-80' : 'text-gray-600'}`}>{count}</span>
|
|
187
|
+
</button>
|
|
188
|
+
);
|
|
189
|
+
})}
|
|
133
190
|
</div>
|
|
134
191
|
<input
|
|
135
192
|
value={search}
|
|
@@ -182,12 +239,27 @@ export default function ServerLogsPage() {
|
|
|
182
239
|
) : (
|
|
183
240
|
<div className="divide-y divide-[#0d0d18] max-h-[calc(100vh-260px)] overflow-y-auto">
|
|
184
241
|
{filtered.map((l, i) => (
|
|
185
|
-
<div key={`${l.ts}-${i}`} className="
|
|
186
|
-
|
|
242
|
+
<div key={`${l.ts}-${i}`} className="relative pl-3 pr-3 py-1 hover:bg-[#0a0a14] flex gap-3">
|
|
243
|
+
{/* Round 32: 2px left rail keyed to level. Makes warn/error
|
|
244
|
+
spike visible in a wall of `log` lines without users having
|
|
245
|
+
to read the level chip on every row. */}
|
|
246
|
+
<span
|
|
247
|
+
aria-hidden
|
|
248
|
+
className="absolute left-0 top-0 bottom-0 w-0.5"
|
|
249
|
+
style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
|
|
250
|
+
/>
|
|
251
|
+
<span className="text-gray-600 shrink-0 w-[100px] text-[10px] tabular-nums">{shortTime(l.ts)}</span>
|
|
187
252
|
<span className={`shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
|
|
188
253
|
{l.level}
|
|
189
254
|
</span>
|
|
190
|
-
|
|
255
|
+
{/* Round 85: CommHub stamps each log line with a `[HH:MM:SS]`
|
|
256
|
+
prefix, but the row already shows shortTime(l.ts) in its
|
|
257
|
+
own 100px column on the left. Strip the duplicate
|
|
258
|
+
prefix at display time so the message starts directly
|
|
259
|
+
with the alias / agent / action. */}
|
|
260
|
+
<span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>
|
|
261
|
+
{highlightSearch(l.line.replace(/^\[\d{2}:\d{2}:\d{2}\]\s*/, ''), search)}
|
|
262
|
+
</span>
|
|
191
263
|
</div>
|
|
192
264
|
))}
|
|
193
265
|
</div>
|
|
@@ -4,6 +4,7 @@ import { useEffect, useState, useCallback } from 'react';
|
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { timeAgo } from '../../components/utils';
|
|
6
6
|
import { EmptyState } from '../../components/EmptyState';
|
|
7
|
+
import { AliasAvatar } from '../../components/AliasAvatar';
|
|
7
8
|
import useSWR from 'swr';
|
|
8
9
|
|
|
9
10
|
const fetcher = (url: string) => fetch(url).then(r => r.json());
|
|
@@ -123,7 +124,12 @@ export default function NetworksPage() {
|
|
|
123
124
|
|
|
124
125
|
{/* List */}
|
|
125
126
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
126
|
-
<
|
|
127
|
+
<div className="flex items-center justify-between mb-3">
|
|
128
|
+
<h2 className="text-sm font-semibold text-gray-300">My Networks</h2>
|
|
129
|
+
{networks.length > 0 && (
|
|
130
|
+
<span className="text-[10px] text-gray-600 uppercase tracking-wide">{networks.length} total</span>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
127
133
|
{loading ? (
|
|
128
134
|
<div className="animate-pulse space-y-2">{[1,2].map(i => <div key={i} className="h-14 bg-gray-800/20 rounded" />)}</div>
|
|
129
135
|
) : networks.length === 0 ? (
|
|
@@ -136,11 +142,14 @@ export default function NetworksPage() {
|
|
|
136
142
|
<div className="space-y-3">
|
|
137
143
|
{networks.map(n => (
|
|
138
144
|
<div key={n.network_id} className="bg-[#0a0a15] rounded-lg px-4 py-3 border border-[#1a1a2a]">
|
|
139
|
-
<div className="flex items-
|
|
140
|
-
<div className="min-w-0">
|
|
141
|
-
<
|
|
142
|
-
<div className="
|
|
143
|
-
|
|
145
|
+
<div className="flex items-start justify-between gap-3">
|
|
146
|
+
<div className="flex items-start gap-3 min-w-0">
|
|
147
|
+
<AliasAvatar alias={n.network_name} size={28} />
|
|
148
|
+
<div className="min-w-0">
|
|
149
|
+
<div className="text-sm text-white font-medium truncate">{n.network_name}</div>
|
|
150
|
+
<div className="text-[10px] text-gray-500 font-mono truncate" title={n.network_id}>{n.network_id} · {timeAgo(n.created_at)}</div>
|
|
151
|
+
{n.description && <div className="text-xs text-gray-400 mt-1">{n.description}</div>}
|
|
152
|
+
</div>
|
|
144
153
|
</div>
|
|
145
154
|
<div className="flex gap-2 shrink-0">
|
|
146
155
|
<button onClick={() => {
|
|
@@ -169,14 +178,20 @@ export default function NetworksPage() {
|
|
|
169
178
|
{(members[n.network_id] || []).length === 0 ? (
|
|
170
179
|
<div className="text-[10px] text-gray-600">No members or V3 auth required</div>
|
|
171
180
|
) : (
|
|
172
|
-
(members[n.network_id] || []).map(m =>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
(members[n.network_id] || []).map(m => {
|
|
182
|
+
const name = m.username || m.display_name || '?';
|
|
183
|
+
return (
|
|
184
|
+
<div key={m.user_id} className="flex items-center gap-2 justify-between text-xs py-1">
|
|
185
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
186
|
+
<AliasAvatar alias={name} size={16} />
|
|
187
|
+
<span className="text-gray-300 truncate">{name}</span>
|
|
188
|
+
</div>
|
|
189
|
+
<span className={`px-1.5 py-0.5 rounded border text-[10px] shrink-0 ${
|
|
176
190
|
m.role === 'owner' ? 'text-yellow-300 border-yellow-500/20' : 'text-gray-400 border-gray-600/20'
|
|
177
191
|
}`}>{m.role}</span>
|
|
178
192
|
</div>
|
|
179
|
-
|
|
193
|
+
);
|
|
194
|
+
})
|
|
180
195
|
)}
|
|
181
196
|
|
|
182
197
|
{/* Invite */}
|
|
@@ -196,8 +211,17 @@ export default function NetworksPage() {
|
|
|
196
211
|
</div>
|
|
197
212
|
{inviteCode && (
|
|
198
213
|
<div className="mt-2 bg-green-500/5 border border-green-500/20 rounded px-3 py-2">
|
|
199
|
-
<div className="
|
|
200
|
-
|
|
214
|
+
<div className="flex items-center justify-between mb-1">
|
|
215
|
+
<div className="text-[10px] text-green-400 uppercase tracking-wide">Invite code</div>
|
|
216
|
+
<button
|
|
217
|
+
type="button"
|
|
218
|
+
onClick={() => { navigator.clipboard?.writeText(inviteCode).catch(() => {}); }}
|
|
219
|
+
className="text-[10px] text-green-300 hover:text-green-200 px-1.5 py-0.5 rounded border border-green-500/20 hover:bg-green-500/10"
|
|
220
|
+
>
|
|
221
|
+
Copy
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
<code className="text-xs text-green-300 select-all break-all">{inviteCode}</code>
|
|
201
225
|
</div>
|
|
202
226
|
)}
|
|
203
227
|
</div>
|
package/app/settings/page.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { useAnetConfig, useHealth, useLicense } from '../lib/hooks';
|
|
6
|
+
import { DASHBOARD_VERSION } from '../lib/version';
|
|
6
7
|
|
|
7
8
|
export default function SettingsPage() {
|
|
8
9
|
const { config } = useAnetConfig();
|
|
@@ -18,12 +19,31 @@ export default function SettingsPage() {
|
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
<div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
|
|
21
|
-
<h1 className="text-2xl font-bold text-white mb-
|
|
22
|
+
<h1 className="text-2xl font-bold text-white mb-3 lg:ml-0 ml-10">Settings</h1>
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
{/* Section anchor nav (round 28) — jump to a group instead of scrolling
|
|
25
|
+
through the whole page. Mirrors the section headers below; pages
|
|
26
|
+
with 7+ cards benefit from a visible table-of-contents. */}
|
|
27
|
+
<nav className="mb-8 flex flex-wrap gap-1 text-xs">
|
|
28
|
+
{[
|
|
29
|
+
{ href: '#connection', label: 'Connection' },
|
|
30
|
+
{ href: '#account', label: 'Account' },
|
|
31
|
+
{ href: '#resources', label: 'Resources' },
|
|
32
|
+
].map(a => (
|
|
33
|
+
<a key={a.href} href={a.href}
|
|
34
|
+
className="rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
|
|
35
|
+
{a.label}
|
|
36
|
+
</a>
|
|
37
|
+
))}
|
|
38
|
+
</nav>
|
|
39
|
+
|
|
40
|
+
<div className="max-w-2xl space-y-10">
|
|
24
41
|
{/* ── Group: Connection ─────────────────────────────────── */}
|
|
25
|
-
<div className="space-y-4">
|
|
26
|
-
<div className="
|
|
42
|
+
<div id="connection" className="space-y-4 scroll-mt-6">
|
|
43
|
+
<div className="flex items-center gap-2 px-1">
|
|
44
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Connection</div>
|
|
45
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
46
|
+
</div>
|
|
27
47
|
|
|
28
48
|
{/* CommHub Connection */}
|
|
29
49
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
@@ -72,7 +92,7 @@ export default function SettingsPage() {
|
|
|
72
92
|
<span className={`text-gray-300 ${valueClass}`}>{health?.sessions ?? '--'}</span>
|
|
73
93
|
</div>
|
|
74
94
|
<div className={rowClass}>
|
|
75
|
-
<span className="text-gray-500">SSE
|
|
95
|
+
<span className="text-gray-500">SSE streams <span className="text-gray-600">· server</span></span>
|
|
76
96
|
<span className={`text-gray-300 ${valueClass}`}>{health?.sse_connections ?? '--'}</span>
|
|
77
97
|
</div>
|
|
78
98
|
<div className={rowClass}>
|
|
@@ -88,7 +108,9 @@ export default function SettingsPage() {
|
|
|
88
108
|
<div className="space-y-3 text-sm">
|
|
89
109
|
<div className={rowClass}>
|
|
90
110
|
<span className="text-gray-500">Version</span>
|
|
91
|
-
<span className={`text-gray-300 ${valueClass}`}
|
|
111
|
+
<span className={`text-gray-300 font-mono ${valueClass}`} title="From package.json @sleep2agi/agent-network-dashboard">
|
|
112
|
+
{DASHBOARD_VERSION}
|
|
113
|
+
</span>
|
|
92
114
|
</div>
|
|
93
115
|
<div className={rowClass}>
|
|
94
116
|
<span className="text-gray-500">Data Layer</span>
|
|
@@ -96,15 +118,18 @@ export default function SettingsPage() {
|
|
|
96
118
|
</div>
|
|
97
119
|
<div className={rowClass}>
|
|
98
120
|
<span className="text-gray-500">Pages</span>
|
|
99
|
-
<span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Settings</span>
|
|
121
|
+
<span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Networks, Audit, Server Logs, Admin, Settings</span>
|
|
100
122
|
</div>
|
|
101
123
|
</div>
|
|
102
124
|
</section>
|
|
103
125
|
</div>
|
|
104
126
|
|
|
105
127
|
{/* ── Group: Account ────────────────────────────────────── */}
|
|
106
|
-
<div className="space-y-4">
|
|
107
|
-
<div className="
|
|
128
|
+
<div id="account" className="space-y-4 scroll-mt-6">
|
|
129
|
+
<div className="flex items-center gap-2 px-1">
|
|
130
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Account</div>
|
|
131
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
132
|
+
</div>
|
|
108
133
|
|
|
109
134
|
{/* License */}
|
|
110
135
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
@@ -232,8 +257,11 @@ export default function SettingsPage() {
|
|
|
232
257
|
</div>
|
|
233
258
|
|
|
234
259
|
{/* ── Group: Resources ──────────────────────────────────── */}
|
|
235
|
-
<div className="space-y-4">
|
|
236
|
-
<div className="
|
|
260
|
+
<div id="resources" className="space-y-4 scroll-mt-6">
|
|
261
|
+
<div className="flex items-center gap-2 px-1">
|
|
262
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Resources</div>
|
|
263
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
264
|
+
</div>
|
|
237
265
|
|
|
238
266
|
{/* API Tokens + Networks */}
|
|
239
267
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
package/app/tasks/[id]/page.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { use } from 'react';
|
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import useSWR from 'swr';
|
|
6
6
|
import { timeAgo } from '../../components/utils';
|
|
7
|
+
import { STATUS_CHIP_CLASS } from '../../lib/status';
|
|
7
8
|
|
|
8
9
|
const fetcher = async (url: string) => {
|
|
9
10
|
const res = await fetch(url);
|
|
@@ -55,18 +56,6 @@ function Timeline({ steps }: { steps: TimelineStep[] }) {
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const STATUS_COLORS: Record<string, string> = {
|
|
59
|
-
created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
|
|
60
|
-
delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
|
|
61
|
-
acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
|
|
62
|
-
running: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
63
|
-
replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
|
|
64
|
-
closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
65
|
-
failed: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
66
|
-
cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
67
|
-
expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
59
|
export default function TaskDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
|
71
60
|
const { id } = use(params);
|
|
72
61
|
const { data, isLoading } = useSWR(
|
|
@@ -105,7 +94,7 @@ export default function TaskDetailPage({ params }: { params: Promise<{ id: strin
|
|
|
105
94
|
);
|
|
106
95
|
}
|
|
107
96
|
|
|
108
|
-
const statusColor =
|
|
97
|
+
const statusColor = STATUS_CHIP_CLASS[task.status] || STATUS_CHIP_CLASS.created;
|
|
109
98
|
const steps: TimelineStep[] = [
|
|
110
99
|
{ label: 'Created', time: task.created_at, color: 'text-gray-400' },
|
|
111
100
|
{ label: 'Delivered', time: task.delivered_at, color: 'text-blue-400' },
|