weaver-work-cli 0.1.25 → 0.1.26

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 (81) hide show
  1. package/README.md +30 -30
  2. package/dist/cmd/root.js +1 -0
  3. package/dist/core/runtime-agent.js +19 -0
  4. package/dist/internal/e10/auth/commands.js +47 -27
  5. package/dist/internal/e10/auth/xiaoe.js +62 -43
  6. package/dist/internal/e10/context.js +5 -27
  7. package/dist/shortcuts/archive-oa/constants.js +66 -0
  8. package/dist/shortcuts/archive-oa/continuation.js +70 -0
  9. package/dist/shortcuts/archive-oa/errors.js +53 -0
  10. package/dist/shortcuts/archive-oa/host.js +157 -0
  11. package/dist/shortcuts/archive-oa/index.js +225 -0
  12. package/dist/shortcuts/archive-oa/manifest.js +62 -0
  13. package/dist/shortcuts/archive-oa/operations/borrow-car.js +159 -0
  14. package/dist/shortcuts/archive-oa/operations/borrow-url.js +108 -0
  15. package/dist/shortcuts/archive-oa/operations/confirm.js +48 -0
  16. package/dist/shortcuts/archive-oa/operations/contract.js +120 -0
  17. package/dist/shortcuts/archive-oa/operations/locate.js +50 -0
  18. package/dist/shortcuts/archive-oa/operations/registry.js +40 -0
  19. package/dist/shortcuts/archive-oa/operations/shared.js +181 -0
  20. package/dist/shortcuts/archive-oa/operations/types.js +45 -0
  21. package/dist/shortcuts/archive-oa/operations.js +13 -0
  22. package/dist/shortcuts/index.js +2 -0
  23. package/dist/shortcuts/invoice/host.js +2 -1
  24. package/dist/shortcuts/invoice/manifest.js +111 -0
  25. package/dist/shortcuts/invoice/operations/issuing-qys.js +361 -0
  26. package/dist/shortcuts/invoice/operations/issuing.js +5 -5
  27. package/dist/shortcuts/invoice/operations/registry.js +4 -0
  28. package/dist/shortcuts/workflow/endpoints.js +11 -0
  29. package/dist/shortcuts/workflow/host.js +2 -2
  30. package/dist/shortcuts/workflow/index.js +6 -36
  31. package/dist/shortcuts/workflow/operations/chatscene.js +270 -0
  32. package/dist/shortcuts/workflow/operations/form.js +899 -158
  33. package/dist/shortcuts/workflow/operations/read.js +111 -6
  34. package/dist/shortcuts/workflow/operations/sign-input.js +106 -0
  35. package/dist/shortcuts/workflow/operations/write.js +9 -1
  36. package/dist/shortcuts/workflow/stdout-compact.js +9 -1
  37. package/docs/archive-oa.md +79 -0
  38. package/docs/e10-auth.md +7 -6
  39. package/package.json +1 -1
  40. package/skill-template/business-info.json +16 -0
  41. package/skill-template/domains/archive-oa.md +19 -0
  42. package/skill-template/domains/yepiaotong.md +2 -2
  43. package/skills/weaver-e10-archive-oa/SKILL.md +159 -0
  44. package/skills/weaver-e10-archive-oa/references/archive-locate.md +49 -0
  45. package/skills/weaver-e10-archive-oa/references/borrow-car.md +76 -0
  46. package/skills/weaver-e10-archive-oa/references/borrow-flow.md +53 -0
  47. package/skills/weaver-e10-archive-oa/references/contract-search.md +63 -0
  48. package/skills/weaver-e10-archive-oa/references/disabled-capabilities.md +29 -0
  49. package/skills/weaver-e10-archive-oa/references/qys-contract-search.md +47 -0
  50. package/skills/weaver-e10-archive-oa/references/source-manifest.json +400 -0
  51. package/skills/weaver-e10-builder/SKILL.md +22 -0
  52. package/skills/weaver-e10-caizhilian/SKILL.md +37 -15
  53. package/skills/weaver-e10-caizhilian/references/agent-entry.md +7 -3
  54. package/skills/weaver-e10-caizhilian/references/collaboration.md +5 -3
  55. package/skills/weaver-e10-caizhilian/references/document.md +33 -11
  56. package/skills/weaver-e10-caizhilian/references/file-collection.md +4 -3
  57. package/skills/weaver-e10-caizhilian/references/file-upload.md +2 -3
  58. package/skills/weaver-e10-caizhilian/references/files.md +9 -3
  59. package/skills/weaver-e10-caizhilian/references/folder.md +48 -16
  60. package/skills/weaver-e10-caizhilian/references/setting-attach.md +13 -39
  61. package/skills/weaver-e10-caizhilian/references/setting-backup.md +13 -51
  62. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +20 -51
  63. package/skills/weaver-e10-caizhilian/references/setting-storage.md +82 -147
  64. package/skills/weaver-e10-caizhilian/references/withheld.md +20 -44
  65. package/skills/weaver-e10-shared/SKILL.md +22 -20
  66. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +14 -15
  67. package/skills/weaver-e10-shared/references/non-interactive-environment.md +10 -10
  68. package/skills/weaver-e10-workflow/references/source-manifest.json +32 -22
  69. package/skills/weaver-e10-workflow/references/workflow-create.md +68 -28
  70. package/skills/weaver-e10-yepiaotong/SKILL.md +10 -7
  71. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +49 -1
  72. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-qys-ledger.md +66 -0
  73. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +59 -36
  74. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +54 -509
  75. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +182 -345
  76. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -5
  77. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +153 -345
  78. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +5 -5
  79. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +182 -154
  80. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +96 -0
  81. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +0 -119
@@ -1478,6 +1478,25 @@ async function enforceSearchStdoutBudget(result, env, fileName) {
1478
1478
  };
1479
1479
  }
1480
1480
  // ---------------------------------------------------------------------------
