@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.
Files changed (230) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +34 -34
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +29 -0
  7. package/.next/routes-manifest.json +12 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +2 -2
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +13 -13
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +15 -15
  27. package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/upload/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/upload/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/upload/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/upload/route.js +7 -0
  37. package/.next/server/app/api/hub/upload/route.js.map +5 -0
  38. package/.next/server/app/api/hub/upload/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/upload/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +15 -15
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +15 -15
  44. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +2 -2
  49. package/.next/server/app/login.rsc +15 -15
  50. package/.next/server/app/login.segments/_full.segment.rsc +15 -15
  51. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +15 -15
  59. package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/manifest.webmanifest/route/app-paths-manifest.json +3 -0
  66. package/.next/server/app/manifest.webmanifest/route/build-manifest.json +9 -0
  67. package/.next/server/app/manifest.webmanifest/route/server-reference-manifest.json +4 -0
  68. package/.next/server/app/manifest.webmanifest/route.js +8 -0
  69. package/.next/server/app/manifest.webmanifest/route.js.map +5 -0
  70. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
  71. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +3 -0
  72. package/.next/server/app/manifest.webmanifest.body +1 -0
  73. package/.next/server/app/manifest.webmanifest.meta +1 -0
  74. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/messages.html +2 -2
  76. package/.next/server/app/messages.rsc +15 -15
  77. package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
  78. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  83. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/node.html +2 -2
  85. package/.next/server/app/node.rsc +15 -15
  86. package/.next/server/app/node.segments/_full.segment.rsc +15 -15
  87. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  92. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/nodes.html +2 -2
  94. package/.next/server/app/nodes.rsc +15 -15
  95. package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
  96. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  97. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  98. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  99. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  100. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  101. package/.next/server/app/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/server-logs.html +2 -2
  104. package/.next/server/app/server-logs.rsc +15 -15
  105. package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
  106. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  112. package/.next/server/app/settings/networks.html +2 -2
  113. package/.next/server/app/settings/networks.rsc +15 -15
  114. package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
  115. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  116. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  117. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  118. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  119. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  120. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  121. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/settings/tokens.html +2 -2
  124. package/.next/server/app/settings/tokens.rsc +15 -15
  125. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
  126. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  131. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  132. package/.next/server/app/settings.html +2 -2
  133. package/.next/server/app/settings.rsc +15 -15
  134. package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  135. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  136. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  137. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  138. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  139. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  140. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  141. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  142. package/.next/server/app/tasks.html +2 -2
  143. package/.next/server/app/tasks.rsc +15 -15
  144. package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
  145. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  146. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  147. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  148. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  149. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  150. package/.next/server/app-paths-manifest.json +2 -0
  151. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +18 -0
  152. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -0
  153. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js +3 -0
  154. package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js +3 -0
  155. package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js.map +1 -0
  156. package/.next/server/chunks/{[externals]__11vad82._.js → [externals]__036g1.i._.js} +2 -2
  157. package/.next/server/chunks/[externals]__036g1.i._.js.map +1 -0
  158. package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js +3 -0
  159. package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js.map +1 -0
  160. package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js → [root-of-the-server]__0jndzts._.js} +2 -2
  161. package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js.map → [root-of-the-server]__0jndzts._.js.map} +1 -1
  162. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js +1 -1
  163. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js.map +1 -1
  164. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js +1 -1
  165. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js.map +1 -1
  166. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js +1 -1
  167. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js.map +1 -1
  168. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js +1 -1
  169. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js.map +1 -1
  170. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js +1 -1
  171. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js.map +1 -1
  172. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js +1 -1
  173. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js.map +1 -1
  174. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js +1 -1
  175. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js.map +1 -1
  176. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js +1 -1
  177. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js.map +1 -1
  178. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js +1 -1
  179. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js.map +1 -1
  180. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js +1 -1
  181. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js.map +1 -1
  182. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js +1 -1
  183. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js.map +1 -1
  184. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js +1 -1
  185. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js.map +1 -1
  186. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js +1 -1
  187. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js.map +1 -1
  188. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js +1 -1
  189. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js.map +1 -1
  190. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js +1 -1
  191. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js.map +1 -1
  192. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +4 -1
  193. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
  194. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  195. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  196. package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js +1 -1
  197. package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js.map +1 -1
  198. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  199. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  200. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  201. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  202. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  203. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  204. package/.next/server/middleware-build-manifest.js +3 -3
  205. package/.next/server/middleware.js +2 -2
  206. package/.next/server/middleware.js.nft.json +1 -1
  207. package/.next/server/pages/404.html +2 -2
  208. package/.next/server/pages/500.html +1 -1
  209. package/.next/static/chunks/{0cjutq9mapkb7.js → 07to0dy21pil-.js} +1 -1
  210. package/.next/static/chunks/{13lpe1ad6sd-p.js → 09rzd1.kojbsa.js} +1 -1
  211. package/.next/static/chunks/0ae.az.vfm-il.css +2 -0
  212. package/.next/static/chunks/{07~8sx3_5uiox.js → 0ir6pphfe2yvm.js} +4 -1
  213. package/.next/static/chunks/{0v4-5tng.uh.7.js → 0k-c1chkvf78s.js} +2 -2
  214. package/.next/static/chunks/{0nkzy-apq7r0c.js → 0uqplti.5qwnv.js} +1 -1
  215. package/.next/trace +2 -2
  216. package/.next/trace-build +1 -1
  217. package/.next/types/routes.d.ts +2 -1
  218. package/.next/types/validator.ts +9 -0
  219. package/app/api/hub/upload/route.ts +83 -0
  220. package/app/components/AppShell.tsx +3 -1
  221. package/app/components/MobileNav.tsx +45 -0
  222. package/app/components/TaskChatPanel.tsx +82 -5
  223. package/app/layout.tsx +6 -0
  224. package/app/manifest.ts +23 -0
  225. package/package.json +1 -1
  226. package/.next/static/chunks/16-7qr7qx9zrz.css +0 -2
  227. /package/.next/server/chunks/{[externals]__11vad82._.js.map → 0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js.map} +0 -0
  228. /package/.next/static/{f2Eapuqm0T-focdAHRC28 → OCWKpyGElLOWutcoW5Ya9}/_buildManifest.js +0 -0
  229. /package/.next/static/{f2Eapuqm0T-focdAHRC28 → OCWKpyGElLOWutcoW5Ya9}/_clientMiddlewareManifest.js +0 -0
  230. /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-16 text-sm text-[var(--fg)] placeholder-[var(--fg-dim)] focus:border-cyan-500/40 focus:outline-none resize-none transition-colors"
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 · Shift+Enter for newline</span>
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",
@@ -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.266",
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",