weaver-work-cli 0.1.31 → 0.1.33

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 (186) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/{archive-U766FTFH.js → archive-FVFY6GZN.js} +2 -2
  3. package/dist/chunks/{archive-oa-DMWEKX3L.js → archive-oa-NW3OD4UV.js} +2 -2
  4. package/dist/chunks/{asset-TCR7VDEV.js → asset-7QJKMIDV.js} +2 -2
  5. package/dist/chunks/{attachment-content-AQNX5N3S.js → attachment-content-OSAVA6K3.js} +3 -2
  6. package/dist/chunks/{blog-EVZWGDVM.js → blog-I7SDPNW4.js} +2 -2
  7. package/dist/chunks/{caizhilian-JOWS6SL5.js → caizhilian-O37ZIQNY.js} +2 -2
  8. package/dist/chunks/{calendar-5WNCV66Q.js → calendar-7KXE4ED3.js} +2 -2
  9. package/dist/chunks/{chunk-VZTXRG6X.js → chunk-2FLWXFVS.js} +4 -4
  10. package/dist/chunks/{chunk-QWY6PGL6.js → chunk-2LV74IWP.js} +361 -56
  11. package/dist/chunks/chunk-3SO6QL6U.js +73 -0
  12. package/dist/chunks/{chunk-H3WDBYJW.js → chunk-6ATGWRZM.js} +6 -4
  13. package/dist/chunks/chunk-BYATLLEF.js +68 -0
  14. package/dist/chunks/chunk-BYBSCN4X.js +932 -0
  15. package/dist/chunks/{chunk-324YPTC2.js → chunk-D223PTXU.js} +11 -2
  16. package/dist/chunks/{chunk-R2IJAK7T.js → chunk-EEWPQE3O.js} +2 -2
  17. package/dist/chunks/{chunk-J7RJTZSV.js → chunk-GFPXQIY2.js} +2 -2
  18. package/dist/chunks/{chunk-AC7KJWC5.js → chunk-HEL5XTYI.js} +12 -6
  19. package/dist/chunks/{chunk-CVZ2JOSW.js → chunk-IHUYP6LO.js} +1 -1
  20. package/dist/chunks/{chunk-R7O5PUWO.js → chunk-ILMBNVSG.js} +2 -2
  21. package/dist/chunks/{chunk-6V7ZLF34.js → chunk-KAS45KNR.js} +1 -1
  22. package/dist/chunks/{chunk-H6XSTSLM.js → chunk-MEEFGWTM.js} +54 -196
  23. package/dist/chunks/{chunk-GCG2NMWR.js → chunk-MQ2I5AA3.js} +4 -2
  24. package/dist/chunks/chunk-NHA62QD7.js +774 -0
  25. package/dist/chunks/{chunk-2DUV44UX.js → chunk-OCU4RLHC.js} +641 -309
  26. package/dist/chunks/{chunk-65QMVWHS.js → chunk-PRHJGPMA.js} +2 -2
  27. package/dist/chunks/{chunk-WHD4XMRP.js → chunk-Q6LWB5HX.js} +2 -2
  28. package/dist/chunks/{chunk-D4LTJSJD.js → chunk-RBGOMK4Y.js} +50 -33
  29. package/dist/chunks/chunk-VBCFFFGT.js +226 -0
  30. package/dist/chunks/{ebuilder-form-NJ4YK6CC.js → chunk-WRRJ4GG2.js} +22 -1148
  31. package/dist/chunks/{chunk-RCGYCOMG.js → chunk-WY74UBJ6.js} +264 -750
  32. package/dist/chunks/conflict-SFNEVJY5.js +21 -0
  33. package/dist/chunks/continuation-RN6PZN2M.js +11 -0
  34. package/dist/chunks/cowork-CKWYHXYB.js +2508 -0
  35. package/dist/chunks/ebuilder-form-KOLJRVZD.js +121 -0
  36. package/dist/chunks/{ehr-MMYN36VA.js → ehr-WFOC3B4D.js} +1174 -20
  37. package/dist/chunks/{esb-HIWHOU7Q.js → esb-LJZTN663.js} +4 -4
  38. package/dist/chunks/{fna-KBACQTA5.js → fna-F7QRIIZC.js} +2 -2
  39. package/dist/chunks/{host-3OHS5QUX.js → host-BCGUVEWX.js} +3 -3
  40. package/dist/chunks/host-BQBRHVR3.js +23 -0
  41. package/dist/chunks/{host-KQL5N4HO.js → host-DGE2TZD4.js} +3 -3
  42. package/dist/chunks/{host-LLSZQCWR.js → host-KXPTEWFS.js} +3 -3
  43. package/dist/chunks/{host-IG6KEH5Q.js → host-VQ6JJXGQ.js} +3 -3
  44. package/dist/chunks/{hrm-LYEC67ZQ.js → hrm-NI6XAKW5.js} +3 -3
  45. package/dist/chunks/{im-RN2UKAEU.js → im-7TRTF3KT.js} +88 -12
  46. package/dist/chunks/{invoice-EEPHLAF6.js → invoice-AOXWJKYT.js} +3 -3
  47. package/dist/chunks/{jiuchuanhui-WVVZYSJE.js → jiuchuanhui-7NQWEZAW.js} +2 -2
  48. package/dist/chunks/{lazy-dispatch-MDOFFL2S.js → lazy-dispatch-RNS4EE4S.js} +4 -4
  49. package/dist/chunks/{mail-LNQLTF6L.js → mail-VDHJAB4O.js} +3 -3
  50. package/dist/chunks/{meeting-XSD3BDVQ.js → meeting-GA7XCNNP.js} +214 -43
  51. package/dist/chunks/{okr-TMWTIU2K.js → okr-TQIRERPQ.js} +2 -2
  52. package/dist/chunks/operations-5QL3AECO.js +11 -0
  53. package/dist/chunks/{plan-A2FSFA7T.js → plan-4RY4LN2P.js} +2 -2
  54. package/dist/chunks/{project-T26QLBQF.js → project-D6XT2OC5.js} +2 -2
  55. package/dist/chunks/{registry-JCFEYXOE.js → registry-JHRIDQ3A.js} +4 -4
  56. package/dist/chunks/{registry-2VLUAESA.js → registry-OZSMDVPP.js} +2 -2
  57. package/dist/chunks/{registry-ZJMJVWFA.js → registry-QKMI43Q6.js} +8 -6
  58. package/dist/chunks/{registry-Q7OQJ3SU.js → registry-VOGKFFTR.js} +2 -2
  59. package/dist/chunks/{room-recommend-UR4ZDZS6.js → room-recommend-D4EUOOWY.js} +4 -3
  60. package/dist/chunks/{schedule-5L2PM4M6.js → schedule-XPFBHPCV.js} +4 -3
  61. package/dist/chunks/{workflow-TR6PG2SL.js → workflow-HLBZRWIY.js} +172 -6
  62. package/dist/core/package-paths.js +16 -2
  63. package/dist/index.js +30 -25
  64. package/dist/internal/e10/auth-recovery.js +14 -4
  65. package/dist/shortcuts/contract/host.js +34 -59
  66. package/dist/shortcuts/cowork/continuation.js +70 -0
  67. package/dist/shortcuts/cowork/errors.js +59 -0
  68. package/dist/shortcuts/cowork/host.js +172 -0
  69. package/dist/shortcuts/cowork/index.js +166 -0
  70. package/dist/shortcuts/cowork/manifest.js +65 -0
  71. package/dist/shortcuts/cowork/operations/discuss-write.js +277 -0
  72. package/dist/shortcuts/cowork/operations/item-write.js +380 -0
  73. package/dist/shortcuts/cowork/operations/member-write.js +283 -0
  74. package/dist/shortcuts/cowork/operations/permission-write.js +286 -0
  75. package/dist/shortcuts/cowork/operations/read.js +423 -0
  76. package/dist/shortcuts/cowork/operations/registry.js +39 -0
  77. package/dist/shortcuts/cowork/operations/review-write.js +157 -0
  78. package/dist/shortcuts/cowork/operations/shared.js +215 -0
  79. package/dist/shortcuts/cowork/operations/types.js +24 -0
  80. package/dist/shortcuts/cowork/operations.js +19 -0
  81. package/dist/shortcuts/ehr/host.js +4 -1
  82. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +757 -0
  83. package/dist/shortcuts/ehr/operations/recruit-position.js +367 -0
  84. package/dist/shortcuts/ehr/operations/recruit-stats.js +133 -0
  85. package/dist/shortcuts/ehr/operations/recruit-write.js +8 -16
  86. package/dist/shortcuts/ehr/operations/recruit.js +35 -10
  87. package/dist/shortcuts/ehr/operations/registry.js +6 -0
  88. package/dist/shortcuts/im/diagnostics.js +97 -0
  89. package/dist/shortcuts/im/host.js +58 -31
  90. package/dist/shortcuts/im/index.js +7 -2
  91. package/dist/shortcuts/im/operations/at-mention.js +127 -0
  92. package/dist/shortcuts/im/operations/file-user.js +10 -2
  93. package/dist/shortcuts/im/operations/group-read.js +26 -5
  94. package/dist/shortcuts/im/operations/group-write.js +35 -28
  95. package/dist/shortcuts/im/operations/msg-sync.js +7 -2
  96. package/dist/shortcuts/im/operations/msg-write.js +76 -10
  97. package/dist/shortcuts/im/operations/quote-mention.js +119 -0
  98. package/dist/shortcuts/im/operations/session.js +1 -1
  99. package/dist/shortcuts/im/operations/shared.js +10 -0
  100. package/dist/shortcuts/im/operations/types.js +13 -0
  101. package/dist/shortcuts/index.js +5 -0
  102. package/dist/shortcuts/mail/host.js +4 -1
  103. package/dist/shortcuts/meeting/host.js +27 -34
  104. package/dist/shortcuts/meeting/operations/create.js +4 -1
  105. package/dist/shortcuts/meeting/operations/eb/catalog.js +5 -0
  106. package/dist/shortcuts/meeting/operations/eb/conflict.js +78 -7
  107. package/dist/shortcuts/meeting/operations/eb/create-flow.js +168 -26
  108. package/dist/shortcuts/meeting/operations/eb/factory.js +69 -1
  109. package/dist/shortcuts/meeting/operations/eb/index.js +16 -3
  110. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +14 -3
  111. package/dist/shortcuts/meeting/operations/eb-fallback.js +159 -0
  112. package/dist/shortcuts/meeting/operations/env.js +93 -46
  113. package/dist/shortcuts/meeting/operations/form-fields.js +110 -0
  114. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  115. package/dist/shortcuts/meeting/operations/standard-guard.js +71 -0
  116. package/dist/shortcuts/meeting/operations.js +16 -0
  117. package/dist/shortcuts/workflow/index.js +110 -1
  118. package/dist/shortcuts/workflow/operations/form.js +24 -3
  119. package/dist/shortcuts/workflow/operations/read.js +104 -23
  120. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +448 -0
  121. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.js +106 -0
  122. package/dist/shortcuts/workflow/operations/todoSmartConvert/businessRules.js +47 -0
  123. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +30 -23
  124. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +1 -0
  125. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +1 -0
  126. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +2 -1
  127. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +2 -4
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +138 -181
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +12 -7
  130. package/docs/e10-auth.md +1 -1
  131. package/docs/im.md +8 -1
  132. package/docs/todo-smart-convert/README.md +1 -1
  133. package/docs/todo-smart-convert/data-contract.md +2 -2
  134. package/docs/todo-smart-convert/subgroup-rules.md +6 -0
  135. package/package.json +1 -1
  136. package/skill-template/domains/yimiaoban.md +3 -3
  137. package/skill-template/skill-template.md +1 -1
  138. package/skills/weaver-e10-blog/SKILL.md +5 -5
  139. package/skills/weaver-e10-caizhilian/SKILL.md +5 -5
  140. package/skills/weaver-e10-calendar/SKILL.md +5 -5
  141. package/skills/weaver-e10-esb/SKILL.md +5 -5
  142. package/skills/weaver-e10-hrm/SKILL.md +5 -5
  143. package/skills/weaver-e10-jinchengda/SKILL.md +2 -2
  144. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -5
  145. package/skills/weaver-e10-jucailin/SKILL.md +18 -12
  146. package/skills/weaver-e10-jucailin/references/recruit-create.md +104 -0
  147. package/skills/weaver-e10-mail/SKILL.md +5 -5
  148. package/skills/weaver-e10-meeting/SKILL.md +171 -25
  149. package/skills/weaver-e10-meeting/references/meeting-eb.md +4 -3
  150. package/skills/weaver-e10-meeting/references/meeting-queries.md +9 -6
  151. package/skills/weaver-e10-meeting/references/meeting-verification.md +8 -6
  152. package/skills/weaver-e10-meeting.zip +0 -0
  153. package/skills/weaver-e10-okr/SKILL.md +5 -5
  154. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  155. package/skills/weaver-e10-plan/SKILL.md +5 -5
  156. package/skills/weaver-e10-shared/SKILL.md +12 -12
  157. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -4
  158. package/skills/weaver-e10-wenshuding/SKILL.md +5 -5
  159. package/skills/weaver-e10-workflow/SKILL.md +29 -5
  160. package/skills/weaver-e10-workflow/aigroup/default.json +88 -0
  161. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +66 -14
  162. package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +1 -1
  163. package/skills/weaver-e10-workflow/references/workflow-memory.md +1 -1
  164. package/skills/weaver-e10-workflow/references/workflow-query.md +12 -10
  165. package/skills/weaver-e10-workflow/references/workflow-view.md +2 -2
  166. package/skills/weaver-e10-workflow/tools/todo-serve.cjs +239 -0
  167. package/skills/weaver-e10-workflow/{scripts → tools}/todo-summary.cjs +218 -28
  168. package/skills/weaver-e10-yepiaotong/SKILL.md +10 -8
  169. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +7 -7
  170. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +10 -10
  171. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +40 -37
  172. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -4
  173. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +27 -38
  174. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +4 -4
  175. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +37 -29
  176. package/skills/weaver-e10-yimiaoban/SKILL.md +156 -33
  177. package/skills/weaver-e10-yimiaoban/references/ding-write.md +1 -1
  178. package/skills/weaver-e10-yimiaoban/references/error-codes.md +5 -1
  179. package/skills/weaver-e10-yimiaoban/references/group-read.md +5 -1
  180. package/skills/weaver-e10-yimiaoban/references/group-write.md +6 -4
  181. package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -0
  182. package/skills/weaver-e10-yimiaoban/references/msg-write.md +34 -2
  183. package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +5 -2
  184. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +79 -58
  185. package/skills/weaver-e10-yimiaoban/references/user-remind.md +2 -2
  186. package/skills/weaver-e10-ziguanjia/SKILL.md +5 -5
