- P1: Banner v1.3→v1.5, Hooks 29→34 - P1: 卸载脚本补删 更新Bookworm.lnk - P1: git stash pop 安全检查 - P2: Playwright 检测改用 npm list - P2: 代理端口扫描 500ms async 超时 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
207 lines
8.0 KiB
Markdown
207 lines
8.0 KiB
Markdown
# Bookworm Portable 项目经验与避坑指南
|
||
|
||
> 2026-04-01 | 从可行性评估到 E2E 验证的完整实战记录
|
||
|
||
---
|
||
|
||
## 一、项目概要
|
||
|
||
**目标**: 让任意 Windows 电脑通过一行命令激活 Bookworm (97 Skills + 18 Agents + 28 Hooks) + 中转站 API
|
||
|
||
**最终架构**: Gitea 私有仓 (code.letcareme.com) + AES-256 加密凭证 + 自动代理检测 + 一键安装脚本
|
||
|
||
**耗时**: 1 天 (评估 → 设计 → 三路专家审查 → P0/P1 修复 → 部署 → E2E 验证 → 踩坑修复)
|
||
|
||
---
|
||
|
||
## 二、关键决策与转折点
|
||
|
||
### 决策 1: USB → Git 方案切换
|
||
- **初始方案**: U盘存储全部系统文件 + 加密凭证
|
||
- **问题**: Windows NTFS Junction 跨驱动器失败 (实测 Accessible: False)、USB IOPS 瓶颈 (hook 延迟 200-600ms)、体积 2.1GB
|
||
- **最终方案**: Git + 模板渲染,USB 变为可选 (纯云端即可)
|
||
- **教训**: 方案评估阶段多投入时间做实测,不要假设跨驱动器操作能工作
|
||
|
||
### 决策 2: GitHub → 国内 Gitea
|
||
- **问题**: 国内无 VPN 无法访问 GitHub
|
||
- **方案**: 阿里云 ECS 自建 Gitea,国内直连
|
||
- **教训**: 面向国内用户的工具链必须全程可达,不能依赖海外服务
|
||
|
||
### 决策 3: bookworm.letcareme.com → code.letcareme.com
|
||
- **问题**: bookworm.letcareme.com 已被 Bookworm Web 项目 Nginx 配置占用
|
||
- **解决**: 换用 code.letcareme.com,申请新证书
|
||
- **教训**: 部署前检查域名占用,`grep -r "域名" /etc/nginx/` 先扫一遍
|
||
|
||
---
|
||
|
||
## 三、必踩的坑 (按严重度排序)
|
||
|
||
### 坑 1: Claude Code 国内启动检查 (CRITICAL)
|
||
```
|
||
现象: "Failed to connect to api.anthropic.com: ERR_BAD_REQUEST"
|
||
原因: Claude Code 启动时硬编码检查 api.anthropic.com,与 ANTHROPIC_BASE_URL 无关
|
||
影响: 国内无代理 = Claude Code 完全无法启动
|
||
解决: 必须有代理/VPN,且必须设 HTTPS_PROXY 环境变量 (Node.js 不读系统代理)
|
||
耗时: 2 小时排查
|
||
```
|
||
|
||
### 坑 2: Node.js 不读 Windows 系统代理 (CRITICAL)
|
||
```
|
||
现象: PowerShell (Invoke-WebRequest) 能通,Claude Code 不通
|
||
原因: Node.js 只读 HTTPS_PROXY/HTTP_PROXY 环境变量,不读 Windows IE/系统代理设置
|
||
解决: 用 [System.Net.WebRequest]::DefaultWebProxy.GetProxy() 发现实际代理端口
|
||
然后 $env:HTTPS_PROXY = "http://127.0.0.1:端口"
|
||
教训: 系统代理 ≠ 进程代理,Node.js 应用必须显式设环境变量
|
||
```
|
||
|
||
### 坑 3: settings.json 中 ${VAR} 不展开 (CRITICAL)
|
||
```
|
||
现象: "API Error: Invalid URL"
|
||
原因: settings.template.json 中 "ANTHROPIC_BASE_URL": "${ANTHROPIC_BASE_URL}"
|
||
Claude Code 把 ${ANTHROPIC_BASE_URL} 当字面字符串,不展开环境变量
|
||
实际发送的 URL 是 "${ANTHROPIC_BASE_URL}/v1/messages"
|
||
解决: 从 settings.json env 段删除 ANTHROPIC_API_KEY 和 ANTHROPIC_BASE_URL
|
||
让 install.ps1 注入的进程环境变量直接生效
|
||
教训: Claude Code settings.json 的 ${} 语法行为未文档化,不要假设它能展开
|
||
```
|
||
|
||
### 坑 4: PowerShell 5.1 的 ?. 语法 (HIGH)
|
||
```
|
||
现象: "表达式或语句中包含意外的标记 '?.Source'"
|
||
原因: ?. (null-conditional operator) 是 PS 7+ 专属语法,PS 5.1 不支持
|
||
解决: 改为 if ($x) { $x.Property } else { $null }
|
||
教训: 目标机可能只有 PS 5.1 (Windows 自带),所有脚本必须 PS 5.1 兼容
|
||
```
|
||
|
||
### 坑 5: PowerShell 5.1 的 git stderr 问题 (HIGH)
|
||
```
|
||
现象: git clone 实际成功但脚本报 [ERROR] 克隆失败
|
||
原因: git 把进度信息写到 stderr,PS 5.1 的 $ErrorActionPreference="Stop"
|
||
把 stderr 输出当成终止性错误抛出异常
|
||
解决: git 命令前临时设 $ErrorActionPreference = "Continue"
|
||
用 $LASTEXITCODE 判断实际成功/失败
|
||
教训: PS 5.1 + 外部命令 + Stop 模式 = 必炸,所有外部命令都要处理
|
||
```
|
||
|
||
### 坑 6: UTF-8 BOM 问题 (HIGH)
|
||
```
|
||
现象: "<# : 无法将 '<#' 项识别为 cmdlet" 或中文乱码
|
||
原因: Write 工具生成的文件无 BOM,PS 5.1 按 ANSI 解析 → 中文乱码
|
||
修复时 BOM 加了两次 → 双 BOM 导致 <# 注释块被当成代码
|
||
解决: 统一用 bash 添加一次 BOM: printf '\xEF\xBB\xBF' > tmp && cat file >> tmp
|
||
每次编辑后检查: head -c 3 file | xxd -p 应为 efbbbf
|
||
教训: 面向 PS 5.1 的 .ps1 文件必须有且仅有一个 UTF-8 BOM
|
||
```
|
||
|
||
### 坑 7: openssl 路径不在默认位置 (MEDIUM)
|
||
```
|
||
现象: "[ERROR] openssl 未找到"
|
||
原因: Git for Windows 安装在 D:\Git 而非默认 C:\Program Files\Git
|
||
解决: 搜索多个路径: C:\Program Files\Git, D:\Git, D:\Git\mingw64\bin
|
||
教训: 不要硬编码第三方软件路径,提供多路径搜索
|
||
```
|
||
|
||
### 坑 8: Gitea 端口被占用 (MEDIUM)
|
||
```
|
||
现象: 部署脚本设 3000 端口但已被 my-oa-frontend 容器占用
|
||
解决: 改为 3300 端口
|
||
教训: 部署前 netstat -tlnp | grep 端口 先检查
|
||
```
|
||
|
||
### 坑 9: Nginx 反代不传 Authorization 头 (MEDIUM)
|
||
```
|
||
现象: git push 通过 Nginx 反代时 404
|
||
原因: Nginx 默认传 Authorization 但旧的 bookworm-web.conf 先匹配了域名
|
||
解决: 换域名 + 添加 proxy_set_header Authorization $http_authorization
|
||
教训: 多个 Nginx 配置文件可能竞争同一个 server_name
|
||
```
|
||
|
||
### 坑 10: git clone 私有仓需要凭证 (LOW)
|
||
```
|
||
现象: install.ps1 中 git clone 静默失败 (无密码提示)
|
||
解决: 先手动 git clone 触发凭证输入 + git config credential.helper store
|
||
教训: 自动化脚本中的 git 操作需要预配置凭证
|
||
```
|
||
|
||
---
|
||
|
||
## 四、安全经验
|
||
|
||
### 三路专家审查的价值
|
||
- **红队**: 发现 14 个攻击向量,其中 INSTALL_LOCK=false 成功率 95%
|
||
- **代码审查**: 发现 5 个 Blocker + 13 个 Warning
|
||
- **架构审查**: 发现仓库体积失控 (预估 50MB 实际 500MB+)
|
||
- **交叉验证**: 三路独立发现的 openssl 命令行密码泄露问题,置信度极高
|
||
- **教训**: 多专家并行审查在 P0 修复前执行,投入产出比极高
|
||
|
||
### 安全加固清单 (实际落地的)
|
||
- [x] Gitea INSTALL_LOCK=true + 注册关闭 + CLI 创建管理员
|
||
- [x] Gitea 二进制 SHA256 校验
|
||
- [x] HTTPS (Let's Encrypt + Nginx 反代 + HSTS)
|
||
- [x] fail2ban (5次/小时 → 封24h)
|
||
- [x] Gitea 仅 127.0.0.1 监听
|
||
- [x] openssl 密码通过 stdin 管道 (不暴露进程列表)
|
||
- [x] PBKDF2 600,000 迭代 (OWASP 2023)
|
||
- [x] 凭证仅进程级环境变量 (不写磁盘)
|
||
- [x] .gitignore 排除凭证/大文件/敏感数据
|
||
- [x] git add 前人工确认 + 大文件检测
|
||
- [x] stop.ps1 清理 Git Credential Manager 缓存
|
||
|
||
---
|
||
|
||
## 五、PowerShell 跨版本兼容速查表
|
||
|
||
| PS 7+ 写法 | PS 5.1 兼容写法 |
|
||
|---|---|
|
||
| `$obj?.Property` | `if ($obj) { $obj.Property } else { $null }` |
|
||
| `$cmd?.Source` | `$cmd = Get-Command x -EA 0; if ($cmd) { $cmd.Source }` |
|
||
| `??` 空合并 | `if ($x) { $x } else { $default }` |
|
||
| UTF-8 无 BOM | 必须有 BOM: `\xEF\xBB\xBF` |
|
||
| `$ErrorActionPreference="Stop"` + git | 临时改 `Continue`,用 `$LASTEXITCODE` 判断 |
|
||
|
||
---
|
||
|
||
## 六、代理/VPN 检测方法速查
|
||
|
||
```powershell
|
||
# 最可靠: .NET DefaultWebProxy (覆盖所有系统代理)
|
||
[System.Net.WebRequest]::DefaultWebProxy.GetProxy("https://api.anthropic.com")
|
||
|
||
# IE 注册表
|
||
Get-ItemProperty "HKCU:\...\Internet Settings" | Select ProxyServer, ProxyEnable
|
||
|
||
# 端口扫描 (Clash 7890, V2Ray 10808, 快柠檬 10792)
|
||
Test-NetConnection 127.0.0.1 -Port 7890
|
||
```
|
||
|
||
---
|
||
|
||
## 七、项目最终产出
|
||
|
||
```
|
||
13 个文件 (bookworm-portable/)
|
||
2 个 Gitea 仓库 (bookworm-config 514文件/14MB, bookworm-boot 6文件)
|
||
3 个 ECS 部署脚本 (Gitea + HTTPS + 防火墙)
|
||
7 项 P0 安全修复
|
||
4 项 P1 安全加固
|
||
1 个保姆式 HTML 教程
|
||
1 个快速参考 TXT 手册
|
||
1 份经验避坑指南 (本文档)
|
||
|
||
E2E 验证: 远程 Windows Server → 从零安装 → Claude Code 启动成功
|
||
```
|
||
|
||
---
|
||
|
||
## 八、如果重新来过,我会...
|
||
|
||
1. **一开始就做 PS 5.1 兼容测试** — 不假设目标机有 PS 7
|
||
2. **一开始就加代理检测** — 国内 Claude Code 的核心门槛
|
||
3. **不在 settings.json env 段放 ${} 变量** — 行为未文档化
|
||
4. **先检查域名和端口占用** — 再写部署脚本
|
||
5. **三路专家审查提前到设计阶段** — 而非代码写完后再审
|
||
|
||
---
|
||
|
||
*Bookworm Portable v1.2 | code.letcareme.com | 2026-04-01*
|