跳转至

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 版本引入了两项重大的架构迁移:

  1. 进程管理:PM2 → 基于 Bun 的自定义 ProcessManager
  2. 数据库驱动: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

核心机制:

  1. PID 文件管理:
  2. 文件:~/.claude-mem/.worker.pid
  3. 内容:进程 ID(例如 "35557")
  4. 验证:通过 kill(pid, 0) 信号检查进程是否存在

  5. 端口文件管理:

  6. 文件:~/.claude-mem/.worker.port
  7. 内容:两行(端口号、PID)
  8. 用途:跟踪端口绑定并验证 PID 匹配

  9. 健康检查:

  10. 第 1 层:PID 文件是否存在?
  11. 第 2 层:进程是否存活?(kill(pid, 0))
  12. 第 3 层:HTTP 健康检查(GET /health)
  13. 三项都必须通过才能视为“健康”状态

优势: - 无外部依赖 - 代码库更简单(直接控制) - 更好的错误处理和验证 - 平台无关(由 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 时间戳

2025-12-13T00:18:39.673Z

用途: - 一次性迁移标记 - 防止每次启动时重复执行 PM2 清理 - 在重启和重新启动后持续存在

生命周期: - 创建:更新到 7.1.0+ 后的首次钩子触发(所有平台) - 更新:从不 - 删除:从不(用户可以手动删除以强制重新迁移)

用户体验时间线

更新后的首次会话

Note

这是关键的迁移时刻。整个过程大约需要 2-5 秒。

分步执行:

  1. 钩子触发(最常见的是 SessionStart)
  2. 工作进程状态检查:无 PID 文件 → 工作进程未运行
  3. 迁移检查:无标记文件 → 执行 PM2 清理
  4. PM2 清理:pm2 delete claude-mem-worker(旧工作进程已终止)
  5. 标记文件创建:~/.claude-mem/.pm2-migrated 带时间戳
  6. 新工作进程启动:Bun 进程已生成,PID/端口文件已创建
  7. 验证:进程检查 + HTTP 健康检查
  8. 钩子完成:Claude Code 会话正常启动

用户可观察到的行为: - 首次启动有轻微延迟(PM2 清理 + 新工作进程生成) - 无错误消息(清理失败会被静默处理) - 通过 npm run worker:status 显示工作进程正在运行 - 旧 PM2 工作进程不再出现在 pm2 list 中

后续会话

迁移完成后,每次钩子触发都走快速路径:

  1. PID 文件存在?是
  2. 进程存活?是
  3. HTTP 健康检查?成功
  4. 结果:工作进程已在运行,完成(约 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/pids/claude-mem-worker.pid

New: ~/.claude-mem/.worker.pid

进程状态:

Old: PM2 daemon memory (pm2 save)

New: ~/.claude-mem/.worker.pid
     ~/.claude-mem/.worker.port
     ~/.claude-mem/.pm2-migrated (all platforms)

数据库(不变):

Same: ~/.claude-mem/claude-mem.db

用户可见的变化

更新前:

$ 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。

手动解决:

rm ~/.claude-mem/.worker.pid
rm ~/.claude-mem/.worker.port
npm run worker:start

场景三:端口已被占用

错误: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 的迁移是一次一次性、自动、透明的过渡,它:

  1. 移除外部依赖(PM2、better-sqlite3)
  2. 简化架构(直接进程控制)
  3. 改善跨平台支持(尤其是 Windows)
  4. 保留用户数据(数据库、设置、日志不变)
  5. 无需用户操作(首次 hook 触发时自动完成)

关键迁移时机:更新到 7.1.0+ 后的首次 hook 触发 持续时间:约 2-5 秒(一次性延迟) 影响:无缝过渡,用户无感知 回滚:不需要(迁移只向前,安全)

对大多数用户来说,迁移将是完全透明的——他们不会看到任何错误,不会有数据丢失,并且会体验到更高的可靠性和更简单的故障排查。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/claude-mem