weaver-work-cli 0.1.25 → 0.1.28

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 (173) hide show
  1. package/README.md +35 -30
  2. package/dist/cmd/root.js +1 -0
  3. package/dist/cmd/setup/index.js +1 -1
  4. package/dist/core/runtime-agent.js +19 -0
  5. package/dist/internal/e10/auth/commands.js +48 -33
  6. package/dist/internal/e10/auth/messages.js +3 -0
  7. package/dist/internal/e10/auth/session.js +8 -0
  8. package/dist/internal/e10/auth/xiaoe.js +62 -43
  9. package/dist/internal/e10/auth-recovery.js +148 -0
  10. package/dist/internal/e10/client.js +47 -15
  11. package/dist/internal/e10/context.js +9 -31
  12. package/dist/internal/e10/index.js +1 -0
  13. package/dist/internal/e10/request-runtime.js +51 -9
  14. package/dist/shortcuts/archive/host.js +3 -2
  15. package/dist/shortcuts/archive/manifest.js +1 -1
  16. package/dist/shortcuts/archive-oa/constants.js +66 -0
  17. package/dist/shortcuts/archive-oa/continuation.js +70 -0
  18. package/dist/shortcuts/archive-oa/errors.js +53 -0
  19. package/dist/shortcuts/archive-oa/host.js +158 -0
  20. package/dist/shortcuts/archive-oa/index.js +225 -0
  21. package/dist/shortcuts/archive-oa/manifest.js +62 -0
  22. package/dist/shortcuts/archive-oa/operations/borrow-car.js +159 -0
  23. package/dist/shortcuts/archive-oa/operations/borrow-url.js +108 -0
  24. package/dist/shortcuts/archive-oa/operations/confirm.js +48 -0
  25. package/dist/shortcuts/archive-oa/operations/contract.js +120 -0
  26. package/dist/shortcuts/archive-oa/operations/locate.js +50 -0
  27. package/dist/shortcuts/archive-oa/operations/registry.js +40 -0
  28. package/dist/shortcuts/archive-oa/operations/shared.js +181 -0
  29. package/dist/shortcuts/archive-oa/operations/types.js +45 -0
  30. package/dist/shortcuts/archive-oa/operations.js +13 -0
  31. package/dist/shortcuts/asset/host.js +7 -6
  32. package/dist/shortcuts/blog/host.js +4 -3
  33. package/dist/shortcuts/caizhilian/host.js +3 -2
  34. package/dist/shortcuts/caizhilian/manifest.js +1 -1
  35. package/dist/shortcuts/calendar/host.js +2 -1
  36. package/dist/shortcuts/contract/host.js +34 -59
  37. package/dist/shortcuts/ehr/host.js +2 -1
  38. package/dist/shortcuts/esb/host.js +2 -1
  39. package/dist/shortcuts/fna/host.js +6 -3
  40. package/dist/shortcuts/fna/manifest.js +10 -5
  41. package/dist/shortcuts/fna/operations/expense-chart.js +162 -0
  42. package/dist/shortcuts/fna/operations/registry.js +2 -0
  43. package/dist/shortcuts/hrm/host.js +2 -1
  44. package/dist/shortcuts/im/host.js +3 -2
  45. package/dist/shortcuts/index.js +2 -0
  46. package/dist/shortcuts/invoice/host.js +9 -7
  47. package/dist/shortcuts/invoice/manifest.js +38 -47
  48. package/dist/shortcuts/invoice/operations/issuing-qys.js +328 -0
  49. package/dist/shortcuts/invoice/operations/registry.js +2 -3
  50. package/dist/shortcuts/jiuchuanhui/host.js +2 -1
  51. package/dist/shortcuts/mail/host.js +2 -1
  52. package/dist/shortcuts/meeting/host.js +2 -1
  53. package/dist/shortcuts/meeting/operations/eb/conflict.js +78 -12
  54. package/dist/shortcuts/okr/host.js +2 -1
  55. package/dist/shortcuts/plan/host.js +2 -1
  56. package/dist/shortcuts/project/host.js +2 -1
  57. package/dist/shortcuts/workflow/endpoints.js +11 -0
  58. package/dist/shortcuts/workflow/host.js +5 -7
  59. package/dist/shortcuts/workflow/index.js +6 -36
  60. package/dist/shortcuts/workflow/operations/chatscene.js +252 -0
  61. package/dist/shortcuts/workflow/operations/form.js +1047 -191
  62. package/dist/shortcuts/workflow/operations/read.js +137 -8
  63. package/dist/shortcuts/workflow/operations/sign-input.js +106 -0
  64. package/dist/shortcuts/workflow/operations/write.js +9 -1
  65. package/dist/shortcuts/workflow/stdout-compact.js +16 -1
  66. package/docs/SKILL.md +1 -1
  67. package/docs/_catalog.md +1 -1
  68. package/docs/agent-invoice.md +1 -1
  69. package/docs/archive-oa.md +79 -0
  70. package/docs/caizhilian.md +1 -1
  71. package/docs/e10-auth.md +7 -6
  72. package/docs/fna.md +7 -3
  73. package/docs/invoice.md +4 -6
  74. package/package.json +1 -1
  75. package/skill-template/business-info.json +20 -1
  76. package/skill-template/domains/archive-oa.md +19 -0
  77. package/skill-template/domains/caizhilian.md +2 -1
  78. package/skill-template/domains/qiyecheng.md +1 -0
  79. package/skill-template/domains/yepiaotong.md +8 -4
  80. package/skill-template/skill-template.md +1 -0
  81. package/skills/weaver-e10-archive-oa/SKILL.md +159 -0
  82. package/skills/weaver-e10-archive-oa/references/archive-locate.md +49 -0
  83. package/skills/weaver-e10-archive-oa/references/borrow-car.md +76 -0
  84. package/skills/weaver-e10-archive-oa/references/borrow-flow.md +53 -0
  85. package/skills/weaver-e10-archive-oa/references/contract-search.md +63 -0
  86. package/skills/weaver-e10-archive-oa/references/disabled-capabilities.md +29 -0
  87. package/skills/weaver-e10-archive-oa/references/qys-contract-search.md +47 -0
  88. package/skills/weaver-e10-archive-oa/references/source-manifest.json +400 -0
  89. package/skills/weaver-e10-blog/SKILL.md +6 -1
  90. package/skills/weaver-e10-blog/references/blog-query.md +3 -1
  91. package/skills/weaver-e10-builder/SKILL.md +22 -0
  92. package/skills/weaver-e10-caizhilian/SKILL.md +47 -16
  93. package/skills/weaver-e10-caizhilian/references/action-flow.md +70 -0
  94. package/skills/weaver-e10-caizhilian/references/agent-entry.md +12 -4
  95. package/skills/weaver-e10-caizhilian/references/auth-and-install.md +207 -0
  96. package/skills/weaver-e10-caizhilian/references/collaboration.md +5 -3
  97. package/skills/weaver-e10-caizhilian/references/document.md +33 -11
  98. package/skills/weaver-e10-caizhilian/references/file-collection.md +57 -54
  99. package/skills/weaver-e10-caizhilian/references/file-upload.md +5 -6
  100. package/skills/weaver-e10-caizhilian/references/files.md +9 -3
  101. package/skills/weaver-e10-caizhilian/references/folder.md +48 -16
  102. package/skills/weaver-e10-caizhilian/references/host-executor-prompt.md +32 -0
  103. package/skills/weaver-e10-caizhilian/references/host-platform-integration.md +46 -0
  104. package/skills/weaver-e10-caizhilian/references/rule-index.md +56 -0
  105. package/skills/weaver-e10-caizhilian/references/setting-attach.md +17 -54
  106. package/skills/weaver-e10-caizhilian/references/setting-backup.md +16 -63
  107. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +24 -64
  108. package/skills/weaver-e10-caizhilian/references/setting-storage-docs-summary.md +126 -0
  109. package/skills/weaver-e10-caizhilian/references/setting-storage-field-map.md +57 -0
  110. package/skills/weaver-e10-caizhilian/references/setting-storage.md +143 -548
  111. package/skills/weaver-e10-caizhilian/references/source-manifest.json +7 -7
  112. package/skills/weaver-e10-caizhilian/references/task-workflow.md +87 -0
  113. package/skills/weaver-e10-caizhilian/references/withheld.md +21 -45
  114. package/skills/weaver-e10-caizhilian/references/write-protocol.md +7 -0
  115. package/skills/weaver-e10-calendar/SKILL.md +6 -1
  116. package/skills/weaver-e10-esb/SKILL.md +8 -0
  117. package/skills/weaver-e10-esb/references/input-format.md +3 -1
  118. package/skills/weaver-e10-esb/references/source-manifest.json +5 -5
  119. package/skills/weaver-e10-hrm/SKILL.md +6 -1
  120. package/skills/weaver-e10-hrm/references/employee.md +1 -1
  121. package/skills/weaver-e10-hrm/references/org.md +1 -1
  122. package/skills/weaver-e10-hrm/references/position.md +1 -1
  123. package/skills/weaver-e10-jinchengda/SKILL.md +2 -1
  124. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +1 -1
  125. package/skills/weaver-e10-jiuchuanhui/SKILL.md +6 -1
  126. package/skills/weaver-e10-jucailin/SKILL.md +6 -1
  127. package/skills/weaver-e10-jucailin/references/hr-contact.md +1 -1
  128. package/skills/weaver-e10-jucailin/references/salary.md +1 -1
  129. package/skills/weaver-e10-mail/SKILL.md +6 -1
  130. package/skills/weaver-e10-meeting/SKILL.md +11 -6
  131. package/skills/weaver-e10-meeting/references/meeting-eb.md +7 -3
  132. package/skills/weaver-e10-meeting/references/meeting-queries.md +3 -1
  133. package/skills/weaver-e10-meeting/references/meeting-verification.md +4 -2
  134. package/skills/weaver-e10-okr/SKILL.md +6 -1
  135. package/skills/weaver-e10-okr/references/source-manifest.json +5 -5
  136. package/skills/weaver-e10-plan/SKILL.md +6 -1
  137. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +3 -1
  138. package/skills/weaver-e10-plan/references/plan-report-query.md +3 -1
  139. package/skills/weaver-e10-plan/references/source-manifest.json +7 -7
  140. package/skills/weaver-e10-qiyecheng/SKILL.md +16 -6
  141. package/skills/weaver-e10-qiyecheng/references/catalog.md +6 -3
  142. package/skills/weaver-e10-qiyecheng/references/expense-center.md +2 -0
  143. package/skills/weaver-e10-qiyecheng/references/expense-chart.md +78 -0
  144. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +87 -33
  145. package/skills/weaver-e10-shared/SKILL.md +53 -28
  146. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +22 -20
  147. package/skills/weaver-e10-shared/references/non-interactive-environment.md +13 -13
  148. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +3 -1
  149. package/skills/weaver-e10-wenshuding/SKILL.md +6 -1
  150. package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
  151. package/skills/weaver-e10-workflow/references/source-manifest.json +33 -24
  152. package/skills/weaver-e10-workflow/references/workflow-create.md +41 -26
  153. package/skills/weaver-e10-yepiaotong/SKILL.md +23 -18
  154. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +87 -45
  155. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +152 -0
  156. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +210 -0
  157. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +59 -36
  158. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +63 -512
  159. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +205 -346
  160. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -5
  161. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +231 -344
  162. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +5 -5
  163. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +201 -162
  164. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +121 -145
  165. package/skills/weaver-e10-yimiaoban/SKILL.md +9 -2
  166. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +1 -1
  167. package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -1
  168. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +9 -9
  169. package/skills/weaver-e10-ziguanjia/SKILL.md +6 -1
  170. package/dist/shortcuts/invoice/operations/issuing.js +0 -198
  171. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +0 -161
  172. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +0 -107
  173. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +0 -119
