@sleep2agi/agent-network-dashboard 0.4.5-preview.2 → 0.4.5-preview.21

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 (238) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +46 -46
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +14 -14
  25. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +14 -14
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +14 -14
  41. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page.js.nft.json +1 -1
  48. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  49. package/.next/server/app/logs.html +2 -2
  50. package/.next/server/app/logs.rsc +14 -14
  51. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  52. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  53. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  54. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  55. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  56. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  57. package/.next/server/app/messages/page.js.nft.json +1 -1
  58. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  59. package/.next/server/app/messages.html +2 -2
  60. package/.next/server/app/messages.rsc +14 -14
  61. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  62. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  63. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  64. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  65. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  66. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  67. package/.next/server/app/node/page.js.nft.json +1 -1
  68. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  69. package/.next/server/app/node.html +2 -2
  70. package/.next/server/app/node.rsc +14 -14
  71. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  72. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  74. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  75. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  76. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  77. package/.next/server/app/nodes/page.js.nft.json +1 -1
  78. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  79. package/.next/server/app/nodes.html +2 -2
  80. package/.next/server/app/nodes.rsc +14 -14
  81. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  82. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  83. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  84. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  85. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  86. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  87. package/.next/server/app/page.js.nft.json +1 -1
  88. package/.next/server/app/page_client-reference-manifest.js +1 -1
  89. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  90. package/.next/server/app/server-logs.html +2 -2
  91. package/.next/server/app/server-logs.rsc +14 -14
  92. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  93. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  94. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  95. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  96. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  97. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  98. package/.next/server/app/settings/networks/page.js.nft.json +1 -1
  99. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/settings/networks.html +2 -2
  101. package/.next/server/app/settings/networks.rsc +14 -14
  102. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  103. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  104. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  105. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  106. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  108. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  109. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  110. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  111. package/.next/server/app/settings/tokens.html +2 -2
  112. package/.next/server/app/settings/tokens.rsc +14 -14
  113. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  114. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  115. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  116. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  117. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  119. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  120. package/.next/server/app/settings.html +2 -2
  121. package/.next/server/app/settings.rsc +14 -14
  122. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  123. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  124. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  125. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  126. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  128. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  129. package/.next/server/app/tasks/page.js.nft.json +1 -1
  130. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  131. package/.next/server/app/tasks.html +2 -2
  132. package/.next/server/app/tasks.rsc +14 -14
  133. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  134. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  135. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  136. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  137. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  138. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  139. package/.next/server/chunks/ssr/[root-of-the-server]__02ee4km._.js +3 -0
  140. package/.next/server/chunks/ssr/[root-of-the-server]__02ee4km._.js.map +1 -0
  141. package/.next/server/chunks/ssr/[root-of-the-server]__0jjqrcf._.js +3 -0
  142. package/.next/server/chunks/ssr/[root-of-the-server]__0jjqrcf._.js.map +1 -0
  143. package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.js +3 -0
  144. package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.js.map +1 -0
  145. package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js +1 -1
  146. package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js.map +1 -1
  147. package/.next/server/chunks/ssr/_0nne7mz._.js +3 -0
  148. package/.next/server/chunks/ssr/_0nne7mz._.js.map +1 -0
  149. package/.next/server/chunks/ssr/app_0.zbtn5._.js +9 -0
  150. package/.next/server/chunks/ssr/app_0.zbtn5._.js.map +1 -0
  151. package/.next/server/chunks/ssr/app_04n78pr._.js +3 -0
  152. package/.next/server/chunks/ssr/app_04n78pr._.js.map +1 -0
  153. package/.next/server/chunks/ssr/app_0_agk0r._.js +3 -0
  154. package/.next/server/chunks/ssr/app_0_agk0r._.js.map +1 -0
  155. package/.next/server/chunks/ssr/app_0dk5wao._.js +1 -1
  156. package/.next/server/chunks/ssr/app_0dk5wao._.js.map +1 -1
  157. package/.next/server/chunks/ssr/app_components_08ys5y3._.js +2 -2
  158. package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -1
  159. package/.next/server/chunks/ssr/app_components_0lhp_k.._.js +3 -0
  160. package/.next/server/chunks/ssr/app_components_0lhp_k.._.js.map +1 -0
  161. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +1 -1
  162. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +1 -1
  163. package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js +1 -1
  164. package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js.map +1 -1
  165. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
  166. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
  167. package/.next/server/middleware-build-manifest.js +3 -3
  168. package/.next/server/pages/404.html +2 -2
  169. package/.next/server/pages/500.html +1 -1
  170. package/.next/server/server-reference-manifest.js +1 -1
  171. package/.next/server/server-reference-manifest.json +1 -1
  172. package/.next/static/chunks/00-ce-5_uu76b.js +1 -0
  173. package/.next/static/chunks/0943n0t.9c-o2.js +1 -0
  174. package/.next/static/chunks/{07okhs5561y5y.js → 0c1bp~g7.8ru..js} +1 -1
  175. package/.next/static/chunks/0eajktwdxy7qt.js +1 -0
  176. package/.next/static/chunks/0hqhvq6f83m38.js +1 -0
  177. package/.next/static/chunks/0irp6cdaafs7m.css +2 -0
  178. package/.next/static/chunks/0l3gf0ickq445.js +1 -0
  179. package/.next/static/chunks/0pf4_9.-8s34p.js +1 -0
  180. package/.next/static/chunks/0q4rltx2ngh5m.js +1 -0
  181. package/.next/static/chunks/0qnpc7rbxn12u.js +1 -0
  182. package/.next/static/chunks/10-ka4k4ni9u2.js +1 -0
  183. package/.next/static/chunks/15nn5pyy3l5zo.js +7 -0
  184. package/.next/static/chunks/169v~dd1tdxdm.js +7 -0
  185. package/.next/static/chunks/17uwnuk~0zoo3.js +1 -0
  186. package/.next/trace +2 -2
  187. package/.next/trace-build +1 -1
  188. package/app/components/AgentCard.tsx +6 -2
  189. package/app/components/AliasAvatar.tsx +51 -0
  190. package/app/components/CommandPalette.tsx +53 -20
  191. package/app/components/HelpOverlay.tsx +50 -10
  192. package/app/components/Sidebar.tsx +40 -35
  193. package/app/components/TaskChatPanel.tsx +8 -10
  194. package/app/components/TaskDrawer.tsx +62 -23
  195. package/app/components/TopoGraph.tsx +16 -3
  196. package/app/globals.css +129 -0
  197. package/app/login/page.tsx +37 -7
  198. package/app/logs/page.tsx +28 -4
  199. package/app/messages/page.tsx +79 -48
  200. package/app/nodes/page.tsx +21 -23
  201. package/app/page.tsx +66 -13
  202. package/app/server-logs/page.tsx +37 -3
  203. package/app/settings/networks/page.tsx +37 -13
  204. package/app/settings/page.tsx +33 -8
  205. package/app/tasks/page.tsx +95 -35
  206. package/package.json +1 -1
  207. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js +0 -3
  208. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js.map +0 -1
  209. package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js +0 -3
  210. package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js.map +0 -1
  211. package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js +0 -3
  212. package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js.map +0 -1
  213. package/.next/server/chunks/ssr/_0d-fuox._.js +0 -3
  214. package/.next/server/chunks/ssr/_0d-fuox._.js.map +0 -1
  215. package/.next/server/chunks/ssr/app_0.0wpa~._.js +0 -3
  216. package/.next/server/chunks/ssr/app_0.0wpa~._.js.map +0 -1
  217. package/.next/server/chunks/ssr/app_0et5yua._.js +0 -3
  218. package/.next/server/chunks/ssr/app_0et5yua._.js.map +0 -1
  219. package/.next/server/chunks/ssr/app_0ff~kjj._.js +0 -9
  220. package/.next/server/chunks/ssr/app_0ff~kjj._.js.map +0 -1
  221. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +0 -3
  222. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +0 -1
  223. package/.next/static/chunks/051gslfjka1kj.js +0 -1
  224. package/.next/static/chunks/06t_vzei8s9_h.js +0 -1
  225. package/.next/static/chunks/0b1ymo-btjfha.js +0 -1
  226. package/.next/static/chunks/0e7c40p~wq665.js +0 -1
  227. package/.next/static/chunks/0g7m7zk.v7g6h.js +0 -1
  228. package/.next/static/chunks/0khpyxpdrp~ty.js +0 -1
  229. package/.next/static/chunks/0lo_g.q1fm-hr.js +0 -7
  230. package/.next/static/chunks/0ogeals5ufhho.js +0 -1
  231. package/.next/static/chunks/0qgc758gz3on4.js +0 -7
  232. package/.next/static/chunks/0xxk2ms76kkuz.css +0 -2
  233. package/.next/static/chunks/1031v6nw33ud3.js +0 -1
  234. package/.next/static/chunks/10njj7kn9ylkf.js +0 -1
  235. package/.next/static/chunks/13.dst7liziwo.js +0 -1
  236. /package/.next/static/{dvjBKNMRovte6RFP_k8Lq → I8oYGCFqEglZXHP7xG8Cd}/_buildManifest.js +0 -0
  237. /package/.next/static/{dvjBKNMRovte6RFP_k8Lq → I8oYGCFqEglZXHP7xG8Cd}/_clientMiddlewareManifest.js +0 -0
  238. /package/.next/static/{dvjBKNMRovte6RFP_k8Lq → I8oYGCFqEglZXHP7xG8Cd}/_ssgManifest.js +0 -0
