@rezti/dsh-rez-suite 0.1.48 → 0.1.50

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +3 -3
  3. package/README.zh.md +2 -2
  4. package/cordis.patch.yml +1 -1
  5. package/lib/client.d.ts +37 -3
  6. package/lib/client.js +533 -8
  7. package/lib/index.js +247 -19
  8. package/lib/style.css +18 -0
  9. package/package.json +4 -3
  10. package/src/boss/mount.ts +4 -3
  11. package/src/boss/register.ts +1 -1
  12. package/src/boss/seed.ts +29 -0
  13. package/src/changelog.ts +48 -0
  14. package/src/channel-board.ts +55 -0
  15. package/src/client/api.ts +15 -0
  16. package/src/client/locales.ts +74 -6
  17. package/src/client/panel/BoardTab.tsx +136 -0
  18. package/src/client/panel/ConfigTab.tsx +137 -2
  19. package/src/client/panel/StatusTab.tsx +31 -0
  20. package/src/client/panel/panel.module.css +18 -0
  21. package/src/client/settings-card.tsx +4 -1
  22. package/src/esphome-lab.ts +4 -3
  23. package/src/index.ts +3 -2
  24. package/src/observe-mcp.ts +1 -1
  25. package/src/protocol.ts +37 -0
  26. package/src/routes.ts +7 -1
  27. package/src/tools.ts +49 -6
  28. package/src/wecom-cli.ts +125 -0
  29. package/templates/boss/ops/AGENTS.md +2 -0
  30. package/templates/shared/wecom-cli.SOURCE.md +5 -0
  31. package/templates/shared/wecom-office/SKILL.md +31 -0
  32. package/templates/shared/wecomcli-calendar/SKILL.md +303 -0
  33. package/templates/shared/wecomcli-contact/SKILL.md +58 -0
  34. package/templates/shared/wecomcli-disk/SKILL.md +389 -0
  35. package/templates/shared/wecomcli-doc/SKILL.md +137 -0
  36. package/templates/shared/wecomcli-doc-manage/SKILL.md +132 -0
  37. package/templates/shared/wecomcli-email/SKILL.md +218 -0
  38. package/templates/shared/wecomcli-media/SKILL.md +98 -0
  39. package/templates/shared/wecomcli-meeting/SKILL.md +373 -0
  40. package/templates/shared/wecomcli-message/SKILL.md +200 -0
  41. package/templates/shared/wecomcli-shared/SKILL.md +73 -0
  42. package/templates/shared/wecomcli-sheet/SKILL.md +172 -0
  43. package/templates/shared/wecomcli-smartpage/SKILL.md +170 -0
  44. package/templates/shared/wecomcli-smartsheet/SKILL.md +154 -0
  45. package/templates/shared/wecomcli-todo/SKILL.md +76 -0
  46. package/templates/staff/design/AGENTS.md +2 -1
  47. package/templates/staff/ecommerce/.agents/skills/ops-ecommerce/SKILL.md +6 -0
  48. package/templates/staff/ecommerce/AGENTS.md +48 -0
  49. package/templates/staff/ecommerce/BOOTSTRAP.md +23 -0
  50. package/templates/staff/ecommerce/IDENTITY.md +8 -0
  51. package/templates/staff/ecommerce/MEMORY.md +9 -0
  52. package/templates/staff/ecommerce/PRIORITIES.md +3 -0
  53. package/templates/staff/ecommerce/SOUL.md +5 -0
  54. package/templates/staff/ecommerce/USER.md +8 -0
  55. package/templates/staff/hr/.agents/skills/staff-onboard-keys/SKILL.md +9 -7
  56. package/templates/staff/lab/AGENTS.md +1 -1
  57. package/templates/staff/publish/.agents/skills/ops-publish/SKILL.md +6 -0
  58. package/templates/staff/publish/AGENTS.md +56 -0
  59. package/templates/staff/publish/BOOTSTRAP.md +23 -0
  60. package/templates/staff/publish/IDENTITY.md +8 -0
  61. package/templates/staff/publish/MEMORY.md +9 -0
  62. package/templates/staff/publish/PRIORITIES.md +3 -0
  63. package/templates/staff/publish/SOUL.md +6 -0
  64. package/templates/staff/publish/USER.md +8 -0
package/src/protocol.ts CHANGED
@@ -123,11 +123,25 @@ export interface RezServerStatus {
123
123
  startedAt?: number
124
124
  }
125
125
 
