From 9485581f909bb81d6dd6711bacc469ec20b97f42 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 8 Mar 2026 12:03:25 +0000 Subject: [PATCH] =?UTF-8?q?v1.1.0:=20=E6=94=AF=E6=8C=81=20Windows=20?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=AB=AF=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增跨平台进程终止 killProcess(),Windows 使用 taskkill,Linux 使用 SIGTERM/SIGKILL - spawn 增加 windowsHide 参数,HOME 路径增加 USERPROFILE fallback - 新增 start.bat 一键启动脚本 - README 快速开始分 Linux/Windows 两部分,新增 Windows 部署章节 Co-Authored-By: Claude Opus 4.6 --- README.md | 48 +++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- server.js | 25 ++++++++++++++++++++----- start.bat | 20 ++++++++++++++++++++ 4 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 start.bat diff --git a/README.md b/README.md index df4bd2f..50f50aa 100644 --- a/README.md +++ b/README.md @@ -38,21 +38,28 @@ https://github.com/ZgDaniel/cc-web 给我装! ## 快速开始 +### Linux / macOS + ```bash -# 克隆项目 -git clone https://github.com/your-username/cc-web.git +git clone https://github.com/ZgDaniel/cc-web.git cd cc-web - -# 安装依赖 npm install - -# 创建配置文件(可选,不设密码则首次启动自动生成) -cp .env.example .env - -# 启动 +cp .env.example .env # 可选,不设密码则首次启动自动生成 npm start ``` +### Windows + +```cmd +git clone https://github.com/ZgDaniel/cc-web.git +cd cc-web +npm install +copy .env.example .env & REM 可选 +``` +然后双击 `start.bat`,或在终端运行 `node server.js`。 + +--- + 启动后访问 `http://localhost:8002`,输入密码即可使用。 ## 配置 @@ -106,6 +113,7 @@ cc-web/ ├── sessions/ # 对话历史 JSON 文件(运行时生成) ├── logs/ # 进程生命周期日志(运行时生成) ├── .env.example # 环境变量模板 +├── start.bat # Windows 一键启动脚本 ├── .gitignore ├── package.json └── README.md @@ -210,6 +218,24 @@ server { } ``` +### Windows 部署 + +适用于在个人电脑上运行 CC-Web,通过手机远程控制 Claude Code。 + +**启动方式**:双击 `start.bat`,或在终端运行: +```cmd +cd cc-web +npm install +node server.js +``` + +**局域网访问**(手机和电脑在同一 WiFi): +- 直接访问 `http://电脑局域网IP:8002` + +**远程访问**(外出时用手机控制家里电脑): +- 推荐使用 [Tailscale](https://tailscale.com/) — 电脑和手机各安装一个,自动组网,免费够用 +- 或使用 [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/)(需域名) + ## 技术栈 - **后端**:Node.js + [ws](https://github.com/websockets/ws)(唯一依赖) @@ -219,5 +245,5 @@ server { ## 补充说明 -- 暂时只支持linux服务器,后续再vibe电脑端 -- 暂时只支持claude code,后续再vibe codex +- 支持 Linux 服务器和 Windows 个人电脑部署 +- 暂时只支持 Claude Code,后续再 vibe codex diff --git a/package.json b/package.json index a140e00..54cab3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc-web", - "version": "1.0.0", + "version": "1.1.0", "private": true, "scripts": { "start": "node server.js" diff --git a/server.js b/server.js index 0424bf9..936ff44 100644 --- a/server.js +++ b/server.js @@ -284,6 +284,8 @@ function modelShortName(fullModel) { return entry ? entry[0] : null; } +const IS_WIN = process.platform === 'win32'; + function isProcessRunning(pid) { try { process.kill(pid, 0); @@ -293,6 +295,18 @@ function isProcessRunning(pid) { } } +function killProcess(pid, force = false) { + try { + if (IS_WIN) { + const args = ['/T', '/PID', String(pid)]; + if (force) args.unshift('/F'); + spawn('taskkill', args, { windowsHide: true }); + } else { + process.kill(pid, force ? 'SIGKILL' : 'SIGTERM'); + } + } catch {} +} + function cleanRunDir(sessionId) { const dir = runDir(sessionId); try { @@ -731,7 +745,7 @@ function handleSlashCommand(ws, text, sessionId) { if (session) { if (activeProcesses.has(sessionId)) { const entry = activeProcesses.get(sessionId); - try { process.kill(entry.pid, 'SIGTERM'); } catch {} + killProcess(entry.pid); if (entry.tailer) entry.tailer.stop(); activeProcesses.delete(sessionId); cleanRunDir(sessionId); @@ -899,7 +913,7 @@ function handleLoadSession(ws, sessionId) { function handleDeleteSession(ws, sessionId) { if (activeProcesses.has(sessionId)) { const entry = activeProcesses.get(sessionId); - try { process.kill(entry.pid, 'SIGTERM'); } catch {} + try { killProcess(entry.pid); } catch {} if (entry.tailer) entry.tailer.stop(); activeProcesses.delete(sessionId); if (entry.ws) wsSend(entry.ws, { type: 'done', sessionId }); @@ -961,9 +975,9 @@ function handleAbort(ws) { if (!entry) return; plog('INFO', 'user_abort', { sessionId: sessionId.slice(0, 8), pid: entry.pid }); - try { process.kill(entry.pid, 'SIGTERM'); } catch {} + killProcess(entry.pid); setTimeout(() => { - try { process.kill(entry.pid, 'SIGKILL'); } catch {} + killProcess(entry.pid, true); }, 3000); // handleProcessComplete will be triggered by the PID monitor } @@ -1061,9 +1075,10 @@ function handleMessage(ws, msg) { try { proc = spawn(CLAUDE_PATH, args, { env, - cwd: process.env.HOME || process.cwd(), + cwd: process.env.HOME || process.env.USERPROFILE || process.cwd(), stdio: [inputFd, outputFd, errorFd], detached: true, + windowsHide: true, }); } catch (err) { fs.closeSync(inputFd); diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..1050a37 --- /dev/null +++ b/start.bat @@ -0,0 +1,20 @@ +@echo off +chcp 65001 >nul 2>&1 +cd /d "%~dp0" + +where node >nul 2>&1 +if %errorlevel% neq 0 ( + echo [ERROR] Node.js not found. Please install Node.js first. + echo https://nodejs.org/ + pause + exit /b 1 +) + +if not exist node_modules ( + echo Installing dependencies... + npm install +) + +echo Starting CC-Web... +node server.js +pause