package/app/page.tsx CHANGED
@@ -10,6 +10,7 @@ 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';
13
14
  import { UserBar } from './components/UserBar';
14
15
  import { CommandCenter, useCommandCenter } from './components/CommandCenter';
15
16
  import { DispatchPanel } from './components/DispatchPanel';
@@ -40,6 +41,7 @@ export default function Dashboard() {
40
41
  const cmd = useCommandCenter();
41
42
  const [showDispatch, setShowDispatch] = useState(false);
42
43
  const [inbox, setInbox] = useState<InboxMessage[]>([]);
44
+ const [agentFilter, setAgentFilter] = useState<'all' | 'working' | 'idle' | 'offline'>('all');
43
45
  const { mutate } = useSWRConfig();
44
46
 
45
47
  // SSE: instant revalidation on CommHub events
@@ -120,7 +122,7 @@ export default function Dashboard() {
120
122
  </div>
121
123
 
122
124
  {/* Dispatch + User Bar */}
123
- <div className="flex items-center gap-3 mb-2">
125
+ <div className="flex items-center gap-3 mb-4">
124
126
  <button onClick={() => setShowDispatch(true)}
125
127
  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">
126
128
  ⚡ Dispatch
@@ -189,9 +191,9 @@ export default function Dashboard() {
189
191
  {/* Quick Actions — split into two distinct intents:
190
192
  (1) Top: live stat cards (carry data, drill-in on click)
191
193
  (2) Bottom: pure nav rail (no number, icon + label)
192
- Previously these were mixed in one row of 6, mixing data cells
193
- ("0/0", "--", "0") with placeholder arrows ("→ Messages").
194
- Users couldn't tell which were stats vs which were navigation. */}
194
+ Round 24 wrap both in a labelled block so the rhythm reads as
195
+ "here are the main jumps" instead of two disconnected strips. */}
196
+ <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 mb-2">Quick navigation</div>
195
197
  <section className="mb-3 grid grid-cols-3 gap-2 sm:gap-3">
196
198
  {(() => {
197
199
  // Build breakdown popover content per card. Pure data — pure CSS
@@ -314,9 +316,11 @@ export default function Dashboard() {
314
316
  <span className={`w-1.5 h-1.5 rounded-full shrink-0 ${
315
317
  t.status === 'replied' ? 'bg-purple-400' : t.status === 'running' ? 'bg-green-400' : t.status === 'failed' ? 'bg-red-400' : 'bg-blue-400'
316
318
  }`} />
317
- <span className="text-blue-400 shrink-0">{t.from_name || '?'}</span>
319
+ {t.from_name && <AliasAvatar alias={t.from_name} size={14} />}
320
+ <span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.from_name || '?'}</span>
318
321
  <span className="text-gray-600">&rarr;</span>
319
- <span className="text-cyan-400 shrink-0">{t.to_name || '?'}</span>
322
+ {t.to_name && <AliasAvatar alias={t.to_name} size={14} />}
323
+ <span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.to_name || '?'}</span>
320
324
  <span className="text-gray-500 truncate flex-1">{t.content?.slice(0, 40) || '--'}</span>
321
325
  <span className={`shrink-0 px-1.5 py-0.5 rounded text-[10px] border ${
322
326
  t.status === 'replied' ? 'text-purple-300 border-purple-500/20' : t.status === 'failed' ? 'text-red-300 border-red-500/20' : 'text-gray-500 border-gray-700/30'
@@ -356,13 +360,62 @@ export default function Dashboard() {
356
360
 
357
361
  {sessions.length === 0 && !sessError ? (
358
362
  <EmptyState hint={sessHint} />
359
- ) : (
360
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3 sm:gap-4">
361
- {sortedSessions.map(s => (
362
- <AgentCard key={s.alias} session={s} hasSse={isOnline(s)} sseCount={sseLookup(s) || 0} onChat={cmd.openTab} />
363
- ))}
364
- </div>
365
- )}
363
+ ) : (() => {
364
+ const counts = {
365
+ all: sortedSessions.length,
366
+ working: sortedSessions.filter(s => isOnline(s) && s.status === 'working').length,
367
+ idle: sortedSessions.filter(s => isOnline(s) && s.status !== 'working').length,
368
+ offline: sortedSessions.filter(s => !isOnline(s)).length,
369
+ };
370
+ const filtered = sortedSessions.filter(s => {
371
+ if (agentFilter === 'all') return true;
372
+ if (agentFilter === 'offline') return !isOnline(s);
373
+ if (agentFilter === 'working') return isOnline(s) && s.status === 'working';
374
+ if (agentFilter === 'idle') return isOnline(s) && s.status !== 'working';
375
+ return true;
376
+ });
377
+ // Filter chip color keyed to status (round 34): working=green, idle=cyan,
378
+ // offline=gray, all=neutral. Inline hex dots avoid Tailwind v4 purge.
379
+ const chips: { key: typeof agentFilter; label: string; dot?: string }[] = [
380
+ { key: 'all', label: 'All' },
381
+ { key: 'working', label: 'Working', dot: '#4ade80' },
382
+ { key: 'idle', label: 'Idle', dot: '#22d3ee' },
383
+ { key: 'offline', label: 'Offline', dot: '#6b7280' },
384
+ ];
385
+ return (
386
+ <>
387
+ <div className="mb-3 flex flex-wrap items-center gap-1">
388
+ {chips.map(c => (
389
+ <button
390
+ key={c.key}
391
+ type="button"
392
+ onClick={() => setAgentFilter(c.key)}
393
+ disabled={counts[c.key] === 0 && c.key !== 'all'}
394
+ 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 ${
395
+ agentFilter === c.key
396
+ ? 'bg-cyan-500/10 text-cyan-300 border border-cyan-500/30'
397
+ : 'text-gray-500 hover:text-gray-200 hover:bg-[#1a1a2a] border border-transparent'
398
+ }`}
399
+ >
400
+ {c.dot && <span aria-hidden className="inline-block w-1.5 h-1.5 rounded-full" style={{ backgroundColor: c.dot }} />}
401
+ <span>{c.label}</span>
402
+ <span className={`text-[10px] tabular-nums ${agentFilter === c.key ? 'text-cyan-400' : 'text-gray-600'}`}>{counts[c.key]}</span>
403
+ </button>
404
+ ))}
405
+ </div>
406
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3 sm:gap-4">
407
+ {filtered.map(s => (
408
+ <AgentCard key={s.alias} session={s} hasSse={isOnline(s)} sseCount={sseLookup(s) || 0} onChat={cmd.openTab} />
409
+ ))}
410
+ </div>
411
+ {filtered.length === 0 && (
412
+ <div className="mt-4 text-center text-xs text-gray-600">
413
+ No agents match "{agentFilter}" — <button onClick={() => setAgentFilter('all')} className="underline hover:text-gray-400">Show all</button>
414
+ </div>
415
+ )}
416
+ </>
417
+ );
418
+ })()}
366
419
 
