项目矩阵 个人工程基建 DDevHub
DDORA-MAINTAINED · CC ORCHESTRATOR

Nexora DevHub

派 CC 修 CC 的闭环平台 — 多项目、多端、一台控制台。DevHub 改自己、跑自己、修自己。

访问 devhub.dora.restry.cn 仅内网可达 Restry/nexora-devhub private repo

背景痛点 · Why this exists

5 月之前派 CC 干活,踩坑按出现顺序排列 — 不是想象,是真出过事

DevHub 一句话:让"派单 → 跑 → 看结果 → 复盘"这条链跟项目数量、设备形态、环境差异解耦

5 个核心抽象 · Five abstractions

Project
本地 git 仓库 + 运行命令 + env + 健康分 + AI 业务摘要
生产 6 个
Task
一次派 CC 的执行记录 · 状态机 / stdout / cost / routingNotes
78 / 65%
Snapshot Template
install / dev / build / probe 四段模板 + envHints,AI 自动 probe
PR #5~#11
Env Slot + PortLease
独立 env 槽 + 唯一端口账本,防多项目并发起 dev 撞端口
PR #31
CC Bridge (MCP)
不再直接 spawn,经 MCP → systemd-run --user --scope 跑 claude
PR #49+#56
截图位 · 健康分 dashboard 项目健康分 + 告警(成功率<0.7 或连击≥3 触发)+ 最近提交 + AI 业务摘要 URL https://devhub.dora.restry.cn/workspace/<projectId> 主页侧栏 · 来源 PR #16/#19/#42 次要
截图位 · Snapshot 模板编辑 AI prompt 改写 + 三段式探测/env/spawn 模板 URL https://devhub.dora.restry.cn/workspace/<projectId> → Snapshot → Templates · 来源 PR #8/#9/#10 次要

架构 · System architecture

三端入口(桌面 Web / 手机 PWA / 飞书 Hermes Agent)→ tRPC over HTTPS → server.ts (Next.js 16 + tRPC + WebSocket /pty) → MCP/systemd-run 调度执行层 → Postgres + 项目本地 .devhub/

入口层(三选一发起任务)
  ┌─ 桌面 Web         /workspace/<projectId>   (Tasks/Chat/Files/Git/Env/Terminal/Snapshot)
  ├─ 手机 Web (PWA)   /m/projects, /m/tasks, /m/chat   (bottom-tab + 下拉刷新)
  └─ Hermes Agent 飞书机器人   通过 DevHub MCP server 派单(dispatch_task)

         ↓ tRPC over HTTPS (subscriptions + live polling 3s)

业务层  server.ts (Next.js 16 + tRPC + WebSocket /pty)
  ├─ Task Dispatcher + reconcile   状态机 / 取消 / 孤儿接管
  ├─ Env Manager (W1/W2/v2)         ai-managed 自动 probe
  ├─ Snapshot Preview               起项目 dev server + 预览子域名反代
  ├─ Health Monitor                 健康分=成功率,<0.7 或连击≥3 触发告警
  ├─ MCP Server (16 个工具)         给 Hermes / Claude Code 反向回调
  └─ Authz (审批 + 项目成员绑定)    admin / member / owner 三档兜底

         ↓ MCP stdio                    ↓ systemd-run --user --scope

执行层
  ├─ lark-channel-bridge (cc-bridge)   每个任务独立 cgroup unit
  └─ claude CLI (Anthropic)             cwd=项目本地路径

         ↓                                  ↓

数据层
  ├─ Postgres (schema=claude)              User / Project / Task / Snapshot / PortLease / ...
  ├─ 项目本地仓库 .devhub/                  recipe + 任务 stdout/jsonl
  └─ Hermes state.db                       会话 / 记忆 / 技能(独立进程)

关键数据流(一次"飞书派任务"完整链路):飞书消息 → Hermes Agent → DevHub MCP dispatch_task → Task DB 入库(pending)→ Dispatcher 起 cc-bridge → systemd-run 起 claude 进程 → claude 在项目目录跑 → stdout/jsonl 实时回写 → 前端三端 live polling 3s 刷新 → 完成回写 status + cost + memory_patches → 飞书回包"✅ 任务完成"。

截图位 · Workspace 桌面主视图 左侧项目切换 + 中间 PTY 终端 + 右侧 PRD 对话 + 任务流,单页全栈 URL https://devhub.dora.restry.cn/workspace/<projectId>(拿 nexora-devhub 自己当样本,套娃感强) 必截

