weaver-work-cli 0.1.23 → 0.1.24
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 +23 -2
- package/dist/internal/e10/auth/commands.js +194 -16
- package/dist/internal/e10/auth/xiaoe.js +15 -6
- package/dist/internal/e10/context.js +3 -3
- package/dist/internal/e10/request-runtime.js +46 -4
- package/dist/shortcuts/caizhilian/file-upload/metadata.js +124 -0
- package/dist/shortcuts/caizhilian/file-upload/multipart.js +47 -0
- package/dist/shortcuts/caizhilian/file-upload/protocol.js +116 -0
- package/dist/shortcuts/caizhilian/file-upload/receipt.js +37 -0
- package/dist/shortcuts/caizhilian/host.js +13 -5
- package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +72 -179
- package/dist/shortcuts/caizhilian/operations/handlers/folder.js +701 -4
- package/dist/shortcuts/caizhilian/operations/registry.js +5 -1
- package/dist/shortcuts/caizhilian/operations/specs.js +52 -0
- package/dist/shortcuts/workflow/continuation.js +14 -9
- package/dist/shortcuts/workflow/endpoints.js +11 -1
- package/dist/shortcuts/workflow/operations/flow-status.js +63 -0
- package/dist/shortcuts/workflow/operations/form.js +650 -518
- package/dist/shortcuts/workflow/operations/read.js +374 -13
- package/dist/shortcuts/workflow/operations/shared.js +3 -0
- package/dist/shortcuts/workflow/operations/write.js +5 -5
- package/dist/shortcuts/workflow/operations.js +8 -6
- package/docs/caizhilian.md +8 -2
- package/docs/e10-auth.md +18 -3
- package/package.json +1 -1
- package/skills/weaver-e10-caizhilian/SKILL.md +8 -3
- package/skills/weaver-e10-caizhilian/references/file-upload.md +100 -295
- package/skills/weaver-e10-caizhilian/references/folder.md +31 -3
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +536 -25
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +9 -1
- package/skills/weaver-e10-shared/SKILL.md +12 -2
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +21 -3
- package/skills/weaver-e10-workflow/SKILL.md +10 -8
- package/skills/weaver-e10-workflow/references/source-manifest.json +36 -31
- package/skills/weaver-e10-workflow/references/workflow-batch.md +2 -4
- package/skills/weaver-e10-workflow/references/workflow-create.md +58 -36
- package/skills/weaver-e10-workflow/references/workflow-edit.md +2 -1
- package/skills/weaver-e10-workflow/references/workflow-memory.md +2 -2
- package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -1
- package/skills/weaver-e10-workflow/references/workflow-query.md +9 -7
- package/skills/weaver-e10-workflow/references/workflow-share.md +2 -0
|
@@ -65,7 +65,10 @@ function prepareDefinition(spec, entry) {
|
|
|
65
65
|
summary: `${spec.summary}|prepare:只读校验与请求体预览,签发 apply 所需的 continuation,不产生副作用`,
|
|
66
66
|
risk: 'read-before-write',
|
|
67
67
|
requiresConfirmation: true,
|
|
68
|
-
handler: (args, env) =>
|
|
68
|
+
handler: (args, env) => {
|
|
69
|
+
entry.validateInput?.(args);
|
|
70
|
+
return entry.prepare(args, env);
|
|
71
|
+
},
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
@@ -83,6 +86,7 @@ function applyDefinition(spec, entry) {
|
|
|
83
86
|
requiresConfirmation: true,
|
|
84
87
|
inputSchema: applySchemaOf(spec.inputSchema, base.required ?? []),
|
|
85
88
|
handler: async (args, env) => {
|
|
89
|
+
entry.validateInput?.(args);
|
|
86
90
|
const lease = await verifyLease(args, name, env);
|
|
87
91
|
assertLeaseArgs(lease, args, name);
|
|
88
92
|
assertLeaseTarget(lease, args, entry.targetKeys ?? [], name);
|
|
@@ -271,6 +271,8 @@ export const caizhilianOperationSpecs = [
|
|
|
271
271
|
position_attribute: LIST('岗位属性'),
|
|
272
272
|
position_level_department: LIST('岗位层级-部门'),
|
|
273
273
|
position_level_subcompany: LIST('岗位层级-分部'),
|
|
274
|
+
seclevel_min: INT('密级下限,范围 -999~999', { minimum: -999, maximum: 999 }),
|
|
275
|
+
seclevel_max: INT('密级上限,范围 -999~999', { minimum: -999, maximum: 999 }),
|
|
274
276
|
}, ['folder_id', 'permission_type', 'obj_type'], { requiresConfirmation: true }),
|
|
275
277
|
spec('folder', 'permission.delete', 'delete_folder_permission', 'high-risk-write', '删除文件夹文档权限设置', {
|
|
276
278
|
folder_id: ID('文件夹 ID'),
|
|
@@ -309,6 +311,50 @@ export const caizhilianOperationSpecs = [
|
|
|
309
311
|
spec('folder', 'permission.sync-setting', 'sync_folder_setting_permission', 'high-risk-write', '将当前文件夹的"管理员"权限设置同步到下级子文件夹', {
|
|
310
312
|
id: ID('文件夹 ID'),
|
|
311
313
|
keepOldSettingForAllFolders: BOOL('是否保留原有管理员设置(true=保留,false=不保留)'),
|
|
314
|
+
}, ['id', 'keepOldSettingForAllFolders'], { requiresConfirmation: true }),
|
|
315
|
+
spec('folder', 'permission.sync-maintain', 'sync_folder_maintain_permission', 'high-risk-write', '将当前文件夹的"文件夹维护"权限设置同步到下级子文件夹', {
|
|
316
|
+
id: ID('文件夹 ID'),
|
|
317
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有文件夹维护设置(true=保留,false=不保留)'),
|
|
318
|
+
}, ['id', 'keepOldSettingForAllFolders'], { requiresConfirmation: true }),
|
|
319
|
+
spec('folder', 'permission.sync-download', 'sync_folder_download_permission', 'high-risk-write', '将当前文件夹的"下载权限"设置同步到下级子文件夹', {
|
|
320
|
+
id: ID('文件夹 ID'),
|
|
321
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有下载权限设置(true=保留,false=不保留)'),
|
|
322
|
+
}, ['id'], { requiresConfirmation: true }),
|
|
323
|
+
spec('folder', 'permission.sync-delete', 'sync_folder_delete_permission', 'high-risk-write', '将当前文件夹的"删除权限"设置同步到下级子文件夹', {
|
|
324
|
+
id: ID('文件夹 ID'),
|
|
325
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有删除权限设置(true=保留,false=不保留)'),
|
|
326
|
+
}, ['id'], { requiresConfirmation: true }),
|
|
327
|
+
spec('folder', 'permission.sync-copy', 'sync_folder_copy_permission', 'high-risk-write', '将当前文件夹的"复制权限"设置同步到下级子文件夹', {
|
|
328
|
+
id: ID('文件夹 ID'),
|
|
329
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有复制权限设置(true=保留,false=不保留)'),
|
|
330
|
+
}, ['id', 'keepOldSettingForAllFolders'], { requiresConfirmation: true }),
|
|
331
|
+
spec('folder', 'permission.sync-create', 'sync_folder_create_permission', 'high-risk-write', '将当前文件夹的"新建文档权限"设置同步到下级子文件夹', {
|
|
332
|
+
id: ID('文件夹 ID'),
|
|
333
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有新建文档权限设置(true=保留,false=不保留)'),
|
|
334
|
+
}, ['id', 'keepOldSettingForAllFolders'], { requiresConfirmation: true }),
|
|
335
|
+
spec('folder', 'permission.sync-move', 'sync_folder_move_permission', 'high-risk-write', '将当前文件夹的"移动权限"设置同步到下级子文件夹', {
|
|
336
|
+
id: ID('文件夹 ID'),
|
|
337
|
+
keepOldSettingForAllFolders: BOOL('是否保留原有移动权限设置(true=保留,false=不保留)'),
|
|
338
|
+
}, ['id', 'keepOldSettingForAllFolders'], { requiresConfirmation: true }),
|
|
339
|
+
spec('folder', 'permission.sync-participant', 'sync_folder_participant', 'high-risk-write', '将当前文件夹的"默认参与人"设置同步到下级子文件夹或历史文档', {
|
|
340
|
+
id: ID('文件夹 ID'),
|
|
341
|
+
sync_to_folder_docs: BOOL('是否同步到当前文件夹内的历史文档'),
|
|
342
|
+
sync_to_all_folders: BOOL('是否同步到所有下级文件夹'),
|
|
343
|
+
sync_to_all_sub_folder_docs: BOOL('是否同步到所有下级文件夹内的历史文档'),
|
|
344
|
+
keep_old_for_folder_docs: BOOL('原有参与人是否保留(同步到历史文档时)', { default: true }),
|
|
345
|
+
keep_old_for_all_folders: BOOL('原有参与人是否保留(同步到下级文件夹时)', { default: true }),
|
|
346
|
+
keep_old_for_all_sub_folder_docs: BOOL('原有参与人是否保留(同步到下级文件夹历史文档时)', { default: true }),
|
|
347
|
+
}, ['id'], { requiresConfirmation: true }),
|
|
348
|
+
spec('folder', 'permission.sync-default-share', 'sync_folder_default_share', 'high-risk-write', '将当前文件夹的"默认共享人"设置同步到下级子文件夹或历史文档', {
|
|
349
|
+
id: ID('文件夹 ID'),
|
|
350
|
+
sync_to_folder_docs: BOOL('是否同步到当前文件夹内的历史文档'),
|
|
351
|
+
sync_to_all_folders: BOOL('是否同步到所有下级文件夹'),
|
|
352
|
+
sync_to_all_sub_folder_docs: BOOL('是否同步到所有下级文件夹内的历史文档'),
|
|
353
|
+
keep_old_default_share_for_folder_docs: BOOL('原有默认共享人是否保留(同步到历史文档时)', { default: true }),
|
|
354
|
+
keep_old_non_default_share_for_folder_docs: BOOL('原有非默认共享人是否保留(同步到历史文档时)', { default: true }),
|
|
355
|
+
keep_old_default_share_for_all_folders: BOOL('原有默认共享人是否保留(同步到下级文件夹时)', { default: true }),
|
|
356
|
+
keep_old_default_share_for_sub_folder_docs: BOOL('原有默认共享人是否保留(同步到下级文件夹历史文档时)', { default: true }),
|
|
357
|
+
keep_old_non_default_share_for_sub_folder_docs: BOOL('原有非默认共享人是否保留(同步到下级文件夹历史文档时)', { default: true }),
|
|
312
358
|
}, ['id'], { requiresConfirmation: true }),
|
|
313
359
|
// ── setting-attach ──────────────────────────────────────
|
|
314
360
|
spec('setting-attach', 'get', 'get_setting_attach', 'read', '读取附件设置(文件设置 → 附件设置)', {}, [], {
|
|
@@ -544,11 +590,17 @@ export const caizhilianOperationSpecs = [
|
|
|
544
590
|
spec('file-upload', 'prepare', 'prepare_upload', 'read', '准备上传(本地文件校验与元数据)', {
|
|
545
591
|
file_path: FILE('本地文件绝对路径'),
|
|
546
592
|
module: S('业务模块(如 document、workflow 等)'),
|
|
593
|
+
context_file: FILE('可选业务上下文 JSON 文件绝对路径(最大 64 KiB)'),
|
|
594
|
+
chat_id: S('宿主对话标识,用于隔离上传回执;默认 cli'),
|
|
595
|
+
timeout_seconds: INT('上传超时秒数(1–3600,默认 300)', { minimum: 1, maximum: 3600, default: 300 }),
|
|
547
596
|
}, ['file_path', 'module']),
|
|
548
597
|
spec('file-upload', 'upload', 'upload_file', 'high-risk-write', '上传文件到文件服务', {
|
|
549
598
|
file_path: FILE('本地文件绝对路径'),
|
|
550
599
|
module: S('业务模块(如 document、workflow 等)'),
|
|
551
600
|
user_confirmed: BOOL('用户已确认上传'),
|
|
601
|
+
context_file: FILE('可选业务上下文 JSON 文件绝对路径(最大 64 KiB)'),
|
|
602
|
+
chat_id: S('宿主对话标识,用于隔离上传回执;默认 cli'),
|
|
603
|
+
timeout_seconds: INT('上传超时秒数(1–3600,默认 300)', { minimum: 1, maximum: 3600, default: 300 }),
|
|
552
604
|
}, ['file_path', 'module', 'user_confirmed'], { requiresConfirmation: true }),
|
|
553
605
|
// ── org ─────────────────────────────────────────────────
|
|
554
606
|
spec('org', 'search-user', 'search_user', 'read', '人员浏览器检索(源 search_user → /api/hrm/common/browser/data/resource)', {
|
|
@@ -189,15 +189,20 @@ export function continuationReplayError(replay, expectedOperation) {
|
|
|
189
189
|
return workflowAlreadyAppliedError(operation, replay);
|
|
190
190
|
}
|
|
191
191
|
if (replay.state === 'saving') {
|
|
192
|
-
// 写入请求已发出但结果未知:一律按"结果不确定"
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
192
|
+
// 写入请求已发出但结果未知:一律按"结果不确定"处理,**本次不执行、也不重放**。
|
|
193
|
+
//
|
|
194
|
+
// 恢复路径按**句柄形态**分述,且**不再出现"等一会儿原样重跑"**——`createForm` 签发的
|
|
195
|
+
// `wc-` 句柄是没有幂等窗口的(`consume` 不传 `ttlMs`,一次性且永久),"等约 1 分钟再跑"
|
|
196
|
+
// 对它是结构性无效的死路(2026-09-28 用户裁定改写)。批量写侧虽有 60s 确定性键窗口,
|
|
197
|
+
// 但同样不该由模型自动重跑。
|
|
198
|
+
const recovery = operation === 'workflow.createForm'
|
|
199
|
+
? '要重新发起,请**重新执行 workflow.createForm 获取新句柄**(本次句柄已消费,重发同一句柄无效)。'
|
|
200
|
+
: /Apply$/u.test(operation)
|
|
201
|
+
? '确认未生效后,重跑对应的 Prepare 获取新句柄。'
|
|
202
|
+
: '该命令没有可重跑的 Prepare,也**不要原样重跑**;请把结果如实告知用户。';
|
|
203
|
+
return workflowPartialError('该 continuation 对应的上一次执行**已经发出写入请求,但结果未确认**(进程被中止或回执丢失),本次未执行。'
|
|
204
|
+
+ '**请把"上一次结果未确认"如实告知用户并停下,不要自行继续**'
|
|
205
|
+
+ '(不要重试、不要改用其它命令反复推断、不要替用户下结论)。' + recovery, replay, { stage: 'continuation_replay', ...replay });
|
|
201
206
|
}
|
|
202
207
|
if (replay.state === 'claimed') {
|
|
203
208
|
// 只抢到句柄、未发出任何写入:并发的另一次执行正在占用,等它结束再重试即可。
|
|
@@ -124,10 +124,20 @@ export const FORM_ENDPOINTS = {
|
|
|
124
124
|
create: '/api/workflow/core/flow/create',
|
|
125
125
|
/** 提交(复用查询侧同一路径) */
|
|
126
126
|
submit: '/api/workflow/core/paService/v1/submitRequest',
|
|
127
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* 节点字段校验的**第一跳**:轻量预检。
|
|
129
|
+
*
|
|
130
|
+
* 服务端只回一个布尔(是否需要把校验结果弹给用户),据此决定要不要再去取详细规则集
|
|
131
|
+
* (`conformanceSet` + `verificationConditionRule`)。开关 `passPopUpPrompt`/`noPassPrompt`
|
|
132
|
+
* 都为真时前端**不调**本接口,直接取详细规则集(2026-09-28 双 HAR 实证)。
|
|
133
|
+
*/
|
|
134
|
+
verifyNodeFieldByList: '/api/workflow/core/verification/verifyNodeFieldByList',
|
|
135
|
+
/** 节点符合性校验(详细规则集,仅在需要展示校验结果时才取) */
|
|
128
136
|
conformanceSet: '/api/bs/workflow/pathdef/nodeConformance/getConformanceSetByVerification',
|
|
129
137
|
verificationRequestData: '/api/workflow/core/verification/getRequestDataByRequestId',
|
|
130
138
|
verificationConditionRule: '/api/bs/workflow/pathdef/nodeConformance/verificationConditionRule',
|
|
139
|
+
/** 提交已创建的流程(创建后页面重载拿到 view 页鉴权再提交,与真机同路径) */
|
|
140
|
+
flowSubmit: '/api/workflow/core/flow/submit',
|
|
131
141
|
/** 租户级文件上传设置(单文件大小上限 + 格式黑白名单) */
|
|
132
142
|
tenantSetting: '/api/file/tenant/setting',
|
|
133
143
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 流程状态(`flowstatus`)系统条件。
|
|
3
|
+
*
|
|
4
|
+
* ★ `flowstatus` 是**选项类**系统条件,服务端只认 `[{term, multiContent:[<代码>]}]`
|
|
5
|
+
* —— **不是** `[{content, term}]`。`content` 形态是给"文本类"条件(requestname /
|
|
6
|
+
* requestmark)用的;把取值塞进 `content`,服务端会**静默忽略**该条件
|
|
7
|
+
* ⇒ 按流程状态筛选恒不生效、退化成全量。
|
|
8
|
+
*
|
|
9
|
+
* 来源对照:V11.3 `e10_workflow.py:8519 flow_status_condition` 产出的就是
|
|
10
|
+
* `[{"term": "in", "multiContent": [...]}]`;V12.1 移植时退化成了 `[{content, term}]`
|
|
11
|
+
* (2026-09-28 用户实测预期结构为 `[{"term":"in","multiContent":["notFinish","finish"]}]`)。
|
|
12
|
+
*
|
|
13
|
+
* ★★ 取值口径(2026-09-28 用户裁定=方案 C):**只认四个选项代码,不做中文口语词识别**。
|
|
14
|
+
* 说明层(schema 描述 / 报错文案 / `workflow-query.md` 的筛选条件表)已经列出四个代码及其
|
|
15
|
+
* 含义,"用户口语 → 代码"的翻译由**模型**完成;CLI 只做代码的校验与纯归一,且:
|
|
16
|
+
* - 出现任何一个非代码取值 ⇒ **整体报错并停止**,不静默丢弃——丢掉一个取值就少一层筛选,
|
|
17
|
+
* 等于把结果静默放大到全量,与本项目"禁止静默丢条件"的纪律相悖;
|
|
18
|
+
* - 大小写不敏感(`Finish` → `finish`)属纯归一,不做任何语义猜测。
|
|
19
|
+
*/
|
|
20
|
+
/** 服务端认可的流程状态选项(唯一定义处,说明文案由它派生,避免两处口径漂移)。 */
|
|
21
|
+
const FLOW_STATUS_OPTIONS = [
|
|
22
|
+
{ code: 'finish', meaning: '已结束' },
|
|
23
|
+
{ code: 'normalFinish', meaning: '正常结束' },
|
|
24
|
+
{ code: 'forceFinish', meaning: '强制结束' },
|
|
25
|
+
{ code: 'notFinish', meaning: '未结束·审批中' },
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* 模型可见的取值说明(schema 描述与报错文案共用同一份,避免两处口径漂移)。
|
|
29
|
+
*
|
|
30
|
+
* ★ **代码在前、含义在后**(`finish(已结束)`):与本条契约"传的就是这个 key"一致,
|
|
31
|
+
* 也和 `workflow-query.md` 筛选条件表的写法逐字对齐(2026-09-28 用户要求统一)。
|
|
32
|
+
*/
|
|
33
|
+
export const FLOW_STATUS_HINT = `${FLOW_STATUS_OPTIONS.map((item) => `${item.code}(${item.meaning})`).join('、')}`
|
|
34
|
+
+ ';只接受上述四个代码、不接受中文,可逗号分隔多值';
|
|
35
|
+
/**
|
|
36
|
+
* `--flow-status` 取值 → `formDatas.flowstatus` 条件项。
|
|
37
|
+
*
|
|
38
|
+
* 返回 `null` 仅代表**输入为空**(调用方不设该条件)。取值非法一律通过 `invalid` 上报,
|
|
39
|
+
* 由调用方报错——**绝不静默丢弃条件**(丢弃会退化成全量查询)。
|
|
40
|
+
*/
|
|
41
|
+
export function flowStatusCondition(value, term) {
|
|
42
|
+
const text = String(value ?? '').trim();
|
|
43
|
+
if (!text)
|
|
44
|
+
return null;
|
|
45
|
+
const byLowerCode = new Map(FLOW_STATUS_OPTIONS.map((item) => [item.code.toLowerCase(), item.code]));
|
|
46
|
+
const codes = [];
|
|
47
|
+
const invalid = [];
|
|
48
|
+
for (const partRaw of text.split(/[,,、]/u)) {
|
|
49
|
+
const part = partRaw.trim();
|
|
50
|
+
if (!part)
|
|
51
|
+
continue;
|
|
52
|
+
const code = byLowerCode.get(part.toLowerCase());
|
|
53
|
+
if (code)
|
|
54
|
+
codes.push(code);
|
|
55
|
+
else
|
|
56
|
+
invalid.push(part);
|
|
57
|
+
}
|
|
58
|
+
if (invalid.length || !codes.length) {
|
|
59
|
+
return { condition: [], invalid: invalid.length ? invalid : [text] };
|
|
60
|
+
}
|
|
61
|
+
const normalizedTerm = String(term ?? '').trim().toLowerCase() === 'notin' ? 'notin' : 'in';
|
|
62
|
+
return { condition: [{ term: normalizedTerm, multiContent: [...new Set(codes)] }], invalid: [] };
|
|
63
|
+
}
|