From 5b63214bc1f34eca6f6da75009ba90d64a29c817 Mon Sep 17 00:00:00 2001 From: chiguyong Date: Sat, 13 Jun 2026 10:01:26 +0800 Subject: [PATCH] fix(gui): address all P1 code review findings - AgentLayout: lazy-load views via defineAsyncComponent, wire route meta to quadrant tab switching - QuadrantPanel: ARIA tablist/tab/tabpanel roles, keyboard nav, v-if via computed, expose setActiveTab - SplitPane: touch support, keyboard resize, ARIA separator role - ChatMessage: DOMPurify sanitization, anchor toolCalls regex to line start - TerminalEmulator: fix ANSI span imbalance with depth tracking - theme.ts: read CSS custom properties at runtime via readToken() - responsive.css: fix bottom-right auto-collapse selector - app.py: path traversal protection, exclude docs/openapi.json - skills.ts: use BaseApiClient.request() for installSkill/uninstallSkill --- src/agentkit/server/app.py | 10 +- .../server/frontend/package-lock.json | 28 ++++ src/agentkit/server/frontend/package.json | 2 + .../server/frontend/src/api/skills.ts | 29 ++-- .../src/components/chat/ChatMessage.vue | 20 ++- .../src/components/layout/AgentLayout.vue | 49 +++++- .../src/components/layout/QuadrantPanel.vue | 50 ++++++- .../src/components/layout/SplitPane.vue | 76 ++++++++-- .../components/terminal/TerminalEmulator.vue | 63 +++++++- .../server/frontend/src/styles/responsive.css | 4 +- .../server/frontend/src/styles/theme.ts | 57 +++---- ...-CFKQRBqo.css => AgentLayout-BoHQ-707.css} | 2 +- .../static/assets/AgentLayout-C3jirk5z.js | 1 - .../static/assets/AgentLayout-CtYJ1_b1.js | 2 + .../static/assets/AppLayout-BSXe3ZUc.js | 1 - .../static/assets/AppLayout-CVkEttfS.js | 1 + ...jHecyI.js => AppstoreOutlined-D2IhpVp5.js} | 2 +- .../server/static/assets/ChatView-DA2wPu1A.js | 33 ++++ ...iew-D21rRrKq.css => ChatView-DGuMaiL8.css} | 2 +- .../server/static/assets/ChatView-VBSqbtQ5.js | 31 ---- .../server/static/assets/Checkbox-BKRvy9FI.js | 1 - .../server/static/assets/Checkbox-CGUdikGP.js | 1 + .../static/assets/ComputerUseView-BslO-Pem.js | 1 + .../static/assets/ComputerUseView-CW9s7NtE.js | 1 - ...BZTTXnow.js => DeleteOutlined-C9vx5Gyj.js} | 12 +- ...ajX3wuP.js => DesktopOutlined-ClGl-80a.js} | 2 +- ...pdown-XbNR4d_F.js => Dropdown-BfmL-5TH.js} | 10 +- ...-Bf98iUCx.js => EvolutionView-Knd2v7RU.js} | 4 +- .../assets/FolderOpenOutlined-DjFpnogK.js | 1 - .../assets/FolderOpenOutlined-aJTansHZ.js | 1 + ...7YeCp9s.js => FormItemContext-C7duC9vx.js} | 2 +- .../server/static/assets/KeyCode-c8YLmrmC.js | 1 + ...sIvYT.js => KnowledgeBaseView-B2lh4rdM.js} | 8 +- ...d-CBUjbbRo.js => LeftOutlined-Cjp7xl2E.js} | 2 +- .../static/assets/PlusOutlined-BZYJdEGN.js | 1 + ...gDlD6Oj.js => SettingOutlined-B1U74h0J.js} | 2 +- .../static/assets/SettingsView-CNi_QVj1.js | 1 - .../static/assets/SettingsView-wpl4DuIA.js | 1 + .../static/assets/SkillsView-TRawAwRE.js | 1 + .../static/assets/SkillsView-vANXhvRv.js | 1 - .../static/assets/TerminalView-B49qvo-w.js | 1 - .../static/assets/TerminalView-CWX3TF8I.js | 1 + ...C8cu2qRi.css => TerminalView-Dg1PpXnU.css} | 2 +- ...d-CvCrh-WB.js => UserOutlined-C607Q7TU.js} | 2 +- ...w-Dv5wD9j5.js => WorkflowView-BhS6yKYX.js} | 4 +- .../_plugin-vue_export-helper-BpJgGuqH.js | 1 + .../_plugin-vue_export-helper-Da7nhmaa.js | 1 - .../server/static/assets/base-BzUb4EcV.js | 1 + .../server/static/assets/base-DPp00EV9.js | 1 - .../{chat-pmO9UsHL.js => chat-DurNpMLt.js} | 2 +- .../static/assets/devWarning-Ct6dVMcH.js | 1 + .../server/static/assets/index-B8imcus9.js | 3 + .../server/static/assets/index-B9Cq1xD6.js | 1 + .../server/static/assets/index-BMDKnjoj.js | 1 + .../server/static/assets/index-Bh3tEcch.js | 3 - .../{index-CWabtNoR.js => index-Bhzy1-DW.js} | 2 +- .../{index-gcNyH-By.js => index-Bm5ZpeOU.js} | 8 +- .../{index-Dd95JYlE.js => index-Bw7-qyYZ.js} | 8 +- .../{index-ldpn1699.js => index-CPf0IATs.js} | 4 +- .../{index-C60lOYOg.js => index-CQzZ-h-2.js} | 8 +- .../server/static/assets/index-CUxjTFZM.js | 13 ++ .../server/static/assets/index-Cdm90D30.js | 141 ++++++++++++++++++ .../{index-DDQQQHWy.js => index-Chz7LVBG.js} | 4 +- .../server/static/assets/index-CivBWDtQ.js | 1 + ...{index-vR_mL1Yy.css => index-CqwY2lQz.css} | 2 +- .../server/static/assets/index-D4-AezQY.js | 3 - .../server/static/assets/index-D4egOf7T.js | 13 -- .../server/static/assets/index-DBNRwHot.js | 1 + .../server/static/assets/index-DQ-PTKeR.js | 1 + .../server/static/assets/index-DS4F546Z.js | 1 + .../{index-BEjLgAar.js => index-DUKU9Yum.js} | 12 +- .../server/static/assets/index-DWEEelzT.js | 3 + .../server/static/assets/index-DlUg5G7X.js | 1 + .../server/static/assets/index-DoEPe2JM.js | 1 - .../server/static/assets/index-DuhbuVXy.js | 1 - .../server/static/assets/index-DwzKHBT7.js | 1 - .../server/static/assets/index-KxFPh2gJ.js | 23 --- .../server/static/assets/index-RLKibtJr.js | 1 - .../server/static/assets/index-YXX8X6VB.js | 1 - .../server/static/assets/index-fKiBikrc.js | 1 - .../server/static/assets/index-gqpFY6Qt.js | 141 ------------------ .../server/static/assets/index-zQkkOnqV.js | 23 +++ ...ttrs-Ko_UXWn6.js => pickAttrs-cDcy8MZ0.js} | 2 +- ...Cg4ra.js => responsiveObserve-Dze5mRNR.js} | 2 +- ...r-B3mut-jB.js => styleChecker-DuXayfgz.js} | 2 +- .../{zoom-BFNOWbJv.js => zoom-DFzZ47uz.js} | 8 +- src/agentkit/server/static/index.html | 4 +- 87 files changed, 607 insertions(+), 372 deletions(-) rename src/agentkit/server/static/assets/{AgentLayout-CFKQRBqo.css => AgentLayout-BoHQ-707.css} (59%) delete mode 100644 src/agentkit/server/static/assets/AgentLayout-C3jirk5z.js create mode 100644 src/agentkit/server/static/assets/AgentLayout-CtYJ1_b1.js delete mode 100644 src/agentkit/server/static/assets/AppLayout-BSXe3ZUc.js create mode 100644 src/agentkit/server/static/assets/AppLayout-CVkEttfS.js rename src/agentkit/server/static/assets/{AppstoreOutlined-fAjHecyI.js => AppstoreOutlined-D2IhpVp5.js} (95%) create mode 100644 src/agentkit/server/static/assets/ChatView-DA2wPu1A.js rename src/agentkit/server/static/assets/{ChatView-D21rRrKq.css => ChatView-DGuMaiL8.css} (79%) delete mode 100644 src/agentkit/server/static/assets/ChatView-VBSqbtQ5.js delete mode 100644 src/agentkit/server/static/assets/Checkbox-BKRvy9FI.js create mode 100644 src/agentkit/server/static/assets/Checkbox-CGUdikGP.js create mode 100644 src/agentkit/server/static/assets/ComputerUseView-BslO-Pem.js delete mode 100644 src/agentkit/server/static/assets/ComputerUseView-CW9s7NtE.js rename src/agentkit/server/static/assets/{DeleteOutlined-BZTTXnow.js => DeleteOutlined-C9vx5Gyj.js} (96%) rename src/agentkit/server/static/assets/{DesktopOutlined-DajX3wuP.js => DesktopOutlined-ClGl-80a.js} (93%) rename src/agentkit/server/static/assets/{Dropdown-XbNR4d_F.js => Dropdown-BfmL-5TH.js} (64%) rename src/agentkit/server/static/assets/{EvolutionView-Bf98iUCx.js => EvolutionView-Knd2v7RU.js} (96%) delete mode 100644 src/agentkit/server/static/assets/FolderOpenOutlined-DjFpnogK.js create mode 100644 src/agentkit/server/static/assets/FolderOpenOutlined-aJTansHZ.js rename src/agentkit/server/static/assets/{FormItemContext-C7YeCp9s.js => FormItemContext-C7duC9vx.js} (78%) create mode 100644 src/agentkit/server/static/assets/KeyCode-c8YLmrmC.js rename src/agentkit/server/static/assets/{KnowledgeBaseView-DF8sIvYT.js => KnowledgeBaseView-B2lh4rdM.js} (83%) rename src/agentkit/server/static/assets/{LeftOutlined-CBUjbbRo.js => LeftOutlined-Cjp7xl2E.js} (96%) create mode 100644 src/agentkit/server/static/assets/PlusOutlined-BZYJdEGN.js rename src/agentkit/server/static/assets/{SettingOutlined-BgDlD6Oj.js => SettingOutlined-B1U74h0J.js} (98%) delete mode 100644 src/agentkit/server/static/assets/SettingsView-CNi_QVj1.js create mode 100644 src/agentkit/server/static/assets/SettingsView-wpl4DuIA.js create mode 100644 src/agentkit/server/static/assets/SkillsView-TRawAwRE.js delete mode 100644 src/agentkit/server/static/assets/SkillsView-vANXhvRv.js delete mode 100644 src/agentkit/server/static/assets/TerminalView-B49qvo-w.js create mode 100644 src/agentkit/server/static/assets/TerminalView-CWX3TF8I.js rename src/agentkit/server/static/assets/{TerminalView-C8cu2qRi.css => TerminalView-Dg1PpXnU.css} (79%) rename src/agentkit/server/static/assets/{UserOutlined-CvCrh-WB.js => UserOutlined-C607Q7TU.js} (97%) rename src/agentkit/server/static/assets/{WorkflowView-Dv5wD9j5.js => WorkflowView-BhS6yKYX.js} (61%) create mode 100644 src/agentkit/server/static/assets/_plugin-vue_export-helper-BpJgGuqH.js delete mode 100644 src/agentkit/server/static/assets/_plugin-vue_export-helper-Da7nhmaa.js create mode 100644 src/agentkit/server/static/assets/base-BzUb4EcV.js delete mode 100644 src/agentkit/server/static/assets/base-DPp00EV9.js rename src/agentkit/server/static/assets/{chat-pmO9UsHL.js => chat-DurNpMLt.js} (98%) create mode 100644 src/agentkit/server/static/assets/devWarning-Ct6dVMcH.js create mode 100644 src/agentkit/server/static/assets/index-B8imcus9.js create mode 100644 src/agentkit/server/static/assets/index-B9Cq1xD6.js create mode 100644 src/agentkit/server/static/assets/index-BMDKnjoj.js delete mode 100644 src/agentkit/server/static/assets/index-Bh3tEcch.js rename src/agentkit/server/static/assets/{index-CWabtNoR.js => index-Bhzy1-DW.js} (81%) rename src/agentkit/server/static/assets/{index-gcNyH-By.js => index-Bm5ZpeOU.js} (73%) rename src/agentkit/server/static/assets/{index-Dd95JYlE.js => index-Bw7-qyYZ.js} (64%) rename src/agentkit/server/static/assets/{index-ldpn1699.js => index-CPf0IATs.js} (54%) rename src/agentkit/server/static/assets/{index-C60lOYOg.js => index-CQzZ-h-2.js} (78%) create mode 100644 src/agentkit/server/static/assets/index-CUxjTFZM.js create mode 100644 src/agentkit/server/static/assets/index-Cdm90D30.js rename src/agentkit/server/static/assets/{index-DDQQQHWy.js => index-Chz7LVBG.js} (82%) create mode 100644 src/agentkit/server/static/assets/index-CivBWDtQ.js rename src/agentkit/server/static/assets/{index-vR_mL1Yy.css => index-CqwY2lQz.css} (85%) delete mode 100644 src/agentkit/server/static/assets/index-D4-AezQY.js delete mode 100644 src/agentkit/server/static/assets/index-D4egOf7T.js create mode 100644 src/agentkit/server/static/assets/index-DBNRwHot.js create mode 100644 src/agentkit/server/static/assets/index-DQ-PTKeR.js create mode 100644 src/agentkit/server/static/assets/index-DS4F546Z.js rename src/agentkit/server/static/assets/{index-BEjLgAar.js => index-DUKU9Yum.js} (65%) create mode 100644 src/agentkit/server/static/assets/index-DWEEelzT.js create mode 100644 src/agentkit/server/static/assets/index-DlUg5G7X.js delete mode 100644 src/agentkit/server/static/assets/index-DoEPe2JM.js delete mode 100644 src/agentkit/server/static/assets/index-DuhbuVXy.js delete mode 100644 src/agentkit/server/static/assets/index-DwzKHBT7.js delete mode 100644 src/agentkit/server/static/assets/index-KxFPh2gJ.js delete mode 100644 src/agentkit/server/static/assets/index-RLKibtJr.js delete mode 100644 src/agentkit/server/static/assets/index-YXX8X6VB.js delete mode 100644 src/agentkit/server/static/assets/index-fKiBikrc.js delete mode 100644 src/agentkit/server/static/assets/index-gqpFY6Qt.js create mode 100644 src/agentkit/server/static/assets/index-zQkkOnqV.js rename src/agentkit/server/static/assets/{pickAttrs-Ko_UXWn6.js => pickAttrs-cDcy8MZ0.js} (97%) rename src/agentkit/server/static/assets/{responsiveObserve-CWMCg4ra.js => responsiveObserve-Dze5mRNR.js} (93%) rename src/agentkit/server/static/assets/{styleChecker-B3mut-jB.js => styleChecker-DuXayfgz.js} (92%) rename src/agentkit/server/static/assets/{zoom-BFNOWbJv.js => zoom-DFzZ47uz.js} (51%) diff --git a/src/agentkit/server/app.py b/src/agentkit/server/app.py index 133131b..511dd0a 100644 --- a/src/agentkit/server/app.py +++ b/src/agentkit/server/app.py @@ -696,11 +696,13 @@ def create_app( @app.get("/{path:path}", response_class=HTMLResponse, include_in_schema=False) async def spa_fallback(path: str): """Serve index.html for SPA client-side routing.""" - # Don't intercept API routes - if path.startswith("api/"): + # Don't intercept API routes or docs + if path.startswith("api/") or path.startswith("docs") or path == "openapi.json": return HTMLResponse("

Not Found

", status_code=404) - # Try to serve a real static file first - file_path = _static_dir / path + # Try to serve a real static file first (with path traversal protection) + file_path = (_static_dir / path).resolve() + if not str(file_path).startswith(str(_static_dir.resolve())): + return HTMLResponse("

Forbidden

", status_code=403) if file_path.is_file(): return FileResponse(str(file_path)) # Fallback to index.html for SPA routing diff --git a/src/agentkit/server/frontend/package-lock.json b/src/agentkit/server/frontend/package-lock.json index 09600c8..390f6f7 100644 --- a/src/agentkit/server/frontend/package-lock.json +++ b/src/agentkit/server/frontend/package-lock.json @@ -13,12 +13,14 @@ "@vue-flow/controls": "^1.1.0", "@vue-flow/core": "^1.41.0", "ant-design-vue": "^4.2.0", + "dompurify": "^3.4.10", "markdown-it": "^14.2.0", "pinia": "^2.2.0", "vue": "^3.5.0", "vue-router": "^4.4.0" }, "devDependencies": { + "@types/dompurify": "^3.0.5", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.1.0", "echarts": "^6.1.0", @@ -933,6 +935,16 @@ "nanopop": "^2.1.0" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", @@ -965,6 +977,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.20", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", @@ -1511,6 +1530,15 @@ "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==", "license": "MIT" }, + "node_modules/dompurify": { + "version": "3.4.10", + "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.10.tgz", + "integrity": "sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/echarts": { "version": "6.1.0", "resolved": "https://registry.npmmirror.com/echarts/-/echarts-6.1.0.tgz", diff --git a/src/agentkit/server/frontend/package.json b/src/agentkit/server/frontend/package.json index c17e88a..e24e515 100644 --- a/src/agentkit/server/frontend/package.json +++ b/src/agentkit/server/frontend/package.json @@ -14,12 +14,14 @@ "@vue-flow/controls": "^1.1.0", "@vue-flow/core": "^1.41.0", "ant-design-vue": "^4.2.0", + "dompurify": "^3.4.10", "markdown-it": "^14.2.0", "pinia": "^2.2.0", "vue": "^3.5.0", "vue-router": "^4.4.0" }, "devDependencies": { + "@types/dompurify": "^3.0.5", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.1.0", "echarts": "^6.1.0", diff --git a/src/agentkit/server/frontend/src/api/skills.ts b/src/agentkit/server/frontend/src/api/skills.ts index d3bdbbb..10889cd 100644 --- a/src/agentkit/server/frontend/src/api/skills.ts +++ b/src/agentkit/server/frontend/src/api/skills.ts @@ -77,28 +77,21 @@ class SkillsApiClient extends BaseApiClient { source?: string ): Promise<{ status: string; name: string; path: string }> { // The install endpoint is on /api/v1/skills/install, not under skill-management - const url = `/api/v1/skills/install` - const resp = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name, source }), - }) - if (!resp.ok) { - const err = await resp.json().catch(() => ({ detail: resp.statusText })) - throw new Error(err.detail ?? resp.statusText) - } - return resp.json() + return this.request<{ status: string; name: string; path: string }>( + '/api/v1/skills/install', + { + method: 'POST', + body: JSON.stringify({ name, source }), + } + ) } /** Uninstall a skill */ async uninstallSkill(skillName: string): Promise<{ status: string; name: string }> { - const url = `/api/v1/skills/${encodeURIComponent(skillName)}` - const resp = await fetch(url, { method: 'DELETE' }) - if (!resp.ok) { - const err = await resp.json().catch(() => ({ detail: resp.statusText })) - throw new Error(err.detail ?? resp.statusText) - } - return resp.json() + return this.request<{ status: string; name: string }>( + `/api/v1/skills/${encodeURIComponent(skillName)}`, + { method: 'DELETE' } + ) } /** Reload a skill */ diff --git a/src/agentkit/server/frontend/src/components/chat/ChatMessage.vue b/src/agentkit/server/frontend/src/components/chat/ChatMessage.vue index ff31436..296197a 100644 --- a/src/agentkit/server/frontend/src/components/chat/ChatMessage.vue +++ b/src/agentkit/server/frontend/src/components/chat/ChatMessage.vue @@ -54,6 +54,7 @@