weaver-work-cli 0.1.7 → 0.1.20

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 (264) hide show
  1. package/README.md +11 -11
  2. package/dist/cmd/action.js +76 -0
  3. package/dist/cmd/index.js +2 -0
  4. package/dist/cmd/refs/index.js +103 -0
  5. package/dist/cmd/register.js +31 -0
  6. package/dist/cmd/root.js +1 -0
  7. package/dist/cmd/setup/index.js +25 -5
  8. package/dist/cmd/skills/index.js +37 -5
  9. package/dist/core/connector.js +54 -0
  10. package/dist/core/context.js +2 -1
  11. package/dist/core/input.js +48 -0
  12. package/dist/core/output.js +298 -3
  13. package/dist/core/refs.js +432 -0
  14. package/dist/core/state.js +26 -0
  15. package/dist/internal/e10/auth/commands.js +2 -2
  16. package/dist/internal/e10/auth/session.js +14 -0
  17. package/dist/internal/e10/auth/xiaoe.js +116 -51
  18. package/dist/internal/e10/request-runtime.js +5 -0
  19. package/dist/internal/skills/install.js +148 -28
  20. package/dist/internal/skills/reader.js +12 -9
  21. package/dist/shortcuts/blog/continuation.js +74 -0
  22. package/dist/shortcuts/blog/errors.js +60 -0
  23. package/dist/shortcuts/blog/host.js +175 -0
  24. package/dist/shortcuts/blog/index.js +324 -0
  25. package/dist/shortcuts/blog/manifest.js +415 -0
  26. package/dist/shortcuts/blog/operations/comment.js +125 -0
  27. package/dist/shortcuts/blog/operations/comments.js +31 -0
  28. package/dist/shortcuts/blog/operations/edit.js +131 -0
  29. package/dist/shortcuts/blog/operations/export.js +46 -0
  30. package/dist/shortcuts/blog/operations/like.js +80 -0
  31. package/dist/shortcuts/blog/operations/publish.js +157 -0
  32. package/dist/shortcuts/blog/operations/query-user.js +58 -0
  33. package/dist/shortcuts/blog/operations/query.js +96 -0
  34. package/dist/shortcuts/blog/operations/registry.js +54 -0
  35. package/dist/shortcuts/blog/operations/remind.js +176 -0
  36. package/dist/shortcuts/blog/operations/shared.js +330 -0
  37. package/dist/shortcuts/blog/operations/types.js +16 -0
  38. package/dist/shortcuts/blog/operations.js +9 -0
  39. package/dist/shortcuts/caizhilian/continuation.js +70 -0
  40. package/dist/shortcuts/caizhilian/errors.js +53 -0
  41. package/dist/shortcuts/caizhilian/host.js +190 -0
  42. package/dist/shortcuts/caizhilian/index.js +186 -0
  43. package/dist/shortcuts/caizhilian/manifest.js +42 -0
  44. package/dist/shortcuts/caizhilian/operations/api.js +801 -0
  45. package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
  46. package/dist/shortcuts/caizhilian/operations/files.js +306 -0
  47. package/dist/shortcuts/caizhilian/operations/form.js +170 -0
  48. package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
  49. package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
  50. package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +346 -0
  51. package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
  52. package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
  53. package/dist/shortcuts/caizhilian/operations/handlers/folder.js +969 -0
  54. package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
  55. package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
  56. package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
  57. package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
  58. package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
  59. package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
  60. package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
  61. package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
  62. package/dist/shortcuts/caizhilian/operations/specs.js +623 -0
  63. package/dist/shortcuts/caizhilian/operations/types.js +57 -0
  64. package/dist/shortcuts/caizhilian/operations.js +21 -0
  65. package/dist/shortcuts/contract/continuation.js +70 -0
  66. package/dist/shortcuts/contract/errors.js +62 -0
  67. package/dist/shortcuts/contract/host.js +205 -0
  68. package/dist/shortcuts/contract/index.js +318 -0
  69. package/dist/shortcuts/contract/manifest.js +596 -0
  70. package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
  71. package/dist/shortcuts/contract/operations/form-info.js +371 -0
  72. package/dist/shortcuts/contract/operations/read.js +118 -0
  73. package/dist/shortcuts/contract/operations/registry.js +43 -0
  74. package/dist/shortcuts/contract/operations/request.js +300 -0
  75. package/dist/shortcuts/contract/operations/shared.js +133 -0
  76. package/dist/shortcuts/contract/operations/subject.js +157 -0
  77. package/dist/shortcuts/contract/operations/template.js +81 -0
  78. package/dist/shortcuts/contract/operations/types.js +7 -0
  79. package/dist/shortcuts/contract/operations/workflow.js +52 -0
  80. package/dist/shortcuts/contract/operations/write.js +131 -0
  81. package/dist/shortcuts/contract/operations.js +8 -0
  82. package/dist/shortcuts/hrm/index.js +83 -2
  83. package/dist/shortcuts/hrm/operations/employee.js +160 -30
  84. package/dist/shortcuts/hrm/operations/org.js +17 -3
  85. package/dist/shortcuts/hrm/operations/position.js +18 -2
  86. package/dist/shortcuts/index.js +5 -1
  87. package/dist/shortcuts/invoice/index.js +74 -36
  88. package/dist/shortcuts/invoice/manifest.js +674 -4
  89. package/dist/shortcuts/invoice/markdown.js +172 -0
  90. package/dist/shortcuts/invoice/operations/add.js +3 -3
  91. package/dist/shortcuts/invoice/operations/delete.js +2 -2
  92. package/dist/shortcuts/invoice/operations/get.js +10 -2
  93. package/dist/shortcuts/invoice/operations/import.js +2 -2
  94. package/dist/shortcuts/invoice/operations/registry.js +8 -1
  95. package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
  96. package/dist/shortcuts/invoice/operations/reim.js +85 -2
  97. package/dist/shortcuts/invoice/operations/shared.js +75 -8
  98. package/dist/shortcuts/invoice/operations/update.js +2 -2
  99. package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
  100. package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
  101. package/dist/shortcuts/invoice/projection.js +328 -0
  102. package/dist/shortcuts/meeting/host.js +18 -1
  103. package/dist/shortcuts/meeting/manifest.js +30 -18
  104. package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
  105. package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
  106. package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
  107. package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
  108. package/dist/shortcuts/meeting/operations/env.js +8 -1
  109. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  110. package/dist/shortcuts/meeting/operations/signs.js +216 -2
  111. package/dist/shortcuts/workflow/endpoints.js +2 -1
  112. package/dist/shortcuts/workflow/errors.js +8 -2
  113. package/dist/shortcuts/workflow/host.js +6 -3
  114. package/dist/shortcuts/workflow/operations/form.js +931 -533
  115. package/dist/shortcuts/workflow/operations/read.js +462 -81
  116. package/dist/shortcuts/workflow/operations/shared.js +44 -0
  117. package/dist/shortcuts/workflow/operations/write.js +760 -56
  118. package/dist/shortcuts/workflow/operations.js +36 -10
  119. package/docs/_catalog.md +6 -0
  120. package/docs/agent-invoice.md +5 -1
  121. package/docs/agent-io-channel-design.md +468 -0
  122. package/docs/agent-skill-install.md +9 -4
  123. package/docs/blog.md +123 -0
  124. package/docs/caizhilian.md +304 -0
  125. package/docs/contract.md +91 -0
  126. package/docs/e10-auth.md +2 -2
  127. package/docs/hrm.md +19 -9
  128. package/docs/invoice.md +5 -1
  129. package/docs/meeting.md +1 -1
  130. package/docs/operation-manual.md +15 -3
  131. package/docs/skill-review-2026-09-19.md +2 -2
  132. package/package.json +2 -2
  133. package/scripts/package-skill.mjs +33 -20
  134. package/skill-template/business-info.json +113 -14
  135. package/skill-template/domains/blog.md +24 -0
  136. package/skill-template/domains/caizhilian.md +22 -0
  137. package/skill-template/domains/jinchengda.md +23 -0
  138. package/skill-template/domains/meeting.md +1 -1
  139. package/skill-template/domains/workflow.md +1 -1
  140. package/skill-template/skill-template.md +1 -1
  141. package/skills/weaver-e10-blog/SKILL.md +111 -0
  142. package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
  143. package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
  144. package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
  145. package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
  146. package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
  147. package/skills/weaver-e10-blog/references/blog-export.md +42 -0
  148. package/skills/weaver-e10-blog/references/blog-like.md +50 -0
  149. package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
  150. package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
  151. package/skills/weaver-e10-blog/references/blog-query.md +49 -0
  152. package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
  153. package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
  154. package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
  155. package/skills/weaver-e10-caizhilian/SKILL.md +216 -0
  156. package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
  157. package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
  158. package/skills/weaver-e10-caizhilian/references/document.md +111 -0
  159. package/skills/weaver-e10-caizhilian/references/file-collection.md +136 -0
  160. package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
  161. package/skills/weaver-e10-caizhilian/references/files.md +120 -0
  162. package/skills/weaver-e10-caizhilian/references/folder.md +118 -0
  163. package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
  164. package/skills/weaver-e10-caizhilian/references/setting-attach.md +410 -0
  165. package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
  166. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
  167. package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
  168. package/skills/weaver-e10-caizhilian/references/source-manifest.json +5079 -0
  169. package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
  170. package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
  171. package/skills/weaver-e10-calendar/SKILL.md +4 -3
  172. package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
  173. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
  174. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
  175. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
  176. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
  177. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
  178. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
  179. package/skills/weaver-e10-esb/SKILL.md +2 -2
  180. package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
  181. package/skills/weaver-e10-hrm/SKILL.md +54 -6
  182. package/skills/weaver-e10-hrm/references/employee.md +89 -15
  183. package/skills/weaver-e10-hrm/references/org.md +7 -4
  184. package/skills/weaver-e10-hrm/references/position.md +14 -7
  185. package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
  186. package/skills/weaver-e10-jinchengda/SKILL.md +166 -0
  187. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
  188. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
  189. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
  190. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
  191. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
  192. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
  193. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
  194. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
  195. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
  196. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
  197. package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
  198. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
  199. package/skills/weaver-e10-jucailin/SKILL.md +4 -3
  200. package/skills/weaver-e10-mail/SKILL.md +4 -11
  201. package/skills/weaver-e10-meeting/SKILL.md +66 -32
  202. package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
  203. package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
  204. package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
  205. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
  206. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
  207. package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
  208. package/skills/weaver-e10-okr/SKILL.md +6 -11
  209. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  210. package/skills/weaver-e10-plan/SKILL.md +3 -3
  211. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
  212. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
  213. package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
  214. package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
  215. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
  216. package/skills/weaver-e10-shared/SKILL.md +36 -25
  217. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +10 -31
  218. package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
  219. package/skills/weaver-e10-shijingran/SKILL.md +2 -1
  220. package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
  221. package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
  222. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
  223. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
  224. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
  225. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
  226. package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
  227. package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
  228. package/skills/weaver-e10-workflow/SKILL.md +32 -37
  229. package/skills/weaver-e10-workflow/references/source-manifest.json +40 -77
  230. package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
  231. package/skills/weaver-e10-workflow/references/workflow-create.md +79 -109
  232. package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
  233. package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
  234. package/skills/weaver-e10-workflow/references/workflow-query.md +37 -20
  235. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  236. package/skills/weaver-e10-workflow/references/workflow-view.md +4 -4
  237. package/skills/weaver-e10-yepiaotong/SKILL.md +75 -39
  238. package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
  239. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
  240. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +5 -5
  241. package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
  242. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  243. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  244. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  245. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  246. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  247. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +90 -10
  248. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  249. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  250. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  251. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  252. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  253. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  254. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  255. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  256. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  257. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  258. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  259. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  260. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  261. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  262. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  263. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  264. package/scripts/sync-connector-skills.mjs +0 -172