126
+ /** Official @wecom/cli office APIs. Not mcp__wechat__*. */
127
+ export interface RezWecomCliModule {
128
+ id: string
129
+ label: string
130
+ }
131
+
132
+ export interface RezWecomCliStatus {
133
+ installed: boolean
134
+ authorized: boolean
135
+ modules: RezWecomCliModule[]
136
+ hint: string
137
+ }
138
+
126
139
  /** Complete /api/dsh-rez-suite/status response. */
127
140
  export interface RezStatusResponse {
128
141
  role: RezRoleId
129
142
  servers: RezServerStatus[]
130
143
  totalRegisteredTools: number
144
+ wecomCli: RezWecomCliStatus
131
145
  }
132
146
 
133
147
  /** QClaw-style WeChat channel status (scan QR, then talk in WeChat). */
@@ -142,6 +156,28 @@ export interface RezWeixinStatus {
142
156
  hint: string
143
157
  }
144
158
 
159
+ /** Accio-style WeCom AI bot: one BotID+Secret per Harness room. */
160
+ export type RezWecomBindingPhase = 'idle' | 'connecting' | 'authenticated' | 'error'
161
+
162
+ export interface RezWecomBotPublic {
163
+ room: string
164
+ botId: string
165
+ secretConfigured: boolean
166
+ }
167
+
168
+ export interface RezWecomBindingStatus {
169
+ room: string
170
+ botId: string
171
+ phase: RezWecomBindingPhase
172
+ hint: string
173
+ }
174
+
175
+ export interface RezWecomChannelsResponse {
176
+ rooms: string[]
177
+ bots: RezWecomBotPublic[]
178
+ status: RezWecomBindingStatus[]
179
+ }
180
+
145
181
  /** One audit ledger row. */
146
182
  export interface RezAuditRecord {
147
183
  id: number
@@ -197,5 +233,6 @@ export const REZ_API = {
197
233
  auditReset: REZ_API_BASE + '/audit/reset',
198
234
  weixin: REZ_API_BASE + '/weixin',
199
235
  weixinVerify: REZ_API_BASE + '/weixin/verify',
236
+ wecomChannels: REZ_API_BASE + '/wecom/channels',
200
237
  update: REZ_API_BASE + '/update',
201
238
  } as const
package/src/routes.ts CHANGED
@@ -18,6 +18,7 @@ import { isRezRoleId, REZ_API } from './protocol.ts'
18
18
  import type { McpHost } from './mcp-host.ts'
19
19
  import type { TokenManager } from './token-manager.ts'
20
20
  import { publicConfig } from './store.ts'
21
+ import { probeWecomCli } from './wecom-cli.ts'
21
22
 
22
23
  const MAX_JSON_BODY_BYTES = 256 * 1024
23
24
  const SECRET_MASK = '********'
@@ -200,7 +201,12 @@ export function makeRoutes(deps: RezRoutesDeps): WebRoute[] {
200
201
  if (!guard(req, res, 'GET')) return
201
202
  const servers = host.status()
202
203
  const totalRegisteredTools = servers.reduce((sum, server) => sum + server.registeredTools, 0)
203
- writeJson(res, 200, { role: getConfig().role, servers, totalRegisteredTools })
204
+ writeJson(res, 200, {
205
+ role: getConfig().role,
206
+ servers,
207
+ totalRegisteredTools,
208
+ wecomCli: probeWecomCli(),
209
+ })
204
210
  },
205
211
  },
206
212
  {
package/src/tools.ts CHANGED
@@ -8,12 +8,13 @@ import { defineTool as defineToolOriginal } from '@deepseek-ai/dsh-tools'
8
8
  /** Loosen the typed helper for the suite's small fixed tools. */
9
9
  const defineTool = defineToolOriginal as unknown as (options: any) => any
10
10
  import type { ContentBlock } from '@deepseek-ai/dsh-llm'
11
- import type { RezConfig, RezTestResult } from './protocol.ts'
11
+ import type { RezConfig, RezTestResult, RezWecomCliStatus } from './protocol.ts'
12
12
  import type { McpHost } from './mcp-host.ts'
13
13
  import type { TokenManager } from './token-manager.ts'
14
14
  import { publicConfig } from './store.ts'
15
15
  import { cwdFromExecution } from './rooms.ts'
16
16
  import { compileEsphomeFirmware } from './esphome-lab.ts'
17
+ import { probeWecomCli } from './wecom-cli.ts'
17
18
 
18
19
  function text(value: string): ContentBlock[] {
19
20
  return [{ type: 'text', text: value }]
@@ -30,10 +31,25 @@ function renderTest(results: RezTestResult[]): string {
30
31
  }).join('\n')
31
32
  }
