Note
历史迁移文档
本文档描述了 v7.1.0(2025 年 12 月)中发生的 PM2 到 Bun 迁移。如果你是首次安装 claude-mem,该迁移已经完成,你可以使用主指南中记录的当前基于 Bun 的系统。
本文档保留给从早于 v7.1.0 的版本升级的用户。
PM2 到 Bun 迁移:完整技术文档¶
版本:7.1.0 日期:2025 年 12 月 迁移类型:进程管理(PM2 → Bun)+ 数据库驱动(better-sqlite3 → bun:sqlite)
执行摘要¶
claude-mem 7.1.0 版本引入了两项重大的架构迁移:
- 进程管理:PM2 → 基于 Bun 的自定义 ProcessManager
- 数据库驱动:better-sqlite3 npm 包 → bun:sqlite 运行时模块
这两项迁移对最终用户都是自动且透明的。在更新到 7.1.0+ 之后,当钩子首次触发时,系统会一次性清理旧版 PM2 进程,并过渡到新架构。
关键优势¶
- 简化依赖:移除 PM2 和 better-sqlite3 npm 包
- 改善跨平台支持:更好的 Windows 兼容性
- 更快的安装:无需原生模块编译
- 内置运行时:利用 Bun 内置的进程管理和 SQLite
- 降低复杂性:自定义 ProcessManager 比 PM2 集成更简单
迁移影响¶
- 数据保留:用户数据、设置和数据库保持不变
- 自动清理:旧 PM2 进程自动终止(所有平台)
- 无需用户操作:迁移在首次钩子触发时自动进行
- 向后兼容:SQLite 数据库格式不变(仅驱动更换)
架构对比¶
旧系统(基于 PM2){#old-system-pm2-based}¶
进程管理(PM2)
组件:PM2(进程管理器 2)
- 包:pm2 npm 依赖
- 进程名称:claude-mem-worker
- 管理:外部 PM2 守护进程管理生命周期
- 发现:pm2 list、pm2 describe 命令
- 自动重启:PM2 在崩溃时自动重启
- 日志:~/.pm2/logs/claude-mem-worker-*.log
- PID 文件:~/.pm2/pids/claude-mem-worker.pid
生命周期命令:
pm2 start <script> # Start worker
pm2 stop claude-mem-worker # Stop worker
pm2 restart claude-mem-worker # Restart worker
pm2 delete claude-mem-worker # Remove from PM2
pm2 logs claude-mem-worker # View logs
痛点: - 需要额外的 npm 依赖 - PM2 守护进程必须处于运行状态 - 可能与其他 PM2 进程冲突 - Windows 兼容性问题 - 对简单用例来说配置过于复杂
数据库驱动(better-sqlite3)
组件:better-sqlite3
- 包:better-sqlite3 npm 包(原生模块)
- 安装:需要原生编译(node-gyp)
- Windows:需要 Visual Studio 构建工具 + Python
- 导入:import Database from 'better-sqlite3'
安装要求: - Node.js 开发头文件 - C++ 编译器(Mac/Linux 上为 gcc/clang,Windows 上为 MSVC) - Python(用于 node-gyp) - Windows:Visual Studio 构建工具
新系统(基于 Bun){#new-system-bun-based}¶
进程管理(自定义 ProcessManager)
组件:自定义 ProcessManager(src/services/process/ProcessManager.ts)
- 包:Bun 内置 API(无外部依赖)
- 进程生成:以分离模式(detached mode)执行 Bun.spawn()
- 管理:通过 PID 文件进行直接进程控制
- 发现:PID 文件 + 进程存在性检查 + HTTP 健康检查
- 自动重启:检测到故障时由钩子触发重启
- 日志:~/.claude-mem/logs/worker-YYYY-MM-DD.log
- PID 文件:~/.claude-mem/.worker.pid
- 端口文件:~/.claude-mem/.worker.port(新增)
生命周期命令:
npm run worker:start # Start worker
npm run worker:stop # Stop worker
npm run worker:restart # Restart worker
npm run worker:status # Check status
npm run worker:logs # View logs
核心机制:
- PID 文件管理:
- 文件:
~/.claude-mem/.worker.pid - 内容:进程 ID(例如 "35557")
-
验证:通过
kill(pid, 0)信号检查进程是否存在 -
端口文件管理:
- 文件:
~/.claude-mem/.worker.port - 内容:两行(端口号、PID)
-
用途:跟踪端口绑定并验证 PID 匹配
-
健康检查:
- 第 1 层:PID 文件是否存在?
- 第 2 层:进程是否存活?(
kill(pid, 0)) - 第 3 层:HTTP 健康检查(
GET /health) - 三项都必须通过才能视为“健康”状态
优势: - 无外部依赖 - 代码库更简单(直接控制) - 更好的错误处理和验证 - 平台无关(由 Bun 处理平台差异)
数据库驱动(bun:sqlite)
组件:bun:sqlite
- 包:内置于 Bun 运行时(无 npm 包)
- 安装:无需安装(随 Bun ≥1.0 提供)
- 平台:凡 Bun 可运行之处皆可运行
- 导入:import { Database } from 'bun:sqlite'
- API:与 better-sqlite3 类似(同步)
安装要求: - Bun ≥1.0(缺失时自动安装) - 无需原生编译 - 无需平台特定的构建工具
兼容性:
- SQLite 数据库格式:不变
- 数据库文件:~/.claude-mem/claude-mem.db(相同位置)
- 查询语法:完全相同(两者都使用 SQLite SQL)
迁移机制¶
一次性 PM2 清理¶
迁移系统采用基于标记的方式,确保 PM2 清理只执行一次。
实现:src/shared/worker-utils.ts:73-86
// Clean up legacy PM2 (one-time migration)
const pm2MigratedMarker = join(DATA_DIR, '.pm2-migrated');
if (!existsSync(pm2MigratedMarker)) {
try {
spawnSync('pm2', ['delete', 'claude-mem-worker'], { stdio: 'ignore' });
// Mark migration as complete
writeFileSync(pm2MigratedMarker, new Date().toISOString(), 'utf-8');
logger.debug('SYSTEM', 'PM2 cleanup completed and marked');
} catch {
// PM2 not installed or process doesn't exist - still mark as migrated
writeFileSync(pm2MigratedMarker, new Date().toISOString(), 'utf-8');
}
}
迁移触发点¶
1. 钩子执行
SessionStart、UserPromptSubmit 或 PostToolUse 钩子使用新的 7.1.0 代码执行
2. 工作进程状态检查
ensureWorkerRunning() 检查 ~/.claude-mem/.worker.pid 是否存在(更新后首次运行时不存在)
3. 启动工作进程决策
工作进程未运行 → 调用 startWorker()
4. 迁移检查
检查 ~/.claude-mem/.pm2-migrated 是否存在
5. PM2 清理
执行 pm2 delete claude-mem-worker(忽略错误),创建标记文件
6. 新工作进程启动
生成新的由 Bun 管理的工作进程,并带有 PID 和端口文件
标记文件¶
位置:~/.claude-mem/.pm2-migrated
内容:ISO 8601 时间戳
用途: - 一次性迁移标记 - 防止每次启动时重复执行 PM2 清理 - 在重启和重新启动后持续存在
生命周期: - 创建:更新到 7.1.0+ 后的首次钩子触发(所有平台) - 更新:从不 - 删除:从不(用户可以手动删除以强制重新迁移)
用户体验时间线¶
更新后的首次会话¶
Note
这是关键的迁移时刻。整个过程大约需要 2-5 秒。
分步执行:
- 钩子触发(最常见的是 SessionStart)
- 工作进程状态检查:无 PID 文件 → 工作进程未运行
- 迁移检查:无标记文件 → 执行 PM2 清理
- PM2 清理:
pm2 delete claude-mem-worker(旧工作进程已终止) - 标记文件创建:
~/.claude-mem/.pm2-migrated带时间戳 - 新工作进程启动:Bun 进程已生成,PID/端口文件已创建
- 验证:进程检查 + HTTP 健康检查
- 钩子完成:Claude Code 会话正常启动
用户可观察到的行为:
- 首次启动有轻微延迟(PM2 清理 + 新工作进程生成)
- 无错误消息(清理失败会被静默处理)
- 通过 npm run worker:status 显示工作进程正在运行
- 旧 PM2 工作进程不再出现在 pm2 list 中
后续会话¶
迁移完成后,每次钩子触发都走快速路径:
- PID 文件存在?是
- 进程存活?是
- HTTP 健康检查?成功
- 结果:工作进程已在运行,完成(约 50ms)
后续会话不再运行迁移逻辑。
平台特定行为¶
平台对比¶
| 平台 | macOS | Linux | Windows |
|---|---|---|---|
| PM2 清理 | 会尝试 | 会尝试 | 会尝试 |
| 标记文件 | 会创建 | 会创建 | 会创建 |
| 进程信号 | POSIX(原生) | POSIX(原生) | Bun 抽象 |
| Bun 支持 | 完整 | 完整 | 完整 |
| PID 文件 | 是 | 是 | 是 |
| 端口文件 | 是 | 是 | 是 |
| 健康检查 | HTTP | HTTP | HTTP |
| 迁移延迟 | 首次约 2-5 秒 | 首次约 2-5 秒 | 首次约 2-5 秒 |
平台说明¶
- POSIX 信号处理原生可用
- Bun 完全支持
- 无需特定平台的变通方案
- 与 macOS 行为一致
- POSIX 信号处理
- 适用于 Ubuntu、Debian、RHEL、CentOS、Arch
- Alpine 可能要求 glibc(而非 musl)
- PM2 清理现在会执行(由于 try/catch 而安全)
- Bun 抽象了信号处理差异
- Path 模块处理 Windows 分隔符
- 文件锁定由 SQLite 处理
可观察到的变化¶
命令变化¶
| 旧(PM2) | 新(Bun) | 备注 |
|---|---|---|
pm2 list |
npm run worker:status |
显示工作进程状态 |
pm2 start <script> |
npm run worker:start |
启动工作进程 |
pm2 stop claude-mem-worker |
npm run worker:stop |
停止工作进程 |
pm2 restart claude-mem-worker |
npm run worker:restart |
重启工作进程 |
pm2 delete claude-mem-worker |
npm run worker:stop |
移除工作进程 |
pm2 logs claude-mem-worker |
npm run worker:logs |
查看日志 |
pm2 describe claude-mem-worker |
npm run worker:status |
详细状态 |
pm2 monit |
无对应命令 | PM2 专用监控 |
文件位置变化¶
日志:
Old: ~/.pm2/logs/claude-mem-worker-out.log
~/.pm2/logs/claude-mem-worker-error.log
New: ~/.claude-mem/logs/worker-YYYY-MM-DD.log
PID 文件:
进程状态:
Old: PM2 daemon memory (pm2 save)
New: ~/.claude-mem/.worker.pid
~/.claude-mem/.worker.port
~/.claude-mem/.pm2-migrated (all platforms)
数据库(不变):
用户可见的变化¶
更新前:
$ pm2 list
┌────┬────────────────────┬─────────┬─────────┬──────────┐
│ id │ name │ status │ restart │ uptime │
├────┼────────────────────┼─────────┼─────────┼──────────┤
│ 0 │ claude-mem-worker │ online │ 0 │ 2d 5h │
└────┴────────────────────┴─────────┴─────────┴──────────┘
更新后:
$ pm2 list
# Empty - worker no longer managed by PM2
$ npm run worker:status
Worker is running
PID: 35557
Port: 37777
Uptime: 2h 15m
孤儿文件¶
迁移完成后,以下 PM2 文件可能会保留(可以安全删除):
~/.pm2/ # Entire PM2 directory
~/.pm2/logs/ # Old logs
~/.pm2/pids/ # Old PID files
~/.pm2/pm2.log # PM2 daemon log
~/.pm2/dump.pm2 # PM2 process dump
清理(可选):
# Remove PM2 entirely (if not used for other processes)
pm2 kill
rm -rf ~/.pm2
# Or just remove claude-mem logs
rm -f ~/.pm2/logs/claude-mem-worker-*.log
rm -f ~/.pm2/pids/claude-mem-worker.pid
文件系统状态¶
状态目录结构¶
迁移前(PM2 系统):
~/.claude-mem/
├── claude-mem.db # Database (unchanged)
├── chroma/ # Vector embeddings (unchanged)
├── logs/ # Application logs (unchanged)
└── settings.json # User settings (unchanged)
~/.pm2/
├── logs/
│ ├── claude-mem-worker-out.log
│ └── claude-mem-worker-error.log
├── pids/
│ └── claude-mem-worker.pid
└── pm2.log
迁移后(Bun 系统):
~/.claude-mem/
├── claude-mem.db # Database (same file)
├── chroma/ # Vector embeddings (unchanged)
├── logs/
│ └── worker-2025-12-13.log # New log format
├── settings.json # User settings (unchanged)
├── .worker.pid # NEW: Process ID
├── .worker.port # NEW: Port + PID
└── .pm2-migrated # NEW: Migration marker (all platforms)
~/.pm2/ # Orphaned (safe to delete)
├── logs/ # Old logs (no longer written)
├── pids/ # Old PID (no longer updated)
└── pm2.log # PM2 daemon log (not used)
边界情况与故障排除¶
场景一:迁移失败(PM2 仍在运行)¶
Warning
这种情况很少见,但如果 PM2 启用了 watch 模式,或者进程被手动重启,就可能发生。
症状:
- pm2 list 仍然显示 claude-mem-worker
- 日志中出现端口冲突错误
- Worker 无法启动
解决方法:
# Manual cleanup
pm2 delete claude-mem-worker
pm2 save # Persist the deletion
# Force re-migration (optional)
rm ~/.claude-mem/.pm2-migrated
# Restart worker
npm run worker:restart
场景二:过期的 PID 文件(进程已死)¶
症状:
- npm run worker:status 显示 "not running"
- .worker.pid 文件存在
- PID 不存在
自动恢复:下一次 hook 触发时会检测到死进程,并启动一个新的 worker。
手动解决:
场景三:端口已被占用¶
错误:EADDRINUSE: address already in use
解决方法:
# Check what's using the port
lsof -i :37777
# Kill the process
kill -9 <PID>
# Restart worker
npm run worker:restart
常见错误消息¶
| 错误 | 原因 | 解决方法 |
|---|---|---|
EADDRINUSE |
端口已被占用 | 运行 lsof -i :37777,然后终止冲突进程 |
No such process |
过期的 PID 文件 | 下次 hook 触发时自动清理 |
pm2: command not found |
未安装 PM2 | 无需处理(错误会被捕获并忽略) |
Invalid port X |
端口校验失败 | 更新设置中的 CLAUDE_MEM_WORKER_PORT |
开发者注意事项¶
测试迁移¶
# 1. Install old version (with PM2)
git checkout <pre-7.1.0-tag>
npm install && npm run build && npm run sync-marketplace
# 2. Start PM2 worker
pm2 start plugin/scripts/worker-cli.js --name claude-mem-worker
# 3. Update to new version
git checkout main
npm install && npm run build && npm run sync-marketplace
# 4. Trigger hook
node plugin/scripts/session-start-hook.js
# 5. Verify migration
pm2 list # Should NOT show claude-mem-worker
cat ~/.claude-mem/.pm2-migrated # Should exist
npm run worker:status # Should show Bun worker running
架构决策¶
为什么用自定义 ProcessManager 而不是 PM2? 1. 简单性:直接控制,无需外部守护进程 2. 依赖:移除 npm 依赖 3. 跨平台:Bun 处理平台差异 4. 打包体积:减小插件包体积 5. 可控性:细粒度的错误处理和校验
为什么用一次性标记而不是始终运行 PM2 删除? 1. 性能:避免不必要的进程派生 2. 幂等性:迁移恰好运行一次 3. 调试:时间戳显示迁移发生的时间 4. 简单性:清晰的迁移状态
为什么在所有平台上运行 PM2 清理? 1. 迁移质量:清理孤立进程 2. 一致性:所有平台行为一致 3. 安全性:错误处理已就绪(try/catch) 4. 无副作用:如果未安装 PM2,错误会被捕获并忽略
总结¶
从 PM2 到基于 Bun 的 ProcessManager 的迁移是一次一次性、自动、透明的过渡,它:
- 移除外部依赖(PM2、better-sqlite3)
- 简化架构(直接进程控制)
- 改善跨平台支持(尤其是 Windows)
- 保留用户数据(数据库、设置、日志不变)
- 无需用户操作(首次 hook 触发时自动完成)
关键迁移时机:更新到 7.1.0+ 后的首次 hook 触发 持续时间:约 2-5 秒(一次性延迟) 影响:无缝过渡,用户无感知 回滚:不需要(迁移只向前,安全)
对大多数用户来说,迁移将是完全透明的——他们不会看到任何错误,不会有数据丢失,并且会体验到更高的可靠性和更简单的故障排查。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/claude-mem