@rezti/dsh-rez-suite 0.1.52 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,12 +5,16 @@
5
5
  升级:
6
6
 
7
7
  ```bash
8
- dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.52
8
+ dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.53
9
9
  ```
10
10
 
11
11
  不要用裸 `plugin update`,不要写 `latest`(新 profile 有 24 小时发布冷却,当天可能装不上)。不要装 **0.1.46**(GitHub 子依赖会被 DSH 拦住)。不要装 **0.1.51**(误用 `npm publish` 把 `workspace:` 打进包了)。
12
12
 
13
- 发版顺序:`wechat@0.1.16` → `suite@0.1.52`。**必须用 `pnpm publish`**(会把 `workspace:` 写成真实版本),不要用 `npm publish`。
13
+ 发版顺序:`wechat@0.1.16` → `suite@0.1.53`。**必须用 `pnpm publish`**(会把 `workspace:` 写成真实版本),不要用 `npm publish`。
14
+
15
+ ## 0.1.53 — 2026-08-20
16
+
17
+ - **suite 自带 `@wecom/cli@1.1.0`**:装 suite 即带办公 CLI(含 `mail`)。启动时把其 bin 插到 PATH 最前,避开 Homebrew 全局 `0.1.9`。房间消息仍设置页 BotID+Secret;办公授权仍 `wecom-cli auth init`(可扫码)。
14
18
 
15
19
  ## 0.1.52 — 2026-08-20
16
20
 
package/README.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  雷泽插件的**唯一安装入口**。Odoo / 企微 / HA / TAPD / GSC / 企查查(仅法务助理房间) / 天眼查(仅老板) / 工作区(老板三室 + 全员助理)都由这一包拉齐,不要再逐个 `plugin add`,**不要**再装 `@rezti/dsh-rez-boss-desk`。
4
4
 
5
- 发版顺序(必做):先发 `@rezti/dsh-rez-wechat@0.1.16` → `@rezti/dsh-rez-suite@0.1.52`。**发 suite 必须用 `pnpm publish`**(会把 `workspace:` 写成真实版本),不要用 `npm publish`。员工机安装包仍钉已发布的 suite,不要顺手改审批镜像 `SUITE_VERSION`。
5
+ 发版顺序(必做):先发 `@rezti/dsh-rez-wechat@0.1.16` → `@rezti/dsh-rez-suite@0.1.53`。**发 suite 必须用 `pnpm publish`**(会把 `workspace:` 写成真实版本),不要用 `npm publish`。员工机安装包仍钉已发布的 suite,不要顺手改审批镜像 `SUITE_VERSION`。
6
6
 
7
7
  设置里选身份后,左侧自己出现对应房间(全员三助理;工程师另加实验室;运营另加设计、发布、电商;老板另加搞钱/产品/运营),不必手动添加工作区。实验室:大模型写 YAML、调 MCP、给下载链接。
8
8
 
9
- 回形针走 npm 包 `@rezti/dsh-web-file-uploader@0.2.3`(不要 GitHub:DSH 会拦子依赖 git,0.1.46 因此装不上)。
9
+ 回形针走 npm 包 `@rezti/dsh-web-file-uploader@0.2.3`(不要 GitHub:DSH 会拦子依赖 git,0.1.46 因此装不上)。suite 自带 `@wecom/cli@1.1.0`(企微办公;不要依赖 Homebrew 全局 0.1.x)。
10
10
 
11
11
  员工机不要 `SUITE_VERSION=latest`:新 profile 默认 `minimumReleaseAge: 1440`,当天发的包可能装不到。钉死版本,或把 profile 的 `minimumReleaseAge` 改成 `0`。
12
12
 
13
13
  ```bash
14
14
  # 第一次 / 升级(钉死版本,避开 24h 发布冷却)
15
- dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.52
15
+ dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.53
16
16
  ```
17
17
 
18
18
  本版改了什么:见 [CHANGELOG.md](./CHANGELOG.md)。