package/README.md CHANGED
@@ -126,7 +126,7 @@ weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --agent_type "<当
126
126
  weaver-work-cli invoice schema
127
127
  ```
128
128
 
129
- 如果要让 Codex/Agent 一键识别发票业务,把随包 Skill 安装到 Codex skills 目录:
129
+ 如果要让当前 Agent 一键识别发票业务,把随包 Skill 安装到它的 skills 目录:
130
130
 
131
131
  ```text
132
132
  weaver-work-cli setup invoice --install-skill
@@ -135,11 +135,10 @@ weaver-work-cli skills install yepiaotong
135
135
  ```
136
136
 
137
137
  `skills install yepiaotong` 会同时安装 `weaver-e10-shared` 和
138
- `weaver-e10-yepiaotong`。默认目标目录优先使用环境变量 `CODEX_HOME` 下的
139
- `skills` 子目录;未设置 `CODEX_HOME` 时使用用户主目录下的 `.codex/skills`
140
- (Windows PowerShell 示例:`"$env:USERPROFILE\.codex\skills"`);需要自定义时可加
141
- `--target-dir <path>`。默认目标面向 Codex CLI;WorkBuddy 等桌面 Agent 的
142
- 用户级 Skill 目录不是默认目标,需显式指定:
138
+ `weaver-e10-yepiaotong`。CLI 会优先使用 `--target-dir` 或 `WEAVER_SKILLS_ROOT`,
139
+ 否则根据当前进程的 Codex/WorkBuddy 环境信号选择当前用户的 skills 目录。
140
+ 宿主不明确、候选目录冲突或其它 Agent 未提供路径时,CLI 会停止安装并要求显式指定,
141
+ 避免把 Skill 写到当前 Agent 不会读取的位置:
143
142
 
144
143
  Windows PowerShell:
145
144
 
@@ -212,11 +211,12 @@ weaver-work-cli auth login --base-url "https://weapp.xxx.cn"
212
211
  weaver-work-cli auth oidc --base-url "https://weapp.xxx.cn"
213
212
  ```