1481
+ // 命令耗时(`durationMs`)
1482
+ // ---------------------------------------------------------------------------
1483
+ /**
1484
+ * 给 envelope 的 `data` 顶层补一个 `durationMs`(整数毫秒)。
1485
+ *
1486
+ * 口径与 `createForm` 的 `durationMs` **完全一致**(2026-10-19 用户裁定把这条命令也纳入):
1487
+ * 起点 = 命令 handler 入口(含会话解析、取数、落盘等全部动作),终点 = 返回之前;
1488
+ * 放在 `data` 顶层而不是信封 `meta`,这样「超线落盘」那一档 `data` 只剩一句提示时耗时依然在。
1489
+ *
1490
+ * 只在 `workflow.search` / `workflow.memoryPrompt` 两条命令上接线(用户明确「其他命令暂时不动」)。
1491
+ * `data` 不是普通对象(数组/字符串/空)时原样返回,绝不因为加耗时而改变返回形态。
1492
+ */
1493
+ function stampDurationMs(envelope, startedAt) {
1494
+ const data = envelope?.data;
1495
+ if (!data || typeof data !== 'object' || Array.isArray(data))
1496
+ return envelope;
1497
+ return { ...envelope, data: { ...data, durationMs: Date.now() - startedAt } };
1498
+ }
1499
+ // ---------------------------------------------------------------------------
1481
1500
  // 序号定位(查询类写索引;查看类读索引)
1482
1501
  // ---------------------------------------------------------------------------
1483
1502
  async function saveListIndex(env, normalized) {
@@ -1566,6 +1585,8 @@ const workflowSearch = {
1566
1585
  queryJson: { type: 'object', description: '已生成的完整查询 JSON(跳过 build-query)' },
1567
1586
  }),
1568
1587
  handler: async (args, env) => {
1588
+ // 耗时起点:整条命令的入口(含 plan 门禁的版本探测、取数、落盘)。
1589
+ const startedAt = Date.now();
1569
1590
  const plan = args.planJson;
1570
1591
  if (plan && typeof plan === 'object') {
1571
1592
  // plan 批量编排仅智能分组模式支持(来源:Python cmd_workflow_search_plan 首行的
@@ -1578,7 +1599,7 @@ const workflowSearch = {
1578
1599
  throw workflowValidationError('plan_requires_smart_mode', `plan 批量编排模式仅支持智能分组模式(OA ≥ ${formatOaVersion(SMART_GROUPING_MIN_OA_VERSION)});低版本请改用单条 workflow.search`, { retryAllowed: false });
1579
1600
  }
1580
1601
  // 顶层 pageSize 即 plan 的展示窗口 page_cap(来源:Python cmd_workflow_search_plan 的 page_cap)
1581
- return executePlanSearch(env, plan, asInt(args.pageSize) ?? 10);
1602
+ return stampDurationMs(await executePlanSearch(env, plan, asInt(args.pageSize) ?? 10), startedAt);
1582
1603
  }
1583
1604
  const category = (asString(args.category) || 'todo');
1584
1605
  const searchMode = await resolveSearchMode(env);
@@ -1588,7 +1609,7 @@ const workflowSearch = {
1588
1609
  // grouping_active 分支):未显式指定子分类、非 total-only、非批量取数时按「待处理/超时/紧急」
1589
1610
  // 分组取数并输出分组统计。smart 模式不做 CLI 侧分组(分组由模型按 countDetails 规划)。
1590
1611
  if (searchMode === 'legacy' && category === 'todo' && !batchType && !totalOnly && !hasExplicitTab(args)) {
1591
- return executeLegacyGroupedTodoSearch(env, args);
1612
+ return stampDurationMs(await executeLegacyGroupedTodoSearch(env, args), startedAt);
1592
1613
  }
1593
1614
  const { query, tabid } = await buildSearchQuery(env, { ...args, searchMode }, 'list');
1594
1615
  const prefix = ENDPOINT_PREFIX[category];
@@ -1652,13 +1673,13 @@ const workflowSearch = {
1652
1673
  }
1653
1674
  }
1654
1675
  if (totalOnly) {
1655
- return workflowSuccess('workflow.search', {
1676
+ return stampDurationMs(workflowSuccess('workflow.search', {
1656
1677
  type: 'workflowSearch',
1657
1678
  category,
1658
1679
  categoryName: CATEGORY_NAMES[category],
1659
1680
  total,
1660
1681
  finalMarkdown: buildSearchFinalMarkdown(null, probes, total, category, true, searchMode === 'smart', waitReadTotal),
1661
- });
1682
+ }), startedAt);
1662
1683
  }
1663
1684
  // 卡片摘要行只在 **legacy(低版本模式)** 生成——契约见 references/workflow-query.md
1664
1685
  // 「执行模式与返回形态」的「卡片摘要行」一行(低版本=标题/摘要/辅助三行;智能分组=标题/辅助
@@ -1674,7 +1695,7 @@ const workflowSearch = {
1674
1695
  await enrichWorkflowCardSummaries(env, normalized, asInt(args.pageSize) ?? normalized.pageSize);
1675
1696
  }
1676
1697
  const index = await saveListIndex(env, normalized);
1677
- return workflowSuccess('workflow.search', await enforceSearchStdoutBudget({
1698
+ return stampDurationMs(workflowSuccess('workflow.search', await enforceSearchStdoutBudget({
1678
1699
  type: 'workflowSearch',
1679
1700
  category,
1680
1701
  categoryName: CATEGORY_NAMES[category],
@@ -1688,7 +1709,7 @@ const workflowSearch = {
1688
1709
  finalMarkdown: buildSearchFinalMarkdown(normalized, probes, total, category, false, searchMode === 'smart', waitReadTotal, withCardSummaries),
1689
1710
  queryId: index.queryId,
1690
1711
  sessionMap: { queryId: index.queryId, itemCount: normalized.items.length },
1691
- }, env, { json: 'last_search.json', md: 'last_search.md' }));
1712
+ }, env, { json: 'last_search.json', md: 'last_search.md' })), startedAt);
1692
1713
  },
1693
1714
  };
1694
1715
  // ---------------------------------------------------------------------------
