基于 /Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked 逆向分析
WorkBuddy Desktop v5.2.5 + CodeBuddy CLI v2.106.4 | 腾讯出品
一、整体架构概览
graph TB
subgraph "操作系统层"
OS["macOS / Windows<br/>Darwin / Win32"]
FSE["FSEvents API<br/>(macOS 文件监听)"]
PTY["Pseudoterminal API<br/>(openpty / ConPTY)"]
SQLITE["SQLite Engine<br/>(C++ 绑定)"]
end
subgraph "Electron 应用外壳 (WorkBuddy Desktop v5.2.5)"
ELECTRON["Electron Main Process<br/>Node.js + Chromium"]
RENDER["Renderer Process<br/>React/Vue UI + WebView"]
DEVTOOLS["DevTools Panel<br/>Ghostty WASM Terminal"]
PRELOAD["Preload Scripts<br/>IPC 安全桥接"]
end
subgraph "CLI 核心宿主 (CodeBuddy CLI v2.106.4)"
CLI["cli/bin/codebuddy<br/>Node.js 启动器"]
TUI["TUI Mode<br/>Ink + React 19<br/>~16MB Bundle"]
DAEMON["Daemon Mode<br/>HTTP Server<br/>Port 51862+"]
HEADLESS["Headless Mode<br/>-p / --print<br/>CI/CD 管道"]
WEBUI["Web UI<br/>Workers / Logs / Metrics"]
end
subgraph "原生模块层 (app.asar.unpacked)"
NATIVE_MOD["node_modules/"]
BETTER_SQL["better-sqlite3<br/>v12.8.0 (MIT)"]
NODE_PTY["node-pty<br/>v1.1.0 (Microsoft)"]
LYDELL_PTY["@lydell/node-pty-darwin-arm64<br/>v1.2.0-beta.12"]
DOCS_ENGINE["@tencent/docs-engine<br/>v0.0.1-beta.9"]
FSE_MOD["fsevents<br/>(nunjucks 嵌套依赖)"]
end
subgraph "资源与扩展层 (resources/)"
RES["resources/"]
BUILT_PLUGINS["builtin-plugins/"]
BUILT_SKILLS["builtin-skills/<br/>15 个内置技能"]
BUILT_MCP["builtin-mcp-apps/"]
TEMPLATES["templates/<br/>Nunjucks 提示词模板"]
DEVTERM["devtools-terminal/<br/>Chrome Extension v3"]
BRANDING["channel-branding/<br/>OEM 品牌资源"]
end
subgraph "AI 推理后端"
BACKEND["copilot.tencent.com<br/>AI 推理集群"]
MCP_PROXY["MCP Connector Proxy<br/>Port 61047"]
end
subgraph "用户数据层 (~/.workbuddy/)"
USER_DATA["~/.workbuddy/"]
DB["workbuddy.db<br/>(SQLite + WAL)"]
SOUL["SOUL.md<br/>AI 灵魂"]
IDENTITY["IDENTITY.md<br/>智能体身份"]
MEMORY["memory/<br/>项目级记忆"]
SESSIONS["sessions/<br/>会话 JSON"]
TRACES["traces/<br/>运行追踪"]
end
OS --> ELECTRON
ELECTRON --> RENDER
ELECTRON --> PRELOAD
RENDER --> DEVTOOLS
ELECTRON -->|spawn| CLI
CLI --> TUI
CLI --> DAEMON
CLI --> HEADLESS
DAEMON --> WEBUI
NATIVE_MOD --> BETTER_SQL
NATIVE_MOD --> NODE_PTY
NATIVE_MOD --> LYDELL_PTY
NATIVE_MOD --> DOCS_ENGINE
NATIVE_MOD --> FSE_MOD
CLI --> NATIVE_MOD
CLI --> RES
RES --> BUILT_PLUGINS
RES --> BUILT_SKILLS
RES --> BUILT_MCP
RES --> TEMPLATES
RES --> DEVTERM
RES --> BRANDING
TUI -->|HTTP/WebSocket| BACKEND
DAEMON -->|HTTP API| BACKEND
HEADLESS -->|HTTP API| BACKEND
BACKEND -->|MCP STDIO/SSE/HTTP| MCP_PROXY
CLI -->|读写| USER_DATA
DAEMON -->|读写| DB
TUI -->|读取| SOUL
TUI -->|读取| IDENTITY
TUI -->|读取| MEMORY
TUI -->|写入| SESSIONS
TUI -->|写入| TRACES
FSE -->|API 调用| FSE_MOD
PTY -->|API 调用| NODE_PTY
PTY -->|API 调用| LYDELL_PTY
SQLITE -->|C++ 绑定| BETTER_SQL
DOCS_ENGINE -->|"FFI 加载:libeditor_sdk_ffi.dylib (172 MB)"| EXT1
DOCS_ENGINE -->|"HTTP Server:127.0.0.1:39099 本地文档预览"| EXT2
DEVTERM -->|"WASM 执行:ghostty-vt.wasm 终端仿真"| EXT3
EXT1 & EXT2 & EXT3:::dummy
classDef dummy fill:none,stroke:none
style ELECTRON fill:#e1f5fe
style CLI fill:#fff3e0
style BACKEND fill:#e8f5e9
style USER_DATA fill:#fce4ec
style NATIVE_MOD fill:#f3e5f5
style RES fill:#e0f2f1
二、Electron 主进程与 CLI 双层架构
flowchart LR
subgraph "Electron 主进程"
direction TB
EP1["app-instance.ts<br/>多实例检测"]
EP2["auth.ts<br/>腾讯云 OAuth"]
EP3["daemon-app-server-*.ts<br/>守护进程服务"]
EP4["desktop-monitor-service.ts<br/>桌面监控"]
EP5["helpers.ts<br/>工具函数集"]
EP6["file-authentication-storage.ts<br/>文件认证存储"]
end
subgraph "CLI 进程 (Node.js 子进程)"
direction TB
C1["bin/codebuddy<br/>入口启动器"]
C2["V8 Compile Cache<br/>~37% 启动加速"]
C3["Node >= 18.20.8<br/>版本检查"]
C4["Headless 路由<br/>--print / daemon / -p"]
C5["TUI Bundle<br/>codebuddy.js<br/>Ink + React 19<br/>~16MB"]
C6["Headless Bundle<br/>codebuddy-headless.js"]
end
subgraph "通信层"
COM1["stdio<br/>标准输入输出"]
COM2["HTTP<br/>Web UI / API"]
COM3["WebSocket<br/>实时消息"]
COM4["IPC<br/>Electron 内部"]
end
EP1 -->|"spawn('node', ['bin/codebuddy'])"| C1
EP3 -->|HTTP 端口| COM2
C1 --> C2
C1 --> C3
C1 --> C4
C4 -->|TUI 模式| C5
C4 -->|无头模式| C6
C5 -->|stdio| COM1
C5 -->|WebSocket| COM3
C6 -->|HTTP| COM2
EP1 -->|IPC| COM4
RENDER["Renderer Process<br/>(UI 渲染)"] -->|IPC| COM4
style EP1 fill:#e3f2fd
style EP2 fill:#e3f2fd
style EP3 fill:#e3f2fd
style C1 fill:#fff8e1
style C5 fill:#fff8e1
style C6 fill:#fff8e1
关键设计决策:
- Desktop 与 CLI 版本分离:WorkBuddy Desktop
v5.2.5 是 Electron 外壳,CodeBuddy CLI v2.106.4 是独立 Node.js 进程,两者版本独立演进
- 双 Bundle 策略:TUI 模式用
ink + React 19 提供交互终端,Headless 模式剔除 UI 依赖,显著减小启动开销
- V8 Compile Cache:通过
enableCompileCache() 将 ~16MB bundle 的加载时间从 ~400ms 降到 ~254ms
- --version 快速路径:直接返回版本号,跳过 DI 容器初始化和网络请求
三、原生模块与资源分层
graph LR
subgraph "app.asar.unpacked 目录"
direction TB
subgraph "cli/ 目录"
CLI_DIR["cli/"]
CLI_BIN["bin/codebuddy<br/>入口脚本"]
CLI_DIST["dist/<br/>codebuddy.js / codebuddy-headless.js"]
CLI_WASM["dist/wasm/<br/>tree-sitter.wasm<br/>tree-sitter-bash.wasm"]
CLI_WEBUI["dist/web-ui/<br/>docs/ (200+ 篇)"]
CLI_VENDOR["vendor/<br/>sandbox / ripgrep / genie-trash"]
CLI_PKG["package.json<br/>@genie/agent-cli"]
end
subgraph "node_modules/ 目录"
NODE_DIR["node_modules/"]
BETTER["better-sqlite3<br/>v12.8.0<br/>SQLite 数据库绑定"]
NODE_PTY["node-pty<br/>v1.1.0<br/>Microsoft 伪终端"]
LYDELL["@lydell/<br/>node-pty-darwin-arm64<br/>v1.2.0-beta.12"]
TENCENT["@tencent/<br/>docs-engine<br/>v0.0.1-beta.9"]
NJ["nunjucks<br/>+ fsevents<br/>模板引擎"]
end
subgraph "resources/ 目录"
RES_DIR["resources/"]
PLUGINS["builtin-plugins/<br/>weixinpay (MCP)"]
SKILLS["builtin-skills/<br/>15 个技能"]
MCP_APPS["builtin-mcp-apps/<br/>Ardot / Agently"]
TEMPLATES["templates/<br/>8 模板 + 7 风格"]
DEVT["devtools-terminal/<br/>Ghostty WASM"]
BRAND["channel-branding/<br/>OEM 品牌"]
PRELOADS["*-preload.js<br/>IPC 预加载脚本"]
end
end
CLI_DIR --> CLI_BIN
CLI_DIR --> CLI_DIST
CLI_DIR --> CLI_WASM
CLI_DIR --> CLI_WEBUI
CLI_DIR --> CLI_VENDOR
CLI_DIR --> CLI_PKG
NODE_DIR --> BETTER
NODE_DIR --> NODE_PTY
NODE_DIR --> LYDELL
NODE_DIR --> TENCENT
NODE_DIR --> NJ
RES_DIR --> PLUGINS
RES_DIR --> SKILLS
RES_DIR --> MCP_APPS
RES_DIR --> TEMPLATES
RES_DIR --> DEVT
RES_DIR --> BRAND
RES_DIR --> PRELOADS
style BETTER fill:#ffebee
style NODE_PTY fill:#ffebee
style LYDELL fill:#ffebee
style TENCENT fill:#ffebee
style PLUGINS fill:#e8f5e9
style SKILLS fill:#e8f5e9
style MCP_APPS fill:#e8f5e9
为什么需要 app.asar.unpacked?