@youzan-cloud/zan-saas-cli 0.0.0-stage → 0.1.0-beta.4

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 (329) hide show
  1. package/DEPENDENCIES.json +50 -0
  2. package/INSTALL.md +79 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +1 -0
  5. package/README.md +151 -2
  6. package/THIRD_PARTY_NOTICES.txt +162 -0
  7. package/bin/zan-saas-cli.js +28 -0
  8. package/internal/registry/index.js +123 -0
  9. package/internal/registry/manifest.json +424 -0
  10. package/npm-shrinkwrap.json +86 -0
  11. package/package.json +33 -4
  12. package/skills/README.md +30 -0
  13. package/skills/zan-saas/SKILL.md +54 -0
  14. package/skills/zan-saas/references/host-entry.md +25 -0
  15. package/skills/zan-saas-access/SKILL.md +37 -0
  16. package/skills/zan-saas-access/references/role-list.md +11 -0
  17. package/skills/zan-saas-access/references/staff-perms-get.md +11 -0
  18. package/skills/zan-saas-aftersale/SKILL.md +31 -0
  19. package/skills/zan-saas-aftersale/references/case-list.md +13 -0
  20. package/skills/zan-saas-aftersale/references/case-scan.md +31 -0
  21. package/skills/zan-saas-benefit/SKILL.md +46 -0
  22. package/skills/zan-saas-benefit/references/card-get.md +7 -0
  23. package/skills/zan-saas-benefit/references/card-grant-list.md +11 -0
  24. package/skills/zan-saas-benefit/references/card-list.md +7 -0
  25. package/skills/zan-saas-benefit/references/customer-card-get.md +7 -0
  26. package/skills/zan-saas-benefit/references/customer-card-list.md +7 -0
  27. package/skills/zan-saas-coupon/SKILL.md +41 -0
  28. package/skills/zan-saas-coupon/references/activity-get.md +11 -0
  29. package/skills/zan-saas-coupon/references/activity-list.md +15 -0
  30. package/skills/zan-saas-coupon/references/voucher-get.md +13 -0
  31. package/skills/zan-saas-coupon/references/voucher-list.md +25 -0
  32. package/skills/zan-saas-customer/SKILL.md +73 -0
  33. package/skills/zan-saas-customer/references/customer-scan.md +11 -0
  34. package/skills/zan-saas-customer/references/segment-list.md +22 -0
  35. package/skills/zan-saas-fulfillment/SKILL.md +36 -0
  36. package/skills/zan-saas-fulfillment/references/delivery-get.md +11 -0
  37. package/skills/zan-saas-fulfillment/references/shipment-list.md +11 -0
  38. package/skills/zan-saas-fulfillment/references/waybill-get.md +9 -0
  39. package/skills/zan-saas-interaction/SKILL.md +37 -0
  40. package/skills/zan-saas-interaction/references/lottery-get.md +11 -0
  41. package/skills/zan-saas-interaction/references/lottery-list.md +19 -0
  42. package/skills/zan-saas-item/SKILL.md +61 -0
  43. package/skills/zan-saas-item/references/attribute-list.md +11 -0
  44. package/skills/zan-saas-item/references/brand-list.md +7 -0
  45. package/skills/zan-saas-item/references/classification-get.md +7 -0
  46. package/skills/zan-saas-item/references/classification-list.md +7 -0
  47. package/skills/zan-saas-item/references/group-list.md +9 -0
  48. package/skills/zan-saas-item/references/item-content.md +9 -0
  49. package/skills/zan-saas-item/references/item-get.md +9 -0
  50. package/skills/zan-saas-item/references/item-search.md +23 -0
  51. package/skills/zan-saas-item/references/product-identity.md +11 -0
  52. package/skills/zan-saas-member/SKILL.md +41 -0
  53. package/skills/zan-saas-member/references/customer-level-get.md +7 -0
  54. package/skills/zan-saas-member/references/growth-log-list.md +17 -0
  55. package/skills/zan-saas-member/references/membership-get.md +7 -0
  56. package/skills/zan-saas-order/SKILL.md +56 -0
  57. package/skills/zan-saas-order/references/order-get.md +26 -0
  58. package/skills/zan-saas-order/references/order-list.md +25 -0
  59. package/skills/zan-saas-order/references/order-scan.md +35 -0
  60. package/skills/zan-saas-org/SKILL.md +37 -0
  61. package/skills/zan-saas-org/references/staff-get.md +15 -0
  62. package/skills/zan-saas-org/references/staff-role-get.md +11 -0
  63. package/skills/zan-saas-org/references/unit-get.md +11 -0
  64. package/skills/zan-saas-org/references/unit-list.md +15 -0
  65. package/skills/zan-saas-points/SKILL.md +42 -0
  66. package/skills/zan-saas-points/references/account-get.md +7 -0
  67. package/skills/zan-saas-points/references/log-list.md +17 -0
  68. package/skills/zan-saas-points/references/rule-list.md +2 -0
  69. package/skills/zan-saas-privacy/SKILL.md +30 -0
  70. package/skills/zan-saas-privacy/references/field-decrypt.md +19 -0
  71. package/skills/zan-saas-shared/SKILL.md +94 -0
  72. package/skills/zan-saas-shared/references/authentication.md +52 -0
  73. package/skills/zan-saas-shared/references/value-processing.md +27 -0
  74. package/skills/zan-saas-stock/SKILL.md +28 -0
  75. package/skills/zan-saas-stock/references/log-list.md +11 -0
  76. package/skills/zan-saas-valuecard/SKILL.md +37 -0
  77. package/skills/zan-saas-valuecard/references/card-get.md +13 -0
  78. package/skills/zan-saas-valuecard/references/card-list.md +11 -0
  79. package/src/auth/browser.js +28 -0
  80. package/src/auth/callback-server.js +99 -0
  81. package/src/auth/config.js +53 -0
  82. package/src/auth/credential-store.js +109 -0
  83. package/src/auth/oauth-client.js +105 -0
  84. package/src/auth/pkce.js +28 -0
  85. package/src/auth/session.js +178 -0
  86. package/src/auth/tool.js +25 -0
  87. package/src/cli/command-paths.js +126 -0
  88. package/src/cli/context-help.js +102 -0
  89. package/src/cli/diagnostic-command.js +57 -0
  90. package/src/cli/domain-catalog.js +42 -0
  91. package/src/cli/help-text.js +32 -0
  92. package/src/cli/native-loader.js +120 -0
  93. package/src/cli/native-path.js +22 -0
  94. package/src/cli/parser-policy.js +141 -0
  95. package/src/cli/program.js +134 -0
  96. package/src/cli/skill-catalog.js +241 -0
  97. package/src/commands/native/access/commands.index.json +24 -0
  98. package/src/commands/native/access/domain.json +3 -0
  99. package/src/commands/native/access/role/list/command.js +141 -0
  100. package/src/commands/native/access/role/list/schema.json +393 -0
  101. package/src/commands/native/access/staff-perms/get/command.js +119 -0
  102. package/src/commands/native/access/staff-perms/get/schema.json +703 -0
  103. package/src/commands/native/aftersale/case/get/command.js +104 -0
  104. package/src/commands/native/aftersale/case/get/exact-json.js +52 -0
  105. package/src/commands/native/aftersale/case/get/money.js +28 -0
  106. package/src/commands/native/aftersale/case/get/projection.js +120 -0
  107. package/src/commands/native/aftersale/case/get/query.js +72 -0
  108. package/src/commands/native/aftersale/case/get/schema.json +134 -0
  109. package/src/commands/native/aftersale/case/list/command.js +135 -0
  110. package/src/commands/native/aftersale/case/list/schema.json +1095 -0
  111. package/src/commands/native/aftersale/case/scan/command.js +110 -0
  112. package/src/commands/native/aftersale/case/scan/query.js +62 -0
  113. package/src/commands/native/aftersale/case/scan/scan.js +78 -0
  114. package/src/commands/native/aftersale/case/scan/schema.json +964 -0
  115. package/src/commands/native/aftersale/case/scan/search.js +108 -0
  116. package/src/commands/native/aftersale/commands.index.json +42 -0
  117. package/src/commands/native/aftersale/domain.json +3 -0
  118. package/src/commands/native/aftersale/refund/list/command.js +97 -0
  119. package/src/commands/native/aftersale/refund/list/money.js +25 -0
  120. package/src/commands/native/aftersale/refund/list/projection.js +50 -0
  121. package/src/commands/native/aftersale/refund/list/query.js +73 -0
  122. package/src/commands/native/aftersale/refund/list/schema.json +130 -0
  123. package/src/commands/native/aftersale/shared/query.js +58 -0
  124. package/src/commands/native/aftersale/shared/search.js +128 -0
  125. package/src/commands/native/benefit/card-definition/get/command.js +110 -0
  126. package/src/commands/native/benefit/card-definition/get/schema.json +545 -0
  127. package/src/commands/native/benefit/card-definition/list/command.js +106 -0
  128. package/src/commands/native/benefit/card-definition/list/schema.json +690 -0
  129. package/src/commands/native/benefit/card-grant/list/command.js +135 -0
  130. package/src/commands/native/benefit/card-grant/list/schema.json +758 -0
  131. package/src/commands/native/benefit/commands.index.json +51 -0
  132. package/src/commands/native/benefit/customer-card/get/command.js +110 -0
  133. package/src/commands/native/benefit/customer-card/get/schema.json +340 -0
  134. package/src/commands/native/benefit/customer-card/list/command.js +103 -0
  135. package/src/commands/native/benefit/customer-card/list/schema.json +731 -0
  136. package/src/commands/native/benefit/domain.json +3 -0
  137. package/src/commands/native/coupon/activity/get/command.js +183 -0
  138. package/src/commands/native/coupon/activity/get/schema.json +624 -0
  139. package/src/commands/native/coupon/activity/list/command.js +169 -0
  140. package/src/commands/native/coupon/activity/list/schema.json +845 -0
  141. package/src/commands/native/coupon/commands.index.json +42 -0
  142. package/src/commands/native/coupon/domain.json +3 -0
  143. package/src/commands/native/coupon/shared/query.js +15 -0
  144. package/src/commands/native/coupon/voucher/get/command.js +113 -0
  145. package/src/commands/native/coupon/voucher/get/schema.json +147 -0
  146. package/src/commands/native/coupon/voucher/list/command.js +155 -0
  147. package/src/commands/native/coupon/voucher/list/schema.json +772 -0
  148. package/src/commands/native/customer/commands.index.json +96 -0
  149. package/src/commands/native/customer/customer/get/adapter.js +82 -0
  150. package/src/commands/native/customer/customer/get/command.js +106 -0
  151. package/src/commands/native/customer/customer/get/schema.json +958 -0
  152. package/src/commands/native/customer/customer/list/command.js +78 -0
  153. package/src/commands/native/customer/customer/list/input.js +145 -0
  154. package/src/commands/native/customer/customer/list/query.js +103 -0
  155. package/src/commands/native/customer/customer/list/schema.json +969 -0
  156. package/src/commands/native/customer/customer/scan/command.js +80 -0
  157. package/src/commands/native/customer/customer/scan/scan.js +92 -0
  158. package/src/commands/native/customer/customer/scan/schema.json +211 -0
  159. package/src/commands/native/customer/customer-tag/get/command.js +166 -0
  160. package/src/commands/native/customer/customer-tag/get/schema.json +317 -0
  161. package/src/commands/native/customer/customer-tag/list/command.js +143 -0
  162. package/src/commands/native/customer/customer-tag/list/schema.json +259 -0
  163. package/src/commands/native/customer/domain.json +3 -0
  164. package/src/commands/native/customer/segment/list/command.js +96 -0
  165. package/src/commands/native/customer/segment/list/query.js +98 -0
  166. package/src/commands/native/customer/segment/list/schema.json +135 -0
  167. package/src/commands/native/customer/segment-customer/list/command.js +171 -0
  168. package/src/commands/native/customer/segment-customer/list/schema.json +123 -0
  169. package/src/commands/native/customer/shared/search-input.js +179 -0
  170. package/src/commands/native/customer/shared/search-page.js +54 -0
  171. package/src/commands/native/customer/tag/get/command.js +73 -0
  172. package/src/commands/native/customer/tag/get/schema.json +339 -0
  173. package/src/commands/native/customer/tagcategory/get/command.js +80 -0
  174. package/src/commands/native/customer/tagcategory/get/query.js +98 -0
  175. package/src/commands/native/customer/tagcategory/get/schema.json +339 -0
  176. package/src/commands/native/customer/tagcategory/list/command.js +127 -0
  177. package/src/commands/native/customer/tagcategory/list/schema.json +394 -0
  178. package/src/commands/native/fulfillment/commands.index.json +33 -0
  179. package/src/commands/native/fulfillment/delivery/get/command.js +107 -0
  180. package/src/commands/native/fulfillment/delivery/get/projection.js +142 -0
  181. package/src/commands/native/fulfillment/delivery/get/schema.json +173 -0
  182. package/src/commands/native/fulfillment/domain.json +3 -0
  183. package/src/commands/native/fulfillment/shipment/list/command.js +83 -0
  184. package/src/commands/native/fulfillment/shipment/list/projection.js +130 -0
  185. package/src/commands/native/fulfillment/shipment/list/schema.json +752 -0
  186. package/src/commands/native/fulfillment/waybill/get/command.js +193 -0
  187. package/src/commands/native/fulfillment/waybill/get/response.js +68 -0
  188. package/src/commands/native/fulfillment/waybill/get/schema.json +190 -0
  189. package/src/commands/native/interaction/commands.index.json +24 -0
  190. package/src/commands/native/interaction/domain.json +3 -0
  191. package/src/commands/native/interaction/lottery/get/command.js +61 -0
  192. package/src/commands/native/interaction/lottery/get/query.js +152 -0
  193. package/src/commands/native/interaction/lottery/get/schema.json +1004 -0
  194. package/src/commands/native/interaction/lottery/list/command.js +176 -0
  195. package/src/commands/native/interaction/lottery/list/schema.json +898 -0
  196. package/src/commands/native/item/attribute/list/command.js +122 -0
  197. package/src/commands/native/item/attribute/list/schema.json +727 -0
  198. package/src/commands/native/item/brand/list/command.js +114 -0
  199. package/src/commands/native/item/brand/list/schema.json +725 -0
  200. package/src/commands/native/item/classification/get/command.js +87 -0
  201. package/src/commands/native/item/classification/get/schema.json +596 -0
  202. package/src/commands/native/item/classification/list/command.js +105 -0
  203. package/src/commands/native/item/classification/list/schema.json +676 -0
  204. package/src/commands/native/item/commands.index.json +87 -0
  205. package/src/commands/native/item/domain.json +1 -0
  206. package/src/commands/native/item/group/list/command.js +122 -0
  207. package/src/commands/native/item/group/list/schema.json +809 -0
  208. package/src/commands/native/item/item/get/command.js +122 -0
  209. package/src/commands/native/item/item/get/schema.json +3799 -0
  210. package/src/commands/native/item/item/list/command.js +7 -0
  211. package/src/commands/native/item/item/list/schema.json +1118 -0
  212. package/src/commands/native/item/item/scan/command.js +7 -0
  213. package/src/commands/native/item/item/scan/schema.json +1046 -0
  214. package/src/commands/native/item/item-content/get/command.js +54 -0
  215. package/src/commands/native/item/item-content/get/schema.json +677 -0
  216. package/src/commands/native/item/shared/api.js +31 -0
  217. package/src/commands/native/item/shared/content.js +20 -0
  218. package/src/commands/native/item/shared/diagnostics.js +61 -0
  219. package/src/commands/native/item/shared/h5.js +63 -0
  220. package/src/commands/native/item/shared/identity.js +62 -0
  221. package/src/commands/native/item/shared/normalize.js +49 -0
  222. package/src/commands/native/item/shared/scope.js +22 -0
  223. package/src/commands/native/item/shared/search-command.js +61 -0
  224. package/src/commands/native/item/shared/search-options.js +150 -0
  225. package/src/commands/native/item/shared/search.js +158 -0
  226. package/src/commands/native/member/commands.index.json +60 -0
  227. package/src/commands/native/member/customer-level/get/command.js +122 -0
  228. package/src/commands/native/member/customer-level/get/schema.json +300 -0
  229. package/src/commands/native/member/domain.json +3 -0
  230. package/src/commands/native/member/growth/get/command.js +64 -0
  231. package/src/commands/native/member/growth/get/schema.json +283 -0
  232. package/src/commands/native/member/growth-log/list/command.js +203 -0
  233. package/src/commands/native/member/growth-log/list/schema.json +695 -0
  234. package/src/commands/native/member/level/get/command.js +70 -0
  235. package/src/commands/native/member/level/get/query.js +125 -0
  236. package/src/commands/native/member/level/get/schema.json +339 -0
  237. package/src/commands/native/member/level/list/command.js +50 -0
  238. package/src/commands/native/member/level/list/query.js +109 -0
  239. package/src/commands/native/member/level/list/schema.json +104 -0
  240. package/src/commands/native/member/membership/get/command.js +78 -0
  241. package/src/commands/native/member/membership/get/schema.json +283 -0
  242. package/src/commands/native/order/commands.index.json +33 -0
  243. package/src/commands/native/order/domain.json +3 -0
  244. package/src/commands/native/order/order/get/command.js +155 -0
  245. package/src/commands/native/order/order/get/schema.json +1167 -0
  246. package/src/commands/native/order/order/list/command.js +230 -0
  247. package/src/commands/native/order/order/list/schema.json +1363 -0
  248. package/src/commands/native/order/order/scan/command.js +148 -0
  249. package/src/commands/native/order/order/scan/scan.js +89 -0
  250. package/src/commands/native/order/order/scan/schema.json +1445 -0
  251. package/src/commands/native/order/shared/projection.js +183 -0
  252. package/src/commands/native/order/shared/query.js +84 -0
  253. package/src/commands/native/order/shared/search.js +112 -0
  254. package/src/commands/native/org/commands.index.json +42 -0
  255. package/src/commands/native/org/domain.json +3 -0
  256. package/src/commands/native/org/shared/single-staff.js +115 -0
  257. package/src/commands/native/org/staff/get/command.js +148 -0
  258. package/src/commands/native/org/staff/get/schema.json +114 -0
  259. package/src/commands/native/org/staff-role/get/command.js +45 -0
  260. package/src/commands/native/org/staff-role/get/schema.json +93 -0
  261. package/src/commands/native/org/unit/get/command.js +131 -0
  262. package/src/commands/native/org/unit/get/schema.json +478 -0
  263. package/src/commands/native/org/unit/list/command.js +187 -0
  264. package/src/commands/native/org/unit/list/schema.json +828 -0
  265. package/src/commands/native/points/account/get/command.js +65 -0
  266. package/src/commands/native/points/account/get/schema.json +303 -0
  267. package/src/commands/native/points/commands.index.json +33 -0
  268. package/src/commands/native/points/domain.json +3 -0
  269. package/src/commands/native/points/log/list/command.js +141 -0
  270. package/src/commands/native/points/log/list/schema.json +761 -0
  271. package/src/commands/native/points/rule/list/command.js +82 -0
  272. package/src/commands/native/points/rule/list/schema.json +294 -0
  273. package/src/commands/native/privacy/commands.index.json +15 -0
  274. package/src/commands/native/privacy/domain.json +3 -0
  275. package/src/commands/native/privacy/field/decrypt/command.js +185 -0
  276. package/src/commands/native/privacy/field/decrypt/schema.json +656 -0
  277. package/src/commands/native/privacy/shared/order-source.js +33 -0
  278. package/src/commands/native/stock/commands.index.json +15 -0
  279. package/src/commands/native/stock/domain.json +3 -0
  280. package/src/commands/native/stock/log/list/command.js +207 -0
  281. package/src/commands/native/stock/log/list/schema.json +922 -0
  282. package/src/commands/native/valuecard/card/get/command.js +107 -0
  283. package/src/commands/native/valuecard/card/get/schema.json +683 -0
  284. package/src/commands/native/valuecard/card/list/command.js +136 -0
  285. package/src/commands/native/valuecard/card/list/pagination.js +23 -0
  286. package/src/commands/native/valuecard/card/list/schema.json +780 -0
  287. package/src/commands/native/valuecard/commands.index.json +24 -0
  288. package/src/commands/native/valuecard/domain.json +3 -0
  289. package/src/commands/native/valuecard/shared/projection.js +57 -0
  290. package/src/commands/system/api.js +47 -0
  291. package/src/commands/system/auth.js +118 -0
  292. package/src/commands/system/contracts.js +373 -0
  293. package/src/commands/system/doctor.js +24 -0
  294. package/src/commands/system/install.js +41 -0
  295. package/src/commands/system/printer.js +16 -0
  296. package/src/commands/system/schema.js +162 -0
  297. package/src/commands/system/shop-guidance.js +9 -0
  298. package/src/commands/system/shop.js +237 -0
  299. package/src/commands/system/skills.js +49 -0
  300. package/src/execution/beijing-date-time.js +29 -0
  301. package/src/execution/beijing-time.js +21 -0
  302. package/src/execution/bounded-input.js +42 -0
  303. package/src/execution/command-io.js +137 -0
  304. package/src/execution/command-policy.js +41 -0
  305. package/src/execution/command-schema.js +131 -0
  306. package/src/execution/datetime.js +83 -0
  307. package/src/execution/error-details.js +399 -0
  308. package/src/execution/error-model.js +82 -0
  309. package/src/execution/exact-json.js +80 -0
  310. package/src/execution/list-total.js +10 -0
  311. package/src/execution/money.js +59 -0
  312. package/src/execution/parser-error.js +66 -0
  313. package/src/execution/privacy.js +197 -0
  314. package/src/execution/public-schema.js +52 -0
  315. package/src/execution/query-api.js +101 -0
  316. package/src/execution/range.js +84 -0
  317. package/src/execution/request-state.js +55 -0
  318. package/src/execution/schema-completeness.js +116 -0
  319. package/src/execution/shop-query.js +54 -0
  320. package/src/execution/target-shop-default.js +23 -0
  321. package/src/execution/target-shop.js +49 -0
  322. package/src/identity/context-store.js +54 -0
  323. package/src/identity/context.js +38 -0
  324. package/src/installation/install.js +158 -0
  325. package/src/installation/npm-error.js +93 -0
  326. package/src/installation/skill.js +113 -0
  327. package/src/transport/account-api.js +152 -0
  328. package/src/transport/api.js +199 -0
  329. package/src/transport/shop-app.js +63 -0
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const { Command } = require('commander');
4
+ const { InputIssueError, bindError, modelAt, constraints } = require('../execution/error-details');
5
+
6
+ class DiagnosticCommand extends Command {
7
+ constructor(name, { path, schema, optionPaths = {}, parameterCategory = 'validation' }) {
8
+ super(name);
9
+ this.diagnosticContext = { command: this, path, schema, optionPaths, parameterCategory };
10
+ this.exitOverride(error => {
11
+ if (['commander.helpDisplayed', 'commander.version'].includes(error.code)) throw error;
12
+ throw this.issue('constraint');
13
+ });
14
+ }
15
+
16
+ issue(reason, option) {
17
+ const context = this.diagnosticContext;
18
+ const attribute = option?.attributeName();
19
+ const mapped = Object.entries(context.optionPaths).find(([, value]) => value === attribute)?.[0];
20
+ const path = mapped || (attribute && modelAt(context.schema, 'input', `/${attribute}`) ? `/${attribute}` : undefined);
21
+ const expected = option?.argChoices ? { enum: [...option.argChoices] } :
22
+ path ? constraints(modelAt(context.schema, 'input', path)) : undefined;
23
+ return bindError(new InputIssueError([{ source: 'argv', reason,
24
+ ...(option ? { option: option.long || option.short } : {}),
25
+ ...(path ? { path } : {}), ...(expected ? { expected } : {}) }]), context);
26
+ }
27
+
28
+ addOption(option) {
29
+ const parse = option.parseArg;
30
+ if (parse) option.argParser((value, previous) => {
31
+ try { return parse.call(option, value, previous); }
32
+ catch (error) {
33
+ if (error.code === 'commander.invalidArgument') throw this.issue('invalid_choice', option);
34
+ throw error;
35
+ }
36
+ });
37
+ return super.addOption(option);
38
+ }
39
+
40
+ missingMandatoryOptionValue(option) { throw this.issue('required', option); }
41
+ optionMissingArgument(option) { throw this.issue('missing_value', option); }
42
+ missingArgument() { throw this.issue('required'); }
43
+ unknownOption() { throw this.issue('unknown_option'); }
44
+ unknownCommand() { throw this.issue('unknown_command'); }
45
+ _excessArguments() {
46
+ if (!this._allowExcessArguments) throw this.issue('excess_arguments');
47
+ }
48
+
49
+ action(handler) {
50
+ return super.action(async (...args) => {
51
+ try { return await handler.apply(this, args); }
52
+ catch (error) { throw bindError(error, this.diagnosticContext); }
53
+ });
54
+ }
55
+ }
56
+
57
+ module.exports = { DiagnosticCommand };
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { validateSkillFrontmatter } = require('./skill-catalog');
6
+
7
+ const skillsRoot = path.join(__dirname, '..', '..', 'skills');
8
+ const namePattern = /^[a-z][a-z0-9-]*$/;
9
+ const reservedNames = new Set(['api', 'auth', 'doctor', 'help', 'schema', 'shop', 'skills', 'shared']);
10
+
11
+ function validDomainName(name) {
12
+ return namePattern.test(name) && !reservedNames.has(name);
13
+ }
14
+
15
+ function domainSkillName(domain) {
16
+ return `zan-saas-${domain}`;
17
+ }
18
+
19
+ function regularFile(filename) {
20
+ return fs.lstatSync(filename).isFile();
21
+ }
22
+
23
+ function loadDomain(root, domain, rootSkills = skillsRoot) {
24
+ if (!validDomainName(domain)) throw new Error(`Invalid native domain: ${domain}`);
25
+ const filename = path.join(root, domain, 'domain.json');
26
+ try {
27
+ if (!regularFile(filename)) throw new Error(`Invalid domain descriptor: ${domain}`);
28
+ } catch (error) {
29
+ if (error.code === 'ENOENT') throw new Error(`Missing domain descriptor: ${domain}/domain.json`);
30
+ throw error;
31
+ }
32
+ const descriptor = JSON.parse(fs.readFileSync(filename, 'utf8'));
33
+ if (typeof descriptor.description !== 'string' || !descriptor.description.trim() ||
34
+ Object.keys(descriptor).some(key => key !== 'description')) {
35
+ throw new Error(`Invalid domain descriptor: ${domain}`);
36
+ }
37
+ const skill = domainSkillName(domain);
38
+ validateSkillFrontmatter(rootSkills, { name: skill });
39
+ return { name: domain, description: descriptor.description.trim(), skill };
40
+ }
41
+
42
+ module.exports = { skillsRoot, validDomainName, domainSkillName, loadDomain };
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ const { Help } = require('commander');
4
+
5
+ const headings = {
6
+ Usage: '用法', Arguments: '参数', Options: '选项',
7
+ 'Global Options': '全局选项', Commands: '命令'
8
+ };
9
+ const annotations = { choices: '可选值', default: '默认值', preset: '预设值', env: '环境变量' };
10
+ const positionLabels = { hq: '总部', shop: '店铺', all: '总部、店铺' };
11
+ const riskLabels = { read: '只读', low: '低', medium: '中', high: '高' };
12
+
13
+ function localizeAnnotations(description) {
14
+ return description.replace(/(\(|, )(choices|default|preset|env): /g,
15
+ (_match, separator, name) => `${separator}${annotations[name]}:`);
16
+ }
17
+
18
+ const chineseHelp = {
19
+ formatHelp(command, helper) {
20
+ return Help.prototype.formatHelp.call(this, command, helper)
21
+ .replace(/^(Usage|Arguments|Options|Global Options|Commands):/gm,
22
+ (_match, name) => `${headings[name]}:`);
23
+ },
24
+ optionDescription(option) {
25
+ return localizeAnnotations(Help.prototype.optionDescription.call(this, option));
26
+ },
27
+ argumentDescription(argument) {
28
+ return localizeAnnotations(Help.prototype.argumentDescription.call(this, argument));
29
+ }
30
+ };
31
+
32
+ module.exports = { chineseHelp, positionLabels, riskLabels };
@@ -0,0 +1,120 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const {
6
+ addAtomicCommand, addTaskCommand, registerDomain, publishedDomains, validateAvailability
7
+ } = require('./command-paths');
8
+ const { loadDomain, skillsRoot, validDomainName } = require('./domain-catalog');
9
+ const { loadCommandSchema, attachCommandSchema } = require('../execution/command-schema');
10
+ const { nativeCommandLocation } = require('./native-path');
11
+ const { isDeepStrictEqual } = require('node:util');
12
+
13
+ const nativeRoot = path.join(__dirname, '..', 'commands', 'native');
14
+ const namePattern = /^[a-z][a-z0-9-]*$/;
15
+ const taskPattern = /^\+[a-z][a-z0-9-]*$/;
16
+
17
+ function directories(root) {
18
+ return fs.readdirSync(root, { withFileTypes: true })
19
+ .filter(entry => entry.isDirectory())
20
+ .map(entry => entry.name)
21
+ .sort();
22
+ }
23
+
24
+ function discoverNative(root = nativeRoot) {
25
+ const domains = directories(root);
26
+ const modules = [];
27
+ for (const domain of domains) {
28
+ if (!validDomainName(domain)) {
29
+ throw new Error(`Invalid native domain: ${domain}`);
30
+ }
31
+ const groups = directories(path.join(root, domain));
32
+ for (const group of groups) {
33
+ if (group === 'shared') continue;
34
+ if (taskPattern.test(group)) {
35
+ modules.push(`${domain}/${group}/command.js`);
36
+ } else if (namePattern.test(group)) {
37
+ const actions = directories(path.join(root, domain, group));
38
+ if (!actions.length) throw new Error(`Native resource has no actions: ${domain}/${group}`);
39
+ for (const action of actions) {
40
+ if (!namePattern.test(action)) throw new Error(`Invalid native action: ${action}`);
41
+ modules.push(`${domain}/${group}/${action}/command.js`);
42
+ }
43
+ } else {
44
+ throw new Error(`Invalid native command group: ${group}`);
45
+ }
46
+ }
47
+ }
48
+ for (const modulePath of modules) {
49
+ if (!fs.existsSync(path.join(root, modulePath)) ||
50
+ !fs.statSync(path.join(root, modulePath)).isFile()) {
51
+ throw new Error(`Missing native command entry: ${modulePath}`);
52
+ }
53
+ }
54
+ return { domains, modules };
55
+ }
56
+
57
+ function registerNativeDomains(program, domains) {
58
+ const registeredDomains = new Set();
59
+ for (const entry of domains) {
60
+ if (!validDomainName(entry.name) || registeredDomains.has(entry.name)) {
61
+ throw new Error(`Invalid native domain manifest entry: ${entry.name}`);
62
+ }
63
+ registerDomain(program, entry);
64
+ registeredDomains.add(entry.name);
65
+ }
66
+
67
+ return registeredDomains;
68
+ }
69
+
70
+ function registerNativeDomain(program, domain, businessCommands, root = nativeRoot,
71
+ rootSkills = skillsRoot, context) {
72
+ const definition = loadDomain(root, domain.name, rootSkills);
73
+ if (definition.description !== domain.description || definition.skill !== domain.skill) {
74
+ throw new Error(`Native domain differs from manifest: ${domain.name}`);
75
+ }
76
+ if (!publishedDomains(program).some(entry => entry.name === domain.name)) {
77
+ throw new Error(`Native command has no registered domain: ${domain.name}`);
78
+ }
79
+
80
+ const seen = new Set();
81
+ for (const entry of businessCommands) {
82
+ const parts = entry.module.split('/');
83
+ const location = nativeCommandLocation(entry.module, entry.resourcePath);
84
+ // A generated manifest is still validated before loading any module.
85
+ if (!location || entry.path !== location.path || entry.kind !== location.kind || seen.has(entry.path)) {
86
+ throw new Error(`Invalid native command manifest entry: ${entry.path}`);
87
+ }
88
+ seen.add(entry.path);
89
+ if (parts[0] !== domain.name) {
90
+ throw new Error(`Native command is in the wrong domain index: ${entry.path}`);
91
+ }
92
+ const definition = require(path.join(root, entry.module));
93
+ const { kind } = location;
94
+ if (Object.hasOwn(definition, 'mutationRisk') ||
95
+ !isDeepStrictEqual(definition.resourcePath, entry.resourcePath) ||
96
+ definition.kind !== kind || definition.risk !== entry.risk ||
97
+ definition.at !== entry.at ||
98
+ validateAvailability(definition.availability) !== validateAvailability(entry.availability) ||
99
+ typeof definition.createCommand !== 'function') {
100
+ throw new Error(`Native command contract differs from manifest: ${entry.path}`);
101
+ }
102
+ const command = definition.createCommand({ context });
103
+ if (command.name() !== parts[kind === 'task' ? 1 : 2]) {
104
+ throw new Error(`Native command name differs from directory: ${entry.path}`);
105
+ }
106
+ if (!command.description().trim()) {
107
+ throw new Error(`Native command needs a help description: ${entry.path}`);
108
+ }
109
+ attachCommandSchema(command, loadCommandSchema(
110
+ path.join(root, ...parts.slice(0, -1), 'schema.json')
111
+ ));
112
+ if (kind === 'task') {
113
+ addTaskCommand(program, parts[0], command, entry.risk, entry.at, entry.availability);
114
+ } else {
115
+ addAtomicCommand(program, parts[0], location.resources, command, entry.risk, entry.at, entry.availability);
116
+ }
117
+ }
118
+ }
119
+
120
+ module.exports = { nativeRoot, discoverNative, registerNativeDomains, registerNativeDomain };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const namePattern = /^[a-z][a-z0-9-]*$/;
4
+ const taskPattern = /^\+[a-z][a-z0-9-]*$/;
5
+
6
+ function nativeCommandLocation(modulePath, resourcePath) {
7
+ if (typeof modulePath !== 'string') return null;
8
+ const parts = modulePath.split('/');
9
+ if (!namePattern.test(parts[0]) || parts.at(-1) !== 'command.js') return null;
10
+ if (parts.length === 3 && taskPattern.test(parts[1]) && resourcePath === undefined) {
11
+ return { domain: parts[0], kind: 'task', action: parts[1], path: `${parts[0]}.${parts[1]}` };
12
+ }
13
+ if (parts.length !== 4 || !namePattern.test(parts[1]) || !namePattern.test(parts[2])) return null;
14
+ const resources = resourcePath === undefined ? [parts[1]] : resourcePath;
15
+ if (!Array.isArray(resources) || !resources.length ||
16
+ !resources.every(value => typeof value === 'string' && namePattern.test(value)) ||
17
+ resources.join('-') !== parts[1]) return null;
18
+ return { domain: parts[0], kind: 'atomic', action: parts[2], resources,
19
+ path: [parts[0], ...resources, parts[2]].join('.') };
20
+ }
21
+
22
+ module.exports = { nativeCommandLocation };
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ const { Command } = require('commander');
4
+ const { commandPath } = require('./command-paths');
5
+ const { commandSchema } = require('../execution/command-schema');
6
+ const { modelAt, constraints } = require('../execution/error-details');
7
+ const { ParserError } = require('../execution/parser-error');
8
+
9
+ const parserReasons = {
10
+ 'commander.unknownCommand': 'unknown_command',
11
+ 'commander.unknownOption': 'unknown_option',
12
+ 'commander.missingArgument': 'required',
13
+ 'commander.missingMandatoryOptionValue': 'required',
14
+ 'commander.optionMissingArgument': 'missing_value',
15
+ 'commander.invalidArgument': 'invalid_choice',
16
+ 'commander.excessArguments': 'excess_arguments',
17
+ 'commander.conflictingOption': 'constraint'
18
+ };
19
+ const messages = {
20
+ unknown_command: '未知命令,请查看帮助或候选命令',
21
+ unknown_option: '命令中有不支持的选项,请查看此命令 --help',
22
+ excess_arguments: '命令中有多余的位置参数,请查看此命令 --help',
23
+ required: '缺少必填参数,请查看此命令 --help',
24
+ missing_value: '请为参数提供值',
25
+ invalid_choice: '参数值不在允许范围内,请查看此命令 --help',
26
+ constraint: '参数组合不符合要求,请查看此命令 --help'
27
+ };
28
+ const safeName = /^(?:[a-z][a-z0-9-]*|\+[a-z][a-z0-9-]*)$/;
29
+
30
+ function distance(left, right) {
31
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
32
+ for (let i = 0; i < left.length; i++) {
33
+ const row = [i + 1];
34
+ for (let j = 0; j < right.length; j++) {
35
+ row.push(Math.min(row[j] + 1, previous[j + 1] + 1,
36
+ previous[j] + (left[i] === right[j] ? 0 : 1)));
37
+ }
38
+ previous = row;
39
+ }
40
+ return previous[right.length];
41
+ }
42
+
43
+ function suggestedArgv(command, token) {
44
+ if (typeof token !== 'string' || token.length < 3 || token.length > 64 || !safeName.test(token)) return [];
45
+ const prefix = commandPath(command).split('.').filter(Boolean);
46
+ const names = command.createHelp().visibleCommands(command)
47
+ .flatMap(child => [child.name(), ...child.aliases()]).filter(name => safeName.test(name));
48
+ const candidates = [...new Set(names)].map(name => ({ name, distance: distance(token, name) }))
49
+ .filter(candidate => candidate.distance <= (token.length > 5 ? 2 : 1))
50
+ .sort((left, right) => left.distance - right.distance || left.name.localeCompare(right.name, 'en'));
51
+ return candidates.filter(candidate => candidate.distance === candidates[0].distance)
52
+ .slice(0, 3).map(candidate => [...prefix, candidate.name]);
53
+ }
54
+
55
+ function parserFailure(command, reason, { option, argument, token } = {}) {
56
+ const path = commandPath(command);
57
+ const helpArgv = [...path.split('.').filter(Boolean), '--help'];
58
+ const issue = { source: 'argv', reason };
59
+ const schema = commandSchema(command);
60
+ const attribute = option?.attributeName() || argument?.name();
61
+ const modelPath = attribute && `/${attribute}`;
62
+ const model = schema && modelPath && modelAt(schema, 'input', modelPath);
63
+ if (option) issue.option = option.long || option.short;
64
+ if (model) issue.path = modelPath;
65
+ if (option?.argChoices) issue.expected = { enum: [...option.argChoices] };
66
+ else if (model && Object.keys(constraints(model)).length) issue.expected = constraints(model);
67
+ const suggestions = reason === 'unknown_command' ? suggestedArgv(command, token) : [];
68
+ const message = option && reason === 'required' ? `请提供 ${issue.option}` :
69
+ option && reason === 'missing_value' ? `请为 ${issue.option} 提供值` :
70
+ option?.argChoices && reason === 'invalid_choice' ?
71
+ `${issue.option} 只能使用:${option.argChoices.join('、')}` :
72
+ argument && reason === 'required' ? `请提供参数 <${argument.name()}>` : messages[reason];
73
+ return new ParserError(message, {
74
+ command: path, issues: [issue], helpArgv,
75
+ ...(suggestions.length ? { suggestions } : {}),
76
+ nextStep: `${suggestions.length ? `你可能想执行 zan-saas ${suggestions[0].join(' ')};` : ''}` +
77
+ `查看帮助:zan-saas ${helpArgv.join(' ')}`
78
+ });
79
+ }
80
+
81
+ function configureParserPolicy(prepareHelp) {
82
+ const installed = new WeakSet();
83
+ const options = new WeakSet();
84
+ function install(command) {
85
+ if (!installed.has(command)) {
86
+ installed.add(command);
87
+ const help = command.help;
88
+ command.help = function(context) {
89
+ const navigation = context?.error && !this._actionHandler &&
90
+ !this._defaultCommandName && this.args.length === 0;
91
+ return help.call(this, navigation ? { error: false } : context);
92
+ };
93
+ const error = command.error;
94
+ command.error = function(message, settings) {
95
+ const reason = parserReasons[settings?.code];
96
+ if (reason) throw parserFailure(this, reason);
97
+ return error.call(this, message, settings);
98
+ };
99
+ const methods = {
100
+ missingMandatoryOptionValue(option) { throw parserFailure(this, 'required', { option }); },
101
+ optionMissingArgument(option) { throw parserFailure(this, 'missing_value', { option }); },
102
+ missingArgument(name) {
103
+ throw parserFailure(this, 'required', {
104
+ argument: this.registeredArguments.find(argument => argument.name() === name)
105
+ });
106
+ },
107
+ unknownOption() {
108
+ if (!this._allowUnknownOption) throw parserFailure(this, 'unknown_option');
109
+ },
110
+ unknownCommand() {
111
+ prepareHelp(this);
112
+ throw parserFailure(this, 'unknown_command', { token: this.args[0] });
113
+ },
114
+ _excessArguments() {
115
+ if (!this._allowExcessArguments) throw parserFailure(this, 'excess_arguments');
116
+ }
117
+ };
118
+ for (const [name, handler] of Object.entries(methods)) {
119
+ if (command[name] === Command.prototype[name]) command[name] = handler;
120
+ }
121
+ }
122
+ for (const option of command.options) {
123
+ if (options.has(option)) continue;
124
+ options.add(option);
125
+ const parse = option.parseArg;
126
+ if (parse) option.argParser(function(...args) {
127
+ try { return parse.apply(this, args); }
128
+ catch (error) {
129
+ if (error.code === 'commander.invalidArgument') {
130
+ throw parserFailure(command, 'invalid_choice', { option });
131
+ }
132
+ throw error;
133
+ }
134
+ });
135
+ }
136
+ for (const child of command.commands) install(child);
137
+ }
138
+ return install;
139
+ }
140
+
141
+ module.exports = { configureParserPolicy, parserFailure };
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ const { Command } = require('commander');
4
+ const { version } = require('../../package.json');
5
+ const { createSchemaCommand } = require('../commands/system/schema');
6
+ const { createDoctorCommand } = require('../commands/system/doctor');
7
+ const { createApiCommand } = require('../commands/system/api');
8
+ const { createSkillsCommand } = require('../commands/system/skills');
9
+ const { createInstallCommand } = require('../commands/system/install');
10
+ const { createAuthCommand } = require('../commands/system/auth');
11
+ const { createShopCommand } = require('../commands/system/shop');
12
+ const {
13
+ registerNativeDomains, registerNativeDomain, nativeRoot
14
+ } = require('./native-loader');
15
+ const { skillsRoot } = require('./domain-catalog');
16
+ const { validateSkillCatalog } = require('./skill-catalog');
17
+ const { openRegistry } = require('../../internal/registry');
18
+ const { readContext } = require('../identity/context');
19
+ const { contextInput } = require('../identity/context-store');
20
+ const { assertAvailable } = require('../execution/command-policy');
21
+ const { commandSchema } = require('../execution/command-schema');
22
+ const { commandContract, commandPath } = require('./command-paths');
23
+ const { configureContextHelp, isContextHelp } = require('./context-help');
24
+ const { configureParserPolicy } = require('./parser-policy');
25
+
26
+ function createProgram({ catalog = openRegistry(), root = nativeRoot, rootSkills = skillsRoot,
27
+ eagerDomains = false, context = contextInput(process.env) } = {}) {
28
+ const { domains, skills, businessCommandCount } = catalog.manifest;
29
+ const domainNames = new Set(domains.map(domain => domain.name));
30
+ const loadedDomains = new Set();
31
+ const program = new Command();
32
+ const contextState = readContext(context);
33
+ const installParsing = configureParserPolicy(prepareHelp);
34
+ const installHelp = configureContextHelp(program, contextState, loadDomain);
35
+
36
+ function loadDomain(name) {
37
+ if (loadedDomains.has(name)) return;
38
+ const domain = domains.find(entry => entry.name === name);
39
+ if (!domain) return;
40
+ const index = catalog.loadDomain(name);
41
+ registerNativeDomain(program, domain, index.businessCommands, root, rootSkills,
42
+ contextState.context);
43
+ loadedDomains.add(name);
44
+ installHelp();
45
+ installParsing(program);
46
+ }
47
+
48
+ program.name('zan-saas')
49
+ .description('有赞 SaaS 命令行工具')
50
+ .version(version, '-V, --version', '显示版本号')
51
+ .exitOverride()
52
+ .configureOutput({ writeErr: () => {} });
53
+
54
+ program.addCommand(createSchemaCommand(() => program, path => {
55
+ if (!path) {
56
+ for (const domain of domains) loadDomain(domain.name);
57
+ return;
58
+ }
59
+ const name = path.split('.')[0];
60
+ if (domainNames.has(name)) loadDomain(name);
61
+ }));
62
+ program.addCommand(createDoctorCommand(businessCommandCount));
63
+ program.addCommand(createApiCommand());
64
+ program.addCommand(createAuthCommand());
65
+ program.addCommand(createShopCommand());
66
+ program.addCommand(createSkillsCommand(skills, rootSkills));
67
+ program.addCommand(createInstallCommand());
68
+ function configureSystemErrors(command) {
69
+ command.exitOverride().configureOutput({ writeErr: program.configureOutput().writeErr });
70
+ for (const child of command.commands) configureSystemErrors(child);
71
+ }
72
+ for (const command of program.commands) configureSystemErrors(command);
73
+ registerNativeDomains(program, domains);
74
+ validateSkillCatalog(skills, domains);
75
+ installHelp();
76
+ installParsing(program);
77
+
78
+ program.hook('preSubcommand', (parent, command) => {
79
+ if (parent === program && domainNames.has(command.name())) loadDomain(command.name());
80
+ installHelp(command);
81
+ installParsing(command);
82
+ });
83
+ program.hook('preAction', (_parent, command) => {
84
+ if (isContextHelp(command)) return;
85
+ const contract = commandContract(commandPath(command), command);
86
+ if (['atomic', 'task'].includes(contract.kind)) {
87
+ const details = commandSchema(command)?.error?.properties?.details;
88
+ const stages = details?.properties?.stage?.enum;
89
+ assertAvailable(contract.at, contextState, {
90
+ dryRun: command.opts().dryRun === true, command: commandPath(command),
91
+ includeDetails: Boolean(details),
92
+ contextStage: stages?.includes('configuration') && !stages.includes('context')
93
+ ? 'configuration' : 'context'
94
+ });
95
+ }
96
+ });
97
+ function prepareHelp(target) {
98
+ if (target === program) {
99
+ for (const domain of domains) loadDomain(domain.name);
100
+ return;
101
+ }
102
+ let command = target;
103
+ while (command.parent && command.parent !== program) command = command.parent;
104
+ if (command.parent === program && domainNames.has(command.name())) {
105
+ loadDomain(command.name());
106
+ }
107
+ }
108
+ program.on('beforeAllHelp', helpContext => prepareHelp(helpContext.command));
109
+
110
+ if (skills.some(skill => skill.type === 'entry')) {
111
+ program.addHelpText('after', `
112
+ 快速开始:
113
+ 1. 登录:zan-saas auth login,按提示在浏览器完成授权
114
+ 2. 查看当前店铺:zan-saas shop current;已有有效上下文时沿用
115
+ 3. 需要选店时:zan-saas shop list,按用户指定的店名或选择执行 zan-saas shop use <kdtId>
116
+ 4. 阅读使用指引:zan-saas skills read zan-saas
117
+
118
+ 查询命令:
119
+ zan-saas help auth login
120
+ zan-saas help <domain> <resource> <action>
121
+ zan-saas schema <path>
122
+ 命令路径中的各部分须分别传参;也可使用各层命令的 --help。
123
+
124
+ 领域业务命令需要登录凭证和店铺上下文;--dry-run 仅预览,不访问业务接口。
125
+ 当前领域业务命令均为只读查询;api 为独立的原始接口入口,调用前查看 zan-saas help api。
126
+ 使用指引解释操作方式,可执行命令以当前安装版的 help 和 schema 为准。`);
127
+ }
128
+ if (eagerDomains) {
129
+ for (const domain of domains) loadDomain(domain.name);
130
+ }
131
+ return program;
132
+ }
133
+
134
+ module.exports = { createProgram };