140 lines
5.7 KiB
Markdown
140 lines
5.7 KiB
Markdown
# v1 → v2 迁移 — 开发指南
|
||
|
||
如何测试、开发和调试迁移流程。
|
||
|
||
## 快速入门
|
||
|
||
```bash
|
||
# 完整周期:重置 → 迁移 → Claude 完成
|
||
bash migrate-v2-reset.sh && bash migrate-v2.sh
|
||
```
|
||
|
||
## 架构
|
||
|
||
两部分迁移:
|
||
|
||
1. **`migrate-v2.sh`**——确定性 bash 脚本。处理前提条件、DB 种子、文件拷贝、频道安装、容器构建、服务切换。写入 `logs/setup-migration/handoff.json`,然后 `exec` 到 Claude 中。
|
||
|
||
2. **`/migrate-from-v1` 技能**——由 Claude 驱动。读取交接文件,种子 owner/roles,清理 CLAUDE.local.md,验证容器配置,移植 fork 定制。
|
||
|
||
## 文件布局
|
||
|
||
```
|
||
migrate-v2.sh # 入口点
|
||
migrate-v2-reset.sh # 清除 v2 状态以重新测试
|
||
setup/migrate-v2/
|
||
env.ts # 阶段 1a:合并 .env
|
||
db.ts # 阶段 1b:种子 v2 DB
|
||
groups.ts # 阶段 1c:拷贝 group 目录 + container.json
|
||
sessions.ts # 阶段 1d:拷贝 session 并设置续接
|
||
tasks.ts # 阶段 1e:移植计划任务
|
||
channel-auth.ts # 阶段 2b:拷贝频道认证状态
|
||
select-channels.ts # 阶段 2a:clack 多选
|
||
switchover-prompt.ts # 服务切换提示
|
||
setup/migrate-v2/shared.ts # 共享辅助函数(JID 解析、触发器映射等)
|
||
.claude/skills/migrate-from-v1/ # Claude 技能
|
||
logs/setup-migration/handoff.json # 由 migrate-v2.sh 写入,由技能读取
|
||
logs/migrate-steps/*.log # 每个步骤的原始输出
|
||
```
|
||
|
||
## 开发循环
|
||
|
||
```bash
|
||
# 重置 v2 到干净状态(保留 node_modules)
|
||
bash migrate-v2-reset.sh
|
||
|
||
# 以非交互式频道选择运行迁移
|
||
NANOCLAW_CHANNELS="telegram" bash migrate-v2.sh
|
||
|
||
# 或以交互方式运行(clack 多选)
|
||
bash migrate-v2.sh
|
||
```
|
||
|
||
`migrate-v2-reset.sh` 清除:`data/`、`logs/`、`.env`、`groups/`(恢复 git 跟踪的)、`container/skills/`(恢复 git 跟踪的)、`src/channels/`(恢复 git 跟踪的)。
|
||
|
||
它**不**清除 `node_modules/`(重新安装成本高昂)。
|
||
|
||
## 测试单个步骤
|
||
|
||
每个步骤是一个独立的 TypeScript 文件:
|
||
|
||
```bash
|
||
# 运行单个步骤(在 pnpm install 之后)
|
||
pnpm exec tsx setup/migrate-v2/env.ts /path/to/v1
|
||
pnpm exec tsx setup/migrate-v2/db.ts /path/to/v1
|
||
pnpm exec tsx setup/migrate-v2/groups.ts /path/to/v1
|
||
pnpm exec tsx setup/migrate-v2/sessions.ts /path/to/v1
|
||
pnpm exec tsx setup/migrate-v2/tasks.ts /path/to/v1
|
||
pnpm exec tsx setup/migrate-v2/channel-auth.ts /path/to/v1 telegram discord
|
||
```
|
||
|
||
每个步骤向 stdout 打印 `OK:<details>`、`SKIPPED:<reason>` 或错误。成功/跳过时退出 0,失败时退出非零值。
|
||
|
||
## 调试
|
||
|
||
### 检查已迁移的内容
|
||
|
||
```bash
|
||
# Agent groups
|
||
sqlite3 data/v2.db "SELECT * FROM agent_groups"
|
||
|
||
# Messaging groups + 连线
|
||
sqlite3 data/v2.db "SELECT mg.id, mg.channel_type, mg.platform_id, mg.unknown_sender_policy, mga.engage_mode, mga.engage_pattern FROM messaging_groups mg JOIN messaging_group_agents mga ON mga.messaging_group_id = mg.id"
|
||
|
||
# Sessions
|
||
sqlite3 data/v2.db "SELECT * FROM sessions"
|
||
|
||
# 用户和角色
|
||
sqlite3 data/v2.db "SELECT * FROM users"
|
||
sqlite3 data/v2.db "SELECT * FROM user_roles"
|
||
|
||
# Session 续接(哪个 Claude Code session 将被恢复)
|
||
AG_ID=$(sqlite3 data/v2.db "SELECT id FROM agent_groups LIMIT 1")
|
||
SESS_ID=$(sqlite3 data/v2.db "SELECT id FROM sessions LIMIT 1")
|
||
sqlite3 data/v2-sessions/$AG_ID/$SESS_ID/outbound.db "SELECT * FROM session_state"
|
||
|
||
# 计划任务
|
||
sqlite3 data/v2-sessions/$AG_ID/$SESS_ID/inbound.db "SELECT id, kind, recurrence, status FROM messages_in WHERE kind='task'"
|
||
```
|
||
|
||
### 检查交接文件
|
||
|
||
```bash
|
||
python3 -m json.tool logs/setup-migration/handoff.json
|
||
```
|
||
|
||
### 常见问题
|
||
|
||
**切换后 bot 不响应:**
|
||
1. 检查两个服务都没有运行:`systemctl --user list-units 'nanoclaw*'`
|
||
2. 检查错误日志:`tail logs/nanoclaw.error.log`
|
||
3. 检查发送者策略:`sqlite3 data/v2.db "SELECT unknown_sender_policy FROM messaging_groups"`——在 owner 被种子之前必须是 `public`
|
||
4. 检查触发模式:`sqlite3 data/v2.db "SELECT engage_mode, engage_pattern FROM messaging_group_agents"`——应为 `pattern` / `.` 用于响应所有内容
|
||
|
||
**Session 未从 v1 续接:**
|
||
1. 检查是否设置了续接:见上方的"Session 续接"查询
|
||
2. 检查 JSONL 是否在正确路径下:`ls data/v2-sessions/<ag_id>/.claude-shared/projects/-workspace-agent/`
|
||
3. v1 session JSONL 应从 `-workspace-group/` 拷贝到 `-workspace-agent/`(v2 容器 CWD 为 `/workspace/agent`)
|
||
|
||
**服务切换回退不起作用:**
|
||
1. v2 服务名称为 `nanoclaw-v2-<hash>`——找到它:`systemctl --user list-units 'nanoclaw*'`
|
||
2. 手动停止:`systemctl --user stop <unit> && systemctl --user disable <unit>`
|
||
3. 重启 v1:`systemctl --user start nanoclaw`
|
||
|
||
### 步骤日志
|
||
|
||
每个步骤将原始输出写入 `logs/migrate-steps/<step>.log`。当某个步骤失败时读取这些日志:
|
||
|
||
```bash
|
||
cat logs/migrate-steps/1b-db.log
|
||
cat logs/migrate-steps/1d-sessions.log
|
||
```
|
||
|
||
## 关键决策
|
||
|
||
- `unknown_sender_policy` 在迁移期间设置为 `public`,以便 bot 立即响应。`/migrate-from-v1` 技能在种子 owner 后收紧此设置。
|
||
- v1 中 `requires_trigger=0` 优先于非空的 `trigger_pattern`——它意味着"响应所有内容"。
|
||
- v1 `container_config.additionalMounts` 直接写入 v2 `container.json`(相同格式)。
|
||
- v1 Claude Code session 从 `-workspace-group/` 拷贝到 `-workspace-agent/`,session ID 以 `continuation:claude` 写入 `outbound.db`,以便 agent-runner 恢复同一对话。
|
||
- 结尾的 `exec claude "/migrate-from-v1"` 替换了 bash 进程——`write_handoff` 在 `exec` 之前显式调用,因为 EXIT 陷阱不会在 `exec` 时触发。
|