367
420
  <InboxPanel messages={inbox} />
368
421
 
@@ -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);
@@ -182,12 +206,22 @@ export default function ServerLogsPage() {
182
206
  ) : (
183
207
  <div className="divide-y divide-[#0d0d18] max-h-[calc(100vh-260px)] overflow-y-auto">
184
208
  {filtered.map((l, i) => (
185
- <div key={`${l.ts}-${i}`} className="px-3 py-1 hover:bg-[#0a0a14] flex gap-3">
186
- <span className="text-gray-600 shrink-0 w-[110px] text-[10px]">{shortTime(l.ts)}</span>
209
+ <div key={`${l.ts}-${i}`} className="relative pl-3 pr-3 py-1 hover:bg-[#0a0a14] flex gap-3">
210
+ {/* Round 32: 2px left rail keyed to level. Makes warn/error
211
+ spike visible in a wall of `log` lines without users having
212
+ to read the level chip on every row. */}
213
+ <span
214
+ aria-hidden
215
+ className="absolute left-0 top-0 bottom-0 w-0.5"
216
+ style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
217
+ />
218
+ <span className="text-gray-600 shrink-0 w-[100px] text-[10px] tabular-nums">{shortTime(l.ts)}</span>
187
219
  <span className={`shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
188
220
  {l.level}
189
221
  </span>
190
- <span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>{l.line}</span>
222
+ <span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>
223
+ {highlightSearch(l.line, search)}
224
+ </span>
191
225
  </div>
192
226
  ))}
193
227
  </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
- <h2 className="text-sm font-semibold text-gray-300 mb-3">My Networks</h2>
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-center justify-between">
140
- <div className="min-w-0">
141
- <div className="text-sm text-white font-medium">{n.network_name}</div>
142
- <div className="text-[10px] text-gray-500">{n.network_id} · {timeAgo(n.created_at)}</div>
143
- {n.description && <div className="text-xs text-gray-400 mt-1">{n.description}</div>}
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
- <div key={m.user_id} className="flex items-center justify-between text-xs py-1">
174
- <span className="text-gray-300">{m.username || m.display_name}</span>
175
- <span className={`px-1.5 py-0.5 rounded border text-[10px] ${
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="text-[10px] text-green-400 mb-1">Invite code:</div>
200
- <code className="text-xs text-green-300 select-all">{inviteCode}</code>
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>
@@ -18,12 +18,31 @@ export default function SettingsPage() {
18
18
 
19
19
  return (
20
20
  <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-6 lg:ml-0 ml-10">Settings</h1>
21
+ <h1 className="text-2xl font-bold text-white mb-3 lg:ml-0 ml-10">Settings</h1>
22
22
 
23
- <div className="max-w-2xl space-y-8">
23
+ {/* Section anchor nav (round 28) — jump to a group instead of scrolling
24
+ through the whole page. Mirrors the section headers below; pages
25
+ with 7+ cards benefit from a visible table-of-contents. */}
26
+ <nav className="mb-8 flex flex-wrap gap-1 text-xs">
27
+ {[
28
+ { href: '#connection', label: 'Connection' },
29
+ { href: '#account', label: 'Account' },
30
+ { href: '#resources', label: 'Resources' },
31
+ ].map(a => (
32
+ <a key={a.href} href={a.href}
33
+ className="rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
34
+ {a.label}
35
+ </a>
36
+ ))}
37
+ </nav>
38
+
39
+ <div className="max-w-2xl space-y-10">
24
40
  {/* ── Group: Connection ─────────────────────────────────── */}
25
- <div className="space-y-4">
26
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Connection</div>
41
+ <div id="connection" className="space-y-4 scroll-mt-6">
42
+ <div className="flex items-center gap-2 px-1">
43
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Connection</div>
44
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
45
+ </div>
27
46
 
28
47
  {/* CommHub Connection */}
29
48
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
@@ -103,8 +122,11 @@ export default function SettingsPage() {
103
122
  </div>
104
123
 
105
124
  {/* ── Group: Account ────────────────────────────────────── */}
106
- <div className="space-y-4">
107
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Account</div>
125
+ <div id="account" className="space-y-4 scroll-mt-6">
126
+ <div className="flex items-center gap-2 px-1">
127
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Account</div>
128
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
129
+ </div>
108
130
 
109
131
  {/* License */}
110
132
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
@@ -232,8 +254,11 @@ export default function SettingsPage() {
232
254
  </div>
233
255
 
234
256
  {/* ── Group: Resources ──────────────────────────────────── */}
235
- <div className="space-y-4">
236
- <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Resources</div>
257
+ <div id="resources" className="space-y-4 scroll-mt-6">
258
+ <div className="flex items-center gap-2 px-1">
259
+ <div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Resources</div>
260
+ <div className="flex-1 h-px bg-[#2a2a4a]" />
261
+ </div>
237
262
 
238
263
  {/* API Tokens + Networks */}
239
264
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
@@ -6,6 +6,7 @@ import Link from 'next/link';
6
6
  import { useNetworkId } from '../lib/network-context';
7
7
  import { TaskDrawer } from '../components/TaskDrawer';
8
8
  import { EmptyState } from '../components/EmptyState';
9
+ import { AliasAvatar } from '../components/AliasAvatar';
9
10
 
10
11
  interface Task {
11
12
  task_id: string;
@@ -178,22 +179,37 @@ function TasksContent() {
178
179
  </div>
179
180
  </div>
180
181
 
181
- {/* From/To Filters */}
182
- <div className="flex flex-wrap gap-3 mb-6">
183
- <input
184
- type="text"
185
- value={filterFrom}
186
- onChange={e => setFilterFrom(e.target.value)}
187
- placeholder="From node..."
188
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-40"
189
- />
190
- <input
191
- type="text"
192
- value={filterTo}
193
- onChange={e => setFilterTo(e.target.value)}
194
- placeholder="To node..."
195
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-40"
196
- />
182
+ {/* From/To Filters — same visual block as the status tabs above. */}
183
+ <div className="flex flex-wrap items-center gap-2 mb-6">
184
+ <div className="flex items-center gap-1.5 rounded-lg border border-[#2a2a4a] bg-[#111128] px-2.5 py-1.5 focus-within:border-blue-500/40">
185
+ <span className="text-[10px] uppercase tracking-wide text-gray-600">From</span>
186
+ <input
187
+ type="text"
188
+ value={filterFrom}
189
+ onChange={e => setFilterFrom(e.target.value)}
190
+ placeholder="any node"
191
+ className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
192
+ />
193
+ </div>
194
+ <div className="flex items-center gap-1.5 rounded-lg border border-[#2a2a4a] bg-[#111128] px-2.5 py-1.5 focus-within:border-blue-500/40">
195
+ <span className="text-[10px] uppercase tracking-wide text-gray-600">To</span>
196
+ <input
197
+ type="text"
198
+ value={filterTo}
199
+ onChange={e => setFilterTo(e.target.value)}
200
+ placeholder="any node"
201
+ className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
202
+ />
203
+ </div>
204
+ {(filterStatus || filterFrom || filterTo) && (
205
+ <button
206
+ type="button"
207
+ onClick={() => { setFilterStatus(''); setFilterFrom(''); setFilterTo(''); }}
208
+ className="rounded-lg border border-gray-700 px-2.5 py-1.5 text-[11px] text-gray-500 hover:text-gray-200 hover:border-gray-600"
209
+ >
210
+ Clear filters
211
+ </button>
212
+ )}
197
213
  </div>
198
214
 
199
215
  {/* Status distribution */}
@@ -250,22 +266,39 @@ function TasksContent() {
250
266
  <div className="col-span-2">Time</div>
251
267
  </div>
252
268
 
253
- {tasks.map(t => (
269
+ {tasks.map(t => {
270
+ const isOpen = expanded.has(t.task_id);
271
+ return (
254
272
  <div
255
273
  key={t.task_id}
256
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-4 py-3 hover:border-[#3a3a5a] transition-colors cursor-pointer"
274
+ className={`anet-task-row group bg-[#111128] border rounded-lg px-4 py-3 transition-all duration-200 cursor-pointer ${
275
+ isOpen
276
+ ? 'border-[#3a3a5a] shadow-lg shadow-black/20'
277
+ : 'border-[#2a2a4a] hover:border-[#3a3a5a] hover:bg-[#15152e]'
278
+ }`}
257
279
  onClick={() => toggleExpand(t.task_id)}
280
+ role="button"
281
+ tabIndex={0}
282
+ aria-expanded={isOpen}
283
+ onKeyDown={(e) => {
284
+ if (e.key === 'Enter' || e.key === ' ') {
285
+ e.preventDefault();
286
+ toggleExpand(t.task_id);
287
+ }
288
+ }}
258
289
  >
259
290
  {/* Desktop row */}
260
291
  <div className="hidden sm:grid sm:grid-cols-12 gap-2 items-center">
261
292
  <div className="col-span-1">
262
293
  <span className={statusBadge(t.status)}>{t.status}</span>
263
294
  </div>
264
- <div className="col-span-2 text-sm text-blue-400 truncate" title={t.from_name}>
265
- {t.from_name || '--'}
295
+ <div className="col-span-2 flex items-center gap-1.5 min-w-0" title={t.from_name}>
296
+ {t.from_name && <AliasAvatar alias={t.from_name} size={18} />}
297
+ <span className="truncate text-sm text-gray-200">{t.from_name || '--'}</span>
266
298
  </div>
267
- <div className="col-span-2 text-sm text-cyan-400 truncate" title={t.to_name}>
268
- {t.to_name || '--'}
299
+ <div className="col-span-2 flex items-center gap-1.5 min-w-0" title={t.to_name}>
300
+ {t.to_name && <AliasAvatar alias={t.to_name} size={18} />}
301
+ <span className="truncate text-sm text-gray-200">{t.to_name || '--'}</span>
269
302
  </div>
270
303
  <div className="col-span-4 text-xs text-gray-400 truncate" title={t.content}>
271
304
  {t.content || '--'}
@@ -273,8 +306,15 @@ function TasksContent() {
273
306
  <div className="col-span-1">
274
307
  <span className={`text-xs ${priorityBadge(t.priority)}`}>{t.priority || 'normal'}</span>
275
308
  </div>
276
- <div className="col-span-2 text-xs text-gray-500" title={t.created_at}>
277
- {timeAgo(t.created_at)}
309
+ <div className="col-span-2 text-xs text-gray-500 flex items-center justify-between gap-2" title={t.created_at}>
310
+ <span className="truncate">{timeAgo(t.created_at)}</span>
311
+ <svg
312
+ aria-hidden
313
+ className={`shrink-0 text-gray-600 group-hover:text-gray-400 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
314
+ width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"
315
+ >
316
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 9l6 6 6-6" />
317
+ </svg>
278
318
  </div>
279
319
  </div>
280
320
 
@@ -282,20 +322,38 @@ function TasksContent() {
282
322
  <div className="sm:hidden space-y-2">
283
323
  <div className="flex items-center justify-between">
284
324
  <span className={statusBadge(t.status)}>{t.status}</span>
285
- <span className={`text-xs ${priorityBadge(t.priority)}`}>{t.priority || 'normal'}</span>
325
+ <div className="flex items-center gap-2">
326
+ <span className={`text-xs ${priorityBadge(t.priority)}`}>{t.priority || 'normal'}</span>
327
+ <svg
328
+ aria-hidden
329
+ className={`text-gray-600 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
330
+ width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"
331
+ >
332
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 9l6 6 6-6" />
333
+ </svg>
334
+ </div>
286
335
  </div>
287
- <div className="text-xs text-gray-500">
288
- <span className="text-blue-400">{t.from_name || '--'}</span>
289
- <span className="text-gray-600 mx-1">&rarr;</span>
290
- <span className="text-cyan-400">{t.to_name || '--'}</span>
336
+ <div className="flex items-center gap-1.5 text-xs text-gray-300 min-w-0">
337
+ {t.from_name && <AliasAvatar alias={t.from_name} size={16} />}
338
+ <span className="truncate max-w-[40%]">{t.from_name || '--'}</span>
339
+ <span className="text-gray-600">&rarr;</span>
340
+ {t.to_name && <AliasAvatar alias={t.to_name} size={16} />}
341
+ <span className="truncate max-w-[40%]">{t.to_name || '--'}</span>
291
342
  </div>
292
- <div className="text-xs text-gray-400 truncate">{t.content || '--'}</div>
343
+ <div className="text-xs text-gray-400 line-clamp-1">{t.content || '--'}</div>
293
344
  <div className="text-xs text-gray-600">{timeAgo(t.created_at)}</div>
294
345
  </div>
295
346
 
296
- {/* Expanded detail */}
297
- {expanded.has(t.task_id) && (
298
- <div className="mt-3 pt-3 border-t border-[#2a2a4a] space-y-3">
347
+ {/* Expanded detail — always mounted; grid-rows 0fr↔1fr trick gives
348
+ content-aware smooth max-height animation without measuring DOM. */}
349
+ <div
350
+ className={`grid transition-all duration-300 ease-out ${
351
+ isOpen ? 'grid-rows-[1fr] opacity-100 mt-3' : 'grid-rows-[0fr] opacity-0'
352
+ }`}
353
+ aria-hidden={!isOpen}
354
+ >
355
+ <div className="overflow-hidden">
356
+ <div className="pt-3 border-t border-[#2a2a4a] space-y-3">
299
357
  {t.content && (
300
358
  <div>
301
359
  <div className="text-xs text-gray-600 mb-1">Content</div>
@@ -357,9 +415,11 @@ function TasksContent() {
357
415
  </div>
358
416
  </div>
359
417
  </div>
360
- )}
418
+ </div>
419
+ </div>
361
420
  </div>
362
- ))}
421
+ );
422
+ })}
363
423
  </div>
364
424
  )}
365
425
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.4.5-preview.2",
3
+ "version": "0.4.5-preview.21",
4
4
  "description": "Agent Network Dashboard — Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
@@ -1,3 +0,0 @@
1
- module.exports=[50227,(a,b,c)=>{b.exports=a.x("node:path",()=>require("node:path"))},43515,a=>{"use strict";a.s(["formatUptime",0,function(a){let b=Math.floor(a/3600),c=Math.floor(a%3600/60);return b>0?`${b}h ${c}m`:`${c}m`},"timeAgo",0,function(a){if(!a)return"--";let b=Math.floor((Date.now()-new Date(a.replace(" ","T")+"Z").getTime())/1e3);return b<60?`${b}s ago`:b<3600?`${Math.floor(b/60)}m ago`:b<86400?`${Math.floor(b/3600)}h ago`:`${Math.floor(b/86400)}d ago`}])},53142,a=>{"use strict";var b=a.i(87924),c=a.i(38246);let d={nodes:{title:"No agents in this network",sub:"Agent sessions will appear here once they connect to the CommHub."},tasks:{title:"No tasks yet",sub:"Tasks will appear here when agents send them via CommHub."},messages:{title:"No messages",sub:"Messages between agents will appear here."},logs:{title:"No audit logs",sub:"Events will appear here when users register, login, or perform actions."},tokens:{title:"No API tokens",sub:"Create one to authenticate CLI tools and external integrations."},networks:{title:"No networks found",sub:"Create one or sign in with V3 auth to see your networks."},generic:{title:"Nothing here yet",sub:"Data will appear here once available."}};function e({variant:a,size:c}){let d={width:c,height:c},f={fill:"none",stroke:"currentColor",strokeWidth:1.25,strokeLinecap:"round",strokeLinejoin:"round"};switch(a){case"nodes":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("circle",{cx:"32",cy:"14",r:"4"}),(0,b.jsx)("circle",{cx:"14",cy:"44",r:"4"}),(0,b.jsx)("circle",{cx:"50",cy:"44",r:"4"}),(0,b.jsx)("line",{x1:"32",y1:"18",x2:"14",y2:"40",strokeDasharray:"3 3",opacity:"0.6"}),(0,b.jsx)("line",{x1:"32",y1:"18",x2:"50",y2:"40",strokeDasharray:"3 3",opacity:"0.6"}),(0,b.jsx)("line",{x1:"18",y1:"44",x2:"46",y2:"44",strokeDasharray:"3 3",opacity:"0.6"})]})});case"tasks":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("rect",{x:"14",y:"14",width:"36",height:"6",rx:"1.5"}),(0,b.jsx)("rect",{x:"14",y:"26",width:"36",height:"6",rx:"1.5",opacity:"0.6"}),(0,b.jsx)("rect",{x:"14",y:"38",width:"36",height:"6",rx:"1.5",opacity:"0.35"})]})});case"messages":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("path",{d:"M14 18 h36 a3 3 0 0 1 3 3 v18 a3 3 0 0 1 -3 3 h-18 l-8 6 v-6 h-10 a3 3 0 0 1 -3 -3 v-18 a3 3 0 0 1 3 -3 z"}),(0,b.jsx)("line",{x1:"22",y1:"28",x2:"42",y2:"28",opacity:"0.5"}),(0,b.jsx)("line",{x1:"22",y1:"34",x2:"36",y2:"34",opacity:"0.5"})]})});case"logs":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("path",{d:"M18 12 h22 l8 8 v32 a2 2 0 0 1 -2 2 h-28 a2 2 0 0 1 -2 -2 v-38 a2 2 0 0 1 2 -2 z"}),(0,b.jsx)("path",{d:"M40 12 v8 h8",opacity:"0.6"}),(0,b.jsx)("line",{x1:"24",y1:"32",x2:"40",y2:"32",opacity:"0.55"}),(0,b.jsx)("line",{x1:"24",y1:"38",x2:"40",y2:"38",opacity:"0.4"}),(0,b.jsx)("line",{x1:"24",y1:"44",x2:"34",y2:"44",opacity:"0.25"})]})});case"tokens":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("circle",{cx:"22",cy:"32",r:"8"}),(0,b.jsx)("line",{x1:"30",y1:"32",x2:"54",y2:"32"}),(0,b.jsx)("line",{x1:"44",y1:"32",x2:"44",y2:"38"}),(0,b.jsx)("line",{x1:"50",y1:"32",x2:"50",y2:"40"})]})});case"networks":return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("circle",{cx:"32",cy:"32",r:"18"}),(0,b.jsx)("ellipse",{cx:"32",cy:"32",rx:"18",ry:"9",opacity:"0.55"}),(0,b.jsx)("line",{x1:"14",y1:"32",x2:"50",y2:"32",opacity:"0.55"}),(0,b.jsx)("line",{x1:"32",y1:"14",x2:"32",y2:"50",opacity:"0.55"})]})});default:return(0,b.jsx)("svg",{viewBox:"0 0 64 64",...d,children:(0,b.jsxs)("g",{...f,children:[(0,b.jsx)("circle",{cx:"32",cy:"32",r:"14",strokeDasharray:"3 3",opacity:"0.6"}),(0,b.jsx)("circle",{cx:"32",cy:"32",r:"3"})]})})}}function f({variant:a="generic",title:g,sub:h,cta:i,compact:j=!1}){let k=d[a],l=g??k.title,m=h??k.sub;return(0,b.jsxs)("div",{className:`text-center ${j?"py-8":"py-16"} px-4`,role:"status",children:[(0,b.jsx)("div",{className:"anet-empty-glyph inline-flex items-center justify-center mb-4 text-gray-500","aria-hidden":!0,children:(0,b.jsx)(e,{variant:a,size:j?40:56})}),(0,b.jsx)("h3",{className:`font-medium text-gray-300 ${j?"text-sm":"text-base"}`,children:l}),m&&(0,b.jsx)("p",{className:`text-gray-500 ${j?"text-xs mt-1.5":"text-sm mt-2"} max-w-md mx-auto leading-relaxed`,children:m}),i&&(0,b.jsx)("div",{className:"mt-4",children:i.href?(0,b.jsxs)(c.default,{href:i.href,className:"inline-flex items-center gap-1.5 text-sm font-medium text-cyan-400 hover:text-cyan-300",children:[i.label,(0,b.jsx)("span",{"aria-hidden":!0,children:"→"})]}):(0,b.jsxs)("button",{onClick:i.onClick,className:"inline-flex items-center gap-1.5 text-sm font-medium text-cyan-400 hover:text-cyan-300",children:[i.label,(0,b.jsx)("span",{"aria-hidden":!0,children:"→"})]})})]})}a.s(["EmptyState",0,f,"NodesEmptyState",0,function({hint:a}){return a?.global_count?(0,b.jsx)(f,{variant:"nodes",title:"No agents in this network",sub:`Server has ${a.global_count} nodes globally, but none are registered to the current network. Switch network or contact admin.`}):(0,b.jsx)(f,{variant:"nodes",cta:{label:"Run anet quickstart",href:"https://anet.sh"}})}])},44332,a=>{"use strict";var b=a.i(72596),c=a.i(22397);let d=async a=>{let b=await fetch(a);if(401===b.status)throw window.location.assign("/login"),Error("unauthorized");return b.json()},e={refreshInterval:5e3,dedupingInterval:3e3};function f(a,b){if(!b)return a;let c=a.includes("?")?"&":"?";return`${a}${c}network_id=${encodeURIComponent(b)}`}a.s(["useAnetConfig",0,function(){let{data:a}=(0,b.default)("/api/anet/config",d,{refreshInterval:3e4});return{config:a||null}},"useHealth",0,function(){let{data:a,error:c}=(0,b.default)("/api/hub/health",d,e);return{health:a||null,error:c}},"useLicense",0,function(){let{data:a,error:c}=(0,b.default)("/api/hub/license",d,{refreshInterval:6e4});return{license:a?.ok?a:null,error:c}},"useMessages",0,function(a=100){let{networkId:g}=(0,c.useNetworkId)(),{data:h,error:i,isLoading:j}=(0,b.default)(f(`/api/hub/messages?limit=${a}`,g),d,e);return{messages:h?.messages||[],error:i,isLoading:j}},"useSessions",0,function(){let{networkId:a}=(0,c.useNetworkId)(),{data:g,error:h,isLoading:i}=(0,b.default)(f("/api/hub/status",a),d,e);return{sessions:g?.sessions||[],hint:g?._hint,error:h,isLoading:i}},"useStats",0,function(){let{networkId:a}=(0,c.useNetworkId)(),{data:g,error:h}=(0,b.default)(f("/api/hub/stats",a),d,e);return{stats:g?.ok?g:null,error:h}},"useTasks",0,function(a){let{networkId:g}=(0,c.useNetworkId)(),h=new URLSearchParams({limit:"100",...a}).toString(),{data:i,error:j,isLoading:k}=(0,b.default)(f(`/api/hub/tasks?${h}`,g),d,e);return{tasks:i?.tasks||[],count:i?.count??0,source:i?.source,error:j,isLoading:k}}])},54826,(a,b,c)=>{},15299,(a,b,c)=>{a.r(54826);var d=a.r(72131),e=d&&"object"==typeof d&&"default"in d?d:{default:d},f="u">typeof process&&process.env&&!0,g=function(a){return"[object String]"===Object.prototype.toString.call(a)},h=function(){function a(a){var b=void 0===a?{}:a,c=b.name,d=void 0===c?"stylesheet":c,e=b.optimizeForSpeed,h=void 0===e?f:e;i(g(d),"`name` must be a string"),this._name=d,this._deletedRulePlaceholder="#"+d+"-deleted-rule____{}",i("boolean"==typeof h,"`optimizeForSpeed` must be a boolean"),this._optimizeForSpeed=h,this._serverSheet=void 0,this._tags=[],this._injected=!1,this._rulesCount=0,this._nonce=null}var b,c=a.prototype;return c.setOptimizeForSpeed=function(a){i("boolean"==typeof a,"`setOptimizeForSpeed` accepts a boolean"),i(0===this._rulesCount,"optimizeForSpeed cannot be when rules have already been inserted"),this.flush(),this._optimizeForSpeed=a,this.inject()},c.isOptimizeForSpeed=function(){return this._optimizeForSpeed},c.inject=function(){var a=this;i(!this._injected,"sheet already injected"),this._injected=!0,this._serverSheet={cssRules:[],insertRule:function(b,c){return"number"==typeof c?a._serverSheet.cssRules[c]={cssText:b}:a._serverSheet.cssRules.push({cssText:b}),c},deleteRule:function(b){a._serverSheet.cssRules[b]=null}}},c.getSheetForTag=function(a){if(a.sheet)return a.sheet;for(var b=0;b<document.styleSheets.length;b++)if(document.styleSheets[b].ownerNode===a)return document.styleSheets[b]},c.getSheet=function(){return this.getSheetForTag(this._tags[this._tags.length-1])},c.insertRule=function(a,b){return i(g(a),"`insertRule` accepts only strings"),"number"!=typeof b&&(b=this._serverSheet.cssRules.length),this._serverSheet.insertRule(a,b),this._rulesCount++},c.replaceRule=function(a,b){this._optimizeForSpeed;var c=this._serverSheet;if(b.trim()||(b=this._deletedRulePlaceholder),!c.cssRules[a])return a;c.deleteRule(a);try{c.insertRule(b,a)}catch(d){f||console.warn("StyleSheet: illegal rule: \n\n"+b+"\n\nSee https://stackoverflow.com/q/20007992 for more info"),c.insertRule(this._deletedRulePlaceholder,a)}return a},c.deleteRule=function(a){this._serverSheet.deleteRule(a)},c.flush=function(){this._injected=!1,this._rulesCount=0,this._serverSheet.cssRules=[]},c.cssRules=function(){return this._serverSheet.cssRules},c.makeStyleTag=function(a,b,c){b&&i(g(b),"makeStyleTag accepts only strings as second parameter");var d=document.createElement("style");this._nonce&&d.setAttribute("nonce",this._nonce),d.type="text/css",d.setAttribute("data-"+a,""),b&&d.appendChild(document.createTextNode(b));var e=document.head||document.getElementsByTagName("head")[0];return c?e.insertBefore(d,c):e.appendChild(d),d},b=[{key:"length",get:function(){return this._rulesCount}}],function(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}(a.prototype,b),a}();function i(a,b){if(!a)throw Error("StyleSheet: "+b+".")}var j=function(a){for(var b=5381,c=a.length;c;)b=33*b^a.charCodeAt(--c);return b>>>0},k={};function l(a,b){if(!b)return"jsx-"+a;var c=String(b),d=a+c;return k[d]||(k[d]="jsx-"+j(a+"-"+c)),k[d]}function m(a,b){var c=a+(b=b.replace(/\/style/gi,"\\/style"));return k[c]||(k[c]=b.replace(/__jsx-style-dynamic-selector/g,a)),k[c]}var n=function(){function a(a){var b=void 0===a?{}:a,c=b.styleSheet,d=void 0===c?null:c,e=b.optimizeForSpeed,f=void 0!==e&&e;this._sheet=d||new h({name:"styled-jsx",optimizeForSpeed:f}),this._sheet.inject(),d&&"boolean"==typeof f&&(this._sheet.setOptimizeForSpeed(f),this._optimizeForSpeed=this._sheet.isOptimizeForSpeed()),this._fromServer=void 0,this._indices={},this._instancesCounts={}}var b=a.prototype;return b.add=function(a){var b=this;void 0===this._optimizeForSpeed&&(this._optimizeForSpeed=Array.isArray(a.children),this._sheet.setOptimizeForSpeed(this._optimizeForSpeed),this._optimizeForSpeed=this._sheet.isOptimizeForSpeed());var c=this.getIdAndRules(a),d=c.styleId,e=c.rules;if(d in this._instancesCounts){this._instancesCounts[d]+=1;return}var f=e.map(function(a){return b._sheet.insertRule(a)}).filter(function(a){return -1!==a});this._indices[d]=f,this._instancesCounts[d]=1},b.remove=function(a){var b=this,c=this.getIdAndRules(a).styleId;if(function(a,b){if(!a)throw Error("StyleSheetRegistry: "+b+".")}(c in this._instancesCounts,"styleId: `"+c+"` not found"),this._instancesCounts[c]-=1,this._instancesCounts[c]<1){var d=this._fromServer&&this._fromServer[c];d?(d.parentNode.removeChild(d),delete this._fromServer[c]):(this._indices[c].forEach(function(a){return b._sheet.deleteRule(a)}),delete this._indices[c]),delete this._instancesCounts[c]}},b.update=function(a,b){this.add(b),this.remove(a)},b.flush=function(){this._sheet.flush(),this._sheet.inject(),this._fromServer=void 0,this._indices={},this._instancesCounts={}},b.cssRules=function(){var a=this,b=this._fromServer?Object.keys(this._fromServer).map(function(b){return[b,a._fromServer[b]]}):[],c=this._sheet.cssRules();return b.concat(Object.keys(this._indices).map(function(b){return[b,a._indices[b].map(function(a){return c[a].cssText}).join(a._optimizeForSpeed?"":"\n")]}).filter(function(a){return!!a[1]}))},b.styles=function(a){var b,c;return b=this.cssRules(),void 0===(c=a)&&(c={}),b.map(function(a){var b=a[0],d=a[1];return e.default.createElement("style",{id:"__"+b,key:"__"+b,nonce:c.nonce?c.nonce:void 0,dangerouslySetInnerHTML:{__html:d}})})},b.getIdAndRules=function(a){var b=a.children,c=a.dynamic,d=a.id;if(c){var e=l(d,c);return{styleId:e,rules:Array.isArray(b)?b.map(function(a){return m(e,a)}):[m(e,b)]}}return{styleId:l(d),rules:Array.isArray(b)?b:[b]}},b.selectFromServer=function(){return Array.prototype.slice.call(document.querySelectorAll('[id^="__jsx-"]')).reduce(function(a,b){return a[b.id.slice(2)]=b,a},{})},a}(),o=d.createContext(null);function p(){return new n}function q(){return d.useContext(o)}function r(a){var b=q();return b&&b.add(a),null}o.displayName="StyleSheetContext",e.default.useInsertionEffect||e.default.useLayoutEffect,r.dynamic=function(a){return a.map(function(a){return l(a[0],a[1])}).join(" ")},c.StyleRegistry=function(a){var b=a.registry,c=a.children,f=d.useContext(o),g=d.useState(function(){return f||b||p()})[0];return e.default.createElement(o.Provider,{value:g},c)},c.createStyleRegistry=p,c.style=r,c.useStyleRegistry=q},31626,(a,b,c)=>{b.exports=a.r(15299).style}];
2
-
3
- //# sourceMappingURL=%5Broot-of-the-server%5D__0_jnflc._.js.map