package/lib/client.js CHANGED
@@ -994,6 +994,20 @@ function ChannelTable({ kind, lang }) {
994
994
  //#endregion
995
995
  //#region src/changelog.ts
996
996
  const SUITE_CHANGELOG = [
997
+ {
998
+ version: "0.1.53",
999
+ date: "2026-08-20",
1000
+ sections: [{
1001
+ title: {
1002
+ zh: "自带 wecom-cli 1.1",
1003
+ en: "Bundled wecom-cli 1.1"
1004
+ },
1005
+ items: [{
1006
+ zh: "suite 依赖 @wecom/cli@1.1.0,装插件即带 mail/auth。PATH 优先用 profile 里的二进制,避开 Homebrew 0.1.9。房间仍 BotID+Secret;办公 auth init 可扫码。",
1007
+ en: "Suite depends on @wecom/cli@1.1.0 (mail/auth). Prefers the profile binary over Homebrew 0.1.9. Room bots still BotID+Secret; office auth init can QR."
1008
+ }]
1009
+ }]
1010
+ },
997
1011
  {
998
1012
  version: "0.1.52",
999
1013
  date: "2026-08-20",
package/lib/index.js CHANGED
@@ -4,8 +4,8 @@ import { dirname, join, relative } from "node:path";
4
4
  import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, REZ_SYSTEMS, lastMcpStartError, loadRezConnections, mergeConnections, readManagedCredential, secretConfigured, startMcpWhenSecret, writeManagedCredential } from "@rezti/dsh-rez-sso";
5
5
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
6
6
  import { spawn, spawnSync } from "node:child_process";
7
- import { DatabaseSync } from "node:sqlite";
8
7
  import { fileURLToPath } from "node:url";
8
+ import { DatabaseSync } from "node:sqlite";
9
9
  //#region ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
10
10
  /** Return true when a value is `null` or `undefined`. */
11
11
  function isNullable(value) {
@@ -3236,6 +3236,9 @@ function saveConfig(config) {
3236
3236
  * TAPD bug 1154488930001000054: missing MCP row was treated as “no WeCom”.
3237
3237
  * Official CLI v1.1+ opened 通讯录 / 在线文档 etc. for every company size
3238
3238
  * (https://github.com/WecomTeam/wecom-cli). Kelvin enabled those modules.
3239
+ *
3240
+ * Suite depends on @wecom/cli@1.1.0 so the profile gets a modern binary.
3241
+ * Prefer that over a global Homebrew 0.1.x that lacks `mail` / `auth`.
3239
3242
  */
3240
3243
  /** Official @wecom/cli >=1.1 catalog. Do not invent extra HTTP APIs. */
3241
3244
  const WECOM_CLI_CATALOG = [
@@ -3294,18 +3297,22 @@ const WECOM_CLI_CATALOG = [
3294
3297
  ];
3295
3298
  const MODULE_LABELS = WECOM_CLI_CATALOG.map((row) => row.label).join("/");
3296
3299
  const HINT_MISSING = [
3297
- `未安装官方 wecom-cli(@wecom/cli >=1.1)。企微办公能力(${MODULE_LABELS})走 CLI,不是 MCP。`,
3298
- "安装:npm i -g @wecom/cli ,然后 wecom-cli auth init。",
3299
- "官方已面向各规模企业开放通讯录等能力,不要再说「超过 10 人只能用文档」。",
3300
- "不要因为 rez_status 没有 wechat MCP 行就说没有企微能力。"
3300
+ `未找到可用的 wecom-cli >=1.1(企微办公:${MODULE_LABELS})。`,
3301
+ "suite 已依赖 @wecom/cli@1.1.0:请重装 @rezti/dsh-rez-suite,或确认 profile 的 node_modules 里有 @wecom/cli。",
3302
+ "不要用 Homebrew 全局 0.1.x(没有 mail/auth)。不要因为没有 wechat MCP 就说没有企微能力。"
3303
+ ].join("");
3304
+ const HINT_OUTDATED = [
3305
+ "PATH 上的 wecom-cli 过旧(<1.1,没有 mail 等子命令)。",
3306
+ "请用 suite 自带的 @wecom/cli@1.1.0:重装 suite,或把 profile node_modules/.bin 放在 PATH 最前。",
3307
+ "不要 npm i -g 覆盖到旧 Homebrew 路径前面仍是 0.1.9。"
3301
3308
  ].join("");
3302
3309
  const HINT_UNAUTH = [
3303
- "已安装 wecom-cli,尚未授权。跑 wecom-cli auth init",
3310
+ "已安装 wecom-cli >=1.1,尚未授权。跑 wecom-cli auth init(可扫码)。",
3304
3311
  `授权后可用:${MODULE_LABELS}。本企业已开通这些品类。`,
3305
3312
  "CLI 报某模块未授权才说该能力未开通;不要说「没有企微 MCP」,也不要说 10 人限制。"
3306
3313
  ].join("");
3307
3314
  const HINT_OK = [
3308
- `企微办公能力走 wecom-cli >=1.1(${MODULE_LABELS})。本企业已开通。`,
3315
+ `企微办公能力走 wecom-cli >=1.1(${MODULE_LABELS})。本企业已开通。suite 已带 @wecom/cli。`,
3309
3316
  "按房间里官方 wecomcli-* 技能执行,命令以 wecom-cli <模块> --help 为准。",
3310
3317
  "不要用旧帮助中心「超过 10 人只能用文档」的说法。CLI 报未授权才说该模块未开通。",
3311
3318
  "设置页 BotID+Secret 只是房间消息通道。不要把没有 mcp__wechat__* 当成没有企微能力。"
@@ -3313,8 +3320,32 @@ const HINT_OK = [
3313
3320
  function wecomCliCatalog() {
3314
3321
  return WECOM_CLI_CATALOG.map((row) => ({ ...row }));
3315
3322
  }
3323
+ /** Resolve suite-bundled @wecom/cli binary; undefined if not installed next to suite. */
3324
+ function resolveWecomCliBin() {
3325
+ const require = createRequire(import.meta.url);
3326
+ try {
3327
+ const bin = join(dirname(require.resolve("@wecom/cli/package.json")), "bin", "wecom.js");
3328
+ if (existsSync(bin)) return bin;
3329
+ } catch {}
3330
+ const here = dirname(fileURLToPath(import.meta.url));
3331
+ for (const candidate of [
3332
+ join(here, "..", "node_modules", "@wecom", "cli", "bin", "wecom.js"),
3333
+ join(here, "..", "..", "node_modules", "@wecom", "cli", "bin", "wecom.js"),
3334
+ join(here, "..", "..", "..", "node_modules", "@wecom", "cli", "bin", "wecom.js")
3335
+ ]) if (existsSync(candidate)) return candidate;
3336
+ }
3337
+ /** Put suite-bundled wecom-cli ahead of global Homebrew 0.1.x on PATH. */
3338
+ function ensureWecomCliOnPath() {
3339
+ const bin = resolveWecomCliBin();
3340
+ if (bin === void 0) return void 0;
3341
+ const dir = dirname(bin);
3342
+ const pathKey = process.platform === "win32" ? "Path" : "PATH";
3343
+ const parts = (process.env[pathKey] ?? "").split(process.platform === "win32" ? ";" : ":").filter(Boolean);
3344
+ if (parts[0] !== dir) process.env[pathKey] = [dir, ...parts.filter((p) => p !== dir)].join(process.platform === "win32" ? ";" : ":");
3345
+ return bin;
3346
+ }
3316
3347
  function defaultWecomCliRunner(argv) {
3317
- const result = spawnSync("wecom-cli", argv, {
3348
+ const result = spawnSync(resolveWecomCliBin() ?? "wecom-cli", argv, {
3318
3349
  encoding: "utf8",
3319
3350
  timeout: 4e3,
3320
3351
  env: process.env
@@ -3331,6 +3362,15 @@ function looksUnauthorized(text, status) {
3331
3362
  if (status !== 0) return true;
3332
3363
  return /未授权|未登录|未初始化|not (logged|auth)|unauthorized|请先.*auth|auth init/i.test(text);
3333
3364
  }
3365
+ /** 0.1.x has no mail; 1.1 help lists mail. */
3366
+ function isWecomCliModern(helpOrVersionText) {
3367
+ const text = helpOrVersionText.toLowerCase();
3368
+ if (/\b0\.1\.\d+\b/.test(text) && !/\b1\.\d+\.\d+\b/.test(text)) return false;
3369
+ if (/\bunrecognized subcommand\b/.test(text)) return false;
3370
+ if (/\bmail\b/.test(text)) return true;
3371
+ if (/\b1\.\d+\.\d+\b/.test(text) || /wecom-cli\s*\(?\s*1\./i.test(helpOrVersionText)) return true;
3372
+ return false;
3373
+ }
3334
3374
  function withCatalog(partial) {
3335
3375
  return {
3336
3376
  ...partial,
@@ -3338,34 +3378,48 @@ function withCatalog(partial) {
3338
3378
  };
3339
3379
  }
3340
3380
  function probeWecomCli(run = defaultWecomCliRunner) {
3381
+ const bin = resolveWecomCliBin();
3341
3382
  const help = run(["--help"]);
3342
3383
  if (help.errorCode === "ENOENT") return withCatalog({
3343
3384
  installed: false,
3344
3385
  authorized: false,
3345
- hint: HINT_MISSING
3386
+ hint: HINT_MISSING,
3387
+ bin
3346
3388
  });
3347
3389
  const helpText = help.stdout + help.stderr;
3348
3390
  if (help.status !== 0 && helpText.trim().length === 0) return withCatalog({
3349
3391
  installed: false,
3350
3392
  authorized: false,
3351
- hint: HINT_MISSING
3393
+ hint: HINT_MISSING,
3394
+ bin
3395
+ });
3396
+ const version = run(["--version"]);
3397
+ const versionText = version.stdout + version.stderr;
3398
+ if (!isWecomCliModern(helpText + "\n" + versionText)) return withCatalog({
3399
+ installed: false,
3400
+ authorized: false,
3401
+ hint: HINT_OUTDATED,
3402
+ bin
3352
3403
  });
3353
3404
  const auth = run(["auth", "show"]);
3354
3405
  const text = auth.stdout + auth.stderr;
3355
3406
  if (auth.errorCode === "ENOENT") return withCatalog({
3356
3407
  installed: false,
3357
3408
  authorized: false,
3358
- hint: HINT_MISSING
3409
+ hint: HINT_MISSING,
3410
+ bin
3359
3411
  });
3360
3412
  if (looksUnauthorized(text, auth.status)) return withCatalog({
3361
3413
  installed: true,
3362
3414
  authorized: false,
3363
- hint: HINT_UNAUTH
3415
+ hint: HINT_UNAUTH,
3416
+ bin
3364
3417
  });
3365
3418
  return withCatalog({
3366
3419
  installed: true,
3367
3420
  authorized: true,
3368
- hint: HINT_OK
3421
+ hint: HINT_OK,
3422
+ bin
3369
3423
  });
3370
3424
  }
3371
3425
  //#endregion
@@ -8911,6 +8965,7 @@ function delegatedHost(ctx, getConfig) {
8911
8965
  };
8912
8966
  }
8913
8967
  function apply(ctx, config) {
8968
+ ensureWecomCliOnPath();
8914
8969
  let current = () => normalize(config);
8915
8970
  const syncWorkspaces = mountRezWorkspaces(ctx, () => current().role);
8916
8971
  const syncQcc = mountQccMcp(ctx);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rezti/dsh-rez-suite",
3
- "description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.52",
4
- "version": "0.1.52",
3
+ "description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.53",
4
+ "version": "0.1.53",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -47,6 +47,7 @@
47
47
  "@rezti/dsh-rez-token-manager": "^0.1.4",
48
48
  "@rezti/dsh-rez-wechat": "^0.1.16",
49
49
  "@rezti/dsh-web-file-uploader": "0.2.3",
50
+ "@wecom/cli": "1.1.0",
50
51
  "@modelcontextprotocol/sdk": "^1.30.0",
51
52
  "zod": "^4.4.3"
52
53
  },
package/src/changelog.ts CHANGED
@@ -20,6 +20,21 @@ export interface ChangelogRelease {
20
20
  }
21
21
 
22
22
  export const SUITE_CHANGELOG: readonly ChangelogRelease[] = [
23
+ {
24
+ version: '0.1.53',
25
+ date: '2026-08-20',
26
+ sections: [
27
+ {
28
+ title: { zh: '自带 wecom-cli 1.1', en: 'Bundled wecom-cli 1.1' },
29
+ items: [
30
+ {
31
+ zh: 'suite 依赖 @wecom/cli@1.1.0,装插件即带 mail/auth。PATH 优先用 profile 里的二进制,避开 Homebrew 0.1.9。房间仍 BotID+Secret;办公 auth init 可扫码。',
32
+ en: 'Suite depends on @wecom/cli@1.1.0 (mail/auth). Prefers the profile binary over Homebrew 0.1.9. Room bots still BotID+Secret; office auth init can QR.',
33
+ },
34
+ ],
35
+ },
36
+ ],
37
+ },
23
38
  {
24
39
  version: '0.1.52',
25
40
  date: '2026-08-20',
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ import { TokenManager } from './token-manager.ts'
28
28
  import { rezConfigTool, rezEsphomeCompileTool, rezStatusTool } from './tools.ts'
29
29
  import type { RezConfig } from './protocol.ts'
30
30
  import { workspaceGuidanceFor, mountRezWorkspaces } from './boss/mount.ts'
31
+ import { ensureWecomCliOnPath } from './wecom-cli.ts'
31
32
  import { liveSelfUpdate } from './self-update.ts'
32
33
  import { mountTianyanchaMcp } from './tianyancha.ts'
33
34
  import { mountQccMcp, QCC_COMPANY_MCP_URL } from './qcc.ts'
@@ -149,6 +150,8 @@ function delegatedHost(ctx: Context, getConfig: () => RezConfig): McpHost {
149
150
  }
150
151
 
151
152
  export function apply(ctx: Context, config?: RezConfig): void {
153
+ // Prefer suite-bundled @wecom/cli@1.1 over global Homebrew 0.1.x (no mail).
154
+ ensureWecomCliOnPath()
152
155
  let current: () => RezConfig = () => normalize(config)
153
156
  const syncWorkspaces = mountRezWorkspaces(ctx, () => current().role)
154
157
  const syncQcc = mountQccMcp(ctx)
package/src/wecom-cli.ts CHANGED
@@ -3,9 +3,16 @@
3
3
  * TAPD bug 1154488930001000054: missing MCP row was treated as “no WeCom”.
4
4
  * Official CLI v1.1+ opened 通讯录 / 在线文档 etc. for every company size
5
5
  * (https://github.com/WecomTeam/wecom-cli). Kelvin enabled those modules.
6
+ *
7
+ * Suite depends on @wecom/cli@1.1.0 so the profile gets a modern binary.
8
+ * Prefer that over a global Homebrew 0.1.x that lacks `mail` / `auth`.
6
9
  */
7
10
 
8
11
  import { spawnSync } from 'node:child_process'
12
+ import { existsSync } from 'node:fs'
13
+ import { dirname, join } from 'node:path'
14
+ import { createRequire } from 'node:module'
15
+ import { fileURLToPath } from 'node:url'
9
16
 
10
17
  export interface WecomCliModule {
11
18
  id: string
@@ -17,6 +24,8 @@ export interface WecomCliStatus {
17
24
  authorized: boolean
18
25
  modules: WecomCliModule[]
19
26
  hint: string
27
+ /** Absolute path preferred for agent shell (suite-bundled). */
28
+ bin?: string
20
29
  }
21
30
 
22
31
  export interface WecomCliRunResult {
@@ -48,20 +57,25 @@ export const WECOM_CLI_CATALOG: readonly WecomCliModule[] = [
48
57
  const MODULE_LABELS = WECOM_CLI_CATALOG.map((row) => row.label).join('/')
49
58
 
50
59
  const HINT_MISSING = [
51
- `未安装官方 wecom-cli(@wecom/cli >=1.1)。企微办公能力(${MODULE_LABELS})走 CLI,不是 MCP。`,
52
- '安装:npm i -g @wecom/cli ,然后 wecom-cli auth init。',
53
- '官方已面向各规模企业开放通讯录等能力,不要再说「超过 10 人只能用文档」。',
54
- '不要因为 rez_status 没有 wechat MCP 行就说没有企微能力。',
60
+ `未找到可用的 wecom-cli >=1.1(企微办公:${MODULE_LABELS})。`,
61
+ 'suite 已依赖 @wecom/cli@1.1.0:请重装 @rezti/dsh-rez-suite,或确认 profile 的 node_modules 里有 @wecom/cli。',
62
+ '不要用 Homebrew 全局 0.1.x(没有 mail/auth)。不要因为没有 wechat MCP 就说没有企微能力。',
63
+ ].join('')
64
+
65
+ const HINT_OUTDATED = [
66
+ 'PATH 上的 wecom-cli 过旧(<1.1,没有 mail 等子命令)。',
67
+ '请用 suite 自带的 @wecom/cli@1.1.0:重装 suite,或把 profile node_modules/.bin 放在 PATH 最前。',
68
+ '不要 npm i -g 覆盖到旧 Homebrew 路径前面仍是 0.1.9。',
55
69
  ].join('')
56
70
 
57
71
  const HINT_UNAUTH = [
58
- '已安装 wecom-cli,尚未授权。跑 wecom-cli auth init',
72
+ '已安装 wecom-cli >=1.1,尚未授权。跑 wecom-cli auth init(可扫码)。',
59
73
  `授权后可用:${MODULE_LABELS}。本企业已开通这些品类。`,
60
74
  'CLI 报某模块未授权才说该能力未开通;不要说「没有企微 MCP」,也不要说 10 人限制。',
61
75
  ].join('')
62
76
 
63
77
  const HINT_OK = [
64
- `企微办公能力走 wecom-cli >=1.1(${MODULE_LABELS})。本企业已开通。`,
78
+ `企微办公能力走 wecom-cli >=1.1(${MODULE_LABELS})。本企业已开通。suite 已带 @wecom/cli。`,
65
79
  '按房间里官方 wecomcli-* 技能执行,命令以 wecom-cli <模块> --help 为准。',
66
80
  '不要用旧帮助中心「超过 10 人只能用文档」的说法。CLI 报未授权才说该模块未开通。',
67
81
  '设置页 BotID+Secret 只是房间消息通道。不要把没有 mcp__wechat__* 当成没有企微能力。',
@@ -79,8 +93,46 @@ export function parseWecomCliHelpModules(helpText: string): WecomCliModule[] {
79
93
  })
80
94
  }
81
95
 
96
+ /** Resolve suite-bundled @wecom/cli binary; undefined if not installed next to suite. */
97
+ export function resolveWecomCliBin(): string | undefined {
98
+ const require = createRequire(import.meta.url)
99
+ try {
100
+ const pkgJson = require.resolve('@wecom/cli/package.json')
101
+ const bin = join(dirname(pkgJson), 'bin', 'wecom.js')
102
+ if (existsSync(bin)) return bin
103
+ } catch {
104
+ // not hoisted next to this module
105
+ }
106
+ // Fallbacks when published layout differs
107
+ const here = dirname(fileURLToPath(import.meta.url))
108
+ for (const candidate of [
109
+ join(here, '..', 'node_modules', '@wecom', 'cli', 'bin', 'wecom.js'),
110
+ join(here, '..', '..', 'node_modules', '@wecom', 'cli', 'bin', 'wecom.js'),
111
+ join(here, '..', '..', '..', 'node_modules', '@wecom', 'cli', 'bin', 'wecom.js'),
112
+ ]) {
113
+ if (existsSync(candidate)) return candidate
114
+ }
115
+ return undefined
116
+ }
117
+
118
+ /** Put suite-bundled wecom-cli ahead of global Homebrew 0.1.x on PATH. */
119
+ export function ensureWecomCliOnPath(): string | undefined {
120
+ const bin = resolveWecomCliBin()
121
+ if (bin === undefined) return undefined
122
+ const dir = dirname(bin)
123
+ const pathKey = process.platform === 'win32' ? 'Path' : 'PATH'
124
+ const current = process.env[pathKey] ?? ''
125
+ const parts = current.split(process.platform === 'win32' ? ';' : ':').filter(Boolean)
126
+ if (parts[0] !== dir) {
127
+ process.env[pathKey] = [dir, ...parts.filter((p) => p !== dir)].join(process.platform === 'win32' ? ';' : ':')
128
+ }
129
+ return bin
130
+ }
131
+
82
132
  export function defaultWecomCliRunner(argv: string[]): WecomCliRunResult {
83
- const result = spawnSync('wecom-cli', argv, {
133
+ const bundled = resolveWecomCliBin()
134
+ const cmd = bundled ?? 'wecom-cli'
135
+ const result = spawnSync(cmd, argv, {
84
136
  encoding: 'utf8',
85
137
  timeout: 4000,
86
138
  env: process.env,
@@ -99,27 +151,44 @@ function looksUnauthorized(text: string, status: number | null): boolean {
99
151
  return /未授权|未登录|未初始化|not (logged|auth)|unauthorized|请先.*auth|auth init/i.test(text)
100
152
  }
101
153
 
154
+ /** 0.1.x has no mail; 1.1 help lists mail. */
155
+ export function isWecomCliModern(helpOrVersionText: string): boolean {
156
+ const text = helpOrVersionText.toLowerCase()
157
+ if (/\b0\.1\.\d+\b/.test(text) && !/\b1\.\d+\.\d+\b/.test(text)) return false
158
+ if (/\bunrecognized subcommand\b/.test(text)) return false
159
+ if (/\bmail\b/.test(text)) return true
160
+ if (/\b1\.\d+\.\d+\b/.test(text) || /wecom-cli\s*\(?\s*1\./i.test(helpOrVersionText)) return true
161
+ return false
162
+ }
163
+
102
164
  function withCatalog(partial: Omit<WecomCliStatus, 'modules'>): WecomCliStatus {
103
165
  return { ...partial, modules: wecomCliCatalog() }
104
166
  }
105
167
 
106
168
  export function probeWecomCli(run: WecomCliRunner = defaultWecomCliRunner): WecomCliStatus {
169
+ const bin = resolveWecomCliBin()
107
170
  const help = run(['--help'])
108
171
  if (help.errorCode === 'ENOENT') {
109
- return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
172
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING, bin })
110
173
  }
111
174
  const helpText = help.stdout + help.stderr
112
175
  if (help.status !== 0 && helpText.trim().length === 0) {
113
- return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
176
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING, bin })
177
+ }
178
+
179
+ const version = run(['--version'])
180
+ const versionText = version.stdout + version.stderr
181
+ if (!isWecomCliModern(helpText + '\n' + versionText)) {
182
+ return withCatalog({ installed: false, authorized: false, hint: HINT_OUTDATED, bin })
114
183
  }
115
184
 
116
185
  const auth = run(['auth', 'show'])
117
186
  const text = auth.stdout + auth.stderr
118
187
  if (auth.errorCode === 'ENOENT') {
119
- return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
188
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING, bin })
120
189
  }
121
190
  if (looksUnauthorized(text, auth.status)) {
122
- return withCatalog({ installed: true, authorized: false, hint: HINT_UNAUTH })
191
+ return withCatalog({ installed: true, authorized: false, hint: HINT_UNAUTH, bin })
123
192
  }
124
- return withCatalog({ installed: true, authorized: true, hint: HINT_OK })
193
+ return withCatalog({ installed: true, authorized: true, hint: HINT_OK, bin })
125
194
  }
@@ -6,4 +6,4 @@
6
6
 
7
7
  雷泽自己的索引是 `wecom-office/SKILL.md`。
8
8
 
9
- 运行时 CLI:`npm i -g @wecom/cli@>=1.1.0`,然后 `wecom-cli auth init`。`0.1.x` 命令面不同,不要混用。
9
+ 运行时 CLI:suite 依赖钉 `@wecom/cli@1.1.0`(随插件进 profile)。启动时把其 `bin` 目录插到 PATH 最前,避开 Homebrew 全局 `0.1.x`。授权:`wecom-cli auth init`。`wecomcli-shared` 的安装步骤已改成「重装 suite」,不要再引导 `npm i -g`。
@@ -28,5 +28,5 @@ description: 企业微信办公能力索引。官方 wecom-cli v1.1+ 已面向
28
28
  2. 先读 `wecomcli-shared`,再读对应 `wecomcli-*`。子命令以 `wecom-cli <模块> --help` 为准。
29
29
  3. CLI 返回未授权 / 无权限,才说**该模块未开通**。不要总结成「当前系统没有企业微信 MCP」,也不要说「超过 10 人没有通讯录」。
30
30
  4. 设置页 **BotID + Secret** 是房间**消息通道**(同事在企微跟哪个 bot 说话进哪个 Harness 房间)。**继续用手填绑定,不要改成扫码。**
31
- 5. 办公 API 另走 `wecom-cli auth init`(官方支持扫码,也可 `--manual`)。两套凭证不要混。
31
+ 5. 办公 API:suite 已带 `@wecom/cli@1.1.0`。授权跑 `wecom-cli auth init`(可扫码)。若 PATH 上是 Homebrew `0.1.9`,改用 profile / `npx --no-install wecom-cli`。两套凭证不要混。
32
32
  6. 未另批前不要群发、不要改通讯录结构、不要对外发邮件。
@@ -11,18 +11,19 @@ description: wecom-cli 业务技能的公共前置检查、获取机器人及授
11
11
 
12
12
  ## Step 1:检查 CLI 安装与版本
13
13
 
14
+ ReZ-TI suite 已依赖 `@wecom/cli@1.1.0`(随 `dsh plugin add @rezti/dsh-rez-suite` 装进 profile)。**优先用 profile 里的二进制**,不要用 Homebrew 全局 `0.1.x`(没有 `mail` / 完整 `auth`)。
15
+
14
16
  ```bash
15
17
  wecom-cli --version
18
+ # 若输出是 0.1.x,或 which wecom-cli 指向 /opt/homebrew,改用:
19
+ # npx --no-install wecom-cli --version
20
+ # 或:node "$(node -p \"require('path').join(require('path').dirname(require.resolve('@wecom/cli/package.json')),'bin/wecom.js')\")" --version
16
21
  ```
17
22
 
18
- - 命令成功,且输出中的版本号不低于 `1.1.0` → 继续 Step 2。
19
- - 命令不存在、执行报错或版本号低于 `1.1.0` → 执行安装/升级:
20
-
21
- ```bash
22
- npm install -g @wecom/cli
23
- ```
23
+ - 命令成功,且版本号不低于 `1.1.0` → 继续 Step 2。
24
+ - 命令不存在、执行报错或版本仍低于 `1.1.0` → **不要** `npm install -g @wecom/cli`(容易仍被旧 PATH 盖住)。请用户重装 suite:`dsh plugin --profile web add @rezti/dsh-rez-suite@<当前版>`。仅当 suite 依赖确实缺失时,才可:`npm i @wecom/cli@1.1.0 --prefix "$HOME/.local"` 并把 `$HOME/.local/bin` 放到 PATH **最前**。
24
25
 
25
- 安装完成后重新执行 `wecom-cli --version`;仍失败或版本仍低于 `1.1.0` 时停止业务操作,并把错误告知用户。
26
+ 安装/重装后重新执行 `wecom-cli --version`;仍失败或版本仍低于 `1.1.0` 时停止业务操作,并把错误告知用户。
26
27
 
27
28
  ## Step 2:检查授权状态
28
29