@@ -4815,6 +4836,8 @@ const workflowMemoryPrompt = {
4815
4836
  dynamicContext: { type: 'object', description: '动态上下文参数(可选,合并进 dynamicContextParams)' },
4816
4837
  }),
4817
4838
  handler: async (args, env) => {
4839
+ // 耗时起点:整条命令的入口(含记忆接口那一趟与会话解析)。
4840
+ const startedAt = Date.now();
4818
4841
  // 记忆接口契约(来源:Python memory_chat_params):
4819
4842
  // { appid: <记忆应用 ID>, dynamicContextParams: { workflowId?, ...额外上下文 } }
4820
4843
  // 缺 appid 时接口判为无效应用,直接返回空 promptText(available=false)——
@@ -4897,6 +4920,8 @@ const workflowMemoryPrompt = {
4897
4920
  if (!hasOaContext) {
4898
4921
  result.oaContextMissingReason = '当前 E10 会话中未包含 OA 版本与用户身份信息。请先运行 weaver-work-cli auth status 确认登录态;若会话确已失效,请用 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 重新登录后重试。';
4899
4922
  }
4923
+ // 命令耗时(毫秒):与 createForm / workflow.search 同口径,`data` 顶层。
4924
+ result.durationMs = Date.now() - startedAt;
4900
4925
  return workflowSuccess('workflow.memoryPrompt', result, {
4901
4926
  oaContextMissing: !hasOaContext,
4902
4927
  });
@@ -5165,6 +5190,86 @@ export async function fetchHistoryListSnapshot(env, options = {}) {
5165
5190
  return null;
5166
5191
  }
5167
5192
  }
5193
+ /**
5194
+ * 「多候选里按用户近一年的发起记录消歧」——`createForm` 澄清前的最后一次尝试。
5195
+ *
5196
+ * 场景(2026-10-20 用户裁定):`workflow` 只给了名称、**多命中又没有全名精确匹配**,本来要返回
5197
+ * 多候选澄清。但用户往往只发起过其中之一 ⇒ 用「我发起的」**工作流树**把候选里他真发起过的捞出来:
5198
+ * 只剩一个就直接用它,不必再问一遍;否则照旧澄清。
5199
+ *
5200
+ * 请求 = `getMineListData` 的 body **去掉分页**(`customDisplayPage`),外加一个 `workflowid`
5201
+ * 条件把范围**限定在待澄清候选上**("涉及这次要澄清的那些工作流的流程")。`workflowid` 属
5202
+ * 浏览按钮类条件,**只认 `browserDatas`**——`multiContent` 会被服务端静默忽略(2026-09-29 实测)。
5203
+ *
5204
+ * 判据(用户逐字给定):`data.data` 恰一个根、该根 `children` 恰一个子 ⇒ 唯一。子节点 id 形如
5205
+ * `wf_<workflowId>`,去掉 `wf_` 即工作流 ID。
5206
+ *
5207
+ * 返回 `null` 的**所有**情形(候选不足 2 个 / 无数据 / 多个根 / 单个根多个子 / 命中项不在候选内 /
5208
+ * 接口失败或返回异常)都由调用方**回落原澄清**:本函数只回答"能不能确定",绝不上抛错误
5209
+ * ——兜底查询的失败不能顶掉用户本来要看到的那条澄清文案。
5210
+ */
5211
+ export async function pickUniqueMineWorkflow(candidates, env) {
5212
+ const candidateIds = [];
5213
+ const nameById = new Map();
5214
+ for (const item of toArray(candidates)) {
5215
+ const id = String(item?.id ?? '').trim();
5216
+ if (!id || nameById.has(id))
5217
+ continue;
5218
+ candidateIds.push(id);
5219
+ nameById.set(id, String(item?.name ?? ''));
5220
+ }
5221
+ // 只有一个候选时上游已经直接采用了,不会走到这里;0 个也没得消歧。
5222
+ if (candidateIds.length < 2)
5223
+ return null;
5224
+ try {
5225
+ const now = new Date();
5226
+ const start = new Date(now.getTime() - HISTORY_LOOKBACK_DAYS * 24 * 60 * 60 * 1000);
5227
+ const response = await env.host.workflowRequest({
5228
+ method: 'POST',
5229
+ path: WORKFLOW_ENDPOINTS.MINE_TREE,
5230
+ operation: 'workflow.createForm.mineTree',
5231
+ body: {
5232
+ params: { tabid: DEFAULT_TABID.mine, dimension: 'mine', flowSpareType: 1, outType: 0 },
5233
+ searchParams: {
5234
+ formDatas: {
5235
+ // 与历史取数同一口径:非草稿(未结束 + 已结束)。选项类条件必须是 `multiContent`。
5236
+ flowstatus: flowStatusCondition('notFinish,finish')?.condition ?? [],
5237
+ createdatetime: [{
5238
+ dateContent: [formatLocalDate(start), formatLocalDate(now)],
5239
+ term: 'like',
5240
+ }],
5241
+ workflowid: [{
5242
+ term: 'in',
5243
+ browserDatas: candidateIds.map((id) => ({ id, name: nameById.get(id) ?? '' })),
5244
+ }],
5245
+ },
5246
+ type: 'and',
5247
+ initSearch: false,
5248
+ },
5249
+ },
5250
+ });
5251
+ const body = (response && typeof response === 'object' ? response : {});
5252
+ if (body.fail === true || body.status === false)
5253
+ return null;
5254
+ const roots = toArray(body.data?.data);
5255
+ if (roots.length !== 1)
5256
+ return null;
5257
+ const children = toArray(roots[0]?.children);
5258
+ if (children.length !== 1)
5259
+ return null;
5260
+ const rawId = String(children[0]?.id ?? '').trim();
5261
+ const id = rawId.startsWith('wf_') ? rawId.slice(3) : rawId;
5262
+ // 双保险:服务端若没认 `workflowid` 条件、回了别的流程,这里必须挡掉(宁可澄清也不猜)。
5263
+ if (!id || !nameById.has(id))
5264
+ return null;
5265
+ const title = String(children[0]?.title ?? '').trim();
5266
+ return { id, name: title || (nameById.get(id) ?? '') };
5267
+ }
5268
+ catch {
5269
+ // 认证/网络/格式错误一律按"没能确定"处理 —— 澄清才是用户能继续操作的出口。
5270
+ return null;
5271
+ }
5272
+ }
5168
5273
  export async function buildHistoryRequestData(env, fields, targets, options = {},
5169
5274
  /**
5170
5275
  * 已提前发起的列表请求(见 `fetchHistoryListSnapshot`)。传入即直接复用,不再重复发请求;
@@ -0,0 +1,106 @@
1
+ import { AJAX_QUERY, FORM_ENDPOINTS } from '../endpoints.js';
2
+ /** 接口没给出可用值时的兜底 —— 与真机默认值一致(`微软雅黑/Microsoft YaHei`、`18/18px`)。 */
3
+ const DEFAULT_FONT_FAMILY = 'Microsoft YaHei';
4
+ const DEFAULT_FONT_SIZE = '18px';
5
+ const DEFAULT_FONT_ID = 'wea_rich_text_default_font';
6
+ /**
7
+ * `defaultFontFamily` / `defaultFontSize` 都是「中文名/实际值」的双段形态
8
+ * (`微软雅黑/Microsoft YaHei`、`18/18px`)。CSS 要的是斜杠**后面**那段;没有斜杠就整段用。
9
+ */
10
+ function cssValue(raw, fallback) {
11
+ const text = String(raw ?? '').trim();
12
+ if (!text)
13
+ return fallback;
14
+ const segments = text.split('/').map((item) => item.trim()).filter(Boolean);
15
+ const value = segments.length ? segments[segments.length - 1] : '';
16
+ if (!value)
17
+ return fallback;
18
+ return value;
19
+ }
20
+ /** 字号只给数字(`18`)时补单位 —— 否则拼出来的 `font-size:18` 是无效 CSS,浏览器会整条丢弃。 */
21
+ function cssFontSize(raw) {
22
+ const value = cssValue(raw, DEFAULT_FONT_SIZE);
23
+ return /^\d+(\.\d+)?$/u.test(value) ? `${value}px` : value;
24
+ }
25
+ /**
26
+ * 取签字意见的默认样式。**失败一律返回 `null`**(调用方据此退回原行为),绝不阻断提交 ——
27
+ * 这只是给意见加样式,不能因为样式接口挂了就让整条流程发不出去。
28
+ */
29
+ export async function loadSignInputCfg(env, target) {
30
+ const workflowId = String(target.workflowId ?? '').trim();
31
+ const requestId = String(target.requestId ?? '').trim();
32
+ // 发起流程用 `workflowId` + `isCreate:1`;提交已有流程用 `requestId` + `isCreate:0`。
33
+ const isCreate = workflowId && !requestId;
34
+ if (!isCreate && !requestId)
35
+ return null;
36
+ const body = isCreate
37
+ ? { isRemark: '', workflowId, isCreate: 1 }
38
+ : {
39
+ isRemark: 0,
40
+ operResultType: 'SUCCESS_RELOAD_CURRENT_PAGE',
41
+ userSpecifiedNodeId: '0',
42
+ autoMaxDisplay: '0',
43
+ isCreate: 0,
44
+ requestId,
45
+ };
46
+ try {
47
+ const response = await env.host.workflowRequest({
48
+ method: 'POST',
49
+ path: FORM_ENDPOINTS.signInputInfo,
50
+ // 与同族 `flowPage/*` 接口(loadBaseParam / loadFrameParam / loadMinorInfo)取同一份公共 query。
51
+ query: AJAX_QUERY,
52
+ body,
53
+ operation: isCreate ? 'workflow.createRequest' : 'workflow.operateApply',
54
+ });
55
+ const cfg = response?.data?.signInputCfg;
56
+ if (!cfg || typeof cfg !== 'object')
57
+ return null;
58
+ const raw = cfg.plainTextSignInput;
59
+ return {
60
+ plainText: raw === true || String(raw ?? '').toLowerCase() === 'true',
61
+ fontFamily: cssValue(cfg.defaultFontFamily, DEFAULT_FONT_FAMILY),
62
+ fontSize: cssFontSize(cfg.defaultFontSize),
63
+ };
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function escapeText(text) {
70
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
71
+ }
72
+ /**
73
+ * 把签字意见包成服务端能正确渲染的形态。
74
+ *
75
+ * - 空内容 ⇒ `_weaverMte_`(与历史一致,表示"没有意见");
76
+ * - 已带 `_weaverMte_` 前缀(模型自己给了富文本)⇒ 原样,不重复嵌套;
77
+ * - `cfg` 取不到 ⇒ 退回历史行为 `_weaverMte_<p>内容</p>`(不知道协商格式就不猜);
78
+ * - `plainTextSignInput` 为真 ⇒ `<span id=... style=...>内容</span>`;
79
+ * - 否则 ⇒ `_weaverMte_<div id=... style=...><p>内容</p></div>`。
80
+ */
81
+ export function wrapSignRemark(raw, cfg) {
82
+ const text = String(raw ?? '').trim();
83
+ if (!text)
84
+ return '_weaverMte_';
85
+ if (text.startsWith('_weaverMte_'))
86
+ return text;
87
+ if (!cfg)
88
+ return `_weaverMte_<p>${escapeText(text)}</p>`;
89
+ const style = `font-family:${cfg.fontFamily};font-size:${cfg.fontSize};`;
90
+ const escaped = escapeText(text);
91
+ if (cfg.plainText) {
92
+ return `<span id="${DEFAULT_FONT_ID}" style="${style}">${escaped}</span>`;
93
+ }
94
+ return `_weaverMte_<div id="${DEFAULT_FONT_ID}" style="${style}"><p>${escaped}</p></div>`;
95
+ }
96
+ /**
97
+ * 提交侧的入口:**只在真有意见、且还没带富文本前缀时**才去问样式接口。
98
+ *
99
+ * 没有意见 / 已经是 `_weaverMte_` 富文本 ⇒ 一次请求都不发(这两档包与不包结果一样)。
100
+ */
101
+ export async function resolveSignRemark(env, raw, target) {
102
+ const text = String(raw ?? '').trim();
103
+ if (!text || text.startsWith('_weaverMte_'))
104
+ return wrapSignRemark(raw, null);
105
+ return wrapSignRemark(text, await loadSignInputCfg(env, target));
106
+ }
@@ -5,6 +5,7 @@ import { defaultTabIdOf, rethrowAuthOrNetworkError, runWorkflowSearch } from './
5
5
  import { CONTINUATION_INPUT, SCHEMA_CATEGORY, SCHEMA_CONFIRM, SCHEMA_INDEX, SCHEMA_REQUEST_ID, schemaOf, workflowSuccess, } from './types.js';
6
6
  import { asInt, asString, asStringArray, compact, responseData, detailDataOf, toArray, resolveMemoryAppId } from './shared.js';
7
7
  import { WORKFLOW_ENDPOINTS } from '../endpoints.js';
8
+ import { resolveSignRemark } from './sign-input.js';
8
9
  import { findIndexedItem, loadSearchIndex, } from '../state.js';
9
10
  // ===========================================================================
10
11
  // 工作流写操作(operate / share / batch / memory)
@@ -1013,7 +1014,14 @@ const workflowOperateApply = {
1013
1014
  verification,
1014
1015
  });
1015
1016
  }
1016
- const payload = await withUserId(env, { requestId, remark });
1017
+ // 提交前先按租户配置把签字意见包成带默认字体/字号的形态(已有流程 ⇒ `isCreate:0` + `requestId`)。
1018
+ // 取不到样式时 `resolveSignRemark` 内部已退回原形态,不会阻断提交。
1019
+ // **没意见就原样传空串**:这条命令历史上空意见就是 `''`(不像发起侧用 `_weaverMte_` 占位),
1020
+ // 改成占位符会改变提交请求体,没有必要。
1021
+ const signRemark = String(remark ?? '').trim()
1022
+ ? await resolveSignRemark(env, remark, { requestId })
1023
+ : remark;
1024
+ const payload = await withUserId(env, { requestId, remark: signRemark });
1017
1025
  if (nodeId)
1018
1026
  payload.userCurrentNodeId = nodeId;
1019
1027
  if (prepared.requestName)
@@ -25,7 +25,15 @@
25
25
  * 两边都正向引用,依赖图仍是单向的。
26
26
  */
27
27
  export const COMPACT_STDOUT_PATHS = {
28
- 'workflow.createForm': ['data.fields', 'data.historyRequestData', 'data.systemFields'],
28
+ // `data.data`(智能填参算出的字段值)与 `fields` 同构:键是字段 id、层级深,压掉缩进省得可观。
29
+ // 它与 `historyRequestData` / `systemFields` **互斥出现**(智能体成功时后两者已被删),
30
+ // 三个都登记在这里不会互相干扰。
31
+ 'workflow.createForm': [
32
+ 'data.fields',
33
+ 'data.historyRequestData',
34
+ 'data.systemFields',
35
+ 'data.data',
36
+ ],
29
37
  };
30
38
  /** 该 operation 的紧凑路径(不声明 = 维持全量缩进)。导出让用例锁住接线。 */
31
39
  export function compactStdoutPaths(operation) {
@@ -0,0 +1,79 @@
1
+ # 泛微商务合同档案查询 CLI
2
+
3
+ 来源 Skill:`weaver-e10-archive-oa`。本模块把接口文档中可安全自动化的能力封装为 `weaver-work-cli archive-oa` 命令,供 AI Agent 通过结构化 JSON 调用。
4
+
5
+ 源码位于 `src/shortcuts/archive-oa`。命令注册、`operation manifest` 和业务执行逻辑分离:manifest 由 `operations/registry.ts` 汇总各 operation 定义生成,具体执行逻辑在 `src/shortcuts/archive-oa/operations` 中按业务分组维护(档案定位、合同查询、借阅车、发起借阅)。
6
+
7
+ 面向 Agent 的说明位于 `skills/weaver-e10-archive-oa`。入口 `SKILL.md` 只负责共享规则和 reference 路由;具体细节拆在 `skills/weaver-e10-archive-oa/references/`,与飞书 CLI 的业务 Skill 组织方式保持一致。
8
+
9
+ ## 公共前置
10
+
11
+ 先通过 E10 鉴权模块登录:
12
+
13
+ ```text
14
+ weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --agent_type "<当前智能体名称>"
15
+ weaver-work-cli auth status --no-check
16
+ ```
17
+
18
+ 每个业务请求由 CLI 托管:
19
+
20
+ 1. 读取当前 `weaver-work-cli auth` Profile 的 E10 会话。
21
+ 2. 请求只允许 `/api/`(ebuilder 业务接口)与 `/openserver/`(E10 开放服务)路径,由 CLI 携带 E10 Cookie/ETEAMSID 与必要的业务头。
22
+ 3. 响应按以下规则判定业务成功:ebuilder 列表/ESB 看 `code=200` 与 `status=true`;ebuilder 自定义接口看 `datajson.status`。
23
+
24
+ Agent 不得在业务 JSON 中传 Cookie、ETEAMSID、Token 或内部控制字段。
25
+
26
+ ## Agent 入口
27
+
28
+ 优先使用 `archive-oa run <operation>`:
29
+
30
+ Windows PowerShell:
31
+
32
+ ```powershell
33
+ weaver-work-cli archive-oa schema
34
+ weaver-work-cli --json archive-oa run archive-oa.contract.list --input-json '{"pageNo":1}'
35
+ ```
36
+
37
+ macOS/Linux(bash/zsh):
38
+
39
+ ```bash
40
+ weaver-work-cli archive-oa schema
41
+ weaver-work-cli --json archive-oa run archive-oa.contract.list --input-json '{"pageNo":1}'
42
+ ```
43
+
44
+ 复杂或多行 JSON:先写成 UTF-8 文件,再用 `--input <path>`(`--input -` 读 stdin)。不要存在 `~/.tmp` 之类的路径假设;Windows 用 `$env:TEMP`,macOS/Linux 用临时目录。
45
+
46
+ ## operation 划分
47
+
48
+ | 分组 | operation |
49
+ | --- | --- |
50
+ | 档案定位 | `archive-oa.archive.locate` |
51
+ | 泛微合同 | `archive-oa.contract.list`、`archive-oa.contract.fields` |
52
+ | 泛微借阅车 | `archive-oa.borrow-car.list`、`archive-oa.borrow-car.add.prepare`、`archive-oa.borrow-car.add.apply` |
53
+ | 契约锁合同 | `archive-oa.qys.contract.list` |
54
+ | 契约锁借阅车 | `archive-oa.qys.borrow-car.list`、`archive-oa.qys.borrow-car.add.prepare`、`archive-oa.qys.borrow-car.add.apply` |
55
+
56
+ | 发起借阅(生成流程页 URL) | `archive-oa.borrow.start.url`、`archive-oa.qys.borrow.start.url` |
57
+
58
+ **发起借阅由 `archive-oa.borrow.start.url` 生成流程创建页 URL**:`<baseUrl>/sp/workflow/flowpage/fullCreate/100003460000002090?fb=<fb>&data=<JSON>`,`data` 为 `[{hth, htmc, yfhd, fph}]`。该 operation 只读:不写数据、不触发流程、不需要后台应用凭据;URL 可直接在浏览器打开,表单由用户填写并提交。已下线的 `archive-oa.borrow.start.prepare` / `.apply`(OpenESB webhook)不再提供。
59
+
60
+ ## 两套链路的参数差异
61
+
62
+ | 项 | 泛微商务合同 | 契约锁协作采纳 |
63
+ | --- | --- | --- |
64
+ | 合同查询 `passid` / `listId` | `850741972510941185` / `850741985437786120` | `850742853046345728` / `1190611964371984450` |
65
+ | 借阅车 `listId` | `882393480417296390` | `883819516961857839` |
66
+ | 加入借阅车 `esbActionIds` | `882395911377174530` | `892706856573648911` |
67
+
68
+ ## 写操作确认协议
69
+
70
+ - `*.prepare`:只读回查与参数校验(加入借阅车会查一次借阅车现状),返回 `preview` 与 `continuation`。
71
+ - `*.apply`:必须带 `continuation` 且 `confirm: true`(CLI `--yes`);票据含环境与登录账号指纹,10 分钟有效。
72
+ - 发起借阅用 `archive-oa.borrow.start.url` 生成流程页 URL(只读,`risk: read`):生成后优先用系统默认浏览器打开,无法打开时原样输出完整 URL;流程表单由用户在浏览器确认并提交。OpenESB 版 `borrow.start.prepare` / `.apply` 已下线。
73
+ - `failNum > 0` 时返回 `partial / write_uncertain`,不自动重放。
74
+
75
+ ## 参考
76
+
77
+ - `skills/weaver-e10-archive-oa/SKILL.md`:Agent 路由与执行原则。
78
+ - `skills/weaver-e10-archive-oa/references/`:按意图拆分的用法说明。
79
+ - `skill-template/domains/archive-oa.md`:业务域 Agent 调优要点。
package/docs/e10-auth.md CHANGED
@@ -57,18 +57,19 @@ Chrome/Edge 原生确认框。宿主需要返回特定会话时,可以传
57
57
  返回地址只接受桌面应用自定义协议,不接受 `http(s)`、脚本或本地文件 URL。“始终打开”
58
58
  偏好保存在当前本地回调地址的浏览器存储中。本地 CLI 打开通道默认保留 15 分钟;通道
59
59
  结束后页面会自动退回浏览器自定义协议打开方式,不直接显示网络请求错误。
60
- 智能小 E 统一登录成功时不会经过浏览器;只有统一登录不可用并降级为桌面浏览器 OAuth 时,
61
- 才显示“返回智能小 E”弹层。
60
+ 智能小 E 统一登录不会经过浏览器,也不会显示“返回智能小 E”弹层;该弹层只属于用户另行显式发起的 `auth login` 浏览器 OAuth。
62
61
 
63
- 只有当前智能体身份明确为 XiaoE 时,`auth xiaoe` 和未显式指定认证来源的业务命令才先使用统一登录。Web/沙箱宿主可由成对的 `XIAOE_USER_TOKEN` + `XIAOE_DOMAIN` 识别;桌面客户端则可由其精确注入的 `XIAOE_DATA_ROOT`、`XIAOE_ROBOT_DATA_ROOT`、`XIAOE_USER_SETTINGS_PATH`、`XIAOE_ROBOT_ROOT` 运行时路径标记识别。通用 `WORKBUDDY_*` / `CODEX_*` 变量可能来自 XiaoE 复用的智能体运行时,不能覆盖这些 XiaoE 信号;明确的 `WORKBUDDY_AGENT_TYPE` / `CODEX_AGENT_TYPE` 非 XiaoE 身份除外。普通 `XIAOE_SKILL_*`、`WEAVER_XIAOE_*` 配置或仅在磁盘上发现客户端文件仍不足以触发。这里的 `XIAOE_DOMAIN` 是 XiaoE/erpa 换票服务地址,不是 E10 登录地址。Node 主路径调用与 `xiaoe_env.py` / `tokens.py` 相同的接口,校验业务 `code`(`"0000"` 或 `200`)及 `data.eteamId`,保留系统 CA 校验,并只跟随同源重定向或同主机 HTTP→HTTPS 升级。Node 路径失败时,CLI 会在内部执行 npm 包自带的 `scripts/xiaoe_env.py` 兼容路径;它只依赖 Python 标准库,凭证 stdout 由 CLI 内部消费,不进入 Agent 上下文,也不会关闭 TLS 证书校验。之后才依次读取宿主 `xiaoeUserData/tokens.py`、桌面 XiaoE 客户端本地会话和 `WEAVER_XIAOE_*` ETEAMSID 映射。客户端会话优先读取 `XIAOE_USER_SETTINGS_PATH`,再从 `XIAOE_ROBOT_DATA_ROOT` / `XIAOE_ROBOT_ROOT` 查找,最后回退 macOS `~/Library/Application Support/XiaoE/Robots/<id>/user_settings.json` 或 Windows `%LOCALAPPDATA%/XiaoE/Robots/<id>/user_settings.json`;只要其中的 `session_id` 与 `erpa` 有效,Node 即可在没有 `tokens.py` 时直接换票。宿主不会注入 ETEAMSID 环境变量,CLI 也不读取 `XIAOE_ETEAMSID`。
62
+ XiaoE 宿主身份有两种判定入口:桌面客户端由 Agent 根据“智能小 e 客户端”“小 E”、`XiaoE`、`xiaoe-client` 等明确上下文判断,并传入 `--runtime-agent xiaoe`;网页端沿用原有环境变量规则,成对的 `XIAOE_USER_TOKEN` + `XIAOE_DOMAIN` 会被识别为 XiaoE。工作目录、客户端文件、内置 Node.js、历史登录、路径变量或单个凭据变量都不能单独用于判定。判定为 XiaoE 后,Agent 显式执行 `weaver-work-cli auth xiaoe`。
64
63
 
65
- E10 目标地址按“显式 `--base-url` → 专用 `WEAVER_XIAOE_*` E10 配置/映射 → 当前 profile → `https://www.e-cology.com.cn`”解析,绝不会把 `XIAOE_DOMAIN` 当作 E10 地址。凭证可用时,CLI 直接保存登录态,保存的 `agentType` 固定为 `XiaoE`;业务命令在未显式指定 `--auth` / `--profile` 时,也会自动优先尝试 XiaoE 统一登录。统一登录失败后,`auth xiaoe` 仅在 macOS/Windows 桌面宿主自动降级浏览器 OAuth;Web/Linux 环境直接返回 `authentication/xiaoe_web_credential_unavailable`,不再启动无法回调的 localhost 登录。用户明确希望浏览器授权且宿主能够接收本地回调时,可以显式运行 `auth login`。
64
+ CLI 依次尝试成对的平台凭据、npm 包自带的 `scripts/xiaoe_env.py` 兼容路径、宿主 `xiaoeUserData/tokens.py`、桌面 XiaoE 客户端本地会话和 `WEAVER_XIAOE_*` ETEAMSID 映射。客户端会话优先读取 `XIAOE_USER_SETTINGS_PATH`,再从 `XIAOE_ROBOT_DATA_ROOT` / `XIAOE_ROBOT_ROOT` 查找,最后回退 macOS `~/Library/Application Support/XiaoE/Robots/<id>/user_settings.json` 或 Windows `%LOCALAPPDATA%/XiaoE/Robots/<id>/user_settings.json`。这些变量和文件只用于定位凭据,不能用于识别宿主。`XIAOE_DOMAIN` 是 XiaoE/erpa 换票服务地址,不是 E10 登录地址。
65
+
66
+ E10 目标地址按“显式 `--base-url` → 专用 `WEAVER_XIAOE_*` E10 配置/映射 → 当前 profile → `https://www.e-cology.com.cn`”解析。凭证可用时,CLI 直接保存登录态,保存的 `agentType` 固定为 `XiaoE`。统一登录失败时,所有操作系统都返回 `authentication/xiaoe_credential_unavailable`,不启动浏览器 OAuth。需要浏览器授权时必须另行显式执行 `auth login`。
66
67
 
67
68
  脚本兼容链路会先自动发现桌面 XiaoE 客户端内置 Python(macOS 应用包与 Windows 按用户/全局安装均支持),再回退到系统 `python3`、`/usr/bin/python3`、`python`;Windows 的系统回退顺序为 `python`、`python3`、`py`。可用 `WEAVER_XIAOE_TOKEN_SCRIPT_PYTHON` 精确指定解释器,用 `WEAVER_XIAOE_TOKEN_SCRIPT_PATH` 指定桌面宿主脚本路径(旧名 `XIAOE_TOKEN_SCRIPT_PYTHON` / `XIAOE_TOKEN_SCRIPT_PATH` 仍兼容)。CLI 自带的 `xiaoe_env.py` 位于 npm 包 `scripts/`,不会放入 `weaver-e10-shared` Skill;Skill 包禁止携带或直接执行 Token 脚本。`XIAOE_` 是平台保留前缀,本 CLI 的自定义变量统一使用 `WEAVER_XIAOE_*`。
68
69
 
69
- `auth xiaoe` 会先打印不含凭证的 preflight 摘要,明确给出 `hostDetected`、`attempted`、`tokenScriptDetected`、`clientSettingsDetected` 和目标 `baseUrl`。进入脚本分支时会打印 `Trying XiaoE token script credential...`;进入内置 Node 会话分支时会打印 `Trying built-in XiaoE client session credential...`。成功结果和后续 `auth status` 会显示 `credentialSourceStep`、`credentialSourceType`、`credentialSourceLabel`;`2 / token_script / xiaoeUserData/tokens.py` 表示宿主脚本换票,`3 / xiaoe_client / XiaoE client settings` 表示没有依赖 `tokens.py`、由 Node 直接换票。
70
+ `auth xiaoe` 会先打印不含凭证的 preflight 摘要,给出 `authMode=xiaoe`、`credentialSourceDetected`、`attempted`、各凭据源检测结果和目标 `baseUrl`。进入脚本分支时会打印 `Trying XiaoE token script credential...`;进入内置 Node 会话分支时会打印 `Trying built-in XiaoE client session credential...`。成功结果和后续 `auth status` 会显示 `credentialSourceStep`、`credentialSourceType`、`credentialSourceLabel`。
70
71
 
71
- 上述“业务命令自动优先尝试”仅适用于当前智能体身份明确为 XiaoE 的进程。WorkBuddy、Codex 及其他智能体不会因为本机存在 XiaoE 文件或配置而进入该分支,并必须使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
72
+ 业务命令不会自动建立 XiaoE 登录态。桌面客户端需要 XiaoE 专属业务行为时,Agent 必须显式传全局参数,例如 `weaver-work-cli --runtime-agent xiaoe workflow ...`;网页端由原有环境变量规则自动识别,不要求补这个参数。`--runtime-agent` 只控制运行时行为,不携带也不选择凭据。WorkBuddy、Codex 及其他智能体使用 `auth login --base-url <url> --agent_type <agent_type>` 的浏览器 OAuth 流程。
72
73
 
73
74
  无法使用浏览器回调时,可以用 `auth set` 直接写入浏览器中复制到的
74
75
  `ETEAMSID`。命令会调用 `teamsCheck` 解析当前用户、租户和组织信息,并写入
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weaver-work-cli",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "面向泛微工作工具链的可扩展 TypeScript CLI 基础框架(E10 业票通等能力)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -454,5 +454,21 @@
454
454
  "审批进度",
455
455
  "审批意见",
456
456
  "今承达"
457
+ ],
458
+ "weaver-e10-archive-oa": [
459
+ "档案定位",
460
+ "合同号定位",
461
+ "泛微合同",
462
+ "契约锁",
463
+ "商务合同查询",
464
+ "合同查询",
465
+ "借阅车",
466
+ "加入借阅车",
467
+ "发起借阅",
468
+ "案例",
469
+ "契约锁案例",
470
+ "合同借阅",
471
+ "档案记录",
472
+ "合同编号"
457
473
  ]
458
474
  }
@@ -0,0 +1,19 @@
1
+ 本业务域为「泛微商务合同档案查询」:E10 档案定位与 OA 商务合同借阅。入口 `SKILL.md` 按飞书 `lark-approval` 风格组织:CRITICAL 共享前置、路由优先级、逐 operation 命令表、处理链、执行原则、写操作失败决策树和「不在范围」。具体用法拆到 `references/`:
2
+
3
+ - `archive-locate.md`:按合同号定位档案(`htbh` 必须放 body 顶层)。
4
+ - `contract-search.md`:泛微商务合同查询与可搜索字段配置(`commonSearchFields` 构造规则)。
5
+ - `qys-contract-search.md`:契约锁协作采纳(案例)合同查询(默认只查已归档)。
6
+ - `borrow-car.md`:借阅车列表;加入借阅车是临时铺垫操作,`prepare -> apply` **免用户确认、直接执行**。
7
+ - `borrow-flow.md`:发起借阅的职责边界(用户在浏览器登录后自己操作)与给用户的指引。
8
+ - `disabled-capabilities.md`:接口未收录与必须由用户在界面完成的操作边界。
9
+
10
+ Agent 调优重点:
11
+
12
+ - `weaver-work-cli archive-oa schema` 是 operation 合约第一信息源。
13
+ - 业务输入不得包含 Cookie、ETEAMSID、Token 或内部控制字段;**禁止向用户索要后台应用凭据**(如 OpenESB 的 appKey / corpid / appSecret),交互中不暴露此类概念。
14
+ - 泛微商务合同与契约锁协作采纳是两套独立应用:`passid` / `listId` / `esbActionIds` / `objId` 与流程页 `fb` 均不同,禁止交叉混用。
15
+ - 合同行 ID(`data[*].id`)与档案记录 ID 只作为后续 operation 入参,不展示给最终用户。
16
+ - 列表结果**只查 `pageNo=1`、禁止翻页**(不得请求 `pageNo≥2`,不循环翻页累加),单次最多展示 20 条;需要更多数据时提示用户缩小筛选条件,不得翻页获取。
17
+ - 写操作只接受 CLI 生成的 continuation,不手工构造或复用;**加入借阅车每次最多 6 条,且免用户确认**(临时铺垫操作,只为后续发起借阅做准备,不产生最终业务结果,直接 prepare → apply 后事后回报)。发起借阅不在 CLI 能力内,由用户在界面完成。
18
+ - 发起借阅用 `archive-oa.borrow.start.url` 生成流程页 URL(`fullCreate/<flowId>?fb=<fb>&data=<JSON>`,只读、无需后台凭据):优先用系统默认浏览器直接打开,打不开时原样输出完整 URL 让用户手动打开;表单填写与提交由用户完成,不代填、不代提交。
19
+ - 结果为 `partial/write_uncertain` 时停止,不自动重放写请求。
@@ -13,7 +13,7 @@
13
13
  - `invoice-add.md`:手工新增发票的 `prepare -> apply` 写入协议。
14
14
  - `invoice-update.md`:编辑发票的 `prepare -> apply` 写入协议。
15
15
  - `invoice-delete.md`:删除发票的 `prepare -> apply` 写入协议。
16
- - `invoice-reim.md`:发票报销自动填单的查询、组装和报销单创建/更新确认链。
16
+ - `invoice-reim.md`:发票报销自动填单的数据准备;报销单创建/修改调用 `weaver-e10-workflow` 的 `workflow.createForm` / `workflow.createRequest` / `workflow.edit*`。
17
17
  - `invoice-issuing.md`:智能开票或存台账的开票 payload 确认链。
18
18
  - `invoice-disabled-capabilities.md`:共享、转让、标签等暂未暴露能力的处理边界。
19
19
 
@@ -23,7 +23,7 @@ Agent 调优重点:
23
23
  - 业务输入不得包含 Cookie、ETEAMSID、Token 或内部控制字段。
24
24
  - 写操作只接受 CLI 生成的 continuation,不手工构造或复用。
25
25
  - `validate=false` 只能说明跳过查验,不能描述为查验通过。
26
- - 报销单创建、报销单更新、开票和存台账必须先展示结构化 JSON 摘要并取得明确确认;报销单创建字段在 operation 顶层逐项传入,不包通用 `payload`。
26
+ - 报销单创建、报销单修改、开票和存台账必须先展示摘要并取得明确确认;报销单按 `workflow.createRequest` 的 `{main, details}` 结构组装 `data`。
27
27
  - 开票前从用户输入、附件或对话上下文提取购方字段;当前资料包没有客户档案查询 operation,资料不足时追问用户补齐。
28
28
  - 当前资料包没有红字发票红冲接口,`invoice.red.*` 不属于可用 operation。
29
29
  - 结果为 `partial/write_uncertain` 时停止,不自动重放写请求。