---
name: swing-wechat-env-config
description: 微信公众号工作流的自动环境与草稿推送条件预检。由 swing-wechat-workflow 完成极速/高质量模式选择后自动运行；首次需要生图时按用户选择检查 swing-wechat 内置生图或当前 Agent ImgGen，仅为内置生图按需补装缺失的 Baoyu Skill，同时检查 Python/Node、图片预览展示、附件读取、微信公众号凭据、多账号、IP 白名单和草稿连接方式；凭据缺失时调用 swing-wechat-secret-config 安全配置。不得在聊天中索取或输出密钥。
---

# 微信公众号发布前环境检查（Preflight）

## 概述

本 Skill 是 swing-wechat-workflow 的**自动前置门控**。选择生成模式后检查全部能力，并只在进入对应动作前阻止缺少的必要条件；缺少发布凭据可以继续生成和展示完整成品，但用户确认推送草稿后、执行草稿创建前必须补齐。

## 网关职责

`swing-wechat-workflow` 先根据用户输入调用品牌画像路由器，再调用本 Skill。品牌画像和内容规则可以随 Skill 下载；账号密钥和本地账号状态只能存在用户机器上。

- 线上或可下载文件：品牌画像、写作规则、视觉规则、非敏感素材路径；
- 本地文件：AppID、AppSecret、access token、IP 白名单和账号配置状态；
- 本 Skill 不从 Skill 目录读取密钥，也不把密钥写入 Skill 包、文章产物或日志。

## 检查清单

### 1. Skill 依赖检查

| Skill | 路径 | 用途 |
|-------|------|------|
| swing-wechat-secret-config | `~/.agents/skills/swing-wechat-secret-config/` | 安全配置默认账号凭据 |
| swing-wechat-markdown | `~/.agents/skills/swing-wechat-markdown/` | 内容生成 |
| swing-wechat-html | `~/.agents/skills/swing-wechat-html/` | 排版格式化 |
| swing-wechat-layout | `~/.agents/skills/swing-wechat-layout/` | 品牌固定结构模板 |
| swing-wechat-publish | `~/.agents/skills/swing-wechat-publish/` | 发布到公众号 |
| swing-wechat-workflow | `~/.agents/skills/swing-wechat-workflow/` | 总编排 |
| swing-wechat 内置生图 | `~/.agents/skills/baoyu-{name}/` | 用户选择后按当前视觉能力安装和调用 |
| 当前 Agent ImgGen | 由宿主 Agent 决定 | 用户选择后读取系统级生图 Skill 或工具规范 |
| brand profiles | `swing-wechat-workflow/profiles/brand_profiles.json` | 公众号品牌和内容路由规则，不含密钥 |

**检查方式**：逐一验证目录是否存在，SKILL.md 是否可读。

新安装的 Skill 使用 `~/.agents/skills/{skill-name}/` 扁平目录。检查时同时兼容 `$CODEX_HOME/skills/{skill-name}/`、项目级 `.agents/skills/` 和旧的 `baoyu/{skill-name}/` 分组目录。

品牌画像统一随 `swing-wechat-workflow` 安装在 `profiles/`，任何线上索引或下载清单都不得包含 `~/.wechat/`、`.env`、`config.json`、access token 或 AppSecret。

#### 生图方式与内置视觉依赖 profile

工作流首次需要生成图片时必须先取得 `image_generation_provider`：

- `swing-wechat`：用户界面名称为“swing-wechat 内置生图”，内部使用下表所列 Baoyu Skill。
- `agent-imggen`：用户界面名称为“当前 Agent ImgGen”，使用宿主 AI 工具自身的系统级图片生成能力。

选择前只检查两条链路能否被发现，不安装依赖。选择 `agent-imggen` 时不得检查或安装下表中的 Baoyu 视觉 Skill；选择 `swing-wechat` 后才计算和补装最小依赖集。

按文章当前需要的视觉能力选择最小依赖集，不要默认安装整个 `baoyu-skills` 仓库：

| 能力 | 安装的 Skill | 触发条件 |
| --- | --- | --- |
| `cover` | `baoyu-cover-image` | 需要封面方案或封面图 |
| `scene` | `baoyu-article-illustrator` | 需要章节引言图或场景插图 |
| `infographic` | `baoyu-infographic` | 正文包含数据、对比或知识信息图 |
| `diagram` | `baoyu-diagram` | 正文包含流程、架构、关系或时间线 |
| `image-gen` | `baoyu-image-gen` | 使用 Baoyu 图像生成后端 |
| `layout` | `baoyu-markdown-to-html` | 用户明确选择 Baoyu HTML 主题 |