214
213
 
215
- 在 XiaoE 宿主环境中,`auth login` / `auth oidc` / `auth xiaoe` 会优先读取宿主
216
- `xiaoeUserData/tokens.py`;脚本不可用(不存在、执行失败,或所用 Python 缺少
217
- `requests` 等依赖)时,CLI 会改用内置的原生等价实现读取 XiaoE 客户端本地会话
218
- (`XiaoE/Robots/*/user_settings.json` 或 `XIAOE_USER_TOKEN`)换取 `eteamId`,
219
- 不依赖本机 Python 环境;仍没有可用凭证时,再读取宿主注入的 `XIAOE_*`
214
+ 在 XiaoE 宿主环境中,`auth login` / `auth oidc` / `auth xiaoe` 会优先使用平台注入的
215
+ `XIAOE_USER_TOKEN` + `XIAOE_DOMAIN` 换取 ETEAMSID(沙箱环境唯一可用来源);
216
+ 拿不到时再读取宿主 `xiaoeUserData/tokens.py`;脚本不可用(不存在、执行失败,或所用
217
+ Python 缺少 `requests` 等依赖)时,CLI 会改用内置的原生等价实现读取桌面 XiaoE
218
+ 客户端本地会话(`XiaoE/Robots/<id>/user_settings.json`)换取 `eteamId`,
219
+ 不依赖本机 Python 环境;仍没有可用凭证时,再读取 `WEAVER_XIAOE_*`
220
220
  环境变量,匹配当前登录域名对应的统一登录凭证。匹配成功时直接保存登录态,
221
221
  并固定使用 `agentType=XiaoE`;凭证只返回 `eteamId` 时,会使用凭证中的
222
222
  域名或资料包默认域名 `https://www.e-cology.com.cn`;仍没有可用凭证时才考虑当前
@@ -1,13 +1,89 @@
1
1
  import { ExitCode, isCliError, toErrorEnvelope } from '../core/errors.js';