@@ -0,0 +1,120 @@
1
+ import { QYS_CONTRACT, QYS_DEFAULT_ARCHIVED_FIELD, QYS_DEFAULT_ARCHIVED_VALUE, WEAVER_CONTRACT, } from '../constants.js';
2
+ import { READ, archiveOaSuccess, schemaOf, SCHEMA_PAGE_NO, SCHEMA_SEARCH_FIELDS, } from './types.js';
3
+ import { assertEnum, buildCommonSearchFields, normalizeListResult, positiveInt, unwrapData } from './shared.js';
4
+ const CONTRACT_LIST_PATH = '/api/ebuilder/form/list/getListDatas';
5
+ function filterPath(profile) {
6
+ return `/api/ebuilder/form/listFilter/showFilters/${profile.listId}`;
7
+ }
8
+ function operationName(profile, action) {
9
+ return profile.source === 'qys'
10
+ ? `archive-oa.qys.contract.${action}`
11
+ : `archive-oa.contract.${action}`;
12
+ }
13
+ /** getListDatas 的固定请求体(分页与视图参数)。 */
14
+ function listBody(profile, pageNo, commonSearchFields, matchType) {
15
+ return {
16
+ current: pageNo,
17
+ requiredMatch: 1,
18
+ mapResult: '1',
19
+ recheckQuery: '1',
20
+ cusMenuId: profile.cusMenuId,
21
+ urlPageTitle: profile.urlPageTitle,
22
+ searchParamData: {
23
+ searchType: '2',
24
+ type: matchType,
25
+ commonSearchFields,
26
+ groupFields: {},
27
+ },
28
+ listId: profile.listId,
29
+ isFollow: 0,
30
+ isShareToMe: 0,
31
+ filterCondition: {},
32
+ browser: 'Chrome',
33
+ };
34
+ }
35
+ /** 搜索字段配置(showFilters):只有源资料收录了该辅助接口才生成。 */
36
+ function makeFieldsOperation(profile) {
37
+ const name = operationName(profile, 'fields');
38
+ return {
39
+ name,
40
+ resource: 'contract',
41
+ method: 'fields',
42
+ action: name,
43
+ summary: `查询${profile.source === 'qys' ? '契约锁' : '泛微'}商务合同可搜索字段配置(构造 commonSearchFields 用)`,
44
+ capability: 'contract-search',
45
+ risk: READ,
46
+ inputSchema: schemaOf({}, { description: '查询合同搜索字段配置,无需入参' }),
47
+ handler: async (_args, env) => {
48
+ const response = await env.host.request({
49
+ method: 'GET',
50
+ path: filterPath(profile),
51
+ operation: name,
52
+ query: { passid: profile.passId, sf_request_type: 'ajax' },
53
+ });
54
+ const data = (unwrapData(response) ?? {});
55
+ const fields = Array.isArray(data.fields)
56
+ ? data.fields
57
+ : Array.isArray(data)
58
+ ? data
59
+ : [];
60
+ return archiveOaSuccess(name, { fields }, { source: profile.source, listId: profile.listId, count: Array.isArray(fields) ? fields.length : 0 });
61
+ },
62
+ };
63
+ }
64
+ /** 商务合同列表查询。 */
65
+ function makeListOperation(profile) {
66
+ const name = operationName(profile, 'list');
67
+ const isQys = profile.source === 'qys';
68
+ return {
69
+ name,
70
+ resource: 'contract',
71
+ method: 'list',
72
+ action: name,
73
+ summary: `查询${isQys ? '契约锁协作采纳(案例)' : '泛微'}商务合同列表`,
74
+ capability: 'contract-search',
75
+ risk: READ,
76
+ inputSchema: schemaOf({
77
+ pageNo: { ...SCHEMA_PAGE_NO, description: '页码,从 1 开始,缺省 1' },
78
+ fields: SCHEMA_SEARCH_FIELDS,
79
+ matchType: {
80
+ type: 'string',
81
+ enum: ['and', 'or'],
82
+ description: '多条件组合方式,缺省 and(源资料约定默认「且」)',
83
+ },
84
+ ...(isQys
85
+ ? {
86
+ archivedOnly: {
87
+ type: 'boolean',
88
+ description: `默认 true:附加 ${QYS_DEFAULT_ARCHIVED_FIELD}=${QYS_DEFAULT_ARCHIVED_VALUE}(处理进度=已归档)过滤`,
89
+ },
90
+ }
91
+ : {}),
92
+ }, { description: `查询${isQys ? '契约锁' : '泛微'}商务合同列表` }),
93
+ handler: async (args, env) => {
94
+ const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
95
+ const matchType = args.matchType === undefined ? 'and' : assertEnum(args.matchType, ['and', 'or'], 'matchType');
96
+ const extra = [];
97
+ const archivedOnly = args.archivedOnly === undefined ? true : args.archivedOnly;
98
+ if (isQys && archivedOnly === true) {
99
+ extra.push({
100
+ field: QYS_DEFAULT_ARCHIVED_FIELD,
101
+ conditionType: 'like',
102
+ value: QYS_DEFAULT_ARCHIVED_VALUE,
103
+ });
104
+ }
105
+ const commonSearchFields = buildCommonSearchFields(args.fields, extra);
106
+ const response = await env.host.request({
107
+ method: 'POST',
108
+ path: CONTRACT_LIST_PATH,
109
+ operation: name,
110
+ query: { passid: profile.passId, cid: profile.passId, sf_request_type: 'ajax' },
111
+ body: listBody(profile, pageNo, commonSearchFields, matchType),
112
+ });
113
+ const { rows, meta } = normalizeListResult(name, response, profile.source);
114
+ return archiveOaSuccess(name, { rows, searchFields: commonSearchFields }, { ...meta, pageNo, archivedOnly: isQys ? archivedOnly : undefined });
115
+ },
116
+ };
117
+ }
118
+ export const weaverContractFieldsOperation = makeFieldsOperation(WEAVER_CONTRACT);
119
+ export const weaverContractListOperation = makeListOperation(WEAVER_CONTRACT);
120
+ export const qysContractListOperation = makeListOperation(QYS_CONTRACT);
@@ -0,0 +1,50 @@
1
+ import { ARCHIVE_LOCATE_PATH } from '../constants.js';
2
+ import { READ, archiveOaSuccess, schemaOf, SCHEMA_PAGE_NO, } from './types.js';
3
+ import { positiveInt, requireText } from './shared.js';
4
+ export const ARCHIVE_LOCATE = 'archive-oa.archive.locate';
5
+ /**
6
+ * 按合同号定位档案。
7
+ *
8
+ * 接口契约要点(源资料实测):合同号必须放在 body 顶层 `htbh`;
9
+ * 放进 `datajson.mainTable` 会被忽略并返回整表默认列表。
10
+ */
11
+ export const archiveLocateOperation = {
12
+ name: ARCHIVE_LOCATE,
13
+ resource: 'archive',
14
+ method: 'locate',
15
+ action: ARCHIVE_LOCATE,
16
+ summary: '按合同号定位档案:返回档案记录 ID / 题名 / 合同号',
17
+ capability: 'archive-locate',
18
+ risk: READ,
19
+ inputSchema: schemaOf({
20
+ contractNo: { type: 'string', minLength: 1, description: '合同号(对应接口顶层 htbh)' },
21
+ pageNo: { ...SCHEMA_PAGE_NO, description: '页码,从 1 开始,缺省 1' },
22
+ }, { required: ['contractNo'], description: '按合同号定位档案' }),
23
+ handler: async (args, env) => {
24
+ const contractNo = requireText(args.contractNo, 'contractNo');
25
+ const pageNo = positiveInt(args.pageNo, 'pageNo', 1);
26
+ const response = await env.host.request({
27
+ method: 'POST',
28
+ path: ARCHIVE_LOCATE_PATH,
29
+ operation: ARCHIVE_LOCATE,
30
+ body: {
31
+ htbh: contractNo,
32
+ datajson: {
33
+ mainTable: {},
34
+ pageInfo: { pageNo: String(pageNo) },
35
+ },
36
+ },
37
+ });
38
+ const datajson = (response?.datajson ?? {});
39
+ const datas = Array.isArray(datajson.datas) ? datajson.datas : [];
40
+ const rows = datas.map((item) => {
41
+ const mainTable = (item?.mainTable ?? {});
42
+ return {
43
+ id: String(mainTable.id ?? '').trim(),
44
+ title: String(mainTable.folder_title ?? '').trim(),
45
+ contractNo: String(mainTable.hth ?? contractNo).trim(),
46
+ };
47
+ });
48
+ return archiveOaSuccess(ARCHIVE_LOCATE, { rows, matched: rows.length > 0 }, { count: rows.length, contractNo, pageNo });
49
+ },
50
+ };
@@ -0,0 +1,40 @@
1
+ import { QYS_BORROW_CAR, QYS_ESB_ADD, WEAVER_BORROW_CAR, WEAVER_ESB_ADD } from '../constants.js';
2
+ import { archiveOaValidationError } from '../errors.js';
3
+ import { createBorrowCarOperations } from './borrow-car.js';
4
+ import { createBorrowStartUrlOperation } from './borrow-url.js';
5
+ import { qysContractListOperation, weaverContractFieldsOperation, weaverContractListOperation, } from './contract.js';
6
+ import { archiveLocateOperation } from './locate.js';
7
+ function buildRegistry() {
8
+ const registry = new Map();
9
+ const definitions = [
10
+ archiveLocateOperation,
11
+ weaverContractFieldsOperation,
12
+ weaverContractListOperation,
13
+ qysContractListOperation,
14
+ ...createBorrowCarOperations(WEAVER_BORROW_CAR, WEAVER_ESB_ADD),
15
+ ...createBorrowCarOperations(QYS_BORROW_CAR, QYS_ESB_ADD),
16
+ createBorrowStartUrlOperation(WEAVER_BORROW_CAR),
17
+ createBorrowStartUrlOperation(QYS_BORROW_CAR),
18
+ ];
19
+ for (const definition of definitions) {
20
+ if (registry.has(definition.name)) {
21
+ throw new Error(`duplicate archive-oa operation: ${definition.name}`);
22
+ }
23
+ registry.set(definition.name, definition);
24
+ }
25
+ return registry;
26
+ }
27
+ const archiveOaOperationRegistry = buildRegistry();
28
+ export function archiveOaOperationDefinitions() {
29
+ return [...archiveOaOperationRegistry.values()];
30
+ }
31
+ export function getArchiveOaOperation(operation) {
32
+ const definition = archiveOaOperationRegistry.get(operation);
33
+ if (!definition) {
34
+ throw archiveOaValidationError('operation_unknown', `archive-oa 不支持操作:${operation}`);
35
+ }
36
+ return definition;
37
+ }
38
+ export function hasArchiveOaOperation(operation) {
39
+ return archiveOaOperationRegistry.has(operation);
40
+ }
@@ -0,0 +1,181 @@
1
+ import { MAX_BORROW_ITEMS } from '../constants.js';
2
+ import { archiveOaValidationError } from '../errors.js';
3
+ /** 原型污染防护:禁止出现在输入对象中的键。 */
4
+ const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
5
+ const CONDITION_TYPES = new Set(['like', 'between', 'in', 'inC']);
6
+ export function asObject(value) {
7
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
8
+ throw archiveOaValidationError('input_not_object', '输入必须是 JSON 对象');
9
+ }
10
+ return value;
11
+ }
12
+ export function assertNoForbiddenKeys(input) {
13
+ for (const key of Object.keys(input)) {
14
+ if (FORBIDDEN_KEYS.has(key)) {
15
+ throw archiveOaValidationError('forbidden_key', `输入包含非法字段:${key}`);
16
+ }
17
+ }
18
+ }
19
+ /** 取出响应中的业务数据。 */
20
+ export function unwrapData(response) {
21
+ if (!response || typeof response !== 'object')
22
+ return response;
23
+ return response.data;
24
+ }
25
+ /** 正整数入参(页码)。 */
26
+ export function positiveInt(value, field, fallback) {
27
+ if (value === undefined || value === null || value === '')
28
+ return fallback;
29
+ const parsed = Number(value);
30
+ if (!Number.isInteger(parsed) || parsed <= 0) {
31
+ throw archiveOaValidationError('number_invalid', `${field} 必须是正整数`);
32
+ }
33
+ return parsed;
34
+ }
35
+ /** 非空字符串。 */
36
+ export function requireText(value, field) {
37
+ if (typeof value !== 'string' || !value.trim()) {
38
+ throw archiveOaValidationError('field_required', `${field} 必填`);
39
+ }
40
+ return value.trim();
41
+ }
42
+ /** 安全布尔。 */
43
+ export function requireBoolean(value, field) {
44
+ if (typeof value !== 'boolean') {
45
+ throw archiveOaValidationError('boolean_invalid', `${field} 必须是布尔值`);
46
+ }
47
+ return value;
48
+ }
49
+ /** 校验枚举。 */
50
+ export function assertEnum(value, allowed, field) {
51
+ const text = typeof value === 'string' ? value : String(value);
52
+ if (!allowed.includes(text)) {
53
+ throw archiveOaValidationError('enum_invalid', `${field} 取值必须是 ${allowed.join(' / ')} 之一`);
54
+ }
55
+ return text;
56
+ }
57
+ /** 数字 ID 数组(合同数据行 id / 借阅车记录 id)。 */
58
+ export function requireIdList(value, field, max = MAX_BORROW_ITEMS) {
59
+ if (!Array.isArray(value) || value.length === 0) {
60
+ throw archiveOaValidationError('id_list_required', `${field} 必填且至少一项`);
61
+ }
62
+ if (value.length > max) {
63
+ throw archiveOaValidationError('id_list_too_many', `${field} 最多 ${max} 条,当前 ${value.length} 条`);
64
+ }
65
+ const seen = new Set();
66
+ return value.map((item, index) => {
67
+ const text = typeof item === 'string' ? item.trim() : String(item ?? '').trim();
68
+ if (!/^\d+$/.test(text)) {
69
+ throw archiveOaValidationError('id_list_invalid', `${field}[${index}] 必须是数字 ID`);
70
+ }
71
+ if (seen.has(text)) {
72
+ throw archiveOaValidationError('id_list_duplicated', `${field}[${index}] 重复:${text}`);
73
+ }
74
+ seen.add(text);
75
+ return text;
76
+ });
77
+ }
78
+ /** 合同号数组(发起借阅的 hth)。 */
79
+ export function requireContractNoList(value, field, max = MAX_BORROW_ITEMS) {
80
+ if (!Array.isArray(value) || value.length === 0) {
81
+ throw archiveOaValidationError('contract_no_required', `${field} 必填且至少一项`);
82
+ }
83
+ if (value.length > max) {
84
+ throw archiveOaValidationError('contract_no_too_many', `${field} 最多 ${max} 个,当前 ${value.length} 个`);
85
+ }
86
+ return value.map((item, index) => {
87
+ if (typeof item !== 'string' || !item.trim()) {
88
+ throw archiveOaValidationError('contract_no_invalid', `${field}[${index}] 必须是非空合同号`);
89
+ }
90
+ const text = item.trim();
91
+ if (/[,\s]/.test(text)) {
92
+ throw archiveOaValidationError('contract_no_invalid', `${field}[${index}] 不能包含逗号或空白`);
93
+ }
94
+ return text;
95
+ });
96
+ }
97
+ /**
98
+ * 组装 getListDatas 的 commonSearchFields。
99
+ *
100
+ * 契约:`{ "<field>": [{ "<field>_condition_type": "<type>", "<field>": <value> }] }`。
101
+ * between 的值既支持 `{min,max}` 也支持 `[起,止]`;in / inC 的值必须是数组。
102
+ */
103
+ export function buildCommonSearchFields(fields, extra = []) {
104
+ const result = {};
105
+ const items = [...extra];
106
+ if (fields !== undefined && fields !== null) {
107
+ if (!Array.isArray(fields)) {
108
+ throw archiveOaValidationError('search_fields_invalid', 'fields 必须是数组');
109
+ }
110
+ for (const [index, raw] of fields.entries()) {
111
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
112
+ throw archiveOaValidationError('search_fields_invalid', `fields[${index}] 必须是对象`);
113
+ }
114
+ const item = raw;
115
+ const field = requireText(item.field, `fields[${index}].field`);
116
+ const conditionType = item.conditionType === undefined || item.conditionType === null
117
+ ? 'like'
118
+ : String(item.conditionType);
119
+ if (!CONDITION_TYPES.has(conditionType)) {
120
+ throw archiveOaValidationError('search_condition_invalid', `fields[${index}].conditionType 必须是 like / between / in / inC 之一`);
121
+ }
122
+ if (item.value === undefined || item.value === null) {
123
+ throw archiveOaValidationError('search_value_required', `fields[${index}].value 必填`);
124
+ }
125
+ items.push({ field, conditionType, value: item.value });
126
+ }
127
+ }
128
+ for (const item of items) {
129
+ const value = normalizeConditionValue(item.conditionType, item.value, item.field);
130
+ result[item.field] = [{ [`${item.field}_condition_type`]: item.conditionType, [item.field]: value }];
131
+ }
132
+ return result;
133
+ }
134
+ function normalizeConditionValue(conditionType, value, field) {
135
+ if (conditionType === 'between') {
136
+ if (Array.isArray(value)) {
137
+ if (value.length !== 2) {
138
+ throw archiveOaValidationError('search_value_invalid', `${field} 的 between 条件需要两个值`);
139
+ }
140
+ return { min: String(value[0]), max: String(value[1]) };
141
+ }
142
+ if (value && typeof value === 'object') {
143
+ const range = value;
144
+ if (range.min === undefined || range.max === undefined) {
145
+ throw archiveOaValidationError('search_value_invalid', `${field} 的 between 条件需要 min / max`);
146
+ }
147
+ return { min: String(range.min), max: String(range.max) };
148
+ }
149
+ throw archiveOaValidationError('search_value_invalid', `${field} 的 between 条件必须是 {min,max} 或 [起,止]`);
150
+ }
151
+ if (conditionType === 'in' || conditionType === 'inC') {
152
+ if (!Array.isArray(value)) {
153
+ throw archiveOaValidationError('search_value_invalid', `${field} 的 ${conditionType} 条件必须是数组`);
154
+ }
155
+ return value;
156
+ }
157
+ return value;
158
+ }
159
+ /** 从 getListDatas 响应里提取行与列,并对内部 ID 做静默缓存(不回显给模型以外的地方)。 */
160
+ export function normalizeListResult(action, response, source) {
161
+ const data = (unwrapData(response) ?? {});
162
+ const rows = Array.isArray(data.data) ? data.data : [];
163
+ const columns = Array.isArray(data.columns)
164
+ ? data.columns.map((column) => ({
165
+ title: String(column?.title ?? ''),
166
+ dataIndex: String(column?.dataIndex ?? ''),
167
+ }))
168
+ : [];
169
+ return {
170
+ rows,
171
+ meta: {
172
+ source,
173
+ count: rows.length,
174
+ current: data.current ?? undefined,
175
+ pageSize: data.pageSize ?? undefined,
176
+ /** 接口可能返回 total=0 但 data 非空,属于正常现象,不做无数据判定。 */
177
+ total: data.total ?? undefined,
178
+ columns,
179
+ },
180
+ };
181
+ }
@@ -0,0 +1,45 @@
1
+ export const READ = 'read';
2
+ export const READ_BEFORE_WRITE = 'read-before-write';
3
+ export const HIGH_RISK_WRITE = 'high-risk-write';
4
+ export function archiveOaSuccess(action, data, meta) {
5
+ const payload = { ok: true, action, data };
6
+ if (meta && Object.keys(meta).length > 0)
7
+ payload.meta = meta;
8
+ return payload;
9
+ }
10
+ /** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
11
+ export function schemaOf(properties, options = {}) {
12
+ return {
13
+ type: 'object',
14
+ additionalProperties: false,
15
+ properties,
16
+ ...(options.required?.length ? { required: options.required } : {}),
17
+ ...(options.description ? { description: options.description } : {}),
18
+ };
19
+ }
20
+ export const SCHEMA_PAGE_NO = { type: 'integer', minimum: 1 };
21
+ export const SCHEMA_ID_STRING = { type: 'string', pattern: '^\\d+$' };
22
+ export const SCHEMA_ID_LIST = { type: 'array', items: SCHEMA_ID_STRING, minItems: 1 };
23
+ export const SCHEMA_CONTRACT_NO_LIST = {
24
+ type: 'array',
25
+ items: { type: 'string', minLength: 1 },
26
+ minItems: 1,
27
+ };
28
+ export const SCHEMA_SEARCH_FIELDS = {
29
+ type: 'array',
30
+ description: '搜索条件数组,元素为 { field, conditionType, value }',
31
+ items: {
32
+ type: 'object',
33
+ additionalProperties: false,
34
+ required: ['field', 'value'],
35
+ properties: {
36
+ field: { type: 'string', minLength: 1, description: '字段内部名,如 hth / khmc / htzje / zxfa' },
37
+ conditionType: {
38
+ type: 'string',
39
+ enum: ['like', 'between', 'in', 'inC'],
40
+ description: '条件类型,缺省 like;between 用 {min,max} 或 [起,止];in/inC 用数组',
41
+ },
42
+ value: { description: '条件值:like 为字符串,between 为对象或二元数组,in/inC 为数组' },
43
+ },
44
+ },
45
+ };
@@ -0,0 +1,13 @@
1
+ import { getArchiveOaOperation } from './operations/registry.js';
2
+ import { asObject, assertNoForbiddenKeys } from './operations/shared.js';
3
+ /**
4
+ * 执行一个 archive-oa 操作。操作定义(入参 schema、风险等级、是否必须确认)
5
+ * 全部来自 operations/registry,CLI 侧只负责校验输入形状。
6
+ */
7
+ export async function executeArchiveOaOperation(operation, rawInput, env) {
8
+ const definition = getArchiveOaOperation(operation);
9
+ const input = asObject(rawInput);
10
+ assertNoForbiddenKeys(input);
11
+ return definition.handler(input, { ctx: env.ctx, host: env.host, continuation: env.continuation });
12
+ }
13
+ export { archiveOaOperationDefinitions, getArchiveOaOperation, hasArchiveOaOperation } from './operations/registry.js';
@@ -1,3 +1,4 @@
1
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
1
2
  import { requireE10Session } from '../../internal/e10/context.js';
