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
@@ -0,0 +1,508 @@
1
+ import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import { chmod, mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
4
+ import { homedir, hostname } from 'node:os';
5
+ import { resolve } from 'node:path';
6
+ import { workflowAlreadyAppliedError, workflowPartialError, workflowValidationError, } from './errors.js';
7
+ const DEFAULT_TTL_MS = 10 * 60_000;
8
+ /**
9
+ * 「上一次只抢到句柄、还没发出写入」的**安全重占宽限期**。
10
+ *
11
+ * 抢到句柄到把阶段推进到 `saving`(发出写入请求前)之间还有若干本地/只读步骤
12
+ * (只读校验、字段联动等),这段时间内不能让别人抢走,否则两次执行会同时写入。
13
+ * 超过宽限期仍停在 `claimed`、且记录里的进程已不存在 ⇒ 上一次确实死在写入之前,可安全重占。
14
+ */
15
+ const RECLAIM_GRACE_MS = 30_000;
16
+ /**
17
+ * 短句柄格式:`wc-` + 16 位十六进制(8 字节随机)。
18
+ *
19
+ * 句柄只是**本机不透明 id**,不是可解开的令牌——prepare 算好的载荷落在
20
+ * `<stateRoot>/state/workflow/continuation-pending/<句柄>`,apply 时按句柄取回。
21
+ * 这样模型只需要原样搬运 ~19 个字符,而不必复制数百字符的 base64 载荷。
22
+ *
23
+ * 安全:句柄来自模型入参,**必须先过这条严格正则**才能拼进文件路径,
24
+ * 否则 `wc-../../…` 之类的输入会造成路径穿越。
25
+ */
26
+ const HANDLE_PATTERN = /^wc-[0-9a-f]{16}$/;
27
+ /**
28
+ * 状态目录清理阈值(V12.1.23):只有目录内条目数**超过**该值时,才顺手清掉已过期的条目。
29
+ *
30
+ * 背景(对齐 V11.3):V11.3 的写门禁状态是**单文件** `session_maps.json`,直接覆盖写、
31
+ * 写路径**零删除**,所以不会触发宿主沙箱的「文件删除审批」。V12 的 continuation 是
32
+ * 「一句柄一文件」,只能靠删除让目录有界;而「每次写操作都顺手清理」会让**每一次**
33
+ * apply / prepare 都产生删除动作——宿主据此判定该命令要删文件,弹出提权确认,并把命令在
34
+ * 无沙箱下**重跑一次**(重跑撞一次性门禁 → 模型拿到 `ALREADY_APPLIED`)。
35
+ * 改成阈值触发后,日常写操作**零删除**;只有残留累积到阈值时才做一次集中清理。
36
+ */
37
+ const SWEEP_THRESHOLD = 200;
38
+ /** 目录条目数是否已达到需要清理的规模。读取失败视为「不需要」,本判断不产生副作用。 */
39
+ async function shouldSweep(dir) {
40
+ try {
41
+ return (await readdir(dir)).length > SWEEP_THRESHOLD;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ function stateRoot(ctx) {
48
+ const configured = ctx?.config?.path();
49
+ if (configured)
50
+ return resolve(configured, '..');
51
+ return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
52
+ }
53
+ /** 旧版自包含令牌的签名密钥(仅兼容路径在用:verify 旧令牌 / 验证其签名)。 */
54
+ async function loadKey(ctx) {
55
+ const keysDir = resolve(stateRoot(ctx), 'keys');
56
+ const filename = resolve(keysDir, 'workflow-continuation.key');
57
+ await mkdir(keysDir, { recursive: true, mode: 0o700 });
58
+ if (existsSync(filename))
59
+ return readFile(filename);
60
+ const key = randomBytes(32);
61
+ try {
62
+ await writeFile(filename, key, { flag: 'wx', mode: 0o600 });
63
+ if (process.platform !== 'win32')
64
+ await chmod(filename, 0o600).catch(() => undefined);
65
+ return key;
66
+ }
67
+ catch {
68
+ return readFile(filename);
69
+ }
70
+ }
71
+ function signature(payload, key) {
72
+ return createHmac('sha256', key).update(payload).digest('base64url');
73
+ }
74
+ /** 引用的稳定指纹(sha256),用于一次性消费去重——不依赖签名 key,跨进程一致。 */
75
+ function referenceFingerprint(reference) {
76
+ return createHash('sha256').update(reference).digest('hex');
77
+ }
78
+ /** 托管中的续跑载荷目录(与 search-index.json 同层 state/workflow/)。 */
79
+ function pendingDir(ctx) {
80
+ return resolve(stateRoot(ctx), 'state', 'workflow', 'continuation-pending');
81
+ }
82
+ /** 已消费续跑引用的落盘目录(与 search-index.json 同层 state/workflow/)。 */
83
+ function consumedDir(ctx) {
84
+ return resolve(stateRoot(ctx), 'state', 'workflow', 'continuation-consumed');
85
+ }
86
+ /** 清理已过期的消费记录,避免目录无限增长。兼容旧版「纯数字 expiresAt」标记与新版 JSON 标记。 */
87
+ async function sweepConsumed(dir) {
88
+ let names;
89
+ try {
90
+ names = await readdir(dir);
91
+ }
92
+ catch {
93
+ return;
94
+ }
95
+ const now = Date.now();
96
+ for (const name of names) {
97
+ const file = resolve(dir, name);
98
+ try {
99
+ const content = await readFile(file, 'utf8');
100
+ let expiresAt = Number(content);
101
+ if (!Number.isFinite(expiresAt)) {
102
+ try {
103
+ expiresAt = Number(JSON.parse(content)?.expiresAt);
104
+ }
105
+ catch {
106
+ /* 无法解析:保守留档,不删 */
107
+ }
108
+ }
109
+ if (Number.isFinite(expiresAt) && expiresAt < now)
110
+ await unlink(file);
111
+ }
112
+ catch {
113
+ /* 单文件清理失败不影响主流程 */
114
+ }
115
+ }
116
+ }
117
+ /** 读取消费标记;返回 null 表示「未被消费」。旧版纯数字标记归一化为 `{state:'unknown'}`。 */
118
+ async function readConsumedMarker(ctx, reference) {
119
+ const file = resolve(consumedDir(ctx), referenceFingerprint(reference));
120
+ let raw;
121
+ try {
122
+ raw = await readFile(file, 'utf8');
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ try {
128
+ const parsed = JSON.parse(raw);
129
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
130
+ return parsed;
131
+ }
132
+ catch {
133
+ /* 旧版格式(纯数字)走下面归一化 */
134
+ }
135
+ return { state: 'unknown' };
136
+ }
137
+ /** 标记里记录的进程是否还活着(同 hostname 才判;没有 pid 或判定不了时一律按"还活着"保守处理)。 */
138
+ function markerProcessAlive(marker) {
139
+ const pid = Number(marker.pid);
140
+ if (!Number.isFinite(pid) || pid <= 0)
141
+ return true;
142
+ if (typeof marker.hostname === 'string' && marker.hostname && marker.hostname !== hostname())
143
+ return true;
144
+ try {
145
+ process.kill(pid, 0);
146
+ return true;
147
+ }
148
+ catch (error) {
149
+ return error?.code !== 'ESRCH';
150
+ }
151
+ }
152
+ function replayOf(marker) {
153
+ let state = 'unknown';
154
+ if (marker.state === 'succeeded') {
155
+ state = 'succeeded';
156
+ }
157
+ else if (marker.state === 'running') {
158
+ // V12.1.21 起标记带 stage;更早的标记(无 stage)无法区分"已发出写入"与"刚抢到",保守按 saving 处理。
159
+ state = marker.stage === 'claimed' ? 'claimed'
160
+ : marker.stage === 'committed' ? 'committed'
161
+ : 'saving';
162
+ }
163
+ const stage = marker.stage === 'claimed' || marker.stage === 'saving' || marker.stage === 'committed'
164
+ ? marker.stage
165
+ : undefined;
166
+ return {
167
+ state,
168
+ ...(stage ? { stage } : {}),
169
+ ...(typeof marker.operation === 'string' ? { operation: marker.operation } : {}),
170
+ ...(Number.isFinite(Number(marker.consumedAt)) ? { consumedAt: Number(marker.consumedAt) } : {}),
171
+ ...(Number.isFinite(Number(marker.finishedAt)) ? { finishedAt: Number(marker.finishedAt) } : {}),
172
+ ...(typeof marker.requestId === 'string' && marker.requestId ? { requestId: marker.requestId } : {}),
173
+ ...(state === 'succeeded' ? { result: marker.result ?? null } : {}),
174
+ };
175
+ }
176
+ /**
177
+ * 引用已被占用时的错误:**上一次执行成功** → 幂等重放(调度层转成功信封);否则维持「已使用」语义。
178
+ *
179
+ * 上一次的状态必须并入文案——旧文案默认「已使用=上次失败了」,直接诱导模型重跑 Prepare,
180
+ * 而重跑在批量门禁(写入成功后条目已不在可执行列表)里必然失败,是一条死路。
181
+ */
182
+ export function continuationReplayError(replay, expectedOperation) {
183
+ const operation = replay.operation ?? expectedOperation;
184
+ if (replay.state === 'succeeded') {
185
+ return workflowAlreadyAppliedError(operation, replay);
186
+ }
187
+ if (replay.state === 'committed') {
188
+ // 服务端已返回成功回执、只是没来得及回写结果:同样属"已经做过",按幂等回报。
189
+ return workflowAlreadyAppliedError(operation, replay);
190
+ }
191
+ if (replay.state === 'saving') {
192
+ // 写入请求已发出但结果未知:一律按"结果不确定"处理(先只读核对,禁止重复提交)。
193
+ // 恢复路径按命令形态分述:单命令(batchSubmit)没有 Prepare 可重跑,只能等幂等窗口过去后原样重跑。
194
+ const recovery = /Apply$/u.test(operation)
195
+ ? '确认未生效后再重跑对应的 Prepare 获取新句柄。'
196
+ : '确认未生效后,等本次写入的幂等保护窗口过去(约 1 分钟)再原样重跑同一条命令(同一批 requestId 在窗口内会被拦住)。';
197
+ return workflowPartialError('该 continuation 对应的上一次执行**已经发出写入请求,但结果未确认**(进程被中止或回执丢失),'
198
+ + '本次未执行、也不要重复提交。请先只读核对(如 workflow.search --category done --requestId <目标ID>'
199
+ + ',或 workflow.search --category mine 看是否已出现目标条目)确认真实状态,'
200
+ + recovery, replay, { stage: 'continuation_replay', ...replay });
201
+ }
202
+ if (replay.state === 'claimed') {
203
+ // 只抢到句柄、未发出任何写入:并发的另一次执行正在占用,等它结束再重试即可。
204
+ return workflowValidationError('continuation_busy', '该 continuation 正被另一次执行占用(上一次只抢到句柄、尚未发出写入请求),本次未执行。'
205
+ + '请稍后重试同一条命令;若上一次执行其实已经完成,直接看返回结果即可。', replay);
206
+ }
207
+ return workflowValidationError('continuation_consumed', 'continuation 已使用(一次性),禁止重复提交。若怀疑上一次没生效,请先只读核对目标流程是否已存在'
208
+ + '(workflow.search --category done/mine --requestId <目标ID>),确认未生效再重跑对应的 Prepare。', replay);
209
+ }
210
+ /** 清理已过期但仍未被 apply 消费的托管载荷,避免 prepare 后不了了之的调用累积。 */
211
+ async function sweepPending(dir) {
212
+ let names;
213
+ try {
214
+ names = await readdir(dir);
215
+ }
216
+ catch {
217
+ return;
218
+ }
219
+ const now = Date.now();
220
+ for (const name of names) {
221
+ const file = resolve(dir, name);
222
+ try {
223
+ const value = JSON.parse(await readFile(file, 'utf8'));
224
+ const expiresAt = Number(value?.expiresAt);
225
+ if (Number.isFinite(expiresAt) && expiresAt < now)
226
+ await unlink(file);
227
+ }
228
+ catch {
229
+ /* 单文件清理失败不影响主流程 */
230
+ }
231
+ }
232
+ }
233
+ /** 按句柄读取托管载荷;文件不在 = 已被消费 / 已过期 / 不是本机签发的句柄。 */
234
+ async function readPendingPayload(ctx, handle) {
235
+ let content;
236
+ try {
237
+ content = await readFile(resolve(pendingDir(ctx), handle), 'utf8');
238
+ }
239
+ catch {
240
+ throw workflowValidationError('continuation_expired', 'continuation 在本机状态目录中不存在(已被使用、已过期,或不是本机签发的句柄)。'
241
+ + '请重新执行对应的 Prepare 命令后再 apply,不要复用旧句柄。');
242
+ }
243
+ try {
244
+ return JSON.parse(content);
245
+ }
246
+ catch {
247
+ throw workflowValidationError('continuation_invalid', 'continuation 本机载荷无法解析,请重新执行对应的 Prepare 命令');
248
+ }
249
+ }
250
+ /**
251
+ * 兼容 V12.1.11 及更早版本签发的**自包含签名令牌**(`base64url(JSON).HMAC-SHA256`):
252
+ * 升级前已在会话里拿到的旧令牌仍可用,不必重新 prepare。新会话一律走短句柄。
253
+ */
254
+ async function verifyLegacyToken(ctx, token) {
255
+ const [body, provided] = token.split('.');
256
+ const expected = signature(body, await loadKey(ctx));
257
+ const valid = typeof provided === 'string'
258
+ && provided.length === expected.length
259
+ && timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
260
+ if (!valid)
261
+ throw workflowValidationError('continuation_invalid', 'continuation 签名校验失败');
262
+ try {
263
+ return JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
264
+ }
265
+ catch {
266
+ throw workflowValidationError('continuation_invalid', 'continuation 内容无法解析');
267
+ }
268
+ }
269
+ /**
270
+ * 尝试**安全重占**一个"只抢到句柄、还没发出写入请求"的标记。
271
+ *
272
+ * 返回 `null` 表示重占成功(调用方可继续);返回 replay 表示仍被占用(不得写入)。
273
+ *
274
+ * 三条同时成立才允许重占:① 阶段停在 `claimed`;② 距占用已超过 `RECLAIM_GRACE_MS`;
275
+ * ③ 记录里的进程已经不在了。⇒ 上一次确定死在"发出写入请求之前",重占不会造成重复写入。
276
+ *
277
+ * 原子性不靠 `wx`(此时文件已存在):先写入自己的 `owner`,再**回读校验 owner 是否还是自己**——
278
+ * 并发重占时只有最后一个写入者能读到自己的 owner,其余全部退出,不存在"先判存在再写"的窗口。
279
+ */
280
+ async function tryReclaim(ctx, reference, marker, record) {
281
+ const age = Date.now() - Number(marker.consumedAt);
282
+ const claimable = marker.state === 'running' && marker.stage === 'claimed';
283
+ if (!claimable || !Number.isFinite(age) || age < RECLAIM_GRACE_MS || markerProcessAlive(marker)) {
284
+ return replayOf(marker);
285
+ }
286
+ const file = resolve(consumedDir(ctx), referenceFingerprint(reference));
287
+ try {
288
+ await writeFile(file, JSON.stringify(record), { mode: 0o600 });
289
+ }
290
+ catch {
291
+ return replayOf(marker);
292
+ }
293
+ const after = await readConsumedMarker(ctx, reference);
294
+ if (after && after.owner === record.owner)
295
+ return null;
296
+ return replayOf(after ?? marker);
297
+ }
298
+ /**
299
+ * **有界幂等窗口内的过期接管**:把一条「已确认成功、但幂等窗口已过」的消费标记原子换成本次的标记。
300
+ *
301
+ * 只在调用方**显式声明了窗口**(`consume(..., ttlMs)`)时才会被调用,且只接管 `succeeded` 标记:
302
+ * `saving`(写入已发出、结果未知)与 `running/committed`(服务端已回执但本机没落结果)都**不在**
303
+ * 接管范围内——它们永不放行重放;`running/claimed`(并发占用中)交给 `tryReclaim` 按
304
+ * 「宽限期 + 进程已死」判定,不在这里抢。
305
+ *
306
+ * 返回 true 表示本次已接管(调用方可继续写入)。原子性同 `tryReclaim`:先写入自己的 owner,
307
+ * 再**回读校验 owner 仍是自己**——并发接管时只有最后一个写入者能读到自己,其余按"仍被占用"退出。
308
+ */
309
+ async function tryTakeoverExpiredWindow(ctx, reference, marker, record) {
310
+ const expiresAt = Number(marker.expiresAt);
311
+ if (!Number.isFinite(expiresAt) || expiresAt >= Date.now())
312
+ return false;
313
+ if (marker.state !== 'succeeded')
314
+ return false;
315
+ const file = resolve(consumedDir(ctx), referenceFingerprint(reference));
316
+ try {
317
+ await writeFile(file, JSON.stringify(record), { mode: 0o600 });
318
+ }
319
+ catch {
320
+ return false;
321
+ }
322
+ const after = await readConsumedMarker(ctx, reference);
323
+ return Boolean(after && after.owner === record.owner);
324
+ }
325
+ export function createWorkflowContinuationSigner(ctx) {
326
+ return {
327
+ async issue(payload, ttlMs = DEFAULT_TTL_MS) {
328
+ const issuedAt = Date.now();
329
+ const ttl = Number.isFinite(ttlMs) ? Math.trunc(ttlMs) : DEFAULT_TTL_MS;
330
+ const record = JSON.stringify({ ...payload, issuedAt, expiresAt: issuedAt + ttl });
331
+ const dir = pendingDir(ctx);
332
+ await mkdir(dir, { recursive: true, mode: 0o700 });
333
+ // `wx` 保证不覆盖已存在的载荷;极小概率撞名时换一个句柄重试。
334
+ for (let attempt = 0; attempt < 5; attempt += 1) {
335
+ const handle = `wc-${randomBytes(8).toString('hex')}`;
336
+ try {
337
+ await writeFile(resolve(dir, handle), record, { flag: 'wx', mode: 0o600 });
338
+ if (await shouldSweep(dir))
339
+ await sweepPending(dir);
340
+ return handle;
341
+ }
342
+ catch (error) {
343
+ if (error?.code !== 'EEXIST') {
344
+ throw workflowValidationError('continuation_unavailable', `本机状态目录不可写,无法托管 continuation:${dir}`);
345
+ }
346
+ }
347
+ }
348
+ throw workflowValidationError('continuation_unavailable', '签发 continuation 失败(句柄连续冲突),请重试');
349
+ },
350
+ async verify(reference, expectedOperation) {
351
+ if (typeof reference !== 'string' || !reference.trim()) {
352
+ throw workflowValidationError('continuation_invalid', 'continuation 无效(缺少入参)');
353
+ }
354
+ // 引用两侧可能被模型带上空白,统一归一化,保证 verify 与 consume 的指纹一致。
355
+ const ref = reference.trim();
356
+ if (ref.startsWith('wc-') && !HANDLE_PATTERN.test(ref)) {
357
+ throw workflowValidationError('continuation_invalid', 'continuation 句柄格式无效:必须原样回传 Prepare 返回的 wc-… 值,禁止手工构造或拼接。');
358
+ }
359
+ // 一次性消费检查放在最前:必须先看消费标记,才能把重放正确判定为「已使用」而不是「不存在」。
360
+ // 标记里带着上一次的执行阶段与结果,于是:已成功/已创建 → 幂等回报(ALREADY_APPLIED);
361
+ // 已发出写入但未确认 → 按"结果不确定"处理;只抢到句柄未发出写入 → 允许继续(由 consume 安全重占)。
362
+ const marker = await readConsumedMarker(ctx, ref);
363
+ if (marker) {
364
+ const replay = replayOf(marker);
365
+ if (replay.state !== 'claimed')
366
+ throw continuationReplayError(replay, expectedOperation);
367
+ }
368
+ const handle = HANDLE_PATTERN.test(ref);
369
+ const value = handle
370
+ ? await readPendingPayload(ctx, ref)
371
+ : await verifyLegacyToken(ctx, ref);
372
+ if (value.operation !== expectedOperation) {
373
+ throw workflowValidationError('continuation_mismatch', 'continuation 不属于当前操作');
374
+ }
375
+ if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
376
+ // 过期句柄的托管载荷**不在这里删**:写路径保持零删除动作(见 SWEEP_THRESHOLD 注释),
377
+ // 过期载荷由 issue 时阈值触发的 sweepPending 统一回收。
378
+ throw workflowValidationError('continuation_expired', 'continuation 已过期');
379
+ }
380
+ return value;
381
+ },
382
+ async consume(reference, operation, ttlMs) {
383
+ if (typeof reference !== 'string' || !reference.trim())
384
+ return { claimed: true };
385
+ const ref = reference.trim();
386
+ const handle = HANDLE_PATTERN.test(ref);
387
+ // 有界幂等窗口:>0 表示调用方按"确定性键 + 保护窗口"使用,窗口过期后允许接管已成功的旧标记。
388
+ const window = Number.isFinite(ttlMs) && Number(ttlMs) > 0 ? Math.trunc(Number(ttlMs)) : 0;
389
+ let expiresAt = Date.now() + (window || DEFAULT_TTL_MS);
390
+ let prepared;
391
+ try {
392
+ const value = handle
393
+ ? JSON.parse(await readFile(resolve(pendingDir(ctx), ref), 'utf8'))
394
+ : JSON.parse(Buffer.from(ref.split('.')[0], 'base64url').toString('utf8'));
395
+ // 有界窗口优先:句柄自带的 expiresAt 是 prepare 载荷的有效期,与"幂等窗口"不是一回事,
396
+ // 因此传了窗口就不再让载荷覆盖(确定性键本来也没有载荷)。
397
+ if (!window && Number.isSafeInteger(value.expiresAt))
398
+ expiresAt = value.expiresAt;
399
+ // 留档"这次准备写什么":既是审计线索,也让"未确认成功"的重放能报出目标范围。
400
+ prepared = value.prepared;
401
+ }
402
+ catch {
403
+ /* 载荷缺失/解析失败:沿用默认 TTL,仍会落一条消费记录防重放 */
404
+ }
405
+ const dir = consumedDir(ctx);
406
+ const file = resolve(dir, referenceFingerprint(ref));
407
+ const record = {
408
+ state: 'running',
409
+ stage: 'claimed',
410
+ ...(operation ? { operation } : {}),
411
+ // owner/pid/hostname 只服务于"上一次死在写入之前"的安全重占判定(见 tryReclaim)。
412
+ owner: randomUUID(),
413
+ pid: process.pid,
414
+ hostname: hostname(),
415
+ consumedAt: Date.now(),
416
+ expiresAt,
417
+ ...(prepared === undefined ? {} : { prepared }),
418
+ };
419
+ try {
420
+ await mkdir(dir, { recursive: true, mode: 0o700 });
421
+ // 抢占判据 = 这次 `wx` 创建本身:抢不到(EEXIST)说明已被另一次执行占用,调用方**不得再写入**。
422
+ await writeFile(file, JSON.stringify(record), { flag: 'wx', mode: 0o600 });
423
+ }
424
+ catch (error) {
425
+ if (error?.code === 'EEXIST') {
426
+ const existing = await readConsumedMarker(ctx, ref);
427
+ if (!existing)
428
+ return { claimed: false, previous: { state: 'unknown' } };
429
+ // 有界窗口:窗口已过且上一次已确认成功 → 本次原子接管,允许第二次**合法**提交。
430
+ if (window && await tryTakeoverExpiredWindow(ctx, ref, existing, record)) {
431
+ if (await shouldSweep(dir))
432
+ await sweepConsumed(dir);
433
+ return { claimed: true };
434
+ }
435
+ const blocker = await tryReclaim(ctx, ref, existing, record);
436
+ if (!blocker)
437
+ return { claimed: true };
438
+ return { claimed: false, previous: blocker };
439
+ }
440
+ // 目录不可写:本机无法留痕,只能沿用旧行为放行(无标记可防重放,属环境限制而非参数问题)。
441
+ }
442
+ // 注意:**这里不再删除托管载荷**。载荷要留到重放判定真的需要它的时候
443
+ // (上一次只抢到句柄、死在写入之前时,允许用同一句柄继续;删了就只能重跑 Prepare)。
444
+ // 清理时机:**只在已消费标记累积超过阈值时**顺手清一次(见 SWEEP_THRESHOLD 注释),
445
+ // 而不是每次写操作都清——日常写路径必须保持零删除动作。
446
+ if (await shouldSweep(dir))
447
+ await sweepConsumed(dir);
448
+ return { claimed: true };
449
+ },
450
+ async markStage(reference, stage, extra) {
451
+ if (typeof reference !== 'string' || !reference.trim())
452
+ return;
453
+ const file = resolve(consumedDir(ctx), referenceFingerprint(reference.trim()));
454
+ let raw;
455
+ try {
456
+ raw = await readFile(file, 'utf8');
457
+ }
458
+ catch {
459
+ // 本机没有标记(状态目录不可写等环境限制):不阻断,沿用"无痕迹"的旧行为。
460
+ return;
461
+ }
462
+ let marker;
463
+ try {
464
+ marker = JSON.parse(raw);
465
+ }
466
+ catch {
467
+ // 旧版纯数字标记:没有可写阶段的位置,跳过。
468
+ return;
469
+ }
470
+ if (!marker || typeof marker !== 'object' || Array.isArray(marker))
471
+ return;
472
+ try {
473
+ await writeFile(file, JSON.stringify({ ...marker, stage, ...(extra ?? {}) }), { mode: 0o600 });
474
+ }
475
+ catch {
476
+ // 有标记却写不进阶段 ⇒ 记录不了进度就不能发出写入请求(否则会留下无法判定的一次性写入)。
477
+ throw workflowValidationError('continuation_unavailable', '本机无法记录本次写入的进度(状态目录不可写),为避免留下无法判定结果的一次性写入,已停止本次操作。');
478
+ }
479
+ },
480
+ async settle(reference, result, operation) {
481
+ if (typeof reference !== 'string' || !reference.trim())
482
+ return;
483
+ const ref = reference.trim();
484
+ const file = resolve(consumedDir(ctx), referenceFingerprint(ref));
485
+ try {
486
+ const raw = await readFile(file, 'utf8');
487
+ const marker = JSON.parse(raw);
488
+ // 旧版标记(纯数字)没有承载执行结果的位置,保持原样不动。
489
+ if (marker && typeof marker === 'object' && !Array.isArray(marker)) {
490
+ await writeFile(file, JSON.stringify({
491
+ ...marker,
492
+ state: 'succeeded',
493
+ ...(operation ? { operation } : {}),
494
+ finishedAt: Date.now(),
495
+ result: result ?? null,
496
+ }), { mode: 0o600 });
497
+ }
498
+ }
499
+ catch {
500
+ /* 回写失败不影响命令结果:重放只会退化成「未确认」提示 */
501
+ }
502
+ // 写入已确认成功后**不再删除托管载荷**(V11.3 对齐:写路径零删除动作)。
503
+ // 原因:删除会让宿主沙箱判定该命令要删文件 → 弹提权确认 → 命令无沙箱重跑一次。
504
+ // 载荷保留无害:消费标记里本就存着同一份 prepared 载荷,且 verify 在读到消费标记后
505
+ // 即短路(不会回读 pending);过期载荷由 issue 时阈值触发的 sweepPending 统一回收。
506
+ },
507
+ };
508
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * E10 工作流接口路径表。
3
+ *
4
+ * 来源:原 Python skill 的 `scripts/e10_workflow.py`(`DEFAULT_ENDPOINTS`)与
5
+ * `scripts/e10_workflow_create.py`(`ENDPOINTS`)。只保留 CLI 侧真正需要调用的路径;
6
+ * Python 内部用于调试的裸接口能力(raw-post / raw-get)不迁移。
7
+ */
8
+ /** 流程查询与操作(原 e10_workflow.py DEFAULT_ENDPOINTS) */
9
+ export const WORKFLOW_ENDPOINTS = {
10
+ /** 记忆:读取用户记忆提示词 */
11
+ MEMORY_PROMPT: '/api/ai/application/memory/buildAgentMemoryPrompt',
12
+ /** 记忆:保存/抽取用户记忆 */
13
+ MEMORY_EXTRACT: '/api/ai/application/memory/agentMemoryExtract',
14
+ /** 流程路径浏览按钮:按关键字解析工作流 */
15
+ WORKFLOW_PATH_BROWSER: '/api/workflow/pathdef/common/browser/complete/workflowPathBrowser',
16
+ /** 流程大类浏览按钮:解析大分类 / tabid */
17
+ WORKFLOW_TYPE_BROWSER: '/api/workflow/pathdef/common/browser/complete/workflowPathTypeBrowser',
18
+ /** 资源浏览按钮(人员/角色/群组等) */
19
+ RESOURCE_BROWSER: '/api/workflow/core/common/browser/complete/resource',
20
+ /** 部门浏览按钮 */
21
+ DEPT_BROWSER: '/api/hrm/common/browser/complete/department',
22
+ /** 分部浏览按钮 */
23
+ SUBCOMPANY_BROWSER: '/api/hrm/common/browser/complete/subcompany',
24
+ /** 群组浏览按钮(共享对象 group;来源:Python EMPLOYEE_SCOPE_BROWSER_TYPES group→channel) */
25
+ GROUP_BROWSER: '/api/hrm/common/browser/complete/channel',
26
+ /** 角色浏览按钮(共享对象 role) */
27
+ ROLE_BROWSER: '/api/hrm/common/browser/complete/role',
28
+ /** 岗位浏览按钮(共享对象 position) */
29
+ POSITION_BROWSER: '/api/hrm/common/browser/complete/position',
30
+ /** 人员浏览按钮(共享对象 user;Python EmployeeScope module=hrm) */
31
+ HRM_RESOURCE_BROWSER: '/api/hrm/common/browser/complete/resource',
32
+ /** 节点浏览按钮 */
33
+ NODE_BROWSER: '/api/workflow/pathdef/common/browser/data/workflowPathNodeBrowser',
34
+ /** 字段选项浏览按钮 */
35
+ FIELD_OPTION_BROWSER: '/api/workflow/core/common/browser/complete/field_option',
36
+ /** 流程列表查询(带表单字段) */
37
+ SEARCH_AD_WITH_FORM: '/api/workflow/list/searchad/getListSearchAdWithForm',
38
+ /** 查询动态依赖数据(紧急程度等) */
39
+ QUERY_DEPENDENCY: '/api/workflow/list/smartQuery/getQueryDependency',
40
+ /** 名称转 ID(系统查询字段 / 浏览按钮字段) */
41
+ RELATE_NAME2ID: '/api/workflow/list/smartQuery/relateName2Id',
42
+ /** 待办总数 / 列表 */
43
+ TODO_TOTAL: '/api/workflow/list/total/getTodoListTotal',
44
+ TODO_LIST: '/api/workflow/list/data/getTodoListData',
45
+ /** 待办分组计数(按工作流/参与节点/参与身份三元组) */
46
+ TODO_GROUP_STAT: '/api/workflow/list/count/getTodoGroupCount',
47
+ /** 已办总数 / 列表 */
48
+ DONE_TOTAL: '/api/workflow/list/total/getDoneListTotal',
49
+ DONE_LIST: '/api/workflow/list/data/getDoneListData',
50
+ /** 我发起总数 / 列表 */
51
+ MINE_TOTAL: '/api/workflow/list/total/getMineListTotal',
52
+ MINE_LIST: '/api/workflow/list/data/getMineListData',
53
+ /** 全部总数 / 列表 */
54
+ ALL_TOTAL: '/api/workflow/list/total/getAllListTotal',
55
+ ALL_LIST: '/api/workflow/list/data/getAllListData',
56
+ /** 批量操作专用查询(可执行批量提交/催办/置已读等的待办数据) */
57
+ TODO_TOTAL_BATCH_SUBMIT: '/api/workflow/list/total/getTodoListTotal/batch/batchSubmit',
58
+ TODO_LIST_BATCH_SUBMIT: '/api/workflow/list/data/getTodoListData/batch/batchSubmit',
59
+ TODO_TOTAL_BATCH_URGE: '/api/workflow/list/total/getTodoListTotal/batch/batchUrge',
60
+ TODO_LIST_BATCH_URGE: '/api/workflow/list/data/getTodoListData/batch/batchUrge',
61
+ TODO_TOTAL_BATCH_READ: '/api/workflow/list/total/getTodoListTotal/batch/batchRead',
62
+ TODO_LIST_BATCH_READ: '/api/workflow/list/data/getTodoListData/batch/batchRead',
63
+ TODO_TOTAL_BATCH_SHARE: '/api/workflow/list/total/getTodoListTotal/batch/batchShare',
64
+ TODO_LIST_BATCH_SHARE: '/api/workflow/list/data/getTodoListData/batch/batchShare',
65
+ TODO_TOTAL_BATCH_FORWARD: '/api/workflow/list/total/getTodoListTotal/batch/batchForward',
66
+ TODO_LIST_BATCH_FORWARD: '/api/workflow/list/data/getTodoListData/batch/batchForward',
67
+ TODO_TOTAL_BATCH_REJECT: '/api/workflow/list/total/getTodoListTotal/batch/batchReject',
68
+ TODO_LIST_BATCH_REJECT: '/api/workflow/list/data/getTodoListData/batch/batchReject',
69
+ /** 流程详情 / 流转日志 */
70
+ REQUEST_DETAIL: '/api/workflow/core/paService/v1/getWorkflowRequest',
71
+ REQUEST_LOG: '/api/workflow/core/paService/v1/getRequestLog',
72
+ /** 单条流程操作 */
73
+ SUBMIT_REQUEST: '/api/workflow/core/paService/v1/submitRequest',
74
+ REJECT_REQUEST: '/api/workflow/core/paService/v1/rejectRequest',
75
+ TRANSFER_REQUEST: '/api/workflow/core/paService/v1/doTurnTodo',
76
+ DELEGATE_REQUEST: '/api/workflow/core/paService/v1/manualAgentRequest',
77
+ FORWARD_REQUEST: '/api/workflow/core/paService/v1/forward',
78
+ CC_REQUEST: '/api/workflow/core/paService/v1/manualCC',
79
+ ADD_REVIEW_REQUEST: '/api/workflow/core/paService/v1/addSignOperator',
80
+ BATCH_SUBMIT: '/api/workflow/core/paService/v1/batchSubmit',
81
+ FORCE_DRAW_BACK: '/api/workflow/core/paService/v1/doForceDrawBack',
82
+ FORCE_OVER: '/api/workflow/core/paService/v1/doForceOver',
83
+ CONSUL: '/api/workflow/core/paService/v1/consul',
84
+ CONSUL_REPLY: '/api/workflow/core/paService/v1/consulReply',
85
+ DELETE_REQUEST: '/api/workflow/core/paService/v1/deleteRequest',
86
+ SUPERVISE: '/api/workflow/core/paService/v1/requestSupervise',
87
+ BATCH_SUPERVISE: '/api/workflow/core/flow/batchSupervise',
88
+ BATCH_READ: '/api/workflow/list/operate/doBatchRead',
89
+ /** 流程共享 */
90
+ SHARE_ADD_FORM: '/api/workflow/list/share/getAddShareForm',
91
+ SHARE_ADD: '/api/workflow/list/share/addRequestShare2',
92
+ /** 批量转发 */
93
+ BATCH_FORWARD: '/api/workflow/core/flow/operate/batchForward',
94
+ FORWARD_ALLOW_ALL: '/api/workflow/core/flow/operate/getReceiverAllowAllConfig',
95
+ /** 批量退回 */
96
+ BATCH_REJECT: '/api/workflow/core/flow/operate/batchReject',
97
+ /** 流程节点符合性校验(发起前字段校验) */
98
+ CONFORMANCE_SET: '/api/bs/workflow/pathdef/nodeConformance/getConformanceSetByVerification',
99
+ VERIFICATION_REQUEST_DATA: '/api/workflow/core/verification/getRequestDataByRequestId',
100
+ VERIFICATION_CONDITION_RULE: '/api/bs/workflow/pathdef/nodeConformance/verificationConditionRule',
101
+ };
102
+ /** 表单 / 发起流程(原 e10_workflow_create.py ENDPOINTS) */
103
+ export const FORM_ENDPOINTS = {
104
+ /** 可发起工作流列表 */
105
+ workflowCreateList: '/api/workflow/list/newflow/getNewFlowListData',
106
+ /** 发起流程:基础参数 */
107
+ loadBase: '/api/workflow/core/flowPage/loadBaseParam',
108
+ /** 发起流程:页面框架参数 */
109
+ loadFrame: '/api/workflow/core/flowPage/loadFrameParam',
110
+ /** 发起流程:次要信息(安全级别默认值等) */
111
+ loadMinor: '/api/workflow/core/flowPage/loadMinorInfo',
112
+ /** 表单布局 */
113
+ formLayout: '/api/workflow/core/form/core/getFormLayout',
114
+ /** 表单数据 + 布局(修改已存在流程时用) */
115
+ formDataAndLayout: '/api/workflow/core/form/core/getFormDataAndLayout',
116
+ /** 名称转 ID(AI 字段映射) */
117
+ nameToId: '/api/ai/chat/fieldMapping/getRelateData',
118
+ /** 字段联动 */
119
+ fdLinkage: '/api/workflow/core/form/linkage/executeFdLinkage',
120
+ /** 保存表单数据 */
121
+ save: '/api/workflow/core/form/core/saveFormData',
122
+ /** 发起流程 */
123
+ create: '/api/workflow/core/flow/create',
124
+ /** 提交(复用查询侧同一路径) */
125
+ submit: '/api/workflow/core/paService/v1/submitRequest',
126
+ /** 节点符合性校验 */
127
+ conformanceSet: '/api/bs/workflow/pathdef/nodeConformance/getConformanceSetByVerification',
128
+ verificationRequestData: '/api/workflow/core/verification/getRequestDataByRequestId',
129
+ verificationConditionRule: '/api/bs/workflow/pathdef/nodeConformance/verificationConditionRule',
130
+ /** 租户级文件上传设置(单文件大小上限 + 格式黑白名单) */
131
+ tenantSetting: '/api/file/tenant/setting',
132
+ };
133
+ /** 表单接口公共 query 参数(原 AJAX_QUERY) */
134
+ export const AJAX_QUERY = { sf_request_type: 'ajax' };
135
+ /**
136
+ * 表单接口路径:按 relateType 切换 module 前缀。
137
+ *
138
+ * `path` 必须是**完整路径**(传 `FORM_ENDPOINTS.xxx`);传短 key 会因匹配不到
139
+ * `/api/workflow/core/` 而原样返回,导致请求路径非法。
140
+ *
141
+ * - WEA_FORM(工作流表单)→ 保持 `FORM_ENDPOINTS` 默认地址
142
+ * - WEA_EBUILDER(建模表单)→ `/api/workflow/core/` 段替换为 `/api/ebuilder/flow/`
143
+ */
144
+ export function formApiUrl(path, relateType) {
145
+ if (String(relateType || '').trim() === 'WEA_EBUILDER') {
146
+ return path.replace('/api/workflow/core/', '/api/ebuilder/flow/');
147
+ }
148
+ return path;
149
+ }