@@ -2,15 +2,22 @@ import { createRequire as __esbuildCreateRequire } from 'node:module';
2
2
  const require = __esbuildCreateRequire(import.meta.url);
3
3
  import {
4
4
  createWorkflowHost
5
- } from "./chunk-65QMVWHS.js";
5
+ } from "./chunk-PRHJGPMA.js";
6
6
  import {
7
+ USER_INTENT_RULE_FILE,
8
+ aigroupUserDir,
7
9
  asObject,
8
10
  assertNoForbiddenKeys,
9
11
  compactStdoutPaths,
12
+ emptyRuleFile,
10
13
  getWorkflowOperation,
14
+ loadRuleFile,
15
+ loadRules,
16
+ resetActiveRules,
17
+ validateRuleFile,
11
18
  workflowOperationDefinitions,
12
19
  workflowSuccess
13
- } from "./chunk-2DUV44UX.js";
20
+ } from "./chunk-OCU4RLHC.js";
14
21
  import {
15
22
  createWorkflowContinuationSigner
16
23
  } from "./chunk-EZALDOQH.js";
@@ -21,8 +28,8 @@ import {
21
28
  } from "./chunk-FMPJJYYI.js";
22
29
  import "./chunk-EG6ZU444.js";
23
30
  import "./chunk-AJ62BGAW.js";
