@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,241 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const crypto = require('node:crypto');
6
+
7
+ const systemSkillDefinitions = Object.freeze([
8
+ {
9
+ name: 'zan-saas',
10
+ type: 'entry',
11
+ dependsOn: ['zan-saas-shared']
12
+ },
13
+ {
14
+ name: 'zan-saas-shared',
15
+ type: 'shared',
16
+ dependsOn: []
17
+ }
18
+ ]);
19
+
20
+ function regularFile(filename) {
21
+ return fs.lstatSync(filename).isFile();
22
+ }
23
+
24
+ function parseFrontmatter(content) {
25
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
26
+ const fields = {};
27
+ for (const line of match?.[1]?.split(/\r?\n/) || []) {
28
+ const field = line.match(/^(name|description):[ \t]*(.*)$/);
29
+ if (!field) continue;
30
+ if (Object.hasOwn(fields, field[1])) {
31
+ throw new Error(`Duplicate skill frontmatter field: ${field[1]}`);
32
+ }
33
+ fields[field[1]] = field[2].trim();
34
+ }
35
+ return fields;
36
+ }
37
+
38
+ function validateSkillName(name) {
39
+ if (typeof name !== 'string' ||
40
+ !(name === 'zan-saas' || /^zan-saas-[a-z][a-z0-9-]*$/.test(name))) {
41
+ throw new Error(`Invalid skill name: ${name}`);
42
+ }
43
+ }
44
+
45
+ function safeRelativePath(relativePath) {
46
+ if (typeof relativePath !== 'string') throw new Error('Invalid skill path');
47
+ const parts = relativePath.split('/');
48
+ if (!relativePath.endsWith('.md') ||
49
+ !parts.length ||
50
+ parts.some(part => !part || part === '.' || part === '..' || part.includes('\\'))) {
51
+ throw new Error('Invalid skill path');
52
+ }
53
+ }
54
+
55
+ function skillDirectory(root, skill) {
56
+ validateSkillName(skill);
57
+ return path.join(root, skill);
58
+ }
59
+
60
+ function readSkillFile(root, skill, relativePath = 'SKILL.md') {
61
+ safeRelativePath(relativePath);
62
+ let current = skillDirectory(root, skill);
63
+ try {
64
+ for (const part of relativePath.split('/')) {
65
+ if (!fs.lstatSync(current).isDirectory()) throw new Error('Invalid skill path');
66
+ current = path.join(current, part);
67
+ }
68
+ if (!regularFile(current)) throw new Error('Invalid skill path');
69
+ return fs.readFileSync(current, 'utf8');
70
+ } catch (error) {
71
+ if (['ENOENT', 'ENOTDIR'].includes(error.code)) {
72
+ throw new Error(`Skill file not found: ${skill}/${relativePath}`);
73
+ }
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ function markdownFiles(root, skill) {
79
+ const base = skillDirectory(root, skill);
80
+ const files = [];
81
+
82
+ function visit(current, relative = '') {
83
+ for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) =>
84
+ left.name < right.name ? -1 : left.name > right.name ? 1 : 0)) {
85
+ const filename = path.join(current, entry.name);
86
+ const childRelative = relative ? `${relative}/${entry.name}` : entry.name;
87
+ if (entry.isDirectory()) visit(filename, childRelative);
88
+ else if (entry.isFile()) {
89
+ if (!entry.name.endsWith('.md')) {
90
+ throw new Error(`Only Markdown files can be bundled in a skill: ${filename}`);
91
+ }
92
+ files.push(childRelative);
93
+ } else {
94
+ throw new Error(`Unsupported skill file: ${filename}`);
95
+ }
96
+ }
97
+ }
98
+
99
+ try {
100
+ if (!fs.lstatSync(base).isDirectory()) throw new Error(`Invalid skill directory: ${skill}`);
101
+ } catch (error) {
102
+ if (error.code === 'ENOENT') throw new Error(`Skill directory not found: ${skill}`);
103
+ throw error;
104
+ }
105
+ visit(base);
106
+ return files;
107
+ }
108
+
109
+ function validateSkillFrontmatter(root, definition) {
110
+ const content = readSkillFile(root, definition.name);
111
+ const frontmatter = parseFrontmatter(content);
112
+ if (frontmatter.name !== definition.name || !frontmatter.description) {
113
+ throw new Error(`Skill needs matching name and description: ${definition.name}`);
114
+ }
115
+ return {
116
+ ...definition,
117
+ description: frontmatter.description
118
+ };
119
+ }
120
+
121
+ function buildSkillCatalog(root, domains, businessCommands) {
122
+ const definitions = [
123
+ ...systemSkillDefinitions,
124
+ ...domains.map(domain => ({
125
+ name: domain.skill,
126
+ type: 'domain',
127
+ domain: domain.name,
128
+ dependsOn: ['zan-saas-shared']
129
+ }))
130
+ ];
131
+ const names = new Set(definitions.map(entry => entry.name));
132
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
133
+ if (entry.isFile() && entry.name === 'README.md') continue;
134
+ if (!entry.isDirectory() || !names.has(entry.name)) {
135
+ throw new Error(`Skill has no published registration: ${entry.name}`);
136
+ }
137
+ }
138
+ const catalog = definitions.map(definition => {
139
+ const loaded = validateSkillFrontmatter(root, definition);
140
+ const files = markdownFiles(root, definition.name);
141
+ const hash = crypto.createHash('sha256');
142
+ for (const filename of files) {
143
+ hash.update(filename);
144
+ hash.update('\0');
145
+ hash.update(readSkillFile(root, definition.name, filename));
146
+ hash.update('\0');
147
+ }
148
+ const domain = domains.find(entry => entry.name === definition.domain);
149
+ return {
150
+ name: loaded.name,
151
+ type: loaded.type,
152
+ ...(domain ? { domain: domain.name } : {}),
153
+ description: domain ? domain.description : loaded.description,
154
+ dependsOn: [...loaded.dependsOn],
155
+ commandRefs: businessCommands.filter(command => domain &&
156
+ command.path.startsWith(`${domain.name}.`)).map(command => command.path).sort(),
157
+ files,
158
+ fingerprint: hash.digest('hex')
159
+ };
160
+ });
161
+ validateSkillCatalog(catalog, domains, businessCommands);
162
+ return catalog;
163
+ }
164
+
165
+ function validateSkillCatalog(catalog, domains, businessCommands) {
166
+ if (!Array.isArray(catalog)) throw new Error('Skill catalog is missing');
167
+ const entries = new Map();
168
+ const domainMap = new Map(domains.map(domain => [domain.name, domain]));
169
+ const commands = new Set((businessCommands || []).map(command => command.path));
170
+ const validateCommandRefs = businessCommands !== undefined;
171
+ for (const entry of catalog) {
172
+ if (!entry || typeof entry !== 'object') throw new Error('Invalid skill catalog entry');
173
+ validateSkillName(entry.name);
174
+ const system = systemSkillDefinitions.find(candidate => candidate.name === entry.name);
175
+ const domain = domainMap.get(entry.domain);
176
+ const validOwner = system
177
+ ? entry.type === system.type && entry.domain === undefined
178
+ : entry.type === 'domain' && domain?.skill === entry.name;
179
+ const commandRefsInvalid = validateCommandRefs
180
+ ? !Array.isArray(entry.commandRefs) ||
181
+ new Set(entry.commandRefs).size !== entry.commandRefs.length ||
182
+ entry.commandRefs.some(key => !commands.has(key) ||
183
+ !domain || !key.startsWith(`${domain.name}.`))
184
+ : entry.commandRefs !== undefined &&
185
+ (!Array.isArray(entry.commandRefs) ||
186
+ new Set(entry.commandRefs).size !== entry.commandRefs.length);
187
+ if (!validOwner || entries.has(entry.name) ||
188
+ typeof entry.description !== 'string' || !entry.description.trim() ||
189
+ !Array.isArray(entry.dependsOn) || !entry.dependsOn.every(name => typeof name === 'string') ||
190
+ new Set(entry.dependsOn).size !== entry.dependsOn.length ||
191
+ commandRefsInvalid ||
192
+ !Array.isArray(entry.files) || !entry.files.includes('SKILL.md') ||
193
+ new Set(entry.files).size !== entry.files.length ||
194
+ !/^[a-f0-9]{64}$/.test(entry.fingerprint)) {
195
+ throw new Error(`Invalid skill catalog entry: ${entry.name}`);
196
+ }
197
+ for (const file of entry.files) safeRelativePath(file);
198
+ const required = system ? system.dependsOn : ['zan-saas-shared'];
199
+ if (required.some(name => !entry.dependsOn.includes(name))) {
200
+ throw new Error(`Skill dependency is missing: ${entry.name}`);
201
+ }
202
+ entries.set(entry.name, entry);
203
+ }
204
+ if (catalog.length && !entries.has('zan-saas')) {
205
+ throw new Error('Skill entry not published: zan-saas');
206
+ }
207
+ for (const domain of domains) {
208
+ if (!entries.has(domain.skill)) throw new Error(`Domain skill not published: ${domain.skill}`);
209
+ }
210
+ const visiting = new Set();
211
+ const visited = new Set();
212
+ function visit(name) {
213
+ if (visiting.has(name)) throw new Error(`Skill dependency cycle: ${name}`);
214
+ if (visited.has(name)) return;
215
+ const entry = entries.get(name);
216
+ if (!entry) throw new Error(`Skill dependency not published: ${name}`);
217
+ visiting.add(name);
218
+ for (const dependency of entry.dependsOn) visit(dependency);
219
+ visiting.delete(name);
220
+ visited.add(name);
221
+ }
222
+ for (const name of entries.keys()) visit(name);
223
+ }
224
+
225
+ function readPublishedSkillFile(root, catalog, skill, relativePath = 'SKILL.md') {
226
+ const entry = catalog.find(candidate => candidate.name === skill);
227
+ if (!entry) throw new Error(`Skill not published: ${skill}`);
228
+ safeRelativePath(relativePath);
229
+ if (!entry.files.includes(relativePath)) {
230
+ throw new Error(`Skill file not published: ${skill}/${relativePath}`);
231
+ }
232
+ return readSkillFile(root, skill, relativePath);
233
+ }
234
+
235
+ module.exports = {
236
+ buildSkillCatalog,
237
+ readPublishedSkillFile,
238
+ readSkillFile,
239
+ validateSkillFrontmatter,
240
+ validateSkillCatalog
241
+ };
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": 3,
3
+ "domain": "access",
4
+ "businessCommands": [
5
+ {
6
+ "path": "access.role.list",
7
+ "kind": "atomic",
8
+ "risk": "read",
9
+ "availability": "available",
10
+ "at": "all",
11
+ "module": "access/role/list/command.js",
12
+ "fingerprint": "1f1a25bdb5f2252635920dd6f154e7692cefbe5c37bf5c164aff4c0c73a23313"
13
+ },
14
+ {
15
+ "path": "access.staff-perms.get",
16
+ "kind": "atomic",
17
+ "risk": "read",
18
+ "availability": "available",
19
+ "at": "shop",
20
+ "module": "access/staff-perms/get/command.js",
21
+ "fingerprint": "65cb2c9fdefa0300d8a35f6d2951af997a1251f40f64172505898d56d9a810ce"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "访问控制:店铺角色定义与员工权限点"
3
+ }
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ const { withTargetShopDefault } = require('../../../../../execution/target-shop-default');
4
+
5
+ const { Command, Option } = require('commander');
6
+ const { resolveTargetShop } = require('../../../../../execution/target-shop');
7
+ const { readContext } = require('../../../../../identity/context');
8
+ const { validateCommandValue } = require('../../../../../execution/command-schema');
9
+ const { readApiEnvelope, assertApiSuccess, object } = require('../../../../../execution/query-api');
10
+ const { failure, bindingPath } = require('../../../../../execution/shop-query');
11
+ const { snapshotApiEnvironment } = require('../../../../../transport/api');
12
+ const schema = require('./schema.json');
13
+
14
+ const command = 'access.role.list';
15
+ const organizationPath = 'youzan.shop.chain.descendent.organization.list/1.0.1';
16
+ const maxOrganizationPages = 20;
17
+ const pageSize = 50;
18
+ const reject = (category, stage, scope, state, facts) =>
19
+ failure(command, category, stage, scope, state, facts);
20
+
21
+ function organizationBody(pageNo) {
22
+ return { page_num: pageNo, page_size: pageSize, with_dept_info: false,
23
+ with_shop_status_and_lifecycle_end_time: false, with_crm_shop: false };
24
+ }
25
+
26
+ function previewFor(input, context) {
27
+ const crossShop = context && String(input.kdtId) !== context.shopId;
28
+ const requests = [{ stage: 'shop-binding', method: 'POST', path: bindingPath, body: {} }];
29
+ if (crossShop) requests.push({ stage: 'target-membership', method: 'POST',
30
+ path: organizationPath, body: organizationBody(1) });
31
+ requests.push({ stage: 'roles', method: 'POST', path: schema.api.path,
32
+ body: { third_tenant_id: String(input.kdtId), third_user_id: '-1',
33
+ namespace: 'np_yz_shop', filter: { biz_key_group: 'shop_ability' } } });
34
+ return { scope: input, requests, maxOrganizationPages };
35
+ }
36
+
37
+ async function verifyMembership(read, input) {
38
+ const seen = new Set();
39
+ let total;
40
+ for (let pageNo = 1; pageNo <= maxOrganizationPages; pageNo += 1) {
41
+ const { data, facts } = await read(organizationPath, organizationBody(pageNo), 'target-membership');
42
+ const invalid = category => reject(category, 'target-membership', input, 'sent', facts);
43
+ if (!object(data) || !Array.isArray(data.organization_list) ||
44
+ data.page_num !== pageNo || data.page_size !== pageSize ||
45
+ !Number.isSafeInteger(data.total) || data.total < 0 ||
46
+ data.organization_list.length !== Math.min(pageSize, Math.max(0, data.total - seen.size)) ||
47
+ (total !== undefined && total !== data.total)) throw invalid('protocol');
48
+ total = data.total;
49
+ for (const unit of data.organization_list) {
50
+ if (!object(unit) || !Number.isSafeInteger(unit.kdt_id) || unit.kdt_id < 1 ||
51
+ !Number.isSafeInteger(unit.role) || unit.role < 0 || unit.role === 9 ||
52
+ seen.has(unit.kdt_id)) throw invalid('protocol');
53
+ seen.add(unit.kdt_id);
54
+ }
55
+ if (seen.has(input.kdtId)) return;
56
+ if (seen.size === total || pageNo === maxOrganizationPages) throw invalid('scope_unresolved');
57
+ }
58
+ }
59
+
60
+ async function execute(input, context, preview) {
61
+ let env;
62
+ try { env = await snapshotApiEnvironment(undefined, context.shopId); }
63
+ catch { throw reject('configuration', 'configuration', input, 'not_sent'); }
64
+ async function read(path, body, stage) {
65
+ const { result, facts } = await readApiEnvelope(path, body,
66
+ (category, details, state, at) => reject(category,
67
+ at === 'configuration' ? at : stage, input, state, details), env);
68
+ assertApiSuccess(result, category => { throw reject(category, stage, input, 'sent', facts); });
69
+ return { data: result.data, facts };
70
+ }
71
+ const binding = await read(bindingPath, {}, 'shop-binding');
72
+ if (!object(binding.data) || !Number.isSafeInteger(binding.data.sid) || binding.data.sid < 1) {
73
+ throw reject('protocol', 'shop-binding', input, 'sent', binding.facts);
74
+ }
75
+ if (String(binding.data.sid) !== context.shopId) {
76
+ throw reject('scope_unresolved', 'shop-binding', input, 'sent', binding.facts);
77
+ }
78
+ if (String(input.kdtId) !== context.shopId) {
79
+ if (context.orgType !== 'hq' || String(binding.data.sid) !== context.hq || binding.data.role !== 1) {
80
+ throw reject('scope_unresolved', 'shop-binding', input, 'sent', binding.facts);
81
+ }
82
+ await verifyMembership(read, input);
83
+ }
84
+ const request = preview.requests.at(-1);
85
+ const { data, facts } = await read(request.path, request.body, request.stage);
86
+ try {
87
+ if (!Array.isArray(data)) throw new TypeError('Invalid roles');
88
+ const roles = data.map(source => {
89
+ if (!object(source)) throw new TypeError('Invalid role');
90
+ const role = { roleId: source.role_id, name: source.name };
91
+ for (const [field, upstream] of Object.entries({ bizCode: 'biz_code',
92
+ roleType: 'role_type_value', remark: 'remark' })) {
93
+ if (source[upstream] !== undefined && source[upstream] !== null) role[field] = source[upstream];
94
+ }
95
+ return role;
96
+ });
97
+ const output = { data: { roles } };
98
+ validateCommandValue(schema, 'output', output);
99
+ return output;
100
+ } catch {
101
+ throw reject('protocol', 'roles-model', input, 'sent', facts);
102
+ }
103
+ }
104
+
105
+ function createCommand({ context } = {}) {
106
+ return new Command('list')
107
+ .description('查询目标店铺本次返回的角色定义目录,不查询员工角色或有效权限')
108
+ .allowExcessArguments(false)
109
+ .exitOverride(error => {
110
+ if (['commander.helpDisplayed', 'commander.version'].includes(error.code)) throw error;
111
+ throw reject('parameter', 'input');
112
+ })
113
+ .option('--kdt-id <id>', '目标店铺正安全整数;省略取上下文 shopId,非总部不可跨店')
114
+ .addOption(new Option('--format <format>', 'json 或 pretty,仅改变缩进')
115
+ .choices(['json', 'pretty']).default('json'))
116
+ .option('--dry-run', '零网络预览,不读取凭证,不声称已核实归属')
117
+ .addHelpText('after', `
118
+ 输出 data.roles,核心 roleId、name;可选 bizCode、roleType、remark 仅按返回映射。
119
+ 可选字符串字段缺失或为 null 时省略,空字符串保留。
120
+ roleType 保留原始整数码,不推导 system/custom。空目录只表示本次查询为空。
121
+ 保留原顺序,不排序、去重、分页或截断角色,不承诺返回全系统角色。
122
+ 先核实凭证主体与当前店铺;总部跨店另查下级组织,每页 50 条、最多 20 页。
123
+ 归属未核实、分页异常或达到核验预算时停止,不发角色请求,不换凭证或重试。
124
+ 筛选项均不发送;目录和组织角色不证明店铺员工授权或权限隔离验收通过。
125
+ 契约:zan-saas schema access.role.list --format json
126
+ 示例:zan-saas access role list --kdt-id 63077 --dry-run
127
+ `)
128
+ .action(async options => {
129
+ const input = resolveTargetShop(options, context, command);
130
+ const state = readContext(context);
131
+ const rawTarget = Object.hasOwn(options, 'kdtId') ? options.kdtId : state.context?.shopId;
132
+ if (String(input.kdtId) !== rawTarget) throw reject('validation', 'input', input);
133
+ validateCommandValue(schema, 'input', input);
134
+ const preview = previewFor(input, state.context);
135
+ validateCommandValue(schema, 'preview', preview);
136
+ const output = options.dryRun ? preview : await execute(input, state.context, preview);
137
+ console.log(JSON.stringify(output, null, options.format === 'pretty' ? 2 : 0));
138
+ });
139
+ }
140
+
141
+ module.exports = { kind: 'atomic', risk: 'read', at: 'all', createCommand: withTargetShopDefault(createCommand) };