当前品牌 HTML 已由 `swing-wechat-html` 负责，因此 `baoyu-markdown-to-html` 不属于默认安装项；只有用户选择 Baoyu 排版主题时，才安装 `wechat-layout` profile。

缺失时由 Agent 把本次需要的 Skill 名直接传给 GitHub 安装命令：

```bash
npx skills add JimLiu/baoyu-skills \
  --skill baoyu-cover-image \
  --skill baoyu-article-illustrator \
  --global --agent codex --yes --copy
```

命令中只能包含本次缺失的 Skill，不得使用 `--all` 或 `--skill '*'`。已安装的 Skill 不重复传入。安装完成后必须复检；新安装 Skill 会在下一次 Codex 任务中进入技能索引，当前任务需要继续时可直接读取其 `SKILL.md` 并遵循其中流程。

**缺失处理**：
- swing-wechat-* 缺失 → 告知用户需要安装，提供安装命令
- 用户选择 swing-wechat 内置生图且当前视觉能力缺失 → 将能力映射成具体 Skill 名，直接运行 `npx skills add JimLiu/baoyu-skills --skill <name> ...`，只补装缺失技能
- 用户选择当前 Agent ImgGen 但系统级生图不可用 → 说明不可用原因并让用户修复或改选 swing-wechat 内置生图，不得自动切换
- Baoyu 排版能力缺失 → 仅当用户选用 Baoyu HTML 排版时补装 `baoyu-markdown-to-html`
- 如果 Baoyu 依赖无法安装，保留 `swing-wechat-html` 作为品牌排版 fallback；视觉阶段仍需检查当前是否有可用图片生成或用户提供的图片

### 2. 运行环境检查

| 依赖 | 检查方式 | 最低版本 |
|------|---------|---------|
| Python | `python --version` | 3.10+ |
| Node.js | `node --version` | 18+（baoyu-markdown-to-html 需要） |
| 所选生图方式 + 图片展示 | 检查内置生图依赖或当前 Agent ImgGen，且可在当前对话展示生成结果 | 所选链路可用 |
| tsx（可选） | `npx tsx --version` | baoyu-markdown-to-html 需要 |

**缺失处理**：
- Python 缺失 → 引导安装
- Node.js 缺失 → 告知 baoyu-markdown-to-html 不可用，可使用 swing-wechat-html 的 Python 排版作为替代
- 所选生图方式或展示不可用 → 告知无法进行“看图选方案”的门控；等待用户修复或明确切换生图方式，不得静默切换，也不得用提示词文本替代缩略图候选

### 3. 微信公众号多账号配置

#### 3.1 配置目录结构

```
~/.wechat/
├── .env                      # 推荐：默认账号凭据
├── config.json               # 默认账号（旧配置兼容）
├── accounts/                  # 多账号目录
│   ├── tiny-wings/
│   │   └── credentials.env    # 本地隐藏输入生成，禁止上传
│   ├── sanhe-xu/
│   │   └── credentials.env    # 本地隐藏输入生成，禁止上传
│   └── {account-id}/
│       └── credentials.env
├── ssh/                       # SSH 隧道配置
│   └── tunnel.json            # 隧道连接信息
└── cdp/                       # Chrome CDP 配置
    └── browser.json           # 浏览器连接信息
```

#### 3.2 本地账号凭据配置

默认使用账号目录中的 `credentials.env`。旧版用户级 `~/.wechat/.env` 和 JSON 配置仍可兼容读取，但新配置不再把多个账号混写在同一个文件中。

```dotenv
WECHAT_APP_ID=wxXXXXXXXXXXXXXXXX
WECHAT_APP_SECRET=your_app_secret
```

JSON 仅用于兼容旧项目：

```json
{
  "name": "抟微科技Tiny Wings",
  "app_id": "wx5c2d2e4f52d2bc28",
  "app_secret": "your_app_secret",
  "ip_whitelist": ["111.19.6.239"],
  "is_default": true
}
```

#### 3.3 多账号配置文件格式

每个账号一个独立 JSON 文件，文件名即账号标识：

`~/.wechat/accounts/tiny-wings.json`:
```json
{
  "name": "抟微科技Tiny Wings",
  "app_id": "wxXXXXXXXXXXXXXXXX",
  "app_secret": "your_app_secret",
  "ip_whitelist": ["IP1", "IP2"]
}
```

`~/.wechat/accounts/other-account.json`:
```json
{
  "name": "其他公众号",
  "app_id": "wxYYYYYYYYYYYYYYYY",
  "app_secret": "other_app_secret",
  "ip_whitelist": ["IP3"]
}
```

#### 3.4 检查逻辑