2
3
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
4
  import { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -176,7 +177,7 @@ export function createZiguanjiaHost(ctx) {
176
177
  bigIntStrings: true,
177
178
  });
178
179
  if (response.status === 401 || response.status === 302) {
179
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { httpStatus: response.status });
180
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
180
181
  }
181
182
  if (response.parseError) {
182
183
  throw ziguanjiaNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
@@ -242,7 +243,7 @@ export function createZiguanjiaHost(ctx) {
242
243
  bigIntStrings: false,
243
244
  });
244
245
  if (response.status === 401 || response.status === 302) {
245
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { httpStatus: response.status });
246
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
246
247
  }
247
248
  if (response.parseError) {
248
249
  throw ziguanjiaNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
@@ -315,7 +316,7 @@ export function createZiguanjiaHost(ctx) {
315
316
  bigIntStrings: true,
316
317
  });
317
318
  if (response.status === 401 || response.status === 302) {
318
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { httpStatus: response.status });
319
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
319
320
  }
320
321
  if (response.parseError) {
321
322
  throw ziguanjiaNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
@@ -340,7 +341,7 @@ export function createZiguanjiaHost(ctx) {
340
341
  }
341
342
  function handleEbusiness(value) {
342
343
  if (isEbSessionExpired(value)) {
343
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { ret: ebCode(value) });
344
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { ret: ebCode(value) });
344
345
  }
