@sleep2agi/agent-network-dashboard 0.5.3-preview.266 → 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 +34 -34
- 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_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/{0cjutq9mapkb7.js → 07to0dy21pil-.js} +1 -1
- package/.next/static/chunks/{13lpe1ad6sd-p.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/{0nkzy-apq7r0c.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/layout.tsx +6 -0
- package/app/manifest.ts +23 -0
- package/package.json +1 -1
- 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/{f2Eapuqm0T-focdAHRC28 → OCWKpyGElLOWutcoW5Ya9}/_buildManifest.js +0 -0
- /package/.next/static/{f2Eapuqm0T-focdAHRC28 → OCWKpyGElLOWutcoW5Ya9}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{f2Eapuqm0T-focdAHRC28 → 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
|
</>
|
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