Parcourir la source

更名:对外接口与网关 qiwe 改为 qiwei(工具名/路径/环境变量,旧 QIWE_* 环境变量保留兼容)

gangvy il y a 2 mois
Parent
commit
d091f5ed31
30 fichiers modifiés avec 406 ajouts et 398 suppressions
  1. 4 4
      claude-code/claude-code-qiwe-assistant/.env.example
  2. 1 1
      claude-code/claude-code-qiwe-assistant/.mcp.json
  3. 43 43
      claude-code/claude-code-qiwe-assistant/README.md
  4. 1 1
      claude-code/claude-code-qiwe-assistant/bin/qiwei-official-cli.js
  5. 6 6
      claude-code/claude-code-qiwe-assistant/docs/OUTPUT-STANDARD.md
  6. 10 10
      claude-code/claude-code-qiwe-assistant/docs/specs/qiwei-subscription-flow.md
  7. 3 3
      claude-code/claude-code-qiwe-assistant/mcp/catalog/qiwei-endpoints.json
  8. 1 1
      claude-code/claude-code-qiwe-assistant/mcp/src/core/api-catalog.js
  9. 27 19
      claude-code/claude-code-qiwe-assistant/mcp/src/core/credentials.js
  10. 4 4
      claude-code/claude-code-qiwe-assistant/mcp/src/core/login-flow-server.js
  11. 1 1
      claude-code/claude-code-qiwe-assistant/mcp/src/core/output-paths.js
  12. 6 6
      claude-code/claude-code-qiwe-assistant/mcp/src/core/subscribe-page.js
  13. 6 6
      claude-code/claude-code-qiwe-assistant/mcp/src/core/wecom-cli-runtime.js
  14. 9 9
      claude-code/claude-code-qiwe-assistant/mcp/src/providers/wecom-official-cli.js
  15. 45 45
      claude-code/claude-code-qiwe-assistant/mcp/src/server.js
  16. 34 34
      claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-api-catalog-run.js
  17. 58 58
      claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-login-run.js
  18. 19 19
      claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-subscription-run.js
  19. 8 8
      claude-code/claude-code-qiwe-assistant/mcp/src/tools/wecom-official-cli-run.js
  20. 2 2
      claude-code/claude-code-qiwe-assistant/package-lock.json
  21. 4 4
      claude-code/claude-code-qiwe-assistant/package.json
  22. 7 7
      claude-code/claude-code-qiwe-assistant/scripts/build-catalog.py
  23. 25 25
      claude-code/claude-code-qiwe-assistant/scripts/smoke-test.js
  24. 5 5
      claude-code/claude-code-qiwe-assistant/scripts/wecom-cli-smoke-test.js
  25. 0 42
      claude-code/claude-code-qiwe-assistant/skills/qiwe-login/SKILL.md
  26. 8 8
      claude-code/claude-code-qiwe-assistant/skills/qiwei-api-catalog/SKILL.md
  27. 13 13
      claude-code/claude-code-qiwe-assistant/skills/qiwei-capability-router/SKILL.md
  28. 42 0
      claude-code/claude-code-qiwe-assistant/skills/qiwei-login/SKILL.md
  29. 7 7
      claude-code/claude-code-qiwe-assistant/skills/qiwei-official-doc/SKILL.md
  30. 7 7
      claude-code/claude-code-qiwe-assistant/skills/qiwei-official-meeting/SKILL.md

+ 4 - 4
claude-code/claude-code-qiwe-assistant/.env.example

@@ -1,17 +1,17 @@
 # Fmode 网关转发的企业微信接口
 # Fmode 网关转发的企业微信接口
-QIWE_API_BASE=https://server.fmode.cn/api/qiwe
+QIWEI_API_BASE=https://server.fmode.cn/api/qiwei
 
 
 # 可选:稳定标识一个企微设备;留空时技能首次使用会自动生成并保存。
 # 可选:稳定标识一个企微设备;留空时技能首次使用会自动生成并保存。
-QIWE_UID=
+QIWEI_UID=
 
 
 # 可选:通常会自动复用 Claude Code 已配置的 Fmode NewAPI sk- token。
 # 可选:通常会自动复用 Claude Code 已配置的 Fmode NewAPI sk- token。
 # 也可以显式配置平台 sessionToken 或 Fmode API token。
 # 也可以显式配置平台 sessionToken 或 Fmode API token。
-QIWE_AUTH_TOKEN=
+QIWEI_AUTH_TOKEN=
 
 
 # 企业微信接口访问凭据和设备上下文仅由 Fmode 网关管理,客户端无需配置。
 # 企业微信接口访问凭据和设备上下文仅由 Fmode 网关管理,客户端无需配置。
 
 
 # 可选:覆盖企业微信官方 CLI 程序缓存目录。
 # 可选:覆盖企业微信官方 CLI 程序缓存目录。
-QIWE_OFFICIAL_CLI_RUNTIME_DIR=
+QIWEI_OFFICIAL_CLI_RUNTIME_DIR=
 
 
 # 可选:覆盖官方 CLI 的本地加密认证目录;默认 ~/.config/wecom。
 # 可选:覆盖官方 CLI 的本地加密认证目录;默认 ~/.config/wecom。
 WECOM_CLI_CONFIG_DIR=
 WECOM_CLI_CONFIG_DIR=

+ 1 - 1
claude-code/claude-code-qiwe-assistant/.mcp.json