32
33
 
33
- export function rezStatusTool(host: McpHost, config: () => RezConfig) {
34
+ function renderWecomCli(cli: RezWecomCliStatus | undefined): string {
35
+ const row = cli ?? { installed: false, authorized: false, modules: [], hint: '' }
36
+ const modules = (row.modules ?? []).map((item) => `${item.label}(${item.id})`).join('、')
37
+ return [
38
+ 'wecom-cli(企微办公能力,不是 MCP): installed=' + (row.installed ? 'yes' : 'no')
39
+ + ' authorized=' + (row.authorized ? 'yes' : 'no'),
40
+ modules.length > 0 ? 'modules: ' + modules : 'modules: (官方目录见 wecomcli-* 技能)',
41
+ row.hint,
42
+ ].join('\n')
43
+ }
44
+
45
+ export function rezStatusTool(
46
+ host: McpHost,
47
+ config: () => RezConfig,
48
+ wecomCli: () => RezWecomCliStatus = probeWecomCli,
49
+ ) {
34
50
  return defineTool({
35
51
  name: 'rez_status',
36
- description: 'List the dsh-rez-suite MCP server statuses and how many tools each server registered for the current role. Triggers: rez, MCP, 连接状态.',
52
+ description: 'List MCP server statuses plus WeCom office APIs (official wecom-cli, not mcp__wechat__). Triggers: rez, MCP, 连接状态, 企业微信, 企微能力.',
37
53
  parameters: {},
38
54
  output: {
39
55
  schema: {
@@ -57,10 +73,36 @@ export function rezStatusTool(host: McpHost, config: () => RezConfig) {
57
73
  },
58
74
  },
59
75
  },
76
+ wecomCli: {
77
+ type: 'object',
78
+ required: true,
79
+ additionalProperties: false,
80
+ properties: {
81
+ installed: { type: 'boolean', required: true },
82
+ authorized: { type: 'boolean', required: true },
83
+ hint: { type: 'string', required: true },
84
+ modules: {
85
+ type: 'array',
86
+ required: true,
87
+ items: {
88
+ type: 'object',
89
+ additionalProperties: false,
90
+ properties: {
91
+ id: { type: 'string', required: true },
92
+ label: { type: 'string', required: true },
93
+ },
94
+ },
95
+ },
96
+ },
97
+ },
60
98
  },
61
99
  additionalProperties: false,
62
100
  },
63
- render: (_args: any, value: { role?: string; servers?: Array<Record<string, unknown>> }) => {
101
+ render: (_args: any, value: {
102
+ role?: string
103
+ servers?: Array<Record<string, unknown>>
104
+ wecomCli?: RezWecomCliStatus
105
+ }) => {
64
106
  const rows = (value.servers ?? []).map(server => [
65
107
  server.server,
66
108
  server.state,
@@ -68,7 +110,8 @@ export function rezStatusTool(host: McpHost, config: () => RezConfig) {
68
110
  server.registeredTools,
69
111
  server.lastError ?? '',
70
112
  ].join(' | '))
71
- return text('role: ' + (value.role ?? '') + '\n' + ['server | state | discovered | registered | error', '--- | --- | --- | --- | ---', ...rows].join('\n'))
113
+ const mcp = 'role: ' + (value.role ?? '') + '\n' + ['server | state | discovered | registered | error', '--- | --- | --- | --- | ---', ...rows].join('\n')
114
+ return text(mcp + '\n\n' + renderWecomCli(value.wecomCli))
72
115
  },
73
116
  },
74
117
  async execute(_args: unknown, exec?: { agent?: unknown }) {
@@ -78,7 +121,7 @@ export function rezStatusTool(host: McpHost, config: () => RezConfig) {
78
121
  if (clean.startedAt === undefined) delete clean.startedAt
79
122
  return clean
80
123
  })
81
- return { role: config().role, servers }
124
+ return { role: config().role, servers, wecomCli: wecomCli() }
82
125
  },
83
126
  })
84
127
  }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Official WeCom office APIs are @wecom/cli, not mcp__wechat__*.