11 条关键里程碑 · Milestones

  1. 05-08立项首个 commit Initial commit: nexora-devhub project(6ffd9ea),项目开张
  2. 05-15主入口从 Mattermost/Clawline 切到 Hermes Agent + 飞书;同日 env-mgmt step1/2 上线(DB schema + EnvChips UI)
  3. 05-16单日 55+ commits 爆发:env-W1(SSRF/XSS/越权一锅端)+ env-W2 状态机 + settings-v2 W1.5→W1.16 + mcp-W1.11 加 16 个 Hermes MCP 工具
  4. 05-19CC 首次在 PackHorizon 和 Agent Portal 上跑探测任务(DevHub recipe 协议跑通)
  5. 05-20DevHub 本地化生产部署(nexora-devhub.service systemd 单元上线),task assignment JSON 派单协议稳定
  6. 05-21一天合 13 PR:Snapshot Phase 1 PR-1.1→1.7 串发 + strip-polluted-env-from-child-procs(PR #11)+ release v0.2.1
  7. 05-253 个 worktree 同时跑 issue #14/#15/#16,DevHub MCP server (PR #17) + routingNotes (PR #18) + 健康分 (PR #19) 三连合
  8. 05-28多用户接入:用户审批 + 项目成员绑定(PR #26),admin/member 兜底(PR #27/#28)
  9. 05-29越权审计 4 处修复(PR #33 IDOR / #34 snapshot+task authz / #32 ProjectEnv 对账 / #35 Task 索引反规范化)
  10. 05-3018 个 PR (#30~#48) 一日合:dispatcher 本机 spawn → MCP 客户端、IDOR 4 处修、健康分=成功率重定义、port-pool 退役 → PortLease 唯一账本、手机端项目卡片页
    截图位 · 手机 PWA 任务页 移动端 running 任务彩虹呼吸边 + 通知红点 URL https://devhub.dora.restry.cn/m/tasks · 用手机或 DevTools 375px 截 · 来源 PR #50 / f4454e1 必截
  11. 05-31SIGTERM 143 三连发修复(PR #56 cgroup 隔离 / #57 singleton pin globalThis / #58 ANTHROPIC env 透传)+ 任务卡片 inline live 状态(PR #59)+ DEPLOY.md 全套写完
    截图位 · 任务卡片 live 状态(最好录 GIF) running 任务行内联显示 CC 当前 tool / 最后一行输出 + 呼吸彩虹边 URL https://devhub.dora.restry.cn/workspace/<projectId> → 派一单 CC 现场录 · 来源 PR #59 + PR #38 必截

12 条关键 PR · Top commits

从 318 commits / 56 PR 里挑出"架构改动 / 安全修复 / 性能优化 / 大爆发日"四个维度的代表。

PR #11安全fix(aiManagedDev): env 隔离 + 三段 envHints 合并 — 子进程 NODE_ENV 污染彻底根治,prisma migrate dev 不再当 prod 跑
PR #13发版release: v0.2.1 — decouple probe + strip polluted env · 第一个稳定 release tag
PR #14→#17架构feat(mcp): DevHub MCP server — 反向暴露 16 个工具给 Hermes / CC 调用,闭环关键
PR #19优化feat(health): project health score + alerts — 健康分模型上线,给 #44 重定义为"成功率"打底
PR #26架构feat(auth): user approval + project member binding — 单用户 → 多用户演进的分水岭
PR #33安全fix(authz): 修复越权/IDOR 四处 — 多用户上线后必须打掉的 P0 越权
PR #31架构refactor(port): 退役 port-pool, PortLease 成为唯一端口账本 — 双账本合一,符合"不留新老共存"铁律
PR #49架构feat(cc): 用 MCP 替代本机 spawn(e36d4ce)— 从孤儿子进程 → 受 cgroup 管的命名 unit
PR #56修复fix: 用 systemd-run --scope 隔离 cc-bridge cgroup 防自杀 — SIGTERM 143 真凶第一刀
PR #57修复fix(mcp): pin ClaudeCodeMcp singleton to globalThis — 防 Next.js 模块复制造双 bridge(4 天误判 5 方向的最大坑)
PR #58修复fix(mcp): pass ANTHROPIC_* env into systemd-run user scope — cc-bridge 终于能拿到 API key
PR #59爆发feat(tasks): inline live status — 任务卡片行内实时显示 CC 当前工具 / 最后一行输出,3s 刷新,桌面+手机统一
PR #50+#51爆发feat: 手机端 MCP UI 对齐 + 任务闭环通知 — 移动办公闭环

名场面 · Hall of Fame

HALL OF FAME · 4 天 / 5 方向 / 11 行 diff

PR #57 · globalThis singleton 误判史诗

TL;DR · 4 天,误判 5 个方向,最后一行 console.log("NEW CLIENT") 定因 —— Next.js 同进程把同一 module import 了 N 次,new ClaudeCodeMcpClient() 被 new 出多份, 多个 cc-bridge cgroup scope 并存,一个 scope 回收把另一个持有的 CC 子进程连坐 SIGTERM(exit 143)。

现象

派 CC 单子,跑着跑着 claude 子进程突然 exit 143(SIGTERM),但谁也没 kill 它。CC 自己日志 ~/.claude/projects/*/jsonl 最后一行还在好好写 tool call,下一秒就没了。手动 claude -p "ping" 一切正常。

4 天里误判的 5 个方向

  1. pkill -f 自杀
    怀疑 dispatch 脚本里 pkill -f claude 把自己 prompt 里出现的 "claude" 匹配上自杀。查 ps -ef 看父子关系。
    pkill 那条根本没跑。
  2. systemd OOM
    journalctl -k | grep -i oom,再 systemd-cgtop 看 cc-bridge scope 内存。
    没 OOM 记录,内存才几十 M。
  3. ANTHROPIC env 没透传
    因为 PR #58 刚修过 env 透传,以为没修干净。在子进程里 claude -p "ping"
    跑通了,env 确实在。
  4. MCP transport 心跳超时主动断
    改 SDK 心跳间隔、加 reconnect。
    没用;断完 CC 还是 143。
  5. Next.js HMR 重载
    怀疑 dev 模式热重载把 module 重新求值,dispose 老 client。关 HMR 跑 prod build。
    还是复现 — 但这里其实摸到了边。

定位的关键一击

ClaudeCodeMcpClient 构造函数里加 console.log("NEW CLIENT", Math.random()),发了一单 CC 后,日志里冒出来 2 行 NEW CLIENT —— 同一进程里 client 被 new 了两次。 继续 ps -ef | grep cc-bridge,看见两个 cc-bridge-*.scope 同时存在,每个 scope 各自挂着一个 claude。其中一个 transport 抖了一下触发 reconnect,systemd 把那个 scope 回收,cgroup v2 连坐把另一份的 claude 也 SIGTERM 了

根因

Next.js 的 bundler 在同一 Node 进程里会把同一个 module 解析 / import 多次(route bundle、RSC bundle、ESM/CJS 互转),module-local 的 export const claudeCodeMcp = new ClaudeCodeMcpClient() 因此被求值多次 —— 这是 Prisma 官方文档里的经典坑("prevent multiple instances of Prisma Client in development"),CC bridge 撞了同一个雷。

修复一句话

把单例钉到 globalThis[Symbol.for("nexora.devhub.claudeCodeMcp")],全进程唯一。11 行 diff,2026-05-31 18:45 合入 e36d4ce (PR #57),立刻不复发。

沉淀到 memory 的教训

子进程启动就死,先 claude -p "ping" 验环境,再查架构 + 看任务实际跑了什么命令(~/.claude/projects/*/jsonl 最后一行),再扣机制。pkill -f <pat><pat> 出现在自己 prompt 参数里会自杀。

衍生:Next.js 里所有"全进程应当只有一个"的对象(DB client / MCP bridge / 后台 worker),都要 pin 到 globalThis,别信 module-local const

技术栈 · Tech stack

Frontend
Next.js 16(App Router)· React 19 · TypeScript · tRPC v11 · TanStack Query · Radix UI · Tailwind · Monaco Editor · xterm.js
Backend
自定义 server.ts(Next + WebSocket /pty)· tRPC · Prisma 5 · isomorphic-git
Database
Postgres(schema=claude,复用 supabase-db 容器,与 dev 同库)
协议层
MCP(@modelcontextprotocol/sdk)· HTTP Proxy(预览子域名反代)
CC 集成
lark-channel-bridge(GitHub 包,自 build dist/)· claude CLI · systemd-run --user --scope 每任务独立 cgroup
入口适配
Hermes Agent(Python)· 多通道:飞书 / Telegram / Discord / Mattermost / ACP
部署
systemd nexora-devhub.service(KillMode=process,restart 不杀子任务)· Caddy 反代 · 内网域名
包管理 / ORM
pnpm(fccb 子项目)+ npm(主项目)· Prisma 5 已 squash baseline(PR #54)
Testing
vitest · promptAssist / snapshot.applyAsRecipe / snapshotTemplate

代码规模:235 个 .ts/.tsx 文件,src 下分 app(路由)/ components / lib / server(tRPC routers 19 个 + 业务 lib)。

现状 + 未结清债务 · Status & debt

现状(2026-06-01 实测)

  • 部署 · 本机 systemd nexora-devhub.service,监听 6006,Caddy 反代到 devhub.dora.restry.cn(仅内网可达,未对公网)
  • 数据规模 · 6 个项目 · 78 历史任务(成功率 65%)· 2 个用户
  • 代码体量 · 318 commits · 56 merged PR · 33 model/enum · 19 tRPC routers
  • 里程碑版本 · v0.2.1(5/21 发布;之后未再打 tag,算技术债)
  • CI/CD · 暂无 GitHub Actions · merge → 本地 npm run build && systemctl restart 手动部署
  • 跨机部署 · DEPLOY.md 已写完(5/31)· subagent dry-run 验证 A 级 · 另一台机器部署 500 排障未完

未结清债务(按优先级)

  • P0部署机 500 根因未定位(缺 journalctl 日志,等回包)
  • P0没有自动备份策略(DB 改动前手动 pg_dump 是铁律,但没自动化)
  • P0没有 CI(test / lint / typecheck 都在本地跑,依赖人)
  • P1/m/* 手机端跟桌面端 UI 双套(~2800 行重复),REFACTOR_PLAN.md 已有未执行
  • P1v0.2.1 之后没打 tag,发版规范断了
  • P1chat 路由 / 任务详情仍有少量 N+1 风险(PR #35/#36/#39 砍了一批,有死角)
  • P2没有 Sentry / 错误聚合,前端报错只在浏览器 console
  • P2Snapshot 模板 AI probe 偶发漏字段(envHints),需要人工补
  • P2健康分定义稳定了,但还没做"按 owner / 按时间段"维度筛选