Skip to content

About

企业微信第三方应用市场 (open.work.weixin.qq.com/appStore) OpenCLI 插件 — 分类/搜索/详情/清单同步/开通/卸载全流程原子能力

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

opencli-plugin-wecom-store

将 企业微信第三方应用市场 CLI 化的 OpenCLI 插件,覆盖分类/搜索/详情/清单同步/开通/卸载全流程原子能力。

  • 站点 slug: wecom-store
  • 命令数: 16
  • 依赖: @jackwener/opencli >=1.8.0
  • 数据目录: ~/.opencli/data/wecom-store/

安装

# 假定已全局安装 opencli
npm i -g @jackwener/opencli

# 本仓库以 symlink 形式挂到 ~/.opencli/plugins/
opencli plugin install /path/to/opencli-plugin-wecom-store

安装后 opencli list 应能看到 wecom-store 分组下 16 条命令。

命令总览

命令 策略 需登录 用途
search PUBLIC 建议 关键词搜索(匿名调用返回空,走本地索引更靠谱)
category-list PUBLIC 否 拉取分类树(12 一级 + 26 二级 = 28 组合)
category-apps PUBLIC 否 单个 (id, subId) 下的全量应用
home-recommend PUBLIC 否 首页推荐位应用
industry-list PUBLIC 否 行业解决方案
hardware-list PUBLIC 否 硬件生态清单
app-price PUBLIC 否 应用套餐/价格信息(可匿名读取的部分)
app-get COOKIE 是 应用详情(getAppDetail,匿名会 -30001)
app-history COOKIE 是 应用版本历史(同上)
me COOKIE 是 登录态验证:拿 corp_id / user_id / role
index-sync PUBLIC 否 全量/增量同步应用清单到本地
index-query LOCAL 否 从本地清单按条件筛(离线,快)
index-diff LOCAL 否 展示最近的增量 diff 事件
capture-install COOKIE 是 抓取「开通应用」完整请求链
install COOKIE 是 开通免费/试用版应用,默认 scope=self
uninstall COOKIE 是 卸载/停用已开通应用

快速开始

1. 免登录:探索目录

# 分类树
opencli wecom-store category-list

# 某个二级分类的全量应用
opencli wecom-store category-apps --id 1 --subId 1002

# 全量同步 (~5-8s,抓 28 个分类页 SSR)
opencli wecom-store index-sync --mode full

# 本地查询
opencli wecom-store index-query --q AI --limit 10
opencli wecom-store index-query --category "客户联系"
opencli wecom-store index-query --brand "腾讯"

index-sync 会写以下文件到 ~/.opencli/data/wecom-store/:

  • apps.json — {updated_at, apps[{thirdapp_id, name, slogan, logo, brand_name, category_id, ...}]}
  • fingerprints.json — sha1 指纹,用于增量 diff
  • history.jsonl — 每次 sync 的 added/removed/changed 事件流

增量:

opencli wecom-store index-sync --mode incremental
opencli wecom-store index-diff --limit 20

2. 登录态:详情/开通

登录:直接在系统 Chrome 打开 https://open.work.weixin.qq.com/appStore/ 用管理员账号登过,OpenCLI 通过扩展复用登录态。

# 验证登录态
opencli wecom-store me

# 应用详情
opencli wecom-store app-get "MTk3MDMyNDk4ODA3OTE0MV8xMDMyMDE4XzE="

# 版本历史
opencli wecom-store app-history "MTk3MDMyNDk4ODA3OTE0MV8xMDMyMDE4XzE="

thirdapp_id 就是应用详情页 URL 中 /appStore/app/ 后面的 base64 段,也可以从 index-query / category-apps 输出中拷贝。

3. 开通应用(写路径)

⚠️ 写路径是整个插件里最"敏感"的部分,直接落到企业。使用双重护栏:

  1. --dry-run(默认开启):只打印计划,不发请求。
  2. --confirm:真正执行前必须显式加。
  3. 每次执行都会记 audit log 到 ~/.opencli/data/wecom-store/install-audit.jsonl。