3
+ * TAPD bug 1154488930001000054: missing MCP row was treated as “no WeCom”.
4
+ * Official CLI v1.1+ opened 通讯录 / 在线文档 etc. for every company size
5
+ * (https://github.com/WecomTeam/wecom-cli). Kelvin enabled those modules.
6
+ */
7
+
8
+ import { spawnSync } from 'node:child_process'
9
+
10
+ export interface WecomCliModule {
11
+ id: string
12
+ label: string
13
+ }
14
+
15
+ export interface WecomCliStatus {
16
+ installed: boolean
17
+ authorized: boolean
18
+ modules: WecomCliModule[]
19
+ hint: string
20
+ }
21
+
22
+ export interface WecomCliRunResult {
23
+ status: number | null
24
+ stdout: string
25
+ stderr: string
26
+ errorCode?: string
27
+ }
28
+
29
+ export type WecomCliRunner = (argv: string[]) => WecomCliRunResult
30
+
31
+ /** Official @wecom/cli >=1.1 catalog. Do not invent extra HTTP APIs. */
32
+ export const WECOM_CLI_CATALOG: readonly WecomCliModule[] = [
33
+ { id: 'contact', label: '通讯录' },
34
+ { id: 'doc', label: '在线文档' },
35
+ { id: 'doc-manage', label: '文档管理' },
36
+ { id: 'sheet', label: '在线表格' },
37
+ { id: 'smartsheet', label: '智能表格' },
38
+ { id: 'smartpage', label: '智能文档' },
39
+ { id: 'todo', label: '待办' },
40
+ { id: 'calendar', label: '日程' },
41
+ { id: 'meeting', label: '会议' },
42
+ { id: 'email', label: '邮件' },
43
+ { id: 'disk', label: '微盘' },
44
+ { id: 'message', label: '消息' },
45
+ { id: 'media', label: '媒体文件' },
46
+ ]
47
+
48
+ const MODULE_LABELS = WECOM_CLI_CATALOG.map((row) => row.label).join('/')
49
+
50
+ 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 行就说没有企微能力。',
55
+ ].join('')
56
+
57
+ const HINT_UNAUTH = [
58
+ '已安装 wecom-cli,尚未授权。跑 wecom-cli auth init。',
59
+ `授权后可用:${MODULE_LABELS}。本企业已开通这些品类。`,
60
+ 'CLI 报某模块未授权才说该能力未开通;不要说「没有企微 MCP」,也不要说 10 人限制。',
61
+ ].join('')
62
+
63
+ const HINT_OK = [
64
+ `企微办公能力走 wecom-cli >=1.1(${MODULE_LABELS})。本企业已开通。`,
65
+ '按房间里官方 wecomcli-* 技能执行,命令以 wecom-cli <模块> --help 为准。',
66
+ '不要用旧帮助中心「超过 10 人只能用文档」的说法。CLI 报未授权才说该模块未开通。',
67
+ '设置页 BotID+Secret 只是房间消息通道。不要把没有 mcp__wechat__* 当成没有企微能力。',
68
+ ].join('')
69
+
70
+ export function wecomCliCatalog(): WecomCliModule[] {
71
+ return WECOM_CLI_CATALOG.map((row) => ({ ...row }))
72
+ }
73
+
74
+ export function parseWecomCliHelpModules(helpText: string): WecomCliModule[] {
75
+ const text = helpText.toLowerCase()
76
+ return WECOM_CLI_CATALOG.filter((row) => {
77
+ const token = row.id.replace(/-/g, '[-_ ]?')
78
+ return new RegExp(`(?:^|[^a-z])${token}(?:[^a-z]|$)`).test(text)
79
+ })
80
+ }
81
+
82
+ export function defaultWecomCliRunner(argv: string[]): WecomCliRunResult {
83
+ const result = spawnSync('wecom-cli', argv, {
84
+ encoding: 'utf8',
85
+ timeout: 4000,
86
+ env: process.env,
87
+ })
88
+ const error = result.error as NodeJS.ErrnoException | undefined
89
+ return {
90
+ status: result.status,
91
+ stdout: typeof result.stdout === 'string' ? result.stdout : '',
92
+ stderr: typeof result.stderr === 'string' ? result.stderr : '',
93
+ errorCode: error?.code,
94
+ }
95
+ }
96
+
97
+ function looksUnauthorized(text: string, status: number | null): boolean {
98
+ if (status !== 0) return true
99
+ return /未授权|未登录|未初始化|not (logged|auth)|unauthorized|请先.*auth|auth init/i.test(text)
100
+ }
101
+
102
+ function withCatalog(partial: Omit<WecomCliStatus, 'modules'>): WecomCliStatus {
103
+ return { ...partial, modules: wecomCliCatalog() }
104
+ }
105
+
106
+ export function probeWecomCli(run: WecomCliRunner = defaultWecomCliRunner): WecomCliStatus {
107
+ const help = run(['--help'])
108
+ if (help.errorCode === 'ENOENT') {
109
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
110
+ }
111
+ const helpText = help.stdout + help.stderr
112
+ if (help.status !== 0 && helpText.trim().length === 0) {
113
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
114
+ }
115
+
116
+ const auth = run(['auth', 'show'])
117
+ const text = auth.stdout + auth.stderr
118
+ if (auth.errorCode === 'ENOENT') {
119
+ return withCatalog({ installed: false, authorized: false, hint: HINT_MISSING })
120
+ }
121
+ if (looksUnauthorized(text, auth.status)) {
122
+ return withCatalog({ installed: true, authorized: false, hint: HINT_UNAUTH })
123
+ }
124
+ return withCatalog({ installed: true, authorized: true, hint: HINT_OK })
125
+ }
@@ -41,6 +41,8 @@
41
41
  - Odoo 只读采购在途 / 库存 / 逾期;TAPD 只看迭代红灯。
