# claude-agent-sdk-web **Repository Path**: HSKS/claude-agent-sdk-web ## Basic Information - **Project Name**: claude-agent-sdk-web - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-04 - **Last Updated**: 2026-10-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # claude-agent-web **Web 版 Claude Code**:多人共用的 AI 编码智能体平台。基于 `@anthropic-ai/claude-agent-sdk` 驱动 CLI 子进程,Bun + Hono 后端(无 DB,运行时状态落 sidecar 文件),React 聊天前端, 可编译为单文件可执行部署(含 deep-link 拉起与自更新)。 > 开发前必读 [开发规范.md](./开发规范.md):强约定 + 固定范式,`packages/backend/src/modules/auth/` > 是业务模块标准范式(活教材),`packages/backend/src/modules/agent/` 是复杂模块(SSE/注册表/子进程)进阶参考。 ## 功能特性 - **AI 编码会话**:SSE 流式输出、工具调用特化渲染(Bash/文件树/搜索结果…)、工具审批 (可修改参数/总是放行)、中断、执行模式四档(auto/standard/safe/plan 热切换)、 子代理上下文面板、图片输入、斜杠命令与 @ 提及、文件树在线编辑与上传、会话回滚(checkpoint) - **本地普通用户登录**:用户名 + 密码,scrypt 哈希校验,**首次登录即注册**(无需独立注册接口); 用户信息落 `{AGENT_CONFIG_ROOT}/{username}/user-profile.json`;登录成功签发本地 HS256 JWT, HttpOnly cookie 下发;admin 由 `AUTH_ADMIN_USERS` 白名单提升 - **多用户隔离**:每用户独立 `CLAUDE_CONFIG_DIR`,会话注册表按目录做属主校验与配额 (每用户/全局上限、空闲回收、绝对寿命、evict 接管) - **无 DB 存储**:项目元数据(`.project.json`)与会话 runMode 快照(`.meta.json`)落 `AGENT_CONFIG_ROOT//` sidecar,会话历史即 SDK JSONL——一人一套,零跨用户并发 - **工作空间**:路径后端推导(用户只输项目名),落 `AGENT_CONFIG_ROOT//workspaces//` - **deep-link 部署**:`csmcode://download?url=&name=&user=` 拉起 + 解压落 uuid 工作空间(同名复用既有 uuid 原地覆盖,会话历史保留);单实例锁 + 热转发 - **全局配置分发**:admin 维护一份 `GLOBAL_CONFIG_ROOT`(skills/agents/commands/hooks/plugins/ system-prompt),所有用户会话自动获得(放文件即生效,新会话起) - **工程化**:结构化日志 traceId 全链路、单文件编译产物(内嵌前端)、自更新(OBS 清单)、 BASE_URL 子路径部署、请求体 AES-256-GCM 加密(可选) ## 技术栈 | 关注点 | 选型 | |---|---| | 运行时 / 包管理 / 测试 | bun(`bun test`) | | 后端框架 | hono + zod v4(`vh` 校验中间件,无 OpenAPI 文档层) | | AI 能力 | @anthropic-ai/claude-agent-sdk(spawn CLI 子进程,streaming-input 常驻) | | 持久化 | 无 DB:`AGENT_CONFIG_ROOT` sidecar 文件(原子写 tmp+rename) | | 认证 | 本地 scrypt + HS256 JWT(node:crypto),无外部认证服务 | | 前端 | Vite + React 19 + Tailwind v4 + zustand + shadcn 范式组件 + sonner | | 契约 | `@csm/shared`:请求 zod schema(运行时校验 + 类型双导出)+ 响应纯 TS 接口 | | 日志 | pino(结构化 JSON,dev 彩色 pretty) | | Lint / Format | biome(backend/shared)/ prettier(webui/) | ## 快速开始(开发) ```bash # 0) 前置:Node/Bun 已装;复制 .env.example 为 .env 并填 Anthropic 网关三件套 cp .env.example .env # 必填:ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN / ANTHROPIC_MODEL # 可选:JWT_SECRET(dev 不配则降级固定常量;生产必须配) bun install # 全 workspace 依赖 bun run dev:backend # 后端(热重载)-> http://localhost:(.env 配,默认 10068) bun run dev:webui # 前端(另开终端)-> http://localhost:3001(proxy 转后端 PORT) ``` - 健康检查:`/healthz`(存活)、`/readyz`(就绪) - 首次访问 `/` 自动跳 `/login`;输入任意用户名 + 密码即注册并登录 - 全部环境变量说明见 [`.env.example`](./.env.example) ## 配置说明 `.env` 是唯一配置入口(后端 `env.ts` 启动时 zod 校验,失败即退出)。关键项: ### 认证(本地登录) | 变量 | 必填 | 说明 | |---|---|---| | `JWT_SECRET` | 生产必填 | base64 ≥32 字节,生成 `openssl rand -base64 32`;dev 不配则降级固定常量(不安全,仅供 dev/test) | | `AUTH_ADMIN_USERS` | 否 | 管理员白名单,逗号分隔用户名,命中即 admin,其余 user | 用户信息落 `{AGENT_CONFIG_ROOT}/{username}/user-profile.json`(scrypt 哈希 + salt,首次登录即注册)。 ### Anthropic 网关(agent 调 LLM 必填) | 变量 | 说明 | |---|---| | `ANTHROPIC_BASE_URL` | 网关地址(兼容 Anthropic 协议) | | `ANTHROPIC_AUTH_TOKEN` | 网关令牌 | | `ANTHROPIC_MODEL` | 默认模型别名 | | `ANTHROPIC_FALLBACK_MODELS` | 备用模型(逗号分隔,主模型过载时按序切换) | | `AGENT_EFFORT` | 推理深度 low\|medium\|high\|xhigh\|max | | `AGENT_THINKING` | 思考模式 adaptive\|disabled\|enabled | ### agent 会话治理 | 变量 | 说明 | |---|---| | `AGENT_CONFIG_ROOT` | 每用户 CLAUDE_CONFIG_DIR 根(会话数据按用户隔离) | | `GLOBAL_CONFIG_ROOT` | 全局共享配置根(skills/agents/plugins/system-prompt) | | `WORKSPACE_ROOT_NAME` | 工作空间子树名(项目目录 = `//<此>/`) | | `AGENT_SESSION_IDLE_MINUTES` | 空闲回收时长(分钟) | | `AGENT_SESSION_MAX_LIFE_HOURS` | 会话绝对寿命兜底(小时) | | `AGENT_MAX_SESSIONS_PER_USER` / `AGENT_MAX_TOTAL_SESSIONS` | 每用户 / 全局活跃会话上限 | ### 部署相关 | 变量 | 说明 | |---|---| | `PORT` | 后端端口(dev 默认 10068) | | `BASE_URL` | 子路径部署前缀(如 `/csmcode`,须 `/` 开头无尾斜杠;同时驱动前端构建 base) | | `BODY_ENC_KEY` | 请求体 AES-256-GCM 密钥(base64 32 字节,前后端同值;留空 = 加密关闭) | | `DEEP_LINK_SCHEME` | 自定义协议 scheme(默认 `csmcode`) | | `UPDATE_MANIFEST_URL` | 自更新清单地址(未配置则禁用自更新) | ## 使用指南 ### 登录 访问 `/login` → 输入用户名 + 密码 → 通过验证码 → 登录成功跳 `/chat`。 **首次登录自动注册**:任意用户名 + 密码首次登录即创建 `user-profile.json` 并落 scrypt 哈希; 后续登录校验哈希,密码错误返回 `AUTH_FAILED`。退出登录清 cookie。 ### AI 编码会话 1. 左栏「项目」新建项目(输项目名,后端推导工作空间路径) 2. 项目下「新会话」开启 CLI 子进程,进入聊天 3. 输入框发消息 / @ 提及文件 / `/斜杠命令`;支持图片附件 4. 工具调用:Bash/文件写操作按执行模式走审批卡(可改参数/总是放行/拒绝) 5. 中栏顶部:使用指南、设置(全局配置编辑)、文件面板开合、用量圈 6. 右栏文件树:在线编辑、新建/上传/移动/删除 **执行模式**(输入框下方切换,仅空闲会话可切,热切换不重启进程): - `auto`:自动放行工具(bypassPermissions) - `standard`:仅 AskUserQuestion 走审批,命令/写文件直通 - `safe`:命令 + 文件写 + 问卷全审批 - `plan`:只读规划模式 **人设**:输入框下方选择器,`/system-prompt/.md` 提供;新会话选定,resume 由 sidecar 快照回填。 ### deep-link 部署 `csmcode://download?url=&name=&user=` 协议拉起: 冷启动下载 zip 暂存 → 开浏览器到 `/chat?staged=` → 前端解析已登录用户 → 解压落 uuid 工作空间 (同名复用既有 uuid 原地覆盖,会话历史保留)→ 跳转聊天。用户须已登录。 ### admin 全局配置 admin 维护 `GLOBAL_CONFIG_ROOT`(默认 `data/global-configs`,目录不存在 = 功能自动关闭): | 通道 | 目录 | 机制 | |---|---|---| | skills / agents / commands / hooks | `/skills//SKILL.md`、`/agents/*.md`、`/commands/`、`/hooks/` | 整个 `` 作为本地插件包经 SDK `Options.plugins` 注入;每次开会话重读盘,**admin 改动对新会话即时生效**。skill 调用带命名空间:`/global:skill-name` | | marketplace 插件 | `/plugins/` | 用户目录 `plugins/` 整目录 junction 到全局(Windows junction 免特权);admin 预装一次全员共享。已知取舍:CLI 运行时回写共享目录,多用户并发有竞争损坏风险,admin 变更请在低峰 | | 人设库 | `/system-prompt/.md` | 前端选择器;正文以 systemPrompt append 注入(`snapshot:false` 支持热切换) | ## 目录结构 ``` ├── packages/ │ ├── backend/ │ │ └── src/ │ │ ├── index.ts # 进程入口:单实例锁 + deep-link 分流 + Bun.serve + 优雅停机 │ │ ├── app.ts # 应用组装:中间件 -> 路由 -> onError/notFound(不监听端口,可测) │ │ ├── env.ts # 环境变量 zod 校验,导出类型安全的 env(配置唯一来源) │ │ ├── core/ # 框架地基(默认禁止修改):错误码/AppError/响应工厂/日志/中间件 │ │ ├── modules/ # 业务模块区(开发主战场) │ │ │ ├── auth/ # 本地用户登录 + HS256 JWT(标准范式) │ │ │ ├── agent/ # AI 会话核心:project-store/会话/审批/SSE + SDK 集成层 │ │ │ └── system/ # deep-link 拉起/自更新/安装引导 │ │ ├── routes/ # 路由总表:healthz/readyz + 模块挂载 │ │ └── utils/ # 跨模块纯函数工具 │ ├── webui/ # React 前端(pages/components/hooks/stores/lib,详见开发规范第 13 节) │ └── shared/ # @csm/shared:FE↔BE 契约(dto/sdk-protocol/messages/sse-events) └── packages/backend/scripts/ # build 编译编排 / install 安装器 / publish-obs 发布 ``` ## 存储布局(无 DB) ``` AGENT_CONFIG_ROOT/ └── / # 每用户根 = userConfigDir()(encodeURIComponent 隔离) ├── user-profile.json # 本地登录信息(username + scrypt passwordHash + createdAt) ├── projects//.jsonl # SDK 会话转录 ├── projects//.meta.json # 会话 runMode/personaId 快照 ├── sessions/.json # CLI 槽位(SDK 写) └── workspaces// # 项目工作空间(CLI cwd) └── .project.json # { name, createdAt } ``` ## 请求处理链路 ``` Bun.serve └─ requestContext traceId 生成/透传 + child logger(AsyncLocalStorage) └─ accessLog 每请求一条汇总日志(method/path/status/durationMs/ip/ua) └─ [cors] CORS_ORIGIN 配置时启用 └─ routes route handler:c.req.valid() -> service -> ok() └─ service 业务逻辑;错误只 throw AppError;不依赖 hono └─ project-store / user-profile-store / agent-session-history sidecar 文件读写(原子写) └─ onError AppError -> 注册表查 status;未知 -> 500(日志留全量) └─ notFound 未匹配路由统一 404 ``` 统一响应:成功 `{ "data": ... }`;失败 `{ "error": { "code", "message", "traceId" } }`。 会话操作特殊协议:POST/DELETE 类携带 header `x-session-id` + `x-workspace-dir`(base64url); SSE 端点走 query 参数(EventSource 不支持自定义 header)。 ## 日志与问题排查 **traceId 三处一致**:每条日志字段、响应头 `x-request-id`、错误响应体 `error.traceId`。 拿到一个 traceId 即可在 `logs/`(按日滚动,保留期 `LOG_RETENTION_DAYS`)还原该请求完整链路。 | 手段 | 开启方式 | |---|---| | 请求体日志(截断 2KB,敏感字段脱敏) | `LOG_BODY=true` | | dev 彩色单行日志 | `NODE_ENV=development`(默认) | | 生产 JSON 日志 | `NODE_ENV=production` | ## 打包部署 ```bash bun run build # 产物:bin/csmcode-{platform}-{arch}-{version}[.exe] —— 唯一产物,单文件(约 300MB) # 内嵌 bun 运行时 + 前端页面 # 交叉编译:bun run build -- --target=win(短名)或 bun-{windows|linux|darwin}-{x64|arm64} # 注意:bun install 按本机 OS 只装本机平台的 CLI 包,交叉编译前需手工补装目标平台包: # bun add @anthropic-ai/claude-agent-sdk-linux-x64@0.3.278 # 版本对齐 dependencies 中的 SDK # (仅构建期解析需要,构建完可还原 package.json / bun.lock) # musl / darwin-x64 / arm64 变体不内嵌 CLI:产物需目标机设置 AGENT_CLI_PATH 指向自备 claude 二进制 ``` 部署用安装器一键装(下载 + 协议注册 + PATH shim + 开始菜单 + 自动生成 JWT_SECRET): ```powershell # Windows irm https:///install-win.ps1 | iex # Linux / macOS 对应 install-linux.sh / install-mac.sh ``` - 安装后命令:`csmcode`(启动 + 开浏览器)/ `csmcode update`(自更新)/ `csmcode uninstall` - 也会自动读取 exe 同目录下的 `.env` - 可执行文件同时服务前端页面与 API:`/` 为前端入口,`/api/*` 为后端接口 - **子路径部署**(nginx 反代场景):`.env` 设 `BASE_URL=/claude`(一处配置全链生效); 构建 exe 时须设相同值(驱动前端产物路径),启动时同理 ## 二次开发 ### 分层架构与禁令 ``` route(*.route.ts) 解析请求 -> 调 service -> 包装响应 └ service(*.service.ts) 业务逻辑、业务错误 └ 存储层(sidecar 文件:project-store.ts / user-profile-store.ts / agent-session-history.ts) ``` | 层 | 必须 | 禁止 | |---|---|---| | route | handler 只做 `c.req.valid()` → service → `ok()/created()`;每端点 `vh(target, schema)` 声明校验 | 写业务逻辑、try-catch、直接读文件 | | service | `throw new AppError(...)` 表达业务错误;返回 DTO | import hono;绕过存储层读写文件 | | 存储层 | sidecar 原子写(tmp+rename);id 一律 uuid;每用户隔离(首参 username) | 抛 AppError(由 service 判定);做业务判断 | - `core/**` 默认禁止修改(`error-codes.ts` 仅允许追加) - 业务错误唯一方式:`throw new AppError('CODE')`;错误码在 `core/error-codes.ts` 追加 - `z` 一律从 `'zod'` 导入(无 OpenAPI 文档层);`app` 一律 `createApp()` 构造 - 日志用 `log()`(自动携带 traceId/username);禁止 `console.*` - 完整规范见 [开发规范.md](./开发规范.md) ### 新增后端业务模块(7 步) 以新增 `product` 模块为例: 1. 新建 `packages/backend/src/modules/product/`,核心文件照抄 `src/modules/auth/` 改名 (schema / service / route / index;领域辅助按需追加) 2. 请求/响应契约写进 `packages/shared/src/dto/`(请求 zod schema + 响应纯 TS 接口); `product.schema.ts` 仅 re-export 3. 写 `product.service.ts`;需要的错误码到 `core/error-codes.ts` 追加(`PRODUCT_XXX`) 4. 若需持久化:sidecar 文件模式照抄 `agent/project-store.ts`(原子写 + uuid + 每用户隔离), 落 `userConfigDir()` 之下 5. 写 `product.route.ts` + `index.ts`(`createApp()` + `registerProductRoutes`; 需登录的模块在 index.ts 挂 `app.use('*', requireAuth())`) 6. `src/routes/index.ts` 挂载 `app.route('/api/products', productApp)` 7. 补 `product.test.ts`(`app.request()` 集成测试,每个请求带 `content-type: application/json`) ### 新增前端页面(5 步) 1. 跨组件共享数据先写 `stores/xxx.ts`(照抄 `stores/auth.ts`) 2. 新建 `pages/XxxPage.tsx`:函数组件,数据拉取放 `useEffect`,反馈用 `toast` 3. `App.tsx` 登记 Route(受保护页面挂 `AuthGuard` 内层) 4. 后端新接口契约写 `packages/shared/src/dto/`,前端经 `lib/agent-types.ts` type-only re-export 消费 5. 自检:`cd packages/webui && bunx tsc --noEmit -p tsconfig.app.json` 零错 前端分层(chat 域专用,勿打散): ``` agent-types.ts(类型 re-export) → agent-api.ts(REST) → useChatAgent.ts(SSE + 状态机) → useChatAgentApi.ts(组件消费接口) → components/chat/**(纯展示组件) stores/chat.ts(消息视图状态,useChatAgent 写入) ``` ### 交付自检(每次改动必须全绿) ```bash bun run typecheck && bun run lint && bun test # 涉及前端再加: cd packages/webui && bunx tsc --noEmit -p tsconfig.app.json ``` ## 常见问题 - **Windows 终端 curl 中文乱码**:Git Bash 的 curl 以本地编码发送 body,属客户端问题; 用 `--data-binary @file.json`(UTF-8 文件)或代码内 fetch 验证。 - **bun --hot 后行为异常**:热重载旧实例状态可能残留,重启 dev 即可。 - **改后端代码后 AI 会话报「会话不存在」**:dev 热重载会中断活跃 CLI 子进程(属预期), 刷新页面即走恢复链路;生产编译版无此现象。 - **创建会话后列表空**:看后端日志是否 `executable_launch_failed`(ENOENT)—— 项目工作空间目录被删/移动会导致 CLI spawn 静默失败,现已有前置校验显式报错, 按提示移除项目重建即可。 - **agent 调用 LLM 报错**:检查 `.env` 的 Anthropic 网关三件套是否填写正确。 - **生产启动报 JWT_SECRET 校验失败**:`.env.production` 占位 `REPLACE_ME_WITH_openssl_rand_base64_32` 须替换为 `openssl rand -base64 32` 实际生成的值,否则 env.ts 启动校验失败。