1. 先检查进程环境变量和项目/用户级 `.env`
2. 再检查 `~/.wechat/config.json`（旧版单账号配置）
3. 再检查 `~/.wechat/accounts/` 目录（由上层工作流选择账号）
4. 读取用户本地账号目录的状态，不读取或回显密钥值
5. 如果目标账号凭据不存在且当前目标需要发布 → 调用 `swing-wechat-secret-config --account {account-id}`，不得在聊天中收集密钥
6. 统计可用账号数量，呈现给用户选择

#### 3.5 缺失凭据处理

1. 运行 `swing-wechat-secret-config/scripts/configure_wechat_credentials.py --account {account-id} --status`，只读取状态，不输出值。
2. 状态未就绪时，让用户通过本地交互终端运行配置脚本。AppSecret 必须隐藏输入两次，并在用户确认后写入 `~/.wechat/accounts/{account-id}/credentials.env`。
3. Agent 命令工具没有交互式 TTY 时，暂停门控并让用户在自己的终端运行；不得退回到聊天输入。
4. 用户回复“配置完成”后重新运行 `--status`，成功后再测试 access token 和 IP 白名单。
5. 用户已在聊天中发送 AppSecret 时，不得保存或继续使用，提醒用户立即重置。

### 4. 发布模式配置

#### 4.1 三种发布模式

| 模式 | 说明 | 适用场景 | 配置要求 |
|------|------|---------|---------|
| **API 直连** | 直接调用微信公众号 API | 固定 IP 环境（办公室） | AppID + AppSecret + IP 白名单 |
| **Remote SSH 隧道** | 通过 SSH 隧道转发 API 请求 | 多 IP 环境（家里+办公室） | 云服务器 + SSH 密钥 |
| **Chrome CDP** | 浏览器自动化发布 | 无 API 权限或需扫码登录 | Chrome 浏览器 + 管理员扫码 |

#### 4.2 Remote SSH 隧道配置

`~/.wechat/ssh/tunnel.json`:
```json
{
  "enabled": false,
  "ssh_host": "your-cloud-server-ip",
  "ssh_port": 22,
  "ssh_user": "username",
  "ssh_key_path": "~/.ssh/id_rsa",
  "remote_bind_host": "127.0.0.1",
  "remote_bind_port": 8080,
  "local_bind_port": 8080,
  "wechat_api_proxy": "http://127.0.0.1:8080"
}
```

**工作原理**：
1. 云服务器 IP 已加入微信白名单
2. SSH 隧道将本地请求转发到云服务器
3. API 请求通过 `wechat_api_proxy` 地址发出，经隧道到达云服务器
4. 云服务器以白名单 IP 向微信 API 发起请求

**配置引导**：
- 如果用户需要多 IP 发布 → 引导配置 SSH 隧道
- 需要用户提供：云服务器 IP、SSH 用户名、SSH 密钥路径
- 配置完成后自动测试连接

#### 4.3 Chrome CDP 配置

`~/.wechat/cdp/browser.json`:
```json
{
  "enabled": false,
  "browser_path": "C:/Program Files/Google/Chrome/Application/chrome.exe",
  "debug_port": 9222,
  "user_data_dir": "~/.wechat/cdp/chrome-profile",
  "login_url": "https://mp.weixin.qq.com/"
}
```

**注意**：Chrome CDP 模式需要管理员扫码登录，适合管理员本人使用，不适合市场部运维人员。

### 5. 引导流程

#### 5.0 网关分发流程

```
用户输入
  → 识别内容所属产品线
  → 路由到 tiny-wings 或 sanhe-xu 品牌画像
  → 展示推断依据并要求用户确认目标公众号
  → 识别该公众号的内容方向
  → 进入统一的文章、视觉、排版和草稿流程
```

目标公众号不明确时必须暂停询问；不能先生成内容再猜测账号。

#### 5.1 首次使用引导

