weaver-work-cli 0.1.5 → 0.1.7

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 (225) hide show
  1. package/README.md +14 -9
  2. package/dist/cmd/skills/index.js +60 -0
  3. package/dist/internal/e10/auth/commands.js +48 -32
  4. package/dist/internal/e10/auth/session.js +44 -3
  5. package/dist/internal/e10/auth/xiaoe.js +363 -11
  6. package/dist/internal/e10/context.js +4 -2
  7. package/dist/internal/skills/detector.js +42 -0
  8. package/dist/internal/skills/install.js +50 -3
  9. package/dist/shortcuts/archive/render/search-format.js +12 -3
  10. package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
  11. package/dist/shortcuts/ebuilder-form/errors.js +54 -0
  12. package/dist/shortcuts/ebuilder-form/host.js +1238 -0
  13. package/dist/shortcuts/ebuilder-form/index.js +108 -0
  14. package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
  15. package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
  16. package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
  17. package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
  18. package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
  19. package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
  20. package/dist/shortcuts/ebuilder-form/operations.js +8 -0
  21. package/dist/shortcuts/ehr/operations/salary.js +2 -140
  22. package/dist/shortcuts/im/continuation.js +70 -0
  23. package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
  24. package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
  25. package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
  26. package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
  27. package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
  28. package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
  29. package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
  30. package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
  31. package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
  32. package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
  33. package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
  34. package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
  35. package/dist/shortcuts/im/operations/shared.js +442 -0
  36. package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
  37. package/dist/shortcuts/im/operations/user-remind.js +201 -0
  38. package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
  39. package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
  40. package/dist/shortcuts/index.js +10 -2
  41. package/dist/shortcuts/invoice/manifest.js +113 -10
  42. package/dist/shortcuts/invoice/operations/import.js +39 -14
  43. package/dist/shortcuts/invoice/operations/reim.js +38 -18
  44. package/dist/shortcuts/invoice/operations/shared.js +35 -6
  45. package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
  46. package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
  47. package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
  48. package/dist/shortcuts/okr/errors.js +70 -0
  49. package/dist/shortcuts/okr/host.js +169 -0
  50. package/dist/shortcuts/okr/index.js +155 -0
  51. package/dist/shortcuts/okr/manifest.js +70 -0
  52. package/dist/shortcuts/okr/operations/link.js +305 -0
  53. package/dist/shortcuts/okr/operations/read.js +481 -0
  54. package/dist/shortcuts/okr/operations/registry.js +31 -0
  55. package/dist/shortcuts/okr/operations/shared.js +381 -0
  56. package/dist/shortcuts/okr/operations/types.js +88 -0
  57. package/dist/shortcuts/okr/operations/write.js +838 -0
  58. package/dist/shortcuts/okr/operations.js +8 -0
  59. package/dist/shortcuts/project/continuation.js +70 -0
  60. package/dist/shortcuts/project/errors.js +53 -0
  61. package/dist/shortcuts/project/host.js +160 -0
  62. package/dist/shortcuts/project/index.js +106 -0
  63. package/dist/shortcuts/project/manifest.js +36 -0
  64. package/dist/shortcuts/project/operations/read.js +551 -0
  65. package/dist/shortcuts/project/operations/registry.js +31 -0
  66. package/dist/shortcuts/project/operations/shared.js +68 -0
  67. package/dist/shortcuts/project/operations/types.js +39 -0
  68. package/dist/shortcuts/project/operations/write.js +266 -0
  69. package/dist/shortcuts/project/operations.js +8 -0
  70. package/dist/shortcuts/workflow/continuation.js +508 -0
  71. package/dist/shortcuts/workflow/endpoints.js +149 -0
  72. package/dist/shortcuts/workflow/errors.js +112 -0
  73. package/dist/shortcuts/workflow/host.js +224 -0
  74. package/dist/shortcuts/workflow/index.js +116 -0
  75. package/dist/shortcuts/workflow/manifest.js +30 -0
  76. package/dist/shortcuts/workflow/operations/form.js +4159 -0
  77. package/dist/shortcuts/workflow/operations/read.js +3778 -0
  78. package/dist/shortcuts/workflow/operations/registry.js +33 -0
  79. package/dist/shortcuts/workflow/operations/shared.js +167 -0
  80. package/dist/shortcuts/workflow/operations/types.js +44 -0
  81. package/dist/shortcuts/workflow/operations/write.js +1991 -0
  82. package/dist/shortcuts/workflow/operations.js +119 -0
  83. package/dist/shortcuts/workflow/state.js +182 -0
  84. package/docs/SKILL.md +5 -3
  85. package/docs/_catalog.md +10 -1
  86. package/docs/agent-invoice.md +2 -2
  87. package/docs/agent-skill-install.md +2 -2
  88. package/docs/e10-auth.md +3 -1
  89. package/docs/ebuilder-form.md +38 -0
  90. package/docs/im.md +104 -0
  91. package/docs/invoice.md +5 -5
  92. package/docs/okr.md +122 -0
  93. package/docs/operation-manual.md +10 -7
  94. package/docs/project.md +69 -0
  95. package/docs/skill-review-2026-09-19.md +68 -0
  96. package/package.json +3 -2
  97. package/scripts/package-skill.mjs +38 -11
  98. package/scripts/sync-connector-skills.mjs +172 -0
  99. package/skill-template/business-info.json +357 -16
  100. package/skill-template/domains/ebuilder-form.md +5 -0
  101. package/skill-template/domains/okr.md +18 -0
  102. package/skill-template/domains/shijingran.md +3 -0
  103. package/skill-template/domains/skill-maker.md +4 -4
  104. package/skill-template/domains/workflow.md +22 -0
  105. package/skill-template/domains/yepiaotong.md +4 -2
  106. package/skill-template/domains/yimiaoban.md +13 -10
  107. package/skill-template/product.json +5 -0
  108. package/skill-template/skill-template.md +1 -0
  109. package/skills/weaver-e10-calendar/SKILL.md +5 -1
  110. package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
  111. package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
  112. package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
  113. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
  114. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
  115. package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
  116. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
  117. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
  118. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
  119. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
  120. package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
  121. package/skills/weaver-e10-esb/SKILL.md +1 -1
  122. package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
  123. package/skills/weaver-e10-hrm/SKILL.md +1 -1
  124. package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
  125. package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
  126. package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
  127. package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
  128. package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
  129. package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
  130. package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
  131. package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
  132. package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
  133. package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
  134. package/skills/weaver-e10-jucailin/SKILL.md +2 -2
  135. package/skills/weaver-e10-mail/SKILL.md +22 -6
  136. package/skills/weaver-e10-meeting/SKILL.md +22 -1
  137. package/skills/weaver-e10-okr/SKILL.md +139 -0
  138. package/skills/weaver-e10-okr/references/okr-align.md +59 -0
  139. package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
  140. package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
  141. package/skills/weaver-e10-okr/references/okr-query.md +105 -0
  142. package/skills/weaver-e10-okr/references/okr-write.md +94 -0
  143. package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
  144. package/skills/weaver-e10-plan/SKILL.md +3 -3
  145. package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
  146. package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
  147. package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
  148. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
  149. package/skills/weaver-e10-shared/SKILL.md +79 -11
  150. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
  151. package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
  152. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
  153. package/skills/weaver-e10-shijingran/SKILL.md +83 -0
  154. package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
  155. package/skills/weaver-e10-shijingran/references/operations.md +154 -0
  156. package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
  157. package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
  158. package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
  159. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
  160. package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
  161. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
  162. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
  163. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
  164. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
  165. package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
  166. package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
  167. package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
  168. package/skills/weaver-e10-workflow/SKILL.md +250 -0
  169. package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
  170. package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
  171. package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
  172. package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
  173. package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
  174. package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
  175. package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
  176. package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
  177. package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
  178. package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
  179. package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
  180. package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
  181. package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
  182. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
  183. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
  184. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
  185. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
  186. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
  187. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
  188. package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
  189. package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
  190. package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
  191. package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
  192. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
  193. package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
  194. package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
  195. package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
  196. package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
  197. package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
  198. package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
  199. package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
  200. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
  201. package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
  202. package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
  203. package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
  204. package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
  205. package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
  206. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
  207. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
  208. package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
  209. package/docs/yimiaoban.md +0 -86
  210. package/skills/weaver-e10-calendar/product.json +0 -8
  211. package/skills/weaver-e10-esb/product.json +0 -8
  212. package/skills/weaver-e10-hrm/product.json +0 -8
  213. package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
  214. package/skills/weaver-e10-jucailin/product.json +0 -8
  215. package/skills/weaver-e10-mail/product.json +0 -8
  216. package/skills/weaver-e10-meeting/product.json +0 -8
  217. package/skills/weaver-e10-plan/product.json +0 -8
  218. package/skills/weaver-e10-qiyecheng/product.json +0 -8
  219. package/skills/weaver-e10-skill-maker/product.json +0 -8
  220. package/skills/weaver-e10-wenshuding/product.json +0 -8
  221. package/skills/weaver-e10-yepiaotong/product.json +0 -8
  222. package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
  223. package/skills/weaver-e10-yimiaoban/product.json +0 -8
  224. package/skills/weaver-e10-ziguanjia/product.json +0 -8
  225. /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
