# 部署

> 大多数用户**直接用 Homebrew 一行装完**（见 [README 快速开始](README.md#快速开始)）。本文档是**手动 / 高级 / 调试**视角——给想了解机制、定制部署流程、或走源码安装的人。

## 源代码 → 运行时映射

| 组件 | 源代码目录 | 运行时目录 | 同步方式 |
|------|-----------|-----------|----------|
| 安装包快照 | 仓库根目录的核心文件 | `~/.forge-hub/package/` | `forge-hub install` 先复制一个运行时 snapshot |
| Hub Server | `hub-server/` | `~/.forge-hub/` | cp 源文件 + `bun install` |
| Channel plugins | `hub-server/channels/` | `~/.forge-hub/channels/` | cp |
| Hub Client | `hub-client/` | `~/.claude/channels/hub/` | cp + `bun install` |
| Dashboard | `hub-dashboard/` | `~/.forge-hub/hub-dashboard/` | cp + `bun install` + `bun run build` |
| forge-hub CLI | `forge-cli/forge.ts` | `~/bin/fh` | symlink 到 staging package 里的 `forge-cli/forge.ts` |
| API token | `HUB_API_TOKEN` env 或手写 | `~/.forge-hub/api-token` | install 时可同步写入（chmod 600） |
| 可发送文件沙箱 | —— | `~/.forge-hub/sendable/` | install 创建；本地 `/send-file` 只允许发这里的文件 |
| Forge Engine | `forge-engine/` | `~/.forge-hub/engine-data/` | **默认不部署**（experimental，需手动配置） |
| Native Client | `hub-app/` | 编译产物 `Forge Hub.app` | `./build.sh` 编译（preview，不在 install 里） |
| Test harness | `hub-test-harness/` | —— | 不部署（开发期跑 `fh hub self-test` 时 spawn） |
| launchd plist | —— | `~/Library/LaunchAgents/com.forge-hub.plist` | `forge-hub install` 自动写 / 或手动 |
| MCP 注册 | —— | `~/.claude.json` 的 `mcpServers.hub` | `forge-hub install` 自动注册 / 或手动 |

**重要**：开发时源码目录是真理来源。`forge-hub install` 会先把源码复制到 `~/.forge-hub/package/` 作为本次安装的 snapshot，再从 snapshot 部署运行时；不要从 `~/.forge-hub/` 反向回推到源码。

> [!NOTE]
> **同步源代码不会覆盖运行时数据。** 运行时目录里的数据文件（`audit.jsonl`、`hub-config.json`、`state/`、`evidence/`、`engine-data/` 等）和源代码文件（`.ts`）的文件名没有重叠。`forge-hub sync` 或手动 `cp` 只复制 `.ts` 文件，不碰运行时数据。详见 [运行时状态.md](运行时状态.md)。

## Homebrew 部署（Mac 最快）

```bash
brew tap LinekForge/forge-hub https://github.com/LinekForge/forge-hub
brew install forge-hub
forge-hub install   # Homebrew sandbox 跑不了部署脚本，装完手动跑一次

# 验证
forge-hub doctor
fh hub status
```

`brew` 自动装 bun 依赖，不需要 PATH 折腾。

> [!NOTE]
> `forge-hub install` 只部署 Hub 主体，不会顺手部署 Forge Engine。Engine 目前保持 experimental / manual setup，见 [forge-engine/README.md](forge-engine/README.md)。

## 源码一键部署（开发 / customize / 非 Mac）

```bash
# 前置：装 Bun（https://bun.sh）——确保 `which bun` 有输出

git clone https://github.com/LinekForge/forge-hub.git ~/forge-hub
cd ~/forge-hub
bun cli.ts install

# 验证
bun cli.ts doctor
fh hub status
```

`install` 会做：
1. 生成安装包快照 `~/.forge-hub/package/`
2. 复制 hub-server / channel plugins / hub-client / hub-dashboard 到运行时目录
3. `bun install` 装依赖，并构建 `~/.forge-hub/hub-dashboard/dist`
4. 写 `~/Library/LaunchAgents/com.forge-hub.plist` + `launchctl bootstrap`（macOS only）
5. 注册 hub MCP 到 `~/.claude.json`
6. 同步 `HUB_API_TOKEN` 到 `~/.forge-hub/api-token`（如果 env 里设置了）
7. 创建 `~/.forge-hub/sendable/`
8. symlink `~/bin/fh`

> **不发 npm**——forge-hub 的目标用户是 Claude Code 使用者，他们通过 agent（让 CC 帮装）访问项目。

## 手动部署（不用 forge-hub install）

如果想精确控制每步，或在 Linux 上自己用 systemd / supervisor / pm2 保活：

```bash
# 1. 复制源文件到运行时
mkdir -p ~/.forge-hub/channels
cp hub-server/*.ts hub-server/*.json ~/.forge-hub/
cp hub-server/channels/*.ts ~/.forge-hub/channels/

mkdir -p ~/.claude/channels/hub
cp hub-client/*.ts hub-client/*.json hub-client/.mcp.json ~/.claude/channels/hub/

# 2. 装依赖
(cd ~/.forge-hub && bun install)
(cd ~/.claude/channels/hub && bun install)

# 2b. 可选：部署 Dashboard 静态页
rm -rf ~/.forge-hub/hub-dashboard
cp -R hub-dashboard ~/.forge-hub/hub-dashboard
(cd ~/.forge-hub/hub-dashboard && bun install && bun run build)
mkdir -p ~/.forge-hub/sendable

# 3. CLI symlink
mkdir -p ~/bin
ln -sf "$(pwd)/forge-cli/forge.ts" ~/bin/fh

# 4. 起 Hub Server（任选其一）
# 4a. macOS launchd（推荐保活）
# 注意：launchd 不展开 `~` / `$HOME`，必须在写 plist 时由 shell 先展开成绝对路径。
# 下面 heredoc 用 unquoted `<<PLIST`（不是 `<<'PLIST'`）让 ${HOME} 展开。
cat > ~/Library/LaunchAgents/com.forge-hub.plist <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.forge-hub</string>
  <key>ProgramArguments</key>
  <array><string>/opt/homebrew/bin/bun</string><string>${HOME}/.forge-hub/hub.ts</string></array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>${HOME}/.forge-hub/hub.log</string>
  <key>StandardErrorPath</key><string>${HOME}/.forge-hub/hub-stderr.log</string>
</dict>
</plist>
PLIST
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.forge-hub.plist

# 4b. 前台跑（开发用）
bun ~/.forge-hub/hub.ts

# 5. 注册 MCP（手动编辑 ~/.claude.json，<you> 替换成 `whoami` 输出）
# 加：
#   "mcpServers": {
#     "hub": {
#       "command": "bun",
#       "args": ["/Users/<you>/.claude/channels/hub/hub-channel.ts"]
#     }
#   }
```

## 升级

```bash
cd ~/forge-hub
git pull
bun cli.ts install   # 重新部署最新版
```

升级**不会清** `~/.forge-hub/state/`（allowlist / pending / 历史）。

## 卸载

```bash
forge-hub uninstall
# 等价于：
# - 关 launchd plist + rm
# - rm 运行时目录（保留 state/）
# - 取消 ~/.claude.json 的 hub 注册
```

要彻底清（含 state）：
```bash
forge-hub uninstall
rm -rf ~/.forge-hub
```

## 验证 / Troubleshoot

```bash
# 体检
forge-hub doctor

# Hub 运行健康
fh hub status

# 已加载通道
fh hub channels

# 在线实例
fh hub peers

# Hub 日志
tail -f ~/.forge-hub/hub.log ~/.forge-hub/hub-stderr.log

# 运行时 self-test（独立 binary，不碰生产 Hub）
fh hub self-test
```

## 常见问题

- **`bun: command not found`**：`brew install oven-sh/bun/bun` 或见 https://bun.sh
- **`launchctl bootstrap` 报 "service already loaded"**：先 `launchctl bootout gui/$(id -u)/com.forge-hub` 再重试
- **Hub 起来但通道全 fail**：看该通道的 config 文件（格式因通道而异，详见 [配置.md](配置.md)——wechat 是 `account.json`，telegram 是 `config.json`，feishu 走 `lark-cli` keychain，imessage 无 config）
- **MCP 没注册成功**：`cat ~/.claude.json | grep -A 5 hub` 检查；手动编辑加上面那段 mcpServers.hub
- **`ffmpeg: command not found` / `lark-cli: command not found`**：launchd 默认 PATH 不含 `/opt/homebrew/bin`——plist 的 `EnvironmentVariables` 里必须显式设 `PATH` 或 `FORGE_FFMPEG_PATH` / `FORGE_LARK_CLI`，详见 [配置.md §加 env 到 Hub 进程](配置.md#加-env-到-hub-进程)
- **Intel Mac**：`/opt/homebrew/` 路径不存在，ffmpeg / lark-cli 在 `/usr/local/bin/`——`FORGE_FFMPEG_PATH` / `FORGE_LARK_CLI` 指向 Intel 路径即可
