@sleep2agi/agent-network-dashboard 0.5.3-preview.265 → 0.5.3-preview.267
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/app-path-routes-manifest.json +2 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +35 -35
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +29 -0
- package/.next/routes-manifest.json +12 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +2 -2
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/server/app/_not-found.rsc +13 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +15 -15
- package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
- 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/api/hub/upload/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/upload/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/upload/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/upload/route.js +7 -0
- package/.next/server/app/api/hub/upload/route.js.map +5 -0
- package/.next/server/app/api/hub/upload/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/upload/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +15 -15
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +15 -15
- package/.next/server/app/login.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +15 -15
- package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
- 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/manifest.webmanifest/route/app-paths-manifest.json +3 -0
- package/.next/server/app/manifest.webmanifest/route/build-manifest.json +9 -0
- package/.next/server/app/manifest.webmanifest/route/server-reference-manifest.json +4 -0
- package/.next/server/app/manifest.webmanifest/route.js +8 -0
- package/.next/server/app/manifest.webmanifest/route.js.map +5 -0
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +3 -0
- package/.next/server/app/manifest.webmanifest.body +1 -0
- package/.next/server/app/manifest.webmanifest.meta +1 -0
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +15 -15
- package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +15 -15
- package/.next/server/app/node.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +15 -15
- package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
- 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_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 +2 -2
- package/.next/server/app/server-logs.rsc +15 -15
- package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +15 -15
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
- 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_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 +2 -2
- package/.next/server/app/settings/tokens.rsc +15 -15
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
- 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 +2 -2
- package/.next/server/app/settings.rsc +15 -15
- package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +15 -15
- package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
- 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/app-paths-manifest.json +2 -0
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +18 -0
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js.map +1 -0
- package/.next/server/chunks/{[externals]__11vad82._.js → [externals]__036g1.i._.js} +2 -2
- package/.next/server/chunks/[externals]__036g1.i._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js.map +1 -0
- package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js → [root-of-the-server]__0jndzts._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js.map → [root-of-the-server]__0jndzts._.js.map} +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +4 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/middleware.js +2 -2
- package/.next/server/middleware.js.nft.json +1 -1
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/{0hgizy171xukz.js → 07to0dy21pil-.js} +1 -1
- package/.next/static/chunks/{07o87nb~cwjcs.js → 089t1exs6apb8.js} +3 -3
- package/.next/static/chunks/{0tl4r8_~2_.r2.js → 09rzd1.kojbsa.js} +1 -1
- package/.next/static/chunks/0ae.az.vfm-il.css +2 -0
- package/.next/static/chunks/{07~8sx3_5uiox.js → 0ir6pphfe2yvm.js} +4 -1
- package/.next/static/chunks/{0v4-5tng.uh.7.js → 0k-c1chkvf78s.js} +2 -2
- package/.next/static/chunks/{0vx~f61uxwjb8.js → 0uqplti.5qwnv.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/app/api/hub/upload/route.ts +83 -0
- package/app/components/AppShell.tsx +3 -1
- package/app/components/MobileNav.tsx +45 -0
- package/app/components/TaskChatPanel.tsx +82 -5
- package/app/components/TopoGraph.tsx +66 -24
- package/app/layout.tsx +6 -0
- package/app/manifest.ts +23 -0
- package/package.json +2 -2
- package/.next/static/chunks/16-7qr7qx9zrz.css +0 -2
- /package/.next/server/chunks/{[externals]__11vad82._.js.map → 0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js.map} +0 -0
- /package/.next/static/{xShHwZyMG8wh9mMzBFNLC → OCWKpyGElLOWutcoW5Ya9}/_buildManifest.js +0 -0
- /package/.next/static/{xShHwZyMG8wh9mMzBFNLC → OCWKpyGElLOWutcoW5Ya9}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{xShHwZyMG8wh9mMzBFNLC → OCWKpyGElLOWutcoW5Ya9}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { requireDashboardAuth } from '@/app/lib/dashboard-auth';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
const UPLOAD_DIR = '/tmp/agent-network-dashboard-uploads';
|
|
7
|
+
const MAX_BYTES = 12 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
function safeName(name: string) {
|
|
10
|
+
const ext = path.extname(name).toLowerCase().replace(/[^a-z0-9.]/g, '');
|
|
11
|
+
return `${Date.now()}-${randomUUID()}${ext || '.bin'}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isImage(type: string, filename: string) {
|
|
15
|
+
return type.startsWith('image/') || /\.(png|jpe?g|gif|webp)$/i.test(filename);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function POST(req: Request) {
|
|
19
|
+
const authFailure = await requireDashboardAuth();
|
|
20
|
+
if (authFailure) return authFailure;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const form = await req.formData();
|
|
24
|
+
const file = form.get('file');
|
|
25
|
+
if (!(file instanceof File)) {
|
|
26
|
+
return Response.json({ error: 'missing file' }, { status: 400 });
|
|
27
|
+
}
|
|
28
|
+
if (!isImage(file.type, file.name)) {
|
|
29
|
+
return Response.json({ error: 'only image uploads are supported' }, { status: 400 });
|
|
30
|
+
}
|
|
31
|
+
if (file.size > MAX_BYTES) {
|
|
32
|
+
return Response.json({ error: 'image is too large', max_bytes: MAX_BYTES }, { status: 413 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await mkdir(UPLOAD_DIR, { recursive: true });
|
|
36
|
+
const filename = safeName(file.name);
|
|
37
|
+
const fullPath = path.join(UPLOAD_DIR, filename);
|
|
38
|
+
const bytes = Buffer.from(await file.arrayBuffer());
|
|
39
|
+
await writeFile(fullPath, bytes);
|
|
40
|
+
|
|
41
|
+
return Response.json({
|
|
42
|
+
ok: true,
|
|
43
|
+
filename,
|
|
44
|
+
path: fullPath,
|
|
45
|
+
url: `/api/hub/upload?file=${encodeURIComponent(filename)}`,
|
|
46
|
+
mime: file.type || 'application/octet-stream',
|
|
47
|
+
size: file.size,
|
|
48
|
+
});
|
|
49
|
+
} catch (e: unknown) {
|
|
50
|
+
return Response.json({ error: 'upload failed', detail: e instanceof Error ? e.message : String(e) }, { status: 500 });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function GET(req: Request) {
|
|
55
|
+
const authFailure = await requireDashboardAuth();
|
|
56
|
+
if (authFailure) return authFailure;
|
|
57
|
+
|
|
58
|
+
const url = new URL(req.url);
|
|
59
|
+
const filename = url.searchParams.get('file') || '';
|
|
60
|
+
if (!/^[0-9]+-[a-f0-9-]+\.[a-z0-9]+$/i.test(filename)) {
|
|
61
|
+
return Response.json({ error: 'invalid file' }, { status: 400 });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const fullPath = path.join(UPLOAD_DIR, filename);
|
|
66
|
+
const data = await readFile(fullPath);
|
|
67
|
+
const ext = path.extname(filename).toLowerCase();
|
|
68
|
+
const mime =
|
|
69
|
+
ext === '.png' ? 'image/png' :
|
|
70
|
+
ext === '.jpg' || ext === '.jpeg' ? 'image/jpeg' :
|
|
71
|
+
ext === '.gif' ? 'image/gif' :
|
|
72
|
+
ext === '.webp' ? 'image/webp' :
|
|
73
|
+
'application/octet-stream';
|
|
74
|
+
return new Response(data, {
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': mime,
|
|
77
|
+
'Cache-Control': 'private, max-age=86400',
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
} catch {
|
|
81
|
+
return Response.json({ error: 'not found' }, { status: 404 });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -6,6 +6,7 @@ import { HealthBanner } from './HealthBanner';
|
|
|
6
6
|
import { CommandPalette } from './CommandPalette';
|
|
7
7
|
import { HelpOverlay } from './HelpOverlay';
|
|
8
8
|
import { ServersDrawer } from './ServersDrawer';
|
|
9
|
+
import { MobileNav } from './MobileNav';
|
|
9
10
|
|
|
10
11
|
const NO_SHELL_PATHS = ['/login'];
|
|
11
12
|
|
|
@@ -21,8 +22,9 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
|
|
21
22
|
<Sidebar />
|
|
22
23
|
<main className="flex-1 min-w-0 flex flex-col">
|
|
23
24
|
<HealthBanner />
|
|
24
|
-
<div className="flex-1 min-w-0">{children}</div>
|
|
25
|
+
<div className="flex-1 min-w-0 pb-20 lg:pb-0">{children}</div>
|
|
25
26
|
</main>
|
|
27
|
+
<MobileNav />
|
|
26
28
|
<CommandPalette />
|
|
27
29
|
<HelpOverlay />
|
|
28
30
|
<ServersDrawer />
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
5
|
+
|
|
6
|
+
const MOBILE_NAV_ITEMS = [
|
|
7
|
+
{ href: '/', label: 'Home', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' },
|
|
8
|
+
{ href: '/tasks', label: 'Tasks', icon: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4' },
|
|
9
|
+
{ href: '/nodes', label: 'Agents', icon: 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01' },
|
|
10
|
+
{ href: '/messages', label: 'Chats', icon: 'M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z' },
|
|
11
|
+
{ href: '/logs', label: 'Status', icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function MobileNav() {
|
|
15
|
+
const pathname = usePathname();
|
|
16
|
+
const isActive = (href: string) => href === '/' ? pathname === '/' : pathname.startsWith(href);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<nav className="fixed inset-x-0 bottom-0 z-40 border-t border-[#2a2a4a] bg-[#0d0d1a]/95 px-1 pb-[calc(env(safe-area-inset-bottom)+0.25rem)] pt-1.5 backdrop-blur lg:hidden">
|
|
20
|
+
<div className="mx-auto grid max-w-md grid-cols-5 gap-1">
|
|
21
|
+
{MOBILE_NAV_ITEMS.map(item => {
|
|
22
|
+
const active = isActive(item.href);
|
|
23
|
+
return (
|
|
24
|
+
<Link
|
|
25
|
+
key={item.href}
|
|
26
|
+
href={item.href}
|
|
27
|
+
prefetch={false}
|
|
28
|
+
aria-current={active ? 'page' : undefined}
|
|
29
|
+
className={`flex min-h-12 flex-col items-center justify-center gap-0.5 rounded-xl px-1 text-[10px] transition-colors ${
|
|
30
|
+
active
|
|
31
|
+
? 'bg-cyan-500/12 text-cyan-300'
|
|
32
|
+
: 'text-gray-500 active:bg-[#1a1a3a] active:text-gray-200'
|
|
33
|
+
}`}
|
|
34
|
+
>
|
|
35
|
+
<svg className="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.6}>
|
|
36
|
+
<path strokeLinecap="round" strokeLinejoin="round" d={item.icon} />
|
|
37
|
+
</svg>
|
|
38
|
+
<span className="max-w-full truncate">{item.label}</span>
|
|
39
|
+
</Link>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
</div>
|
|
43
|
+
</nav>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -123,6 +123,7 @@ interface TaskChatPanelProps {
|
|
|
123
123
|
export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskChatPanelProps) {
|
|
124
124
|
const [messages, setMessages] = useState<ChatTask[]>([]);
|
|
125
125
|
const [input, setInput] = useState('');
|
|
126
|
+
const [attachedFiles, setAttachedFiles] = useState<File[]>([]);
|
|
126
127
|
const [priority, setPriority] = useState('normal');
|
|
127
128
|
const [sending, setSending] = useState(false);
|
|
128
129
|
const [pollingIds, setPollingIds] = useState<Set<string>>(new Set());
|
|
@@ -228,8 +229,21 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
228
229
|
return () => clearInterval(interval);
|
|
229
230
|
}, [pollingIds]);
|
|
230
231
|
|
|
232
|
+
const uploadAttachments = async () => {
|
|
233
|
+
const uploaded = [];
|
|
234
|
+
for (const file of attachedFiles) {
|
|
235
|
+
const form = new FormData();
|
|
236
|
+
form.append('file', file);
|
|
237
|
+
const res = await fetch('/api/hub/upload', { method: 'POST', body: form });
|
|
238
|
+
const data = await res.json().catch(() => ({ ok: false, error: `HTTP ${res.status}` }));
|
|
239
|
+
if (!res.ok || !data.ok) throw new Error(data.error || data.detail || `upload failed: ${file.name}`);
|
|
240
|
+
uploaded.push({ name: file.name, path: data.path, url: data.url });
|
|
241
|
+
}
|
|
242
|
+
return uploaded;
|
|
243
|
+
};
|
|
244
|
+
|
|
231
245
|
const send = async () => {
|
|
232
|
-
if (!input.trim() || sending) return;
|
|
246
|
+
if ((!input.trim() && attachedFiles.length === 0) || sending) return;
|
|
233
247
|
let taskContent = input.trim();
|
|
234
248
|
let sendTo = targetAlias;
|
|
235
249
|
|
|
@@ -245,9 +259,14 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
245
259
|
|
|
246
260
|
setSending(true);
|
|
247
261
|
setInput('');
|
|
262
|
+
setAttachedFiles([]);
|
|
248
263
|
setShowMentions(false);
|
|
249
264
|
if (textareaRef.current) textareaRef.current.style.height = 'auto';
|
|
250
265
|
|
|
266
|
+
const pendingAttachmentText = attachedFiles.length > 0
|
|
267
|
+
? `\n\n[Dashboard 附件待上传]\n${attachedFiles.map(f => `- 图片: ${f.name}`).join('\n')}`
|
|
268
|
+
: '';
|
|
269
|
+
|
|
251
270
|
// Optimistic echo first so the user always sees their message in history
|
|
252
271
|
// even if the network call fails or the server returns ok:false. Earlier
|
|
253
272
|
// version only added on data.ok=true → silent failures looked like
|
|
@@ -259,13 +278,27 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
259
278
|
to_name: sendTo,
|
|
260
279
|
status: 'created',
|
|
261
280
|
priority,
|
|
262
|
-
content: taskContent,
|
|
281
|
+
content: `${taskContent}${pendingAttachmentText}`.trim(),
|
|
263
282
|
result: '',
|
|
264
283
|
created_at: new Date().toISOString(),
|
|
265
284
|
};
|
|
266
285
|
setMessages(prev => [...prev, optimistic]);
|
|
267
286
|
|
|
268
287
|
try {
|
|
288
|
+
const uploaded = await uploadAttachments();
|
|
289
|
+
if (uploaded.length > 0) {
|
|
290
|
+
const attachmentText = [
|
|
291
|
+
'',
|
|
292
|
+
'',
|
|
293
|
+
'[Dashboard 附件]',
|
|
294
|
+
...uploaded.map(f => `- 图片: ${f.path}`),
|
|
295
|
+
...uploaded.map(f => `- 预览: ${f.url}`),
|
|
296
|
+
].join('\n');
|
|
297
|
+
taskContent = `${taskContent}${attachmentText}`.trim();
|
|
298
|
+
setMessages(prev => prev.map(m =>
|
|
299
|
+
m.task_id === localTaskId ? { ...m, content: taskContent } : m,
|
|
300
|
+
));
|
|
301
|
+
}
|
|
269
302
|
const res = await fetch('/api/hub/send', {
|
|
270
303
|
method: 'POST',
|
|
271
304
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -318,6 +351,20 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
318
351
|
setShowMentions(false);
|
|
319
352
|
};
|
|
320
353
|
|
|
354
|
+
const addImageFiles = (files: FileList | File[]) => {
|
|
355
|
+
const images = Array.from(files).filter(file => file.type.startsWith('image/'));
|
|
356
|
+
if (images.length === 0) return;
|
|
357
|
+
setAttachedFiles(prev => [...prev, ...images].slice(0, 6));
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const handlePaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
|
361
|
+
const files = Array.from(e.clipboardData.files || []);
|
|
362
|
+
if (files.some(file => file.type.startsWith('image/'))) {
|
|
363
|
+
addImageFiles(files);
|
|
364
|
+
e.preventDefault();
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
321
368
|
const insertMention = (nodeName: string) => {
|
|
322
369
|
const lastAt = input.lastIndexOf('@');
|
|
323
370
|
const before = input.slice(0, lastAt);
|
|
@@ -437,11 +484,24 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
437
484
|
value={input}
|
|
438
485
|
onChange={e => { handleInputChange(e.target.value); autoResize(e.target); }}
|
|
439
486
|
onKeyDown={handleKeyDown}
|
|
487
|
+
onPaste={handlePaste}
|
|
440
488
|
placeholder={`Message ${alias}...`}
|
|
441
489
|
rows={1}
|
|
442
|
-
className="w-full bg-[var(--bg-secondary)] border border-[var(--border)] rounded-xl px-4 py-2.5 pr-
|
|
490
|
+
className="w-full bg-[var(--bg-secondary)] border border-[var(--border)] rounded-xl px-4 py-2.5 pr-24 text-sm text-[var(--fg)] placeholder-[var(--fg-dim)] focus:border-cyan-500/40 focus:outline-none resize-none transition-colors"
|
|
443
491
|
/>
|
|
444
492
|
<div className="absolute right-2 bottom-1.5 flex items-center gap-1">
|
|
493
|
+
<label className="p-1 text-[var(--fg-dim)] hover:text-cyan-300 cursor-pointer rounded hover:bg-cyan-500/10" title="Attach image">
|
|
494
|
+
<input
|
|
495
|
+
type="file"
|
|
496
|
+
accept="image/*"
|
|
497
|
+
multiple
|
|
498
|
+
className="hidden"
|
|
499
|
+
onChange={e => { if (e.target.files) addImageFiles(e.target.files); e.currentTarget.value = ''; }}
|
|
500
|
+
/>
|
|
501
|
+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
502
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 6.5l-7.78 7.78a3 3 0 104.24 4.24l8.49-8.49a5 5 0 00-7.07-7.07L5.89 11.45a7 7 0 109.9 9.9l7.07-7.07" />
|
|
503
|
+
</svg>
|
|
504
|
+
</label>
|
|
445
505
|
<select value={priority} onChange={e => setPriority(e.target.value)}
|
|
446
506
|
className="bg-transparent text-[9px] text-[var(--fg-dim)] focus:outline-none cursor-pointer">
|
|
447
507
|
<option value="normal">N</option>
|
|
@@ -450,7 +510,7 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
450
510
|
</select>
|
|
451
511
|
</div>
|
|
452
512
|
</div>
|
|
453
|
-
<button onClick={send} disabled={sending || !input.trim()}
|
|
513
|
+
<button onClick={send} disabled={sending || (!input.trim() && attachedFiles.length === 0)}
|
|
454
514
|
className="p-2.5 bg-cyan-600 hover:bg-cyan-500 disabled:bg-[var(--border)] disabled:text-[var(--fg-dim)] text-[var(--fg)] rounded-xl transition-all shrink-0 active:scale-95">
|
|
455
515
|
{sending ? (
|
|
456
516
|
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
@@ -461,9 +521,26 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
461
521
|
)}
|
|
462
522
|
</button>
|
|
463
523
|
</div>
|
|
524
|
+
{attachedFiles.length > 0 && (
|
|
525
|
+
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
526
|
+
{attachedFiles.map((file, index) => (
|
|
527
|
+
<span key={`${file.name}-${index}`} className="inline-flex items-center gap-1.5 max-w-[160px] px-2 py-1 rounded-lg border border-cyan-500/20 bg-cyan-500/8 text-[10px] text-cyan-200">
|
|
528
|
+
<span className="truncate">{file.name}</span>
|
|
529
|
+
<button
|
|
530
|
+
type="button"
|
|
531
|
+
onClick={() => setAttachedFiles(prev => prev.filter((_, i) => i !== index))}
|
|
532
|
+
className="text-cyan-300 hover:text-white"
|
|
533
|
+
aria-label={`Remove ${file.name}`}
|
|
534
|
+
>
|
|
535
|
+
×
|
|
536
|
+
</button>
|
|
537
|
+
</span>
|
|
538
|
+
))}
|
|
539
|
+
</div>
|
|
540
|
+
)}
|
|
464
541
|
<div className="flex justify-between text-[9px] text-[var(--fg-dim)] mt-1.5">
|
|
465
542
|
<span>{input.includes('@') ? `Sending to: ${targetAlias}` : `Type @ to mention another node`}</span>
|
|
466
|
-
<span>Enter to send ·
|
|
543
|
+
<span>Enter to send · paste image</span>
|
|
467
544
|
</div>
|
|
468
545
|
</div>
|
|
469
546
|
</>
|
|
@@ -72,6 +72,19 @@ interface MessageFlow {
|
|
|
72
72
|
created_at: string;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
interface TaskFlow {
|
|
76
|
+
from_name?: string | null;
|
|
77
|
+
from_alias?: string | null;
|
|
78
|
+
to_name?: string | null;
|
|
79
|
+
to_alias?: string | null;
|
|
80
|
+
content?: string | null;
|
|
81
|
+
status?: string | null;
|
|
82
|
+
created_at?: string | null;
|
|
83
|
+
delivered_at?: string | null;
|
|
84
|
+
started_at?: string | null;
|
|
85
|
+
completed_at?: string | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
interface TopoGraphProps {
|
|
76
89
|
sessions: Session[];
|
|
77
90
|
sseSessions: Record<string, number>;
|
|
@@ -559,39 +572,63 @@ function computeGroups(sessions: { alias: string }[]): Record<string, string> {
|
|
|
559
572
|
return keys;
|
|
560
573
|
}
|
|
561
574
|
|
|
562
|
-
function buildFlowLinks(messages: MessageFlow[], positions: Record<string, Point>) {
|
|
575
|
+
function buildFlowLinks(messages: MessageFlow[], tasks: TaskFlow[], positions: Record<string, Point>) {
|
|
563
576
|
const links = new Map<string, FlowLink>();
|
|
564
577
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
)
|
|
578
|
+
const commandAlias = Object.keys(positions).find(a => a.includes('副指挥'))
|
|
579
|
+
?? Object.keys(positions).find(a => a.includes('总指挥'))
|
|
580
|
+
?? null;
|
|
581
|
+
|
|
582
|
+
const resolveAlias = (alias: string | null | undefined) => {
|
|
583
|
+
const clean = (alias || '').trim();
|
|
584
|
+
if (!clean) return null;
|
|
585
|
+
if (positions[clean]) return clean;
|
|
586
|
+
if ((clean === 'api' || clean === 'admin') && commandAlias) return commandAlias;
|
|
587
|
+
return clean;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const addLink = (fromRaw: string | null | undefined, toRaw: string | null | undefined, content: string, createdAt: string) => {
|
|
591
|
+
const from = resolveAlias(fromRaw);
|
|
592
|
+
const to = resolveAlias(toRaw);
|
|
593
|
+
if (!from || !to || !positions[from] || !positions[to] || from === to) {
|
|
571
594
|
return;
|
|
572
595
|
}
|
|
573
596
|
|
|
574
|
-
const key = `${
|
|
597
|
+
const key = `${from}->${to}`;
|
|
575
598
|
const current = links.get(key);
|
|
576
599
|
|
|
577
600
|
// Keep the most-recent timestamp per pair so the render can fade
|
|
578
601
|
// dormant edges (Round 10 freshness fade).
|
|
579
|
-
const incoming =
|
|
602
|
+
const incoming = createdAt || '';
|
|
580
603
|
const last_at = !current
|
|
581
604
|
? incoming
|
|
582
605
|
: (incoming > current.last_at ? incoming : current.last_at);
|
|
583
606
|
|
|
584
607
|
links.set(key, {
|
|
585
608
|
key,
|
|
586
|
-
from
|
|
587
|
-
to
|
|
609
|
+
from,
|
|
610
|
+
to,
|
|
588
611
|
count: (current?.count || 0) + 1,
|
|
589
|
-
content: current?.content ||
|
|
612
|
+
content: current?.content || content,
|
|
590
613
|
last_at,
|
|
591
614
|
});
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
messages.forEach(message => {
|
|
618
|
+
addLink(message.from_alias, message.to_alias, message.content, message.created_at);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
tasks.forEach(task => {
|
|
622
|
+
const from = task.from_alias ?? task.from_name;
|
|
623
|
+
const to = task.to_alias ?? task.to_name;
|
|
624
|
+
const when = task.completed_at || task.started_at || task.delivered_at || task.created_at || '';
|
|
625
|
+
const label = task.content || (task.status ? `task:${task.status}` : 'task');
|
|
626
|
+
addLink(from, to, label, when);
|
|
592
627
|
});
|
|
593
628
|
|
|
594
|
-
return [...links.values()]
|
|
629
|
+
return [...links.values()]
|
|
630
|
+
.sort((a, b) => (b.last_at || '').localeCompare(a.last_at || ''))
|
|
631
|
+
.slice(0, 18);
|
|
595
632
|
}
|
|
596
633
|
|
|
597
634
|
export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProps) {
|
|
@@ -614,6 +651,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
614
651
|
// wants to leave: "show me the rest of the flows" → /messages.
|
|
615
652
|
const router = useRouter();
|
|
616
653
|
const [messages, setMessages] = useState<MessageFlow[]>([]);
|
|
654
|
+
const [tasks, setTasks] = useState<TaskFlow[]>([]);
|
|
617
655
|
// Issue #87: ring | grid layout toggle. Ring is the tiered-radial default;
|
|
618
656
|
// grid arranges nodes in an N×M grid (better for 30+ nodes). Persisted to
|
|
619
657
|
// localStorage like the zoom/pan view state. Declared above nodePositions
|
|
@@ -718,20 +756,24 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
718
756
|
};
|
|
719
757
|
|
|
720
758
|
useEffect(() => {
|
|
721
|
-
const
|
|
759
|
+
const fetchFlows = async () => {
|
|
722
760
|
try {
|
|
723
|
-
const
|
|
724
|
-
|
|
761
|
+
const [messageRes, taskRes] = await Promise.all([
|
|
762
|
+
fetch('/api/hub/messages?limit=50'),
|
|
763
|
+
fetch('/api/hub/tasks?limit=100'),
|
|
764
|
+
]);
|
|
765
|
+
if (messageRes.status === 401 || taskRes.status === 401) {
|
|
725
766
|
window.location.assign('/login');
|
|
726
767
|
return;
|
|
727
768
|
}
|
|
728
769
|
|
|
729
|
-
const
|
|
730
|
-
setMessages(
|
|
770
|
+
const [messageData, taskData] = await Promise.all([messageRes.json(), taskRes.json()]);
|
|
771
|
+
setMessages(messageData.messages || []);
|
|
772
|
+
setTasks(taskData.tasks || []);
|
|
731
773
|
} catch {}
|
|
732
774
|
};
|
|
733
|
-
|
|
734
|
-
const interval = setInterval(
|
|
775
|
+
fetchFlows();
|
|
776
|
+
const interval = setInterval(fetchFlows, 5000);
|
|
735
777
|
return () => clearInterval(interval);
|
|
736
778
|
}, []);
|
|
737
779
|
|
|
@@ -880,7 +922,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
880
922
|
});
|
|
881
923
|
}
|
|
882
924
|
|
|
883
|
-
const links = buildFlowLinks(messages, positions);
|
|
925
|
+
const links = buildFlowLinks(messages, tasks, positions);
|
|
884
926
|
const active = new Set<string>();
|
|
885
927
|
links.forEach(link => { active.add(link.from); active.add(link.to); });
|
|
886
928
|
// #111: one bounding box per multi-member group (Vincent 4722). Each
|
|
@@ -1194,7 +1236,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1194
1236
|
// tree/swimlane. iter4 (a6689b6) suppressed them as "杂线"; Vincent
|
|
1195
1237
|
// now explicitly wants them back — same buildFlowLinks the ring/grid
|
|
1196
1238
|
// layouts use, drawn against the swimlane node positions.
|
|
1197
|
-
const links = buildFlowLinks(messages, positions);
|
|
1239
|
+
const links = buildFlowLinks(messages, tasks, positions);
|
|
1198
1240
|
const active = new Set<string>();
|
|
1199
1241
|
links.forEach(link => { active.add(link.from); active.add(link.to); });
|
|
1200
1242
|
|
|
@@ -1271,7 +1313,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1271
1313
|
positions[s.alias] = polarPoint(index, Math.max(offline.length, 1), offlineR, offlineRotation);
|
|
1272
1314
|
});
|
|
1273
1315
|
|
|
1274
|
-
const links = buildFlowLinks(messages, positions);
|
|
1316
|
+
const links = buildFlowLinks(messages, tasks, positions);
|
|
1275
1317
|
const active = new Set<string>();
|
|
1276
1318
|
links.forEach(link => {
|
|
1277
1319
|
active.add(link.from);
|
|
@@ -1296,7 +1338,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1296
1338
|
treeConnectors: { lines: [], synthLabels: [], synthRoot: false } as TreeLayout,
|
|
1297
1339
|
treeContentBox: null as { x: number; y: number; w: number; h: number } | null,
|
|
1298
1340
|
};
|
|
1299
|
-
}, [messages, sessions, sseSessions, layout, nodeScale]);
|
|
1341
|
+
}, [messages, tasks, sessions, sseSessions, layout, nodeScale]);
|
|
1300
1342
|
|
|
1301
1343
|
const workingCount = onlineNodes.filter(s => s.status === 'working').length;
|
|
1302
1344
|
// Round 744 / Loop: legend header node-count. The legend panel's header
|
package/app/layout.tsx
CHANGED
|
@@ -13,6 +13,12 @@ const geistMono = Geist_Mono({
|
|
|
13
13
|
export const metadata: Metadata = {
|
|
14
14
|
title: "Agent Network Dashboard",
|
|
15
15
|
description: "Real-time monitoring dashboard for Agent Network nodes via CommHub",
|
|
16
|
+
manifest: "/manifest.webmanifest",
|
|
17
|
+
appleWebApp: {
|
|
18
|
+
capable: true,
|
|
19
|
+
statusBarStyle: "black-translucent",
|
|
20
|
+
title: "Agent Network",
|
|
21
|
+
},
|
|
16
22
|
icons: { icon: '/favicon.svg' },
|
|
17
23
|
openGraph: {
|
|
18
24
|
title: "Agent Network Dashboard",
|
package/app/manifest.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MetadataRoute } from 'next';
|
|
2
|
+
|
|
3
|
+
export default function manifest(): MetadataRoute.Manifest {
|
|
4
|
+
return {
|
|
5
|
+
name: 'Agent Network Dashboard',
|
|
6
|
+
short_name: 'AgentNet',
|
|
7
|
+
description: 'Mobile command surface for Agent Network and CommHub',
|
|
8
|
+
start_url: '/',
|
|
9
|
+
scope: '/',
|
|
10
|
+
display: 'standalone',
|
|
11
|
+
background_color: '#0a0a1a',
|
|
12
|
+
theme_color: '#0a0a1a',
|
|
13
|
+
orientation: 'portrait',
|
|
14
|
+
icons: [
|
|
15
|
+
{
|
|
16
|
+
src: '/favicon.svg',
|
|
17
|
+
sizes: 'any',
|
|
18
|
+
type: 'image/svg+xml',
|
|
19
|
+
purpose: 'any',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/agent-network-dashboard",
|
|
3
|
-
"version": "0.5.3-preview.
|
|
3
|
+
"version": "0.5.3-preview.267",
|
|
4
4
|
"description": "Agent Network Dashboard \u2014 Web UI for managing AI Agent networks",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "next dev",
|
|
@@ -44,4 +44,4 @@
|
|
|
44
44
|
"tailwindcss": "^4",
|
|
45
45
|
"typescript": "^5"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|