@@ -1,6 +1,6 @@
1
- import { ymbValidationError } from '../errors.js';
1
+ import { imValidationError } from '../errors.js';
2
2
  import { schemaOf, WRITE, } from './types.js';
3
- import { assertImOk, compact, splitIds, str, toUint } from './shared.js';
3
+ import { assertImOk, compact, splitIds, str, toUint, unwrapInner } from './shared.js';
4
4
  import { makeWritePair } from './write-util.js';
5
5
  /** 解析 uid 列表为 {cid,uid} 成员,支持 failed 移除。 */
6
6
  async function resolveMembers(args, env) {
@@ -50,23 +50,23 @@ async function defaultGroupName(members, nameOf, selfUid, env) {
50
50
  }
51
51
  // ── 建群 ──
52
52
  const groupCreatePair = makeWritePair({
53
- action: 'yimiaoban.group.create',
53
+ action: 'im.group.create',
54
54
  async plan(args, env) {
55
55
  const rawUsers = Array.isArray(args.users) ? args.users.map((x) => String(x)) : splitIds(args.users, 'users');
56
56
  const names = Array.isArray(args.names) ? args.names.map((x) => String(x)) : [];
57
57
  if (names.length && names.length !== rawUsers.length) {
58
- throw ymbValidationError('names_mismatch', 'names 数量必须与 users 一致(顺序一一对应)');
58
+ throw imValidationError('names_mismatch', 'names 数量必须与 users 一致(顺序一一对应)');
59
59
  }
60
60
  const { users, failed, names: resolvedNames } = await resolveMembers(args, env);
61
61
  if (failed.length && args.removeFailed !== true) {
62
- throw ymbValidationError('members_resolve_failed', `以下成员信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
62
+ throw imValidationError('members_resolve_failed', `以下成员信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
63
63
  }
64
64
  if (users.length === 0)
65
- throw ymbValidationError('members_empty', '没有可入群的成员');
65
+ throw imValidationError('members_empty', '没有可入群的成员');
66
66
  // 🔴 群成员必须包含操作者自己(服务端依据 eteamsid 把操作者设为群主)
67
67
  const selfUid = str((await env.host.getSession()).userId);
68
68
  if (selfUid && !users.some((u) => u.uid === selfUid)) {
69
- throw ymbValidationError('self_not_included', `不允许创建不包含自己的群聊:users 必须包含操作者自己(uid=${selfUid})`);
69
+ throw imValidationError('self_not_included', `不允许创建不包含自己的群聊:users 必须包含操作者自己(uid=${selfUid})`);
70
70
  }
71
71
  // 默认群名:调用方 names(与 users 顺序对应)优先,其次 hrm 解析姓名,最后回退 uid
72
72
  const providedNames = {};
@@ -86,7 +86,7 @@ const groupCreatePair = makeWritePair({
86
86
  return !uid || uid === '0' || !cid || cid === '0';
87
87
  });
88
88
  if (adminFailed.length) {
89
- throw ymbValidationError('admins_resolve_failed', `管理员信息获取失败(cid/uid 缺失或为 0):${adminFailed.map((uid) => adminMap[uid]?.name || uid).join('、')};请确认后重试(不可静默替换操作人身份)`, { details: adminFailed });
89
+ throw imValidationError('admins_resolve_failed', `管理员信息获取失败(cid/uid 缺失或为 0):${adminFailed.map((uid) => adminMap[uid]?.name || uid).join('、')};请确认后重试(不可静默替换操作人身份)`, { details: adminFailed });
90
90
  }
91
91
  admins = adminRaw.map((uid) => ({ cid: adminMap[uid]?.cid || '', uid }));
92
92
  }
@@ -114,7 +114,7 @@ const groupCreatePair = makeWritePair({
114
114
  method: 'POST',
115
115
  path: '/api/em/msg/executeIm/group/groupCreate',
116
116
  body,
117
- operation: 'yimiaoban.group.create',
117
+ operation: 'im.group.create',
118
118
  });
119
119
  assertImOk(res, '创建群');
120
120
  // 群 id 可能位于 data / data.data 的 groupId / group_id / id 字段,统一扫描;同时回传服务端原始数据便于核对
@@ -128,17 +128,17 @@ const groupCreatePair = makeWritePair({
128
128
  });
129
129
  // ── 邀请入群 ──
130
130
  const groupInvitePair = makeWritePair({
131
- action: 'yimiaoban.group.invite',
131
+ action: 'im.group.invite',
132
132
  async plan(args, env) {
133
133
  const groupId = str(args.groupId);
134
134
  if (!groupId || groupId === '0')
135
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
135
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
136
136
  const { users, failed } = await resolveMembers(args, env);
137
137
  if (failed.length && args.removeFailed !== true) {
138
- throw ymbValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
138
+ throw imValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
139
139
  }
140
140
  if (users.length === 0)
141
- throw ymbValidationError('members_empty', '没有可邀请的成员');
141
+ throw imValidationError('members_empty', '没有可邀请的成员');
142
142
  return {
143
143
  summary: { action: '邀请入群', groupId, inviteCount: users.length, users: users.map((u) => u.uid), removeFailed: args.removeFailed === true ? failed.map((f) => f.uid) : [] },
144
144
  snapshot: { groupId, users },
@@ -150,7 +150,7 @@ const groupInvitePair = makeWritePair({
150
150
  method: 'POST',
151
151
  path: '/api/em/msg/executeIm/group/inviteGroupUsers',
152
152
  body,
153
- operation: 'yimiaoban.group.invite',
153
+ operation: 'im.group.invite',
154
154
  });
155
155
  assertImOk(res, '邀请入群');
156
156
  return { groupId: plan.groupId, invited: plan.users.length };
@@ -158,17 +158,17 @@ const groupInvitePair = makeWritePair({
158
158
  });
159
159
  // ── 踢出群 ──
160
160
  const groupKickPair = makeWritePair({
161
- action: 'yimiaoban.group.kick',
161
+ action: 'im.group.kick',
162
162
  async plan(args, env) {
163
163
  const groupId = str(args.groupId);
164
164
  if (!groupId || groupId === '0')
165
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
165
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
166
166
  const { users, failed } = await resolveMembers(args, env);
167
167
  if (failed.length && args.removeFailed !== true) {
168
- throw ymbValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
168
+ throw imValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
169
169
  }
170
170
  if (users.length === 0)
171
- throw ymbValidationError('members_empty', '没有可移除的成员');
171
+ throw imValidationError('members_empty', '没有可移除的成员');
172
172
  return {
173
173
  summary: { action: '踢出群成员', groupId, kickCount: users.length, users: users.map((u) => u.uid) },
174
174
  snapshot: { groupId, users },
@@ -180,7 +180,7 @@ const groupKickPair = makeWritePair({
180
180
  method: 'POST',
181
181
  path: '/api/em/msg/executeIm/group/kickGroupUsers',
182
182
  body,
183
- operation: 'yimiaoban.group.kick',
183
+ operation: 'im.group.kick',
184
184
  });
185
185
  assertImOk(res, '踢出群成员');
186
186
  return { groupId: plan.groupId, kicked: plan.users.length };
@@ -188,14 +188,57 @@ const groupKickPair = makeWritePair({
188
188
  });
189
189
  // ── 退群 ──
190
190
  const groupExitPair = makeWritePair({
191
- action: 'yimiaoban.group.exit',
191
+ action: 'im.group.exit',
192
192
  async plan(args, env) {
193
193
  const groupId = str(args.groupId);
194
194
  if (!groupId || groupId === '0')
195
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
195
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
196
+ /**
197
+ * 只读回查群人数与群主:实测「群主/最后一个成员退群」后该群会变成无主空群,
198
+ * 之后 join / destroy 都会被服务端拒绝(1210),必须由客户端或管理员处理。
199
+ * 因此这两种情形要求调用方显式传 acknowledgeOrphanRisk:true 才签发 continuation。
200
+ */
201
+ let memberCount;
202
+ let isOwner = false;
203
+ try {
204
+ const infoRes = await env.host.request({
205
+ method: 'POST',
206
+ path: '/api/em/msg/executeIm/group/queryGroupInfo',
207
+ body: { trans_id: 0, type: 0, mask: 0, group_id: [groupId], flag: 0 },
208
+ operation: 'im.group.exit.plan.info',
209
+ });
210
+ const info = (unwrapInner(infoRes)?.datas || [])[0];
211
+ if (info) {
212
+ const raw = info.user_num ?? info.userNum;
213
+ memberCount = raw === undefined || raw === null ? undefined : Number(raw);
214
+ const hostUid = str(info.host_uid ?? info.hostUid ?? '');
215
+ if (hostUid) {
216
+ const self = await env.host.getSelfIdentity();
217
+ isOwner = hostUid === String(self.uid);
218
+ }
219
+ }
220
+ }
221
+ catch {
222
+ // 回查失败(如已不在群内)不阻断退群,仅在摘要里说明未能核对风险
223
+ }
224
+ const orphanRisk = isOwner || (memberCount !== undefined && memberCount <= 1);
225
+ if (orphanRisk && args.acknowledgeOrphanRisk !== true) {
226
+ throw imValidationError('owner_exit_ack_required', `退群风险确认缺失:${isOwner ? '你是该群群主' : '你是该群最后一个成员'},退群后该群会变成无主空群,之后 join/destroy 都会被服务端拒绝(1210),只能由客户端或管理员处理。确认仍要退群请传 acknowledgeOrphanRisk:true`, { details: { groupId, isOwner, memberCount } });
227
+ }
196
228
  return {
197
- summary: { action: '退出群聊', groupId },
198
- snapshot: { groupId },
229
+ summary: {
230
+ action: '退出群聊',
231
+ groupId,
232
+ ...(memberCount === undefined ? {} : { memberCount }),
233
+ isOwner,
234
+ ...(orphanRisk
235
+ ? {
236
+ orphanRisk: true,
237
+ riskNote: `${isOwner ? '你是群主' : '你是最后一个成员'}:退群后该群会成为无主空群,无法再从 CLI 重新进群或解散(实测 1210 没有权限),需由客户端或管理员处理。`,
238
+ }
239
+ : {}),
240
+ },
241
+ snapshot: { groupId, acknowledgeOrphanRisk: args.acknowledgeOrphanRisk === true },
199
242
  };
200
243
  },
201
244
  async execute(plan, env) {
@@ -203,7 +246,7 @@ const groupExitPair = makeWritePair({
203
246
  method: 'POST',
204
247
  path: '/api/em/msg/executeIm/group/exit',
205
248
  body: { groupId: plan.groupId },
206
- operation: 'yimiaoban.group.exit',
249
+ operation: 'im.group.exit',
207
250
  });
208
251
  assertImOk(res, '退出群聊');
209
252
  return { groupId: plan.groupId };
@@ -211,11 +254,11 @@ const groupExitPair = makeWritePair({
211
254
  });
212
255
  // ── 解散群 ──
213
256
  const groupDestroyPair = makeWritePair({
214
- action: 'yimiaoban.group.destroy',
257
+ action: 'im.group.destroy',
215
258
  async plan(args, env) {
216
259
  const groupId = str(args.groupId);
217
260
  if (!groupId || groupId === '0')
218
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
261
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
219
262
  return {
220
263
  summary: { action: '解散群聊(仅群主可执行,不可恢复)', groupId },
221
264
  snapshot: { groupId },
@@ -226,7 +269,7 @@ const groupDestroyPair = makeWritePair({
226
269
  method: 'POST',
227
270
  path: '/api/em/msg/executeIm/group/destroy',
228
271
  body: { groupId: plan.groupId },
229
- operation: 'yimiaoban.group.destroy',
272
+ operation: 'im.group.destroy',
230
273
  });
231
274
  assertImOk(res, '解散群聊');
232
275
  return { groupId: plan.groupId };
@@ -234,19 +277,19 @@ const groupDestroyPair = makeWritePair({
234
277
  });
235
278
  // ── 申请入群 ──
236
279
  const groupJoinPair = makeWritePair({
237
- action: 'yimiaoban.group.join',
280
+ action: 'im.group.join',
238
281
  async plan(args, env) {
239
282
  const groupId = args.groupId !== undefined && args.groupId !== '' ? str(args.groupId) : '';
240
283
  const token = args.token !== undefined && args.token !== '' ? String(args.token) : '';
241
284
  if (!groupId && !token)
242
- throw ymbValidationError('target_required', 'groupId 与 token 至少其一');
285
+ throw imValidationError('target_required', 'groupId 与 token 至少其一');
243
286
  if (groupId && groupId === '0')
244
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
287
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
245
288
  if (token && token.length > 500)
246
- throw ymbValidationError('token_invalid', 'token 过长');
289
+ throw imValidationError('token_invalid', 'token 过长');
247
290
  const mark = args.mark !== undefined && args.mark !== '' ? String(args.mark) : '';
248
291
  if (mark.length > 100)
249
- throw ymbValidationError('mark_too_long', 'mark 不能超过 100 字节');
292
+ throw imValidationError('mark_too_long', 'mark 不能超过 100 字节');
250
293
  return {
251
294
  summary: { action: '申请加入群聊', groupId: groupId || '(token)', hasMark: Boolean(mark), needApproval: true },
252
295
  snapshot: compact({ groupId: groupId || undefined, token: token || undefined, mark: mark || undefined }),
@@ -266,7 +309,7 @@ const groupJoinPair = makeWritePair({
266
309
  method: 'POST',
267
310
  path: '/api/em/msg/executeIm/group/joinGroup',
268
311
  body,
269
- operation: 'yimiaoban.group.join',
312
+ operation: 'im.group.join',
270
313
  });
271
314
  assertImOk(res, '申请入群');
272
315
  const flag = Number(res?.data?.data?.flag ?? res?.data?.flag ?? -1);
@@ -286,20 +329,20 @@ function parseSwitchs(raw) {
286
329
  const flag = Number(flagRaw);
287
330
  const onOff = Number(onoffRaw);
288
331
  if (!Number.isInteger(flag) || !Number.isInteger(onOff)) {
289
- throw ymbValidationError('switchs_invalid', `switchs 必须形如 flag:onoff(如 1:1,2:0),收到:${pair}`);
332
+ throw imValidationError('switchs_invalid', `switchs 必须形如 flag:onoff(如 1:1,2:0),收到:${pair}`);
290
333
  }
291
334
  return { flag, on_off: onOff };
292
335
  });
293
336
  }
294
337
  // ── 批量改群属性(batModGroupInfo)──
295
338
  const groupModifyPair = makeWritePair({
296
- action: 'yimiaoban.group.modify',
339
+ action: 'im.group.modify',
297
340
  async plan(args, env) {
298
341
  const groupId = str(args.groupId);
299
342
  if (!groupId || groupId === '0')
300
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
343
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
301
344
  if (args.onoff !== undefined) {
302
- throw ymbValidationError('field_forbidden', 'onoff 字段服务端不处理,请用 switchs');
345
+ throw imValidationError('field_forbidden', 'onoff 字段服务端不处理,请用 switchs');
303
346
  }
304
347
  const body = { group_id: groupId };
305
348
  let mask = 0;
@@ -330,31 +373,31 @@ const groupModifyPair = makeWritePair({
330
373
  const resolveOrFail = async (raw, label) => {
331
374
  const { users, failed } = await resolveMembers({ users: raw }, env);
332
375
  if (failed.length && args.removeFailed !== true) {
333
- throw ymbValidationError('members_resolve_failed', `${label}信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
376
+ throw imValidationError('members_resolve_failed', `${label}信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
334
377
  }
335
378
  return users;
336
379
  };
337
380
  if (args.admins) {
338
381
  const users = await resolveOrFail(args.admins, '管理员');
339
382
  if (users.length === 0)
340
- throw ymbValidationError('members_empty', '管理员解析为空');
383
+ throw imValidationError('members_empty', '管理员解析为空');
341
384
  set(1 << 7, 'admins', users);
342
385
  }
343
386
  if (args.delAdmins) {
344
387
  const users = await resolveOrFail(args.delAdmins, '待移除管理员');
345
388
  if (users.length === 0)
346
- throw ymbValidationError('members_empty', '待移除管理员解析为空');
389
+ throw imValidationError('members_empty', '待移除管理员解析为空');
347
390
  // 源 modGroupInfo.js:移除管理员走 del_admins 字段(mask 与 admins 同为 admins 位),不能写进 admins
348
391
  set(1 << 7, 'del_admins', users);
349
392
  }
350
393
  if (args.creator) {
351
394
  const users = await resolveOrFail(args.creator, '新群主');
352
395
  if (!users.length)
353
- throw ymbValidationError('members_empty', '新群主解析失败');
396
+ throw imValidationError('members_empty', '新群主解析失败');
354
397
  set(1 << 6, 'creator', users[0]);
355
398
  }
356
399
  if (mask === 0)
357
- throw ymbValidationError('field_required', '至少修改一项(num/rsonoff/history/switchs/gtId/copyGroupids/admins/delAdmins/creator)');
400
+ throw imValidationError('field_required', '至少修改一项(num/rsonoff/history/switchs/gtId/copyGroupids/admins/delAdmins/creator)');
358
401
  body.mask = mask;
359
402
  return {
360
403
  summary: { action: '批量修改群属性', groupId, mask, changedFields: Object.keys(body).filter((k) => k !== 'group_id' && k !== 'mask') },
@@ -366,7 +409,7 @@ const groupModifyPair = makeWritePair({
366
409
  method: 'POST',
367
410
  path: '/api/em/msg/executeIm/group/batModGroupInfo',
368
411
  body: plan.body,
369
- operation: 'yimiaoban.group.modify',
412
+ operation: 'im.group.modify',
370
413
  });
371
414
  assertImOk(res, '修改群属性');
372
415
  return { groupId: plan.groupId };
@@ -374,31 +417,31 @@ const groupModifyPair = makeWritePair({
374
417
  });
375
418
  // ── 改群名/单字段(modifyGroupInfo)──
376
419
  const groupRenamePair = makeWritePair({
377
- action: 'yimiaoban.group.rename',
420
+ action: 'im.group.rename',
378
421
  async plan(args, env) {
379
422
  const groupId = str(args.groupId);
380
423
  if (!groupId || groupId === '0')
381
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
424
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
382
425
  const forbidden = ['announce', 'type', 'state'];
383
426
  for (const key of forbidden) {
384
427
  if (args[key] !== undefined)
385
- throw ymbValidationError('field_forbidden', `${key} 字段禁用:announce 请用公告操作,type/state 会触发高危行为`);
428
+ throw imValidationError('field_forbidden', `${key} 字段禁用:announce 请用公告操作,type/state 会触发高危行为`);
386
429
  }
387
430
  const fieldKeys = ['name', 'history', 'host', 'display', 'msgSetting', 'mark', 'gtid'].filter((k) => args[k] !== undefined);
388
431
  if (fieldKeys.length === 0)
389
- throw ymbValidationError('field_required', '至少修改一项:name/history/host/display/msgSetting/mark/gtid');
432
+ throw imValidationError('field_required', '至少修改一项:name/history/host/display/msgSetting/mark/gtid');
390
433
  if (fieldKeys.length > 1)
391
- throw ymbValidationError('field_conflict', '本接口一次只能改一项');
434
+ throw imValidationError('field_conflict', '本接口一次只能改一项');
392
435
  const field = fieldKeys[0];
393
436
  const maskOrder = { name: 0, history: 8, host: 4, display: 5, msgSetting: 6, mark: 7, gtid: 9 };
394
437
  let info = {};
395
438
  if (field === 'host') {
396
439
  const { users, failed } = await resolveMembers({ users: args.host }, env);
397
440
  if (failed.length) {
398
- throw ymbValidationError('members_resolve_failed', `群主信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')}`, { details: failed });
441
+ throw imValidationError('members_resolve_failed', `群主信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')}`, { details: failed });
399
442
  }
400
443
  if (!users.length)
401
- throw ymbValidationError('members_empty', '新群主解析失败');
444
+ throw imValidationError('members_empty', '新群主解析失败');
402
445
  info = { host: users[0] };
403
446
  }
404
447
  else if (field === 'history' || field === 'display' || field === 'msgSetting' || field === 'mark') {
@@ -425,7 +468,7 @@ const groupRenamePair = makeWritePair({
425
468
  method: 'POST',
426
469
  path: '/api/em/msg/executeIm/group/modifyGroupInfo',
427
470
  body: { data: plan.data },
428
- operation: 'yimiaoban.group.rename',
471
+ operation: 'im.group.rename',
429
472
  });
430
473
  assertImOk(res, '修改群信息');
431
474
  return { groupId: plan.groupId };
@@ -433,21 +476,21 @@ const groupRenamePair = makeWritePair({
433
476
  });
434
477
  // ── 群公告 增改删 ──
435
478
  const groupAnnounceModifyPair = makeWritePair({
436
- action: 'yimiaoban.group.announce.modify',
479
+ action: 'im.group.announce.modify',
437
480
  async plan(args, env) {
438
481
  const groupId = str(args.groupId);
439
482
  if (!groupId || groupId === '0')
440
- throw ymbValidationError('id_invalid', 'groupId 必须非 0');
483
+ throw imValidationError('id_invalid', 'groupId 必须非 0');
441
484
  const isDel = args.del === true || String(args.del) === '1' || String(args.flag) === '1';
442
485
  const aid = args.aid !== undefined && args.aid !== '' ? String(args.aid) : '0';
443
486
  if (isDel && (!aid || aid === '0'))
444
- throw ymbValidationError('aid_required', '删除公告必须指定 aid');
487
+ throw imValidationError('aid_required', '删除公告必须指定 aid');
445
488
  if (!isDel) {
446
489
  const annouce = args.annouce !== undefined ? String(args.annouce) : '';
447
490
  if (!annouce)
448
- throw ymbValidationError('annouce_required', '新增/修改公告必须提供 annouce');
491
+ throw imValidationError('annouce_required', '新增/修改公告必须提供 annouce');
449
492
  if (annouce.length > 5000)
450
- throw ymbValidationError('annouce_too_long', '公告内容过长');
493
+ throw imValidationError('annouce_too_long', '公告内容过长');
451
494
  }
452
495
  const summary = { action: isDel ? '删除群公告' : (aid && aid !== '0' ? '修改群公告' : '新增群公告'), groupId, aid };
453
496
  const snapshot = { groupId, flag: isDel ? 1 : 0, aid };
@@ -480,7 +523,7 @@ const groupAnnounceModifyPair = makeWritePair({
480
523
  method: 'POST',
481
524
  path: '/api/em/msg/executeIm/group/modifyAnnouce',
482
525
  body,
483
- operation: 'yimiaoban.group.announce.modify',
526
+ operation: 'im.group.announce.modify',
484
527
  });
485
528
  assertImOk(res, '修改群公告');
486
529
  const responseAid = str(res?.data?.data?.aid ?? res?.data?.aid ?? '');
@@ -531,32 +574,36 @@ function buildWriteDefinition(name, method, summary, inputProps, pair) {
531
574
  ];
532
575
  }
533
576
  const groupWriteOps = [
534
- ...buildWriteDefinition('yimiaoban.group.create', 'group.create', '创建普通群', {
577
+ ...buildWriteDefinition('im.group.create', 'group.create', '创建普通群', {
535
578
  users: { type: 'array', items: { type: 'string' }, description: '群成员 uid(逗号串或数组)' },
536
579
  name: { type: 'string', description: '群名(缺省自动生成)' },
537
580
  names: { type: 'array', items: { type: 'string' }, description: '与 users 顺序对应姓名(用于默认群名)' },
538
581
  admins: { type: 'array', items: { type: 'string' }, description: '管理员 uid' },
539
582
  }, groupCreatePair),
540
- ...buildWriteDefinition('yimiaoban.group.invite', 'group.invite', '邀请成员入群', {
583
+ ...buildWriteDefinition('im.group.invite', 'group.invite', '邀请成员入群', {
541
584
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
542
585
  users: { type: 'array', items: { type: 'string' }, description: '被邀请 uid' },
543
586
  }, groupInvitePair),
544
- ...buildWriteDefinition('yimiaoban.group.kick', 'group.kick', '踢出群成员', {
587
+ ...buildWriteDefinition('im.group.kick', 'group.kick', '踢出群成员', {
545
588
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
546
589
  users: { type: 'array', items: { type: 'string' }, description: '被踢 uid' },
547
590
  }, groupKickPair),
548
- ...buildWriteDefinition('yimiaoban.group.exit', 'group.exit', '退出群聊', {
591
+ ...buildWriteDefinition('im.group.exit', 'group.exit', '退出群聊', {
549
592
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
593
+ acknowledgeOrphanRisk: {
594
+ type: 'boolean',
595
+ description: '群主或最后一个成员退群时必须显式传 true(退群后该群会变成无主空群,无法再 join/destroy)',
596
+ },
550
597
  }, groupExitPair),
551
- ...buildWriteDefinition('yimiaoban.group.destroy', 'group.destroy', '解散群聊(仅群主,不可恢复)', {
598
+ ...buildWriteDefinition('im.group.destroy', 'group.destroy', '解散群聊(仅群主,不可恢复)', {
552
599
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
553
600
  }, groupDestroyPair),
554
- ...buildWriteDefinition('yimiaoban.group.join', 'group.join', '申请加入群聊', {
601
+ ...buildWriteDefinition('im.group.join', 'group.join', '申请加入群聊', {
555
602
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id(token 二选一)' },
556
603
  token: { type: 'string', description: '群 token/二维码加入' },
557
604
  mark: { type: 'string', maxLength: 100, description: '申请理由' },
558
605
  }, groupJoinPair),
559
- ...buildWriteDefinition('yimiaoban.group.modify', 'group.modify', '批量修改群属性(batModGroupInfo)', {
606
+ ...buildWriteDefinition('im.group.modify', 'group.modify', '批量修改群属性(batModGroupInfo)', {
560
607
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
561
608
  num: { type: 'integer', description: '最大人数' },
562
609
  switchs: { type: 'string', description: '群开关(flag:onoff 逗号分隔,如 1:1,8:0)' },
@@ -568,7 +615,7 @@ const groupWriteOps = [
568
615
  copyGroupids: { type: 'array', items: { type: 'string' }, description: '拷贝群列表' },
569
616
  gtId: { type: 'string', description: '关联群类型 id' },
570
617
  }, groupModifyPair),
571
- ...buildWriteDefinition('yimiaoban.group.rename', 'group.rename', '修改群信息(一次一项:群名/历史/群主/显示/打扰/标记/gtid)', {
618
+ ...buildWriteDefinition('im.group.rename', 'group.rename', '修改群信息(一次一项:群名/历史/群主/显示/打扰/标记/gtid)', {
572
619
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
573
620
  name: { type: 'string', description: '改群名' },
574
621
  history: { type: 'integer', description: '新人历史消息范围' },
@@ -578,7 +625,7 @@ const groupWriteOps = [
578
625
  mark: { type: 'integer', description: '标记位' },
579
626
  gtid: { type: 'string', description: 'GTID' },
580
627
  }, groupRenamePair),
581
- ...buildWriteDefinition('yimiaoban.group.announce.modify', 'group.announce.modify', '新增/修改/删除群公告', {
628
+ ...buildWriteDefinition('im.group.announce.modify', 'group.announce.modify', '新增/修改/删除群公告', {
582
629
  groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
583
630
  annouce: { type: 'string', description: '公告内容(删除时不需要)' },
584
631
  aid: { type: 'string', description: '公告 id(0/不传=新增;非 0=修改;删除必填)' },