2
+ import { guardFileInput } from '../core/input.js';
3
+ import { RefError, pruneRefs, resolveRefInput } from '../core/refs.js';
4
+ import { refMaxBytes, refMaxCount } from '../core/output.js';
2
5
  function commandFromArgs(args) {
3
6
  const last = args[args.length - 1];
4
7
  return last && typeof last === 'object' && 'name' in last && 'opts' in last ? last : undefined;
5
8
  }
9
+ function optionsHolderFrom(args) {
10
+ return args.find((arg) => Boolean(arg
11
+ && typeof arg === 'object'
12
+ && !Array.isArray(arg)
13
+ && !('opts' in arg)
14
+ && ('inputRef' in arg
15
+ || 'pick' in arg
16
+ || 'pickAs' in arg
17
+ || 'allowFileInput' in arg
18
+ || 'fileInput' in arg)));
19
+ }
20
+ /**
21
+ * 把 `--input-ref`(+ 可选 `--pick`)翻译成等价的内联 JSON,写回 options 的 inputJson。
22
+ *
23
+ * 这样 20 个业务模块各自的 `inputFrom()` 无需任何改动就能消费结果引用:
24
+ * 它们本来就认 `--input-json`,我们只是在参数进入 handler 之前把 ref 展开成它。
25
+ */
26
+ function applyRefInput(ctx, args) {
27
+ const holder = optionsHolderFrom(args);
28
+ if (!holder)
29
+ return;
30
+ const refId = String(holder.inputRef ?? '').trim();
31
+ if (!refId) {
32
+ if (holder.pick || holder.pickAs) {
33
+ throw new RefError('input_ref_required', '--pick 和 --pick-as 必须与 --input-ref 一起使用');
34
+ }
35
+ return;
36
+ }
37
+ if (holder.inputJson !== undefined || holder.input !== undefined) {
38
+ throw new RefError('input_conflict', '不能同时传 --input-ref 与 --input/--input-json');
39
+ }
40
+ const pick = holder.pick === undefined || holder.pick === null || holder.pick === ''
41
+ ? undefined
42
+ : String(holder.pick);
43
+ const pickAs = holder.pickAs === undefined || holder.pickAs === null || holder.pickAs === ''
44
+ ? undefined
45
+ : String(holder.pickAs);
46
+ holder.inputJson = JSON.stringify(resolveRefInput(ctx, {
47
+ refId,
48
+ ...(pick ? { pick } : {}),
49
+ ...(pickAs ? { pickAs } : {}),
50
+ }));
51
+ delete holder.inputRef;
52
+ delete holder.pick;
53
+ delete holder.pickAs;
54
+ }
55
+ function cleanupRefs(ctx) {
56
+ try {
57
+ pruneRefs(ctx, { maxCount: refMaxCount(ctx), maxBytes: refMaxBytes(ctx) });
58
+ }
59
+ catch {
60
+ process.stderr.write('[weaver-work-cli] 暂存结果清理失败;本次命令继续执行。\n');
61
+ }
62
+ }
63
+ function isRefsShow(command) {
64
+ const names = [];
65
+ let current = command;
66
+ while (current) {
67
+ const name = current.name();
68
+ if (name)
69
+ names.unshift(name);
70
+ current = current.parent;
71
+ }
72
+ return names.slice(-2).join(' ') === 'refs show';
73
+ }
6
74
  export function createAction(ctx) {
7
75
  return (handler) => async (...args) => {
8
76
  const commandCtx = ctx.forCommand(commandFromArgs(args));
9
77
  try {
10
78
  await commandCtx.hooks.beforeCommand(commandCtx);
79
+ applyRefInput(commandCtx, args);
80
+ const options = commandCtx.activeCommand?.opts();
81
+ if (options)
82
+ guardFileInput(commandCtx, options);
83
+ // Run GC on every command, but after resolving a supplied ref so an
84
+ // expired handle can still return the specific ref_expired error.
85
+ if (!isRefsShow(commandCtx.activeCommand))
86
+ cleanupRefs(commandCtx);
11
87
  const result = await handler(commandCtx, ...args);
12
88
  await commandCtx.hooks.afterCommand(commandCtx, result);
13
89
  }
package/dist/cmd/index.js CHANGED
@@ -3,6 +3,7 @@ import { configCommand } from './config/index.js';
3
3
  import { docsCommand } from './docs/index.js';
4
4
  import { doctorCommand } from './doctor/index.js';
5
5
  import { e10Command } from './e10/index.js';
6
+ import { refsCommand } from './refs/index.js';
6
7
  import { runCommand } from './run/index.js';
7
8
  import { setupCommand } from './setup/index.js';
8
9
  import { skillsCommand } from './skills/index.js';
@@ -12,6 +13,7 @@ export const builtinCommands = [
12
13
  doctorCommand,
13
14
  commandsCommand,
14
15
  runCommand,
16
+ refsCommand,
15
17
  e10Command,
16
18
  setupCommand,
17
19
  skillsCommand,
@@ -0,0 +1,103 @@
1
+ import { CliError, ExitCode } from '../../core/errors.js';
2
+ import { listRefs, pickValue, pruneRefs, readRef } from '../../core/refs.js';
3
+ import { refsDir } from '../../core/state.js';
4
+ import { bytesOf, DEFAULT_RESULT_THRESHOLD_BYTES, refMaxBytes, refMaxCount, resultThresholdBytes, } from '../../core/output.js';
5
+ /** 解析 `7d` / `24h` / `30m` / `90s` / 纯毫秒数。 */
6
+ function parseDuration(value) {
7
+ const raw = value.trim().toLowerCase();
8
+ const matched = /^(\d+)\s*([dhms])?$/.exec(raw);
9
+ if (!matched) {
10
+ throw new CliError(`无法解析时长:${value}(示例:7d、24h、30m)`, ExitCode.UsageError);
11
+ }
12
+ const amount = Number(matched[1]);
13
+ const unit = matched[2] ?? 'ms';
14
+ const factor = unit === 'd' ? 86_400_000 : unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : 1;
15
+ return amount * factor;
16
+ }
17
+ function asOptions(value) {
18
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
19
+ }
20
+ export const refsCommand = {
21
+ name: 'refs',
22
+ description: 'Inspect and prune persisted large results',
23
+ category: 'system',
24
+ register({ program, action }) {
25
+ const refs = program.command('refs').description('Manage persisted large results');
26
+ refs.command('path')
27
+ .description('Print the directory holding persisted results')
28
+ .action(action((ctx) => {
29
+ const path = refsDir(ctx);
30
+ ctx.output.write({ path }, { text: path });
31
+ }));
32
+ refs.command('list')
33
+ .description('List persisted results, newest first')
34
+ .action(action((ctx) => {
35
+ const now = Date.now();
36
+ const items = listRefs(ctx).filter((row) => Date.parse(row.expiresAt) > now).map((row) => ({
37
+ refId: row.refId,
38
+ operation: row.operation ?? null,
39
+ bytes: row.bytes,
40
+ createdAt: row.createdAt,
41
+ expiresAt: row.expiresAt,
42
+ }));
43
+ ctx.output.write({ count: items.length, items });
44
+ }));
45
+ refs.command('show')
46
+ .description('Print one persisted result in full')
47
+ .argument('<refId>', 'reference id')
48
+ .action(action((ctx, refId) => {
49
+ const id = String(refId ?? '').trim();
50
+ if (!id)
51
+ throw new CliError('refId 不能为空', ExitCode.UsageError);
52
+ const record = readRef(ctx, id);
53
+ const configuredLimit = resultThresholdBytes(ctx);
54
+ const displayLimit = configuredLimit > 0 ? configuredLimit : DEFAULT_RESULT_THRESHOLD_BYTES;
55
+ if (bytesOf(record.envelope) > displayLimit) {
56
+ throw new CliError(`结果太大,不能安全地一次显示。请用 refs get ${id} <json-path> 读取需要的字段。`, ExitCode.UsageError);
57
+ }
58
+ // 用户主动查看完整结果,不再二次落盘。
59
+ ctx.output.write(record.envelope, { json: true, persist: false });
60
+ }));
61
+ refs.command('get')
62
+ .description('Print one value from a persisted result by JSON path')
63
+ .argument('<refId>', 'reference id')
64
+ .argument('<path>', 'JSON path, e.g. data.items[0].fid')
65
+ .action(action((ctx, refId, path) => {
66
+ const id = String(refId ?? '').trim();
67
+ const jsonPath = String(path ?? '').trim();
68
+ if (!id || !jsonPath)
69
+ throw new CliError('refId 和 JSON 路径不能为空', ExitCode.UsageError);
70
+ const record = readRef(ctx, id);
71
+ ctx.output.write(pickValue(record.envelope, jsonPath));
72
+ }));
73
+ refs.command('prune')
74
+ .description('Prune persisted results')
75
+ .option('--older-than <duration>', 'remove results created before this age, e.g. 7d')
76
+ .option('--all', 'remove every persisted result')
77
+ .action(action((ctx, options) => {
78
+ const opts = asOptions(options);
79
+ if (opts.all === true) {
80
+ const result = pruneRefs(ctx, { maxCount: 0, maxBytes: 0 });
81
+ ctx.output.write({
82
+ removed: result.removed,
83
+ kept: result.kept,
84
+ bytes: result.bytes,
85
+ cleanupIncomplete: result.cleanupIncomplete,
86
+ });
87
+ return;
88
+ }
89
+ const olderThanMs = opts.olderThan ? parseDuration(String(opts.olderThan)) : undefined;
90
+ const result = pruneRefs(ctx, {
91
+ maxCount: refMaxCount(ctx),
92
+ maxBytes: refMaxBytes(ctx),
93
+ ...(olderThanMs === undefined ? {} : { olderThanMs }),
94
+ });
95
+ ctx.output.write({
96
+ removed: result.removed,
97
+ kept: result.kept,
98
+ bytes: result.bytes,
99
+ cleanupIncomplete: result.cleanupIncomplete,
100
+ });
101
+ }));
102
+ },
103
+ };
@@ -1,4 +1,34 @@
1
1
  import { createAction } from './action.js';
2
+ const IO_CHANNEL_OPTIONS = [
3
+ { flags: '--input-ref <ref>', description: 'consume a persisted large result reference' },
4
+ { flags: '--pick <path>', description: 'pick a sub path from --input-ref, e.g. items[0].fid' },
5
+ { flags: '--pick-as <field>', description: 'map a scalar picked value to this input field, e.g. fid' },
6
+ { flags: '--allow-file-input', description: 'allow reading JSON input from --input <path>' },
7
+ { flags: '--no-file-input', description: 'reject JSON input from --input <path>' },
8
+ ];
9
+ /**
10
+ * 只给叶子命令补上结果引用相关的输入选项。
11
+ *
12
+ * 两个坑:
13
+ * 1. Commander 的子命令不继承父命令 option,只加在 program 上会被解析成 unknown option,
14
+ * 所以必须递归到每个叶子命令;
15
+ * 2. **绝不能加在中间命令上**——父命令 parseOptions 时会先把 `--input-ref` 当自己的
16
+ * 选项吃掉,子命令就再也收不到(实测 `invoice run op --input-ref x` 的 opts() 为空)。
17
+ */
18
+ function ensureRefInputOptions(command) {
19
+ if (command.commands.length === 0) {
20
+ for (const option of IO_CHANNEL_OPTIONS) {
21
+ const long = option.flags.split(' ')[0];
22
+ if (!command.options.some((existing) => existing.long === long)) {
23
+ command.option(option.flags, option.description);
24
+ }
25
+ }
26
+ return;
27
+ }
28
+ for (const child of command.commands) {
29
+ ensureRefInputOptions(child);
30
+ }
31
+ }
2
32
  export async function registerCommands(program, ctx, commands) {
3
33
  ctx.commands = commands.map((command) => ({
4
34
  name: command.name,
@@ -15,5 +45,6 @@ export async function registerCommands(program, ctx, commands) {
15
45
  for (const command of commands) {
16
46
  await command.register({ program, ctx, action });
17
47
  }
48
+ ensureRefInputOptions(program);
18
49
  }
19
50
  export const registerModules = registerCommands;
package/dist/cmd/root.js CHANGED
@@ -29,6 +29,7 @@ export function createProgram() {
29
29
  .option('--config <path>', 'use a custom weaver-work-cli config file')
30
30
  .option('--auth <path>', 'use a specific E10 auth file')
31
31
  .option('--profile <name>', 'use a specific E10 profile')
32
+ .option('--is-connector [value]', 'run as the WorkBuddy connector: use the fixed E10 profile managed by the connector')
32
33
  .option('--base-url <url>', 'override E10 base URL')
33
34
  .option('--passport-url <url>', 'override E10 passport URL');
34
35
  program.showHelpAfterError();
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { fileURLToPath } from 'node:url';
3
+ import { CliError, ExitCode } from '../../core/errors.js';
3
4
  import { findAuthFile } from '../../internal/e10/auth/session.js';
4
- import { defaultCodexSkillsRoot, installPackagedSkills, resolvePackagedSkillNames } from '../../internal/skills/install.js';
5
+ import { installPackagedSkills, resolvePackagedSkillNames, resolveSkillsRoot, resolveSkillsTargetPath, unresolvedSkillsRootMessage, } from '../../internal/skills/install.js';
5
6
  const SKILLS_DIR = fileURLToPath(new URL('../../../skills/', import.meta.url));
6
7
  function versionMajor(version) {
7
8
  return Number(version.replace(/^v/, '').split('.')[0] || 0);
@@ -15,8 +16,8 @@ export const setupCommand = {
15
16
  .description('One-step setup checks for business shortcuts');
16
17
  setup.command('invoice')
17
18
  .description('Check invoice CLI readiness and optionally install Agent skills')
18
- .option('--install-skill', 'install packaged invoice Agent skill into Codex skills directory')
19
- .option('--skill-target-dir <path>', 'override target Codex skills directory')
19
+ .option('--install-skill', 'install packaged invoice skill into the current Agent skills directory')
20
+ .option('--skill-target-dir <path>', 'override target Agent skills directory')
20
21
  .action(action((ctx, options) => {
21
22
  const opts = options && typeof options === 'object'
22
23
  ? options
@@ -45,8 +46,26 @@ export const setupCommand = {
45
46
  detail: authFile || 'not found',
46
47
  },
47
48
  ];
48
- const skillInstall = opts.installSkill
49
- ? installPackagedSkills(SKILLS_DIR, opts.skillTargetDir || defaultCodexSkillsRoot(ctx.env), 'yepiaotong')
49
+ const specifiedSkillRoot = typeof opts.skillTargetDir === 'string' && opts.skillTargetDir.trim()
50
+ ? resolveSkillsTargetPath(opts.skillTargetDir)
51
+ : '';
52
+ const skillResolution = opts.installSkill
53
+ ? specifiedSkillRoot
54
+ ? { root: specifiedSkillRoot, host: 'explicit', source: 'target-dir' }
55
+ : resolveSkillsRoot(ctx.env)
56
+ : null;
57
+ if (skillResolution?.requiresExplicitTarget) {
58
+ throw new CliError(unresolvedSkillsRootMessage(skillResolution, '--skill-target-dir'), ExitCode.UsageError);
59
+ }
60
+ const skillInstall = skillResolution
61
+ ? {
62
+ ...installPackagedSkills(SKILLS_DIR, skillResolution.root, 'yepiaotong'),
63
+ skillsHost: skillResolution.host,
64
+ skillsRootSource: skillResolution.source,
65
+ warnings: skillResolution.source === 'filesystem-detected'
66
+ ? ['目标目录仅根据本机已有目录推断;如与当前智能体不符,请显式指定。']
67
+ : [],
68
+ }
50
69
  : null;
51
70
  const nextActions = [];
52
71
  if (!checks.find((check) => check.name === 'e10Auth')?.ok) {
@@ -68,6 +87,7 @@ export const setupCommand = {
68
87
  ok ? 'Invoice setup checks passed.' : 'Invoice setup needs attention.',
69
88
  ...checks.map((check) => `- ${check.name}: ${check.ok ? 'ok' : 'missing'} (${check.detail})`),
70
89
  ...(skillInstall ? skillInstall.installed.map((entry) => `- installed skill: ${entry.name} -> ${entry.target}`) : []),
90
+ ...(skillInstall ? skillInstall.warnings.map((warning) => `- warning: ${warning}`) : []),
71
91
  'Next:',
72
92
  ...nextActions.map((actionText) => `- ${actionText}`),
73
93
  ].join('\n'),
@@ -1,9 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { CliError } from '../../core/errors.js';
4
+ import { CliError, ExitCode } from '../../core/errors.js';
5
5
  import { resolveDetectorInfo } from '../../internal/skills/detector.js';
6
- import { defaultCodexSkillsRoot, installPackagedSkills } from '../../internal/skills/install.js';
6
+ import { installPackagedSkills, resolveSkillsRoot, resolveSkillsTargetPath, unresolvedSkillsRootMessage, } from '../../internal/skills/install.js';
7
7
  import { SkillReader } from '../../internal/skills/reader.js';
8
8
  const SKILLS_DIR = fileURLToPath(new URL('../../../skills/', import.meta.url));
9
9
  function reader() {
@@ -18,6 +18,18 @@ function commandOptions(value) {
18
18
  const opts = value.opts();
19
19
  return opts && typeof opts === 'object' ? opts : {};
20
20
  }
21
+ function describeRoot(resolution) {
22
+ if (resolution.source === 'target-dir')
23
+ return '由 --target-dir 指定';
24
+ if (resolution.source === 'explicit-env') {
25
+ return `由 ${resolution.environmentVariable || '宿主环境变量'} 指定(${resolution.host})`;
26
+ }
27
+ if (resolution.source === 'host-detected')
28
+ return `自动探测宿主 ${resolution.host}`;
29
+ if (resolution.source === 'filesystem-detected')
30
+ return `由本机唯一已存在的宿主目录推断(${resolution.host})`;
31
+ return '无法可靠判断宿主';
32
+ }
21
33
  function pythonInvocation(opts) {
22
34
  if (opts.python)
23
35
  return { command: opts.python, args: [] };
@@ -81,17 +93,37 @@ export const skillsCommand = {
81
93
  ctx.output.raw(result.content);
82
94
  }));
83
95
  skills.command('install')
84
- .description('Install packaged skills into Codex skills directory')
96
+ .description('Install packaged skills into the current Agent skills directory')
85
97
  .argument('[target]', 'skill name (weaver-e10-* or legacy weaver-work-cli-*), short name such as skill-maker, or all', 'all')
86
98
  .option('--target-dir <path>', 'override target skills directory')
87
99
  .action(action((ctx, target, options) => {
88
100
  const opts = options && typeof options === 'object' ? options : {};
89
- const targetRoot = opts.targetDir || defaultCodexSkillsRoot(ctx.env);
101
+ const specified = typeof opts.targetDir === 'string' && opts.targetDir.trim()
102
+ ? resolveSkillsTargetPath(opts.targetDir)
103
+ : '';
104
+ const resolution = specified
105
+ ? { root: specified, host: 'explicit', source: 'target-dir' }
106
+ : resolveSkillsRoot(ctx.env);
107
+ if (resolution.requiresExplicitTarget) {
108
+ throw new CliError(unresolvedSkillsRootMessage(resolution), ExitCode.UsageError);
109
+ }
110
+ const targetRoot = resolution.root;
90
111
  const result = installPackagedSkills(SKILLS_DIR, targetRoot, typeof target === 'string' ? target : 'all');
91
- ctx.output.write(result, {
112
+ const warnings = resolution.source === 'filesystem-detected'
113
+ ? ['提示:该目录仅根据本机已有目录推断;如与当前智能体不符,请用 --target-dir 或 WEAVER_SKILLS_ROOT 指定。']
114
+ : [];
115
+ ctx.output.write({
116
+ ...result,
117
+ targetRoot,
118
+ skillsHost: resolution.host,
119
+ skillsRootSource: resolution.source,
120
+ warnings,
121
+ }, {
92
122
  text: [
123
+ `Skills root: ${targetRoot}(${describeRoot(resolution)})`,
93
124
  'Installed packaged skills:',
94
125
  ...result.installed.map((entry) => `- ${entry.name} -> ${entry.target}`),
126
+ ...warnings,
95
127
  ].join('\n'),
96
128
  });
97
129
  }));
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 连接器模式下内部固定使用的 E10 profile。
3
+ *
4
+ * WorkBuddy 连接器由连接器自己完成安装与登录,业务 Skill 不再显式写 `--profile eteams`,
5
+ * 改为由模型按「本次任务是否使用连接器能力」传 `--is-connector true`,由 CLI 内部切到该 profile。
6
+ * 该 profile 的 `config.json` 自带 baseUrl / passportUrl,凭据在 `profiles/<name>/auth`。
7
+ */
8
+ export const CONNECTOR_PROFILE = 'eteams';
9
+ /**
10
+ * 判定 `--is-connector` 的值是否表示「启用连接器模式」。
11
+ *
12
+ * 支持写法:`--is-connector`(裸写)、`--is-connector true`、`--is-connector=1`、`--is-connector yes`。
13
+ * 未传、传 `false` / `0` / `no` 一律视为关闭。
14
+ */
15
+ export function isConnectorEnabled(value) {
16
+ if (value === undefined || value === null || value === false)
17
+ return false;
18
+ if (value === true)
19
+ return true;
20
+ const normalized = String(value).trim().toLowerCase();
21
+ return normalized === '' || normalized === 'true' || normalized === '1' || normalized === 'yes';
22
+ }
23
+ /**
24
+ * 当前进程是否以「连接器模式」运行。
25
+ *
26
+ * 由 `normalizeGlobalOptions` 在每次解析全局参数时刷新。CLI 是一次性进程,
27
+ * 且全局参数在整个进程生命周期内不变,因此这里不存在跨命令残留的问题。
28
+ */
29
+ let connectorMode = false;
30
+ /**
31
+ * 当前进程是否带 `--is-connector` 运行。
32
+ *
33
+ * 供深层模块判断「是否需要为连接器让路」,例如写活跃 profile 指针处:
34
+ * 连接器只应新增自己的认证信息,不得改动用户既有的活跃 profile 选择。
35
+ */
36
+ export function isConnectorMode() {
37
+ return connectorMode;
38
+ }
39
+ /**
40
+ * 把 `--is-connector true` 归一化为「使用固定连接器 profile」,并刷新连接器模式状态。
41
+ *
42
+ * 设计取舍:漏传 `--is-connector` 时**保持原行为**(回退当前活跃 profile),
43
+ * 不做静默仲裁,也不报错——这样非连接器场景的既有用法完全不受影响。
44
+ * 反过来,带了 `--is-connector` 时除了锁定 profile,还会关闭一切对用户
45
+ * 既有认证状态的写入(活跃 profile 指针)。
46
+ */
47
+ export function normalizeGlobalOptions(options) {
48
+ if (!isConnectorEnabled(options.isConnector)) {
49
+ connectorMode = false;
50
+ return options;
51
+ }
52
+ connectorMode = true;
53
+ return { ...options, profile: CONNECTOR_PROFILE };
54
+ }
@@ -1,10 +1,11 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { resolve } from 'node:path';
3
3
  import { ConfigStore } from './config-store.js';
4
+ import { normalizeGlobalOptions } from './connector.js';
4
5
  import { Logger } from './logger.js';
5
6
  import { Output } from './output.js';
6
7
  export function createCliContext(program, hooks, packageInfo) {
7
- const getGlobalOptions = () => program.opts();
8
+ const getGlobalOptions = () => normalizeGlobalOptions(program.opts());
8
9
  const configStore = new ConfigStore(() => {
9
10
  const opts = getGlobalOptions();
10
11
  const home = process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
@@ -0,0 +1,48 @@
1
+ import { CliError, ExitCode } from './errors.js';
2
+ export class FileInputPolicyError extends CliError {
3
+ type = 'policy';
4
+ subtype = 'file_input_denied';
5
+ retryable = false;
6
+ constructor() {
7
+ super('不允许通过 --input <path> 读取 JSON 文件。请改用 --input-json、--input -,或用 --input-ref 传递上一步的结果。', ExitCode.PolicyError);
8
+ this.name = 'FileInputPolicyError';
9
+ }
10
+ }
11
+ function configuredPolicy(ctx) {
12
+ const io = ctx.config.get('io');
13
+ if (io && typeof io === 'object' && !Array.isArray(io) && 'fileInput' in io) {
14
+ return io.fileInput;
15
+ }
16
+ return ctx.config.get('io.fileInput');
17
+ }
18
+ /** Policy precedence: explicit flags, environment, config, then warn. */
19
+ export function resolveFileInputPolicy(ctx, options) {
20
+ const allow = options.allowFileInput === true;
21
+ const deny = options.fileInput === false;
22
+ if (allow && deny) {
23
+ throw new CliError('不能同时传 --allow-file-input 和 --no-file-input', ExitCode.UsageError);
24
+ }
25
+ if (allow)
26
+ return 'allow';
27
+ if (deny)
28
+ return 'deny';
29
+ const fromEnv = (ctx.env.WEAVER_WORK_CLI_FILE_INPUT ?? '').trim().toLowerCase();
30
+ if (fromEnv === 'allow' || fromEnv === 'warn' || fromEnv === 'deny')
31
+ return fromEnv;
32
+ const fromConfig = configuredPolicy(ctx);
33
+ if (fromConfig === 'allow' || fromConfig === 'warn' || fromConfig === 'deny')
34
+ return fromConfig;
35
+ return 'warn';
36
+ }
37
+ /** Warn or reject only JSON file input; stdin and business --file options are separate. */
38
+ export function guardFileInput(ctx, options) {
39
+ const input = options.input;
40
+ if (input === undefined || input === null || String(input).trim() === '' || input === '-')
41
+ return;
42
+ const policy = resolveFileInputPolicy(ctx, options);
43
+ if (policy === 'allow')
44
+ return;
45
+ if (policy === 'deny')
46
+ throw new FileInputPolicyError();
47
+ process.stderr.write('[weaver-work-cli] 使用 --input <path> 读取 JSON 文件;多步传递请优先用 --input-ref,简单数据请用 --input-json。\n');
48
+ }