Files
deerflow-code/.zcode/plans/plan-sess_cc133987-e4af-4a6f-ae18-d9b3f91088e2.md
T

11 KiB
Raw Blame History

Web App 产物模式:多页面站点 / 流程图 / 地图(可降级 + 独立页面风格)

总体架构

让智能体在沙箱里产出完整多页面站点(site.json 清单 + src/pages + public 数据),平台统一构建(预置 node_modules,永不 npm install)与受控静态托管,前端新注册一个风格独立的「站点预览」路由页。

两条铁律(本次新增的核心原则):

  1. 零侵入:新页面不复用现有工作台风格/布局组件,走独立路由 + 独立样式表;对已有代码的修改仅限「一个带开关的入口按钮」。
  2. 可降级:沙箱/模板/node/构建链任何一环部署失败,Web App 功能整体退回"不可用"状态并优雅提示,智能体回退到现有单页 HTML 模式,现有功能零影响。
智能体(技能 web-app)
  0. 检查可用性:/mnt/web-app-template 缺失 → 回退现有单页 HTML 模式(行为与今天完全一致)
  1. scaffold → outputs/my-site/(排除 node_modules/dist)
  2. 写 src/pages/*.tsx、public/data.geojson、编辑 site.json
  3. build_site.py build → 软链/junction 预置 node_modules → vite build → dist/(失败自修复 ≤3 次)
  4. present_files site.json
平台(Gateway,全部能力探测 + 开关保护)
  GET  /api/web-app/capability                  # 可用性探测(配置/模板/node_modules/node 四项检查)
  GET  /api/threads/{tid}/sites                 # 站点列表 + 构建状态
  POST /api/threads/{tid}/sites/{name}/build    # 触发构建
  GET  /api/threads/{tid}/sites/{name}/build-status
  POST /api/threads/{tid}/sites/{name}/preview-url → 签发短时效签名 URL
  GET  /api/public/site-preview/{token}/{path}  # 静态托管 dist/
前端(独立路由 + 独立风格)
  #/sites/:threadId/:siteName                   # SiteStudioPage:全新视觉风格,无现有 chrome
  产物面板 site.json →「打开站点预览」按钮(仅 capability 可用时渲染)

已确认决策:混合构建(技能自修复 + 平台按钮兜底);预览工作台先行;GeoJSON 优先 + 可配置底图。


一、页面风格:独立设计系统(不用现有风格)

新页面 SiteStudioPage 做成**深色"预览工作室"**风格,视觉上与现有工作台完全区隔:

  • 独立路由:直接注册在 App.tsx 顶层(/share/:shareCode、/login 同款先例),不进 PageLayout/WorkspaceLayout/Header/PageSidebar——零现有 chrome 继承,零布局耦合。
  • 独立样式:新建 src/pages/site-studio/site-studio.css(自建 design tokens:深空灰底 #0f1117 系、靛蓝/青渐变强调色、玻璃拟态面板、细腻边框与阴影、系统字体栈),所有类名 site-studio- 前缀,只被该页面 import,不碰 tailwind 配置、全局 CSS、主题 provider。
  • 布局:
    • 顶部悬浮工具栏:站点标题 + 构建状态胶囊(成功/构建中/失败三态,带呼吸灯动画)+ 设备切换分段控件(桌面/平板/手机)+ 刷新/重新构建/新窗口打开;
    • 左侧窄导航栏:site.json 的页面列表(图标 + 标题,点击设 iframe hash 切页),可折叠成纯图标模式;
    • 中央:浏览器壳造型(圆角 + 顶部仿 URL 地址条 + 红黄绿窗控点缀)内嵌 iframe,设备切换时宽度平滑过渡动画;
    • 底部可折叠构建控制台(等宽字体日志、错误红色高亮、构建时长);
    • 加载/失败/未启用态都有设计过的插画式空状态(纯 CSS/SVG,不引组件库新依赖)。
  • 登录守卫:轻量检查 stored auth,缺失跳 #/login(复用既有跳转工具,不动鉴权体系)。

二、降级与故障隔离矩阵(部署失败不影响已有功能)

故障点 行为 对现有功能影响
config web_app.enabled: false(默认关) capability 返回不可用;入口按钮不渲染;技能第 0 步探测失败即回退单页 HTML 无
模板目录/node_modules 未预置(发版遗漏) 同上:capability 四项检查(配置/模板/依赖/node 二进制)逐项报告原因 无
sandbox.mounts 加了模板挂载但目录缺失 已核实 local_sandbox_provider.py:81-90 对不存在宿主路径静默跳过仅告警;Aio provider 同样按"存在才挂"实现 无(沿用既有容错语义)
vite build 失败/超时 子进程隔离(asyncio.to_thread + 超时强杀 + 每站点并发锁),错误收进 JSON;预览页显示日志;智能体读报错自修复 无(不进 gateway 请求主链路)
签名 URL 过期/伪造 403 JSON,页面自动重签 无
前端新页面任何异常 独立路由独立样式,最坏就是该页白屏,error boundary 兜底 无
产物面板入口按钮 唯一改动点:capability 请求失败/不可用 → 按钮不渲染,原 UI 一字不变 无
新 router 注册 纯增量 include_router,异常全部在路由内转结构化响应 无

技能回退语义(关键):SKILL.md 第 0 步强制探测 ls /mnt/web-app-template/scripts/build_site.py——探测失败时明确指示"改用现有方式产出单个 xxx.html",保证部署不完整时智能体产物质量与今天一致。

P1 站点模板(offline-backend-20260512/backend/templates/web-app-template/,源码入库,node_modules gitignore 由发版仓库预置)

  • Vite + React + react-router-dom(HashRouter:静态托管路径下无需服务端 fallback)+ 纯 CSS design tokens(不用 Tailwind v4,规避 oklch 在 chrome100 老内核失效)+ build target es2018。
  • 依赖:react 系、@xyflow/react、maplibre-gl、echarts、mermaid、pmtiles、vite/@vitejs/plugin-react(lockfile 锁死版本)。
  • 内置组件:AppShell(读 site.json 导航)、FlowCanvas(xyflow 流程图封装,自动分层布局)、GeoMap(默认无底图模式:背景 + GeoJSON 点线面离线开箱即用;site.json 可配 tilesUrl/PMTiles)、ChartPanel(echarts)、MermaidBlock(静态图)。
  • router.tsx 用 import.meta.glob('../pages/*.tsx') 自动注册路由——智能体丢页面文件即生效,零手动接线。
  • site.json:{ title, description, nav[], map:{tilesUrl?, tilesType?, pmtiles?}, theme? }。
  • Makefile 加 make webapp-template(开发机一次性 pnpm install);发版仓库构建脚本负责镜像内预置 node_modules(文档写明)。
  • config.yaml sandbox.mounts 增模板只读挂载(templates/web-app-template → /mnt/web-app-template,缺失自动跳过已验证);AioSandboxProvider _get_thread_mounts 小扩展:同样"存在才挂"地补读 config 自定义 mounts。

P2 构建脚本 + 技能包(skills/public/web-app/)

  • SKILL.md:触发词(多页面网站/站点/web app/带流程图或地图的网页);工作流 = ①可用性探测(失败即回退单页 HTML)②scaffold ③配置 site.json ④写页面 ⑤数据放 public/ ⑥build(失败读 JSON 报错修复重试 ≤3)⑦present_files site.json ⑧引导点「站点预览」;硬约束:不手写 dist、一律 /mnt/user-data/... 虚拟路径。
  • scripts/build_site.py(纯标准库):check(能力探测 JSON)/ scaffold(copytree 忽略 node_modules、dist)/ build(定位 node → 软链 node_modules 进站点:Linux symlink、Windows mklink /J junction 免管理员 → node vite.js build → 结构化 JSON 输出,180s 超时)/ status。
  • 模板定位顺序:env DEERFLOW_WEBAPP_TEMPLATE → /mnt/web-app-template(沙箱内)→ 后端相对路径(宿主侧)。

P3 后端:纯引擎 + 路由(全部能力开关保护)

  • packages/harness/deerflow/webapp/(纯引擎,不 import app.,对齐 address_scan.py 先例):站点发现(outputs/ 下 site.json + dist 状态)、构建执行(子进程 + 锁 + 超时)、HMAC token(tid|site|exp,密钥首用生成持久化 {base_dir}/.web-app-secret 0600)。
  • app/gateway/routers/sites.py(app.py 注册):
    • GET /api/web-app/capability:enabled + 模板 + node_modules + node 四项检查,逐项给原因(前端与技能共用);
    • 列表/build/build-status/preview-url:@require_permission("threads", "read", owner_check=True),能力不可用时返回结构化 {"available": false, "reason"} 而非 5xx;
    • GET /api/public/site-preview/{token}/{path}:验签 + 过期 → aresolve_thread_virtual_path 且限定站点 dist/ 根(复用遍历防护)→ 正确 MIME(.js/.mjs→text/javascript、.geojson→application/json、.wasm)→ HTML inline + CSP: default-src 'self' data: blob:; worker-src 'self' blob:(maplibre worker 需要)+ X-Content-Type-Options: nosniff + Cache-Control: no-cache + ETag(mtime)。
  • config.yaml + deerflow/config/configuration.py 加 web_app:(enabled 默认 false、build_timeout、token_ttl、template_dir、node 覆盖)——与 workflows 同款总开关模式。
  • 测试 tests/test_web_app_sites.py:capability 降级路径、token 签发/过期/伪造、路径穿越拒绝、MIME、站点发现(构建子进程 mock)。

P4 前端

  • src/core/sites/api.ts:capability/sites/build/status/mintPreviewUrl + TanStack Query hooks(照 light-apps.ts 模式)。
  • src/pages/site-studio/SiteStudioPage.tsx + site-studio.css + App.tsx 注册顶层路由 /sites/:threadId/:siteName(独立风格见第一节;iframe sandbox="allow-scripts allow-forms allow-popups" 无 allow-same-origin,延续既有安全契约;token 过期自动重签;设备切换;重新构建 → 轮询 build-status → cache-bust 刷新)。
  • artifact-file-detail.tsx 唯一既有文件改动:产物路径以 site.json 结尾且 capability 可用时渲染「打开站点预览」按钮(navigate 到新路由);capability 请求失败/超时一律不渲染,原 UI 不变。
  • token URL 构造等纯函数补单测;pnpm typecheck 通过。

P5 测试与文档

  • 后端 make test + ruff;前端 typecheck + 单测;build_site.py 本机真实跑通 scaffold→build→静态服务器自测 dist。
  • 文档:AGENTS.md 增「Web App 产物」小节(含降级矩阵);发版文档补 node_modules 预置说明。

安全边界

短时效签名 URL(默认 2h)只覆盖该站点 dist/ 静态读;dist/ 根限定 + 复用 aresolve_thread_virtual_path 遍历防护;HTML inline 仅在 site-preview 路径开放(不影响 artifacts 接口既有强制 attachment);CSP + nosniff + iframe sandbox 三重纵深。

二期(本期不做)

源码编辑 + 保存即重建;跨对话站点 gallery;PMTiles 底图资产打包;visualizations/ 内联图表(进行中,互补不冲突)的底图配置统一(其 map-renderer.tsx 硬编码 Google 瓦片属离线隐患,另行处理)。

风险

  • Windows 开发机 junction 链接免管理员,node 前端机必有;生产镜像已含 nodejs+npm。
  • Aio provider 自定义 mounts 扩展是小改动,按"存在才挂"实现即可,不影响现有挂载。
  • dist/ 出现在产物目录扫描(文件噪音)——一期保持现状,二期优化。