24
- import "./chunk-H3WDBYJW.js";
25
- import "./chunk-324YPTC2.js";
31
+ import "./chunk-6ATGWRZM.js";
32
+ import "./chunk-D223PTXU.js";
26
33
  import {
27
34
  isCliError,
28
35
  toErrorEnvelope
@@ -129,6 +136,86 @@ async function executeWorkflowOperation(operation, rawInput, context) {
129
136
  return result;
130
137
  }
131
138
 
139
+ // src/shortcuts/workflow/index.ts
140
+ import { join as join2 } from "node:path";
141
+
142
+ // src/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.ts
143
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
144
+ import { join } from "node:path";
145
+ function normalizeIntentPayload(payload) {
146
+ if (Array.isArray(payload)) return payload;
147
+ if (payload && typeof payload === "object") {
148
+ const record = payload;
149
+ if (Array.isArray(record.rules)) return record.rules;
150
+ if (record.workflowName !== void 0) return [record];
151
+ }
152
+ throw workflowValidationError("aigroup_invalid", "\u5165\u53C2\u5FC5\u987B\u662F\u89C4\u5219\u5BF9\u8C61\u3001\u89C4\u5219\u6570\u7EC4\uFF0C\u6216 { rules: [...] }");
153
+ }
154
+ function namesOf(rule) {
155
+ const raw = rule?.workflowName;
156
+ if (typeof raw === "string") return [raw.trim()].filter(Boolean);
157
+ if (Array.isArray(raw)) return raw.map((item) => String(item ?? "").trim()).filter(Boolean);
158
+ return [];
159
+ }
160
+ function saveUserIntents(payload, options = {}) {
161
+ const dir = options.dir ?? aigroupUserDir(options.ctx);
162
+ const warnings = [];
163
+ const entries = normalizeIntentPayload(payload);
164
+ if (entries.length === 0) throw workflowValidationError("aigroup_invalid", "\u5165\u53C2\u91CC\u6CA1\u6709\u89C4\u5219\u6761\u76EE");
165
+ const starred = entries.filter((rule) => namesOf(rule).includes("*"));
166
+ if (starred.length > 0) {
167
+ throw workflowValidationError(
168
+ "aigroup_wildcard_unsupported",
169
+ "\u7528\u6237\u610F\u56FE\u4E0D\u80FD\u4F7F\u7528\u901A\u914D workflowName\u300C*\u300D\uFF1A\u515C\u5E95\u89C4\u5219\u7531 aigroup/default.json \u51B3\u5B9A"
170
+ );
171
+ }
172
+ const check = validateRuleFile({ schemaVersion: 2, defaults: {}, rules: entries });
173
+ if (check.length > 0) {
174
+ throw workflowValidationError("aigroup_invalid", `\u89C4\u5219\u6821\u9A8C\u5931\u8D25\uFF1A${check.join("\uFF1B")}`);
175
+ }
176
+ const file = join(dir, USER_INTENT_RULE_FILE);
177
+ const existing = existsSync(file) ? loadRuleFile(file) : emptyRuleFile();
178
+ if (existing.error) {
179
+ warnings.push(`${existing.error}\uFF1B\u672C\u6B21\u5C06\u91CD\u5EFA\u8BE5\u6587\u4EF6`);
180
+ }
181
+ const byName = /* @__PURE__ */ new Map();
182
+ const replaced = [];
183
+ for (const rule of existing.file.rules) {
184
+ for (const name of namesOf(rule)) byName.set(name, rule);
185
+ }
186
+ for (const rule of entries) {
187
+ const names = namesOf(rule);
188
+ for (const name of names) {
189
+ if (byName.has(name)) replaced.push(name);
190
+ byName.set(name, rule);
191
+ }
192
+ }
193
+ const merged = [...new Set(byName.values())];
194
+ const text = `${JSON.stringify({ schemaVersion: 2, rules: merged }, null, 2)}
195
+ `;
196
+ mkdirSync(dir, { recursive: true, mode: 448 });
197
+ writeFileSync(file, text, { mode: 384 });
198
+ return { file, saved: entries.length, total: merged.length, replaced: [...new Set(replaced)], warnings };
199
+ }
200
+ function readUserIntents(options = {}) {
201
+ const dir = options.dir ?? aigroupUserDir(options.ctx);
202
+ const file = join(dir, USER_INTENT_RULE_FILE);
203
+ if (!existsSync(file)) return { dir, file, exists: false, error: null, rules: [] };
204
+ const loaded = loadRuleFile(file);
205
+ return { dir, file, exists: true, error: loaded.error, rules: loaded.file.rules };
206
+ }
207
+ function clearUserIntents(options = {}) {
208
+ const dir = options.dir ?? aigroupUserDir(options.ctx);
209
+ const file = join(dir, USER_INTENT_RULE_FILE);
210
+ mkdirSync(dir, { recursive: true, mode: 448 });
211
+ writeFileSync(file, `${JSON.stringify({ schemaVersion: 2, rules: [] }, null, 2)}
212
+ `, { mode: 384 });
213
+ return file;
214
+ }
215
+ function readRawUserIntents(file) {
216
+ return readFileSync(file, "utf8");
217
+ }
218
+
132
219
  // src/shortcuts/workflow/index.ts
133
220
  function asOptions(value) {
134
221
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -218,6 +305,43 @@ async function executeAndPrint(ctx, operation, input) {
218
305
  process.exitCode = isCliError(error) ? error.exitCode : 1 /* GeneralError */;
219
306
  }
220
307
  }
308
+ function fieldNamesOf(inputSchema) {
309
+ const properties = inputSchema.properties;
310
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) return [];
311
+ return Object.keys(properties);
312
+ }
313
+ function manifestIndex() {
314
+ return {
315
+ schemaVersion: workflowManifest.schemaVersion,
316
+ name: workflowManifest.name,
317
+ title: workflowManifest.title,
318
+ version: workflowManifest.version,
319
+ host: workflowManifest.host,
320
+ status: workflowManifest.status,
321
+ sourceSkill: workflowManifest.sourceSkill,
322
+ capabilities: workflowManifest.capabilities,
323
+ operationCount: workflowManifest.operations.length,
324
+ operations: workflowManifest.operations.map((definition) => ({
325
+ name: definition.name,
326
+ resource: definition.resource,
327
+ summary: definition.summary,
328
+ ...definition.capability ? { capability: definition.capability } : {},
329
+ risk: definition.risk,
330
+ ...definition.requiresConfirmation ? { requiresConfirmation: true } : {},
331
+ fields: fieldNamesOf(definition.inputSchema)
332
+ })),
333
+ withheldOperations: workflowManifest.withheldOperations,
334
+ operationsHint: "\u9ED8\u8BA4\u53EA\u5217 operation \u4E0E\u5B57\u6BB5\u540D\uFF1B\u5B57\u6BB5\u7C7B\u578B\u3001\u679A\u4E3E\u548C\u586B\u5199\u53E3\u5F84\u7528 `workflow schema --operation <name>` \u5355\u53D6\uFF0C\u6574\u4EFD\u5B8C\u6574 manifest \u7528 `workflow schema --full`\u3002"
335
+ };
336
+ }
337
+ function findOperationContract(name) {
338
+ const full = name.startsWith("workflow.") ? name : `workflow.${name}`;
339
+ const matched = workflowManifest.operations.find((definition) => definition.name === full);
340
+ if (!matched) {
341
+ throw workflowValidationError("operation_unknown", `\u6D41\u7A0B\u6A21\u5757\u4E0D\u652F\u6301\u64CD\u4F5C\uFF1A${name}`);
342
+ }
343
+ return matched;
344
+ }
221
345
  function withJsonInputOptions(command) {
222
346
  command.option("--input <path>", "JSON input file, or - for stdin").option("--input-json <json>", "inline JSON input object");
223
347
  return command;
@@ -228,8 +352,50 @@ var workflowShortcut = {
228
352
  category: "shortcut",
229
353
  register({ program, action }) {
230
354
  const workflow = program.command("workflow").description("\u6CDB\u5FAEE10\u6D41\u7A0B\u667A\u80FD\u52A9\u624B\uFF08\u5F85\u529E/\u5DF2\u529E/\u6211\u53D1\u8D77/\u5168\u90E8\u3001\u8BE6\u60C5\u3001\u64CD\u4F5C\u3001\u6279\u91CF\u3001\u53D1\u8D77\u3001\u4FEE\u6539\uFF09");
231
- workflow.command("schema").description("Print workflow operation manifest for agents").action(action((ctx) => {
232
- ctx.output.write(workflowManifest, { json: true });
355
+ workflow.command("schema").description("Print workflow operation manifest for agents").option("--operation <name>", "print one operation with its full inputSchema").option("--full", "print the whole manifest (about 65 KB; stdout may be trimmed)").action(action((ctx, options) => {
356
+ const opts = asOptions(options);
357
+ const requested = String(opts.operation ?? "").trim();
358
+ if (requested && opts.full === true) {
359
+ throw workflowValidationError("option_conflict", "\u4E0D\u80FD\u540C\u65F6\u4F20 --operation \u548C --full");
360
+ }
361
+ if (opts.full === true) {
362
+ ctx.output.write(workflowManifest, { json: true });
363
+ return;
364
+ }
365
+ if (requested) {
366
+ ctx.output.write(findOperationContract(requested), { json: true });
367
+ return;
368
+ }
369
+ ctx.output.write(manifestIndex(), { json: true });
370
+ }));
371
+ const aigroup = workflow.command("aigroup").description("ai\u4E8C\u6B21\u5206\u7EC4\u89C4\u5219\u5E93\uFF08\u9ED8\u8BA4\u89C4\u5219 + \u7528\u6237\u610F\u56FE\uFF09");
372
+ aigroup.command("show").description("Print effective ai-group rules (default merged with user intents)").option("--raw", "also print user-intents.json as stored on disk").action(action((ctx, options) => {
373
+ const opts = asOptions(options);
374
+ const loaded = loadRules({ ctx });
375
+ const user = readUserIntents({ ctx });
376
+ ctx.output.write({
377
+ dir: loaded.dir,
378
+ defaultFile: join2(loaded.dir, "default.json"),
379
+ userIntentFile: user.file,
380
+ userIntentExists: user.exists,
381
+ userIntentError: user.error,
382
+ defaults: loaded.file.defaults,
383
+ rules: loaded.file.rules,
384
+ userRules: user.rules,
385
+ warnings: loaded.warnings,
386
+ ...opts.raw === true && user.exists ? { rawUserIntents: readRawUserIntents(user.file) } : {}
387
+ }, { json: true });
388
+ }));
389
+ withJsonInputOptions(aigroup.command("set").description("Save user-intent rules: validated, whole-entry replace by workflowName").action(action(async (ctx, options) => {
390
+ const payload = await inputFrom(options);
391
+ const result = saveUserIntents(payload, { ctx });
392
+ resetActiveRules();
393
+ ctx.output.write(result, { json: true });
394
+ })));
395
+ aigroup.command("clear").description("Empty user-intent rules (default.json is never touched)").action(action((ctx) => {
396
+ const file = clearUserIntents({ ctx });
397
+ resetActiveRules();
398
+ ctx.output.write({ cleared: true, file }, { json: true });
233
399
  }));
234
400
  withJsonInputOptions(workflow.command("run").description("Run a workflow operation with JSON input").argument("<operation>", "operation name, e.g. workflow.search")).action(action(async (ctx, operation, options) => {
235
401
  await executeAndPrint(ctx, String(operation), await inputFrom(options));
@@ -1,4 +1,4 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, realpathSync } from 'node:fs';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  /**
@@ -13,13 +13,27 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
13
13
  * 找不到时(`node -e`、被当成库 import)再退回本模块所在目录,最后兜底 cwd。
14
14
  */
15
15
  let cachedRoot;
16
+ function entryDirectory(entry) {
17
+ const absoluteEntry = resolve(entry);
18
+ try {
19
+ // npm exposes package executables through node_modules/.bin symlinks on
20
+ // POSIX. Walking upward from the symlink would find the consumer's
21
+ // package.json, so resolve the installed script before looking for ours.
22
+ return dirname(realpathSync(absoluteEntry));
23
+ }
24
+ catch {
25
+ // Preserve the previous fallback for synthetic or already-removed argv
26
+ // entries used by embedders and tests.
27
+ return dirname(absoluteEntry);
28
+ }
29
+ }
16
30
  export function packageRootDir() {
17
31
  if (cachedRoot)
18
32
  return cachedRoot;
19
33
  const seeds = [];
20
34
  const entry = process.argv[1];
21
35
  if (entry)
22
- seeds.push(dirname(resolve(entry)));
36
+ seeds.push(entryDirectory(entry));
23
37
  seeds.push(dirname(fileURLToPath(import.meta.url)));
24
38
  for (const seed of seeds) {
25
39
  let dir = seed;
package/dist/index.js CHANGED
@@ -7,12 +7,12 @@ import {
7
7
  firstPositionalToken,
8
8
  registerCommands,
9
9
  toCommandMetadata
10
- } from "./chunks/chunk-J7RJTZSV.js";
10
+ } from "./chunks/chunk-GFPXQIY2.js";
11
11
  import "./chunks/chunk-EG6ZU444.js";
12
12
  import "./chunks/chunk-AJ62BGAW.js";
13
- import "./chunks/chunk-R2IJAK7T.js";
14
- import "./chunks/chunk-H3WDBYJW.js";
15
- import "./chunks/chunk-324YPTC2.js";
13
+ import "./chunks/chunk-EEWPQE3O.js";
14
+ import "./chunks/chunk-6ATGWRZM.js";
15
+ import "./chunks/chunk-D223PTXU.js";
16
16
  import "./chunks/chunk-LUSPKZKY.js";
17
17
  import "./chunks/chunk-BUSC6UAE.js";
18
18
 
@@ -29,7 +29,7 @@ function lazyShortcut(definition) {
29
29
  placeholder.allowExcessArguments(true);
30
30
  placeholder.helpOption(false);
31
31
  placeholder.action(async () => {
32
- const { dispatchLazyShortcut } = await import("./chunks/lazy-dispatch-MDOFFL2S.js");
32
+ const { dispatchLazyShortcut } = await import("./chunks/lazy-dispatch-RNS4EE4S.js");
33
33
  await dispatchLazyShortcut(definition, shortcutMetadata());
34
34
  });
35
35
  }
@@ -39,102 +39,107 @@ var builtinShortcuts = [
39
39
  lazyShortcut({
40
40
  name: "invoice",
41
41
  description: "E10 \u4E1A\u7968\u901A\u53D1\u7968\u7BA1\u7406 CLI",
42
- load: async () => (await import("./chunks/invoice-EEPHLAF6.js")).invoiceShortcut
42
+ load: async () => (await import("./chunks/invoice-AOXWJKYT.js")).invoiceShortcut
43
43
  }),
44
44
  lazyShortcut({
45
45
  name: "ehr",
46
46
  description: "\u6CDB\u5FAEE10\u805A\u624D\u6797\uFF08\u4EBA\u4E8B/\u51FA\u52E4/\u85AA\u916C\uFF09CLI",
47
- load: async () => (await import("./chunks/ehr-MMYN36VA.js")).ehrShortcut
47
+ load: async () => (await import("./chunks/ehr-WFOC3B4D.js")).ehrShortcut
48
+ }),
49
+ lazyShortcut({
50
+ name: "cowork",
51
+ description: "\u6CDB\u5FAEE10\u534F\u4F5C\u533A\uFF08\u534F\u4F5C\u4E8B\u9879/\u8BA8\u8BBA/\u5BA1\u6279/\u5171\u4EAB\u6743\u9650\uFF09CLI",
52
+ load: async () => (await import("./chunks/cowork-CKWYHXYB.js")).coworkShortcut
48
53
  }),
49
54
  lazyShortcut({
50
55
  name: "archive",
51
56
  description: "E10 \u6587\u4E66\u5B9A\u6863\u6848\u7BA1\u7406 CLI",
52
- load: async () => (await import("./chunks/archive-U766FTFH.js")).archiveShortcut
57
+ load: async () => (await import("./chunks/archive-FVFY6GZN.js")).archiveShortcut
53
58
  }),
54
59
  lazyShortcut({
55
60
  name: "archive-oa",
56
61
  description: "E10 \u6863\u6848\u5B9A\u4F4D\u4E0E OA \u5546\u52A1\u5408\u540C\u501F\u9605 CLI",
57
- load: async () => (await import("./chunks/archive-oa-DMWEKX3L.js")).archiveOaShortcut
62
+ load: async () => (await import("./chunks/archive-oa-NW3OD4UV.js")).archiveOaShortcut
58
63
  }),
59
64
  lazyShortcut({
60
65
  name: "calendar",
61
66
  description: "\u6CDB\u5FAEE10\u65E5\u7A0B\u7BA1\u7406 CLI\uFF08\u521B\u5EFA/\u67E5\u8BE2/\u66F4\u65B0/\u5220\u9664/\u5B57\u5178\uFF09",
62
- load: async () => (await import("./chunks/calendar-5WNCV66Q.js")).calendarShortcut
67
+ load: async () => (await import("./chunks/calendar-7KXE4ED3.js")).calendarShortcut
63
68
  }),
64
69
  lazyShortcut({
65
70
  name: "fna",
66
71
  description: "\u6CDB\u5FAEE10\u9F50\u4E1A\u6210\u8D39\u63A7\u4E2D\u5FC3\uFF08\u9884\u7B97/\u8D39\u7528\u4E2D\u5FC3/\u501F\u6B3E\u7BA1\u7406/\u53D1\u7968\u6838\u9500\uFF09CLI",
67
- load: async () => (await import("./chunks/fna-KBACQTA5.js")).fnaShortcut
72
+ load: async () => (await import("./chunks/fna-F7QRIIZC.js")).fnaShortcut
68
73
  }),
69
74
  lazyShortcut({
70
75
  name: "hrm",
71
76
  description: "\u6CDB\u5FAEE10\u7EC4\u7EC7\u7BA1\u7406\uFF08\u5458\u5DE5/\u7EC4\u7EC7/\u5C97\u4F4D\u67E5\u8BE2\uFF09CLI",
72
- load: async () => (await import("./chunks/hrm-LYEC67ZQ.js")).hrmShortcut
77
+ load: async () => (await import("./chunks/hrm-NI6XAKW5.js")).hrmShortcut
73
78
  }),
74
79
  lazyShortcut({
75
80
  name: "mail",
76
81
  description: "\u6CDB\u5FAEE10\u90AE\u4EF6\u7BA1\u7406 CLI",
77
- load: async () => (await import("./chunks/mail-LNQLTF6L.js")).mailShortcut
82
+ load: async () => (await import("./chunks/mail-VDHJAB4O.js")).mailShortcut
78
83
  }),
79
84
  lazyShortcut({
80
85
  name: "meeting",
81
86
  description: "\u6CDB\u5FAEE10\u4F1A\u8BAE\u7BA1\u7406 CLI\uFF08\u6807\u51C6\u7248\uFF1A\u67E5\u8BE2/\u51B2\u7A81/\u9884\u7EA6/\u56DE\u6267/\u7B7E\u5230\uFF09",
82
- load: async () => (await import("./chunks/meeting-XSD3BDVQ.js")).meetingShortcut
87
+ load: async () => (await import("./chunks/meeting-GA7XCNNP.js")).meetingShortcut
83
88
  }),
84
89
  lazyShortcut({
85
90
  name: "okr",
86
91
  description: "\u6CDB\u5FAEE10 OKR \u76EE\u6807\u7BA1\u7406 CLI\uFF08\u76EE\u6807\u67E5\u8BE2/\u65B0\u5EFA/\u7F16\u8F91\u3001\u5173\u952E\u6210\u679C\u3001\u76EE\u6807\u5BF9\u9F50\u3001\u8BC4\u8BBA\uFF09",
87
- load: async () => (await import("./chunks/okr-TMWTIU2K.js")).okrShortcut
92
+ load: async () => (await import("./chunks/okr-TQIRERPQ.js")).okrShortcut
88
93
  }),
89
94
  lazyShortcut({
90
95
  name: "plan",
91
96
  description: "\u6CDB\u5FAEE10\u8BA1\u5212\u62A5\u544A CLI\uFF08\u67E5\u8BE2/\u65B0\u5EFA/\u4FEE\u6539/\u53D1\u5E03/\u64A4\u56DE/\u63D0\u4EA4\u63D0\u9192/\u8BC4\u8BBA\uFF09",
92
- load: async () => (await import("./chunks/plan-A2FSFA7T.js")).planShortcut
97
+ load: async () => (await import("./chunks/plan-4RY4LN2P.js")).planShortcut
93
98
  }),
94
99
  lazyShortcut({
95
100
  name: "esb",
96
101
  description: "\u6CDB\u5FAEE10 ESB \u4E2D\u5FC3\u81EA\u5B9A\u4E49\u89E6\u53D1\u52A8\u4F5C\u6D41 CLI",
97
- load: async () => (await import("./chunks/esb-HIWHOU7Q.js")).esbShortcut
102
+ load: async () => (await import("./chunks/esb-LJZTN663.js")).esbShortcut
98
103
  }),
99
104
  lazyShortcut({
100
105
  name: "ebuilder-form",
101
106
  description: "\u6CDB\u5FAEE10 eBuilder \u8868\u5355\u83DC\u5355\u3001\u5217\u8868\u548C\u8868\u5355\u6570\u636E CLI",
102
- load: async () => (await import("./chunks/ebuilder-form-NJ4YK6CC.js")).ebuilderFormShortcut
107
+ load: async () => (await import("./chunks/ebuilder-form-KOLJRVZD.js")).ebuilderFormShortcut
103
108
  }),
104
109
  lazyShortcut({
105
110
  name: "jiuchuanhui",
106
111
  description: "\u6CDB\u5FAEE10\u4E5D\u6C1A\u6C47\u8425\u9500\u7BA1\u7406 CLI\uFF08\u5BA2\u6237/\u8054\u7CFB\u4EBA/\u5546\u673A/\u7EBF\u7D22/\u8054\u7CFB\u8BA1\u5212/\u516C\u6D77\uFF09",
107
- load: async () => (await import("./chunks/jiuchuanhui-WVVZYSJE.js")).jiuchuanhuiShortcut
112
+ load: async () => (await import("./chunks/jiuchuanhui-7NQWEZAW.js")).jiuchuanhuiShortcut
108
113
  }),
109
114
  lazyShortcut({
110
115
  name: "asset",
111
116
  description: "E10 \u8D44\u4EA7\u7BA1\u7406\uFF08\u8D44\u7BA1\u5BB6\uFF09CLI",
112
- load: async () => (await import("./chunks/asset-TCR7VDEV.js")).assetShortcut
117
+ load: async () => (await import("./chunks/asset-7QJKMIDV.js")).assetShortcut
113
118
  }),
114
119
  lazyShortcut({
115
120
  name: "im",
116
121
  description: "\u6CDB\u5FAEE10\u6613\u79D2\u529E\uFF08\u5373\u65F6\u901A\u8BAF\uFF09CLI",
117
- load: async () => (await import("./chunks/im-RN2UKAEU.js")).imShortcut
122
+ load: async () => (await import("./chunks/im-7TRTF3KT.js")).imShortcut
118
123
  }),
119
124
  lazyShortcut({
120
125
  name: "project",
121
126
  description: "\u6CDB\u5FAEE10\u4E8B\u4E95\u7136\u9879\u76EE\u7BA1\u7406 CLI\uFF08\u9879\u76EE/\u4EFB\u52A1\u67E5\u8BE2\u4E0E\u786E\u8BA4\u5199\u5165\uFF09",
122
- load: async () => (await import("./chunks/project-T26QLBQF.js")).projectShortcut
127
+ load: async () => (await import("./chunks/project-D6XT2OC5.js")).projectShortcut
123
128
  }),
124
129
  lazyShortcut({
125
130
  name: "workflow",
126
131
  description: "\u6CDB\u5FAEE10\u6D41\u7A0B\u667A\u80FD\u52A9\u624B CLI\uFF08\u67E5\u8BE2/\u67E5\u770B/\u64CD\u4F5C/\u6279\u91CF/\u53D1\u8D77/\u4FEE\u6539\uFF09",
127
- load: async () => (await import("./chunks/workflow-TR6PG2SL.js")).workflowShortcut
132
+ load: async () => (await import("./chunks/workflow-HLBZRWIY.js")).workflowShortcut
128
133
  }),
129
134
  lazyShortcut({
130
135
  name: "blog",
131
136
  description: "\u6CDB\u5FAEE10\u5DE5\u4F5C\u65E5\u62A5 CLI\uFF08\u67E5\u8BE2/\u53D1\u5E03/\u7F16\u8F91/\u8BC4\u8BBA/\u70B9\u8D5E/\u63D0\u9192/\u5BFC\u51FA\uFF09",
132
- load: async () => (await import("./chunks/blog-EVZWGDVM.js")).blogShortcut
137
+ load: async () => (await import("./chunks/blog-I7SDPNW4.js")).blogShortcut
133
138
  }),
134
139
  lazyShortcut({
135
140
  name: "caizhilian",
136
141
  description: "\u6CDB\u5FAEE10\u91C7\u77E5\u8FDE\uFF08\u6587\u6863\u4E2D\u5FC3\uFF09CLI",
137
- load: async () => (await import("./chunks/caizhilian-JOWS6SL5.js")).caizhilianShortcut
142
+ load: async () => (await import("./chunks/caizhilian-O37ZIQNY.js")).caizhilianShortcut
138
143
  })
139
144
  ];
140
145
  function shortcutMetadata() {
@@ -1,3 +1,4 @@
1
+ import { isXiaoERuntimeAgent } from '../../core/runtime-agent.js';
1
2
  import { getActiveProfile } from './auth/session.js';
2
3
  import { hasXiaoEEnv, tryXiaoEUnifiedLogin } from './auth/xiaoe.js';
3
4
  const AUTH_REQUIRED_CODES = new Set(['-2', '401']);
@@ -94,12 +95,21 @@ export function isE10AuthenticationRequired(response) {
94
95
  function globalOptions(ctx) {
95
96
  return typeof ctx.getGlobalOptions === 'function' ? ctx.getGlobalOptions() : {};
96
97
  }
97
- /** XiaoE automatic recovery is opt-in through explicit runtime evidence. */
98
- export function canAutomaticallyRecoverXiaoE(ctx) {
98
+ /**
99
+ * Decide whether an expired session can be recovered through XiaoE without
100
+ * involving the caller/Agent. Runtime evidence is preferred, but a persisted
101
+ * XiaoE login is also authoritative: business commands often run in a fresh
102
+ * process where the host no longer injects its original XIAOE_* markers.
103
+ */
104
+ export function canAutomaticallyRecoverXiaoE(ctx, currentSession) {
99
105
  const options = globalOptions(ctx);
100
106
  // An explicitly supplied auth file is caller-owned and must not be replaced
101
107
  // by a profile-backed XiaoE session behind the caller's back.
102
- return !options.auth && hasXiaoEEnv(ctx.env || process.env, options);
108
+ if (options.auth)
109
+ return false;
110
+ return hasXiaoEEnv(ctx.env || process.env, options)
111
+ || currentSession?.loginSource === 'xiaoe-unified'
112
+ || isXiaoERuntimeAgent(currentSession?.agentType);
103
113
  }
104
114
  /**
105
115
  * Silently refresh a stale XiaoE session. Concurrent requests for the same
@@ -107,7 +117,7 @@ export function canAutomaticallyRecoverXiaoE(ctx) {
107
117
  * does not start a burst of login attempts.
108
118
  */
109
119
  export async function recoverXiaoEAuthSession(ctx, currentSession) {
110
- if (!canAutomaticallyRecoverXiaoE(ctx))
120
+ if (!canAutomaticallyRecoverXiaoE(ctx, currentSession))
111
121
  return null;
112
122
  const options = globalOptions(ctx);
113
123
  const profile = options.profile || getActiveProfile();
@@ -3,6 +3,7 @@ import { basename } from 'node:path';
3
3
  import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
4
4
  import { requireE10Session } from '../../internal/e10/context.js';
5
5
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
6
+ import { E10ApiError, createE10RequestRuntime, } from '../../internal/e10/request-runtime.js';
6
7
  import { ContractCliError, contractAuthError, contractNetworkError, contractValidationError, } from './errors.js';
7
8
  const DEFAULT_TIMEOUT_MS = 30_000;
8
9
  function assertPath(requestPath) {
@@ -16,38 +17,6 @@ function assertPath(requestPath) {
16
17
  throw contractValidationError('path_not_allowed', '今承达合同管理只能调用 /api/ 路径');
17
18
  }
18
19
  }
19
- function withQuery(baseUrl, requestPath, query = {}) {
20
- const url = new URL(`${baseUrl}${requestPath}`);
21
- for (const [key, value] of Object.entries(query)) {
22
- if (value === undefined || value === null || value === '')
23
- continue;
24
- if (Array.isArray(value)) {
25
- for (const item of value)
26
- url.searchParams.append(key, String(item));
27
- }
28
- else {
29
- url.searchParams.set(key, String(value));
30
- }
31
- }
32
- return url;
33
- }
34
- async function fetchWithTimeout(url, options, timeoutMs) {
35
- const controller = new AbortController();
36
- const timer = setTimeout(() => controller.abort(), timeoutMs);
37
- try {
38
- return await fetch(url, { ...options, signal: controller.signal, redirect: 'manual' });
39
- }
40
- catch (error) {
41
- if (error instanceof Error && error.name === 'AbortError') {
42
- throw contractNetworkError('timeout', `请求超时(${timeoutMs}ms)`, { retryable: false });
43
- }
44
- const message = error instanceof Error ? error.message : String(error);
45
- throw contractNetworkError('transport', `请求失败:${message}`, { retryable: true });
46
- }
47
- finally {
48
- clearTimeout(timer);
49
- }
50
- }
51
20
  function textOf(value, keys) {
52
21
  for (const key of keys) {
53
22
  const candidate = value?.[key];
@@ -131,15 +100,6 @@ function inferProfile(ctx) {
131
100
  const options = ctx.getGlobalOptions();
132
101
  return options.profile || getActiveProfile();
133
102
  }
134
- function requestHeaders(session) {
135
- return {
136
- Accept: 'application/json, text/plain, */*',
137
- Cookie: session.cookieHeader(),
138
- eteamsid: session.eteamsId,
139
- 'User-Agent': session.uaHeader(),
140
- Origin: session.getBaseUrl(),
141
- };
142
- }
143
103
  export function createContractHost(ctx, dependencies = {}) {
144
104
  let e10Session = null;
145
105
  let sessionContext = null;
@@ -164,10 +124,9 @@ export function createContractHost(ctx, dependencies = {}) {
164
124
  assertPath(path);
165
125
  const session = await ensureSession();
166
126
  const context = await getSession();
167
- const headers = { ...requestHeaders(session), ...(extraHeaders ?? {}) };
168
- const finalQuery = { ...query };
127
+ const finalQuery = Object.fromEntries(Object.entries(query).filter(([, value]) => value !== undefined && value !== null && value !== ''));
169
128
  let finalBody = body;
170
- const init = { method, headers };
129
+ let formData;
171
130
  if (userIdField) {
172
131
  if (!context.userId) {
173
132
  throw contractAuthError('user_id_missing', '当前会话没有可用的用户 ID,请确认 E10 登录态');
@@ -178,9 +137,9 @@ export function createContractHost(ctx, dependencies = {}) {
178
137
  finalBody = { ...finalBody, [userIdField]: context.userId };
179
138
  }
180
139
  if (form || file) {
181
- const data = new FormData();
140
+ formData = new FormData();
182
141
  for (const [key, value] of Object.entries(form ?? {}))
183
- data.append(key, value);
142
+ formData.append(key, value);
184
143
  if (file) {
185
144
  let buffer;
186
145
  try {
@@ -190,25 +149,41 @@ export function createContractHost(ctx, dependencies = {}) {
190
149
  throw contractValidationError('file_unreadable', `无法读取文件:${file.path}`);
191
150
  }
192
151
  const filename = file.filename ?? basename(file.path);
193
- data.append(file.field ?? 'file', new Blob([new Uint8Array(buffer)], { type: file.contentType ?? 'application/octet-stream' }), filename);
152
+ formData.append(file.field ?? 'file', new Blob([new Uint8Array(buffer)], { type: file.contentType ?? 'application/octet-stream' }), filename);
194
153
  }
195
- init.body = data;
196
154
  }
197
- else if (finalBody !== undefined) {
198
- headers['Content-Type'] = 'application/json;charset=UTF-8';
199
- init.body = JSON.stringify(finalBody);
155
+ const runtime = createE10RequestRuntime(ctx, session, {
156
+ interceptors: dependencies.interceptors,
157
+ fetch: dependencies.fetch,
158
+ recoverAuthentication: dependencies.recoverAuthentication,
159
+ });
160
+ let response;
161
+ try {
162
+ response = await runtime.requestJson({
163
+ module: 'contract',
164
+ method,
165
+ path,
166
+ query: finalQuery,
167
+ body: formData ? undefined : finalBody,
168
+ formData,
169
+ headers: extraHeaders,
170
+ timeoutMs,
171
+ });
172
+ }
173
+ catch (error) {
174
+ if (error instanceof E10ApiError && error.type === 'network') {
175
+ throw contractNetworkError(error.subtype, error.message, {
176
+ retryable: error.retryable,
177
+ details: error.details,
178
+ });
179
+ }
180
+ throw error;
200
181
  }
201
- const url = withQuery(context.baseUrl, path, finalQuery);
202
- const response = await fetchWithTimeout(url, init, timeoutMs);
203
182
  if (response.status === 401 || response.status === 302) {
204
183
  throw contractAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status, path });
205
184
  }
206
- const text = await response.text();
207
- let value = {};
208
- try {
209
- value = text ? JSON.parse(text) : {};
210
- }
211
- catch {
185
+ const { text, value } = response;
186
+ if (response.parseError) {
212
187
  throw contractNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, {
213
188
  retryable: false,
214
189
  details: { path, status: response.status },
@@ -0,0 +1,70 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { homedir } from 'node:os';
5
+ import { resolve } from 'node:path';
6
+ import { coworkValidationError } from './errors.js';
7
+ function stateRoot(ctx) {
8
+ const configured = ctx?.config?.path();
9
+ if (configured)
10
+ return resolve(configured, '..');
11
+ return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
12
+ }
13
+ async function loadKey(ctx) {
14
+ const keysDir = resolve(stateRoot(ctx), 'keys');
15
+ const filename = resolve(keysDir, 'cowork-continuation.key');
16
+ await mkdir(keysDir, { recursive: true, mode: 0o700 });
17
+ if (existsSync(filename))
18
+ return readFile(filename);
19
+ const key = randomBytes(32);
20
+ try {
21
+ await writeFile(filename, key, { flag: 'wx', mode: 0o600 });
22
+ if (process.platform !== 'win32')
23
+ await chmod(filename, 0o600).catch(() => undefined);
24
+ return key;
25
+ }
26
+ catch {
27
+ return readFile(filename);
28
+ }
29
+ }
30
+ function encode(value) {
31
+ return Buffer.from(JSON.stringify(value)).toString('base64url');
32
+ }
33
+ function signature(payload, key) {
34
+ return createHmac('sha256', key).update(payload).digest('base64url');
35
+ }
36
+ export function createCoworkContinuationSigner(ctx) {
37
+ return {
38
+ async issue(payload, ttlMs = 10 * 60_000) {
39
+ const issuedAt = Date.now();
40
+ const body = encode({ ...payload, issuedAt, expiresAt: issuedAt + ttlMs });
41
+ const sig = signature(body, await loadKey(ctx));
42
+ return `${body}.${sig}`;
43
+ },
44
+ async verify(token, expectedOperation) {
45
+ if (typeof token !== 'string' || !token.includes('.')) {
46
+ throw coworkValidationError('continuation_invalid', 'continuation 无效');
47
+ }
48
+ const [body, provided] = token.split('.');
49
+ const expected = signature(body, await loadKey(ctx));
50
+ const valid = provided.length === expected.length
51
+ && timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
52
+ if (!valid)
53
+ throw coworkValidationError('continuation_invalid', 'continuation 签名校验失败');
54
+ let value;
55
+ try {
56
+ value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
57
+ }
58
+ catch {
59
+ throw coworkValidationError('continuation_invalid', 'continuation 内容无法解析');
60
+ }
61
+ if (value.operation !== expectedOperation) {
62
+ throw coworkValidationError('continuation_mismatch', 'continuation 不属于当前操作');
63
+ }
64
+ if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
65
+ throw coworkValidationError('continuation_expired', 'continuation 已过期');
66
+ }
67
+ return value;
68
+ },
69
+ };
70
+ }