@@ -1,6 +1,6 @@
 {
 {
   "mcpServers": {
   "mcpServers": {
-    "qiwe": {
+    "qiwei": {
       "command": "node",
       "command": "node",
       "args": [
       "args": [
         "./mcp/src/server.js"
         "./mcp/src/server.js"

+ 43 - 43
claude-code/claude-code-qiwe-assistant/README.md

@@ -23,10 +23,10 @@ Claude Code / MCP
 ```text
 ```text
 ├── .mcp.json
 ├── .mcp.json
 ├── .env.example
 ├── .env.example
-├── bin/qiwe-official-cli.js
+├── bin/qiwei-official-cli.js
 ├── wecom-cli-runtime.json
 ├── wecom-cli-runtime.json
 ├── mcp/
 ├── mcp/
-│   ├── catalog/qiwe-endpoints.json
+│   ├── catalog/qiwei-endpoints.json
 │   └── src/
 │   └── src/
 │       ├── server.js
 │       ├── server.js
 │       ├── core/api-catalog.js
 │       ├── core/api-catalog.js
@@ -35,16 +35,16 @@ Claude Code / MCP
 │       ├── providers/fmode-wecom-gateway.js
 │       ├── providers/fmode-wecom-gateway.js
 │       ├── providers/wecom-official-cli.js
 │       ├── providers/wecom-official-cli.js
 │       └── tools/
 │       └── tools/
-│           ├── qiwe-api-catalog-run.js
-│           ├── qiwe-login-run.js
-│           ├── qiwe-subscription-run.js
+│           ├── qiwei-api-catalog-run.js
+│           ├── qiwei-login-run.js
+│           ├── qiwei-subscription-run.js
 │           └── wecom-official-cli-run.js
 │           └── wecom-official-cli-run.js
 ├── skills/
 ├── skills/
-│   ├── qiwe-api-catalog/SKILL.md
-│   ├── qiwe-login/SKILL.md
-│   ├── qiwe-capability-router/SKILL.md
-│   ├── qiwe-official-meeting/SKILL.md
-│   └── qiwe-official-doc/SKILL.md
+│   ├── qiwei-api-catalog/SKILL.md
+│   ├── qiwei-login/SKILL.md
+│   ├── qiwei-capability-router/SKILL.md
+│   ├── qiwei-official-meeting/SKILL.md
+│   └── qiwei-official-doc/SKILL.md
 ├── docs/                             # 文档(specs/ guides/ generated/,规则见 OUTPUT-STANDARD.md)
 ├── docs/                             # 文档(specs/ guides/ generated/,规则见 OUTPUT-STANDARD.md)
 │   └── OUTPUT-STANDARD.md
 │   └── OUTPUT-STANDARD.md
 └── outputs/                          # 运行期生成文件,按类别归档,git 忽略
 └── outputs/                          # 运行期生成文件,按类别归档,git 忽略
@@ -54,8 +54,8 @@ Claude Code / MCP
 
 
 所有生成文件遵循 [docs/OUTPUT-STANDARD.md](docs/OUTPUT-STANDARD.md):
 所有生成文件遵循 [docs/OUTPUT-STANDARD.md](docs/OUTPUT-STANDARD.md):
 
 
-- 运行期产物统一写入 `outputs/<类别>/`(`login`、`api-calls`、`subscription`、`meetings`、`docs`、`messages`、`smoke`、`tmp`),根目录可用 `QIWE_OUTPUTS_DIR` 覆盖;
-- 覆盖型文件用 latest 模式(如 `outputs/login/qiwe-login-qrcode.png`),按次归档用 run 模式(`outputs/<类别>/<YYYY-MM-DD>/<HHmmss>-<slug>/` + `manifest.json`);
+- 运行期产物统一写入 `outputs/<类别>/`(`login`、`api-calls`、`subscription`、`meetings`、`docs`、`messages`、`smoke`、`tmp`),根目录可用 `QIWEI_OUTPUTS_DIR` 覆盖;
+- 覆盖型文件用 latest 模式(如 `outputs/login/qiwei-login-qrcode.png`),按次归档用 run 模式(`outputs/<类别>/<YYYY-MM-DD>/<HHmmss>-<slug>/` + `manifest.json`);
 - 路径一律通过 `mcp/src/core/output-paths.js` 解析;
 - 路径一律通过 `mcp/src/core/output-paths.js` 解析;
 - 校验:`npm run outputs:validate`。
 - 校验:`npm run outputs:validate`。
 
 
@@ -63,20 +63,20 @@ Claude Code / MCP
 
 
 | 工具 | 说明 |
 | 工具 | 说明 |
 |---|---|
 |---|---|
-| `qiwe_api_search` | 检索 100+ 个企业微信接口 |
-| `qiwe_api_doc` | 查看参数、返回字段和调用模板 |
-| `qiwe_api_call` | `POST /api/qiwe/doApi`,传 `{uid, method, params}` |
-| `qiwe_login_status` | `GET /api/qiwe/login/status?uid=` |
-| `qiwe_login_start` | `POST /api/qiwe/login/start` 并保存二维码 |
-| `qiwe_login_check` | `POST /api/qiwe/login/check` |
-| `qiwe_login_verify` | `POST /api/qiwe/login/verify` |
-| `qiwe_subscription_status` | 查询订阅、席位、到期时间和余额 |
-| `qiwe_subscribe` | 开通、续费或增购席位 |
-| `qiwe_subscription_auto_renew` | 设置自动续费 |
-| `qiwe_official_status` | 检查官方 CLI 下载与授权状态 |
-| `qiwe_official_prepare` | 下载并缓存固定版本官方 CLI |
-| `qiwe_official_help` | 读取官方 category/method 帮助 |
-| `qiwe_official_call` | 结构化调用官方通讯录、文档、会议、消息、日程和待办能力 |
+| `qiwei_api_search` | 检索 100+ 个企业微信接口 |
+| `qiwei_api_doc` | 查看参数、返回字段和调用模板 |
+| `qiwei_api_call` | `POST /api/qiwei/doApi`,传 `{uid, method, params}` |
+| `qiwei_login_status` | `GET /api/qiwei/login/status?uid=` |
+| `qiwei_login_start` | `POST /api/qiwei/login/start` 并保存二维码 |
+| `qiwei_login_check` | `POST /api/qiwei/login/check` |
+| `qiwei_login_verify` | `POST /api/qiwei/login/verify` |
+| `qiwei_subscription_status` | 查询订阅、席位、到期时间和余额 |
+| `qiwei_subscribe` | 开通、续费或增购席位 |
+| `qiwei_subscription_auto_renew` | 设置自动续费 |
+| `qiwei_official_status` | 检查官方 CLI 下载与授权状态 |
+| `qiwei_official_prepare` | 下载并缓存固定版本官方 CLI |
+| `qiwei_official_help` | 读取官方 category/method 帮助 |
+| `qiwei_official_call` | 结构化调用官方通讯录、文档、会议、消息、日程和待办能力 |
 
 
 ## 官方 CLI 通道
 ## 官方 CLI 通道
 
 
@@ -84,11 +84,11 @@ Claude Code / MCP
 
 
 运行时版本固定在 `wecom-cli-runtime.json`,默认安装到用户缓存:
 运行时版本固定在 `wecom-cli-runtime.json`,默认安装到用户缓存:
 
 
-- Windows:`%LOCALAPPDATA%\Fmode\qiwe-assistant\wecom-cli\<version>`
-- macOS:`~/Library/Caches/fmode/qiwe-assistant/wecom-cli/<version>`
-- Linux:`~/.cache/fmode/qiwe-assistant/wecom-cli/<version>`
+- Windows:`%LOCALAPPDATA%\Fmode\qiwei-assistant\wecom-cli\<version>`
+- macOS:`~/Library/Caches/fmode/qiwei-assistant/wecom-cli/<version>`
+- Linux:`~/.cache/fmode/qiwei-assistant/wecom-cli/<version>`
 
 
-可以让 Skill 首次使用时调用 `qiwe_official_prepare`,也可以提前下载:
+可以让 Skill 首次使用时调用 `qiwei_official_prepare`,也可以提前下载:
 
 
 ```bash
 ```bash
 npm run wecom:install
 npm run wecom:install
@@ -106,25 +106,25 @@ npm run wecom:init
 npm run wecom:status
 npm run wecom:status
 ```
 ```
 
 
-官方 CLI 的认证默认保存在 `~/.config/wecom`,也可由 `WECOM_CLI_CONFIG_DIR` 覆盖。该认证与 Fmode token、`QIWE_UID` 和个人企微设备登录完全独立。
+官方 CLI 的认证默认保存在 `~/.config/wecom`,也可由 `WECOM_CLI_CONFIG_DIR` 覆盖。该认证与 Fmode token、`QIWEI_UID` 和个人企微设备登录完全独立。
 
 
 ## 鉴权和本地配置
 ## 鉴权和本地配置
 
 
 - 请求头统一为 `Authorization: Bearer <Fmode token>`。
 - 请求头统一为 `Authorization: Bearer <Fmode token>`。
 - 优先自动读取 Claude Code 已配置的 Fmode NewAPI `sk-` token。
 - 优先自动读取 Claude Code 已配置的 Fmode NewAPI `sk-` token。
-- 也支持 `QIWE_AUTH_TOKEN`、`FMODE_API_KEY`、`FMODE_API_TOKEN`、`NEWAPI_TOKEN` 或平台 sessionToken。
-- `QIWE_API_BASE` 默认 `https://server.fmode.cn/api/qiwe`。
-- `QIWE_UID` 是客户端设备别名;未配置时会生成随机稳定 uid,并写入 `.env.local` 和 `~/.claude/qiwe-credentials.json`。
+- 也支持 `QIWEI_AUTH_TOKEN`、`FMODE_API_KEY`、`FMODE_API_TOKEN`、`NEWAPI_TOKEN` 或平台 sessionToken。
+- `QIWEI_API_BASE` 默认 `https://server.fmode.cn/api/qiwei`。
+- `QIWEI_UID` 是客户端设备别名;未配置时会生成随机稳定 uid,并写入 `.env.local` 和 `~/.claude/qiwei-credentials.json`。
 - 企业微信接口访问凭据和设备上下文由 Fmode 网关管理,不会出现在技能配置或返回结果中。
 - 企业微信接口访问凭据和设备上下文由 Fmode 网关管理,不会出现在技能配置或返回结果中。
 
 
 ## 首次使用
 ## 首次使用
 
 
-1. `qiwe_subscription_status` 检查订阅;
-2. 未订阅时调用 `qiwe_subscribe`,`seats` 表示企微账号席位数;
-3. `qiwe_login_start` 生成二维码;
-4. 用户扫码后每 3–5 秒调用 `qiwe_login_check`;
-5. 状态 `10` 时用 `qiwe_login_verify` 提交 6 位验证码;
-6. 状态 `2` 后用 `qiwe_api_call` 调业务接口。
+1. `qiwei_subscription_status` 检查订阅;
+2. 未订阅时调用 `qiwei_subscribe`,`seats` 表示企微账号席位数;
+3. `qiwei_login_start` 生成二维码;
+4. 用户扫码后每 3–5 秒调用 `qiwei_login_check`;
+5. 状态 `10` 时用 `qiwei_login_verify` 提交 6 位验证码;
+6. 状态 `2` 后用 `qiwei_api_call` 调业务接口。
 
 
 业务调用只传清单中的业务参数:
 业务调用只传清单中的业务参数:
 
 
@@ -153,8 +153,8 @@ npm run outputs:validate
 
 
 ## 限制
 ## 限制
 
 
-- `cloud` 模块的 multipart 文件直传尚未由 Fmode 网关支持,`qiwe_api_call` 会拒绝这类接口。
-- `/login/*`、`/client/*` 不允许通过 `qiwe_api_call` 透传,必须使用登录专用工具。
-- 服务端未挂载前,默认生产地址会返回不可用;可通过 `QIWE_API_BASE` 指向测试环境。
+- `cloud` 模块的 multipart 文件直传尚未由 Fmode 网关支持,`qiwei_api_call` 会拒绝这类接口。
+- `/login/*`、`/client/*` 不允许通过 `qiwei_api_call` 透传,必须使用登录专用工具。
+- 服务端未挂载前,默认生产地址会返回不可用;可通过 `QIWEI_API_BASE` 指向测试环境。
 - 官方 CLI 首次下载需要 npm 网络访问,首次业务调用前需要独立完成企业微信机器人扫码授权。
 - 官方 CLI 首次下载需要 npm 网络访问,首次业务调用前需要独立完成企业微信机器人扫码授权。
 - 官方 CLI 通道失败不会替代或改变原有 Fmode 网关接口通道。
 - 官方 CLI 通道失败不会替代或改变原有 Fmode 网关接口通道。

+ 1 - 1
claude-code/claude-code-qiwe-assistant/bin/qiwe-official-cli.js → claude-code/claude-code-qiwe-assistant/bin/qiwei-official-cli.js

@@ -43,6 +43,6 @@ async function main() {
 }
 }
 
 
 main().catch(error => {
 main().catch(error => {
-  console.error(`qiwe-official-cli failed: ${sanitizeCliText(error.message)}`);
+  console.error(`qiwei-official-cli failed: ${sanitizeCliText(error.message)}`);
   process.exit(1);
   process.exit(1);
 });
 });

+ 6 - 6
claude-code/claude-code-qiwe-assistant/docs/OUTPUT-STANDARD.md

@@ -1,11 +1,11 @@
-# 输出与目录标准(claude-code-qiwe-assistant)
+# 输出与目录标准(claude-code-qiwei-assistant)
 
 
 本文件定义技能包所有生成文件的目录规则。任何工具、脚本或 Skill 生成文件时必须遵循本标准,并通过 `mcp/src/core/output-paths.js` 模块解析路径,禁止直接拼 `process.cwd() + 'outputs'`。
 本文件定义技能包所有生成文件的目录规则。任何工具、脚本或 Skill 生成文件时必须遵循本标准,并通过 `mcp/src/core/output-paths.js` 模块解析路径,禁止直接拼 `process.cwd() + 'outputs'`。
 
 
 ## 1. 顶层目录职责
 ## 1. 顶层目录职责
 
 
 ```text
 ```text
-claude-code-qiwe-assistant/
+claude-code-qiwei-assistant/
 ├── bin/          # CLI 入口,不生成文件
 ├── bin/          # CLI 入口,不生成文件
 ├── mcp/          # MCP 服务源码与接口清单(catalog 为构建产物,由 scripts/build-catalog.py 生成)
 ├── mcp/          # MCP 服务源码与接口清单(catalog 为构建产物,由 scripts/build-catalog.py 生成)
 ├── skills/       # SKILL.md 定义,只读
 ├── skills/       # SKILL.md 定义,只读
@@ -16,14 +16,14 @@ claude-code-qiwe-assistant/
 
 
 ## 2. /outputs 生成规则
 ## 2. /outputs 生成规则
 
 
-统一根目录:`<包根>/outputs`,可用环境变量 `QIWE_OUTPUTS_DIR` 覆盖。目录不存在时由 `output-paths.js` 自动创建。
+统一根目录:`<包根>/outputs`,可用环境变量 `QIWEI_OUTPUTS_DIR` 覆盖。目录不存在时由 `output-paths.js` 自动创建。
 
 
 第一层为固定类别,禁止在 outputs 根目录直接放文件:
 第一层为固定类别,禁止在 outputs 根目录直接放文件:
 
 
 | 类别 | 用途 |
 | 类别 | 用途 |
 |---|---|
 |---|---|
 | `login/` | 扫码登录二维码与预览页(保持固定文件名,最新一次覆盖) |
 | `login/` | 扫码登录二维码与预览页(保持固定文件名,最新一次覆盖) |
-| `api-calls/` | `qiwe_api_call` 落盘的请求/响应样本 |
+| `api-calls/` | `qiwei_api_call` 落盘的请求/响应样本 |
 | `subscription/` | 订阅、席位、余额查询快照 |
 | `subscription/` | 订阅、席位、余额查询快照 |
 | `meetings/` | 官方 CLI 会议相关导出 |
 | `meetings/` | 官方 CLI 会议相关导出 |
 | `docs/` | 官方 CLI 文档能力导出 |
 | `docs/` | 官方 CLI 文档能力导出 |
@@ -42,7 +42,7 @@ claude-code-qiwe-assistant/
 ```json
 ```json
 {
 {
   "createdAt": "2026-07-12T09:00:00.000Z",
   "createdAt": "2026-07-12T09:00:00.000Z",
-  "tool": "qiwe_official_call",
+  "tool": "qiwei_official_call",
   "summary": {},
   "summary": {},
   "files": ["meeting-list.json"]
   "files": ["meeting-list.json"]
 }
 }
@@ -64,7 +64,7 @@ claude-code-qiwe-assistant/
 `mcp/src/core/output-paths.js` 暴露:
 `mcp/src/core/output-paths.js` 暴露:
 
 
 ```js
 ```js
-outputsRoot()                       // outputs 根目录(含 QIWE_OUTPUTS_DIR 覆盖)
+outputsRoot()                       // outputs 根目录(含 QIWEI_OUTPUTS_DIR 覆盖)
 categoryDir(category)               // 确保并返回类别目录
 categoryDir(category)               // 确保并返回类别目录
 latestPath(category, filename)      // latest 模式路径
 latestPath(category, filename)      // latest 模式路径
 createRunDir(category, slug, date?) // run 模式目录
 createRunDir(category, slug, date?) // run 模式目录

+ 10 - 10
claude-code/claude-code-qiwe-assistant/docs/specs/qiwe-subscription-flow.md → claude-code/claude-code-qiwe-assistant/docs/specs/qiwei-subscription-flow.md

@@ -2,7 +2,7 @@
 
 
 ## 流程页服务(默认模式)
 ## 流程页服务(默认模式)
 
 
-`qiwe_login_start` 默认在本地端口(`QIWE_FLOW_PORT`,默认 4310)启动
+`qiwei_login_start` 默认在本地端口(`QIWEI_FLOW_PORT`,默认 4310)启动
 流程页服务(`mcp/src/core/login-flow-server.js`)并自动打开浏览器:
 流程页服务(`mcp/src/core/login-flow-server.js`)并自动打开浏览器:
 
 
 - 页面加载时请求 `/flow/state` 自动判断当前所处步骤
 - 页面加载时请求 `/flow/state` 自动判断当前所处步骤
@@ -18,22 +18,22 @@
 ## 流程总览(静态模式)
 ## 流程总览(静态模式)
 
 
 1. **第 1 步:开通订阅(付费)**
 1. **第 1 步:开通订阅(付费)**
-   - `qiwe_login_start` 会先查询 `GET /subscribe/status`;
+   - `qiwei_login_start` 会先查询 `GET /subscribe/status`;
    - 未开通/已到期时,不直接报错,而是生成套餐选择页
    - 未开通/已到期时,不直接报错,而是生成套餐选择页
-     `outputs/subscription/qiwe-subscribe.html` 并自动打开浏览器;
+     `outputs/subscription/qiwei-subscribe.html` 并自动打开浏览器;
    - 页面上选择席位(1/3/10/20/50 个号)与时长(1/6/12 个月),
    - 页面上选择席位(1/3/10/20/50 个号)与时长(1/6/12 个月),
      点击「开通服务」后调用 `POST /subscribe`,按
      点击「开通服务」后调用 `POST /subscribe`,按
      `席位数 × ¥200/月 × 月数` 从飞马余额一次性扣费;
      `席位数 × ¥200/月 × 月数` 从飞马余额一次性扣费;
-   - 开通成功后重新调用 `qiwe_login_start` 进入第 2 步。
+   - 开通成功后重新调用 `qiwei_login_start` 进入第 2 步。
 
 
 2. **第 2 步:扫码登录**
 2. **第 2 步:扫码登录**
-   - `qiwe_login_start` 生成二维码(`outputs/login/qiwe-login-qrcode.png` + 预览页);
-   - 手机企业微信扫码确认后,`qiwe_login_check` 轮询状态;
-   - 状态 10 时用 `qiwe_login_verify` 提交 6 位验证码。
+   - `qiwei_login_start` 生成二维码(`outputs/login/qiwei-login-qrcode.png` + 预览页);
+   - 手机企业微信扫码确认后,`qiwei_login_check` 轮询状态;
+   - 状态 10 时用 `qiwei_login_verify` 提交 6 位验证码。
 
 
 ## 价格
 ## 价格
 
 
-- 单价:每个账号(席位)**¥200/月**(`QIWE_MONTHLY_PRICE`)。
+- 单价:每个账号(席位)**¥200/月**(`QIWEI_MONTHLY_PRICE`)。
 - 页面套餐:1 号 ¥200/月、3 号 ¥600/月、10 号 ¥2000/月(推荐)、
 - 页面套餐:1 号 ¥200/月、3 号 ¥600/月、10 号 ¥2000/月(推荐)、
   20 号 ¥4000/月、50 号 ¥10000/月;时长 1/6/12 个月。
   20 号 ¥4000/月、50 号 ¥10000/月;时长 1/6/12 个月。
 
 
@@ -41,7 +41,7 @@
 
 
 | 错误码 | HTTP | 含义 | 用户提示 |
 | 错误码 | HTTP | 含义 | 用户提示 |
 | --- | --- | --- | --- |
 | --- | --- | --- | --- |
-| QW-AUTH-401 | 401 | 鉴权失败 | token 无效或过期,检查 QIWE_AUTH_TOKEN 后重试 |
+| QW-AUTH-401 | 401 | 鉴权失败 | token 无效或过期,检查 QIWEI_AUTH_TOKEN 后重试 |
 | QW-PAY-402 | 402 | 飞马余额不足 | 余额不足以完成扣费,请先充值飞马余额 |
 | QW-PAY-402 | 402 | 飞马余额不足 | 余额不足以完成扣费,请先充值飞马余额 |
 | QW-SUB-402 | 402 | 订阅未开通或已到期 | 在套餐页选择席位并点击开通 |
 | QW-SUB-402 | 402 | 订阅未开通或已到期 | 在套餐页选择席位并点击开通 |
 | QW-SEAT-403 | 403 | 席位已满 | 增购席位或停用闲置设备 |
 | QW-SEAT-403 | 403 | 席位已满 | 增购席位或停用闲置设备 |
@@ -52,7 +52,7 @@
 
 
 ## MCP 工具侧状态
 ## MCP 工具侧状态
 
 
-- `needs_subscription`:`qiwe_login_start` 检测到未订阅时返回,
+- `needs_subscription`:`qiwei_login_start` 检测到未订阅时返回,
   `summary.subscribePage` 指向套餐页路径;
   `summary.subscribePage` 指向套餐页路径;
 - `needs_seat`:席位不足;
 - `needs_seat`:席位不足;
 - `needs_auth`:鉴权失败;
 - `needs_auth`:鉴权失败;

+ 3 - 3
claude-code/claude-code-qiwe-assistant/mcp/catalog/qiwe-endpoints.json → claude-code/claude-code-qiwe-assistant/mcp/catalog/qiwei-endpoints.json

@@ -3,11 +3,11 @@
  "updatedAt": "2026-07-10",
  "updatedAt": "2026-07-10",
  "source": "企业微信开放接口资料",
  "source": "企业微信开放接口资料",
  "gateway": {
  "gateway": {
-  "baseUrl": "https://server.fmode.cn/api/qiwe",
+  "baseUrl": "https://server.fmode.cn/api/qiwei",
   "endpoint": "POST {baseUrl}/doApi",
   "endpoint": "POST {baseUrl}/doApi",
   "auth": "Authorization: Bearer <Fmode token>",
   "auth": "Authorization: Bearer <Fmode token>",
   "envelope": "Fmode 网关请求信封:{\"uid\": \"<本地稳定设备标识>\", \"method\": \"<接口 method 路径>\", \"params\": {...}};企业微信接口访问凭据和设备上下文由 Fmode 网关管理。",
   "envelope": "Fmode 网关请求信封:{\"uid\": \"<本地稳定设备标识>\", \"method\": \"<接口 method 路径>\", \"params\": {...}};企业微信接口访问凭据和设备上下文由 Fmode 网关管理。",
-  "note": "所有业务请求均通过 Fmode 网关转发的企业微信接口执行。清单未登记的普通 JSON 接口可用 qiwe_api_call 传 rawMethod + params;登录和设备接口必须使用 qiwe_login_* 专用工具。"
+  "note": "所有业务请求均通过 Fmode 网关转发的企业微信接口执行。清单未登记的普通 JSON 接口可用 qiwei_api_call 传 rawMethod + params;登录和设备接口必须使用 qiwei_login_* 专用工具。"
  },
  },
  "modules": [
  "modules": [
   "client",
   "client",
@@ -5163,7 +5163,7 @@
    "id": "cloud.bigFileDownloadAsync",
    "id": "cloud.bigFileDownloadAsync",
    "module": "cloud",
    "module": "cloud",
    "title": "企微大文件异步下载",
    "title": "企微大文件异步下载",
-   "summary": "文件大于 20M 使用此接口;下载地址为临时云资源(7–15 天不定期清理);异步结果通过回调通知,根据 requestId 匹配。注意:官方文档未公布该接口的 method 路由值,qiwe_api_call 无法直接调用,请参考 docUrl 官方文档确认后用 rawMethod 调用。",
+   "summary": "文件大于 20M 使用此接口;下载地址为临时云资源(7–15 天不定期清理);异步结果通过回调通知,根据 requestId 匹配。注意:官方文档未公布该接口的 method 路由值,qiwei_api_call 无法直接调用,请参考 docUrl 官方文档确认后用 rawMethod 调用。",
    "method": null,
    "method": null,
    "category": [
    "category": [
     "API参考",
     "API参考",

+ 1 - 1
claude-code/claude-code-qiwe-assistant/mcp/src/core/api-catalog.js

@@ -1,7 +1,7 @@
 const fs = require('fs');
 const fs = require('fs');
 const path = require('path');
 const path = require('path');
 
 
-const CATALOG_PATH = path.resolve(__dirname, '..', '..', 'catalog', 'qiwe-endpoints.json');
+const CATALOG_PATH = path.resolve(__dirname, '..', '..', 'catalog', 'qiwei-endpoints.json');
 
 
 let cached;
 let cached;
 
 

+ 27 - 19
claude-code/claude-code-qiwe-assistant/mcp/src/core/credentials.js

@@ -3,8 +3,8 @@ const fs = require('fs');
 const path = require('path');
 const path = require('path');
 const os = require('os');
 const os = require('os');
 
 
-const DEFAULT_API_BASE = 'https://server.fmode.cn/api/qiwe';
-const CREDENTIALS_FILE = path.join(os.homedir(), '.claude', 'qiwe-credentials.json');
+const DEFAULT_API_BASE = 'https://server.fmode.cn/api/qiwei';
+const CREDENTIALS_FILE = path.join(os.homedir(), '.claude', 'qiwei-credentials.json');
 
 
 function readJsonMaybe(filePath) {
 function readJsonMaybe(filePath) {
   try {
   try {
@@ -112,7 +112,7 @@ function normalizeToken(value) {
   return String(value || '').trim().replace(/^Bearer\s+/i, '');
   return String(value || '').trim().replace(/^Bearer\s+/i, '');
 }
 }
 
 
-function readQiweAuthToken(input = {}) {
+function readQiweiAuthToken(input = {}) {
   const fileEnv = readEnvFiles();
   const fileEnv = readEnvFiles();
   const claudeEnv = readClaudeSettingsEnv();
   const claudeEnv = readClaudeSettingsEnv();
   const fmodeConfig = readFmodeConfig();
   const fmodeConfig = readFmodeConfig();
@@ -123,12 +123,14 @@ function readQiweAuthToken(input = {}) {
     input.sessionToken,
     input.sessionToken,
     input.apiToken,
     input.apiToken,
     input.token,
     input.token,
+    fileEnv.QIWEI_AUTH_TOKEN,
     fileEnv.QIWE_AUTH_TOKEN,
     fileEnv.QIWE_AUTH_TOKEN,
     fileEnv.FMODE_API_KEY,
     fileEnv.FMODE_API_KEY,
     fileEnv.FMODE_API_TOKEN,
     fileEnv.FMODE_API_TOKEN,
     fileEnv.NEWAPI_TOKEN,
     fileEnv.NEWAPI_TOKEN,
     fileEnv.VOC_TOKEN,
     fileEnv.VOC_TOKEN,
     fileEnv.VOC_SOCIAL_TOKEN,
     fileEnv.VOC_SOCIAL_TOKEN,
+    process.env.QIWEI_AUTH_TOKEN,
     process.env.QIWE_AUTH_TOKEN,
     process.env.QIWE_AUTH_TOKEN,
     process.env.FMODE_API_KEY,
     process.env.FMODE_API_KEY,
     process.env.FMODE_API_TOKEN,
     process.env.FMODE_API_TOKEN,
@@ -151,26 +153,32 @@ function readCredentialsFile() {
   return readJsonMaybe(CREDENTIALS_FILE);
   return readJsonMaybe(CREDENTIALS_FILE);
 }
 }
 
 
-function readQiweUid(input = {}) {
+function readQiweiUid(input = {}) {
   const fileEnv = readEnvFiles();
   const fileEnv = readEnvFiles();
   const creds = readCredentialsFile();
   const creds = readCredentialsFile();
   return firstNonEmpty([
   return firstNonEmpty([
     input.uid,
     input.uid,
-    input.qiweUid,
+    input.qiweiUid,
+    fileEnv.QIWEI_UID,
     fileEnv.QIWE_UID,
     fileEnv.QIWE_UID,
+    process.env.QIWEI_UID,
     process.env.QIWE_UID,
     process.env.QIWE_UID,
     creds.uid
     creds.uid
   ]);
   ]);
 }
 }
 
 
-function readQiweApiBase(input = {}) {
+function readQiweiApiBase(input = {}) {
   const fileEnv = readEnvFiles();
   const fileEnv = readEnvFiles();
   const creds = readCredentialsFile();
   const creds = readCredentialsFile();
   return String(firstNonEmpty([
   return String(firstNonEmpty([
     input.apiBase,
     input.apiBase,
     input.baseUrl,
     input.baseUrl,
+    fileEnv.QIWEI_API_BASE,
+    fileEnv.QIWEI_RELAY_BASE_URL,
     fileEnv.QIWE_API_BASE,
     fileEnv.QIWE_API_BASE,
     fileEnv.QIWE_RELAY_BASE_URL,
     fileEnv.QIWE_RELAY_BASE_URL,
+    process.env.QIWEI_API_BASE,
+    process.env.QIWEI_RELAY_BASE_URL,
     process.env.QIWE_API_BASE,
     process.env.QIWE_API_BASE,
     process.env.QIWE_RELAY_BASE_URL,
     process.env.QIWE_RELAY_BASE_URL,
     creds.apiBase,
     creds.apiBase,
@@ -178,7 +186,7 @@ function readQiweApiBase(input = {}) {
   ]) || DEFAULT_API_BASE).replace(/\/$/, '');
   ]) || DEFAULT_API_BASE).replace(/\/$/, '');
 }
 }
 
 
-function saveQiweClientConfig({ uid, apiBase } = {}) {
+function saveQiweiClientConfig({ uid, apiBase } = {}) {
   const saved = [];
   const saved = [];
   try {
   try {
     const dir = path.dirname(CREDENTIALS_FILE);
     const dir = path.dirname(CREDENTIALS_FILE);
@@ -196,8 +204,8 @@ function saveQiweClientConfig({ uid, apiBase } = {}) {
   try {
   try {
     const envPath = path.join(process.cwd(), '.env.local');
     const envPath = path.join(process.cwd(), '.env.local');
     const pairs = [];
     const pairs = [];
-    if (uid) pairs.push(['QIWE_UID', uid]);
-    if (apiBase) pairs.push(['QIWE_API_BASE', apiBase]);
+    if (uid) pairs.push(['QIWEI_UID', uid]);
+    if (apiBase) pairs.push(['QIWEI_API_BASE', apiBase]);
     if (pairs.length) {
     if (pairs.length) {
       let content = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
       let content = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
       for (const [key, value] of pairs) {
       for (const [key, value] of pairs) {
@@ -216,26 +224,26 @@ function saveQiweClientConfig({ uid, apiBase } = {}) {
   return saved;
   return saved;
 }
 }
 
 
-function ensureQiweUid(input = {}) {
-  const existing = readQiweUid(input);
+function ensureQiweiUid(input = {}) {
+  const existing = readQiweiUid(input);
   if (existing) return existing;
   if (existing) return existing;
-  const uid = `qiwe-${crypto.randomUUID()}`;
-  saveQiweClientConfig({ uid, apiBase: readQiweApiBase(input) });
+  const uid = `qiwei-${crypto.randomUUID()}`;
+  saveQiweiClientConfig({ uid, apiBase: readQiweiApiBase(input) });
   return uid;
   return uid;
 }
 }
 
 
 function isConfigured(input = {}) {
 function isConfigured(input = {}) {
-  return Boolean(readQiweAuthToken(input) && readQiweUid(input));
+  return Boolean(readQiweiAuthToken(input) && readQiweiUid(input));
 }
 }
 
 
 module.exports = {
 module.exports = {
   DEFAULT_API_BASE,
   DEFAULT_API_BASE,
   CREDENTIALS_FILE,
   CREDENTIALS_FILE,
-  readQiweAuthToken,
-  readQiweUid,
-  ensureQiweUid,
-  readQiweApiBase,
-  saveQiweClientConfig,
+  readQiweiAuthToken,
+  readQiweiUid,
+  ensureQiweiUid,
+  readQiweiApiBase,
+  saveQiweiClientConfig,
   isConfigured,
   isConfigured,
   readEnvFiles,
   readEnvFiles,
   readClaudeSettingsEnv,
   readClaudeSettingsEnv,

+ 4 - 4
claude-code/claude-code-qiwe-assistant/mcp/src/core/login-flow-server.js

@@ -1,6 +1,6 @@
 const http = require('http');
 const http = require('http');
 const { callFmodeWecomGateway } = require('../providers/fmode-wecom-gateway');
 const { callFmodeWecomGateway } = require('../providers/fmode-wecom-gateway');
-const { SEAT_PLANS, DURATION_PLANS, QIWE_MONTHLY_PRICE, classifySubscribeError, ERROR_CODES } = require('./subscribe-page');
+const { SEAT_PLANS, DURATION_PLANS, QIWEI_MONTHLY_PRICE, classifySubscribeError, ERROR_CODES } = require('./subscribe-page');
 
 
 const DEFAULT_FLOW_PORT = 4310;
 const DEFAULT_FLOW_PORT = 4310;
 
 
@@ -221,7 +221,7 @@ function renderVerify() {
 function renderSuccess(detail) {
 function renderSuccess(detail) {
   setStep(4);
   setStep(4);
   panel.innerHTML = '<div class="center"><div class="success">✔ 企业微信登录成功</div>' +
   panel.innerHTML = '<div class="center"><div class="success">✔ 企业微信登录成功</div>' +
-    '<div class="hint">账号:' + (detail.nickname || detail.userId || '未知') + (detail.corpName ? '(' + detail.corpName + ')' : '') + '<br>现在可以回到对话中使用企微接口能力(qiwe_api_call、消息、会议等)。</div></div>';
+    '<div class="hint">账号:' + (detail.nickname || detail.userId || '未知') + (detail.corpName ? '(' + detail.corpName + ')' : '') + '<br>现在可以回到对话中使用企微接口能力(qiwei_api_call、消息、会议等)。</div></div>';
 }
 }
 
 
 (async () => {
 (async () => {
@@ -262,7 +262,7 @@ function createFlowHandler(ctx) {
     try {
     try {
       if (url.pathname === '/' || url.pathname === '/index.html') {
       if (url.pathname === '/' || url.pathname === '/index.html') {
         res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
         res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
-        res.end(buildFlowPageHtml({ monthlyPrice: QIWE_MONTHLY_PRICE }));
+        res.end(buildFlowPageHtml({ monthlyPrice: QIWEI_MONTHLY_PRICE }));
         return;
         return;
       }
       }
       if (url.pathname === '/flow/state') {
       if (url.pathname === '/flow/state') {
@@ -354,7 +354,7 @@ function createFlowHandler(ctx) {
 }
 }
 
 
 function startLoginFlowServer({ token, apiBase, uid, port, onQrcode } = {}) {
 function startLoginFlowServer({ token, apiBase, uid, port, onQrcode } = {}) {
-  const listenPort = Number(port || process.env.QIWE_FLOW_PORT) || DEFAULT_FLOW_PORT;
+  const listenPort = Number(port || process.env.QIWEI_FLOW_PORT) || DEFAULT_FLOW_PORT;
   if (currentServer && currentContext) {
   if (currentServer && currentContext) {
     Object.assign(currentContext, { token, apiBase, uid, onQrcode });
     Object.assign(currentContext, { token, apiBase, uid, onQrcode });
     return Promise.resolve({ url: `http://127.0.0.1:${currentContext.port}/`, alreadyRunning: true });
     return Promise.resolve({ url: `http://127.0.0.1:${currentContext.port}/`, alreadyRunning: true });

+ 1 - 1
claude-code/claude-code-qiwe-assistant/mcp/src/core/output-paths.js

@@ -15,7 +15,7 @@ const OUTPUT_CATEGORIES = Object.freeze([
 ]);
 ]);
 
 
 function outputsRoot() {
 function outputsRoot() {
-  const override = process.env.QIWE_OUTPUTS_DIR;
+  const override = process.env.QIWEI_OUTPUTS_DIR;
   if (override && String(override).trim()) return path.resolve(String(override).trim());
   if (override && String(override).trim()) return path.resolve(String(override).trim());
   return path.join(PACKAGE_ROOT, 'outputs');
   return path.join(PACKAGE_ROOT, 'outputs');
 }
 }

+ 6 - 6
claude-code/claude-code-qiwe-assistant/mcp/src/core/subscribe-page.js

@@ -1,7 +1,7 @@
 const fs = require('fs');
 const fs = require('fs');
 const { latestPath } = require('./output-paths');
 const { latestPath } = require('./output-paths');
 
 
-const QIWE_MONTHLY_PRICE = 200;
+const QIWEI_MONTHLY_PRICE = 200;
 
 
 const SEAT_PLANS = [
 const SEAT_PLANS = [
   { seats: 1, label: '1 个号', note: '个人试用' },
   { seats: 1, label: '1 个号', note: '个人试用' },
@@ -18,7 +18,7 @@ const DURATION_PLANS = [
 ];
 ];
 
 
 const ERROR_CODES = {
 const ERROR_CODES = {
-  'QW-AUTH-401': { http: 401, title: '认证失败', tip: '鉴权 token 无效或已过期,请检查 QIWE_AUTH_TOKEN 后重试。' },
+  'QW-AUTH-401': { http: 401, title: '认证失败', tip: '鉴权 token 无效或已过期,请检查 QIWEI_AUTH_TOKEN 后重试。' },
   'QW-PAY-402': { http: 402, title: '飞马余额不足', tip: '账户余额不足以完成本次订阅扣费,请先充值飞马余额。' },
   'QW-PAY-402': { http: 402, title: '飞马余额不足', tip: '账户余额不足以完成本次订阅扣费,请先充值飞马余额。' },
   'QW-SUB-402': { http: 402, title: '订阅未开通或已到期', tip: '请在套餐页面选择席位并点击开通,扣费成功后即可继续登录。' },
   'QW-SUB-402': { http: 402, title: '订阅未开通或已到期', tip: '请在套餐页面选择席位并点击开通,扣费成功后即可继续登录。' },
   'QW-SEAT-403': { http: 403, title: '席位已满', tip: '当前订阅席位已全部占用,请增购席位或停用闲置设备。' },
   'QW-SEAT-403': { http: 403, title: '席位已满', tip: '当前订阅席位已全部占用,请增购席位或停用闲置设备。' },
@@ -34,7 +34,7 @@ function classifySubscribeError(httpStatus, message) {
   return 'QW-UP-502';
   return 'QW-UP-502';
 }
 }
 
 
-function buildSubscribePageHtml({ apiBase, token, monthlyPrice = QIWE_MONTHLY_PRICE }) {
+function buildSubscribePageHtml({ apiBase, token, monthlyPrice = QIWEI_MONTHLY_PRICE }) {
   const plansJson = JSON.stringify(SEAT_PLANS);
   const plansJson = JSON.stringify(SEAT_PLANS);
   const durationsJson = JSON.stringify(DURATION_PLANS);
   const durationsJson = JSON.stringify(DURATION_PLANS);
   const errorsJson = JSON.stringify(ERROR_CODES);
   const errorsJson = JSON.stringify(ERROR_CODES);
@@ -155,7 +155,7 @@ document.getElementById('buy').onclick = async () => {
       }
       }
     }
     }
     const d = (lastData && lastData.data) || {};
     const d = (lastData && lastData.data) || {};
-    showMsg('ok', '<b>开通成功!</b><br>席位:' + (d.seats || seats) + ',到期时间:' + (d.expireAt || '已延长 ' + months + ' 个月') + '。<br>请回到对话中重新执行 qiwe_login_start 继续扫码登录。');
+    showMsg('ok', '<b>开通成功!</b><br>席位:' + (d.seats || seats) + ',到期时间:' + (d.expireAt || '已延长 ' + months + ' 个月') + '。<br>请回到对话中重新执行 qiwei_login_start 继续扫码登录。');
   } catch (e) {
   } catch (e) {
     showMsg('err', '<b>[QW-UP-502] 网络请求失败</b><br>' + e.message + '<br>请确认网关服务可用后重试。');
     showMsg('err', '<b>[QW-UP-502] 网络请求失败</b><br>' + e.message + '<br>请确认网关服务可用后重试。');
     btn.disabled = false;
     btn.disabled = false;
@@ -179,7 +179,7 @@ render();
 
 
 function saveSubscribePage({ apiBase, token, monthlyPrice } = {}) {
 function saveSubscribePage({ apiBase, token, monthlyPrice } = {}) {
   try {
   try {
-    const htmlPath = latestPath('subscription', 'qiwe-subscribe.html');
+    const htmlPath = latestPath('subscription', 'qiwei-subscribe.html');
     fs.writeFileSync(htmlPath, buildSubscribePageHtml({ apiBase, token, monthlyPrice }), 'utf8');
     fs.writeFileSync(htmlPath, buildSubscribePageHtml({ apiBase, token, monthlyPrice }), 'utf8');
     return htmlPath;
     return htmlPath;
   } catch {
   } catch {
@@ -188,7 +188,7 @@ function saveSubscribePage({ apiBase, token, monthlyPrice } = {}) {
 }
 }
 
 
 module.exports = {
 module.exports = {
-  QIWE_MONTHLY_PRICE,
+  QIWEI_MONTHLY_PRICE,
   SEAT_PLANS,
   SEAT_PLANS,
   DURATION_PLANS,
   DURATION_PLANS,
   ERROR_CODES,
   ERROR_CODES,

+ 6 - 6
claude-code/claude-code-qiwe-assistant/mcp/src/core/wecom-cli-runtime.js

@@ -28,21 +28,21 @@ function isSupportedPlatform() {
 }
 }
 
 
 function getRuntimeRoot() {
 function getRuntimeRoot() {
-  if (process.env.QIWE_OFFICIAL_CLI_RUNTIME_DIR) {
-    return path.resolve(expandHome(process.env.QIWE_OFFICIAL_CLI_RUNTIME_DIR));
+  if (process.env.QIWEI_OFFICIAL_CLI_RUNTIME_DIR) {
+    return path.resolve(expandHome(process.env.QIWEI_OFFICIAL_CLI_RUNTIME_DIR));
   }
   }
 
 
   if (process.platform === 'win32') {
   if (process.platform === 'win32') {
     const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
     const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
-    return path.join(base, 'Fmode', 'qiwe-assistant', 'wecom-cli');
+    return path.join(base, 'Fmode', 'qiwei-assistant', 'wecom-cli');
   }
   }
 
 
   if (process.platform === 'darwin') {
   if (process.platform === 'darwin') {
-    return path.join(os.homedir(), 'Library', 'Caches', 'fmode', 'qiwe-assistant', 'wecom-cli');
+    return path.join(os.homedir(), 'Library', 'Caches', 'fmode', 'qiwei-assistant', 'wecom-cli');
   }
   }
 
 
   const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
   const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
-  return path.join(base, 'fmode', 'qiwe-assistant', 'wecom-cli');
+  return path.join(base, 'fmode', 'qiwei-assistant', 'wecom-cli');
 }
 }
 
 
 function getRuntimeDir() {
 function getRuntimeDir() {
@@ -369,7 +369,7 @@ async function getOfficialCliStatus() {
 }
 }
 
 
 function getInitCommand() {
 function getInitCommand() {
-  const wrapperPath = path.resolve(__dirname, '../../../bin/qiwe-official-cli.js');
+  const wrapperPath = path.resolve(__dirname, '../../../bin/qiwei-official-cli.js');
   return `"${process.execPath}" "${wrapperPath}" init`;
   return `"${process.execPath}" "${wrapperPath}" init`;
 }
 }
 
 

+ 9 - 9
claude-code/claude-code-qiwe-assistant/mcp/src/providers/wecom-official-cli.js

@@ -32,12 +32,12 @@ function initializationError() {
   });
   });
 }
 }
 
 
-async function qiweOfficialStatus() {
+async function qiweiOfficialStatus() {
   const status = await getOfficialCliStatus();
   const status = await getOfficialCliStatus();
   const nextActions = [];
   const nextActions = [];
   if (!status.installed || !status.valid) {
   if (!status.installed || !status.valid) {
     nextActions.push({
     nextActions.push({
-      action: 'qiwe_official_prepare',
+      action: 'qiwei_official_prepare',
       description: `下载并缓存 ${runtimeManifest.package}@${runtimeManifest.version}`
       description: `下载并缓存 ${runtimeManifest.package}@${runtimeManifest.version}`
     });
     });
   } else if (!status.ready) {
   } else if (!status.ready) {
@@ -62,7 +62,7 @@ async function qiweOfficialStatus() {
   });
   });
 }
 }
 
 
-async function qiweOfficialPrepare({ force = false } = {}) {
+async function qiweiOfficialPrepare({ force = false } = {}) {
   try {
   try {
     const installed = await prepareOfficialCli({ force });
     const installed = await prepareOfficialCli({ force });
     const status = await getOfficialCliStatus();
     const status = await getOfficialCliStatus();
@@ -97,7 +97,7 @@ async function qiweOfficialPrepare({ force = false } = {}) {
   }
   }
 }
 }
 
 
-async function qiweOfficialHelp({ category, method } = {}) {
+async function qiweiOfficialHelp({ category, method } = {}) {
   if (method && !category) return errorResult('查询方法帮助时必须同时提供 category。');
   if (method && !category) return errorResult('查询方法帮助时必须同时提供 category。');
   if (category && !OFFICIAL_CATEGORIES.has(category)) {
   if (category && !OFFICIAL_CATEGORIES.has(category)) {
     return errorResult(`不支持的官方 CLI category:${category}`);
     return errorResult(`不支持的官方 CLI category:${category}`);
@@ -143,7 +143,7 @@ async function qiweOfficialHelp({ category, method } = {}) {
   }
   }
 }
 }
 
 
-async function qiweOfficialCall({ category, method, args = {}, timeoutMs } = {}) {
+async function qiweiOfficialCall({ category, method, args = {}, timeoutMs } = {}) {
   if (!OFFICIAL_CATEGORIES.has(category)) {
   if (!OFFICIAL_CATEGORIES.has(category)) {
     return errorResult(`不支持的官方 CLI category:${category || '未提供'}`);
     return errorResult(`不支持的官方 CLI category:${category || '未提供'}`);
   }
   }
@@ -200,8 +200,8 @@ async function qiweOfficialCall({ category, method, args = {}, timeoutMs } = {})
 
 
 module.exports = {
 module.exports = {
   OFFICIAL_CATEGORIES,
   OFFICIAL_CATEGORIES,
-  qiweOfficialStatus,
-  qiweOfficialPrepare,
-  qiweOfficialHelp,
-  qiweOfficialCall
+  qiweiOfficialStatus,
+  qiweiOfficialPrepare,
+  qiweiOfficialHelp,
+  qiweiOfficialCall
 };
 };

+ 45 - 45
claude-code/claude-code-qiwe-assistant/mcp/src/server.js

@@ -2,23 +2,23 @@
 const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
 const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
 const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
 const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
 const { z } = require('zod');
 const { z } = require('zod');
-const { searchQiweApis, getQiweApiDoc, callQiweApi } = require('./tools/qiwe-api-catalog-run');
+const { searchQiweiApis, getQiweiApiDoc, callQiweiApi } = require('./tools/qiwei-api-catalog-run');
 const {
 const {
-  qiweLoginStatus,
-  qiweLoginStart,
-  qiweLoginCheck,
-  qiweLoginVerify
-} = require('./tools/qiwe-login-run');
+  qiweiLoginStatus,
+  qiweiLoginStart,
+  qiweiLoginCheck,
+  qiweiLoginVerify
+} = require('./tools/qiwei-login-run');
 const {
 const {
-  qiweSubscriptionStatus,
-  qiweSubscribe,
-  qiweSubscriptionAutoRenew
-} = require('./tools/qiwe-subscription-run');
+  qiweiSubscriptionStatus,
+  qiweiSubscribe,
+  qiweiSubscriptionAutoRenew
+} = require('./tools/qiwei-subscription-run');
 const {
 const {
-  qiweOfficialStatus,
-  qiweOfficialPrepare,
-  qiweOfficialHelp,
-  qiweOfficialCall
+  qiweiOfficialStatus,
+  qiweiOfficialPrepare,
+  qiweiOfficialHelp,
+  qiweiOfficialCall
 } = require('./tools/wecom-official-cli-run');
 } = require('./tools/wecom-official-cli-run');
 
 
 function asToolResult(result) {
 function asToolResult(result) {
@@ -56,7 +56,7 @@ function createServer() {
   });
   });
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_api_search',
+    'qiwei_api_search',
     {
     {
       title: '检索企业微信开放接口清单',
       title: '检索企业微信开放接口清单',
       description: [
       description: [
@@ -71,24 +71,24 @@ function createServer() {
         limit: z.number().int().min(1).max(200).optional()
         limit: z.number().int().min(1).max(200).optional()
       }
       }
     },
     },
-    wrap(searchQiweApis)
+    wrap(searchQiweiApis)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_api_doc',
+    'qiwei_api_doc',
     {
     {
       title: '读取企业微信接口的详细参数文档',
       title: '读取企业微信接口的详细参数文档',
-      description: '读取清单中某个企业微信接口的完整参数文档(参数表、必填项、返回字段说明、调用模板)。入参 id(如 msg.sendText,来自 qiwe_api_search)或 method 路径(如 /msg/sendText)。',
+      description: '读取清单中某个企业微信接口的完整参数文档(参数表、必填项、返回字段说明、调用模板)。入参 id(如 msg.sendText,来自 qiwei_api_search)或 method 路径(如 /msg/sendText)。',
       inputSchema: {
       inputSchema: {
         id: z.string().optional(),
         id: z.string().optional(),
         method: z.string().optional()
         method: z.string().optional()
       }
       }
     },
     },
-    wrap(getQiweApiDoc)
+    wrap(getQiweiApiDoc)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_api_call',
+    'qiwei_api_call',
     {
     {
       title: '调用企业微信开放接口',
       title: '调用企业微信开放接口',
       description: [
       description: [
@@ -105,11 +105,11 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(callQiweApi)
+    wrap(callQiweiApi)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_login_status',
+    'qiwei_login_status',
     {
     {
       title: '查询企业微信登录/配置状态',
       title: '查询企业微信登录/配置状态',
       description: '通过 Fmode 网关转发的企业微信接口查询当前 uid 对应的设备在线状态。技能包安装后、或任何接口报设备/登录错误时先调用。',
       description: '通过 Fmode 网关转发的企业微信接口查询当前 uid 对应的设备在线状态。技能包安装后、或任何接口报设备/登录错误时先调用。',
@@ -119,16 +119,16 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweLoginStatus)
+    wrap(qiweiLoginStatus)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_login_start',
+    'qiwei_login_start',
     {
     {
       title: '开始企业微信扫码登录',
       title: '开始企业微信扫码登录',
       description: [
       description: [
         '登录两步流程入口:默认在本地端口(默认 4310)启动流程页服务并打开浏览器,页面自动推进:未开通订阅时先展示套餐页(每号 ¥200/月,飞马余额扣费),开通后自动生成二维码并每 3 秒轮询扫码状态,需要验证码时页面提示输入。',
         '登录两步流程入口:默认在本地端口(默认 4310)启动流程页服务并打开浏览器,页面自动推进:未开通订阅时先展示套餐页(每号 ¥200/月,飞马余额扣费),开通后自动生成二维码并每 3 秒轮询扫码状态,需要验证码时页面提示输入。',
-        '传 flowUi=false 回退到静态模式:校验订阅(未开通返回 needs_subscription 并生成套餐页)后生成二维码保存到 outputs/login/qiwe-login-qrcode.png(openBrowser=false 可关闭自动打开)。',
+        '传 flowUi=false 回退到静态模式:校验订阅(未开通返回 needs_subscription 并生成套餐页)后生成二维码保存到 outputs/login/qiwei-login-qrcode.png(openBrowser=false 可关闭自动打开)。',
         '需要有效企微包月订阅和可用席位;无需提供企业微信接口访问凭据、代理或设备参数。'
         '需要有效企微包月订阅和可用席位;无需提供企业微信接口访问凭据、代理或设备参数。'
       ].join(' '),
       ].join(' '),
       inputSchema: {
       inputSchema: {
@@ -140,11 +140,11 @@ function createServer() {
         flowPort: z.number().int().optional()
         flowPort: z.number().int().optional()
       }
       }
     },
     },
-    wrap(qiweLoginStart)
+    wrap(qiweiLoginStart)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_login_check',
+    'qiwei_login_check',
     {
     {
       title: '检测扫码登录状态',
       title: '检测扫码登录状态',
       description: '通过 Fmode 网关转发的企业微信接口轮询扫码登录状态(用户扫码后每 3-5 秒调一次)。状态 10 表示需要 6 位验证码;manual=true 时先尝试免扫码登录。',
       description: '通过 Fmode 网关转发的企业微信接口轮询扫码登录状态(用户扫码后每 3-5 秒调一次)。状态 10 表示需要 6 位验证码;manual=true 时先尝试免扫码登录。',
@@ -155,14 +155,14 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweLoginCheck)
+    wrap(qiweiLoginCheck)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_login_verify',
+    'qiwei_login_verify',
     {
     {
       title: '提交扫码登录的 6 位验证码',
       title: '提交扫码登录的 6 位验证码',
-      description: '当 qiwe_login_check 返回状态 10(需要验证码)时,向用户索要手机端显示的 6 位验证码并通过本工具提交,随后再调 qiwe_login_check 确认登录成功。',
+      description: '当 qiwei_login_check 返回状态 10(需要验证码)时,向用户索要手机端显示的 6 位验证码并通过本工具提交,随后再调 qiwei_login_check 确认登录成功。',
       inputSchema: {
       inputSchema: {
         code: z.string(),
         code: z.string(),
         uid: z.string().optional(),
         uid: z.string().optional(),
@@ -170,11 +170,11 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweLoginVerify)
+    wrap(qiweiLoginVerify)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_subscription_status',
+    'qiwei_subscription_status',
     {
     {
       title: '查询企微包月订阅与席位状态',
       title: '查询企微包月订阅与席位状态',
       description: '查询企微包月订阅、到期时间、自动续费、已购/已用席位和飞马余额。首次登录或收到 402/403 时先调用。',
       description: '查询企微包月订阅、到期时间、自动续费、已购/已用席位和飞马余额。首次登录或收到 402/403 时先调用。',
@@ -183,11 +183,11 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweSubscriptionStatus)
+    wrap(qiweiSubscriptionStatus)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_subscribe',
+    'qiwei_subscribe',
     {
     {
       title: '开通、续费或增购企微席位',
       title: '开通、续费或增购企微席位',
       description: '使用飞马余额开通或续费企微包月订阅。1 个席位对应 1 个企业微信账号;seats 不传时默认 1。',
       description: '使用飞马余额开通或续费企微包月订阅。1 个席位对应 1 个企业微信账号;seats 不传时默认 1。',
@@ -197,11 +197,11 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweSubscribe)
+    wrap(qiweiSubscribe)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_subscription_auto_renew',
+    'qiwei_subscription_auto_renew',
     {
     {
       title: '设置企微订阅自动续费',
       title: '设置企微订阅自动续费',
       description: '开启或关闭企微包月订阅到期自动续费。',
       description: '开启或关闭企微包月订阅到期自动续费。',
@@ -211,21 +211,21 @@ function createServer() {
         apiBase: z.string().optional()
         apiBase: z.string().optional()
       }
       }
     },
     },
-    wrap(qiweSubscriptionAutoRenew)
+    wrap(qiweiSubscriptionAutoRenew)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_official_status',
+    'qiwei_official_status',
     {
     {
       title: '检查企业微信官方 CLI 状态',
       title: '检查企业微信官方 CLI 状态',
       description: '检查固定版本官方 CLI 是否已下载、版本是否正确,以及官方机器人扫码授权是否完成。此工具与 Fmode 网关登录状态完全独立。',
       description: '检查固定版本官方 CLI 是否已下载、版本是否正确,以及官方机器人扫码授权是否完成。此工具与 Fmode 网关登录状态完全独立。',
       inputSchema: {}
       inputSchema: {}
     },
     },
-    wrap(qiweOfficialStatus)
+    wrap(qiweiOfficialStatus)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_official_prepare',
+    'qiwei_official_prepare',
     {
     {
       title: '下载企业微信官方 CLI',
       title: '下载企业微信官方 CLI',
       description: '将固定版本 @wecom/cli 下载到用户级缓存并校验。不会全局安装,不修改官方 CLI,也不会改动 Fmode 网关转发的企业微信接口通道。',
       description: '将固定版本 @wecom/cli 下载到用户级缓存并校验。不会全局安装,不修改官方 CLI,也不会改动 Fmode 网关转发的企业微信接口通道。',
@@ -233,11 +233,11 @@ function createServer() {
         force: z.boolean().optional()
         force: z.boolean().optional()
       }
       }
     },
     },
-    wrap(qiweOfficialPrepare)
+    wrap(qiweiOfficialPrepare)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_official_help',
+    'qiwei_official_help',
     {
     {
       title: '读取企业微信官方 CLI 帮助',
       title: '读取企业微信官方 CLI 帮助',
       description: '读取官方 CLI 总帮助、category 工具列表或具体 method 的参数 schema。category/method 级帮助需要先完成官方 CLI 初始化。',
       description: '读取官方 CLI 总帮助、category 工具列表或具体 method 的参数 schema。category/method 级帮助需要先完成官方 CLI 初始化。',
@@ -246,11 +246,11 @@ function createServer() {
         method: z.string().regex(/^[a-z][a-z0-9_]*$/).optional()
         method: z.string().regex(/^[a-z][a-z0-9_]*$/).optional()
       }
       }
     },
     },
-    wrap(qiweOfficialHelp)
+    wrap(qiweiOfficialHelp)
   );
   );
 
 
   server.registerTool(
   server.registerTool(
-    'qiwe_official_call',
+    'qiwei_official_call',
     {
     {
       title: '调用企业微信官方 CLI 能力',
       title: '调用企业微信官方 CLI 能力',
       description: '通过固定版本官方 CLI 调用通讯录、文档、会议、消息、日程或待办。参数以结构化 JSON 传递,不经过 shell,也不经过 Fmode 网关。',
       description: '通过固定版本官方 CLI 调用通讯录、文档、会议、消息、日程或待办。参数以结构化 JSON 传递,不经过 shell,也不经过 Fmode 网关。',
@@ -261,7 +261,7 @@ function createServer() {
         timeoutMs: z.number().int().min(1000).max(300000).optional()
         timeoutMs: z.number().int().min(1000).max(300000).optional()
       }
       }
     },
     },
-    wrap(qiweOfficialCall)
+    wrap(qiweiOfficialCall)
   );
   );
 
 
   return server;
   return server;

+ 34 - 34
claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwe-api-catalog-run.js → claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-api-catalog-run.js

@@ -6,7 +6,7 @@ const {
   searchEndpoints,
   searchEndpoints,
   summarizeEndpoint
   summarizeEndpoint
 } = require('../core/api-catalog');
 } = require('../core/api-catalog');
-const { readQiweAuthToken, ensureQiweUid, readQiweApiBase } = require('../core/credentials');
+const { readQiweiAuthToken, ensureQiweiUid, readQiweiApiBase } = require('../core/credentials');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { okResult, errorResult } = require('../core/result-envelope');
 const { okResult, errorResult } = require('../core/result-envelope');
 
 
@@ -18,7 +18,7 @@ function buildCallTemplate(endpoint) {
   return args;
   return args;
 }
 }
 
 
-async function searchQiweApis(input = {}) {
+async function searchQiweiApis(input = {}) {
   const catalog = loadCatalog();
   const catalog = loadCatalog();
   const query = input.query || input.q || input.keyword || '';
   const query = input.query || input.q || input.keyword || '';
   const matches = (query || input.module || input.tag)
   const matches = (query || input.module || input.tag)
@@ -37,8 +37,8 @@ async function searchQiweApis(input = {}) {
         `- ${item.id} | ${item.title}(method: ${item.method})必填参数: ${item.requiredParams.join(', ') || '无'}`
         `- ${item.id} | ${item.title}(method: ${item.method})必填参数: ${item.requiredParams.join(', ') || '无'}`
     ),
     ),
     '',
     '',
-    '下一步:用 qiwe_api_doc 读取某个接口的详细参数文档,再用 qiwe_api_call 传参调用。',
-    '清单里没有的接口,可直接用 qiwe_api_call 传 rawMethod + params 调用。'
+    '下一步:用 qiwei_api_doc 读取某个接口的详细参数文档,再用 qiwei_api_call 传参调用。',
+    '清单里没有的接口,可直接用 qiwei_api_call 传 rawMethod + params 调用。'
   ];
   ];
   return okResult({
   return okResult({
     assistantMessage: lines.join('\n'),
     assistantMessage: lines.join('\n'),
@@ -68,15 +68,15 @@ function publicParams(params) {
     }));
     }));
 }
 }
 
 
-async function getQiweApiDoc(input = {}) {
+async function getQiweiApiDoc(input = {}) {
   const idOrMethod = input.id || input.endpointId || input.method || input.path;
   const idOrMethod = input.id || input.endpointId || input.method || input.path;
   if (!idOrMethod) {
   if (!idOrMethod) {
-    return errorResult('请提供接口 id 或 method 路径(可先用 qiwe_api_search 查清单)。');
+    return errorResult('请提供接口 id 或 method 路径(可先用 qiwei_api_search 查清单)。');
   }
   }
   const endpoint = findEndpoint(idOrMethod);
   const endpoint = findEndpoint(idOrMethod);
   if (!endpoint) {
   if (!endpoint) {
     return errorResult(
     return errorResult(
-      `清单里没有找到接口「${idOrMethod}」。可用 qiwe_api_search 查询,或直接用 qiwe_api_call 传 rawMethod 调用未登记接口。`
+      `清单里没有找到接口「${idOrMethod}」。可用 qiwei_api_search 查询,或直接用 qiwei_api_call 传 rawMethod 调用未登记接口。`
     );
     );
   }
   }
   const template = buildCallTemplate(endpoint);
   const template = buildCallTemplate(endpoint);
@@ -85,15 +85,15 @@ async function getQiweApiDoc(input = {}) {
     '',
     '',
     redactSecret(endpoint.summary || ''),
     redactSecret(endpoint.summary || ''),
     '',
     '',
-    `method: ${endpoint.method || '(官方文档未公布,暂不可通过 qiwe_api_call 直接调用)'}(分类:${(endpoint.category || []).join(' > ') || '无'})`,
-    endpoint.apiPath ? `注意:本接口走 ${endpoint.apiPath}${endpoint.contentType ? `(${endpoint.contentType})` : ''};Fmode 网关当前尚未支持 multipart 文件传输,qiwe_api_call 暂不可调用。` : '',
+    `method: ${endpoint.method || '(官方文档未公布,暂不可通过 qiwei_api_call 直接调用)'}(分类:${(endpoint.category || []).join(' > ') || '无'})`,
+    endpoint.apiPath ? `注意:本接口走 ${endpoint.apiPath}${endpoint.contentType ? `(${endpoint.contentType})` : ''};Fmode 网关当前尚未支持 multipart 文件传输,qiwei_api_call 暂不可调用。` : '',
     '',
     '',
     '参数:',
     '参数:',
     ...renderParamLines(endpoint.params),
     ...renderParamLines(endpoint.params),
     '',
     '',
     endpoint.responseHint ? `返回:${redactSecret(endpoint.responseHint)}` : '',
     endpoint.responseHint ? `返回:${redactSecret(endpoint.responseHint)}` : '',
     '',
     '',
-    '调用示例(传给 qiwe_api_call):',
+    '调用示例(传给 qiwei_api_call):',
     '```json',
     '```json',
     JSON.stringify(template, null, 2),
     JSON.stringify(template, null, 2),
     '```'
     '```'
@@ -122,29 +122,29 @@ async function getQiweApiDoc(input = {}) {
 
 
 const BLOCKED_METHOD_PREFIXES = ['/login/', '/client/'];
 const BLOCKED_METHOD_PREFIXES = ['/login/', '/client/'];
 
 
-async function callQiweApi(input = {}) {
+async function callQiweiApi(input = {}) {
   const idOrMethod = input.id || input.endpointId;
   const idOrMethod = input.id || input.endpointId;
   const rawMethod = input.rawMethod || input.method || (idOrMethod ? null : input.path);
   const rawMethod = input.rawMethod || input.method || (idOrMethod ? null : input.path);
   const endpoint = (idOrMethod || rawMethod) ? findEndpoint(idOrMethod || rawMethod) : undefined;
   const endpoint = (idOrMethod || rawMethod) ? findEndpoint(idOrMethod || rawMethod) : undefined;
 
 
   if (!endpoint && !rawMethod) {
   if (!endpoint && !rawMethod) {
-    return errorResult('请提供接口 id(先用 qiwe_api_search 查清单),或提供 rawMethod 调用未登记接口。');
+    return errorResult('请提供接口 id(先用 qiwei_api_search 查清单),或提供 rawMethod 调用未登记接口。');
   }
   }
 
 
-  const token = readQiweAuthToken(input);
+  const token = readQiweiAuthToken(input);
   if (!token) {
   if (!token) {
     return {
     return {
       status: 'needs_auth',
       status: 'needs_auth',
       assistantMessage: [
       assistantMessage: [
         '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。',
         '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。',
         '',
         '',
-        '优先使用 Claude Code 已配置的 Fmode NewAPI sk- token;也可配置 QIWE_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken。',
-        '配置鉴权后先调用 qiwe_subscription_status,再用 qiwe_login_start 完成企微扫码登录。'
+        '优先使用 Claude Code 已配置的 Fmode NewAPI sk- token;也可配置 QIWEI_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken。',
+        '配置鉴权后先调用 qiwei_subscription_status,再用 qiwei_login_start 完成企微扫码登录。'
       ].join('\n'),
       ].join('\n'),
       summary: { errorKind: 'missing_auth_token', recoverable: true },
       summary: { errorKind: 'missing_auth_token', recoverable: true },
       data: {},
       data: {},
       files: [],
       files: [],
-      nextActions: ['配置 Fmode 鉴权 token', '调用 qiwe_subscription_status 查看订阅状态'],
+      nextActions: ['配置 Fmode 鉴权 token', '调用 qiwei_subscription_status 查看订阅状态'],
       warnings: [],
       warnings: [],
       errors: []
       errors: []
     };
     };
@@ -152,7 +152,7 @@ async function callQiweApi(input = {}) {
 
 
   const method = endpoint ? endpoint.method : rawMethod;
   const method = endpoint ? endpoint.method : rawMethod;
   if (BLOCKED_METHOD_PREFIXES.some(prefix => String(method || '').startsWith(prefix))) {
   if (BLOCKED_METHOD_PREFIXES.some(prefix => String(method || '').startsWith(prefix))) {
-    return errorResult('登录和设备类接口必须使用 qiwe_login_* 专用工具,不能通过 qiwe_api_call 透传。');
+    return errorResult('登录和设备类接口必须使用 qiwei_login_* 专用工具,不能通过 qiwei_api_call 透传。');
   }
   }
   const params = input.params && typeof input.params === 'object' ? { ...input.params } : {};
   const params = input.params && typeof input.params === 'object' ? { ...input.params } : {};
 
 
@@ -165,7 +165,7 @@ async function callQiweApi(input = {}) {
     }
     }
     if (endpoint.apiPath && endpoint.apiPath !== '/api/qw/doApi') {
     if (endpoint.apiPath && endpoint.apiPath !== '/api/qw/doApi') {
       return errorResult(
       return errorResult(
-        `接口 ${endpoint.id} 走 ${endpoint.apiPath}(${endpoint.contentType || 'multipart'}),Fmode 网关当前尚未支持文件流,qiwe_api_call 暂不可调用。`
+        `接口 ${endpoint.id} 走 ${endpoint.apiPath}(${endpoint.contentType || 'multipart'}),Fmode 网关当前尚未支持文件流,qiwei_api_call 暂不可调用。`
       );
       );
     }
     }
     const topMissing = (endpoint.params || [])
     const topMissing = (endpoint.params || [])
@@ -179,19 +179,19 @@ async function callQiweApi(input = {}) {
     if (topMissing.length) {
     if (topMissing.length) {
       return {
       return {
         status: 'needs_input',
         status: 'needs_input',
-        assistantMessage: `缺少必填参数:${topMissing.join(', ')}。请用 qiwe_api_doc 查看参数说明后补齐再调用。`,
+        assistantMessage: `缺少必填参数:${topMissing.join(', ')}。请用 qiwei_api_doc 查看参数说明后补齐再调用。`,
         summary: { endpoint: endpoint.id, missing: topMissing },
         summary: { endpoint: endpoint.id, missing: topMissing },
         data: { endpoint: summarizeEndpoint(endpoint), missing: topMissing },
         data: { endpoint: summarizeEndpoint(endpoint), missing: topMissing },
         files: [],
         files: [],
-        nextActions: ['用 qiwe_api_doc 核对必填参数', '补齐参数后重新调用'],
+        nextActions: ['用 qiwei_api_doc 核对必填参数', '补齐参数后重新调用'],
         warnings: [],
         warnings: [],
         errors: [{ message: `缺少必填参数:${topMissing.join(', ')}`, kind: 'request' }]
         errors: [{ message: `缺少必填参数:${topMissing.join(', ')}`, kind: 'request' }]
       };
       };
     }
     }
   }
   }
 
 
-  const apiBase = readQiweApiBase(input);
-  const uid = ensureQiweUid(input);
+  const apiBase = readQiweiApiBase(input);
+  const uid = ensureQiweiUid(input);
   try {
   try {
     const result = await callFmodeWecomGateway({
     const result = await callFmodeWecomGateway({
       gatewayPath: '/doApi',
       gatewayPath: '/doApi',
@@ -212,7 +212,7 @@ async function callQiweApi(input = {}) {
     if (kind === 'auth') {
     if (kind === 'auth') {
       return {
       return {
         status: 'needs_auth',
         status: 'needs_auth',
-        assistantMessage: 'Fmode 鉴权 token 无效或已失效,请更新 QIWE_AUTH_TOKEN/FMODE_API_KEY 或重新登录平台后重试。',
+        assistantMessage: 'Fmode 鉴权 token 无效或已失效,请更新 QIWEI_AUTH_TOKEN/FMODE_API_KEY 或重新登录平台后重试。',
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus, recoverable: true },
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus, recoverable: true },
         data: {},
         data: {},
         files: [],
         files: [],
@@ -224,11 +224,11 @@ async function callQiweApi(input = {}) {
     if (kind === 'device') {
     if (kind === 'device') {
       return {
       return {
         status: 'needs_login',
         status: 'needs_login',
-        assistantMessage: `接口 ${method} 返回登录态错误(${safeMessage})。请先用 qiwe_login_status 检查,再用 qiwe_login_start 重新扫码登录。`,
+        assistantMessage: `接口 ${method} 返回登录态错误(${safeMessage})。请先用 qiwei_login_status 检查,再用 qiwei_login_start 重新扫码登录。`,
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus, recoverable: true },
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus, recoverable: true },
         data: { message: safeMessage },
         data: { message: safeMessage },
         files: [],
         files: [],
-        nextActions: ['调用 qiwe_login_status 检查设备在线状态', '掉线则调用 qiwe_login_start 重新登录'],
+        nextActions: ['调用 qiwei_login_status 检查设备在线状态', '掉线则调用 qiwei_login_start 重新登录'],
         warnings: [],
         warnings: [],
         errors: [{ message: safeMessage, kind, httpStatus }]
         errors: [{ message: safeMessage, kind, httpStatus }]
       };
       };
@@ -236,11 +236,11 @@ async function callQiweApi(input = {}) {
     if (kind === 'billing') {
     if (kind === 'billing') {
       return {
       return {
         status: 'needs_subscription',
         status: 'needs_subscription',
-        assistantMessage: `企微订阅或飞马余额不足(${safeMessage})。请先调用 qiwe_subscription_status,必要时用 qiwe_subscribe 开通或续费。`,
+        assistantMessage: `企微订阅或飞马余额不足(${safeMessage})。请先调用 qiwei_subscription_status,必要时用 qiwei_subscribe 开通或续费。`,
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         data: { message: safeMessage },
         data: { message: safeMessage },
         files: [],
         files: [],
-        nextActions: ['调用 qiwe_subscription_status', '调用 qiwe_subscribe 开通或续费'],
+        nextActions: ['调用 qiwei_subscription_status', '调用 qiwei_subscribe 开通或续费'],
         warnings: [],
         warnings: [],
         errors: []
         errors: []
       };
       };
@@ -248,11 +248,11 @@ async function callQiweApi(input = {}) {
     if (kind === 'permission') {
     if (kind === 'permission') {
       return {
       return {
         status: 'needs_seat',
         status: 'needs_seat',
-        assistantMessage: `企微席位或权限不足(${safeMessage})。请调用 qiwe_subscription_status 检查并按需增购席位。`,
+        assistantMessage: `企微席位或权限不足(${safeMessage})。请调用 qiwei_subscription_status 检查并按需增购席位。`,
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         data: { message: safeMessage },
         data: { message: safeMessage },
         files: [],
         files: [],
-        nextActions: ['调用 qiwe_subscription_status', '调用 qiwe_subscribe 增购席位'],
+        nextActions: ['调用 qiwei_subscription_status', '调用 qiwei_subscribe 增购席位'],
         warnings: [],
         warnings: [],
         errors: []
         errors: []
       };
       };
@@ -260,11 +260,11 @@ async function callQiweApi(input = {}) {
     if (kind === 'request') {
     if (kind === 'request') {
       return {
       return {
         status: 'needs_input',
         status: 'needs_input',
-        assistantMessage: `接口 ${method} 返回入参错误(${safeMessage})。请用 qiwe_api_doc 核对参数后重试。`,
+        assistantMessage: `接口 ${method} 返回入参错误(${safeMessage})。请用 qiwei_api_doc 核对参数后重试。`,
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         summary: { endpoint: endpoint ? endpoint.id : method, errorKind: kind, httpStatus },
         data: { message: safeMessage },
         data: { message: safeMessage },
         files: [],
         files: [],
-        nextActions: ['用 qiwe_api_doc 核对参数', '修正参数后重新调用'],
+        nextActions: ['用 qiwei_api_doc 核对参数', '修正参数后重新调用'],
         warnings: [],
         warnings: [],
         errors: [{ message: safeMessage, kind, httpStatus }]
         errors: [{ message: safeMessage, kind, httpStatus }]
       };
       };
@@ -283,7 +283,7 @@ async function callQiweApi(input = {}) {
 }
 }
 
 
 module.exports = {
 module.exports = {
-  searchQiweApis,
-  getQiweApiDoc,
-  callQiweApi
+  searchQiweiApis,
+  getQiweiApiDoc,
+  callQiweiApi
 };
 };

+ 58 - 58
claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwe-login-run.js → claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-login-run.js

@@ -2,15 +2,15 @@ const fs = require('fs');
 const path = require('path');
 const path = require('path');
 const { spawn } = require('child_process');
 const { spawn } = require('child_process');
 const {
 const {
-  readQiweAuthToken,
-  ensureQiweUid,
-  readQiweApiBase,
-  saveQiweClientConfig
+  readQiweiAuthToken,
+  ensureQiweiUid,
+  readQiweiApiBase,
+  saveQiweiClientConfig
 } = require('../core/credentials');
 } = require('../core/credentials');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { okResult, errorResult } = require('../core/result-envelope');
 const { okResult, errorResult } = require('../core/result-envelope');
 const { latestPath } = require('../core/output-paths');
 const { latestPath } = require('../core/output-paths');
-const { saveSubscribePage, QIWE_MONTHLY_PRICE } = require('../core/subscribe-page');
+const { saveSubscribePage, QIWEI_MONTHLY_PRICE } = require('../core/subscribe-page');
 const { startLoginFlowServer } = require('../core/login-flow-server');
 const { startLoginFlowServer } = require('../core/login-flow-server');
 
 
 const QR_STATUS = {
 const QR_STATUS = {
@@ -27,7 +27,7 @@ function authRequiredResult() {
     status: 'needs_auth',
     status: 'needs_auth',
     assistantMessage: [
     assistantMessage: [
       '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。',
       '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。',
-      '优先复用 Claude Code 已配置的 Fmode NewAPI sk- token;也可配置 QIWE_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken。',
+      '优先复用 Claude Code 已配置的 Fmode NewAPI sk- token;也可配置 QIWEI_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken。',
       '企业微信接口访问凭据和设备上下文由 Fmode 网关管理,技能不会读取或保存。'
       '企业微信接口访问凭据和设备上下文由 Fmode 网关管理,技能不会读取或保存。'
     ].join('\n'),
     ].join('\n'),
     summary: { configured: false, errorKind: 'missing_auth_token', recoverable: true },
     summary: { configured: false, errorKind: 'missing_auth_token', recoverable: true },
@@ -41,7 +41,7 @@ function authRequiredResult() {
 
 
 function saveQrcodeImage(base64Data) {
 function saveQrcodeImage(base64Data) {
   try {
   try {
-    const filePath = latestPath('login', 'qiwe-login-qrcode.png');
+    const filePath = latestPath('login', 'qiwei-login-qrcode.png');
     const data = String(base64Data || '').replace(/^data:image\/\w+;base64,/, '');
     const data = String(base64Data || '').replace(/^data:image\/\w+;base64,/, '');
     fs.writeFileSync(filePath, Buffer.from(data, 'base64'));
     fs.writeFileSync(filePath, Buffer.from(data, 'base64'));
     return filePath;
     return filePath;
@@ -52,7 +52,7 @@ function saveQrcodeImage(base64Data) {
 
 
 function saveQrcodePreviewHtml(qrcodeImagePath) {
 function saveQrcodePreviewHtml(qrcodeImagePath) {
   try {
   try {
-    const htmlPath = latestPath('login', 'qiwe-login-qrcode.html');
+    const htmlPath = latestPath('login', 'qiwei-login-qrcode.html');
     const html = [
     const html = [
       '<!DOCTYPE html>',
       '<!DOCTYPE html>',
       '<html lang="zh-CN">',
       '<html lang="zh-CN">',
@@ -108,11 +108,11 @@ function gatewayErrorResult(error, stage, fallbackMessage) {
   if (kind === 'billing') {
   if (kind === 'billing') {
     return {
     return {
       status: 'needs_subscription',
       status: 'needs_subscription',
-      assistantMessage: `企微订阅未开通、已到期或飞马余额不足(${safeMessage})。请先调用 qiwe_subscription_status,必要时用 qiwe_subscribe 开通或续费。`,
+      assistantMessage: `企微订阅未开通、已到期或飞马余额不足(${safeMessage})。请先调用 qiwei_subscription_status,必要时用 qiwei_subscribe 开通或续费。`,
       summary: { stage, errorKind: kind, recoverable: true },
       summary: { stage, errorKind: kind, recoverable: true },
       data: {},
       data: {},
       files: [],
       files: [],
-      nextActions: ['调用 qiwe_subscription_status', '调用 qiwe_subscribe 开通或续费'],
+      nextActions: ['调用 qiwei_subscription_status', '调用 qiwei_subscribe 开通或续费'],
       warnings: [],
       warnings: [],
       errors: []
       errors: []
     };
     };
@@ -124,7 +124,7 @@ function gatewayErrorResult(error, stage, fallbackMessage) {
       summary: { stage, errorKind: kind, recoverable: true },
       summary: { stage, errorKind: kind, recoverable: true },
       data: {},
       data: {},
       files: [],
       files: [],
-      nextActions: ['调用 qiwe_subscription_status', '调用 qiwe_subscribe 增购席位'],
+      nextActions: ['调用 qiwei_subscription_status', '调用 qiwei_subscribe 增购席位'],
       warnings: [],
       warnings: [],
       errors: []
       errors: []
     };
     };
@@ -135,11 +135,11 @@ function gatewayErrorResult(error, stage, fallbackMessage) {
   });
   });
 }
 }
 
 
-async function qiweLoginStatus(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiLoginStatus(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
-  const uid = ensureQiweUid(input);
-  const apiBase = readQiweApiBase(input);
+  const uid = ensureQiweiUid(input);
+  const apiBase = readQiweiApiBase(input);
   try {
   try {
     const result = await callFmodeWecomGateway({
     const result = await callFmodeWecomGateway({
       gatewayPath: '/login/status',
       gatewayPath: '/login/status',
@@ -154,8 +154,8 @@ async function qiweLoginStatus(input = {}) {
       assistantMessage: data.configured
       assistantMessage: data.configured
         ? online
         ? online
           ? `企业微信设备在线(uid: ${uid}),可以直接调用业务接口。`
           ? `企业微信设备在线(uid: ${uid}),可以直接调用业务接口。`
-          : `企业微信设备当前不在线(状态 ${data.statusCode ?? '未知'}),请调用 qiwe_login_start 重新登录。`
-        : `当前 uid(${uid})尚未配置企业微信设备,请调用 qiwe_login_start 开始扫码登录。`,
+          : `企业微信设备当前不在线(状态 ${data.statusCode ?? '未知'}),请调用 qiwei_login_start 重新登录。`
+        : `当前 uid(${uid})尚未配置企业微信设备,请调用 qiwei_login_start 开始扫码登录。`,
       summary: {
       summary: {
         configured: Boolean(data.configured),
         configured: Boolean(data.configured),
         online,
         online,
@@ -169,7 +169,7 @@ async function qiweLoginStatus(input = {}) {
         statusCode: data.statusCode ?? null,
         statusCode: data.statusCode ?? null,
         detail: data.detail
         detail: data.detail
       },
       },
-      nextActions: online ? [] : ['调用 qiwe_login_start 开始扫码登录']
+      nextActions: online ? [] : ['调用 qiwei_login_start 开始扫码登录']
     });
     });
   } catch (error) {
   } catch (error) {
     return gatewayErrorResult(error, 'loginStatus', '查询登录状态失败');
     return gatewayErrorResult(error, 'loginStatus', '查询登录状态失败');
@@ -182,30 +182,30 @@ function subscriptionRequiredResult({ apiBase, token, uid, detail }) {
   return {
   return {
     status: 'needs_subscription',
     status: 'needs_subscription',
     assistantMessage: [
     assistantMessage: [
-      `登录前需要先开通企微包月订阅:每个账号 ¥${QIWE_MONTHLY_PRICE}/月,从飞马余额扣费。`,
+      `登录前需要先开通企微包月订阅:每个账号 ¥${QIWEI_MONTHLY_PRICE}/月,从飞马余额扣费。`,
       opened
       opened
         ? `已自动打开套餐选择页:${pagePath},请在页面上选择席位与时长并点击「开通服务」。`
         ? `已自动打开套餐选择页:${pagePath},请在页面上选择席位与时长并点击「开通服务」。`
         : pagePath
         : pagePath
           ? `请打开套餐选择页完成开通:${pagePath}`
           ? `请打开套餐选择页完成开通:${pagePath}`
-          : '套餐页生成失败,可直接调用 qiwe_subscribe 开通。',
-      '开通成功后重新调用 qiwe_login_start 进入扫码登录(第 2 步)。',
+          : '套餐页生成失败,可直接调用 qiwei_subscribe 开通。',
+      '开通成功后重新调用 qiwei_login_start 进入扫码登录(第 2 步)。',
       detail ? `订阅状态:${detail}` : ''
       detail ? `订阅状态:${detail}` : ''
     ].filter(Boolean).join('\n'),
     ].filter(Boolean).join('\n'),
-    summary: { stage: 'loginStart', errorKind: 'subscription_required', uid, subscribePage: pagePath || null, browserOpened: opened, monthlyPrice: QIWE_MONTHLY_PRICE },
+    summary: { stage: 'loginStart', errorKind: 'subscription_required', uid, subscribePage: pagePath || null, browserOpened: opened, monthlyPrice: QIWEI_MONTHLY_PRICE },
     data: { uid, subscribePage: pagePath || null },
     data: { uid, subscribePage: pagePath || null },
     files: pagePath ? [pagePath] : [],
     files: pagePath ? [pagePath] : [],
-    nextActions: ['在套餐页开通订阅(或调用 qiwe_subscribe)', '开通后重新调用 qiwe_login_start'],
+    nextActions: ['在套餐页开通订阅(或调用 qiwei_subscribe)', '开通后重新调用 qiwei_login_start'],
     warnings: [],
     warnings: [],
     errors: []
     errors: []
   };
   };
 }
 }
 
 
-async function qiweLoginStart(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiLoginStart(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
-  const uid = ensureQiweUid(input);
-  const apiBase = readQiweApiBase(input);
-  if (input.persistConfig !== false) saveQiweClientConfig({ uid, apiBase });
+  const uid = ensureQiweiUid(input);
+  const apiBase = readQiweiApiBase(input);
+  if (input.persistConfig !== false) saveQiweiClientConfig({ uid, apiBase });
   if (input.flowUi !== false) {
   if (input.flowUi !== false) {
     try {
     try {
       const { url } = await startLoginFlowServer({
       const { url } = await startLoginFlowServer({
@@ -215,7 +215,7 @@ async function qiweLoginStart(input = {}) {
         port: input.flowPort,
         port: input.flowPort,
         onQrcode: (buffer) => {
         onQrcode: (buffer) => {
           try {
           try {
-            fs.writeFileSync(latestPath('login', 'qiwe-login-qrcode.png'), buffer);
+            fs.writeFileSync(latestPath('login', 'qiwei-login-qrcode.png'), buffer);
           } catch {}
           } catch {}
         }
         }
       });
       });
@@ -223,12 +223,12 @@ async function qiweLoginStart(input = {}) {
       return okResult({
       return okResult({
         assistantMessage: [
         assistantMessage: [
           `已启动企微登录流程页服务:${url}${opened ? '(已自动打开浏览器)' : ''}。`,
           `已启动企微登录流程页服务:${url}${opened ? '(已自动打开浏览器)' : ''}。`,
-          `页面会自动完成两步流程:未开通订阅时先展示套餐页(每号 ¥${QIWE_MONTHLY_PRICE}/月,飞马余额扣费),开通后自动生成二维码并每 3 秒自动检测扫码状态,需要验证码时页面会提示输入。`,
-          '也可以用 qiwe_login_check 在对话中轮询登录状态。'
+          `页面会自动完成两步流程:未开通订阅时先展示套餐页(每号 ¥${QIWEI_MONTHLY_PRICE}/月,飞马余额扣费),开通后自动生成二维码并每 3 秒自动检测扫码状态,需要验证码时页面会提示输入。`,
+          '也可以用 qiwei_login_check 在对话中轮询登录状态。'
         ].join('\n'),
         ].join('\n'),
-        summary: { uid, flowUrl: url, browserOpened: Boolean(opened), monthlyPrice: QIWE_MONTHLY_PRICE },
+        summary: { uid, flowUrl: url, browserOpened: Boolean(opened), monthlyPrice: QIWEI_MONTHLY_PRICE },
         data: { uid, flowUrl: url, browserOpened: Boolean(opened) },
         data: { uid, flowUrl: url, browserOpened: Boolean(opened) },
-        nextActions: ['引导用户在流程页完成开通与扫码', '用 qiwe_login_check 轮询登录状态']
+        nextActions: ['引导用户在流程页完成开通与扫码', '用 qiwei_login_check 轮询登录状态']
       });
       });
     } catch (error) {
     } catch (error) {
       // 流程页服务启动失败时回退到静态二维码流程
       // 流程页服务启动失败时回退到静态二维码流程
@@ -273,8 +273,8 @@ async function qiweLoginStart(input = {}) {
           : filePath
           : filePath
             ? `请打开或展示二维码图片:${filePath}`
             ? `请打开或展示二维码图片:${filePath}`
             : 'Fmode 网关未返回可保存的二维码图片,请重试。',
             : 'Fmode 网关未返回可保存的二维码图片,请重试。',
-        '用户扫码并在手机上确认后,调用 qiwe_login_check 轮询状态。',
-        '如果返回状态 10,请向用户索要 6 位验证码并调用 qiwe_login_verify。'
+        '用户扫码并在手机上确认后,调用 qiwei_login_check 轮询状态。',
+        '如果返回状态 10,请向用户索要 6 位验证码并调用 qiwei_login_verify。'
       ].join('\n'),
       ].join('\n'),
       summary: {
       summary: {
         uid,
         uid,
@@ -290,7 +290,7 @@ async function qiweLoginStart(input = {}) {
         loginQrcodeBase64Data: filePath ? undefined : data.loginQrcodeBase64Data
         loginQrcodeBase64Data: filePath ? undefined : data.loginQrcodeBase64Data
       },
       },
       files: [filePath, htmlPath].filter(Boolean),
       files: [filePath, htmlPath].filter(Boolean),
-      nextActions: ['确认用户看到二维码并扫码', '调用 qiwe_login_check 轮询登录状态']
+      nextActions: ['确认用户看到二维码并扫码', '调用 qiwei_login_check 轮询登录状态']
     });
     });
   } catch (error) {
   } catch (error) {
     if (error && (error.kind === 'billing' || error.httpStatus === 402)) {
     if (error && (error.kind === 'billing' || error.httpStatus === 402)) {
@@ -300,11 +300,11 @@ async function qiweLoginStart(input = {}) {
   }
   }
 }
 }
 
 
-async function qiweLoginCheck(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiLoginCheck(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
-  const uid = ensureQiweUid(input);
-  const apiBase = readQiweApiBase(input);
+  const uid = ensureQiweiUid(input);
+  const apiBase = readQiweiApiBase(input);
   try {
   try {
     const result = await callFmodeWecomGateway({
     const result = await callFmodeWecomGateway({
       gatewayPath: '/login/check',
       gatewayPath: '/login/check',
@@ -333,11 +333,11 @@ async function qiweLoginCheck(input = {}) {
     if (statusCode === '10') {
     if (statusCode === '10') {
       return {
       return {
         status: 'needs_verify_code',
         status: 'needs_verify_code',
-        assistantMessage: '用户已在手机上确认,但需要输入手机端显示的 6 位验证码。请取得验证码后调用 qiwe_login_verify。',
+        assistantMessage: '用户已在手机上确认,但需要输入手机端显示的 6 位验证码。请取得验证码后调用 qiwei_login_verify。',
         summary: { loggedIn: false, uid, statusCode: 10 },
         summary: { loggedIn: false, uid, statusCode: 10 },
         data: { uid, status: detail },
         data: { uid, status: detail },
         files: [],
         files: [],
-        nextActions: ['向用户索要 6 位验证码', '调用 qiwe_login_verify'],
+        nextActions: ['向用户索要 6 位验证码', '调用 qiwei_login_verify'],
         warnings: [],
         warnings: [],
         errors: []
         errors: []
       };
       };
@@ -345,34 +345,34 @@ async function qiweLoginCheck(input = {}) {
     return okResult({
     return okResult({
       assistantMessage: `当前扫码状态:${statusCode}(${statusText})。${
       assistantMessage: `当前扫码状态:${statusCode}(${statusText})。${
         ['-1', '1'].includes(statusCode)
         ['-1', '1'].includes(statusCode)
-          ? '请继续等待用户扫码/确认,3-5 秒后再次调用 qiwe_login_check。'
+          ? '请继续等待用户扫码/确认,3-5 秒后再次调用 qiwei_login_check。'
           : statusCode === '4'
           : statusCode === '4'
-            ? '用户取消了登录,可重新调用 qiwe_login_start。'
+            ? '用户取消了登录,可重新调用 qiwei_login_start。'
             : statusCode === '0'
             : statusCode === '0'
-              ? '可再次调用 qiwe_login_check 并传 manual=true 尝试免扫码登录。'
+              ? '可再次调用 qiwei_login_check 并传 manual=true 尝试免扫码登录。'
               : ''
               : ''
       }`,
       }`,
       summary: { loggedIn: false, uid, statusCode: Number(statusCode) },
       summary: { loggedIn: false, uid, statusCode: Number(statusCode) },
       data: { uid, status: detail },
       data: { uid, status: detail },
       nextActions:
       nextActions:
         statusCode === '4'
         statusCode === '4'
-          ? ['重新调用 qiwe_login_start']
+          ? ['重新调用 qiwei_login_start']
           : statusCode === '0'
           : statusCode === '0'
-            ? ['调用 qiwe_login_check,传 manual=true']
-            : ['稍后再次调用 qiwe_login_check']
+            ? ['调用 qiwei_login_check,传 manual=true']
+            : ['稍后再次调用 qiwei_login_check']
     });
     });
   } catch (error) {
   } catch (error) {
     return gatewayErrorResult(error, 'loginCheck', '检测扫码状态失败');
     return gatewayErrorResult(error, 'loginCheck', '检测扫码状态失败');
   }
   }
 }
 }
 
 
-async function qiweLoginVerify(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiLoginVerify(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
-  const uid = ensureQiweUid(input);
+  const uid = ensureQiweiUid(input);
   const code = String(input.code || '').trim();
   const code = String(input.code || '').trim();
   if (!/^\d{6}$/.test(code)) return errorResult('请提供手机端显示的 6 位数字验证码(入参 code)。');
   if (!/^\d{6}$/.test(code)) return errorResult('请提供手机端显示的 6 位数字验证码(入参 code)。');
-  const apiBase = readQiweApiBase(input);
+  const apiBase = readQiweiApiBase(input);
   try {
   try {
     await callFmodeWecomGateway({
     await callFmodeWecomGateway({
       gatewayPath: '/login/verify',
       gatewayPath: '/login/verify',
@@ -381,9 +381,9 @@ async function qiweLoginVerify(input = {}) {
       apiBase
       apiBase
     });
     });
     return okResult({
     return okResult({
-      assistantMessage: '验证码已提交。请调用 qiwe_login_check 再次确认登录状态。',
+      assistantMessage: '验证码已提交。请调用 qiwei_login_check 再次确认登录状态。',
       summary: { verified: true, uid },
       summary: { verified: true, uid },
-      nextActions: ['调用 qiwe_login_check 确认登录状态']
+      nextActions: ['调用 qiwei_login_check 确认登录状态']
     });
     });
   } catch (error) {
   } catch (error) {
     return gatewayErrorResult(error, 'loginVerify', '验证码校验失败');
     return gatewayErrorResult(error, 'loginVerify', '验证码校验失败');
@@ -391,8 +391,8 @@ async function qiweLoginVerify(input = {}) {
 }
 }
 
 
 module.exports = {
 module.exports = {
-  qiweLoginStatus,
-  qiweLoginStart,
-  qiweLoginCheck,
-  qiweLoginVerify
+  qiweiLoginStatus,
+  qiweiLoginStart,
+  qiweiLoginCheck,
+  qiweiLoginVerify
 };
 };

+ 19 - 19
claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwe-subscription-run.js → claude-code/claude-code-qiwe-assistant/mcp/src/tools/qiwei-subscription-run.js

@@ -1,11 +1,11 @@
-const { readQiweAuthToken, readQiweApiBase } = require('../core/credentials');
+const { readQiweiAuthToken, readQiweiApiBase } = require('../core/credentials');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
 const { okResult, errorResult } = require('../core/result-envelope');
 const { okResult, errorResult } = require('../core/result-envelope');
 
 
 function authRequiredResult() {
 function authRequiredResult() {
   return {
   return {
     status: 'needs_auth',
     status: 'needs_auth',
-    assistantMessage: '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。请配置 QIWE_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken 后重试。',
+    assistantMessage: '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。请配置 QIWEI_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken 后重试。',
     summary: { errorKind: 'missing_auth_token', recoverable: true },
     summary: { errorKind: 'missing_auth_token', recoverable: true },
     data: {},
     data: {},
     files: [],
     files: [],
@@ -22,11 +22,11 @@ function subscriptionError(error, stage) {
   if (kind === 'billing') {
   if (kind === 'billing') {
     return {
     return {
       status: 'needs_recharge',
       status: 'needs_recharge',
-      assistantMessage: `飞马余额不足或订阅扣费失败(${safeMessage})。请充值后重新调用 qiwe_subscribe。`,
+      assistantMessage: `飞马余额不足或订阅扣费失败(${safeMessage})。请充值后重新调用 qiwei_subscribe。`,
       summary: { stage, errorKind: kind, recoverable: true },
       summary: { stage, errorKind: kind, recoverable: true },
       data: {},
       data: {},
       files: [],
       files: [],
-      nextActions: ['充值飞马余额', '重新调用 qiwe_subscribe'],
+      nextActions: ['充值飞马余额', '重新调用 qiwei_subscribe'],
       warnings: [],
       warnings: [],
       errors: []
       errors: []
     };
     };
@@ -37,21 +37,21 @@ function subscriptionError(error, stage) {
   });
   });
 }
 }
 
 
-async function qiweSubscriptionStatus(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiSubscriptionStatus(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
   try {
   try {
     const result = await callFmodeWecomGateway({
     const result = await callFmodeWecomGateway({
       gatewayPath: '/subscribe/status',
       gatewayPath: '/subscribe/status',
       httpMethod: 'GET',
       httpMethod: 'GET',
       token,
       token,
-      apiBase: readQiweApiBase(input)
+      apiBase: readQiweiApiBase(input)
     });
     });
     const data = result.data || {};
     const data = result.data || {};
     return okResult({
     return okResult({
       assistantMessage: data.subscribed
       assistantMessage: data.subscribed
         ? `企微包月订阅有效:${data.usedSeats ?? 0}/${data.seats ?? 0} 个席位已使用,到期时间 ${data.expireAt || '未知'}。`
         ? `企微包月订阅有效:${data.usedSeats ?? 0}/${data.seats ?? 0} 个席位已使用,到期时间 ${data.expireAt || '未知'}。`
-        : '企微包月订阅尚未开通或已到期,请调用 qiwe_subscribe 开通或续费。',
+        : '企微包月订阅尚未开通或已到期,请调用 qiwei_subscribe 开通或续费。',
       summary: {
       summary: {
         subscribed: Boolean(data.subscribed),
         subscribed: Boolean(data.subscribed),
         seats: data.seats ?? null,
         seats: data.seats ?? null,
@@ -61,15 +61,15 @@ async function qiweSubscriptionStatus(input = {}) {
         balance: data.balance
         balance: data.balance
       },
       },
       data,
       data,
-      nextActions: data.subscribed ? [] : ['调用 qiwe_subscribe 开通或续费']
+      nextActions: data.subscribed ? [] : ['调用 qiwei_subscribe 开通或续费']
     });
     });
   } catch (error) {
   } catch (error) {
     return subscriptionError(error, 'subscriptionStatus');
     return subscriptionError(error, 'subscriptionStatus');
   }
   }
 }
 }
 
 
-async function qiweSubscribe(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiSubscribe(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
   const seats = input.seats === undefined ? undefined : Number(input.seats);
   const seats = input.seats === undefined ? undefined : Number(input.seats);
   if (seats !== undefined && (!Number.isInteger(seats) || seats < 1)) {
   if (seats !== undefined && (!Number.isInteger(seats) || seats < 1)) {
@@ -80,7 +80,7 @@ async function qiweSubscribe(input = {}) {
       gatewayPath: '/subscribe',
       gatewayPath: '/subscribe',
       body: seats === undefined ? {} : { seats },
       body: seats === undefined ? {} : { seats },
       token,
       token,
-      apiBase: readQiweApiBase(input)
+      apiBase: readQiweiApiBase(input)
     });
     });
     const data = result.data || {};
     const data = result.data || {};
     return okResult({
     return okResult({
@@ -92,15 +92,15 @@ async function qiweSubscribe(input = {}) {
         autoRenew: data.autoRenew ?? null
         autoRenew: data.autoRenew ?? null
       },
       },
       data,
       data,
-      nextActions: ['调用 qiwe_login_start 添加或重新登录企业微信账号']
+      nextActions: ['调用 qiwei_login_start 添加或重新登录企业微信账号']
     });
     });
   } catch (error) {
   } catch (error) {
     return subscriptionError(error, 'subscribe');
     return subscriptionError(error, 'subscribe');
   }
   }
 }
 }
 
 
-async function qiweSubscriptionAutoRenew(input = {}) {
-  const token = readQiweAuthToken(input);
+async function qiweiSubscriptionAutoRenew(input = {}) {
+  const token = readQiweiAuthToken(input);
   if (!token) return authRequiredResult();
   if (!token) return authRequiredResult();
   if (typeof input.autoRenew !== 'boolean') return errorResult('autoRenew 必须是 boolean。');
   if (typeof input.autoRenew !== 'boolean') return errorResult('autoRenew 必须是 boolean。');
   try {
   try {
@@ -108,7 +108,7 @@ async function qiweSubscriptionAutoRenew(input = {}) {
       gatewayPath: '/subscribe/autoRenew',
       gatewayPath: '/subscribe/autoRenew',
       body: { autoRenew: input.autoRenew },
       body: { autoRenew: input.autoRenew },
       token,
       token,
-      apiBase: readQiweApiBase(input)
+      apiBase: readQiweiApiBase(input)
     });
     });
     const data = result.data || {};
     const data = result.data || {};
     return okResult({
     return okResult({
@@ -122,7 +122,7 @@ async function qiweSubscriptionAutoRenew(input = {}) {
 }
 }
 
 
 module.exports = {
 module.exports = {
-  qiweSubscriptionStatus,
-  qiweSubscribe,
-  qiweSubscriptionAutoRenew
+  qiweiSubscriptionStatus,
+  qiweiSubscribe,
+  qiweiSubscriptionAutoRenew
 };
 };

+ 8 - 8
claude-code/claude-code-qiwe-assistant/mcp/src/tools/wecom-official-cli-run.js

@@ -1,13 +1,13 @@
 const {
 const {
-  qiweOfficialStatus,
-  qiweOfficialPrepare,
-  qiweOfficialHelp,
-  qiweOfficialCall
+  qiweiOfficialStatus,
+  qiweiOfficialPrepare,
+  qiweiOfficialHelp,
+  qiweiOfficialCall
 } = require('../providers/wecom-official-cli');
 } = require('../providers/wecom-official-cli');
 
 
 module.exports = {
 module.exports = {
-  qiweOfficialStatus,
-  qiweOfficialPrepare,
-  qiweOfficialHelp,
-  qiweOfficialCall
+  qiweiOfficialStatus,
+  qiweiOfficialPrepare,
+  qiweiOfficialHelp,
+  qiweiOfficialCall
 };
 };

+ 2 - 2
claude-code/claude-code-qiwe-assistant/package-lock.json

@@ -1,11 +1,11 @@
 {
 {
-  "name": "claude-code-qiwe-assistant",
+  "name": "claude-code-qiwei-assistant",
   "version": "0.3.0",
   "version": "0.3.0",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
-      "name": "claude-code-qiwe-assistant",
+      "name": "claude-code-qiwei-assistant",
       "version": "0.3.0",
       "version": "0.3.0",
       "dependencies": {
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.12.1",
         "@modelcontextprotocol/sdk": "^1.12.1",

+ 4 - 4
claude-code/claude-code-qiwe-assistant/package.json

@@ -1,16 +1,16 @@
 {
 {
-  "name": "claude-code-qiwe-assistant",
+  "name": "claude-code-qiwei-assistant",
   "version": "0.3.0",
   "version": "0.3.0",
   "description": "企业微信技能包:保留 Fmode 网关转发的企业微信接口,并增加按需下载的官方 CLI 会议与文档能力",
   "description": "企业微信技能包:保留 Fmode 网关转发的企业微信接口,并增加按需下载的官方 CLI 会议与文档能力",
   "main": "mcp/src/server.js",
   "main": "mcp/src/server.js",
   "scripts": {
   "scripts": {
-    "check": "node --check mcp/src/server.js && node --check mcp/src/core/api-catalog.js && node --check mcp/src/core/credentials.js && node --check mcp/src/core/result-envelope.js && node --check mcp/src/core/output-paths.js && node --check mcp/src/core/subscribe-page.js && node --check mcp/src/core/login-flow-server.js && node --check mcp/src/core/wecom-cli-runtime.js && node --check mcp/src/providers/fmode-wecom-gateway.js && node --check mcp/src/providers/wecom-official-cli.js && node --check mcp/src/tools/qiwe-api-catalog-run.js && node --check mcp/src/tools/qiwe-login-run.js && node --check mcp/src/tools/qiwe-subscription-run.js && node --check mcp/src/tools/wecom-official-cli-run.js && node --check bin/qiwe-official-cli.js && node --check scripts/install-wecom-cli.js && node --check scripts/smoke-test.js && node --check scripts/wecom-cli-smoke-test.js && node --check scripts/validate-output-standard.js",
+    "check": "node --check mcp/src/server.js && node --check mcp/src/core/api-catalog.js && node --check mcp/src/core/credentials.js && node --check mcp/src/core/result-envelope.js && node --check mcp/src/core/output-paths.js && node --check mcp/src/core/subscribe-page.js && node --check mcp/src/core/login-flow-server.js && node --check mcp/src/core/wecom-cli-runtime.js && node --check mcp/src/providers/fmode-wecom-gateway.js && node --check mcp/src/providers/wecom-official-cli.js && node --check mcp/src/tools/qiwei-api-catalog-run.js && node --check mcp/src/tools/qiwei-login-run.js && node --check mcp/src/tools/qiwei-subscription-run.js && node --check mcp/src/tools/wecom-official-cli-run.js && node --check bin/qiwei-official-cli.js && node --check scripts/install-wecom-cli.js && node --check scripts/smoke-test.js && node --check scripts/wecom-cli-smoke-test.js && node --check scripts/validate-output-standard.js",
     "start": "node mcp/src/server.js",
     "start": "node mcp/src/server.js",
     "smoke": "node scripts/smoke-test.js && node scripts/wecom-cli-smoke-test.js",
     "smoke": "node scripts/smoke-test.js && node scripts/wecom-cli-smoke-test.js",
     "outputs:validate": "node scripts/validate-output-standard.js",
     "outputs:validate": "node scripts/validate-output-standard.js",
     "wecom:install": "node scripts/install-wecom-cli.js",
     "wecom:install": "node scripts/install-wecom-cli.js",
-    "wecom:status": "node bin/qiwe-official-cli.js status",
-    "wecom:init": "node bin/qiwe-official-cli.js init"
+    "wecom:status": "node bin/qiwei-official-cli.js status",
+    "wecom:init": "node bin/qiwei-official-cli.js init"
   },
   },
   "dependencies": {
   "dependencies": {
     "@modelcontextprotocol/sdk": "^1.12.1",
     "@modelcontextprotocol/sdk": "^1.12.1",

+ 7 - 7
claude-code/claude-code-qiwe-assistant/scripts/build-catalog.py

@@ -2,10 +2,10 @@
 """Build the enterprise WeChat endpoint catalog from markdown OpenAPI docs."""
 """Build the enterprise WeChat endpoint catalog from markdown OpenAPI docs."""
 import re, yaml, json, html, os, sys, glob
 import re, yaml, json, html, os, sys, glob
 
 
-DOCS = sys.argv[1] if len(sys.argv) > 1 else os.environ.get('QIWE_DOCS_DIR')
-OUT = sys.argv[2] if len(sys.argv) > 2 else os.path.join(os.path.dirname(__file__), '..', 'mcp', 'catalog', 'qiwe-endpoints.json')
+DOCS = sys.argv[1] if len(sys.argv) > 1 else os.environ.get('QIWEI_DOCS_DIR')
+OUT = sys.argv[2] if len(sys.argv) > 2 else os.path.join(os.path.dirname(__file__), '..', 'mcp', 'catalog', 'qiwei-endpoints.json')
 if not DOCS:
 if not DOCS:
-    raise SystemExit('请通过第一个参数或 QIWE_DOCS_DIR 指定接口文档目录')
+    raise SystemExit('请通过第一个参数或 QIWEI_DOCS_DIR 指定接口文档目录')
 
 
 def strip_html(s):
 def strip_html(s):
     if not s: return ''
     if not s: return ''
@@ -55,7 +55,7 @@ for path in sorted(glob.glob(os.path.join(DOCS, '*.md'))):
     try:
     try:
         spec = yaml.safe_load(m.group(1))
         spec = yaml.safe_load(m.group(1))
         paths = spec['paths']
         paths = spec['paths']
-        api_path = next(p for p in ('/api/qw/doApi', '/qiwe/api/qw/doApi', '/api/qw/doFileApi') if p in paths)
+        api_path = next(p for p in ('/api/qw/doApi', '/qiwei/api/qw/doApi', '/api/qw/doFileApi') if p in paths)
         post = paths[api_path]['post']
         post = paths[api_path]['post']
     except Exception:
     except Exception:
         skipped.append(fname); continue
         skipped.append(fname); continue
@@ -111,7 +111,7 @@ MANUAL_ENDPOINTS = [
         'id': 'cloud.bigFileDownloadAsync',
         'id': 'cloud.bigFileDownloadAsync',
         'module': 'cloud',
         'module': 'cloud',
         'title': '企微大文件异步下载',
         'title': '企微大文件异步下载',
-        'summary': '文件大于 20M 使用此接口;下载地址为临时云资源(7–15 天不定期清理);异步结果通过回调通知,根据 requestId 匹配。当前未登记 method 路由值,qiwe_api_call 暂不可调用。',
+        'summary': '文件大于 20M 使用此接口;下载地址为临时云资源(7–15 天不定期清理);异步结果通过回调通知,根据 requestId 匹配。当前未登记 method 路由值,qiwei_api_call 暂不可调用。',
         'method': None,
         'method': None,
         'category': ['API参考', '媒体与运营', '文件与媒体(下载/上传)'],
         'category': ['API参考', '媒体与运营', '文件与媒体(下载/上传)'],
         'docFile': '企微大文件异步下载.md',
         'docFile': '企微大文件异步下载.md',
@@ -141,11 +141,11 @@ catalog = {
     'updatedAt': '2026-07-10',
     'updatedAt': '2026-07-10',
     'source': '企业微信开放接口资料',
     'source': '企业微信开放接口资料',
     'gateway': {
     'gateway': {
-        'baseUrl': 'https://server.fmode.cn/api/qiwe',
+        'baseUrl': 'https://server.fmode.cn/api/qiwei',
         'endpoint': 'POST {baseUrl}/doApi',
         'endpoint': 'POST {baseUrl}/doApi',
         'auth': 'Authorization: Bearer <Fmode token>',
         'auth': 'Authorization: Bearer <Fmode token>',
         'envelope': 'Fmode 网关请求信封:{"uid": "<本地稳定设备标识>", "method": "<接口 method 路径>", "params": {...}};企业微信接口访问凭据和设备上下文由 Fmode 网关管理。',
         'envelope': 'Fmode 网关请求信封:{"uid": "<本地稳定设备标识>", "method": "<接口 method 路径>", "params": {...}};企业微信接口访问凭据和设备上下文由 Fmode 网关管理。',
-        'note': '所有业务请求均通过 Fmode 网关转发的企业微信接口执行。清单未登记的普通 JSON 接口可用 qiwe_api_call 传 rawMethod + params;登录和设备接口必须使用 qiwe_login_* 专用工具。'
+        'note': '所有业务请求均通过 Fmode 网关转发的企业微信接口执行。清单未登记的普通 JSON 接口可用 qiwei_api_call 传 rawMethod + params;登录和设备接口必须使用 qiwei_login_* 专用工具。'
     },
     },
     'modules': sorted({ep['module'] for ep in endpoints}),
     'modules': sorted({ep['module'] for ep in endpoints}),
     'endpoints': endpoints,
     'endpoints': endpoints,

+ 25 - 25
claude-code/claude-code-qiwe-assistant/scripts/smoke-test.js

@@ -2,18 +2,18 @@
 const assert = require('assert');
 const assert = require('assert');
 const http = require('http');
 const http = require('http');
 const { loadCatalog, searchEndpoints, findEndpoint } = require('../mcp/src/core/api-catalog');
 const { loadCatalog, searchEndpoints, findEndpoint } = require('../mcp/src/core/api-catalog');
-const { searchQiweApis, getQiweApiDoc, callQiweApi } = require('../mcp/src/tools/qiwe-api-catalog-run');
+const { searchQiweiApis, getQiweiApiDoc, callQiweiApi } = require('../mcp/src/tools/qiwei-api-catalog-run');
 const {
 const {
-  qiweLoginStatus,
-  qiweLoginStart,
-  qiweLoginCheck,
-  qiweLoginVerify
-} = require('../mcp/src/tools/qiwe-login-run');
+  qiweiLoginStatus,
+  qiweiLoginStart,
+  qiweiLoginCheck,
+  qiweiLoginVerify
+} = require('../mcp/src/tools/qiwei-login-run');
 const {
 const {
-  qiweSubscriptionStatus,
-  qiweSubscribe,
-  qiweSubscriptionAutoRenew
-} = require('../mcp/src/tools/qiwe-subscription-run');
+  qiweiSubscriptionStatus,
+  qiweiSubscribe,
+  qiweiSubscriptionAutoRenew
+} = require('../mcp/src/tools/qiwei-subscription-run');
 
 
 const PRIVATE_PROVIDER_NAME = ['private', 'provider'].join('-');
 const PRIVATE_PROVIDER_NAME = ['private', 'provider'].join('-');
 const PRIVATE_PROVIDER_HOST = ['private', 'provider', 'invalid'].join('.');
 const PRIVATE_PROVIDER_HOST = ['private', 'provider', 'invalid'].join('.');
@@ -106,7 +106,7 @@ async function startMockGateway() {
 async function main() {
 async function main() {
   const catalog = loadCatalog();
   const catalog = loadCatalog();
   assert(catalog.endpoints.length >= 100, 'catalog should contain 100+ endpoints');
   assert(catalog.endpoints.length >= 100, 'catalog should contain 100+ endpoints');
-  assert(catalog.gateway.baseUrl.includes('server.fmode.cn/api/qiwe'));
+  assert(catalog.gateway.baseUrl.includes('server.fmode.cn/api/qiwei'));
   assert(catalog.endpoints.every(endpoint => !Object.hasOwn(endpoint, 'docUrl')));
   assert(catalog.endpoints.every(endpoint => !Object.hasOwn(endpoint, 'docUrl')));
   assertProviderHidden(catalog);
   assertProviderHidden(catalog);
   console.log(`[ok] catalog loaded: ${catalog.endpoints.length} endpoints`);
   console.log(`[ok] catalog loaded: ${catalog.endpoints.length} endpoints`);
@@ -114,10 +114,10 @@ async function main() {
   const hits = searchEndpoints({ query: '发送 文本' });
   const hits = searchEndpoints({ query: '发送 文本' });
   assert(hits.length > 0 && hits[0].module === 'msg');
   assert(hits.length > 0 && hits[0].module === 'msg');
   assert(findEndpoint('/login/getLoginQrcode'));
   assert(findEndpoint('/login/getLoginQrcode'));
-  const searchResult = await searchQiweApis({ query: '朋友圈' });
+  const searchResult = await searchQiweiApis({ query: '朋友圈' });
   assert(searchResult.status === 'ok' && searchResult.data.endpoints.length > 0);
   assert(searchResult.status === 'ok' && searchResult.data.endpoints.length > 0);
   assertProviderHidden(searchResult);
   assertProviderHidden(searchResult);
-  const doc = await getQiweApiDoc({ id: 'user.getProfile' });
+  const doc = await getQiweiApiDoc({ id: 'user.getProfile' });
   assert(doc.status === 'ok' && doc.data.callTemplate);
   assert(doc.status === 'ok' && doc.data.callTemplate);
   assertProviderHidden(doc);
   assertProviderHidden(doc);
   assert(!JSON.stringify(doc).includes('"guid"'));
   assert(!JSON.stringify(doc).includes('"guid"'));
@@ -126,52 +126,52 @@ async function main() {
   const gateway = await startMockGateway();
   const gateway = await startMockGateway();
   const common = {
   const common = {
     authToken: 'sk-test-relay-token',
     authToken: 'sk-test-relay-token',
-    uid: 'qiwe-smoke-device',
+    uid: 'qiwei-smoke-device',
     apiBase: gateway.apiBase,
     apiBase: gateway.apiBase,
     persistConfig: false
     persistConfig: false
   };
   };
   try {
   try {
-    const call = await callQiweApi({ ...common, id: 'user.getProfile', params: {} });
+    const call = await callQiweiApi({ ...common, id: 'user.getProfile', params: {} });
     assert.strictEqual(call.status, 'ok');
     assert.strictEqual(call.status, 'ok');
     const apiRequest = gateway.requests.at(-1);
     const apiRequest = gateway.requests.at(-1);
     assert.strictEqual(apiRequest.path, '/doApi');
     assert.strictEqual(apiRequest.path, '/doApi');
     assert.strictEqual(apiRequest.authorization, 'Bearer sk-test-relay-token');
     assert.strictEqual(apiRequest.authorization, 'Bearer sk-test-relay-token');
     assert.deepStrictEqual(apiRequest.body, {
     assert.deepStrictEqual(apiRequest.body, {
-      uid: 'qiwe-smoke-device',
+      uid: 'qiwei-smoke-device',
       method: '/user/getProfile',
       method: '/user/getProfile',
       params: {}
       params: {}
     });
     });
     assert(!JSON.stringify(apiRequest.body).includes('tokenId'));
     assert(!JSON.stringify(apiRequest.body).includes('tokenId'));
     assert(!JSON.stringify(apiRequest.body).includes('guid'));
     assert(!JSON.stringify(apiRequest.body).includes('guid'));
     assertProviderHidden(call);
     assertProviderHidden(call);
-    console.log('[ok] qiwe_api_call uses the Fmode gateway envelope without private credentials');
+    console.log('[ok] qiwei_api_call uses the Fmode gateway envelope without private credentials');
 
 
-    const blocked = await callQiweApi({ ...common, rawMethod: '/login/manualLogin', params: {} });
+    const blocked = await callQiweiApi({ ...common, rawMethod: '/login/manualLogin', params: {} });
     assert.strictEqual(blocked.status, 'error');
     assert.strictEqual(blocked.status, 'error');
     assert.strictEqual(gateway.requests.at(-1), apiRequest);
     assert.strictEqual(gateway.requests.at(-1), apiRequest);
 
 
-    const subscription = await qiweSubscriptionStatus(common);
+    const subscription = await qiweiSubscriptionStatus(common);
     assert.strictEqual(subscription.status, 'ok');
     assert.strictEqual(subscription.status, 'ok');
     assert.strictEqual(subscription.summary.subscribed, true);
     assert.strictEqual(subscription.summary.subscribed, true);
-    const subscribed = await qiweSubscribe({ ...common, seats: 3 });
+    const subscribed = await qiweiSubscribe({ ...common, seats: 3 });
     assert.strictEqual(subscribed.status, 'ok');
     assert.strictEqual(subscribed.status, 'ok');
     assert.strictEqual(gateway.requests.at(-1).body.seats, 3);
     assert.strictEqual(gateway.requests.at(-1).body.seats, 3);
-    const autoRenew = await qiweSubscriptionAutoRenew({ ...common, autoRenew: false });
+    const autoRenew = await qiweiSubscriptionAutoRenew({ ...common, autoRenew: false });
     assert.strictEqual(autoRenew.status, 'ok');
     assert.strictEqual(autoRenew.status, 'ok');
     assert.strictEqual(gateway.requests.at(-1).body.autoRenew, false);
     assert.strictEqual(gateway.requests.at(-1).body.autoRenew, false);
     console.log('[ok] subscription tools use Fmode gateway routes');
     console.log('[ok] subscription tools use Fmode gateway routes');
 
 
-    const status = await qiweLoginStatus(common);
+    const status = await qiweiLoginStatus(common);
     assert.strictEqual(status.status, 'ok');
     assert.strictEqual(status.status, 'ok');
     assert.strictEqual(gateway.requests.at(-1).query.uid, common.uid);
     assert.strictEqual(gateway.requests.at(-1).query.uid, common.uid);
-    const start = await qiweLoginStart({ ...common, openBrowser: false });
+    const start = await qiweiLoginStart({ ...common, openBrowser: false });
     assert.strictEqual(start.status, 'ok');
     assert.strictEqual(start.status, 'ok');
     assertProviderHidden(start);
     assertProviderHidden(start);
-    const check = await qiweLoginCheck(common);
+    const check = await qiweiLoginCheck(common);
     assert.strictEqual(check.status, 'ok');
     assert.strictEqual(check.status, 'ok');
     assert.strictEqual(check.summary.loggedIn, true);
     assert.strictEqual(check.summary.loggedIn, true);
     assertProviderHidden(check);
     assertProviderHidden(check);
-    const verify = await qiweLoginVerify({ ...common, code: '123456' });
+    const verify = await qiweiLoginVerify({ ...common, code: '123456' });
     assert.strictEqual(verify.status, 'ok');
     assert.strictEqual(verify.status, 'ok');
     assert.deepStrictEqual(gateway.requests.at(-1).body, { uid: common.uid, code: '123456' });
     assert.deepStrictEqual(gateway.requests.at(-1).body, { uid: common.uid, code: '123456' });
     console.log('[ok] login tools use uid-based Fmode gateway routes and hide internal identifiers');
     console.log('[ok] login tools use uid-based Fmode gateway routes and hide internal identifiers');

+ 5 - 5
claude-code/claude-code-qiwe-assistant/scripts/wecom-cli-smoke-test.js

@@ -5,10 +5,10 @@ const os = require('os');
 const path = require('path');
 const path = require('path');
 
 
 async function main() {
 async function main() {
-  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwe-official-cli-test-'));
+  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-official-cli-test-'));
   const runtimeRoot = path.join(tempRoot, 'runtime');
   const runtimeRoot = path.join(tempRoot, 'runtime');
   const configDir = path.join(tempRoot, 'config');
   const configDir = path.join(tempRoot, 'config');
-  process.env.QIWE_OFFICIAL_CLI_RUNTIME_DIR = runtimeRoot;
+  process.env.QIWEI_OFFICIAL_CLI_RUNTIME_DIR = runtimeRoot;
   process.env.WECOM_CLI_CONFIG_DIR = configDir;
   process.env.WECOM_CLI_CONFIG_DIR = configDir;
 
 
   const runtime = require('../mcp/src/core/wecom-cli-runtime');
   const runtime = require('../mcp/src/core/wecom-cli-runtime');
@@ -47,11 +47,11 @@ async function main() {
     assert.strictEqual(readyStatus.authorized, true);
     assert.strictEqual(readyStatus.authorized, true);
     assert.strictEqual(readyStatus.ready, true);
     assert.strictEqual(readyStatus.ready, true);
 
 
-    const help = await provider.qiweOfficialHelp({ category: 'meeting', method: 'create_meeting' });
+    const help = await provider.qiweiOfficialHelp({ category: 'meeting', method: 'create_meeting' });
     assert.strictEqual(help.status, 'ok');
     assert.strictEqual(help.status, 'ok');
     assert.strictEqual(help.data.help, 'help:meeting.create_meeting');
     assert.strictEqual(help.data.help, 'help:meeting.create_meeting');
 
 
-    const call = await provider.qiweOfficialCall({
+    const call = await provider.qiweiOfficialCall({
       category: 'doc',
       category: 'doc',
       method: 'create_doc',
       method: 'create_doc',
       args: { doc_type: 3, doc_name: '测试文档; echo unsafe' }
       args: { doc_type: 3, doc_name: '测试文档; echo unsafe' }
@@ -65,7 +65,7 @@ async function main() {
     assert(!Object.hasOwn(call.data.response, 'providerName'));
     assert(!Object.hasOwn(call.data.response, 'providerName'));
     assert.strictEqual(call.data.response.doc_url, 'https://docs.example.test/document/1');
     assert.strictEqual(call.data.response.doc_url, 'https://docs.example.test/document/1');
 
 
-    const invalidMethod = await provider.qiweOfficialCall({
+    const invalidMethod = await provider.qiweiOfficialCall({
       category: 'doc',
       category: 'doc',
       method: 'create_doc; echo unsafe',
       method: 'create_doc; echo unsafe',
       args: {}
       args: {}

+ 0 - 42
claude-code/claude-code-qiwe-assistant/skills/qiwe-login/SKILL.md

@@ -1,42 +0,0 @@
----
-name: qiwe-login
-description: 通过 Fmode 网关转发的企业微信接口完成扫码登录和订阅管理。适用于首次使用、设备掉线、qiwe_api_call 返回 needs_login、订阅未开通或席位不足;客户端只使用 Fmode 鉴权 token 和 uid,企业微信接口访问凭据由 Fmode 网关管理。
----
-
-# 企业微信扫码登录
-
-## 严格流程
-
-1. **检查订阅**:调用 `qiwe_subscription_status`。
-   - 未订阅或已到期:调用 `qiwe_subscribe`;
-   - 席位已满:用 `qiwe_subscribe` 传新的总 `seats` 数增购;
-   - 余额不足:引导用户充值飞马余额后重试。
-2. **检查设备**:调用 `qiwe_login_status`。
-   - `online=true`:无需重新登录;
-   - 未配置或离线:继续下一步。
-3. **生成二维码**:调用 `qiwe_login_start`。
-   - 工具通过 `/api/qiwe/login/start` 请求 Fmode 网关;
-   - 二维码保存到 `outputs/qiwe-login-qrcode.png`,默认打开 HTML 预览;
-   - 把二维码展示给用户,用手机企业微信扫码并确认。
-4. **轮询状态**:每 3–5 秒调用 `qiwe_login_check`。
-   - `-1 / 1`:继续等待;
-   - `0`:可传 `manual: true` 尝试免扫码登录;
-   - `2`:登录成功;
-   - `4`:用户取消,重新调用 `qiwe_login_start`;
-   - `10`:进入验证码流程。
-5. **验证码**:状态 `10` 时向用户索要手机端显示的 6 位验证码,调用 `qiwe_login_verify`,然后再次检查状态。
-6. **验证业务链路**:登录成功后调用 `qiwe_api_call`(例如 `user.getProfile`)。
-
-## 配置
-
-- `QIWE_API_BASE` 默认 `https://server.fmode.cn/api/qiwe`;
-- Fmode token 自动从 Claude Code / Fmode 配置读取,也可显式设置 `QIWE_AUTH_TOKEN`;
-- `QIWE_UID` 未配置时自动生成并持久化;
-- 不读取、保存或要求用户提供企业微信接口访问凭据。
-
-## 安全约定
-
-- 不展示或记录 Authorization 和 Fmode token;
-- 不向用户暴露接口服务提供方名称、相关域名或控制台;
-- 不绕过 Fmode 网关直连内部服务;
-- 不通过 `qiwe_api_call` 调用 `/login/*` 或 `/client/*`。

+ 8 - 8
claude-code/claude-code-qiwe-assistant/skills/qiwe-api-catalog/SKILL.md → claude-code/claude-code-qiwe-assistant/skills/qiwei-api-catalog/SKILL.md

@@ -1,6 +1,6 @@
 ---
 ---
-name: qiwe-api-catalog
-description: 通过 Fmode 网关转发的企业微信接口调用消息、群管理、联系人、朋友圈、标签、会话、账号和文件等 100+ 个能力;先查清单、读文档,再用 qiwe_api_call 调用。企业微信接口访问凭据由 Fmode 网关管理。
+name: qiwei-api-catalog
+description: 通过 Fmode 网关转发的企业微信接口调用消息、群管理、联系人、朋友圈、标签、会话、账号和文件等 100+ 个能力;先查清单、读文档,再用 qiwei_api_call 调用。企业微信接口访问凭据由 Fmode 网关管理。
 ---
 ---
 
 
 # 企业微信接口清单驱动调用
 # 企业微信接口清单驱动调用
@@ -38,11 +38,11 @@ Fmode 网关负责:
 
 
 ## 标准流程
 ## 标准流程
 
 
-1. 用 `qiwe_api_search` 按关键词、module 或 tag 找接口;
-2. 用 `qiwe_api_doc` 核对必填参数和返回字段;
-3. 用 `qiwe_api_call` 传 `id + params`;
-4. 收到订阅错误时调用 `qiwe_subscription_status`,必要时用 `qiwe_subscribe`;
-5. 收到登录态错误时调用 `qiwe_login_status`,必要时转 `qiwe-login` 技能重新扫码。
+1. 用 `qiwei_api_search` 按关键词、module 或 tag 找接口;
+2. 用 `qiwei_api_doc` 核对必填参数和返回字段;
+3. 用 `qiwei_api_call` 传 `id + params`;
+4. 收到订阅错误时调用 `qiwei_subscription_status`,必要时用 `qiwei_subscribe`;
+5. 收到登录态错误时调用 `qiwei_login_status`,必要时转 `qiwei-login` 技能重新扫码。
 
 
 清单里没有的普通 JSON 接口可以传 `rawMethod + params`。`/login/*`、`/client/*` 必须走登录专用工具,不能透传。
 清单里没有的普通 JSON 接口可以传 `rawMethod + params`。`/login/*`、`/client/*` 必须走登录专用工具,不能透传。
 
 
@@ -56,7 +56,7 @@ Fmode 网关负责:
 - `session`:会话分页与会话组;
 - `session`:会话分页与会话组;
 - `user`:个人与企业信息;
 - `user`:个人与企业信息;
 - `cloud`:文件接口;multipart 文件直传目前不经过中转;
 - `cloud`:文件接口;multipart 文件直传目前不经过中转;
-- `login` / `client`:仅用于文档检索,实际操作走 `qiwe-login`。
+- `login` / `client`:仅用于文档检索,实际操作走 `qiwei-login`。
 
 
 ## 注意事项
 ## 注意事项
 
 

+ 13 - 13
claude-code/claude-code-qiwe-assistant/skills/qiwe-capability-router/SKILL.md → claude-code/claude-code-qiwe-assistant/skills/qiwei-capability-router/SKILL.md

@@ -1,5 +1,5 @@
 ---
 ---
-name: qiwe-capability-router
+name: qiwei-capability-router
 description: 在 Fmode 网关转发的企业微信接口与官方 CLI 通道之间选择正确能力。文档、会议、日程、待办优先使用官方 CLI;客户、客户群、朋友圈、标签、个人企微设备登录等继续使用原有企业微信接口工具。
 description: 在 Fmode 网关转发的企业微信接口与官方 CLI 通道之间选择正确能力。文档、会议、日程、待办优先使用官方 CLI;客户、客户群、朋友圈、标签、个人企微设备登录等继续使用原有企业微信接口工具。
 ---
 ---
 
 
@@ -13,27 +13,27 @@ description: 在 Fmode 网关转发的企业微信接口与官方 CLI 通道之
 |---|---|
 |---|---|
 | 普通文档、在线表格、智能表格、智能文档 | 企业微信官方 CLI |
 | 普通文档、在线表格、智能表格、智能文档 | 企业微信官方 CLI |
 | 会议、日程、待办 | 企业微信官方 CLI |
 | 会议、日程、待办 | 企业微信官方 CLI |
-| 客户、外部联系人、客户群、朋友圈、标签 | Fmode 网关企业微信接口工具 `qiwe_api_*` |
-| 个人企微设备扫码登录、掉线恢复 | Fmode 网关登录工具 `qiwe_login_*` |
-| 订阅、席位、余额 | Fmode 网关订阅工具 `qiwe_subscription_*` |
+| 客户、外部联系人、客户群、朋友圈、标签 | Fmode 网关企业微信接口工具 `qiwei_api_*` |
+| 个人企微设备扫码登录、掉线恢复 | Fmode 网关登录工具 `qiwei_login_*` |
+| 订阅、席位、余额 | Fmode 网关订阅工具 `qiwei_subscription_*` |
 | 通讯录、消息 | 根据用户指定的身份和授权范围选择;不确定时先询问 |
 | 通讯录、消息 | 根据用户指定的身份和授权范围选择;不确定时先询问 |
 
 
 ## 官方 CLI 通道
 ## 官方 CLI 通道
 
 
-1. 调用 `qiwe_official_status`;
-2. CLI 未安装时调用 `qiwe_official_prepare`;
+1. 调用 `qiwei_official_status`;
+2. CLI 未安装时调用 `qiwei_official_prepare`;
 3. 未授权时把工具返回的初始化命令交给用户在终端运行;
 3. 未授权时把工具返回的初始化命令交给用户在终端运行;
-4. 用 `qiwe_official_help` 核对方法参数;
-5. 用 `qiwe_official_call` 执行。
+4. 用 `qiwei_official_help` 核对方法参数;
+5. 用 `qiwei_official_call` 执行。
 
 
 ## Fmode 网关接口通道
 ## Fmode 网关接口通道
 
 
 原有流程保持不变:
 原有流程保持不变:
 
 
-1. `qiwe_api_search`;
-2. `qiwe_api_doc`;
-3. `qiwe_api_call`;
-4. 登录问题转 `qiwe-login`;
-5. 订阅问题调用 `qiwe_subscription_*`。
+1. `qiwei_api_search`;
+2. `qiwei_api_doc`;
+3. `qiwei_api_call`;
+4. 登录问题转 `qiwei-login`;
+5. 订阅问题调用 `qiwei_subscription_*`。
 
 
 不得把官方机器人凭据传给 Fmode 网关,也不得用 Fmode token 初始化官方 CLI。
 不得把官方机器人凭据传给 Fmode 网关,也不得用 Fmode token 初始化官方 CLI。

+ 42 - 0
claude-code/claude-code-qiwe-assistant/skills/qiwei-login/SKILL.md

@@ -0,0 +1,42 @@
+---
+name: qiwei-login
+description: 通过 Fmode 网关转发的企业微信接口完成扫码登录和订阅管理。适用于首次使用、设备掉线、qiwei_api_call 返回 needs_login、订阅未开通或席位不足;客户端只使用 Fmode 鉴权 token 和 uid,企业微信接口访问凭据由 Fmode 网关管理。
+---
+
+# 企业微信扫码登录
+
+## 严格流程
+
+1. **检查订阅**:调用 `qiwei_subscription_status`。
+   - 未订阅或已到期:调用 `qiwei_subscribe`;
+   - 席位已满:用 `qiwei_subscribe` 传新的总 `seats` 数增购;
+   - 余额不足:引导用户充值飞马余额后重试。
+2. **检查设备**:调用 `qiwei_login_status`。
+   - `online=true`:无需重新登录;
+   - 未配置或离线:继续下一步。
+3. **生成二维码**:调用 `qiwei_login_start`。
+   - 工具通过 `/api/qiwei/login/start` 请求 Fmode 网关;
+   - 二维码保存到 `outputs/qiwei-login-qrcode.png`,默认打开 HTML 预览;
+   - 把二维码展示给用户,用手机企业微信扫码并确认。
+4. **轮询状态**:每 3–5 秒调用 `qiwei_login_check`。
+   - `-1 / 1`:继续等待;
+   - `0`:可传 `manual: true` 尝试免扫码登录;
+   - `2`:登录成功;
+   - `4`:用户取消,重新调用 `qiwei_login_start`;
+   - `10`:进入验证码流程。
+5. **验证码**:状态 `10` 时向用户索要手机端显示的 6 位验证码,调用 `qiwei_login_verify`,然后再次检查状态。
+6. **验证业务链路**:登录成功后调用 `qiwei_api_call`(例如 `user.getProfile`)。
+
+## 配置
+
+- `QIWEI_API_BASE` 默认 `https://server.fmode.cn/api/qiwei`;
+- Fmode token 自动从 Claude Code / Fmode 配置读取,也可显式设置 `QIWEI_AUTH_TOKEN`;
+- `QIWEI_UID` 未配置时自动生成并持久化;
+- 不读取、保存或要求用户提供企业微信接口访问凭据。
+
+## 安全约定
+
+- 不展示或记录 Authorization 和 Fmode token;
+- 不向用户暴露接口服务提供方名称、相关域名或控制台;
+- 不绕过 Fmode 网关直连内部服务;
+- 不通过 `qiwei_api_call` 调用 `/login/*` 或 `/client/*`。

+ 7 - 7
claude-code/claude-code-qiwe-assistant/skills/qiwe-official-doc/SKILL.md → claude-code/claude-code-qiwe-assistant/skills/qiwei-official-doc/SKILL.md

@@ -1,11 +1,11 @@
 ---
 ---
-name: qiwe-official-doc
+name: qiwei-official-doc
 description: 通过企业微信官方 CLI 创建普通文档、读取 Markdown 内容和覆写文档。适用于企业微信文档或 doc.weixin.qq.com/doc/ 链接;在线表格、智能表格和智能文档应路由到后续对应技能。
 description: 通过企业微信官方 CLI 创建普通文档、读取 Markdown 内容和覆写文档。适用于企业微信文档或 doc.weixin.qq.com/doc/ 链接;在线表格、智能表格和智能文档应路由到后续对应技能。
 ---
 ---
 
 
 # 企业微信官方普通文档
 # 企业微信官方普通文档
 
 
-本技能只处理 URL 形如 `https://doc.weixin.qq.com/doc/*` 的普通文档,通过 `qiwe_official_*` MCP 工具调用官方 CLI。
+本技能只处理 URL 形如 `https://doc.weixin.qq.com/doc/*` 的普通文档,通过 `qiwei_official_*` MCP 工具调用官方 CLI。
 
 
 以下类型不由本技能直接处理:
 以下类型不由本技能直接处理:
 
 
@@ -17,10 +17,10 @@ description: 通过企业微信官方 CLI 创建普通文档、读取 Markdown 
 
 
 ## 前置检查
 ## 前置检查
 
 
-1. 调用 `qiwe_official_status`;
-2. 未安装时调用 `qiwe_official_prepare`;
+1. 调用 `qiwei_official_status`;
+2. 未安装时调用 `qiwei_official_prepare`;
 3. 未初始化时停止业务调用,把返回的初始化命令交给用户完成扫码;
 3. 未初始化时停止业务调用,把返回的初始化命令交给用户完成扫码;
-4. 调用 `qiwe_official_help` 核对当前方法参数。
+4. 调用 `qiwei_official_help` 核对当前方法参数。
 
 
 ## 支持的方法
 ## 支持的方法
 
 
@@ -91,6 +91,6 @@ description: 通过企业微信官方 CLI 创建普通文档、读取 Markdown 
 ## 错误处理
 ## 错误处理
 
 
 - `851002 incompatible doc type`:重新识别 URL 类型,不要继续用普通文档方法;
 - `851002 incompatible doc type`:重新识别 URL 类型,不要继续用普通文档方法;
-- 未初始化:提示官方 CLI 初始化,不调用原有 `qiwe_login_*`;
-- 方法或 schema 变化:重新调用 `qiwe_official_help`;
+- 未初始化:提示官方 CLI 初始化,不调用原有 `qiwei_login_*`;
+- 方法或 schema 变化:重新调用 `qiwei_official_help`;
 - 不展示机器人 Secret、Authorization 或本地加密配置内容。
 - 不展示机器人 Secret、Authorization 或本地加密配置内容。

+ 7 - 7
claude-code/claude-code-qiwe-assistant/skills/qiwe-official-meeting/SKILL.md → claude-code/claude-code-qiwe-assistant/skills/qiwei-official-meeting/SKILL.md

@@ -1,16 +1,16 @@
 ---
 ---
-name: qiwe-official-meeting
+name: qiwei-official-meeting
 description: 通过企业微信官方 CLI 创建、查询、取消会议及维护受邀成员。适用于预约会议、查看会议列表或详情、取消会议、添加或移除参会人;不使用原有个人企微设备接口。
 description: 通过企业微信官方 CLI 创建、查询、取消会议及维护受邀成员。适用于预约会议、查看会议列表或详情、取消会议、添加或移除参会人;不使用原有个人企微设备接口。
 ---
 ---
 
 
 # 企业微信官方会议
 # 企业微信官方会议
 
 
-所有会议操作通过本技能包的 `qiwe_official_*` MCP 工具调用官方 CLI,不直接拼接 shell 命令。
+所有会议操作通过本技能包的 `qiwei_official_*` MCP 工具调用官方 CLI,不直接拼接 shell 命令。
 
 
 ## 前置检查
 ## 前置检查
 
 
-1. 调用 `qiwe_official_status`;
-2. 未安装时调用 `qiwe_official_prepare`,成功后再次检查状态;
+1. 调用 `qiwei_official_status`;
+2. 未安装时调用 `qiwei_official_prepare`,成功后再次检查状态;
 3. 未初始化时停止业务调用,把返回的初始化命令交给用户完成一次扫码;
 3. 未初始化时停止业务调用,把返回的初始化命令交给用户完成一次扫码;
 4. 初始化完成后再继续。
 4. 初始化完成后再继续。
 
 
@@ -24,7 +24,7 @@ description: 通过企业微信官方 CLI 创建、查询、取消会议及维
 | 取消会议 | `meeting` | `cancel_meeting` |
 | 取消会议 | `meeting` | `cancel_meeting` |
 | 全量更新受邀成员 | `meeting` | `set_invite_meeting_members` |
 | 全量更新受邀成员 | `meeting` | `set_invite_meeting_members` |
 
 
-每次首次使用或参数不确定时,先调用 `qiwe_official_help` 读取当前固定 CLI 版本的方法 schema。
+每次首次使用或参数不确定时,先调用 `qiwei_official_help` 读取当前固定 CLI 版本的方法 schema。
 
 
 ## 创建会议
 ## 创建会议
 
 
@@ -76,7 +76,7 @@ description: 通过企业微信官方 CLI 创建、查询、取消会议及维
 
 
 ## 错误处理
 ## 错误处理
 
 
-- 未初始化:提示执行官方初始化命令,不转到原有 `qiwe_login_*`;
-- 方法或参数变化:重新调用 `qiwe_official_help`;
+- 未初始化:提示执行官方初始化命令,不转到原有 `qiwei_login_*`;
+- 方法或参数变化:重新调用 `qiwei_official_help`;
 - 官方通道失败不影响原有 Fmode 网关接口通道;
 - 官方通道失败不影响原有 Fmode 网关接口通道;
 - 不展示机器人 Secret、Authorization 或本地加密配置内容。
 - 不展示机器人 Secret、Authorization 或本地加密配置内容。