真实的 install 是 两 origin、两步链(2026-07-01 端到端实测跑通):

step0 (前置查询) POST open.work.weixin.qq.com/wwopen/monoApi/appStore/app/getAppDetail
                 body : { appId, app_version:'', termid:65539 }
                 产出 : app_name / on_trial_type
                 (numeric suiteid + ww_corpid 从 app_info.update_content_list[0] 提取,
                  或者 fallback 到本地 index-sync 缓存)

step1 (拿授权码) POST open.work.weixin.qq.com/wwopen/monoApi/appStore/auth/getPreAuthCode
                 body : { service_corpid: <数字>, suiteid: <数字> }
                 产出 : { data: { kitid, preauthcode } }

step2 (提交安装) POST work.weixin.qq.com/wework_admin/third/installApp
                 (⚠️ 不同 origin!  form-urlencoded, 不是 JSON)
                 body : redirect_uri=&pre_auth_code=<code>&suite_id=<数字>
                        &install_app_item[0][thirdapp_id]=<b64>
                        &install_app_item[0][vid_item][]=<self_vid>
                        &install_app_item[0][custom_acquisition_permit]=
                        &b_can_auth_customer_acquisition=false
                        &opt[]=7&from_ad_card=&scene=&scene_val=
                 产出 : { data: { install_app_id: [<numeric>] } }

体验流程:

# 一次性:全量 index-sync 拿全站 numeric suiteid + ww_corpid 缓存
opencli wecom-store index-sync --mode full

# 干跑(默认)— 输出计划: service_corpid / suiteid / vid_item / opt
opencli wecom-store install <thirdapp_id> --self-vid <vid> --plan trial

# 真跑
opencli wecom-store install <thirdapp_id> --self-vid <vid> --plan trial \
  --dry-run=false --confirm=true

参数:

  • thirdapp_ids(位置参数,必填)— 逗号分隔的应用 id 列表 (base64)
  • --plan free|trial(默认 trial)— 拒绝 paid
  • --scope self|vid:<vid,vid>(默认 self)— 可见范围(vid,不是 user_id)
  • --self-vid <vid> — 当前管理员 vid,用于 install_app_item[0][vid_item][];缺省会尝试从 window.currentUser / wework_admin/main/getAdminList 探测,找不到就报错
  • --opt <数字或列表>(默认 7)— opt[] 值,试用/免费默认为 7
  • --dry-run / --dry-run=false — 默认 true
  • --confirm — 真跑必须加,与 --dry-run=false 双重确认

如何拿 self vid:打开 https://work.weixin.qq.com/wework_admin/frame#apps → DevTools → Network → 任一 XHR 的响应/请求 body 中含 vid 字段(如 1688857901263362)。此 vid 与 open.work 上 getMarketUserInfo 返回的 corpid 不是同一个。

卸载:

opencli wecom-store uninstall "APPID_A" --confirm

架构说明

数据源分层

                              ┌─ SSR HTML (Nuxt2)  → 分类/首页/行业/硬件