345
346
  const code = ebCode(value);
346
347
  if (!isEbSuccessCode(code)) {
@@ -363,7 +364,7 @@ export function createZiguanjiaHost(ctx) {
363
364
  bigIntStrings: true,
364
365
  });
365
366
  if (response.status === 401 || response.status === 302) {
366
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { httpStatus: response.status });
367
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
367
368
  }
368
369
  if (response.parseError) {
369
370
  throw ziguanjiaNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
@@ -393,7 +394,7 @@ export function createZiguanjiaHost(ctx) {
393
394
  bigIntStrings: true,
394
395
  });
395
396
  if (response.status === 401 || response.status === 302) {
396
- throw ziguanjiaAuthError('session_expired', 'E10 登录态已失效,请先运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录后再试', { httpStatus: response.status });
397
+ throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
397
398
  }
398
399
  if (response.parseError) {
399
400
  throw ziguanjiaNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
@@ -1,4 +1,5 @@
1
1
  import { ExitCode } from '../../core/errors.js';
2
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
2
3
  import { requireE10Session } from '../../internal/e10/context.js';
3
4
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
4
5
  import { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -75,7 +76,7 @@ export function createBlogHost(ctx, dependencies = {}) {
75
76
  }
76
77
  function assertSession(value) {
77
78
  if (isSessionExpired(value)) {
78
- throw blogAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录');
79
+ throw blogAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE);
79
80
  }
80
81
  }
81
82
  async function blogRequest(request) {
@@ -91,7 +92,7 @@ export function createBlogHost(ctx, dependencies = {}) {
91
92
  timeoutMs,
92
93
  });
93
94
  if (response.status === 401 || response.status === 302) {
94
- throw blogAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录', { httpStatus: response.status });
95
+ throw blogAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
95
96
  }
96
97
  if (response.parseError) {
97
98
  if (response.status < 200 || response.status >= 300) {
@@ -134,7 +135,7 @@ export function createBlogHost(ctx, dependencies = {}) {
134
135
  headers: { Accept: 'application/octet-stream, */*' },
135
136
  });
136
137
  if (response.status === 401 || response.status === 302) {
137
- throw blogAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录', { httpStatus: response.status });
138
+ throw blogAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
138
139
  }
139
140
  const contentType = response.contentType;
140
141
  const declaredLength = Number(response.headers.get('content-length'));
@@ -1,3 +1,4 @@
1
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
1
2
  import { requireE10Session } from '../../internal/e10/context.js';
2
3
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
4
  import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -152,7 +153,7 @@ export function createCaizhilianHost(ctx, dependencies = {}) {
152
153
  async function request(request) {
153
154
  const response = await doRequest(request, false).catch((error) => mapRuntimeNetworkError(error));
154
155
  if (response.status === 401 || response.status === 302) {
155
- throw caizhilianAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status });
156
+ throw caizhilianAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
156
157
  }
157
158
  if (response.parseError) {
158
159
  if (response.status < 200 || response.status >= 300) {
@@ -183,7 +184,7 @@ export function createCaizhilianHost(ctx, dependencies = {}) {
183
184
  async function requestBinary(request) {
184
185
  const response = await doRequest(request, true).catch((error) => mapRuntimeNetworkError(error));
185
186
  if (response.status === 401 || response.status === 302) {
186
- throw caizhilianAuthError('session_expired', 'E10 登录态已失效,请先登录', { httpStatus: response.status });
187
+ throw caizhilianAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
187
188
  }
188
189
  if (response.status < 200 || response.status >= 300) {
189
190
  throw new CaizhilianCliError('api', 'http_error', `采知连下载 HTTP ${response.status}`, {
@@ -19,7 +19,7 @@ const WITHHELD = [
19
19
  },
20
20
  {
21
21
  name: 'caizhilian.system.switch-config',
22
- reason: '源 switch_config 在 Skill 本地多套环境配置间切换;CLI 的环境切换由 `weaver-work-cli config set` / `weaver-work-cli auth login --base-url <域名>` 承担,业务模块不做环境切换。',
22
+ reason: '源 switch_config 在 Skill 本地多套环境配置间切换;CLI 的环境与认证配置体系负责这一职责,业务模块不做环境切换。',
23
23
  },
24
24
  ];
25
25
  function buildOperations() {
@@ -1,3 +1,4 @@
1
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
1
2
  import { requireE10Session } from '../../internal/e10/context.js';
2
3
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
4
  import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -110,7 +111,7 @@ export function createCalendarHost(ctx, dependencies = {}) {
110
111
  mapRuntimeNetworkError(error);
111
112
  }
112
113
  if (response.status === 401 || response.status === 302) {
113
- throw calendarAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status });
114
+ throw calendarAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
114
115
  }
115
116
  if (response.parseError) {
116
117
  if (response.status < 200 || response.status >= 300) {