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.
- package/README.md +14 -9
- package/dist/cmd/skills/index.js +60 -0
- package/dist/internal/e10/auth/commands.js +48 -32
- package/dist/internal/e10/auth/session.js +44 -3
- package/dist/internal/e10/auth/xiaoe.js +363 -11
- package/dist/internal/e10/context.js +4 -2
- package/dist/internal/skills/detector.js +42 -0
- package/dist/internal/skills/install.js +50 -3
- package/dist/shortcuts/archive/render/search-format.js +12 -3
- package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
- package/dist/shortcuts/ebuilder-form/errors.js +54 -0
- package/dist/shortcuts/ebuilder-form/host.js +1238 -0
- package/dist/shortcuts/ebuilder-form/index.js +108 -0
- package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
- package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
- package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
- package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
- package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
- package/dist/shortcuts/ebuilder-form/operations.js +8 -0
- package/dist/shortcuts/ehr/operations/salary.js +2 -140
- package/dist/shortcuts/im/continuation.js +70 -0
- package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
- package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
- package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
- package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
- package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
- package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
- package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
- package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
- package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
- package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
- package/dist/shortcuts/im/operations/shared.js +442 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
- package/dist/shortcuts/im/operations/user-remind.js +201 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
- package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
- package/dist/shortcuts/index.js +10 -2
- package/dist/shortcuts/invoice/manifest.js +113 -10
- package/dist/shortcuts/invoice/operations/import.js +39 -14
- package/dist/shortcuts/invoice/operations/reim.js +38 -18
- package/dist/shortcuts/invoice/operations/shared.js +35 -6
- package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
- package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
- package/dist/shortcuts/okr/errors.js +70 -0
- package/dist/shortcuts/okr/host.js +169 -0
- package/dist/shortcuts/okr/index.js +155 -0
- package/dist/shortcuts/okr/manifest.js +70 -0
- package/dist/shortcuts/okr/operations/link.js +305 -0
- package/dist/shortcuts/okr/operations/read.js +481 -0
- package/dist/shortcuts/okr/operations/registry.js +31 -0
- package/dist/shortcuts/okr/operations/shared.js +381 -0
- package/dist/shortcuts/okr/operations/types.js +88 -0
- package/dist/shortcuts/okr/operations/write.js +838 -0
- package/dist/shortcuts/okr/operations.js +8 -0
- package/dist/shortcuts/project/continuation.js +70 -0
- package/dist/shortcuts/project/errors.js +53 -0
- package/dist/shortcuts/project/host.js +160 -0
- package/dist/shortcuts/project/index.js +106 -0
- package/dist/shortcuts/project/manifest.js +36 -0
- package/dist/shortcuts/project/operations/read.js +551 -0
- package/dist/shortcuts/project/operations/registry.js +31 -0
- package/dist/shortcuts/project/operations/shared.js +68 -0
- package/dist/shortcuts/project/operations/types.js +39 -0
- package/dist/shortcuts/project/operations/write.js +266 -0
- package/dist/shortcuts/project/operations.js +8 -0
- package/dist/shortcuts/workflow/continuation.js +508 -0
- package/dist/shortcuts/workflow/endpoints.js +149 -0
- package/dist/shortcuts/workflow/errors.js +112 -0
- package/dist/shortcuts/workflow/host.js +224 -0
- package/dist/shortcuts/workflow/index.js +116 -0
- package/dist/shortcuts/workflow/manifest.js +30 -0
- package/dist/shortcuts/workflow/operations/form.js +4159 -0
- package/dist/shortcuts/workflow/operations/read.js +3778 -0
- package/dist/shortcuts/workflow/operations/registry.js +33 -0
- package/dist/shortcuts/workflow/operations/shared.js +167 -0
- package/dist/shortcuts/workflow/operations/types.js +44 -0
- package/dist/shortcuts/workflow/operations/write.js +1991 -0
- package/dist/shortcuts/workflow/operations.js +119 -0
- package/dist/shortcuts/workflow/state.js +182 -0
- package/docs/SKILL.md +5 -3
- package/docs/_catalog.md +10 -1
- package/docs/agent-invoice.md +2 -2
- package/docs/agent-skill-install.md +2 -2
- package/docs/e10-auth.md +3 -1
- package/docs/ebuilder-form.md +38 -0
- package/docs/im.md +104 -0
- package/docs/invoice.md +5 -5
- package/docs/okr.md +122 -0
- package/docs/operation-manual.md +10 -7
- package/docs/project.md +69 -0
- package/docs/skill-review-2026-09-19.md +68 -0
- package/package.json +3 -2
- package/scripts/package-skill.mjs +38 -11
- package/scripts/sync-connector-skills.mjs +172 -0
- package/skill-template/business-info.json +357 -16
- package/skill-template/domains/ebuilder-form.md +5 -0
- package/skill-template/domains/okr.md +18 -0
- package/skill-template/domains/shijingran.md +3 -0
- package/skill-template/domains/skill-maker.md +4 -4
- package/skill-template/domains/workflow.md +22 -0
- package/skill-template/domains/yepiaotong.md +4 -2
- package/skill-template/domains/yimiaoban.md +13 -10
- package/skill-template/product.json +5 -0
- package/skill-template/skill-template.md +1 -0
- package/skills/weaver-e10-calendar/SKILL.md +5 -1
- package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
- package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -1
- package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
- package/skills/weaver-e10-hrm/SKILL.md +1 -1
- package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
- package/skills/weaver-e10-jucailin/SKILL.md +2 -2
- package/skills/weaver-e10-mail/SKILL.md +22 -6
- package/skills/weaver-e10-meeting/SKILL.md +22 -1
- package/skills/weaver-e10-okr/SKILL.md +139 -0
- package/skills/weaver-e10-okr/references/okr-align.md +59 -0
- package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
- package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
- package/skills/weaver-e10-okr/references/okr-query.md +105 -0
- package/skills/weaver-e10-okr/references/okr-write.md +94 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
- package/skills/weaver-e10-shared/SKILL.md +79 -11
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
- package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
- package/skills/weaver-e10-shijingran/SKILL.md +83 -0
- package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
- package/skills/weaver-e10-shijingran/references/operations.md +154 -0
- package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
- package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
- package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
- package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
- package/skills/weaver-e10-workflow/SKILL.md +250 -0
- package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
- package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
- package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
- package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
- package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
- package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
- package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
- package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
- package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
- package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
- package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
- package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
- package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
- package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
- package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
- package/docs/yimiaoban.md +0 -86
- package/skills/weaver-e10-calendar/product.json +0 -8
- package/skills/weaver-e10-esb/product.json +0 -8
- package/skills/weaver-e10-hrm/product.json +0 -8
- package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
- package/skills/weaver-e10-jucailin/product.json +0 -8
- package/skills/weaver-e10-mail/product.json +0 -8
- package/skills/weaver-e10-meeting/product.json +0 -8
- package/skills/weaver-e10-plan/product.json +0 -8
- package/skills/weaver-e10-qiyecheng/product.json +0 -8
- package/skills/weaver-e10-skill-maker/product.json +0 -8
- package/skills/weaver-e10-wenshuding/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
- package/skills/weaver-e10-yimiaoban/product.json +0 -8
- package/skills/weaver-e10-ziguanjia/product.json +0 -8
- /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { rename, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, extname, resolve } from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import { READ, schemaOf,
|
|
6
|
-
import { assertImOk, compact, splitIds, str, toUint, unwrapInner } from './shared.js';
|
|
4
|
+
import { imValidationError } from '../errors.js';
|
|
5
|
+
import { READ, schemaOf, imSuccess, } from './types.js';
|
|
6
|
+
import { assertImOk, compact, normalizeAliases, splitIds, str, toUint, unwrapInner } from './shared.js';
|
|
7
7
|
import { fileObjToFile, fileObjToImg } from './write-util.js';
|
|
8
|
+
/** file.upload 旧字段兼容映射(已发布版本接受过这些旧名,按 CLI 规范登记为 deprecated alias)。 */
|
|
9
|
+
const FILE_UPLOAD_ALIASES = {
|
|
10
|
+
file: ['filePath'],
|
|
11
|
+
shareGroup: ['shareGroups'],
|
|
12
|
+
};
|
|
8
13
|
/** 文件上传(两段式,module=im),返回 fileObj + 消息对象两种形态。 */
|
|
9
14
|
const fileUpload = {
|
|
10
|
-
name: '
|
|
15
|
+
name: 'im.file.upload',
|
|
11
16
|
resource: 'file',
|
|
12
17
|
method: 'file.upload',
|
|
13
|
-
action: '
|
|
18
|
+
action: 'im.file.upload',
|
|
14
19
|
summary: '上传本地文件到 E10 文件服务(module=im),返回文件 id(供消息 img/file 引用)',
|
|
15
20
|
capability: 'file',
|
|
16
21
|
risk: 'external-artifact',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
deprecatedInputAliases: FILE_UPLOAD_ALIASES,
|
|
23
|
+
inputSchema: {
|
|
24
|
+
...schemaOf({
|
|
25
|
+
file: { type: 'string', description: '本地文件路径(必填)' },
|
|
26
|
+
filePath: { type: 'string', description: '旧名字段,等价 file(已废弃,与新名同时出现且不一致会报 alias_conflict)' },
|
|
27
|
+
name: { type: 'string', description: '文件名(默认取 basename)' },
|
|
28
|
+
shareGroup: { type: 'string', description: '分享群 id(发群消息用)' },
|
|
29
|
+
shareGroups: { type: 'string', description: '旧名字段,等价 shareGroup(已废弃)' },
|
|
30
|
+
shareUsers: { type: 'array', items: { type: 'string' }, description: '分享用户 uid(发单聊用)' },
|
|
31
|
+
permission: { type: 'string', description: '文件权限(默认 3)' },
|
|
32
|
+
}, { description: '文件上传:file(或旧名 filePath)必填' }),
|
|
33
|
+
anyOf: [{ required: ['file'] }, { required: ['filePath'] }],
|
|
34
|
+
},
|
|
24
35
|
handler: async (args, { host }) => {
|
|
25
|
-
const
|
|
36
|
+
const input = normalizeAliases(args, FILE_UPLOAD_ALIASES);
|
|
37
|
+
const filePath = String(input.file ?? '');
|
|
26
38
|
if (!filePath)
|
|
27
|
-
throw
|
|
39
|
+
throw imValidationError('file_required', 'file 必填');
|
|
28
40
|
const abs = resolve(filePath);
|
|
29
41
|
if (!existsSync(abs))
|
|
30
|
-
throw
|
|
31
|
-
const shareUsers = Array.isArray(
|
|
32
|
-
const shareGroups =
|
|
42
|
+
throw imValidationError('file_not_found', `文件不存在: ${abs}`);
|
|
43
|
+
const shareUsers = Array.isArray(input.shareUsers) ? input.shareUsers.join(',') : String(input.shareUsers ?? '');
|
|
44
|
+
const shareGroups = input.shareGroup;
|
|
33
45
|
const fileObj = await host.uploadFile(abs, {
|
|
34
|
-
name:
|
|
46
|
+
name: input.name !== undefined ? String(input.name) : undefined,
|
|
35
47
|
shareGroups: shareGroups !== undefined && shareGroups !== '' ? String(shareGroups) : undefined,
|
|
36
48
|
shareUsers: shareUsers || undefined,
|
|
37
|
-
permission:
|
|
49
|
+
permission: input.permission !== undefined ? String(input.permission) : undefined,
|
|
38
50
|
});
|
|
39
|
-
return
|
|
51
|
+
return imSuccess('im.file.upload', {
|
|
40
52
|
fileObj,
|
|
41
53
|
img: fileObjToImg(fileObj),
|
|
42
54
|
file: fileObjToFile(fileObj),
|
|
@@ -45,10 +57,10 @@ const fileUpload = {
|
|
|
45
57
|
};
|
|
46
58
|
/** 媒体预览(img/video)URL 构造 + 可选下载。 */
|
|
47
59
|
const filePreview = {
|
|
48
|
-
name: '
|
|
60
|
+
name: 'im.file.preview',
|
|
49
61
|
resource: 'file',
|
|
50
62
|
method: 'file.preview',
|
|
51
|
-
action: '
|
|
63
|
+
action: 'im.file.preview',
|
|
52
64
|
summary: '生成图片/视频预览与下载 URL(需登录态访问;图片支持 small/large/original)',
|
|
53
65
|
capability: 'file',
|
|
54
66
|
risk: READ,
|
|
@@ -68,7 +80,7 @@ const filePreview = {
|
|
|
68
80
|
const groupId = args.groupId !== undefined && args.groupId !== '' ? String(args.groupId) : undefined;
|
|
69
81
|
const user = args.toUid || args.toCid ? { cid: str(args.toCid), uid: str(args.toUid) } : undefined;
|
|
70
82
|
if (!groupId && !user) {
|
|
71
|
-
throw
|
|
83
|
+
throw imValidationError('context_required', 'groupId(群聊)或 toUid+toCid(单聊)必须提供其一');
|
|
72
84
|
}
|
|
73
85
|
const opts = {
|
|
74
86
|
fileId,
|
|
@@ -78,12 +90,12 @@ const filePreview = {
|
|
|
78
90
|
...(groupId ? { groupId } : {}),
|
|
79
91
|
...(user ? { user } : {}),
|
|
80
92
|
};
|
|
81
|
-
return
|
|
93
|
+
return imSuccess('im.file.preview', {
|
|
82
94
|
kind,
|
|
83
95
|
fileId,
|
|
84
96
|
previewUrl: await host.buildPreviewUrl(opts),
|
|
85
97
|
downloadUrl: await host.buildDownloadUrl(opts),
|
|
86
|
-
note: '预览/下载 URL 需要登录态(Cookie+eteamsid),直接嵌入 Markdown 会 401;需要真正可见请用
|
|
98
|
+
note: '预览/下载 URL 需要登录态(Cookie+eteamsid),直接嵌入 Markdown 会 401;需要真正可见请用 im.file.download 落地到本地。',
|
|
87
99
|
});
|
|
88
100
|
},
|
|
89
101
|
};
|
|
@@ -109,10 +121,10 @@ function detectImageExt(buffer) {
|
|
|
109
121
|
}
|
|
110
122
|
/** 消息媒体/文件落地到本地(需登录态;父目录必须存在,禁止覆盖已存在文件)。 */
|
|
111
123
|
const fileDownload = {
|
|
112
|
-
name: '
|
|
124
|
+
name: 'im.file.download',
|
|
113
125
|
resource: 'file',
|
|
114
126
|
method: 'file.download',
|
|
115
|
-
action: '
|
|
127
|
+
action: 'im.file.download',
|
|
116
128
|
summary: '下载消息里的图片/视频/文件到本地(需登录态;父目录必须存在,禁止覆盖)',
|
|
117
129
|
capability: 'file',
|
|
118
130
|
risk: 'local-file-write',
|
|
@@ -133,20 +145,20 @@ const fileDownload = {
|
|
|
133
145
|
const kind = kindRaw === 'video' ? 'video' : 'img';
|
|
134
146
|
const output = String(args.output ?? '').trim();
|
|
135
147
|
if (!output)
|
|
136
|
-
throw
|
|
148
|
+
throw imValidationError('output_required', 'output 必须是明确的本地文件路径');
|
|
137
149
|
const groupId = args.groupId !== undefined && args.groupId !== '' ? String(args.groupId) : undefined;
|
|
138
150
|
const user = args.toUid || args.toCid ? { cid: str(args.toCid), uid: str(args.toUid) } : undefined;
|
|
139
151
|
if (!groupId && !user) {
|
|
140
|
-
throw
|
|
152
|
+
throw imValidationError('context_required', 'groupId(群聊)或 toUid+toCid(单聊)必须提供其一');
|
|
141
153
|
}
|
|
142
154
|
const resolved = resolve(output);
|
|
143
155
|
const parent = await stat(dirname(resolved)).catch(() => null);
|
|
144
156
|
if (!parent)
|
|
145
|
-
throw
|
|
157
|
+
throw imValidationError('output_parent_missing', `输出目录不存在:${dirname(resolved)}`);
|
|
146
158
|
if (!parent.isDirectory())
|
|
147
|
-
throw
|
|
159
|
+
throw imValidationError('output_parent_invalid', '输出文件的父路径不是目录');
|
|
148
160
|
if (existsSync(resolved))
|
|
149
|
-
throw
|
|
161
|
+
throw imValidationError('output_exists', `输出文件已存在,拒绝覆盖:${resolved}`);
|
|
150
162
|
const url = await host.buildDownloadUrl({
|
|
151
163
|
fileId,
|
|
152
164
|
msgid,
|
|
@@ -174,7 +186,7 @@ const fileDownload = {
|
|
|
174
186
|
}
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
|
-
return
|
|
189
|
+
return imSuccess('im.file.download', {
|
|
178
190
|
fileId,
|
|
179
191
|
msgid,
|
|
180
192
|
kind: kindRaw,
|
|
@@ -188,10 +200,10 @@ const fileDownload = {
|
|
|
188
200
|
};
|
|
189
201
|
/** 人员在线状态。 */
|
|
190
202
|
const userState = {
|
|
191
|
-
name: '
|
|
203
|
+
name: 'im.user.state',
|
|
192
204
|
resource: 'user',
|
|
193
205
|
method: 'user.state',
|
|
194
|
-
action: '
|
|
206
|
+
action: 'im.user.state',
|
|
195
207
|
summary: '查询指定人员各在线设备状态(sub_state 强制 0 不订阅)',
|
|
196
208
|
capability: 'user',
|
|
197
209
|
risk: READ,
|
|
@@ -199,6 +211,7 @@ const userState = {
|
|
|
199
211
|
users: { type: 'array', items: { type: 'string' }, description: '人员 uid 列表' },
|
|
200
212
|
devType: { type: 'integer', description: '设备类型(缺省查全部在线设备)' },
|
|
201
213
|
mask: { type: 'integer', description: '信息掩码(默认 7)' },
|
|
214
|
+
removeFailed: { type: 'boolean', description: '解析失败的人员跳过(默认 false:解析失败会报 members_resolve_failed)' },
|
|
202
215
|
}, { required: ['users'], description: '查询人员在线状态' }),
|
|
203
216
|
handler: async (args, { host }) => {
|
|
204
217
|
const rawUids = Array.isArray(args.users) ? args.users.map((x) => String(x)) : splitIds(args.users, 'users');
|
|
@@ -214,7 +227,7 @@ const userState = {
|
|
|
214
227
|
members.push({ cid: p.cid, uid });
|
|
215
228
|
}
|
|
216
229
|
if (failed.length && args.removeFailed !== true) {
|
|
217
|
-
throw
|
|
230
|
+
throw imValidationError('members_resolve_failed', `以下人员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认跳过请传 removeFailed:true`);
|
|
218
231
|
}
|
|
219
232
|
const body = compact({
|
|
220
233
|
users: members,
|
|
@@ -226,7 +239,7 @@ const userState = {
|
|
|
226
239
|
method: 'POST',
|
|
227
240
|
path: '/api/em/msg/executeIm/state/syncUserState',
|
|
228
241
|
body,
|
|
229
|
-
operation: '
|
|
242
|
+
operation: 'im.user.state',
|
|
230
243
|
});
|
|
231
244
|
assertImOk(res, '查询人员状态');
|
|
232
245
|
const inner = unwrapInner(res);
|
|
@@ -246,7 +259,7 @@ const userState = {
|
|
|
246
259
|
}));
|
|
247
260
|
return { uid, name: map[uid]?.name || uid, devices };
|
|
248
261
|
});
|
|
249
|
-
return
|
|
262
|
+
return imSuccess('im.user.state', { total: infos.length, infos });
|
|
250
263
|
},
|
|
251
264
|
};
|
|
252
265
|
function formatTime(ts) {
|
|
@@ -260,10 +273,10 @@ function formatTime(ts) {
|
|
|
260
273
|
}
|
|
261
274
|
/** i18n 标签翻译(内部支撑,也可单独查询)。 */
|
|
262
275
|
const i18nLabels = {
|
|
263
|
-
name: '
|
|
276
|
+
name: 'im.i18n.labels',
|
|
264
277
|
resource: 'i18n',
|
|
265
278
|
method: 'i18n.labels',
|
|
266
|
-
action: '
|
|
279
|
+
action: 'im.i18n.labels',
|
|
267
280
|
summary: '批量翻译国际化标签 id → 当前语言文案',
|
|
268
281
|
capability: 'i18n',
|
|
269
282
|
risk: READ,
|
|
@@ -273,7 +286,7 @@ const i18nLabels = {
|
|
|
273
286
|
handler: async (args, { host }) => {
|
|
274
287
|
const ids = Array.isArray(args.ids) ? args.ids.map((x) => String(x)) : splitIds(args.ids, 'ids');
|
|
275
288
|
const labels = await host.fetchI18nLabels(ids);
|
|
276
|
-
return
|
|
289
|
+
return imSuccess('im.i18n.labels', {
|
|
277
290
|
total: Object.keys(labels).length,
|
|
278
291
|
labels: ids.map((id) => ({ id, text: labels[id] ?? '(未取到文案)' })),
|
|
279
292
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { READ, schemaOf,
|
|
1
|
+
import { imValidationError } from '../errors.js';
|
|
2
|
+
import { READ, schemaOf, imSuccess, } from './types.js';
|
|
3
3
|
import { assertImOk, compact, extractGroupMembers, formatFullTime, splitIds, str, toPositiveInt, toUint, unwrapInner, } from './shared.js';
|
|
4
4
|
const SEARCH_TYPE_MASK = {
|
|
5
5
|
type: 1,
|
|
@@ -14,7 +14,7 @@ function requireCidUid(cid, uid) {
|
|
|
14
14
|
const c = String(cid ?? '');
|
|
15
15
|
const u = String(uid ?? '');
|
|
16
16
|
if (!c || !u || c === '0' || u === '0') {
|
|
17
|
-
throw
|
|
17
|
+
throw imValidationError('self_identity_missing', '该查询需要当前登录人的 cid/uid(非 0),请确认登录态完整');
|
|
18
18
|
}
|
|
19
19
|
return { cid: c, uid: u };
|
|
20
20
|
}
|
|
@@ -22,7 +22,7 @@ function requireCidUid(cid, uid) {
|
|
|
22
22
|
function requireGroupId(value) {
|
|
23
23
|
const text = String(value ?? '').trim();
|
|
24
24
|
if (!/^\d+$/.test(text) || text === '0') {
|
|
25
|
-
throw
|
|
25
|
+
throw imValidationError('id_invalid', `groupId 必须是非 0 数字 ID(收到:${text || '(空)'})`);
|
|
26
26
|
}
|
|
27
27
|
return text;
|
|
28
28
|
}
|
|
@@ -49,10 +49,10 @@ function stateName(state) {
|
|
|
49
49
|
}
|
|
50
50
|
/** 群搜索。 */
|
|
51
51
|
const groupSearch = {
|
|
52
|
-
name: '
|
|
52
|
+
name: 'im.group.search',
|
|
53
53
|
resource: 'group',
|
|
54
54
|
method: 'group.search',
|
|
55
|
-
action: '
|
|
55
|
+
action: 'im.group.search',
|
|
56
56
|
summary: '按条件搜索群(cond+mask 全服务端过滤,禁止本地过滤)',
|
|
57
57
|
capability: 'group',
|
|
58
58
|
risk: READ,
|
|
@@ -93,13 +93,15 @@ const groupSearch = {
|
|
|
93
93
|
}
|
|
94
94
|
if (args.owner === true) {
|
|
95
95
|
const self = await host.getSelfIdentity();
|
|
96
|
+
const { cid, uid } = requireCidUid(self.cid, self.uid);
|
|
96
97
|
mask |= SEARCH_TYPE_MASK.owners;
|
|
97
|
-
cond.owners = [{ cid
|
|
98
|
+
cond.owners = [{ cid, uid }];
|
|
98
99
|
}
|
|
99
100
|
if (args.creator === true) {
|
|
100
101
|
const self = await host.getSelfIdentity();
|
|
102
|
+
const { cid, uid } = requireCidUid(self.cid, self.uid);
|
|
101
103
|
mask |= SEARCH_TYPE_MASK.creators;
|
|
102
|
-
cond.creators = [{ cid
|
|
104
|
+
cond.creators = [{ cid, uid }];
|
|
103
105
|
}
|
|
104
106
|
let create;
|
|
105
107
|
if (args.today === true)
|
|
@@ -114,7 +116,7 @@ const groupSearch = {
|
|
|
114
116
|
cond.create = create;
|
|
115
117
|
}
|
|
116
118
|
if (mask === 0)
|
|
117
|
-
throw
|
|
119
|
+
throw imValidationError('cond_required', '至少提供一个搜索条件(name/type/members/owner/creator/today/yesterday/create*)');
|
|
118
120
|
cond.mask = mask;
|
|
119
121
|
const pageSize = Math.min(toPositiveInt(args.pageSize, 'pageSize') ?? 100, 200);
|
|
120
122
|
const body = { page: { size: pageSize, num: 1 }, cond };
|
|
@@ -122,12 +124,12 @@ const groupSearch = {
|
|
|
122
124
|
method: 'POST',
|
|
123
125
|
path: '/api/em/msg/executeIm/group/searchGroup',
|
|
124
126
|
body,
|
|
125
|
-
operation: '
|
|
127
|
+
operation: 'im.group.search',
|
|
126
128
|
});
|
|
127
129
|
assertImOk(res, '搜索群');
|
|
128
130
|
const inner = unwrapInner(res);
|
|
129
131
|
const datas = inner?.datas || [];
|
|
130
|
-
return
|
|
132
|
+
return imSuccess('im.group.search', {
|
|
131
133
|
total: inner?.total ?? datas.length,
|
|
132
134
|
groups: datas.map((g) => ({
|
|
133
135
|
id: str(g.id),
|
|
@@ -141,10 +143,10 @@ const groupSearch = {
|
|
|
141
143
|
};
|
|
142
144
|
/** 群基础信息(支持多群)。 */
|
|
143
145
|
const groupInfo = {
|
|
144
|
-
name: '
|
|
146
|
+
name: 'im.group.info',
|
|
145
147
|
resource: 'group',
|
|
146
148
|
method: 'group.info',
|
|
147
|
-
action: '
|
|
149
|
+
action: 'im.group.info',
|
|
148
150
|
summary: '按群 id 查基础信息(支持批量;类型位组合 + 状态展示)',
|
|
149
151
|
capability: 'group',
|
|
150
152
|
risk: READ,
|
|
@@ -162,17 +164,17 @@ const groupInfo = {
|
|
|
162
164
|
ids.push(String(args.groupId));
|
|
163
165
|
ids = [...new Set(ids)];
|
|
164
166
|
if (ids.length === 0)
|
|
165
|
-
throw
|
|
167
|
+
throw imValidationError('group_ids_required', 'groupIds / groupId 至少其一');
|
|
166
168
|
for (const id of ids) {
|
|
167
169
|
if (!/^\d+$/.test(id) || id === '0')
|
|
168
|
-
throw
|
|
170
|
+
throw imValidationError('id_invalid', `群 id 必须非 0 数字:${id}`);
|
|
169
171
|
}
|
|
170
172
|
const body = { trans_id: 0, type: 0, mask: 0, group_id: ids, flag: 0 };
|
|
171
173
|
const res = await host.request({
|
|
172
174
|
method: 'POST',
|
|
173
175
|
path: '/api/em/msg/executeIm/group/queryGroupInfo',
|
|
174
176
|
body,
|
|
175
|
-
operation: '
|
|
177
|
+
operation: 'im.group.info',
|
|
176
178
|
});
|
|
177
179
|
assertImOk(res, '查询群信息');
|
|
178
180
|
const inner = unwrapInner(res);
|
|
@@ -189,15 +191,15 @@ const groupInfo = {
|
|
|
189
191
|
stateName: stateName(Number(g.state ?? -1)),
|
|
190
192
|
createTime: formatFullTime(g.create_time ?? g.createTime),
|
|
191
193
|
}));
|
|
192
|
-
return
|
|
194
|
+
return imSuccess('im.group.info', { total: groups.length, groups });
|
|
193
195
|
},
|
|
194
196
|
};
|
|
195
197
|
/** 群成员列表。 */
|
|
196
198
|
const groupUsers = {
|
|
197
|
-
name: '
|
|
199
|
+
name: 'im.group.users',
|
|
198
200
|
resource: 'group',
|
|
199
201
|
method: 'group.users',
|
|
200
|
-
action: '
|
|
202
|
+
action: 'im.group.users',
|
|
201
203
|
summary: '拉取群成员列表(姓名一律走 hrm,默认 mask 不含 name 位)',
|
|
202
204
|
capability: 'group',
|
|
203
205
|
risk: READ,
|
|
@@ -221,7 +223,7 @@ const groupUsers = {
|
|
|
221
223
|
method: 'POST',
|
|
222
224
|
path: '/api/em/msg/executeIm/group/getGroupUsers',
|
|
223
225
|
body,
|
|
224
|
-
operation: '
|
|
226
|
+
operation: 'im.group.users',
|
|
225
227
|
});
|
|
226
228
|
assertImOk(res, '拉取群成员');
|
|
227
229
|
const inner = unwrapInner(res);
|
|
@@ -242,7 +244,7 @@ const groupUsers = {
|
|
|
242
244
|
roleName: hasRole ? roleNames[Number(m.role_type ?? m.role ?? -1)] ?? '-' : undefined,
|
|
243
245
|
addTime: hasAddTime ? formatFullTime(m.add_time ?? m.addTime) : undefined,
|
|
244
246
|
}));
|
|
245
|
-
return
|
|
247
|
+
return imSuccess('im.group.users', {
|
|
246
248
|
groupId,
|
|
247
249
|
totalNum: inner?.total_num ?? rows.length,
|
|
248
250
|
endFlag: inner?.end_flag,
|
|
@@ -253,10 +255,10 @@ const groupUsers = {
|
|
|
253
255
|
};
|
|
254
256
|
/** 批量查群管理员/群主。 */
|
|
255
257
|
const groupAdmins = {
|
|
256
|
-
name: '
|
|
258
|
+
name: 'im.group.admins',
|
|
257
259
|
resource: 'group',
|
|
258
260
|
method: 'group.admins',
|
|
259
|
-
action: '
|
|
261
|
+
action: 'im.group.admins',
|
|
260
262
|
summary: '批量获取群主/管理员(mask 位 1=群主/负责人 2=管理员,默认 3)',
|
|
261
263
|
capability: 'group',
|
|
262
264
|
risk: READ,
|
|
@@ -271,7 +273,7 @@ const groupAdmins = {
|
|
|
271
273
|
method: 'POST',
|
|
272
274
|
path: '/api/em/msg/executeIm/group/getGroupAdminBatch',
|
|
273
275
|
body,
|
|
274
|
-
operation: '
|
|
276
|
+
operation: 'im.group.admins',
|
|
275
277
|
});
|
|
276
278
|
assertImOk(res, '查询群管理员');
|
|
277
279
|
const inner = unwrapInner(res);
|
|
@@ -295,15 +297,15 @@ const groupAdmins = {
|
|
|
295
297
|
roleName: roleNames[Number(it.role ?? -1)] ?? '-',
|
|
296
298
|
})),
|
|
297
299
|
}));
|
|
298
|
-
return
|
|
300
|
+
return imSuccess('im.group.admins', { total: groups.length, groups });
|
|
299
301
|
},
|
|
300
302
|
};
|
|
301
303
|
/** 批量判断群是否存在。 */
|
|
302
304
|
const groupExist = {
|
|
303
|
-
name: '
|
|
305
|
+
name: 'im.group.exist',
|
|
304
306
|
resource: 'group',
|
|
305
307
|
method: 'group.exist',
|
|
306
|
-
action: '
|
|
308
|
+
action: 'im.group.exist',
|
|
307
309
|
summary: '批量判断群是否存在(ret=0 存在;1209 不存在)',
|
|
308
310
|
capability: 'group',
|
|
309
311
|
risk: READ,
|
|
@@ -314,19 +316,19 @@ const groupExist = {
|
|
|
314
316
|
const ids = Array.isArray(args.groupIds) ? args.groupIds.map((x) => String(x)) : splitIds(args.groupIds, 'groupIds');
|
|
315
317
|
for (const id of ids) {
|
|
316
318
|
if (!/^\d+$/.test(id) || id === '0')
|
|
317
|
-
throw
|
|
319
|
+
throw imValidationError('id_invalid', `群 id 必须非 0 数字:${id || '(空)'}`);
|
|
318
320
|
}
|
|
319
321
|
const body = { groupIds: ids };
|
|
320
322
|
const res = await host.request({
|
|
321
323
|
method: 'POST',
|
|
322
324
|
path: '/api/em/msg/executeIm/group/checkGroupExist',
|
|
323
325
|
body,
|
|
324
|
-
operation: '
|
|
326
|
+
operation: 'im.group.exist',
|
|
325
327
|
});
|
|
326
328
|
assertImOk(res, '检查群是否存在');
|
|
327
329
|
const inner = unwrapInner(res);
|
|
328
330
|
const results = inner?.results || [];
|
|
329
|
-
return
|
|
331
|
+
return imSuccess('im.group.exist', {
|
|
330
332
|
groups: results.map((r) => ({
|
|
331
333
|
groupId: str(r.group_id),
|
|
332
334
|
exists: Number(r.ret) === 0,
|
|
@@ -337,10 +339,10 @@ const groupExist = {
|
|
|
337
339
|
};
|
|
338
340
|
/** 检查用户是否在某群内。 */
|
|
339
341
|
const groupUserExist = {
|
|
340
|
-
name: '
|
|
342
|
+
name: 'im.group.userExist',
|
|
341
343
|
resource: 'group',
|
|
342
344
|
method: 'group.userExist',
|
|
343
|
-
action: '
|
|
345
|
+
action: 'im.group.userExist',
|
|
344
346
|
summary: '检查用户是否在某群内(tenantKey 自动注入)',
|
|
345
347
|
capability: 'group',
|
|
346
348
|
risk: READ,
|
|
@@ -353,13 +355,13 @@ const groupUserExist = {
|
|
|
353
355
|
const users = Array.isArray(args.users) ? args.users.map((x) => String(x)) : splitIds(args.users, 'users');
|
|
354
356
|
const session = await host.getSession();
|
|
355
357
|
if (!session.tenantKey)
|
|
356
|
-
throw
|
|
358
|
+
throw imValidationError('tenant_key_missing', '登录态缺少 tenantKey,请重新登录');
|
|
357
359
|
const body = { tenantKey: session.tenantKey, groupId, users };
|
|
358
360
|
const res = await host.request({
|
|
359
361
|
method: 'POST',
|
|
360
362
|
path: '/api/em/msg/executeIm/group/checkGroupUserExist',
|
|
361
363
|
body,
|
|
362
|
-
operation: '
|
|
364
|
+
operation: 'im.group.userExist',
|
|
363
365
|
});
|
|
364
366
|
assertImOk(res, '检查群成员');
|
|
365
367
|
const inner = unwrapInner(res);
|
|
@@ -371,15 +373,15 @@ const groupUserExist = {
|
|
|
371
373
|
name: personMap[uid]?.name || uid,
|
|
372
374
|
inGroup: inSet.has(uid),
|
|
373
375
|
}));
|
|
374
|
-
return
|
|
376
|
+
return imSuccess('im.group.userExist', { groupId, total: rows.length, users: rows });
|
|
375
377
|
},
|
|
376
378
|
};
|
|
377
379
|
/** 群公告:获取单条。 */
|
|
378
380
|
const groupAnnounceGet = {
|
|
379
|
-
name: '
|
|
381
|
+
name: 'im.group.announce.get',
|
|
380
382
|
resource: 'group',
|
|
381
383
|
method: 'group.announce.get',
|
|
382
|
-
action: '
|
|
384
|
+
action: 'im.group.announce.get',
|
|
383
385
|
summary: '获取群公告(aid=0/不传=最新公告)',
|
|
384
386
|
capability: 'group',
|
|
385
387
|
risk: READ,
|
|
@@ -393,17 +395,17 @@ const groupAnnounceGet = {
|
|
|
393
395
|
method: 'POST',
|
|
394
396
|
path: '/api/em/msg/executeIm/group/getAnnouce',
|
|
395
397
|
body,
|
|
396
|
-
operation: '
|
|
398
|
+
operation: 'im.group.announce.get',
|
|
397
399
|
});
|
|
398
400
|
assertImOk(res, '获取群公告');
|
|
399
401
|
const inner = unwrapInner(res);
|
|
400
402
|
// 公告主体位于 data.data.data(兼容 data.data 直挂 annouce 的形态)
|
|
401
403
|
const data = inner && typeof inner === 'object' && inner.data && typeof inner.data === 'object' ? inner.data : inner;
|
|
402
404
|
if (!data || (data.aid === undefined && data.annouce === undefined)) {
|
|
403
|
-
return
|
|
405
|
+
return imSuccess('im.group.announce.get', { found: false });
|
|
404
406
|
}
|
|
405
407
|
const personMap = await host.resolveUsersByIds(data?.add_user?.uid ? [String(data.add_user.uid)] : []);
|
|
406
|
-
return
|
|
408
|
+
return imSuccess('im.group.announce.get', {
|
|
407
409
|
found: true,
|
|
408
410
|
aid: str(data.aid),
|
|
409
411
|
annouce: data.annouce ?? '',
|
|
@@ -418,10 +420,10 @@ const groupAnnounceGet = {
|
|
|
418
420
|
};
|
|
419
421
|
/** 群公告列表。 */
|
|
420
422
|
const groupAnnounceList = {
|
|
421
|
-
name: '
|
|
423
|
+
name: 'im.group.announce.list',
|
|
422
424
|
resource: 'group',
|
|
423
425
|
method: 'group.announce.list',
|
|
424
|
-
action: '
|
|
426
|
+
action: 'im.group.announce.list',
|
|
425
427
|
summary: '群公告列表(按 id 降序,end_flag=1 还有下一页)',
|
|
426
428
|
capability: 'group',
|
|
427
429
|
risk: READ,
|
|
@@ -440,7 +442,7 @@ const groupAnnounceList = {
|
|
|
440
442
|
method: 'POST',
|
|
441
443
|
path: '/api/em/msg/executeIm/group/getAnnouceList',
|
|
442
444
|
body,
|
|
443
|
-
operation: '
|
|
445
|
+
operation: 'im.group.announce.list',
|
|
444
446
|
});
|
|
445
447
|
assertImOk(res, '获取公告列表');
|
|
446
448
|
const inner = unwrapInner(res);
|
|
@@ -457,7 +459,7 @@ const groupAnnounceList = {
|
|
|
457
459
|
mustRead: Number(d.fmark ?? 0) === 1,
|
|
458
460
|
related: str(d.related ?? ''),
|
|
459
461
|
}));
|
|
460
|
-
return
|
|
462
|
+
return imSuccess('im.group.announce.list', {
|
|
461
463
|
total: rows.length,
|
|
462
464
|
endFlag: inner?.end_flag,
|
|
463
465
|
hasMore: Number(inner?.end_flag) === 1,
|