42
42
  - 禁止改库存、群发、自动上架、在这里控 HA。企微只出「需要老板出面」的摘要。
43
43
  - 拍图入库不在本室:运营岗去「设计」房间;终审在产品室。
44
+ - 商品上架预览不在本室:运营岗去「电商」房间(一期阿里;Amazon 等有号)。
45
+ - 媒体分发预览不在本室:运营岗去「发布」房间。
44
46
  - 工商查询请切到法务助理,避免串房间。
45
47
 
46
48
  ## 公司
@@ -0,0 +1,5 @@
1
+ # 官方 wecom-cli Skills 来源
2
+
3
+ `wecomcli-*` 目录里的 `SKILL.md` 来自 [WecomTeam/wecom-cli](https://github.com/WecomTeam/wecom-cli)(MIT)。不要手改这些文件;升级时重新拉取。
4
+
5
+ 雷泽自己的索引是 `wecom-office/SKILL.md`。
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: wecom-office
3
+ description: 企业微信办公能力索引。官方 wecom-cli v1.1+ 已面向各规模企业开放通讯录、在线文档、在线表格、智能表格、智能文档、待办、日程、会议、邮件、微盘、消息。Triggers: 企微、企业微信、通讯录、在线文档、待办、日程、会议、邮件、微盘、智能表格。
4
+ ---
5
+
6
+ 不要看 `rez_status` 的 MCP 表里有没有「wechat」。企微办公能力是官方 CLI(https://github.com/WecomTeam/wecom-cli,MIT),不是 mcp__wechat__*,不是自写 HTTP/WebSocket,也不是旧群 webhook MCP。
7
+
8
+ 官方 **@wecom/cli >= 1.1** 已面向各规模企业开放通讯录等能力(不再受旧帮助中心「超过 10 人只能用文档」限制)。**本企业已开通这些品类。** 命令与工作流以同目录官方 `wecomcli-*` 技能为准,不要自己编 API。
9
+
10
+ | 技能 | 品类 |
11
+ |---|---|
12
+ | `wecomcli-shared` | 安装 / 授权前置检查(先读这个) |
13
+ | `wecomcli-contact` | 通讯录(按姓名/拼音/别名搜 userid) |
14
+ | `wecomcli-doc` | 在线文档 |
15
+ | `wecomcli-doc-manage` | 文档搜索、权限、加入规则 |
16
+ | `wecomcli-sheet` | 在线表格 |
17
+ | `wecomcli-smartsheet` | 智能表格 |
18
+ | `wecomcli-smartpage` | 智能文档 |
19
+ | `wecomcli-todo` | 待办 |
20
+ | `wecomcli-calendar` | 日程 |
21
+ | `wecomcli-meeting` | 会议 |
22
+ | `wecomcli-email` | 邮件 |
23
+ | `wecomcli-disk` | 微盘 |
24
+ | `wecomcli-message` | 消息 |
25
+ | `wecomcli-media` | 媒体文件 |
26
+
27
+ 1. 先看 `rez_status` 的 `wecomCli.modules`,或跑 `wecom-cli auth show`。
28
+ 2. 先读 `wecomcli-shared`,再读对应 `wecomcli-*`。子命令以 `wecom-cli <模块> --help` 为准。
29
+ 3. CLI 返回未授权 / 无权限,才说**该模块未开通**。不要总结成「当前系统没有企业微信 MCP」,也不要说「超过 10 人没有通讯录」。
30
+ 4. 设置页 BotID + Secret 只是房间**消息通道**。办公 API 用 `wecom-cli auth init` 那套凭证,不要混成一回事。
31
+ 5. 未另批前不要群发、不要改通讯录结构、不要对外发邮件。