open.work.weixin.qq.com/appStore ├─ monoApi/appStore/* (POST JSON) → 4 个免登 + 登录端点
                              └─ 详情页 SSR (需登录)

work.weixin.qq.com/wework_admin ── /third/installApp (POST form-urlencoded, 需登录)
                                    ↑ install/uninstall 的真正提交口
  • SSR: fetchSSR() (Node 端),解析 window.__NUXT__=... — 支持新的 JSON+IIFE 混合形态
  • 免登 monoApi: postMonoApi() (Node 端) — 4 个:marketSearch / getAppDetail / getAppHistory / getMarketUserInfo
  • 登录 monoApi (JSON): postMonoApiInPage() (browser 端 fetch, credentials:'include')
  • 登录 wework_admin (form-urlencoded): postFormInPage() (browser 端 fetch, 目标 origin work.weixin.qq.com, 靠 credentials:'include' 自动附另一 origin 的 sso Cookie)

关键坑(血泪总结):

  • 所有 monoApi 端点必须带 QS ?lang=zh_CN&ajax=1&f=json&random=<n>,否则一律 errCode=-30001 参数错误。已在 withAjaxQs() 里自动附上。
  • getAppDetail body 是 camelCase appId + app_version + termid:65539,不是 appid。
  • 详情页 URL 是 ?appId=<b64>&source=homeCategory 查询串,不是 /app/<b64> 路径。
  • service_corpid 与 suiteid 都是数字;getAppDetail 顶层 app_info 反而没有它们,只在 app_info.update_content_list[] 里泄露。install 命令会 fallback 到 ~/.opencli/data/wecom-store/apps.json。
  • servicecorp_info.servicecorp_id 是字符串 kitid(如 ww1ebe7793c4c8bfb0),不是数字 corpid。
  • step2 的 origin 是 work.weixin.qq.com(不是 open.work),标签页可以停在 open.work,fetch(..., credentials:'include') 会自动补另一 origin 的 sso Cookie。

增量指纹

每个 app 的指纹 = sha1(thirdapp_id | name | slogan | brand_name | logo | category_id | sub_category_id)。字段变了才写 changed 事件。

目录结构

opencli-plugin-wecom-store/
├── opencli-plugin.json        # 插件清单
├── package.json
├── README.md
├── shared/
│   ├── endpoints.js           # 端点常量 & Referer/Header
│   ├── nuxt.js                # __NUXT__ 提取器 (JSON+IIFE 混合)
│   ├── api.js                 # fetchSSR / postMonoApi / postMonoApiInPage
│   ├── home.js                # 首页 Nuxt 缓存 & 分类枚举
│   └── index-store.js         # ~/.opencli/data/wecom-store 读写
├── search.js                  # PUBLIC - 关键词搜索
├── category-list.js           # PUBLIC - 分类树
├── category-apps.js           # PUBLIC - 分类下应用
├── home-recommend.js          # PUBLIC - 首页推荐
├── industry-list.js           # PUBLIC - 行业方案
├── hardware-list.js           # PUBLIC - 硬件清单
├── app-price.js               # PUBLIC - 价格
├── app-get.js                 # COOKIE - 应用详情
├── app-history.js             # COOKIE - 版本历史
├── me.js                      # COOKIE - 登录态
├── index-sync.js              # PUBLIC - 全量/增量同步
├── index-query.js             # LOCAL  - 本地筛选
├── index-diff.js              # LOCAL  - 增量事件
├── capture-install.js         # COOKIE - 抓开通请求链
├── install.js                 # COOKIE - 开通
└── uninstall.js               # COOKIE - 卸载

已知限制

  • search 匿名返回空 item[],需登录才有真结果。离线场景请用 index-sync + index-query。
  • install 已在真机端到端跑通(2026-07-01, 励销CRM 试用版, install_app_id=5629502485775491);uninstall 尚未抓包实测,建议先用 capture-install 对齐。
  • on_trial_type / app_flag 等布尔含义未文档化,来自实测的 ctrlState.home 字段。
  • 部分二级分类共用同一 id,total 字段是分区块聚合而非游标,不能作为分页依据。

出错码

沿用 OpenCLI 标准:

  • 2 — ArgumentError(参数非法)
  • 66 — EmptyResultError(无匹配)
  • 77 — AuthRequiredError(登录/Cookie 失效)
  • 75 — TimeoutError(20s 内无响应)
  • 1 — CommandExecutionError(HTTP 异常/解析失败)

License

MIT

About

企业微信第三方应用市场 (open.work.weixin.qq.com/appStore) OpenCLI 插件 — 分类/搜索/详情/清单同步/开通/卸载全流程原子能力

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages