weaver-work-cli 0.1.32 → 0.1.36
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 +2 -2
- package/dist/chunks/{archive-UMGX72K6.js → archive-XBVH7JU7.js} +2 -2
- package/dist/chunks/{archive-oa-N2IYBLF3.js → archive-oa-MZM3EKFQ.js} +2 -2
- package/dist/chunks/{asset-GLEGKFG5.js → asset-4XO7UOUW.js} +2 -2
- package/dist/chunks/{attachment-content-AQNX5N3S.js → attachment-content-VXSWANAC.js} +3 -2
- package/dist/chunks/{blog-E44OTVDA.js → blog-PGZKOPSC.js} +2 -2
- package/dist/chunks/{caizhilian-DQUFZXZL.js → caizhilian-BH4ZVGUN.js} +2 -2
- package/dist/chunks/{calendar-JKPIRKFD.js → calendar-MXEMTIO4.js} +2 -2
- package/dist/chunks/{chunk-QWY6PGL6.js → chunk-2LV74IWP.js} +361 -56
- package/dist/chunks/chunk-3SO6QL6U.js +73 -0
- package/dist/chunks/chunk-5DQEEGCC.js +229 -0
- package/dist/chunks/{chunk-G3QJULO2.js → chunk-6LLJFKJE.js} +1 -1
- package/dist/chunks/{chunk-C6R3B2ML.js → chunk-BPE37FPB.js} +35 -7
- package/dist/chunks/chunk-BYATLLEF.js +68 -0
- package/dist/chunks/chunk-BYBSCN4X.js +932 -0
- package/dist/chunks/{chunk-CVZ2JOSW.js → chunk-F746NKNF.js} +1 -1
- package/dist/chunks/{chunk-GCG2NMWR.js → chunk-HXJQI5NM.js} +4 -2
- package/dist/chunks/{chunk-YRVDW4WN.js → chunk-I6QGIBDM.js} +2 -2
- package/dist/chunks/{chunk-H7J4DC5R.js → chunk-JCSN3JGM.js} +2 -2
- package/dist/chunks/{chunk-2GA5ZIQL.js → chunk-JNG42JKG.js} +30 -1
- package/dist/chunks/{chunk-DW6AUUKS.js → chunk-JUXVYKII.js} +9 -4
- package/dist/chunks/{chunk-6RKD4DRZ.js → chunk-JYKABYCV.js} +2 -2
- package/dist/chunks/{chunk-6EFWDL7Y.js → chunk-NCOVUAZK.js} +2 -2
- package/dist/chunks/{chunk-E4Z23GHM.js → chunk-QHFANTX7.js} +1114 -904
- package/dist/chunks/{ebuilder-form-V5FG2EHC.js → chunk-QYGO5EE2.js} +22 -1148
- package/dist/chunks/{chunk-IURUFGZK.js → chunk-T72N74ZT.js} +50 -33
- package/dist/chunks/{chunk-H6XSTSLM.js → chunk-TC6MZAO3.js} +137 -204
- package/dist/chunks/{chunk-WKI2FBIQ.js → chunk-VPUTVEMC.js} +737 -341
- package/dist/chunks/{chunk-WSKBSQ2U.js → chunk-VPXOPQJI.js} +2 -2
- package/dist/chunks/chunk-WXFPWCXG.js +774 -0
- package/dist/chunks/{chunk-4YC43QQ4.js → chunk-XMGSYD6S.js} +4 -4
- package/dist/chunks/conflict-JAB2LJEZ.js +21 -0
- package/dist/chunks/continuation-RN6PZN2M.js +11 -0
- package/dist/chunks/contract-XI2EEIZU.js +9110 -0
- package/dist/chunks/cowork-SCPGLBA4.js +2527 -0
- package/dist/chunks/ebuilder-form-EYLHNOTA.js +121 -0
- package/dist/chunks/{ehr-HMFY6VIL.js → ehr-EAP77IMT.js} +1344 -27
- package/dist/chunks/{esb-ZYSVB7SG.js → esb-2TGGXEE7.js} +4 -4
- package/dist/chunks/{fna-B7RKPAZD.js → fna-2X7ZE7BU.js} +13 -8
- package/dist/chunks/{host-OCH2BCCV.js → host-C6EL2RKL.js} +3 -3
- package/dist/chunks/{host-45WED2KK.js → host-I4NRK4MX.js} +3 -3
- package/dist/chunks/{host-I4Z6BFYX.js → host-Q4LZGCRU.js} +3 -3
- package/dist/chunks/host-UEI5FGDM.js +23 -0
- package/dist/chunks/{host-KNZ52LJZ.js → host-XCLDDXKQ.js} +3 -3
- package/dist/chunks/{hrm-2KAZGUHI.js → hrm-YXERJM76.js} +3 -3
- package/dist/chunks/{im-ACJJWH4U.js → im-A4VZN2TQ.js} +88 -12
- package/dist/chunks/{invoice-ZK3WCX2U.js → invoice-2Z6YPMIS.js} +3 -3
- package/dist/chunks/{jiuchuanhui-5PQD3IL2.js → jiuchuanhui-AR5GMKZB.js} +2 -2
- package/dist/chunks/{lazy-dispatch-JQXBW3GM.js → lazy-dispatch-QXW7P4X3.js} +4 -4
- package/dist/chunks/{mail-S2HDDXO2.js → mail-7GZ6CPBJ.js} +45 -24
- package/dist/chunks/{meeting-QVN3I37L.js → meeting-3NMGA3MB.js} +278 -43
- package/dist/chunks/{okr-QNG4VC2Q.js → okr-BZ6XUOAG.js} +2 -2
- package/dist/chunks/operations-5QL3AECO.js +11 -0
- package/dist/chunks/{plan-A25EMW6U.js → plan-DR5CMUYP.js} +2 -2
- package/dist/chunks/{project-G6XV66DZ.js → project-6J63MK33.js} +2 -2
- package/dist/chunks/{registry-DC6KR3NH.js → registry-KDD7DHNK.js} +4 -4
- package/dist/chunks/{registry-2VLUAESA.js → registry-OZSMDVPP.js} +2 -2
- package/dist/chunks/{registry-7C5UMIPL.js → registry-TKX2E7C7.js} +2 -2
- package/dist/chunks/{registry-TI3CIUXF.js → registry-VRTNC6UR.js} +8 -6
- package/dist/chunks/{room-recommend-WRTHO6Y2.js → room-recommend-XCWRMB5P.js} +4 -3
- package/dist/chunks/{schedule-KFIITGSK.js → schedule-IVBICP32.js} +4 -3
- package/dist/chunks/{workflow-DJ7SYWAF.js → workflow-D74NFK3C.js} +179 -6
- package/dist/index.js +35 -25
- package/dist/internal/e10/auth-recovery.js +14 -4
- package/dist/internal/e10/fetch-timing.js +31 -0
- package/dist/internal/e10/request-runtime.js +5 -0
- package/dist/shortcuts/contract/host.js +34 -59
- package/dist/shortcuts/cowork/continuation.js +70 -0
- package/dist/shortcuts/cowork/errors.js +59 -0
- package/dist/shortcuts/cowork/host.js +180 -0
- package/dist/shortcuts/cowork/index.js +166 -0
- package/dist/shortcuts/cowork/manifest.js +65 -0
- package/dist/shortcuts/cowork/operations/discuss-write.js +277 -0
- package/dist/shortcuts/cowork/operations/item-write.js +380 -0
- package/dist/shortcuts/cowork/operations/member-write.js +283 -0
- package/dist/shortcuts/cowork/operations/permission-write.js +286 -0
- package/dist/shortcuts/cowork/operations/read.js +423 -0
- package/dist/shortcuts/cowork/operations/registry.js +39 -0
- package/dist/shortcuts/cowork/operations/review-write.js +157 -0
- package/dist/shortcuts/cowork/operations/shared.js +215 -0
- package/dist/shortcuts/cowork/operations/types.js +39 -0
- package/dist/shortcuts/cowork/operations.js +19 -0
- package/dist/shortcuts/ehr/host.js +4 -1
- package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +757 -0
- package/dist/shortcuts/ehr/operations/recruit-position.js +367 -0
- package/dist/shortcuts/ehr/operations/recruit-stats.js +133 -0
- package/dist/shortcuts/ehr/operations/recruit-write.js +43 -16
- package/dist/shortcuts/ehr/operations/recruit.js +182 -10
- package/dist/shortcuts/ehr/operations/registry.js +6 -0
- package/dist/shortcuts/fna/operations/expense-chart.js +19 -7
- package/dist/shortcuts/im/diagnostics.js +97 -0
- package/dist/shortcuts/im/host.js +58 -31
- package/dist/shortcuts/im/index.js +7 -2
- package/dist/shortcuts/im/operations/at-mention.js +127 -0
- package/dist/shortcuts/im/operations/file-user.js +10 -2
- package/dist/shortcuts/im/operations/group-read.js +26 -5
- package/dist/shortcuts/im/operations/group-write.js +35 -28
- package/dist/shortcuts/im/operations/msg-sync.js +7 -2
- package/dist/shortcuts/im/operations/msg-write.js +76 -10
- package/dist/shortcuts/im/operations/quote-mention.js +119 -0
- package/dist/shortcuts/im/operations/session.js +1 -1
- package/dist/shortcuts/im/operations/shared.js +10 -0
- package/dist/shortcuts/im/operations/types.js +13 -0
- package/dist/shortcuts/index.js +10 -0
- package/dist/shortcuts/mail/host.js +12 -1
- package/dist/shortcuts/mail/operations/types.js +17 -2
- package/dist/shortcuts/meeting/host.js +27 -34
- package/dist/shortcuts/meeting/index.js +79 -1
- package/dist/shortcuts/meeting/manifest.js +10 -0
- package/dist/shortcuts/meeting/operations/create.js +4 -1
- package/dist/shortcuts/meeting/operations/eb/catalog.js +10 -0
- package/dist/shortcuts/meeting/operations/eb/conflict.js +84 -8
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +1195 -140
- package/dist/shortcuts/meeting/operations/eb/factory.js +170 -8
- package/dist/shortcuts/meeting/operations/eb/index.js +16 -3
- package/dist/shortcuts/meeting/operations/eb/room-recommend.js +50 -5
- package/dist/shortcuts/meeting/operations/eb-fallback.js +163 -0
- package/dist/shortcuts/meeting/operations/env.js +99 -46
- package/dist/shortcuts/meeting/operations/form-fields.js +110 -0
- package/dist/shortcuts/meeting/operations/invitation.js +79 -20
- package/dist/shortcuts/meeting/operations/registry.js +2 -0
- package/dist/shortcuts/meeting/operations/standard-guard.js +71 -0
- package/dist/shortcuts/meeting/operations.js +16 -0
- package/dist/shortcuts/workflow/endpoints.js +8 -3
- package/dist/shortcuts/workflow/index.js +110 -1
- package/dist/shortcuts/workflow/operations/form.js +204 -36
- package/dist/shortcuts/workflow/operations/read.js +104 -23
- package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +448 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.js +106 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/businessRules.js +47 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +30 -23
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +1 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +1 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +2 -1
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +2 -4
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +138 -181
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +12 -7
- package/dist/shortcuts/workflow/operations.js +23 -1
- package/docs/e10-auth.md +1 -1
- package/docs/ehr.md +1 -1
- package/docs/fna.md +2 -1
- package/docs/im.md +8 -1
- package/docs/todo-smart-convert/README.md +1 -1
- package/docs/todo-smart-convert/data-contract.md +2 -2
- package/docs/todo-smart-convert/subgroup-rules.md +6 -0
- package/package.json +1 -1
- package/skill-template/business-info.json +18 -0
- package/skill-template/domains/cowork.md +14 -0
- package/skill-template/domains/yimiaoban.md +3 -3
- package/skill-template/skill-template.md +1 -1
- package/skills/weaver-e10-blog/SKILL.md +5 -5
- package/skills/weaver-e10-caizhilian/SKILL.md +5 -5
- package/skills/weaver-e10-calendar/SKILL.md +5 -5
- package/skills/weaver-e10-cowork/SKILL.md +101 -0
- package/skills/weaver-e10-cowork/references/cowork-read.md +90 -0
- package/skills/weaver-e10-cowork/references/cowork-write.md +131 -0
- package/skills/weaver-e10-cowork/references/safety-boundaries.md +45 -0
- package/skills/weaver-e10-esb/SKILL.md +5 -5
- package/skills/weaver-e10-hrm/SKILL.md +5 -5
- package/skills/weaver-e10-jinchengda/SKILL.md +2 -2
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -5
- package/skills/weaver-e10-jucailin/SKILL.md +38 -14
- package/skills/weaver-e10-jucailin/references/recruit-create.md +104 -0
- package/skills/weaver-e10-jucailin/references/recruit-read.md +25 -0
- package/skills/weaver-e10-jucailin/references/recruit-write.md +13 -0
- package/skills/weaver-e10-jucailin/references/salary.md +15 -11
- package/skills/weaver-e10-mail/SKILL.md +17 -14
- package/skills/weaver-e10-meeting/SKILL.md +442 -46
- package/skills/weaver-e10-meeting/references/meeting-eb.md +135 -3
- package/skills/weaver-e10-meeting/references/meeting-invitation.md +28 -2
- package/skills/weaver-e10-meeting/references/meeting-queries.md +9 -6
- package/skills/weaver-e10-meeting/references/meeting-verification.md +8 -6
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-okr/SKILL.md +5 -5
- package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
- package/skills/weaver-e10-plan/SKILL.md +5 -5
- package/skills/weaver-e10-qiyecheng/SKILL.md +5 -2
- package/skills/weaver-e10-qiyecheng/references/catalog.md +2 -2
- package/skills/weaver-e10-qiyecheng/references/expense-chart.md +15 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +22 -21
- package/skills/weaver-e10-shared/SKILL.md +12 -12
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -4
- package/skills/weaver-e10-wenshuding/SKILL.md +5 -5
- package/skills/weaver-e10-workflow/SKILL.md +29 -5
- package/skills/weaver-e10-workflow/aigroup/default.json +88 -0
- package/skills/weaver-e10-workflow/references/todo-smart-convert.md +66 -14
- package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +1 -1
- package/skills/weaver-e10-workflow/references/workflow-memory.md +1 -1
- package/skills/weaver-e10-workflow/references/workflow-query.md +12 -10
- package/skills/weaver-e10-workflow/references/workflow-view.md +2 -2
- package/skills/weaver-e10-workflow/tools/todo-serve.cjs +239 -0
- package/skills/weaver-e10-workflow/{scripts → tools}/todo-summary.cjs +218 -28
- package/skills/weaver-e10-yepiaotong/SKILL.md +10 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +7 -7
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +10 -10
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +40 -37
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -4
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +27 -38
- package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +4 -4
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +37 -29
- package/skills/weaver-e10-yimiaoban/SKILL.md +156 -33
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +5 -1
- package/skills/weaver-e10-yimiaoban/references/group-read.md +5 -1
- package/skills/weaver-e10-yimiaoban/references/group-write.md +6 -4
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -0
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +34 -2
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +5 -2
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +79 -58
- package/skills/weaver-e10-yimiaoban/references/user-remind.md +2 -2
- package/skills/weaver-e10-ziguanjia/SKILL.md +5 -5
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { CoworkCliError, coworkNotFoundError, coworkPartialError, coworkValidationError } from '../errors.js';
|
|
2
|
+
import { READ_BEFORE_WRITE, WRITE, SCHEMA_ID, coworkSuccess, schemaOf, } from './types.js';
|
|
3
|
+
import { assertEnum, assertString, compact, normalizeList, requireConfirm, toBase64Html, toId, toPositiveInt, unwrapData, verifyLease, } from './shared.js';
|
|
4
|
+
function isNetworkError(error) {
|
|
5
|
+
return error instanceof CoworkCliError && error.type === 'network';
|
|
6
|
+
}
|
|
7
|
+
/** 从协作详情取版块 id:发布讨论必须带 sectionId,页面是从当前协作上下文带出的。 */
|
|
8
|
+
async function resolveSectionId(host, coworkId) {
|
|
9
|
+
const response = await host.request({
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: '/api/cowork/view/viewCowork',
|
|
12
|
+
form: { id: coworkId },
|
|
13
|
+
operation: 'cowork.discuss.publish.prepare',
|
|
14
|
+
});
|
|
15
|
+
const detail = (unwrapData(response) ?? {});
|
|
16
|
+
if (!detail || Object.keys(detail).length === 0) {
|
|
17
|
+
throw coworkNotFoundError('item_not_found', `协作 ${coworkId} 不存在或无权查看`);
|
|
18
|
+
}
|
|
19
|
+
const sectionId = detail.sectionId ? String(detail.sectionId) : '';
|
|
20
|
+
if (!sectionId) {
|
|
21
|
+
throw coworkValidationError('section_unresolved', '无法从协作详情解析版块 id,请显式传 sectionId');
|
|
22
|
+
}
|
|
23
|
+
return sectionId;
|
|
24
|
+
}
|
|
25
|
+
const discussPublishPrepare = {
|
|
26
|
+
name: 'cowork.discuss.publish.prepare',
|
|
27
|
+
resource: 'cowork',
|
|
28
|
+
method: 'discuss.publish.prepare',
|
|
29
|
+
action: 'cowork.discuss.publish.prepare',
|
|
30
|
+
summary: '发布/回复讨论的确认摘要:先读协作详情确认存在并解析版块 id,不写入',
|
|
31
|
+
risk: READ_BEFORE_WRITE,
|
|
32
|
+
inputSchema: schemaOf({
|
|
33
|
+
coworkId: { ...SCHEMA_ID, description: '协作 id' },
|
|
34
|
+
content: { type: 'string', maxLength: 20000, description: '讨论内容(纯文本)' },
|
|
35
|
+
sectionId: { ...SCHEMA_ID, description: '版块 id;不传则从协作详情自动解析' },
|
|
36
|
+
}, { required: ['coworkId', 'content'] }),
|
|
37
|
+
handler: async (args, env) => {
|
|
38
|
+
const coworkId = toId(args.coworkId, 'coworkId');
|
|
39
|
+
const content = assertString(args.content, 'content', 20000);
|
|
40
|
+
const sectionId = args.sectionId !== undefined
|
|
41
|
+
? toId(args.sectionId, 'sectionId')
|
|
42
|
+
: await resolveSectionId(env.host, coworkId);
|
|
43
|
+
const session = await env.host.getSession();
|
|
44
|
+
const continuation = await env.continuation.issue({
|
|
45
|
+
operation: 'cowork.discuss.publish.apply',
|
|
46
|
+
baseUrl: session.baseUrl,
|
|
47
|
+
profile: session.profile,
|
|
48
|
+
userId: session.userId,
|
|
49
|
+
coworkId,
|
|
50
|
+
sectionId,
|
|
51
|
+
contentLength: content.length,
|
|
52
|
+
contentHash: hashText(content),
|
|
53
|
+
});
|
|
54
|
+
return coworkSuccess('cowork.discuss.publish.prepare', {
|
|
55
|
+
summary: {
|
|
56
|
+
coworkId,
|
|
57
|
+
sectionId,
|
|
58
|
+
sectionIdSource: args.sectionId !== undefined ? 'explicit' : 'resolved-from-view',
|
|
59
|
+
contentPreview: content.length > 120 ? `${content.slice(0, 120)}…` : content,
|
|
60
|
+
contentLength: content.length,
|
|
61
|
+
},
|
|
62
|
+
onApply: '将调用 publishCoworkDiscuss 发布一条讨论(replyType=discuss,client=pc)',
|
|
63
|
+
continuation,
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
function hashText(text) {
|
|
68
|
+
let hash = 0;
|
|
69
|
+
for (let i = 0; i < text.length; i++)
|
|
70
|
+
hash = (hash * 31 + text.charCodeAt(i)) | 0;
|
|
71
|
+
return String(hash >>> 0);
|
|
72
|
+
}
|
|
73
|
+
const discussPublishApply = {
|
|
74
|
+
name: 'cowork.discuss.publish.apply',
|
|
75
|
+
resource: 'cowork',
|
|
76
|
+
method: 'discuss.publish.apply',
|
|
77
|
+
action: 'cowork.discuss.publish.apply',
|
|
78
|
+
summary: '执行发布/回复讨论(需 confirm=true 与 continuation)',
|
|
79
|
+
risk: WRITE,
|
|
80
|
+
requiresConfirmation: true,
|
|
81
|
+
inputSchema: schemaOf({
|
|
82
|
+
coworkId: { ...SCHEMA_ID },
|
|
83
|
+
content: { type: 'string', maxLength: 20000 },
|
|
84
|
+
sectionId: { ...SCHEMA_ID },
|
|
85
|
+
confirm: { type: 'boolean' },
|
|
86
|
+
continuation: { type: 'string' },
|
|
87
|
+
}, { required: ['coworkId', 'content', 'confirm', 'continuation'] }),
|
|
88
|
+
handler: async (args, env) => {
|
|
89
|
+
requireConfirm(args, 'cowork.discuss.publish.apply');
|
|
90
|
+
const lease = await verifyLease(args, 'cowork.discuss.publish.apply', env);
|
|
91
|
+
const coworkId = toId(args.coworkId, 'coworkId');
|
|
92
|
+
const content = assertString(args.content, 'content', 20000);
|
|
93
|
+
if (lease.coworkId !== coworkId || lease.contentHash !== hashText(content)) {
|
|
94
|
+
throw coworkValidationError('target_changed', '协作 id 或讨论内容与 prepare 时不一致,请重新执行 prepare');
|
|
95
|
+
}
|
|
96
|
+
const sectionId = args.sectionId !== undefined
|
|
97
|
+
? toId(args.sectionId, 'sectionId')
|
|
98
|
+
: String(lease.sectionId);
|
|
99
|
+
const body = {
|
|
100
|
+
attachments: '',
|
|
101
|
+
relevanceList: [],
|
|
102
|
+
atLinkList: [],
|
|
103
|
+
desensitization: 0,
|
|
104
|
+
secondAuth: 0,
|
|
105
|
+
clobContent: toBase64Html(content),
|
|
106
|
+
client: 'pc',
|
|
107
|
+
relatedFile: '',
|
|
108
|
+
replyType: 'discuss',
|
|
109
|
+
cwItemsId: coworkId,
|
|
110
|
+
sectionId,
|
|
111
|
+
anonymous: 0,
|
|
112
|
+
approvalStatus: 1,
|
|
113
|
+
};
|
|
114
|
+
let response;
|
|
115
|
+
try {
|
|
116
|
+
response = await env.host.request({
|
|
117
|
+
method: 'POST',
|
|
118
|
+
path: '/api/cowork/main/publishCoworkDiscuss',
|
|
119
|
+
body,
|
|
120
|
+
operation: 'cowork.discuss.publish.apply',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (isNetworkError(error)) {
|
|
125
|
+
throw coworkPartialError('讨论发布请求已发出但未收到确定结果,禁止自动重试:请用 cowork.discuss.list 核对该协作最新讨论', { coworkId });
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
const data = (unwrapData(response) ?? {});
|
|
130
|
+
const root = response;
|
|
131
|
+
return coworkSuccess('cowork.discuss.publish.apply', {
|
|
132
|
+
coworkId,
|
|
133
|
+
sectionId,
|
|
134
|
+
status: data.status ?? root?.status ?? null,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
const DISCUSS_ACTIONS = ['top', 'untop', 'delete', 'approve'];
|
|
139
|
+
const DISCUSS_ACTION_PATHS = {
|
|
140
|
+
top: { path: '/api/cowork/view/topCoworkDiscuss', form: (id) => ({ id }) },
|
|
141
|
+
untop: { path: '/api/cowork/view/cancelTopCoworkDiscuss', form: (id) => ({ id }) },
|
|
142
|
+
delete: { path: '/api/cowork/view/deleteCoworkDiscuss', form: (id, all) => ({ id, isDeleteAll: all ? 1 : 0 }) },
|
|
143
|
+
approve: { path: '/api/cowork/view/approveCoworkDiscuss', form: (id) => ({ id }) },
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* 在协作的讨论记录里定位某条讨论(用于 prepare 的只读回查)。
|
|
147
|
+
* 讨论没有「按讨论 id 查详情」的接口,只能翻页匹配;翻页上限防失控。
|
|
148
|
+
*/
|
|
149
|
+
async function locateDiscuss(env, coworkId, discussId, maxPages = 5, pageSize = 50) {
|
|
150
|
+
for (let page = 1; page <= maxPages; page++) {
|
|
151
|
+
const response = await env.host.request({
|
|
152
|
+
method: 'POST',
|
|
153
|
+
path: '/api/cowork/view/discussRecord',
|
|
154
|
+
body: { coworkId, current: page, pageSize, orders: [{ column: 'lastReply', asc: false }] },
|
|
155
|
+
operation: 'cowork.discuss.action.prepare',
|
|
156
|
+
});
|
|
157
|
+
const { data } = normalizeList(response);
|
|
158
|
+
const rows = data;
|
|
159
|
+
if (!Array.isArray(rows) || rows.length === 0)
|
|
160
|
+
return null;
|
|
161
|
+
const hit = rows.find((row) => String(row.id ?? row.discussId ?? '') === discussId);
|
|
162
|
+
if (hit)
|
|
163
|
+
return hit;
|
|
164
|
+
if (rows.length < pageSize)
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const discussActionPrepare = {
|
|
170
|
+
name: 'cowork.discuss.action.prepare',
|
|
171
|
+
resource: 'cowork',
|
|
172
|
+
method: 'discuss.action.prepare',
|
|
173
|
+
action: 'cowork.discuss.action.prepare',
|
|
174
|
+
summary: '讨论动作摘要(top 置顶 / untop 取消置顶 / delete 删除 / approve 审批通过):在协作讨论中定位该讨论并回显内容,不写入',
|
|
175
|
+
risk: READ_BEFORE_WRITE,
|
|
176
|
+
inputSchema: schemaOf({
|
|
177
|
+
action: { type: 'string', enum: [...DISCUSS_ACTIONS], description: 'top / untop / delete / approve' },
|
|
178
|
+
coworkId: { ...SCHEMA_ID, description: '所属协作 id(用于定位讨论)' },
|
|
179
|
+
discussId: { ...SCHEMA_ID, description: '讨论 id' },
|
|
180
|
+
isDeleteAll: { type: 'boolean', description: 'action=delete 时是否删除全部回复,默认 true(与页面一致)' },
|
|
181
|
+
maxPages: { type: 'integer', minimum: 1, maximum: 20, description: '定位讨论的翻页上限,默认 5' },
|
|
182
|
+
pageSize: { type: 'integer', minimum: 1, maximum: 100, description: '每页条数,默认 50' },
|
|
183
|
+
}, { required: ['action', 'coworkId', 'discussId'] }),
|
|
184
|
+
handler: async (args, env) => {
|
|
185
|
+
const action = assertEnum(args.action, DISCUSS_ACTIONS, 'action');
|
|
186
|
+
const coworkId = toId(args.coworkId, 'coworkId');
|
|
187
|
+
const discussId = toId(args.discussId, 'discussId');
|
|
188
|
+
const hit = await locateDiscuss(env, coworkId, discussId, toPositiveInt(args.maxPages, 'maxPages') ?? 5, toPositiveInt(args.pageSize, 'pageSize') ?? 50);
|
|
189
|
+
if (!hit) {
|
|
190
|
+
throw coworkNotFoundError('discuss_not_found', `在前 ${toPositiveInt(args.maxPages, 'maxPages') ?? 5} 页讨论中未找到讨论 ${discussId}:请确认 coworkId 与讨论 id 是否匹配(禁止对未确认的讨论执行动作)`);
|
|
191
|
+
}
|
|
192
|
+
const session = await env.host.getSession();
|
|
193
|
+
const continuation = await env.continuation.issue({
|
|
194
|
+
operation: 'cowork.discuss.action.apply',
|
|
195
|
+
baseUrl: session.baseUrl,
|
|
196
|
+
profile: session.profile,
|
|
197
|
+
userId: session.userId,
|
|
198
|
+
action,
|
|
199
|
+
coworkId,
|
|
200
|
+
discussId,
|
|
201
|
+
isDeleteAll: args.isDeleteAll === false ? false : true,
|
|
202
|
+
});
|
|
203
|
+
return coworkSuccess('cowork.discuss.action.prepare', {
|
|
204
|
+
summary: {
|
|
205
|
+
action,
|
|
206
|
+
coworkId,
|
|
207
|
+
discussId,
|
|
208
|
+
contentPreview: previewOf(hit),
|
|
209
|
+
author: hit.creatorName ?? hit.username ?? hit.creator ?? null,
|
|
210
|
+
createdTime: hit.createTime ?? hit.createShowTime ?? null,
|
|
211
|
+
isDeleteAll: action === 'delete' ? (args.isDeleteAll === false ? false : true) : undefined,
|
|
212
|
+
},
|
|
213
|
+
continuation,
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
function previewOf(row) {
|
|
218
|
+
const raw = row.clobContent ?? row.content ?? row.summary ?? '';
|
|
219
|
+
const text = String(raw).replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
220
|
+
return text.length > 120 ? `${text.slice(0, 120)}…` : text;
|
|
221
|
+
}
|
|
222
|
+
const discussActionApply = {
|
|
223
|
+
name: 'cowork.discuss.action.apply',
|
|
224
|
+
resource: 'cowork',
|
|
225
|
+
method: 'discuss.action.apply',
|
|
226
|
+
action: 'cowork.discuss.action.apply',
|
|
227
|
+
summary: '执行讨论动作(top / untop / delete / approve),需 confirm=true 与 continuation',
|
|
228
|
+
risk: WRITE,
|
|
229
|
+
requiresConfirmation: true,
|
|
230
|
+
inputSchema: schemaOf({
|
|
231
|
+
action: { type: 'string', enum: [...DISCUSS_ACTIONS] },
|
|
232
|
+
coworkId: { ...SCHEMA_ID },
|
|
233
|
+
discussId: { ...SCHEMA_ID },
|
|
234
|
+
isDeleteAll: { type: 'boolean' },
|
|
235
|
+
confirm: { type: 'boolean' },
|
|
236
|
+
continuation: { type: 'string' },
|
|
237
|
+
}, { required: ['action', 'coworkId', 'discussId', 'confirm', 'continuation'] }),
|
|
238
|
+
handler: async (args, env) => {
|
|
239
|
+
requireConfirm(args, 'cowork.discuss.action.apply');
|
|
240
|
+
const lease = await verifyLease(args, 'cowork.discuss.action.apply', env);
|
|
241
|
+
const action = assertEnum(args.action, DISCUSS_ACTIONS, 'action');
|
|
242
|
+
const coworkId = toId(args.coworkId, 'coworkId');
|
|
243
|
+
const discussId = toId(args.discussId, 'discussId');
|
|
244
|
+
if (lease.action !== action || lease.discussId !== discussId) {
|
|
245
|
+
throw coworkValidationError('target_changed', '与 prepare 时的目标不一致,请重新执行 prepare');
|
|
246
|
+
}
|
|
247
|
+
const isDeleteAll = lease.isDeleteAll !== false;
|
|
248
|
+
const spec = DISCUSS_ACTION_PATHS[action];
|
|
249
|
+
let response;
|
|
250
|
+
try {
|
|
251
|
+
response = await env.host.request({
|
|
252
|
+
method: 'POST',
|
|
253
|
+
path: spec.path,
|
|
254
|
+
form: compact(spec.form(discussId, isDeleteAll)),
|
|
255
|
+
operation: 'cowork.discuss.action.apply',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
if (isNetworkError(error)) {
|
|
260
|
+
throw coworkPartialError('讨论动作请求已发出但未收到确定结果,禁止自动重试:请重新拉取讨论列表核对', { action, discussId });
|
|
261
|
+
}
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
return coworkSuccess('cowork.discuss.action.apply', {
|
|
265
|
+
action,
|
|
266
|
+
coworkId,
|
|
267
|
+
discussId,
|
|
268
|
+
result: unwrapData(response) ?? null,
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
export const coworkDiscussWriteOperations = [
|
|
273
|
+
discussPublishPrepare,
|
|
274
|
+
discussPublishApply,
|
|
275
|
+
discussActionPrepare,
|
|
276
|
+
discussActionApply,
|
|
277
|
+
];
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { CoworkCliError, coworkNotFoundError, coworkPartialError, coworkValidationError } from '../errors.js';
|
|
2
|
+
import { READ_BEFORE_WRITE, WRITE, SCHEMA_ID, SCHEMA_DATETIME, coworkSuccess, schemaOf, } from './types.js';
|
|
3
|
+
import { assertEnum, assertString, requireConfirm, toBase64Html, toId, unwrapData, verifyLease, } from './shared.js';
|
|
4
|
+
function isNetworkError(error) {
|
|
5
|
+
return error instanceof CoworkCliError && error.type === 'network';
|
|
6
|
+
}
|
|
7
|
+
/** 版块合法性校验:从新建表单的 items.sectionId.options 取真实版块 id(脚本实测结构)。 */
|
|
8
|
+
async function resolveSectionOptions(host) {
|
|
9
|
+
const response = await host.request({
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: '/api/cowork/main/getCoworkAddItemForm',
|
|
12
|
+
form: { id: '', type: 'new' },
|
|
13
|
+
operation: 'cowork.item.create.prepare',
|
|
14
|
+
});
|
|
15
|
+
const data = unwrapData(response);
|
|
16
|
+
const options = data?.items?.sectionId?.options;
|
|
17
|
+
if (!Array.isArray(options))
|
|
18
|
+
return [];
|
|
19
|
+
return options.map((o) => ({ id: String(o.id), name: String(o.content ?? o.name ?? '') }));
|
|
20
|
+
}
|
|
21
|
+
const SWITCH_FIELDS = [
|
|
22
|
+
'enableDiscussElog',
|
|
23
|
+
'enableViewLog',
|
|
24
|
+
'expandDetail',
|
|
25
|
+
'allowInvite',
|
|
26
|
+
'allowQuit',
|
|
27
|
+
'allowApply',
|
|
28
|
+
'allowHideDelelted',
|
|
29
|
+
];
|
|
30
|
+
function buildSwitches(args) {
|
|
31
|
+
const switches = {};
|
|
32
|
+
for (const field of SWITCH_FIELDS) {
|
|
33
|
+
const value = args[field];
|
|
34
|
+
switches[field] = value === undefined ? 1 : (value === true || value === 1 || value === '1' ? 1 : 0);
|
|
35
|
+
}
|
|
36
|
+
return switches;
|
|
37
|
+
}
|
|
38
|
+
const itemCreatePrepare = {
|
|
39
|
+
name: 'cowork.item.create.prepare',
|
|
40
|
+
resource: 'cowork',
|
|
41
|
+
method: 'item.create.prepare',
|
|
42
|
+
action: 'cowork.item.create.prepare',
|
|
43
|
+
summary: '新建协作的确认摘要:校验版块 id 是否在本环境真实存在、负责人默认为当前用户、开关默认全开;不写入',
|
|
44
|
+
risk: READ_BEFORE_WRITE,
|
|
45
|
+
inputSchema: schemaOf({
|
|
46
|
+
itemName: { type: 'string', maxLength: 200, description: '协作主题' },
|
|
47
|
+
itemContent: { type: 'string', maxLength: 20000, description: '详细说明(纯文本或 HTML,CLI 内部按接口要求做 Base64 编码)' },
|
|
48
|
+
sectionId: { ...SCHEMA_ID, description: '所属版块 id(用 cowork.form 的 sectionId 选项确定)' },
|
|
49
|
+
principal: { ...SCHEMA_ID, description: '负责人人员 id,默认当前登录用户' },
|
|
50
|
+
endTime: { ...SCHEMA_DATETIME, description: '截止时间 YYYY-MM-DD HH:mm,默认 7 天后' },
|
|
51
|
+
enableDiscussElog: { type: 'boolean', description: '启用讨论日志,默认 true' },
|
|
52
|
+
enableViewLog: { type: 'boolean', description: '启用查看日志,默认 true' },
|
|
53
|
+
expandDetail: { type: 'boolean', description: '展开详情,默认 true' },
|
|
54
|
+
allowInvite: { type: 'boolean', description: '允许邀请,默认 true' },
|
|
55
|
+
allowQuit: { type: 'boolean', description: '允许退出,默认 true' },
|
|
56
|
+
allowApply: { type: 'boolean', description: '允许申请,默认 true' },
|
|
57
|
+
allowHideDelelted: { type: 'boolean', description: '允许隐藏删除,默认 true' },
|
|
58
|
+
}, { required: ['itemName', 'sectionId'] }),
|
|
59
|
+
handler: async (args, env) => {
|
|
60
|
+
const itemName = assertString(args.itemName, 'itemName', 200);
|
|
61
|
+
const sectionId = toId(args.sectionId, 'sectionId');
|
|
62
|
+
const options = await resolveSectionOptions(env.host);
|
|
63
|
+
let sectionName;
|
|
64
|
+
if (options.length > 0) {
|
|
65
|
+
const hit = options.find((o) => o.id === sectionId);
|
|
66
|
+
if (!hit) {
|
|
67
|
+
throw coworkNotFoundError('section_not_found', `版块 id ${sectionId} 不在本环境的可选版块中,请用 cowork.form 重新获取`, { availableSections: options });
|
|
68
|
+
}
|
|
69
|
+
sectionName = hit.name;
|
|
70
|
+
}
|
|
71
|
+
const session = await env.host.getSession();
|
|
72
|
+
const principal = toId(args.principal, 'principal') || session.userId;
|
|
73
|
+
if (!principal) {
|
|
74
|
+
throw coworkValidationError('principal_required', '无法确定负责人:请显式传 principal(人员 id)');
|
|
75
|
+
}
|
|
76
|
+
const endTime = args.endTime
|
|
77
|
+
? assertString(args.endTime, 'endTime', 32)
|
|
78
|
+
: defaultEndTime();
|
|
79
|
+
const switches = buildSwitches(args);
|
|
80
|
+
const itemContent = typeof args.itemContent === 'string' ? args.itemContent : '';
|
|
81
|
+
const summary = {
|
|
82
|
+
itemName,
|
|
83
|
+
sectionId,
|
|
84
|
+
sectionName: sectionName ?? '(未取到版块名,仅校验了表单结构)',
|
|
85
|
+
principal,
|
|
86
|
+
principalIsCurrentUser: principal === session.userId,
|
|
87
|
+
endTime,
|
|
88
|
+
switches,
|
|
89
|
+
contentLength: itemContent.length,
|
|
90
|
+
};
|
|
91
|
+
const continuation = await env.continuation.issue({
|
|
92
|
+
operation: 'cowork.item.create.apply',
|
|
93
|
+
baseUrl: session.baseUrl,
|
|
94
|
+
profile: session.profile,
|
|
95
|
+
userId: session.userId,
|
|
96
|
+
itemName,
|
|
97
|
+
sectionId,
|
|
98
|
+
principal,
|
|
99
|
+
endTime,
|
|
100
|
+
switches,
|
|
101
|
+
contentHash: hashCode(itemContent),
|
|
102
|
+
});
|
|
103
|
+
return coworkSuccess('cowork.item.create.prepare', {
|
|
104
|
+
summary,
|
|
105
|
+
onApply: '将调用 saveItemsInfo 新建协作(主表 + 开关参数),成功后返回新协作 id',
|
|
106
|
+
continuation,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
function hashCode(text) {
|
|
111
|
+
let hash = 0;
|
|
112
|
+
for (let i = 0; i < text.length; i++) {
|
|
113
|
+
hash = (hash * 31 + text.charCodeAt(i)) | 0;
|
|
114
|
+
}
|
|
115
|
+
return String(hash >>> 0);
|
|
116
|
+
}
|
|
117
|
+
function defaultEndTime() {
|
|
118
|
+
const target = new Date(Date.now() + 7 * 24 * 3600 * 1000);
|
|
119
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
120
|
+
return `${target.getFullYear()}-${pad(target.getMonth() + 1)}-${pad(target.getDate())} ${pad(target.getHours())}:${pad(target.getMinutes())}`;
|
|
121
|
+
}
|
|
122
|
+
const itemCreateApply = {
|
|
123
|
+
name: 'cowork.item.create.apply',
|
|
124
|
+
resource: 'cowork',
|
|
125
|
+
method: 'item.create.apply',
|
|
126
|
+
action: 'cowork.item.create.apply',
|
|
127
|
+
summary: '执行新建协作(需 confirm=true 与 prepare 的 continuation)',
|
|
128
|
+
risk: WRITE,
|
|
129
|
+
requiresConfirmation: true,
|
|
130
|
+
inputSchema: schemaOf({
|
|
131
|
+
itemName: { type: 'string', maxLength: 200, description: '协作主题(须与 prepare 一致)' },
|
|
132
|
+
itemContent: { type: 'string', maxLength: 20000, description: '详细说明(须与 prepare 一致)' },
|
|
133
|
+
sectionId: { ...SCHEMA_ID, description: '所属版块 id' },
|
|
134
|
+
principal: { ...SCHEMA_ID, description: '负责人人员 id' },
|
|
135
|
+
endTime: { ...SCHEMA_DATETIME, description: '截止时间' },
|
|
136
|
+
enableDiscussElog: { type: 'boolean' },
|
|
137
|
+
enableViewLog: { type: 'boolean' },
|
|
138
|
+
expandDetail: { type: 'boolean' },
|
|
139
|
+
allowInvite: { type: 'boolean' },
|
|
140
|
+
allowQuit: { type: 'boolean' },
|
|
141
|
+
allowApply: { type: 'boolean' },
|
|
142
|
+
allowHideDelelted: { type: 'boolean' },
|
|
143
|
+
confirm: { type: 'boolean', description: '用户明确确认后传 true' },
|
|
144
|
+
continuation: { type: 'string', description: 'prepare 返回的 continuation' },
|
|
145
|
+
}, { required: ['itemName', 'sectionId', 'confirm', 'continuation'] }),
|
|
146
|
+
handler: async (args, env) => {
|
|
147
|
+
requireConfirm(args, 'cowork.item.create.apply');
|
|
148
|
+
const lease = await verifyLease(args, 'cowork.item.create.apply', env);
|
|
149
|
+
const itemName = assertString(args.itemName, 'itemName', 200);
|
|
150
|
+
const sectionId = toId(args.sectionId, 'sectionId');
|
|
151
|
+
const principal = args.principal !== undefined
|
|
152
|
+
? toId(args.principal, 'principal')
|
|
153
|
+
: String(lease.principal || '');
|
|
154
|
+
const endTime = assertString(args.endTime || lease.endTime, 'endTime', 32);
|
|
155
|
+
const itemContent = typeof args.itemContent === 'string' ? args.itemContent : '';
|
|
156
|
+
const switches = buildSwitches(args);
|
|
157
|
+
// 快照一致性:prepare 与 apply 的目标/内容指纹必须一致,避免用户确认后被替换。
|
|
158
|
+
if (lease.itemName !== itemName || lease.sectionId !== sectionId || lease.principal !== principal) {
|
|
159
|
+
throw coworkValidationError('target_changed', '与 prepare 时的目标不一致(主题/版块/负责人已变化),请重新执行 prepare');
|
|
160
|
+
}
|
|
161
|
+
if (lease.contentHash !== hashCode(itemContent)) {
|
|
162
|
+
throw coworkValidationError('content_changed', '说明内容与 prepare 时不一致,请重新执行 prepare');
|
|
163
|
+
}
|
|
164
|
+
const body = {
|
|
165
|
+
itemName,
|
|
166
|
+
itemContent: toBase64Html(itemContent),
|
|
167
|
+
sectionId,
|
|
168
|
+
principal,
|
|
169
|
+
endTime,
|
|
170
|
+
...switches,
|
|
171
|
+
relatedFile: '',
|
|
172
|
+
params: {
|
|
173
|
+
...switches,
|
|
174
|
+
itemName,
|
|
175
|
+
relatedFile: [],
|
|
176
|
+
principal: principal ? [{ id: principal, name: '' }] : [],
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
let response;
|
|
180
|
+
try {
|
|
181
|
+
response = await env.host.request({
|
|
182
|
+
method: 'POST',
|
|
183
|
+
path: '/api/cowork/main/saveItemsInfo',
|
|
184
|
+
body,
|
|
185
|
+
operation: 'cowork.item.create.apply',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (isNetworkError(error)) {
|
|
190
|
+
throw coworkPartialError('新建协作请求已发出但未收到确定结果,禁止自动重试:请先用 cowork.list 核对是否已创建', { itemName, sectionId });
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
const data = (unwrapData(response) ?? {});
|
|
195
|
+
const root = response;
|
|
196
|
+
const ok = !!(data.status ?? root?.status);
|
|
197
|
+
if (!ok) {
|
|
198
|
+
throw coworkPartialError('新建协作返回未确认成功,请用 cowork.list 核对后再决定是否重试', { itemName, raw: data });
|
|
199
|
+
}
|
|
200
|
+
return coworkSuccess('cowork.item.create.apply', {
|
|
201
|
+
id: data.id ? String(data.id) : null,
|
|
202
|
+
itemName,
|
|
203
|
+
sectionId,
|
|
204
|
+
principal,
|
|
205
|
+
endTime,
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
const ITEM_ACTIONS = ['end'];
|
|
210
|
+
const itemActionPrepare = {
|
|
211
|
+
name: 'cowork.item.action.prepare',
|
|
212
|
+
resource: 'cowork',
|
|
213
|
+
method: 'item.action.prepare',
|
|
214
|
+
action: 'cowork.item.action.prepare',
|
|
215
|
+
summary: '协作级动作的确认摘要(当前支持 end 结束协作):先读详情校验存在与 canEnd,不写入',
|
|
216
|
+
risk: READ_BEFORE_WRITE,
|
|
217
|
+
inputSchema: schemaOf({
|
|
218
|
+
action: { type: 'string', enum: [...ITEM_ACTIONS], description: 'end=结束协作(不可逆)' },
|
|
219
|
+
id: { ...SCHEMA_ID, description: '协作 id' },
|
|
220
|
+
}, { required: ['action', 'id'] }),
|
|
221
|
+
handler: async (args, env) => {
|
|
222
|
+
const action = assertEnum(args.action, ITEM_ACTIONS, 'action');
|
|
223
|
+
const id = toId(args.id, 'id');
|
|
224
|
+
const detailResponse = await env.host.request({
|
|
225
|
+
method: 'POST',
|
|
226
|
+
path: '/api/cowork/view/viewCowork',
|
|
227
|
+
form: { id },
|
|
228
|
+
operation: 'cowork.item.action.prepare',
|
|
229
|
+
});
|
|
230
|
+
const detail = (unwrapData(detailResponse) ?? {});
|
|
231
|
+
if (!detail || Object.keys(detail).length === 0) {
|
|
232
|
+
throw coworkNotFoundError('item_not_found', `协作 ${id} 不存在或无权查看`);
|
|
233
|
+
}
|
|
234
|
+
const canEnd = String(detail.canEnd) === '1';
|
|
235
|
+
if (!canEnd) {
|
|
236
|
+
throw coworkValidationError('not_permitted', `当前用户不可结束该协作(canEnd=${String(detail.canEnd)})`);
|
|
237
|
+
}
|
|
238
|
+
const session = await env.host.getSession();
|
|
239
|
+
const continuation = await env.continuation.issue({
|
|
240
|
+
operation: 'cowork.item.action.apply',
|
|
241
|
+
baseUrl: session.baseUrl,
|
|
242
|
+
profile: session.profile,
|
|
243
|
+
userId: session.userId,
|
|
244
|
+
action,
|
|
245
|
+
id,
|
|
246
|
+
itemName: detail.itemName ?? '',
|
|
247
|
+
});
|
|
248
|
+
return coworkSuccess('cowork.item.action.prepare', {
|
|
249
|
+
summary: {
|
|
250
|
+
action,
|
|
251
|
+
id,
|
|
252
|
+
itemName: detail.itemName ?? '',
|
|
253
|
+
status: detail.itemStatus,
|
|
254
|
+
canEnd,
|
|
255
|
+
warning: action === 'end' ? '结束协作后参与者不可再回复,且页面不提供撤销' : undefined,
|
|
256
|
+
},
|
|
257
|
+
continuation,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
const itemActionApply = {
|
|
262
|
+
name: 'cowork.item.action.apply',
|
|
263
|
+
resource: 'cowork',
|
|
264
|
+
method: 'item.action.apply',
|
|
265
|
+
action: 'cowork.item.action.apply',
|
|
266
|
+
summary: '执行协作级动作(end 结束协作),需 confirm=true 与 continuation',
|
|
267
|
+
risk: WRITE,
|
|
268
|
+
requiresConfirmation: true,
|
|
269
|
+
inputSchema: schemaOf({
|
|
270
|
+
action: { type: 'string', enum: [...ITEM_ACTIONS] },
|
|
271
|
+
id: { ...SCHEMA_ID },
|
|
272
|
+
confirm: { type: 'boolean' },
|
|
273
|
+
continuation: { type: 'string' },
|
|
274
|
+
}, { required: ['action', 'id', 'confirm', 'continuation'] }),
|
|
275
|
+
handler: async (args, env) => {
|
|
276
|
+
requireConfirm(args, 'cowork.item.action.apply');
|
|
277
|
+
const lease = await verifyLease(args, 'cowork.item.action.apply', env);
|
|
278
|
+
const action = assertEnum(args.action, ITEM_ACTIONS, 'action');
|
|
279
|
+
const id = toId(args.id, 'id');
|
|
280
|
+
if (lease.action !== action || lease.id !== id) {
|
|
281
|
+
throw coworkValidationError('target_changed', '与 prepare 时的目标不一致,请重新执行 prepare');
|
|
282
|
+
}
|
|
283
|
+
let response;
|
|
284
|
+
try {
|
|
285
|
+
response = await env.host.request({
|
|
286
|
+
method: 'POST',
|
|
287
|
+
path: '/api/cowork/view/endCoworkItem',
|
|
288
|
+
form: { id },
|
|
289
|
+
operation: 'cowork.item.action.apply',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (isNetworkError(error)) {
|
|
294
|
+
throw coworkPartialError('结束协作请求已发出但未收到确定结果,禁止自动重试:请先用 cowork.view 核对状态', { id });
|
|
295
|
+
}
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
return coworkSuccess('cowork.item.action.apply', { action, id, result: unwrapData(response) ?? null });
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
const ITEM_FLAGS = ['hide', 'vote'];
|
|
302
|
+
const itemFlagPrepare = {
|
|
303
|
+
name: 'cowork.item.flag.prepare',
|
|
304
|
+
resource: 'cowork',
|
|
305
|
+
method: 'item.flag.prepare',
|
|
306
|
+
action: 'cowork.item.flag.prepare',
|
|
307
|
+
summary: '协作标记动作摘要:hide=从我的列表隐藏 / vote=标记投票;先读详情确认协作存在,不写入',
|
|
308
|
+
risk: READ_BEFORE_WRITE,
|
|
309
|
+
inputSchema: schemaOf({
|
|
310
|
+
action: { type: 'string', enum: [...ITEM_FLAGS], description: 'hide 隐藏 / vote 标记投票' },
|
|
311
|
+
id: { ...SCHEMA_ID, description: '协作 id' },
|
|
312
|
+
}, { required: ['action', 'id'] }),
|
|
313
|
+
handler: async (args, env) => {
|
|
314
|
+
const action = assertEnum(args.action, ITEM_FLAGS, 'action');
|
|
315
|
+
const id = toId(args.id, 'id');
|
|
316
|
+
const detailResponse = await env.host.request({
|
|
317
|
+
method: 'POST',
|
|
318
|
+
path: '/api/cowork/view/viewCowork',
|
|
319
|
+
form: { id },
|
|
320
|
+
operation: 'cowork.item.flag.prepare',
|
|
321
|
+
});
|
|
322
|
+
const detail = (unwrapData(detailResponse) ?? {});
|
|
323
|
+
if (!detail || Object.keys(detail).length === 0) {
|
|
324
|
+
throw coworkNotFoundError('item_not_found', `协作 ${id} 不存在或无权查看`);
|
|
325
|
+
}
|
|
326
|
+
const session = await env.host.getSession();
|
|
327
|
+
const continuation = await env.continuation.issue({
|
|
328
|
+
operation: 'cowork.item.flag.apply',
|
|
329
|
+
baseUrl: session.baseUrl,
|
|
330
|
+
profile: session.profile,
|
|
331
|
+
userId: session.userId,
|
|
332
|
+
action,
|
|
333
|
+
id,
|
|
334
|
+
});
|
|
335
|
+
return coworkSuccess('cowork.item.flag.prepare', {
|
|
336
|
+
summary: { action, id, itemName: detail.itemName ?? '' },
|
|
337
|
+
continuation,
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
const itemFlagApply = {
|
|
342
|
+
name: 'cowork.item.flag.apply',
|
|
343
|
+
resource: 'cowork',
|
|
344
|
+
method: 'item.flag.apply',
|
|
345
|
+
action: 'cowork.item.flag.apply',
|
|
346
|
+
summary: '执行协作标记动作(hide / vote),需 confirm=true 与 continuation',
|
|
347
|
+
risk: WRITE,
|
|
348
|
+
requiresConfirmation: true,
|
|
349
|
+
inputSchema: schemaOf({
|
|
350
|
+
action: { type: 'string', enum: [...ITEM_FLAGS] },
|
|
351
|
+
id: { ...SCHEMA_ID },
|
|
352
|
+
confirm: { type: 'boolean' },
|
|
353
|
+
continuation: { type: 'string' },
|
|
354
|
+
}, { required: ['action', 'id', 'confirm', 'continuation'] }),
|
|
355
|
+
handler: async (args, env) => {
|
|
356
|
+
requireConfirm(args, 'cowork.item.flag.apply');
|
|
357
|
+
const lease = await verifyLease(args, 'cowork.item.flag.apply', env);
|
|
358
|
+
const action = assertEnum(args.action, ITEM_FLAGS, 'action');
|
|
359
|
+
const id = toId(args.id, 'id');
|
|
360
|
+
if (lease.action !== action || lease.id !== id) {
|
|
361
|
+
throw coworkValidationError('target_changed', '与 prepare 时的目标不一致,请重新执行 prepare');
|
|
362
|
+
}
|
|
363
|
+
const form = action === 'hide' ? { hidden: 1, ids: id } : { vote: 1, ids: id };
|
|
364
|
+
const response = await env.host.request({
|
|
365
|
+
method: 'POST',
|
|
366
|
+
path: action === 'hide' ? '/api/cowork/behavior/markAsHidden' : '/api/cowork/behavior/markAsVote',
|
|
367
|
+
form,
|
|
368
|
+
operation: 'cowork.item.flag.apply',
|
|
369
|
+
});
|
|
370
|
+
return coworkSuccess('cowork.item.flag.apply', { action, id, result: unwrapData(response) ?? null });
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
export const coworkItemWriteOperations = [
|
|
374
|
+
itemCreatePrepare,
|
|
375
|
+
itemCreateApply,
|
|
376
|
+
itemActionPrepare,
|
|
377
|
+
itemActionApply,
|
|
378
|
+
itemFlagPrepare,
|
|
379
|
+
itemFlagApply,
|
|
380
|
+
];
|