diff --git a/.planning/windows-start-audit/findings.md b/.planning/windows-start-audit/findings.md new file mode 100644 index 0000000..be30e7f --- /dev/null +++ b/.planning/windows-start-audit/findings.md @@ -0,0 +1,48 @@ +# Windows 启动脚本交付审查发现 + +## 初始状态 + +- 项目根目录存在 `start.bat`。 +- 根目录已有另一项已完成任务的计划文件,本次改用独立作用域,避免覆盖。 +- 工作区存在一个与本次审查无关的未跟踪 TODO CSV,不触碰。 + +## 入口与初步脚本审查 + +- `start.bat` 是 20 行 ASCII DOS batch 文件,已使用 `%~dp0` + `cd /d`,因此支持从其他工作目录启动,也支持项目位于不同盘符。 +- 脚本只检查 `node`,未独立检查 `npm`;标准 Node.js 安装通常同时提供 npm,但定制/损坏环境可能例外。 +- 仅在 `node_modules` 整个目录不存在时执行 `npm install`;目录存在但依赖缺失或 lockfile 已更新时不会补装。 +- `npm install` 后没有检查失败状态,即使依赖安装失败仍会继续执行 `node server.js`。 +- `node server.js` 退出后无论成功失败都会 `pause`,但没有明确显示退出码。 +- 仓库同时有 `package-lock.json`,面向别人交付时更适合 `npm ci` 做可复现的首次安装(前提是 lockfile 与 package.json 同步)。 +- README 中已把 `start.bat` 声明为 Windows 启动入口。 + +## 运行时与环境兼容性 + +- `package.json` 的运行依赖只有纯 JavaScript 包 `ws`,没有需要 Windows 本机编译工具链的原生依赖。 +- 服务端已有 `process.platform === 'win32'` 分支:停止子进程时使用 `taskkill`,启动 Agent 子进程时禁用 Unix 的 detached 模式,说明核心服务明确考虑了 Windows。 +- 检索到的 `/usr/bin/node`、`bash`、`ss` 等 Unix 命令位于回归测试脚本或 mock 数据,不在 `start.bat -> node server.js` 的普通启动主链路上。 +- README 要求 Node.js >= 18,但 `start.bat` 只检查“能否找到 node”,没有检查版本,也没有检查 npm;这是交给非开发者使用时最明显的前置条件缺口。 +- 当前 WSL 环境没有 `cmd.exe`,不能在本机执行真实 Windows CMD 动态测试;后续只能做静态批处理核验和 Node 主链路验证,并明确标注这一限制。 + +## 关键 Windows 批处理问题 + +- Windows 的 npm 命令通常由 `npm.cmd` 提供。 +- 在一个 `.bat` 中直接执行另一个 `.bat/.cmd` 而不使用 `call`,控制流不会可靠返回原脚本。 +- 当前第 15 行是 `npm install`,因此首次运行进入安装分支后,存在依赖装完却不继续执行第 18~19 行启动服务的确定性批处理兼容风险;常见表现是用户需要再次双击。 +- 交付版至少应改为 `call npm ...`,并在安装后用 `if errorlevel 1` 阻断失败路径。 +- 服务对 Claude/Codex 使用直接 `spawn(command, args)`;Windows 全局 npm CLI 常见为 `.cmd` shim,完整 Agent 功能还应在真实 Windows 上做一次 CLI 拉起验收。这不影响 `node server.js` 自身启动,但属于交付验收边界。 + +## 验证结果 + +- `node --check server.js`:通过。 +- `node --check lib/agent-runtime.js`:通过。 +- `npm ls --depth=0`:通过,当前运行依赖为 `ws@8.19.0`。 +- 使用隔离的配置、会话、日志目录在 18082 端口启动服务并请求首页:HTTP 成功,首页 10143 字节。 +- `start.bat` 当前为 ASCII + LF;现代 Windows CMD 可解析 LF,且 Windows Git 常会按 `core.autocrlf` 转换,因此不是主要阻断项。 + +## 交付边界 + +- 收件人需安装 Node.js >= 18,且 `node`、`npm` 在 PATH 中。 +- 需要把完整源码运行目录一起交付,不能只发送 `start.bat`。 +- 要使用 Agent 功能,还需安装并登录 Claude Code 或 Codex CLI。 +- 不应把发送者自己的 `.env`、`config/auth.json`、`sessions/`、`logs/` 一并打包,以免泄露密码和会话数据。 diff --git a/.planning/windows-start-audit/progress.md b/.planning/windows-start-audit/progress.md new file mode 100644 index 0000000..9e73401 --- /dev/null +++ b/.planning/windows-start-audit/progress.md @@ -0,0 +1,11 @@ +# Windows 启动脚本交付审查进度 + +- 2026-07-27:启用 `planning-with-files`,建立独立审查计划。 +- 2026-07-27:确认 `start.bat` 位于项目根目录;尚未修改业务文件。 +- 2026-07-27:完成 Trellis 会话上下文读取;当前 Trellis 任务与本次审查无关,未切换任务。 +- 2026-07-27:完成 `start.bat` 初读,已发现依赖安装失败未拦截、依赖目录存在即跳过校验等交付风险。 +- 2026-07-27:codebase-memory 索引状态为 ready;运行主链路具备 Windows 分支,Unix 专属命令主要在测试脚本。 +- 2026-07-27:尝试调用 `cmd.exe` 进行动态验证,当前 WSL 环境未提供该命令,记录为验证限制,不重复尝试。 +- 2026-07-27:确认首次安装分支缺少 `call npm install` 是 Windows 批处理关键缺陷,当前脚本不宜原样交付。 +- 2026-07-27:完成 Node 语法、依赖树和隔离 HTTP 启动验证,主服务入口通过。 +- 2026-07-27:审查完成;结论为 Windows 可支持,但 `start.bat` 交付前需要修正。本轮未修改业务源码。 diff --git a/.planning/windows-start-audit/task_plan.md b/.planning/windows-start-audit/task_plan.md new file mode 100644 index 0000000..140ed12 --- /dev/null +++ b/.planning/windows-start-audit/task_plan.md @@ -0,0 +1,29 @@ +# Windows 启动脚本交付审查计划 + +## 目标 + +判断 `start.bat` 是否能交给其他 Windows 用户直接使用,并核验脚本语法、依赖、路径、首次运行、错误提示和配套文件。 + +## 阶段 + +- [x] 阶段一:读取 Trellis 上下文并确认交付入口与配套文件 +- [x] 阶段二:审查批处理语法、依赖、路径与首次运行行为 +- [x] 阶段三:执行可行的 Windows 兼容性验证与启动链路核对 +- [x] 阶段四:形成是否需要修改及交付条件的结论 + +## 约束 + +- 当前先做只读审查,不擅自修改业务文件。 +- 保留用户已有未提交文件,不覆盖其他会话的计划。 +- 不使用已弃用的 graphify。 + +## 错误记录 + +- 当前 WSL 环境没有 `cmd.exe`,无法直接执行 Windows CMD 动态测试;改用静态检查与主链路交叉验证。 + +## 最终结论 + +- `start.bat` 的路径切换和 Node 启动语法可用于 Windows,Node 服务主入口隔离启动通过。 +- 当前脚本不适合原样交给首次使用者,关键原因是调用 `npm.cmd` 时没有使用 `call`,安装结束后可能不继续启动服务。 +- 交付前应补齐 `call npm ...`、Node >= 18、npm 检查、安装失败阻断和退出码提示。 +- 本轮是审查任务,未修改 `start.bat` 或业务源码。 diff --git a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz index a1ebb5a..8ccdb06 100644 Binary files a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz and b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz differ diff --git a/scripts/regression.js b/scripts/regression.js index 430fead..4369d9a 100644 --- a/scripts/regression.js +++ b/scripts/regression.js @@ -10,6 +10,7 @@ const WebSocket = require('ws'); const REPO_DIR = path.resolve(__dirname, '..'); const SERVER_PATH = path.join(REPO_DIR, 'server.js'); +const WINDOWS_START_PATH = path.join(REPO_DIR, 'start.bat'); const PUBLIC_APP_PATH = path.join(REPO_DIR, 'public', 'app.js'); const PUBLIC_INDEX_PATH = path.join(REPO_DIR, 'public', 'index.html'); const PUBLIC_STYLE_PATH = path.join(REPO_DIR, 'public', 'style.css'); @@ -3587,6 +3588,19 @@ function assertMultiAgentV2CompatibilityContract() { assert(frontendSource.includes('entry.agentPath ? `路径: ${entry.agentPath}`'), 'Sub-agent cards should expose the canonical agent path'); } +function assertWindowsStartupContract() { + const source = fs.readFileSync(WINDOWS_START_PATH, 'utf8').replace(/\r\n/g, '\n'); + + assert(source.includes('cd /d "%~dp0"'), 'Windows startup should switch to its own directory and support other drive letters'); + assert(/where node(?:\.exe)? >nul 2>&1/i.test(source), 'Windows startup should verify that Node.js is available'); + assert(/process\.versions\.node[\s\S]*?>= 18/.test(source), 'Windows startup should reject Node.js versions below 18'); + assert(/where npm\.cmd >nul 2>&1/i.test(source), 'Windows startup should verify that npm.cmd is available'); + assert(/call npm\.cmd (?:ci|install)/i.test(source), 'Windows startup should use call when invoking npm.cmd'); + assert(/if errorlevel 1[\s\S]*?exit \/b 1/i.test(source), 'Windows startup should stop after an environment or dependency failure'); + assert(source.includes('set "APP_EXIT_CODE=%ERRORLEVEL%"'), 'Windows startup should preserve the server exit code'); + assert(source.includes('exit /b %APP_EXIT_CODE%'), 'Windows startup should return the server exit code to the caller'); +} + async function main() { const targetIndex = process.argv.indexOf('--target'); const regressionTarget = targetIndex >= 0 ? String(process.argv[targetIndex + 1] || '').trim() : String(process.env.CC_WEB_REGRESSION_TARGET || '').trim(); @@ -3657,6 +3671,11 @@ async function main() { console.log('Session item tooltip regression checks passed.'); return; } + if (regressionTarget === 'windows-startup') { + assertWindowsStartupContract(); + console.log('Windows startup regression checks passed.'); + return; + } throw new Error(`Unknown regression target: ${regressionTarget}`); } @@ -3682,6 +3701,7 @@ async function main() { assertSessionSwitchRaceContract(); assertCodexAppChildToolFallbackContract(); assertMultiAgentV2CompatibilityContract(); + assertWindowsStartupContract(); const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-web-regression-')); const configDir = path.join(tempRoot, 'config'); diff --git a/start.bat b/start.bat index 1050a37..ce9f951 100644 --- a/start.bat +++ b/start.bat @@ -1,20 +1,64 @@ @echo off +setlocal chcp 65001 >nul 2>&1 cd /d "%~dp0" +if errorlevel 1 ( + echo [ERROR] Failed to open the CC-Web directory. + pause + exit /b 1 +) -where node >nul 2>&1 -if %errorlevel% neq 0 ( - echo [ERROR] Node.js not found. Please install Node.js first. +where node.exe >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Node.js was not found. Install Node.js 18 or later first. echo https://nodejs.org/ pause exit /b 1 ) -if not exist node_modules ( +node.exe -e "process.exit(Number(process.versions.node.split('.')[0]) >= 18 ? 0 : 1)" +if errorlevel 1 ( + echo [ERROR] Node.js 18 or later is required. Current version: + node.exe --version + pause + exit /b 1 +) + +where npm.cmd >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] npm was not found. Reinstall Node.js with npm enabled. + echo https://nodejs.org/ + pause + exit /b 1 +) + +call npm.cmd ls --depth=0 >nul 2>&1 +if errorlevel 1 ( echo Installing dependencies... - npm install + call :install_dependencies + if errorlevel 1 ( + echo [ERROR] Failed to install dependencies. Check the npm output above. + pause + exit /b 1 + ) ) echo Starting CC-Web... -node server.js +node.exe server.js +set "APP_EXIT_CODE=%ERRORLEVEL%" + +if not "%APP_EXIT_CODE%"=="0" ( + echo [ERROR] CC-Web stopped with exit code %APP_EXIT_CODE%. +) + pause +exit /b %APP_EXIT_CODE% + +:install_dependencies +if exist "package-lock.json" ( + call npm.cmd ci +) else ( + call npm.cmd install +) +set "INSTALL_EXIT_CODE=%ERRORLEVEL%" +exit /b %INSTALL_EXIT_CODE% diff --git a/修复会话切换表单不渲染 TO DO list.csv b/修复会话切换表单不渲染 TO DO list.csv new file mode 100644 index 0000000..91176fb --- /dev/null +++ b/修复会话切换表单不渲染 TO DO list.csv @@ -0,0 +1,8 @@ +id,item,status,done_at,notes +1,定位表单计数与消息渲染的会话切换链路,DONE,2026-07-18T16:37:19+08:00,已定位 renderEpoch 提前失效与同会话旧 requestId 响应竞态 +2,编写可复现竞态的回归测试,DONE,2026-07-18T16:50:50+08:00,已新增 session-switch-race 确定性回归测试 +3,运行回归测试并确认当前实现失败,DONE,2026-07-18T16:50:51+08:00,定向回归已确认当前实现失败并命中全部竞态契约 +4,修复会话切换时的表单状态同步与过期请求覆盖,DONE,2026-07-18T17:06:57+08:00,已分离渲染/请求代次并为历史块透传 requestId,定向回归转绿 +5,运行前端相关测试并确认回归通过,DONE,2026-07-18T17:11:39+08:00,定向与完整 npm regression 均通过 +6,执行静态检查并审查影响面,DONE,2026-07-18T19:45:21+08:00,本轮已通过 node --check、diff-check、完整 regression 和发布包 smoke;真实浏览器验证仍待执行 +7,使用真实浏览器验证频繁切换无需刷新,IN_PROGRESS,,