```
Step 1: 检查 Skill 依赖
  → 列出已安装/缺失的 Skill
  → 缺失的提供安装引导

Step 1.5: 检查品牌画像和本地账号
  → 读取可下载的 brand_profiles.json
  → 扫描 ~/.wechat/accounts/ 的非敏感状态
  → 首次使用时分别引导配置目标公众号密钥

Step 2: 检查运行环境
  → Python / Node / 两种生图链路的可发现状态 / 图片预览展示
  → 缺失的提供安装引导

Step 3: 微信公众号配置
  → 自动扫描进程环境变量、项目/用户 .env、旧 JSON 和多账号目录
  → 如果未找到且需要发布：调用 swing-wechat-secret-config
  → 在本地终端隐藏输入 AppSecret；用户确认后保存到 ~/.wechat/.env
  → Agent 不在对话中索取、回显或转存 AppSecret
  → 如果只有一个账号：自动选择
  → 如果有多个账号且无法从上下文判断：仅询问账号名称
  → 引导配置 IP 白名单

Step 4: 发布模式选择
  → 只有一种可用模式时自动选择
  → 多种模式均可用且上下文无法判断时再询问
     - API 直连（推荐，固定 IP）
     - Remote SSH（多 IP 环境）
     - Chrome CDP（需扫码，管理员专用）
  → 如果选 SSH：引导配置隧道
  → 如果选 CDP：引导配置浏览器

Step 5: 验证
  → 测试 access_token 获取
  → 确认 IP 白名单有效
  → 输出就绪报告

首次真正需要生成图片时：
  → workflow 询问“swing-wechat 内置生图 / 当前 Agent ImgGen”
  → 本 Skill 只验证并准备用户选中的链路
  → 选择内置生图时按需安装最小 Baoyu Skill 集
  → 选择 Agent ImgGen 时读取宿主系统级生图规范，不安装 Baoyu 视觉 Skill
```

#### 5.2 后续使用引导

```
Step 1: 快速检查（5秒内完成）
  → 扫描所有依赖是否仍然安装
  → 读取配置文件

Step 2: 账号选择
  → 如果只有 1 个账号 → 自动使用
  → 如果有多个账号 → 让用户选择
  → 询问：本次使用哪个账号？

Step 3: 发布模式选择
  → 如果只有 1 种模式 → 自动使用
  → 如果有多种模式 → 让用户选择
  → 询问：本次使用哪种发布模式？

Step 4: 输出就绪报告
  → 当前账号 / 发布模式 / 依赖状态
  → 所需条件就绪时自动返回 workflow；发现阻塞项时才暂停
```

### 6. 就绪报告格式

```markdown
## 环境检查报告

### 依赖状态
| 项目 | 状态 | 版本 |
|------|------|------|
| swing-wechat-markdown | ✅ 已安装 | - |
| swing-wechat-secret-config | ✅ 已安装 | - |
| swing-wechat-html | ✅ 已安装 | - |
| swing-wechat-layout | ✅ 已安装 | - |
| swing-wechat-publish | ✅ 已安装 | - |
| 生图方式 | ✅ 已选择 | swing-wechat 内置生图 / 当前 Agent ImgGen |
| 所选生图能力 | ✅ 可用 | 具体能力或宿主工具 |
| Python | ✅ 可用 | 3.13.12 |
| Node.js | ✅ 可用 | 22.22.2 |
| ImageGen | ✅ 可用 | 内置 |

### 公众号账号
- 当前选择：抟微科技Tiny Wings
- AppID：wx5c************bc28
- IP 白名单：111.19.6.239

### 品牌画像
- 当前画像：tiny-wings
- 内容方向：技术干货 / 行业洞察
- 视觉方案：blueprint + cool

### 发布模式
- 当前选择：API 直连
- access_token：✅ 获取成功

### 结论
✅ 所有依赖就绪，可以开始执行工作流。
```

### 7. 配置迁移

如果用户只有 `~/.wechat/config.json`（旧版单账号），可调用 `swing-wechat-secret-config` 的 `--migrate-legacy --account {account-id}`。迁移脚本在本地读取旧值，不显示密钥，用户确认后写入对应账号的 `credentials.env`。验证成功前不删除旧文件；删除必须获得用户明确授权。

### 8. 与 workflow 的集成

在 `swing-wechat-workflow` v3 中，Phase 0 调用本 Skill：

```
Phase 0: 环境与配置准备
  → 调用 swing-wechat-env-config
  → 执行检查清单
  → 凭据缺失 → 调用 swing-wechat-secret-config 安全配置并重新检查
  → 其他缺失 → 引导补齐
  → 用户选择账号和发布模式
  → 🔲 门控0：用户确认就绪
  → 进入 Phase 1
```

## 使用方式

### 自动触发

当用户执行 `swing-wechat-workflow` 时，Phase 0 自动调用本 Skill。

### 手动触发

> "检查微信公众号发布环境"
> "帮我配置多账号发布"
> "配置 Remote SSH 隧道"

## 注意事项

- AppSecret 是敏感信息，配置文件不要提交到 git
- AppSecret 不得粘贴到聊天；本地隐藏输入也不能消除已经发送到聊天中的历史记录
- SSH 密钥路径需确认存在且有读取权限
- Chrome CDP 模式不适合自动化场景（需扫码）
- 多账号配置时，每个账号的 IP 白名单独立管理
