From c898ff863d688e9089d4465b303ad8317359c873 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Jun 2026 09:16:31 +0800 Subject: [PATCH] Disable static asset caching --- PROGRESS.md | 6 +++++- tests/test_static_vendor.py | 19 +++++++++++++++++++ web/app.py | 4 ++-- web/static_files.py | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 web/static_files.py diff --git a/PROGRESS.md b/PROGRESS.md index df18cd1..f032cbc 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-06-08(前端模块化 Step 2 完成:抽出 chat.js;main 75 行,路径 1 收官) +最后更新:2026-06-08(task_progress 进度工具 + 前端固定进度区 + 静态资源 no-cache) --- @@ -21,6 +21,10 @@ ## 已完成关键能力 +### 2026-06-08 + +- **新增 Codex 式 `task_progress` 进度工具 + Web 固定进度区**:`TaskProgressTool` 默认注册到 agent,支持 `set_plan/update_step/clear`,返回极短 UI-only 结果;上下文压缩对旧 `task_progress` tool_call/result 做专门折叠,避免进度历史长期占 prompt。前端新增 `progress.js` 做 task 级进度状态合并,修复 `update_step` 只带 `{id,status}` 时因缺标题不显示的问题;当前进度显示从助手消息内提升到 `#task-progress-dock`(对话流下方、输入框上方),历史消息内仍保留进度块作记录。system prompt + coding/ppt/proposal/analyze skill 加轻量使用约定,要求只在多步骤关键阶段少量更新。**部署侧补静态资源 no-cache**:`NoCacheStaticFiles` 替换默认 `StaticFiles`,让浏览器重新校验 `/static/*.js` 等资源,避免前端修复已部署但旧 `chat.js` 仍被缓存导致看不到进度区。校验:`pytest tests/test_context_compaction.py tests/test_task_progress_tool.py tests/test_executor_docker.py tests/test_static_vendor.py -v` 相关集通过;`node --test tests/frontend_task_progress.test.mjs` 2 过;`node --check web/static/js/chat.js web/static/js/progress.js` 过。 + ### 2026-06-06 - **前端模块化 Step 2 收官:抽出 `chat.js`(对话视图)+ main.js 缩成 75 行入口**:最后也是最缠的一块——任务列表(浏览/筛选/滚动)+ selectTask 切换 + renderChatMeta/模型下拉 + renderMessages + live-run 助手 + sendMessage/cancel + fetchSse/handleSseEvent + 润色/粘贴文件 + 完成/废弃/删除/导出/清空(原 main.js 连续区 64–1132)→ `chat.js`(1086 行)。**决策:合一个 chat.js 而非强拆 tasks.js+stream.js**——读完依赖图确认二者共享 `state.liveRuns` + `chat-stream` DOM + run 生命周期,且 live-run 助手(renderLiveRunIfVisible/ensureRunningTaskSubscribed 等)被 selectTask 和 SSE 机器两边调用、骑墙;强拆会制造双向各 ~4-5 个 import 且边界不自然(用户已确认选合一)。导出 `loadTaskList`/`loadModels`/`selectTask`,embed/files/newtask 对这三个的 import 从 `./main.js` 改指 `./chat.js`;`formatUploadProgress` 加 export(粘贴上传进度用)。**chat 不调 enterApp → 与 main 无环**。`main.js` 仅留 `enterApp`(编排)+ `loadStorage` + Esc 关栈 + boot = **75 行入口**,import 精简到 11 行(layout/markdown/media 不再被 main 直接引用,但经 chat 仍在依赖图、副作用照常)。**校验升级**:除 node 全检 + import/export 一致性,新增**从 main BFS 的模块可达性检查**(14/14 可达,确保副作用模块不掉出图)。dev.html 4087 行单文件 → 14 个零构建 ES module + 纯 HTML;main 2719→75。**路径 1(拆文件)完成**,后续可按需进路径 2(给 chat/files 等局部引 Alpine/petite-vue 响应式)。 diff --git a/tests/test_static_vendor.py b/tests/test_static_vendor.py index 3c2b818..74a6ec4 100644 --- a/tests/test_static_vendor.py +++ b/tests/test_static_vendor.py @@ -1,6 +1,12 @@ from pathlib import Path +import tempfile import unittest +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from web.static_files import NoCacheStaticFiles + ROOT = Path(__file__).resolve().parents[1] STATIC_DIR = ROOT / "web" / "static" @@ -43,6 +49,19 @@ class StaticVendorTests(unittest.TestCase): self.assertIn("context_original_chars", src) self.assertIn("cache_hit_tokens", src) + def test_static_js_is_served_with_revalidation_header(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + static_dir = Path(tmp) + (static_dir / "app.js").write_text("export const ok = true;\n", encoding="utf-8") + app = FastAPI() + app.mount("/static", NoCacheStaticFiles(directory=str(static_dir)), name="static") + client = TestClient(app) + + resp = client.get("/static/app.js") + + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.headers.get("cache-control"), "no-cache") + if __name__ == "__main__": unittest.main() diff --git a/web/app.py b/web/app.py index e258f50..1763069 100644 --- a/web/app.py +++ b/web/app.py @@ -25,7 +25,6 @@ from uuid import UUID, uuid4 from fastapi import Depends, FastAPI, File, Form, HTTPException, UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse -from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from sqlalchemy import BigInteger, cast, func, select, update from starlette.background import BackgroundTask @@ -51,6 +50,7 @@ from .auth import ( ) from .broker import broker from .sinks import WebEventSink +from .static_files import NoCacheStaticFiles STATUS_FILTERS = ("active", "completed", "abandoned") @@ -690,7 +690,7 @@ def create_app() -> FastAPI: # Windows 上 mimetypes 偶尔把 .js 判成 text/plain,会令