@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.
- package/DEPENDENCIES.json +50 -0
- package/INSTALL.md +79 -0
- package/LICENSE +21 -0
- package/NOTICE +1 -0
- package/README.md +151 -2
- package/THIRD_PARTY_NOTICES.txt +162 -0
- package/bin/zan-saas-cli.js +28 -0
- package/internal/registry/index.js +123 -0
- package/internal/registry/manifest.json +424 -0
- package/npm-shrinkwrap.json +86 -0
- package/package.json +33 -4
- package/skills/README.md +30 -0
- package/skills/zan-saas/SKILL.md +54 -0
- package/skills/zan-saas/references/host-entry.md +25 -0
- package/skills/zan-saas-access/SKILL.md +37 -0
- package/skills/zan-saas-access/references/role-list.md +11 -0
- package/skills/zan-saas-access/references/staff-perms-get.md +11 -0
- package/skills/zan-saas-aftersale/SKILL.md +31 -0
- package/skills/zan-saas-aftersale/references/case-list.md +13 -0
- package/skills/zan-saas-aftersale/references/case-scan.md +31 -0
- package/skills/zan-saas-benefit/SKILL.md +46 -0
- package/skills/zan-saas-benefit/references/card-get.md +7 -0
- package/skills/zan-saas-benefit/references/card-grant-list.md +11 -0
- package/skills/zan-saas-benefit/references/card-list.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-get.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-list.md +7 -0
- package/skills/zan-saas-coupon/SKILL.md +41 -0
- package/skills/zan-saas-coupon/references/activity-get.md +11 -0
- package/skills/zan-saas-coupon/references/activity-list.md +15 -0
- package/skills/zan-saas-coupon/references/voucher-get.md +13 -0
- package/skills/zan-saas-coupon/references/voucher-list.md +25 -0
- package/skills/zan-saas-customer/SKILL.md +73 -0
- package/skills/zan-saas-customer/references/customer-scan.md +11 -0
- package/skills/zan-saas-customer/references/segment-list.md +22 -0
- package/skills/zan-saas-fulfillment/SKILL.md +36 -0
- package/skills/zan-saas-fulfillment/references/delivery-get.md +11 -0
- package/skills/zan-saas-fulfillment/references/shipment-list.md +11 -0
- package/skills/zan-saas-fulfillment/references/waybill-get.md +9 -0
- package/skills/zan-saas-interaction/SKILL.md +37 -0
- package/skills/zan-saas-interaction/references/lottery-get.md +11 -0
- package/skills/zan-saas-interaction/references/lottery-list.md +19 -0
- package/skills/zan-saas-item/SKILL.md +61 -0
- package/skills/zan-saas-item/references/attribute-list.md +11 -0
- package/skills/zan-saas-item/references/brand-list.md +7 -0
- package/skills/zan-saas-item/references/classification-get.md +7 -0
- package/skills/zan-saas-item/references/classification-list.md +7 -0
- package/skills/zan-saas-item/references/group-list.md +9 -0
- package/skills/zan-saas-item/references/item-content.md +9 -0
- package/skills/zan-saas-item/references/item-get.md +9 -0
- package/skills/zan-saas-item/references/item-search.md +23 -0
- package/skills/zan-saas-item/references/product-identity.md +11 -0
- package/skills/zan-saas-member/SKILL.md +41 -0
- package/skills/zan-saas-member/references/customer-level-get.md +7 -0
- package/skills/zan-saas-member/references/growth-log-list.md +17 -0
- package/skills/zan-saas-member/references/membership-get.md +7 -0
- package/skills/zan-saas-order/SKILL.md +56 -0
- package/skills/zan-saas-order/references/order-get.md +26 -0
- package/skills/zan-saas-order/references/order-list.md +25 -0
- package/skills/zan-saas-order/references/order-scan.md +35 -0
- package/skills/zan-saas-org/SKILL.md +37 -0
- package/skills/zan-saas-org/references/staff-get.md +15 -0
- package/skills/zan-saas-org/references/staff-role-get.md +11 -0
- package/skills/zan-saas-org/references/unit-get.md +11 -0
- package/skills/zan-saas-org/references/unit-list.md +15 -0
- package/skills/zan-saas-points/SKILL.md +42 -0
- package/skills/zan-saas-points/references/account-get.md +7 -0
- package/skills/zan-saas-points/references/log-list.md +17 -0
- package/skills/zan-saas-points/references/rule-list.md +2 -0
- package/skills/zan-saas-privacy/SKILL.md +30 -0
- package/skills/zan-saas-privacy/references/field-decrypt.md +19 -0
- package/skills/zan-saas-shared/SKILL.md +94 -0
- package/skills/zan-saas-shared/references/authentication.md +52 -0
- package/skills/zan-saas-shared/references/value-processing.md +27 -0
- package/skills/zan-saas-stock/SKILL.md +28 -0
- package/skills/zan-saas-stock/references/log-list.md +11 -0
- package/skills/zan-saas-valuecard/SKILL.md +37 -0
- package/skills/zan-saas-valuecard/references/card-get.md +13 -0
- package/skills/zan-saas-valuecard/references/card-list.md +11 -0
- package/src/auth/browser.js +28 -0
- package/src/auth/callback-server.js +99 -0
- package/src/auth/config.js +53 -0
- package/src/auth/credential-store.js +109 -0
- package/src/auth/oauth-client.js +105 -0
- package/src/auth/pkce.js +28 -0
- package/src/auth/session.js +178 -0
- package/src/auth/tool.js +25 -0
- package/src/cli/command-paths.js +126 -0
- package/src/cli/context-help.js +102 -0
- package/src/cli/diagnostic-command.js +57 -0
- package/src/cli/domain-catalog.js +42 -0
- package/src/cli/help-text.js +32 -0
- package/src/cli/native-loader.js +120 -0
- package/src/cli/native-path.js +22 -0
- package/src/cli/parser-policy.js +141 -0
- package/src/cli/program.js +134 -0
- package/src/cli/skill-catalog.js +241 -0
- package/src/commands/native/access/commands.index.json +24 -0
- package/src/commands/native/access/domain.json +3 -0
- package/src/commands/native/access/role/list/command.js +141 -0
- package/src/commands/native/access/role/list/schema.json +393 -0
- package/src/commands/native/access/staff-perms/get/command.js +119 -0
- package/src/commands/native/access/staff-perms/get/schema.json +703 -0
- package/src/commands/native/aftersale/case/get/command.js +104 -0
- package/src/commands/native/aftersale/case/get/exact-json.js +52 -0
- package/src/commands/native/aftersale/case/get/money.js +28 -0
- package/src/commands/native/aftersale/case/get/projection.js +120 -0
- package/src/commands/native/aftersale/case/get/query.js +72 -0
- package/src/commands/native/aftersale/case/get/schema.json +134 -0
- package/src/commands/native/aftersale/case/list/command.js +135 -0
- package/src/commands/native/aftersale/case/list/schema.json +1095 -0
- package/src/commands/native/aftersale/case/scan/command.js +110 -0
- package/src/commands/native/aftersale/case/scan/query.js +62 -0
- package/src/commands/native/aftersale/case/scan/scan.js +78 -0
- package/src/commands/native/aftersale/case/scan/schema.json +964 -0
- package/src/commands/native/aftersale/case/scan/search.js +108 -0
- package/src/commands/native/aftersale/commands.index.json +42 -0
- package/src/commands/native/aftersale/domain.json +3 -0
- package/src/commands/native/aftersale/refund/list/command.js +97 -0
- package/src/commands/native/aftersale/refund/list/money.js +25 -0
- package/src/commands/native/aftersale/refund/list/projection.js +50 -0
- package/src/commands/native/aftersale/refund/list/query.js +73 -0
- package/src/commands/native/aftersale/refund/list/schema.json +130 -0
- package/src/commands/native/aftersale/shared/query.js +58 -0
- package/src/commands/native/aftersale/shared/search.js +128 -0
- package/src/commands/native/benefit/card-definition/get/command.js +110 -0
- package/src/commands/native/benefit/card-definition/get/schema.json +545 -0
- package/src/commands/native/benefit/card-definition/list/command.js +106 -0
- package/src/commands/native/benefit/card-definition/list/schema.json +690 -0
- package/src/commands/native/benefit/card-grant/list/command.js +135 -0
- package/src/commands/native/benefit/card-grant/list/schema.json +758 -0
- package/src/commands/native/benefit/commands.index.json +51 -0
- package/src/commands/native/benefit/customer-card/get/command.js +110 -0
- package/src/commands/native/benefit/customer-card/get/schema.json +340 -0
- package/src/commands/native/benefit/customer-card/list/command.js +103 -0
- package/src/commands/native/benefit/customer-card/list/schema.json +731 -0
- package/src/commands/native/benefit/domain.json +3 -0
- package/src/commands/native/coupon/activity/get/command.js +183 -0
- package/src/commands/native/coupon/activity/get/schema.json +624 -0
- package/src/commands/native/coupon/activity/list/command.js +169 -0
- package/src/commands/native/coupon/activity/list/schema.json +845 -0
- package/src/commands/native/coupon/commands.index.json +42 -0
- package/src/commands/native/coupon/domain.json +3 -0
- package/src/commands/native/coupon/shared/query.js +15 -0
- package/src/commands/native/coupon/voucher/get/command.js +113 -0
- package/src/commands/native/coupon/voucher/get/schema.json +147 -0
- package/src/commands/native/coupon/voucher/list/command.js +155 -0
- package/src/commands/native/coupon/voucher/list/schema.json +772 -0
- package/src/commands/native/customer/commands.index.json +96 -0
- package/src/commands/native/customer/customer/get/adapter.js +82 -0
- package/src/commands/native/customer/customer/get/command.js +106 -0
- package/src/commands/native/customer/customer/get/schema.json +958 -0
- package/src/commands/native/customer/customer/list/command.js +78 -0
- package/src/commands/native/customer/customer/list/input.js +145 -0
- package/src/commands/native/customer/customer/list/query.js +103 -0
- package/src/commands/native/customer/customer/list/schema.json +969 -0
- package/src/commands/native/customer/customer/scan/command.js +80 -0
- package/src/commands/native/customer/customer/scan/scan.js +92 -0
- package/src/commands/native/customer/customer/scan/schema.json +211 -0
- package/src/commands/native/customer/customer-tag/get/command.js +166 -0
- package/src/commands/native/customer/customer-tag/get/schema.json +317 -0
- package/src/commands/native/customer/customer-tag/list/command.js +143 -0
- package/src/commands/native/customer/customer-tag/list/schema.json +259 -0
- package/src/commands/native/customer/domain.json +3 -0
- package/src/commands/native/customer/segment/list/command.js +96 -0
- package/src/commands/native/customer/segment/list/query.js +98 -0
- package/src/commands/native/customer/segment/list/schema.json +135 -0
- package/src/commands/native/customer/segment-customer/list/command.js +171 -0
- package/src/commands/native/customer/segment-customer/list/schema.json +123 -0
- package/src/commands/native/customer/shared/search-input.js +179 -0
- package/src/commands/native/customer/shared/search-page.js +54 -0
- package/src/commands/native/customer/tag/get/command.js +73 -0
- package/src/commands/native/customer/tag/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/get/command.js +80 -0
- package/src/commands/native/customer/tagcategory/get/query.js +98 -0
- package/src/commands/native/customer/tagcategory/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/list/command.js +127 -0
- package/src/commands/native/customer/tagcategory/list/schema.json +394 -0
- package/src/commands/native/fulfillment/commands.index.json +33 -0
- package/src/commands/native/fulfillment/delivery/get/command.js +107 -0
- package/src/commands/native/fulfillment/delivery/get/projection.js +142 -0
- package/src/commands/native/fulfillment/delivery/get/schema.json +173 -0
- package/src/commands/native/fulfillment/domain.json +3 -0
- package/src/commands/native/fulfillment/shipment/list/command.js +83 -0
- package/src/commands/native/fulfillment/shipment/list/projection.js +130 -0
- package/src/commands/native/fulfillment/shipment/list/schema.json +752 -0
- package/src/commands/native/fulfillment/waybill/get/command.js +193 -0
- package/src/commands/native/fulfillment/waybill/get/response.js +68 -0
- package/src/commands/native/fulfillment/waybill/get/schema.json +190 -0
- package/src/commands/native/interaction/commands.index.json +24 -0
- package/src/commands/native/interaction/domain.json +3 -0
- package/src/commands/native/interaction/lottery/get/command.js +61 -0
- package/src/commands/native/interaction/lottery/get/query.js +152 -0
- package/src/commands/native/interaction/lottery/get/schema.json +1004 -0
- package/src/commands/native/interaction/lottery/list/command.js +176 -0
- package/src/commands/native/interaction/lottery/list/schema.json +898 -0
- package/src/commands/native/item/attribute/list/command.js +122 -0
- package/src/commands/native/item/attribute/list/schema.json +727 -0
- package/src/commands/native/item/brand/list/command.js +114 -0
- package/src/commands/native/item/brand/list/schema.json +725 -0
- package/src/commands/native/item/classification/get/command.js +87 -0
- package/src/commands/native/item/classification/get/schema.json +596 -0
- package/src/commands/native/item/classification/list/command.js +105 -0
- package/src/commands/native/item/classification/list/schema.json +676 -0
- package/src/commands/native/item/commands.index.json +87 -0
- package/src/commands/native/item/domain.json +1 -0
- package/src/commands/native/item/group/list/command.js +122 -0
- package/src/commands/native/item/group/list/schema.json +809 -0
- package/src/commands/native/item/item/get/command.js +122 -0
- package/src/commands/native/item/item/get/schema.json +3799 -0
- package/src/commands/native/item/item/list/command.js +7 -0
- package/src/commands/native/item/item/list/schema.json +1118 -0
- package/src/commands/native/item/item/scan/command.js +7 -0
- package/src/commands/native/item/item/scan/schema.json +1046 -0
- package/src/commands/native/item/item-content/get/command.js +54 -0
- package/src/commands/native/item/item-content/get/schema.json +677 -0
- package/src/commands/native/item/shared/api.js +31 -0
- package/src/commands/native/item/shared/content.js +20 -0
- package/src/commands/native/item/shared/diagnostics.js +61 -0
- package/src/commands/native/item/shared/h5.js +63 -0
- package/src/commands/native/item/shared/identity.js +62 -0
- package/src/commands/native/item/shared/normalize.js +49 -0
- package/src/commands/native/item/shared/scope.js +22 -0
- package/src/commands/native/item/shared/search-command.js +61 -0
- package/src/commands/native/item/shared/search-options.js +150 -0
- package/src/commands/native/item/shared/search.js +158 -0
- package/src/commands/native/member/commands.index.json +60 -0
- package/src/commands/native/member/customer-level/get/command.js +122 -0
- package/src/commands/native/member/customer-level/get/schema.json +300 -0
- package/src/commands/native/member/domain.json +3 -0
- package/src/commands/native/member/growth/get/command.js +64 -0
- package/src/commands/native/member/growth/get/schema.json +283 -0
- package/src/commands/native/member/growth-log/list/command.js +203 -0
- package/src/commands/native/member/growth-log/list/schema.json +695 -0
- package/src/commands/native/member/level/get/command.js +70 -0
- package/src/commands/native/member/level/get/query.js +125 -0
- package/src/commands/native/member/level/get/schema.json +339 -0
- package/src/commands/native/member/level/list/command.js +50 -0
- package/src/commands/native/member/level/list/query.js +109 -0
- package/src/commands/native/member/level/list/schema.json +104 -0
- package/src/commands/native/member/membership/get/command.js +78 -0
- package/src/commands/native/member/membership/get/schema.json +283 -0
- package/src/commands/native/order/commands.index.json +33 -0
- package/src/commands/native/order/domain.json +3 -0
- package/src/commands/native/order/order/get/command.js +155 -0
- package/src/commands/native/order/order/get/schema.json +1167 -0
- package/src/commands/native/order/order/list/command.js +230 -0
- package/src/commands/native/order/order/list/schema.json +1363 -0
- package/src/commands/native/order/order/scan/command.js +148 -0
- package/src/commands/native/order/order/scan/scan.js +89 -0
- package/src/commands/native/order/order/scan/schema.json +1445 -0
- package/src/commands/native/order/shared/projection.js +183 -0
- package/src/commands/native/order/shared/query.js +84 -0
- package/src/commands/native/order/shared/search.js +112 -0
- package/src/commands/native/org/commands.index.json +42 -0
- package/src/commands/native/org/domain.json +3 -0
- package/src/commands/native/org/shared/single-staff.js +115 -0
- package/src/commands/native/org/staff/get/command.js +148 -0
- package/src/commands/native/org/staff/get/schema.json +114 -0
- package/src/commands/native/org/staff-role/get/command.js +45 -0
- package/src/commands/native/org/staff-role/get/schema.json +93 -0
- package/src/commands/native/org/unit/get/command.js +131 -0
- package/src/commands/native/org/unit/get/schema.json +478 -0
- package/src/commands/native/org/unit/list/command.js +187 -0
- package/src/commands/native/org/unit/list/schema.json +828 -0
- package/src/commands/native/points/account/get/command.js +65 -0
- package/src/commands/native/points/account/get/schema.json +303 -0
- package/src/commands/native/points/commands.index.json +33 -0
- package/src/commands/native/points/domain.json +3 -0
- package/src/commands/native/points/log/list/command.js +141 -0
- package/src/commands/native/points/log/list/schema.json +761 -0
- package/src/commands/native/points/rule/list/command.js +82 -0
- package/src/commands/native/points/rule/list/schema.json +294 -0
- package/src/commands/native/privacy/commands.index.json +15 -0
- package/src/commands/native/privacy/domain.json +3 -0
- package/src/commands/native/privacy/field/decrypt/command.js +185 -0
- package/src/commands/native/privacy/field/decrypt/schema.json +656 -0
- package/src/commands/native/privacy/shared/order-source.js +33 -0
- package/src/commands/native/stock/commands.index.json +15 -0
- package/src/commands/native/stock/domain.json +3 -0
- package/src/commands/native/stock/log/list/command.js +207 -0
- package/src/commands/native/stock/log/list/schema.json +922 -0
- package/src/commands/native/valuecard/card/get/command.js +107 -0
- package/src/commands/native/valuecard/card/get/schema.json +683 -0
- package/src/commands/native/valuecard/card/list/command.js +136 -0
- package/src/commands/native/valuecard/card/list/pagination.js +23 -0
- package/src/commands/native/valuecard/card/list/schema.json +780 -0
- package/src/commands/native/valuecard/commands.index.json +24 -0
- package/src/commands/native/valuecard/domain.json +3 -0
- package/src/commands/native/valuecard/shared/projection.js +57 -0
- package/src/commands/system/api.js +47 -0
- package/src/commands/system/auth.js +118 -0
- package/src/commands/system/contracts.js +373 -0
- package/src/commands/system/doctor.js +24 -0
- package/src/commands/system/install.js +41 -0
- package/src/commands/system/printer.js +16 -0
- package/src/commands/system/schema.js +162 -0
- package/src/commands/system/shop-guidance.js +9 -0
- package/src/commands/system/shop.js +237 -0
- package/src/commands/system/skills.js +49 -0
- package/src/execution/beijing-date-time.js +29 -0
- package/src/execution/beijing-time.js +21 -0
- package/src/execution/bounded-input.js +42 -0
- package/src/execution/command-io.js +137 -0
- package/src/execution/command-policy.js +41 -0
- package/src/execution/command-schema.js +131 -0
- package/src/execution/datetime.js +83 -0
- package/src/execution/error-details.js +399 -0
- package/src/execution/error-model.js +82 -0
- package/src/execution/exact-json.js +80 -0
- package/src/execution/list-total.js +10 -0
- package/src/execution/money.js +59 -0
- package/src/execution/parser-error.js +66 -0
- package/src/execution/privacy.js +197 -0
- package/src/execution/public-schema.js +52 -0
- package/src/execution/query-api.js +101 -0
- package/src/execution/range.js +84 -0
- package/src/execution/request-state.js +55 -0
- package/src/execution/schema-completeness.js +116 -0
- package/src/execution/shop-query.js +54 -0
- package/src/execution/target-shop-default.js +23 -0
- package/src/execution/target-shop.js +49 -0
- package/src/identity/context-store.js +54 -0
- package/src/identity/context.js +38 -0
- package/src/installation/install.js +158 -0
- package/src/installation/npm-error.js +93 -0
- package/src/installation/skill.js +113 -0
- package/src/transport/account-api.js +152 -0
- package/src/transport/api.js +199 -0
- package/src/transport/shop-app.js +63 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 3,
|
|
3
|
+
"domain": "valuecard",
|
|
4
|
+
"businessCommands": [
|
|
5
|
+
{
|
|
6
|
+
"path": "valuecard.card.get",
|
|
7
|
+
"kind": "atomic",
|
|
8
|
+
"risk": "read",
|
|
9
|
+
"availability": "available",
|
|
10
|
+
"at": "all",
|
|
11
|
+
"module": "valuecard/card/get/command.js",
|
|
12
|
+
"fingerprint": "d5819f697caf55a03a7a111abfa9c86764fb2597a536be9e5e5d705c1a91829e"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"path": "valuecard.card.list",
|
|
16
|
+
"kind": "atomic",
|
|
17
|
+
"risk": "read",
|
|
18
|
+
"availability": "available",
|
|
19
|
+
"at": "all",
|
|
20
|
+
"module": "valuecard/card/list/command.js",
|
|
21
|
+
"fingerprint": "d96d40fd6d68d96f62a809de403fe79274bb4ed671fd166eb6ecba22acd55179"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeMoney } = require('../../../../execution/money');
|
|
4
|
+
const { object } = require('../../../../execution/query-api');
|
|
5
|
+
|
|
6
|
+
function parseInclude(value) {
|
|
7
|
+
if (value === undefined) return [];
|
|
8
|
+
const modules = value.split(',').map(entry => entry.trim());
|
|
9
|
+
if (modules.length !== 1 || modules[0] !== 'template') {
|
|
10
|
+
throw new TypeError('Invalid include selection');
|
|
11
|
+
}
|
|
12
|
+
return modules;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function projectCardType(value, source) {
|
|
16
|
+
if (!['template_type', 'card_sub_type'].includes(source)) {
|
|
17
|
+
throw new TypeError('Invalid card type source');
|
|
18
|
+
}
|
|
19
|
+
if (source === 'template_type' ? !Number.isSafeInteger(value) :
|
|
20
|
+
typeof value !== 'string' || !value.trim()) {
|
|
21
|
+
throw new TypeError('Invalid card type');
|
|
22
|
+
}
|
|
23
|
+
if (value === (source === 'template_type' ? 1001 : '1001')) return 'balance';
|
|
24
|
+
if (value === (source === 'template_type' ? 1002 : '1002')) return 'gift';
|
|
25
|
+
throw new RangeError('Unsupported card type');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function projectBalance(data) {
|
|
29
|
+
if (!object(data)) throw new TypeError('Invalid balance source');
|
|
30
|
+
const amounts = [data.denomination, data.principal_denomination, data.bonus_denomination];
|
|
31
|
+
if (!amounts.every(Number.isSafeInteger) ||
|
|
32
|
+
BigInt(amounts[0]) !== BigInt(amounts[1]) + BigInt(amounts[2])) {
|
|
33
|
+
throw new TypeError('Invalid balance amounts');
|
|
34
|
+
}
|
|
35
|
+
const money = { $comment: 'upstream-money:fen' };
|
|
36
|
+
return {
|
|
37
|
+
totalBalance: normalizeMoney(amounts[0], money),
|
|
38
|
+
principalBalance: normalizeMoney(amounts[1], money),
|
|
39
|
+
bonusBalance: normalizeMoney(amounts[2], money)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function projectTemplate(data) {
|
|
44
|
+
if (!object(data) || typeof data.template_no !== 'string' || !data.template_no.trim()) {
|
|
45
|
+
throw new TypeError('Invalid template number');
|
|
46
|
+
}
|
|
47
|
+
const template = { templateNo: data.template_no };
|
|
48
|
+
if (Object.hasOwn(data, 'template_name')) {
|
|
49
|
+
if (data.template_name !== null && typeof data.template_name !== 'string') {
|
|
50
|
+
throw new TypeError('Invalid template name');
|
|
51
|
+
}
|
|
52
|
+
template.templateName = data.template_name;
|
|
53
|
+
}
|
|
54
|
+
return template;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { parseInclude, projectCardType, projectBalance, projectTemplate };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const { requestApi } = require('../../transport/api');
|
|
5
|
+
const { withSystemSchema } = require('./contracts');
|
|
6
|
+
|
|
7
|
+
async function sendRawApi(method, path, options) {
|
|
8
|
+
if (method !== 'POST') throw new Error('Only POST is supported by the referenced API protocol');
|
|
9
|
+
if (!options.yes) throw new Error('Raw API risk is unknown; review the target and pass --yes to send');
|
|
10
|
+
let body;
|
|
11
|
+
try {
|
|
12
|
+
body = JSON.parse(options.data);
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('--data must be a JSON object');
|
|
15
|
+
}
|
|
16
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
17
|
+
throw new Error('--data must be a JSON object');
|
|
18
|
+
}
|
|
19
|
+
const response = await requestApi({ path, body });
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`API returned HTTP ${response.httpStatus}; request was sent but not confirmed successful`);
|
|
22
|
+
}
|
|
23
|
+
return { sent: true, httpStatus: response.httpStatus, body: response.body };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createApiCommand() {
|
|
27
|
+
return withSystemSchema(new Command('api')
|
|
28
|
+
.description('按接口名和版本发送原始 POST 请求')
|
|
29
|
+
.argument('<method>', '请求方法,仅支持 POST')
|
|
30
|
+
.argument('<path>', '接口名和版本,格式为 <api-name>/<version>')
|
|
31
|
+
.option('--data <json>', 'JSON 对象请求体', '{}')
|
|
32
|
+
.option('--yes', '确认本次请求的未知副作用并发送')
|
|
33
|
+
.addHelpText('after', `
|
|
34
|
+
目标可以是已登记或未登记的 API,不接收任意 URL。
|
|
35
|
+
默认先使用 auth login 保存的个人凭证;受控环境配置 ZAN_SAAS_USER_ACCESS_TOKEN 时使用环境凭证。
|
|
36
|
+
发送须 --yes,仅确认未知副作用,不证明服务端授权。
|
|
37
|
+
退出0、HTTP 2xx 和 sent=true 只说明收到响应,不证明业务成功、异步完成或写入完成。
|
|
38
|
+
body 是脱敏后的原始文本;即使 HTTP 200 正文含 success=false,也不按业务失败改变退出码。
|
|
39
|
+
不提供 --dry-run、自动金额时间转换或自动重试;按目标 API 契约解释 body。
|
|
40
|
+
`)
|
|
41
|
+
.action(async (method, path, options) => {
|
|
42
|
+
const result = await sendRawApi(method, path, options);
|
|
43
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
44
|
+
}), 'api');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { createApiCommand, sendRawApi };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const session = require('../../auth/session');
|
|
5
|
+
const account = require('../../transport/account-api');
|
|
6
|
+
const { readContext } = require('../../identity/context');
|
|
7
|
+
const { contextInput } = require('../../identity/context-store');
|
|
8
|
+
const { withSystemSchema } = require('./contracts');
|
|
9
|
+
const { createPrinter, formatOption } = require('./printer');
|
|
10
|
+
const { shopSelectionHint } = require('./shop-guidance');
|
|
11
|
+
|
|
12
|
+
const pad = value => String(value).padStart(2, '0');
|
|
13
|
+
|
|
14
|
+
// Local time without seconds, e.g. 2026-11-10 15:27.
|
|
15
|
+
function localTime(iso) {
|
|
16
|
+
const date = new Date(iso);
|
|
17
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +
|
|
18
|
+
`${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const cloudAccountHint = '有赞云账号开通未确认,不影响本次个人登录。请先核对账号接口状态。';
|
|
22
|
+
|
|
23
|
+
const text = {
|
|
24
|
+
login: value => {
|
|
25
|
+
const lines = [value.refreshExpiresAt
|
|
26
|
+
? `登录成功,凭证有效期至 ${localTime(value.refreshExpiresAt)}。` : '登录成功。'];
|
|
27
|
+
if (value.cloudAccount?.ensured === false) lines.push(cloudAccountHint);
|
|
28
|
+
lines.push(value.shopId ? `当前店铺上下文:kdtId ${value.shopId}。` : '当前没有有效的店铺上下文。',
|
|
29
|
+
shopSelectionHint);
|
|
30
|
+
return lines.join('\n');
|
|
31
|
+
},
|
|
32
|
+
status: value => {
|
|
33
|
+
if (!value.loggedIn) return `未登录(${value.server})。执行 zan-saas auth login 登录。`;
|
|
34
|
+
const lines = [`已登录(${value.server})`];
|
|
35
|
+
if (value.loginAt) lines.push(`登录时间:${localTime(value.loginAt)}`);
|
|
36
|
+
if (value.refreshExpiresAt) lines.push(`有效期至:${localTime(value.refreshExpiresAt)}`);
|
|
37
|
+
if (value.shopId) lines.push(`当前店铺:${value.shopId}`);
|
|
38
|
+
else lines.push('当前没有有效的店铺上下文。');
|
|
39
|
+
lines.push(shopSelectionHint);
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
},
|
|
42
|
+
logout: value => (value.revoked ? '已退出当前授权服务下的本机个人会话。' : '当前授权服务下未保存个人凭证,无需退出。'),
|
|
43
|
+
whoami: value => `当前用户:${value.nickName || '未设置昵称'}(userId ${value.userId}` +
|
|
44
|
+
`${value.mobile ? `,手机 ${value.mobile}` : ''})`
|
|
45
|
+
};
|
|
46
|
+
const printer = createPrinter(text);
|
|
47
|
+
|
|
48
|
+
function selectedShopId(env = process.env) {
|
|
49
|
+
const state = readContext(contextInput(env));
|
|
50
|
+
return state.status === 'ready' ? state.context.shopId : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Opening the cloud account is best effort: login never fails because of it.
|
|
54
|
+
async function loginWithAccount({ env = process.env, ...options } = {},
|
|
55
|
+
{ login = session.login, ensureCloudUser = account.ensureCloudUser } = {}) {
|
|
56
|
+
const value = await login({ env, ...options });
|
|
57
|
+
let cloudAccount;
|
|
58
|
+
try {
|
|
59
|
+
await ensureCloudUser(env);
|
|
60
|
+
cloudAccount = { ensured: true };
|
|
61
|
+
} catch {
|
|
62
|
+
cloudAccount = { ensured: false };
|
|
63
|
+
}
|
|
64
|
+
return { ...value, shopId: selectedShopId(env), cloudAccount };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const defaults = {
|
|
68
|
+
login: options => loginWithAccount(options),
|
|
69
|
+
logout: options => session.logout(options),
|
|
70
|
+
status: options => ({ ...session.status(options), shopId: selectedShopId(options?.env) }),
|
|
71
|
+
whoami: () => account.whoami()
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function createAuthCommand(impl = defaults) {
|
|
75
|
+
const auth = new Command('auth').description('登录、查看登录状态和退出登录');
|
|
76
|
+
withSystemSchema(auth.command('login')
|
|
77
|
+
.description('在浏览器中授权登录并保存个人凭证')
|
|
78
|
+
.option('--no-browser', '不自动打开浏览器;打印地址后仍等待手动授权')
|
|
79
|
+
.addOption(formatOption())
|
|
80
|
+
.addHelpText('after', `
|
|
81
|
+
登录页回调到 http://127.0.0.1:<port>/callback,默认端口 38721,被占用时改用随机端口。
|
|
82
|
+
默认使用生产授权服务,ZAN_SAAS_OAUTH_BASE_URL 可以覆盖。
|
|
83
|
+
--no-browser 只跳过自动打开浏览器,仍等待授权回调并保存个人凭证,默认超时5分钟。
|
|
84
|
+
人机交接:Agent 启动命令并把输出的登录地址交给用户;用户在浏览器确认后,Agent 等待本命令回调,再执行 zan-saas auth status 确认。用户取消或超时后重新执行本命令。
|
|
85
|
+
登录成功后执行 zan-saas shop current 查看当前店铺。
|
|
86
|
+
首次选店或需要切店时,先执行 zan-saas shop list,由用户选定 kdtId,再执行 zan-saas shop use <kdtId>。
|
|
87
|
+
执行 zan-saas shop current 确认实际生效的店铺,再开始业务查询;切店无需重新登录。
|
|
88
|
+
登录后尝试开通有赞云账号,未确认开通时不影响本次个人登录,应核对账号接口状态。
|
|
89
|
+
浏览器授权完成后仍须成功换取并保存凭证;本命令失败时停止,不进入选店或业务查询。
|
|
90
|
+
`)
|
|
91
|
+
.action(async options => {
|
|
92
|
+
printer('login', options.format)(await impl.login({ browser: options.browser }));
|
|
93
|
+
}), 'auth.login');
|
|
94
|
+
withSystemSchema(auth.command('logout')
|
|
95
|
+
.description('撤销当前授权服务下保存的个人会话,并清除本机凭证和已选店铺')
|
|
96
|
+
.addOption(formatOption())
|
|
97
|
+
.addHelpText('after', `
|
|
98
|
+
仅处理当前授权服务下保存的个人凭证,不清除或撤销环境注入的 token。
|
|
99
|
+
远端撤销失败或结果未知时仍尝试本地清理,失败结果分别说明两者状态。
|
|
100
|
+
成功退出不证明环境覆盖凭证已失效,也不退出其他授权服务下的个人会话。
|
|
101
|
+
`)
|
|
102
|
+
.action(async options => { printer('logout', options.format)(await impl.logout()); }), 'auth.logout');
|
|
103
|
+
withSystemSchema(auth.command('status')
|
|
104
|
+
.description('查看本机保存的凭证状态,不访问网络')
|
|
105
|
+
.addOption(formatOption())
|
|
106
|
+
.addHelpText('after', '\n只检查当前授权服务下的本机个人凭证,不检查环境覆盖凭证、服务端有效性或业务权限。\n')
|
|
107
|
+
.action(options => { printer('status', options.format)(impl.status()); }), 'auth.status');
|
|
108
|
+
withSystemSchema(auth.command('whoami')
|
|
109
|
+
.description('查看当前登录用户,无需选择店铺')
|
|
110
|
+
.addOption(formatOption())
|
|
111
|
+
.addHelpText('after', `
|
|
112
|
+
调用账号接口网关,默认生产环境,ZAN_SAAS_ACCOUNT_API_BASE_URL 可以覆盖。手机号只显示前 3 位和后 4 位。
|
|
113
|
+
`)
|
|
114
|
+
.action(async options => { printer('whoami', options.format)(await impl.whoami()); }), 'auth.whoami');
|
|
115
|
+
return auth;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { createAuthCommand, localTime, loginWithAccount, selectedShopId, text };
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { attachCommandSchema } = require('../../execution/command-schema');
|
|
4
|
+
const { parserErrorModel } = require('../../execution/parser-error');
|
|
5
|
+
|
|
6
|
+
function object(properties, required = Object.keys(properties)) {
|
|
7
|
+
return { type: 'object', additionalProperties: false, required, properties };
|
|
8
|
+
}
|
|
9
|
+
const text = { type: 'string' };
|
|
10
|
+
const format = { type: 'string', enum: ['json', 'pretty'], description: '仅改变 JSON 缩进。' };
|
|
11
|
+
const empty = object({});
|
|
12
|
+
const error = object({
|
|
13
|
+
success: { const: false }, code: { const: -10001 }, message: text
|
|
14
|
+
});
|
|
15
|
+
const model = {
|
|
16
|
+
type: 'object', additionalProperties: true,
|
|
17
|
+
description: 'JSON Schema 模型,关键字与嵌套结构由各命令定义,引用解析使用同级 definitions。'
|
|
18
|
+
};
|
|
19
|
+
const command = object({
|
|
20
|
+
path: text, command: text, usage: text, description: text,
|
|
21
|
+
kind: { enum: ['system', 'raw-api', 'atomic', 'task'] },
|
|
22
|
+
risk: { enum: ['read', 'low', 'medium', 'high', 'unknown'] },
|
|
23
|
+
availability: { enum: ['available', 'preview_only'],
|
|
24
|
+
description: 'available 表示已开放执行;preview_only 表示仅支持离线预览。执行仍受登录、上下文和服务端权限限制。' },
|
|
25
|
+
skill: text, at: { enum: ['hq', 'shop', 'all'] },
|
|
26
|
+
arguments: { type: 'array', items: object({
|
|
27
|
+
name: text, description: text, required: { type: 'boolean' }, variadic: { type: 'boolean' }
|
|
28
|
+
}) },
|
|
29
|
+
options: { type: 'array', items: object({
|
|
30
|
+
flags: text, description: text, required: { type: 'boolean' },
|
|
31
|
+
default: true,
|
|
32
|
+
choices: { type: 'array', items: { anyOf: [
|
|
33
|
+
{ type: 'string' }, { type: 'number' }, { type: 'boolean' }, { type: 'null' }
|
|
34
|
+
] } }
|
|
35
|
+
}, ['flags', 'description', 'required']) },
|
|
36
|
+
schemaVersion: { const: 1 }, input: model, output: model, preview: model, error: model,
|
|
37
|
+
parserError: model,
|
|
38
|
+
definitions: { type: 'object', additionalProperties: { anyOf: [model, { type: 'boolean' }] } },
|
|
39
|
+
modules: { type: 'object', additionalProperties: object({
|
|
40
|
+
base: { type: 'boolean' }, description: text
|
|
41
|
+
}) },
|
|
42
|
+
success: { type: 'object', additionalProperties: true },
|
|
43
|
+
errors: { type: 'object', additionalProperties: true },
|
|
44
|
+
notes: { type: 'array', items: text }
|
|
45
|
+
}, ['path', 'command', 'usage', 'description', 'kind', 'risk', 'availability', 'arguments', 'options',
|
|
46
|
+
'schemaVersion', 'input', 'output', 'parserError']);
|
|
47
|
+
const domain = object({
|
|
48
|
+
path: text, description: text, kind: { const: 'domain' }, skill: text, parserError: model,
|
|
49
|
+
commands: { type: 'array', items: { $ref: '#/definitions/command' } }
|
|
50
|
+
});
|
|
51
|
+
const commandSummaryProperties = Object.fromEntries(
|
|
52
|
+
['path', 'command', 'description', 'kind', 'risk', 'availability', 'skill', 'at']
|
|
53
|
+
.map(key => [key, command.properties[key]]));
|
|
54
|
+
const commandSummaryRequired = ['path', 'command', 'description', 'kind', 'risk', 'availability'];
|
|
55
|
+
const commandSummary = object(commandSummaryProperties, commandSummaryRequired);
|
|
56
|
+
const domainSummary = object({
|
|
57
|
+
path: text, description: text, kind: { const: 'domain' }, skill: text
|
|
58
|
+
});
|
|
59
|
+
const summaryOutput = { oneOf: [
|
|
60
|
+
object({ ...commandSummaryProperties, summary: { const: true } },
|
|
61
|
+
[...commandSummaryRequired, 'summary']),
|
|
62
|
+
object({ ...domainSummary.properties, summary: { const: true },
|
|
63
|
+
commands: { type: 'array', items: commandSummary } }),
|
|
64
|
+
object({ summary: { const: true },
|
|
65
|
+
domains: { type: 'array', items: domainSummary },
|
|
66
|
+
commands: { type: 'array', items: commandSummary } })
|
|
67
|
+
] };
|
|
68
|
+
const sectionOutputs = ['input', 'output', 'preview', 'error'].map(section => object({
|
|
69
|
+
...commandSummaryProperties, schemaVersion: { const: 1 }, section: { const: section },
|
|
70
|
+
[section]: model, definitions: command.properties.definitions, notes: command.properties.notes,
|
|
71
|
+
...(section === 'error' ? { parserError: model } : {}),
|
|
72
|
+
...(section === 'input' ? { usage: text, arguments: command.properties.arguments,
|
|
73
|
+
options: command.properties.options } : {}),
|
|
74
|
+
...(['input', 'output'].includes(section) ? { modules: command.properties.modules } : {}),
|
|
75
|
+
...(section === 'output' ? { success: command.properties.success } : {}),
|
|
76
|
+
...(section === 'error' ? { errors: command.properties.errors } : {})
|
|
77
|
+
}, [...commandSummaryRequired, 'schemaVersion', 'section', section,
|
|
78
|
+
...(section === 'error' ? ['parserError'] : []),
|
|
79
|
+
...(section === 'input' ? ['usage', 'arguments', 'options'] : [])]));
|
|
80
|
+
const skill = object({
|
|
81
|
+
name: text, type: { enum: ['entry', 'shared', 'domain'] }, domain: text, description: text
|
|
82
|
+
}, ['name', 'type', 'description']);
|
|
83
|
+
const serverField = { type: 'string', description: '授权服务地址的 origin,默认生产环境,ZAN_SAAS_OAUTH_BASE_URL 可覆盖;凭证按它分开保存。' };
|
|
84
|
+
const time = { type: ['string', 'null'], description: 'ISO 8601 时间,服务端未返回有效期时为 null。' };
|
|
85
|
+
const credentialStatus = object({
|
|
86
|
+
loggedIn: { type: 'boolean', description: '本机保存个人凭证且已知刷新有效期未到期;未知有效期时保留已有登录记录。不表示环境覆盖凭证或服务端权限有效。' },
|
|
87
|
+
server: serverField,
|
|
88
|
+
clientId: text, tool: text, loginAt: time, accessExpiresAt: time, refreshExpiresAt: time
|
|
89
|
+
}, ['loggedIn', 'server']);
|
|
90
|
+
const shopIdField = {
|
|
91
|
+
type: ['string', 'null'], description: '业务命令当前使用的店铺 ID;没有有效上下文时为 null。'
|
|
92
|
+
};
|
|
93
|
+
const statusOutput = object({ ...credentialStatus.properties, shopId: shopIdField },
|
|
94
|
+
[...credentialStatus.required, 'shopId']);
|
|
95
|
+
const loginOutput = object({
|
|
96
|
+
...statusOutput.properties,
|
|
97
|
+
cloudAccount: { oneOf: [
|
|
98
|
+
object({ ensured: { const: true } }),
|
|
99
|
+
object({ ensured: { const: false } })
|
|
100
|
+
], description: '登录后尝试开通有赞云账号的结果;失败时 ensured 为 false,不影响登录。' }
|
|
101
|
+
}, [...statusOutput.required, 'cloudAccount']);
|
|
102
|
+
const id = { type: 'string', pattern: '^[1-9][0-9]*$' };
|
|
103
|
+
const context = object({
|
|
104
|
+
userId: { type: 'string', minLength: 1 }, hq: id, shopId: id, orgType: { enum: ['hq', 'shop'] }
|
|
105
|
+
});
|
|
106
|
+
const nullableContext = { anyOf: [{ $ref: '#/definitions/context' }, { type: 'null' }] };
|
|
107
|
+
const savedShop = object({
|
|
108
|
+
context: { $ref: '#/definitions/context' }, shopName: { type: ['string', 'null'] }
|
|
109
|
+
});
|
|
110
|
+
const accountNote = '调用账号接口网关,不需要店铺;默认生产环境,ZAN_SAAS_ACCOUNT_API_BASE_URL 可覆盖,token 规则与业务命令相同。';
|
|
111
|
+
const contextNote = '上下文优先级:环境变量 ZAN_SAAS_CONTEXT(即使无效)高于 shop use 保存的店铺;按授权服务地址分开保存。';
|
|
112
|
+
|
|
113
|
+
const contracts = {
|
|
114
|
+
install: {
|
|
115
|
+
input: object({
|
|
116
|
+
host: { type: 'string', description: '可省略;交互选择或检测的 Agent,显式多个名称用逗号分隔。' },
|
|
117
|
+
scope: { enum: ['user', 'project'], description: 'user 为个人目录,project 为当前项目;默认 user。' },
|
|
118
|
+
yes: { type: 'boolean', description: '使用显式宿主或检测结果,无交互提示。' },
|
|
119
|
+
directory: { type: 'string', description: '可选自定义 Skill 根目录,与 host 二选一。' },
|
|
120
|
+
package: { type: 'string', description: '可选本地 npm tgz,省略时持久安装本次运行的精确包版本。' },
|
|
121
|
+
dryRun: { type: 'boolean', description: '只显示计划,不下载或写入。' },
|
|
122
|
+
json: { type: 'boolean', description: '返回 JSON;否则返回面向人的安装结果文本。' }
|
|
123
|
+
}, ['scope']),
|
|
124
|
+
output: { oneOf: [
|
|
125
|
+
{ type: 'string', description: '默认返回版本、宿主入口位置与使用提示。' },
|
|
126
|
+
object({
|
|
127
|
+
success: { const: true, description: 'CLI 及入口文件安装完成;不表示宿主已加载 Skill。' },
|
|
128
|
+
version: text, launcher: text, scope: { enum: ['user', 'project'] },
|
|
129
|
+
cliAction: { enum: ['reuse', 'install'] },
|
|
130
|
+
skillLoading: { const: 'not_verified', description: '尚未在宿主内验证 Skill 的发现及加载。' },
|
|
131
|
+
nextStep: { type: 'string', description: '在宿主中验证入口,或显式指定 Skill 目录的后续操作。' },
|
|
132
|
+
skills: { type: 'array', items: object({
|
|
133
|
+
host: text, path: text, action: { enum: ['unchanged', 'update', 'create'] }
|
|
134
|
+
}) }
|
|
135
|
+
}),
|
|
136
|
+
object({
|
|
137
|
+
success: { const: true, description: '安装计划检查通过;尚未执行安装。' },
|
|
138
|
+
cli: object({ action: { enum: ['reuse', 'install'] }, package: text }),
|
|
139
|
+
scope: { enum: ['user', 'project'] }, dryRun: { const: true },
|
|
140
|
+
skills: { type: 'array', items: object({
|
|
141
|
+
host: text, path: text, action: { enum: ['unchanged', 'update', 'create'] }
|
|
142
|
+
}) }
|
|
143
|
+
})
|
|
144
|
+
] },
|
|
145
|
+
error: object({
|
|
146
|
+
...error.properties,
|
|
147
|
+
details: object({
|
|
148
|
+
category: { const: 'installation', description: '安装子进程执行失败。' },
|
|
149
|
+
stage: { const: 'npm', description: '失败发生在 npm 子进程阶段。' },
|
|
150
|
+
operation: { enum: ['prefix', 'root', 'uninstall', 'install', 'other'], description: '失败的 npm 操作。' },
|
|
151
|
+
npmCode: { type: ['string', 'null'], description: '安全提取的 npm 或子进程错误码;未识别时为 null。' },
|
|
152
|
+
exitCode: { type: ['integer', 'null'], description: 'npm 子进程退出码;无法取得时为 null。' },
|
|
153
|
+
signal: { type: ['string', 'null'], description: '终止信号;未收到信号时为 null。' },
|
|
154
|
+
nextStep: { type: 'string', description: '按已识别原因给出的处理建议,不会自动清理或重试。' },
|
|
155
|
+
conflictPath: { type: 'string', description: '目录冲突时提取的脱敏源路径;其他情况省略。' },
|
|
156
|
+
destinationPath: { type: 'string', description: '目录冲突时提取的脱敏目标路径;其他情况省略。' }
|
|
157
|
+
}, ['category', 'stage', 'operation', 'npmCode', 'exitCode', 'signal', 'nextStep'])
|
|
158
|
+
}, ['success', 'code', 'message']),
|
|
159
|
+
notes: [
|
|
160
|
+
'安装器会持久安装本次 npm 包,并将轻量 Skill 写入明确选择或检测到的宿主目录。默认个人范围;非交互可用 --yes --host。',
|
|
161
|
+
'WorkBuddy 个人目录为 ~/.workbuddy/skills,CodeBuddy 为 ~/.codebuddy/skills;两者项目目录均为 .codebuddy/skills。',
|
|
162
|
+
'普通 npm 安装不执行安装器。dry-run 不下载或写入;同名外部命令、外部 Skill 或已修改入口保留并报错。',
|
|
163
|
+
'安装不执行登录、选店或业务请求;CLI 安装成功不证明宿主已加载 Skill 或业务权限有效。'
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
schema: {
|
|
167
|
+
input: object({
|
|
168
|
+
path: { type: 'array', items: text, description: '可省略,使用空格或点分隔领域及命令路径。' },
|
|
169
|
+
summary: { type: 'boolean', description: '只返回轻量命令目录,不展开字段模型;不能与 section 同时使用。' },
|
|
170
|
+
section: { enum: ['input', 'output', 'preview', 'error'],
|
|
171
|
+
description: '指定完整叶子路径时,仅返回所选模型及引用闭包;input 同时保留 argv 参数、默认值和相关约束。' },
|
|
172
|
+
format
|
|
173
|
+
}, ['format']),
|
|
174
|
+
output: { oneOf: [
|
|
175
|
+
{ $ref: '#/definitions/command' },
|
|
176
|
+
{ $ref: '#/definitions/domain' },
|
|
177
|
+
object({
|
|
178
|
+
parserError: model,
|
|
179
|
+
domains: { type: 'array', items: { $ref: '#/definitions/domain' } },
|
|
180
|
+
commands: { type: 'array', items: { $ref: '#/definitions/command' } }
|
|
181
|
+
}),
|
|
182
|
+
summaryOutput,
|
|
183
|
+
...sectionOutputs
|
|
184
|
+
] },
|
|
185
|
+
definitions: { command, domain },
|
|
186
|
+
notes: [
|
|
187
|
+
'无路径返回完整目录,领域路径返回该领域及命令,叶子路径返回单条调用契约;未发布路径失败。',
|
|
188
|
+
'--summary 可用于根、领域或叶子路径;--section 仅用于叶子路径,未定义的模型返回错误,不生成空模型。两选项不能同时使用。',
|
|
189
|
+
'--section 保留 notes 和所需的递归 definitions;input 保留 arguments/options 与模块说明,output 保留成功说明,error 保留错误说明。',
|
|
190
|
+
'完整根、领域和叶子包含公共 parserError;error 分段同时保留它与原有 error。已有叶子的专用诊断及业务失败仍按 error 处理,summary 不展开错误模型。',
|
|
191
|
+
'input 是解析及必要上下文补全后的业务输入,不是 CLI 接收的 JSON;argv 的必填和默认值见 arguments/options。',
|
|
192
|
+
'不读取凭证、不联网、不按当前上下文删减契约,不证明员工权限或目标店铺开通。',
|
|
193
|
+
'字段模型可能共用同级 definitions;preview 描述实际 --dry-run 结果,不能作为真实执行的成功证据。'
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
doctor: {
|
|
197
|
+
input: object({ format }),
|
|
198
|
+
output: object({
|
|
199
|
+
version: { type: 'string', description: '当前 CLI 包版本。' },
|
|
200
|
+
node: { type: 'string', description: '当前 Node.js 版本。' },
|
|
201
|
+
networkProbes: { const: false },
|
|
202
|
+
authentication: { const: 'not_checked', description: '本次离线诊断未检查认证,不表示已登录、未登录或凭证有效。' },
|
|
203
|
+
businessCommands: { type: 'integer', minimum: 0, description: '当前安装版登记的业务命令数。' }
|
|
204
|
+
}),
|
|
205
|
+
notes: ['离线读取本地安装摘要,不读取凭证、不联网;不证明认证、权限或真实接口可用。']
|
|
206
|
+
},
|
|
207
|
+
'auth.login': {
|
|
208
|
+
input: object({
|
|
209
|
+
browser: { type: 'boolean', description: '默认 true;--no-browser 仅跳过自动打开浏览器,仍打印地址、等待授权回调并保存个人凭证。' }
|
|
210
|
+
}),
|
|
211
|
+
output: loginOutput,
|
|
212
|
+
notes: [
|
|
213
|
+
'打开商家端 CLI 登录页,在 127.0.0.1 临时监听 /callback,拿到授权码后用 PKCE 换个人 token。',
|
|
214
|
+
'token 写入 ~/.zan-saas/credentials.json(权限 0600),按授权服务地址分开保存;输出不含 token。',
|
|
215
|
+
'用户取消或超时(5 分钟)时失败退出,不自动重试。',
|
|
216
|
+
'登录后用短超时尝试开通有赞云账号,未确认时只记录 ensured: false,不因此要求重新登录。',
|
|
217
|
+
'登录成功后用 shop current 查看当前店铺;首次选店或切换时用 shop list 查候选,按用户选择执行 shop use <kdtId>,再用 shop current 核对实际生效的上下文。',
|
|
218
|
+
'浏览器授权完成不代表本命令成功;换取凭证失败时未保存新凭证,停止后续选店和业务请求,按错误核查版本、配置及授权服务路由。'
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
'auth.logout': {
|
|
222
|
+
input: empty,
|
|
223
|
+
output: object({
|
|
224
|
+
loggedOut: { const: true },
|
|
225
|
+
revoked: { type: 'boolean', description: '保存的个人会话已取得服务端撤销成功响应;本地没有凭证时为 false。不表示环境 token 或其他会话被撤销。' },
|
|
226
|
+
server: serverField
|
|
227
|
+
}),
|
|
228
|
+
error: object({
|
|
229
|
+
...error.properties,
|
|
230
|
+
details: object({
|
|
231
|
+
command: { const: 'auth.logout' },
|
|
232
|
+
server: serverField,
|
|
233
|
+
localCredential: { enum: ['deleted', 'delete_failed'],
|
|
234
|
+
description: 'deleted 表示当前个人凭证已在本机清除或已不存在;delete_failed 表示本地清理未成功。' },
|
|
235
|
+
remoteRevocation: { enum: ['revoked', 'failed', 'unknown'],
|
|
236
|
+
description: 'revoked 已取得撤销成功响应;failed 撤销接口返回失败,不推断会话仍有效;unknown 请求或响应未能确认。' },
|
|
237
|
+
httpStatus: { type: 'integer', minimum: 100, maximum: 599,
|
|
238
|
+
description: '仅取得撤销接口失败 HTTP 响应时出现,不回显响应正文。' }
|
|
239
|
+
}, ['command', 'server', 'localCredential', 'remoteRevocation'])
|
|
240
|
+
}, ['success', 'code', 'message']),
|
|
241
|
+
notes: [
|
|
242
|
+
'只撤销当前授权服务下保存的个人会话,并清理本机凭证和当前已选店铺;不清除或撤销环境 token,不退出其他授权服务下的个人会话。',
|
|
243
|
+
'远端撤销失败或结果未知时仍尝试本地清理;失败退出且分别报告两者状态,不自动重试。',
|
|
244
|
+
'本地没有个人凭证时正常返回 revoked=false,不证明外部凭证或服务端其他会话已失效。'
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
'auth.status': {
|
|
248
|
+
input: empty,
|
|
249
|
+
output: statusOutput,
|
|
250
|
+
notes: ['只读本地凭证文件,不联网,不证明 token 在服务端仍有效;按授权服务地址确定读取哪份凭证。']
|
|
251
|
+
},
|
|
252
|
+
'auth.whoami': {
|
|
253
|
+
input: empty,
|
|
254
|
+
output: object({
|
|
255
|
+
userId: { type: 'string', pattern: '^[1-9][0-9]*$', description: '有赞账号 ID,保留字符串。' },
|
|
256
|
+
nickName: { type: ['string', 'null'] },
|
|
257
|
+
mobile: { type: ['string', 'null'], description: '脱敏手机号,只保留前 3 位和后 4 位。' },
|
|
258
|
+
countryCode: { type: ['string', 'null'] }
|
|
259
|
+
}),
|
|
260
|
+
notes: [accountNote, '只输出这四个字段,不输出头像、性别等其他账号资料。']
|
|
261
|
+
},
|
|
262
|
+
'shop.list': {
|
|
263
|
+
input: object({
|
|
264
|
+
name: { type: 'string', minLength: 1, description: '店铺名称关键字,可省略。' },
|
|
265
|
+
pageNo: { type: 'integer', minimum: 1 },
|
|
266
|
+
pageSize: { type: 'integer', minimum: 1, maximum: 50 },
|
|
267
|
+
lineOfBusiness: { type: 'array', minItems: 1, items: { type: 'integer', minimum: 1 },
|
|
268
|
+
description: '业务线编码,默认微商城、教育、酒店、餐饮、美业、零售。' },
|
|
269
|
+
all: { type: 'boolean', description: '为 true 时不查询员工状态,显示全部店铺。' }
|
|
270
|
+
}, ['pageNo', 'pageSize']),
|
|
271
|
+
output: object({
|
|
272
|
+
total: { type: ['integer', 'null'], minimum: 0, description: '服务端在员工停用过滤前的匹配店铺总数;未提供有效总数时为 null,不以本页数量代替,也不是可用店铺总数。' },
|
|
273
|
+
totalScope: { const: 'before_staff_filter', description: 'total 不扣减员工已停用而隐藏的店铺;hidden 只统计当前页。' },
|
|
274
|
+
pageNum: { type: 'integer', minimum: 1 },
|
|
275
|
+
pageSize: { type: 'integer', minimum: 1 },
|
|
276
|
+
hasMore: { type: ['boolean', 'null'], description: '按过滤前总数与本页范围判断是否还有后续;总数未知时为 null,本页为空也不能据此判断取完。' },
|
|
277
|
+
nextPageNo: { type: ['integer', 'null'], minimum: 1, description: '有后续且页码可安全递增时给出下一页;否则为 null,不能单独据此判断取完。' },
|
|
278
|
+
windowLimitReached: { type: 'boolean', description: '页码达到 CLI 可安全表示的整数上限且尚未确认取完;应停止翻页。不是接口分页上限声明。' },
|
|
279
|
+
items: { type: 'array', items: object({
|
|
280
|
+
kdtId: id, shopName: { type: ['string', 'null'] },
|
|
281
|
+
shopRole: { type: ['integer', 'null'], description: '0 单店,1 总部,2 门店或网店,3 仓库,4 合伙人,5 第三方店铺,6 分销供货商店铺,7 前置仓。' },
|
|
282
|
+
orgType: { enum: ['hq', 'shop', null], description: '可选为上下文的组织类型;null 表示暂不能选择。' },
|
|
283
|
+
lifeCycleStatus: { anyOf: [{ type: 'string' }, { type: 'integer' }, { type: 'null' }] }
|
|
284
|
+
}) },
|
|
285
|
+
hidden: { type: 'integer', minimum: 0, description: '本页因员工账号已停用而隐藏的店铺数。' },
|
|
286
|
+
hiddenReason: { enum: ['staff_disabled', null], description: '本页有店铺因当前员工账号已停用而隐藏时为 staff_disabled,否则为 null。' },
|
|
287
|
+
nextStep: { type: ['string', 'null'], description: '有隐藏店铺时提示使用 --all;仅显示店铺,不增加业务权限。' },
|
|
288
|
+
unchecked: { type: 'array', items: id, description: '员工状态查询失败、未过滤而保留的店铺。' },
|
|
289
|
+
uncheckedReason: { type: ['string', 'null'], description: '第一个查询失败的原因;都成功时为 null。' }
|
|
290
|
+
}),
|
|
291
|
+
notes: [
|
|
292
|
+
accountNote, '结果按总部聚合,每项是总部或单店;只有 orgType 非 null 的店铺可以用 shop use 选择。',
|
|
293
|
+
'默认逐个查询当前用户在本页店铺的员工状态,隐藏已停用的店铺;total 仍是服务端总数,每页可能少于 pageSize。',
|
|
294
|
+
'按用户本次选择的 kdtId 执行 shop use <kdtId>,再用 shop current 核对;查询失败不表示账号没有店铺,账号路由错误不通过重复登录恢复。'
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
'shop.use': {
|
|
298
|
+
input: object({ kdtId: id }),
|
|
299
|
+
output: object({
|
|
300
|
+
context: { $ref: '#/definitions/context' }, shopName: { type: ['string', 'null'] },
|
|
301
|
+
overriddenByEnv: { type: 'boolean', description: '为 true 时 ZAN_SAAS_CONTEXT 已设置,业务命令仍以它为准。' }
|
|
302
|
+
}),
|
|
303
|
+
definitions: { context },
|
|
304
|
+
notes: [
|
|
305
|
+
accountNote,
|
|
306
|
+
'先查询当前用户,再在其店铺列表中确认该店铺,只接受总部或单店;hq 与 shopId 都取该店铺 ID。',
|
|
307
|
+
'当前用户在该店铺的员工账号已停用时拒绝;员工状态查询失败不阻止选择。',
|
|
308
|
+
'上下文写入 ~/.zan-saas/context.json(权限 0600),不含凭证;本地上下文不是权限证明,服务端仍校验。',
|
|
309
|
+
'首次选择与切换店铺均使用本命令,无需重新登录;保存后用 shop current 确认实际店铺,overriddenByEnv 为 true 时不能声称当前上下文已切换。',
|
|
310
|
+
contextNote
|
|
311
|
+
]
|
|
312
|
+
},
|
|
313
|
+
'shop.current': {
|
|
314
|
+
input: empty,
|
|
315
|
+
output: object({
|
|
316
|
+
source: { enum: ['env', 'saved', 'missing'] },
|
|
317
|
+
status: { enum: ['ready', 'invalid', 'missing'] },
|
|
318
|
+
context: nullableContext,
|
|
319
|
+
saved: { anyOf: [savedShop, { type: 'null' }] },
|
|
320
|
+
requestShopId: { type: ['string', 'null'], description: '环境变量 ZAN_SAAS_KDT_ID 的有效值;未设置或无效时为 null。不代表所有命令的最终请求店铺。' }
|
|
321
|
+
}),
|
|
322
|
+
definitions: { context },
|
|
323
|
+
notes: ['只读本地文件和环境变量,不联网。requestShopId 是环境变量值;部分命令按其目标店铺设置请求头,具体以命令契约为准。', contextNote]
|
|
324
|
+
},
|
|
325
|
+
'shop.clear': {
|
|
326
|
+
input: empty,
|
|
327
|
+
output: object({ cleared: { type: 'boolean', description: '本地有保存的店铺并已删除时为 true。' }, server: serverField }),
|
|
328
|
+
notes: ['只删除当前授权服务地址下保存的店铺,不联网,不影响 ZAN_SAAS_CONTEXT。']
|
|
329
|
+
},
|
|
330
|
+
'skills.list': {
|
|
331
|
+
input: empty,
|
|
332
|
+
output: object({ skills: { type: 'array', items: skill } }),
|
|
333
|
+
notes: ['返回当前安装版随包指引目录,不表示相应业务命令或员工权限全部具备。']
|
|
334
|
+
},
|
|
335
|
+
'skills.read': {
|
|
336
|
+
input: object({
|
|
337
|
+
skill: { type: 'string', minLength: 1, description: 'skills list 中的完整指引名称。' },
|
|
338
|
+
file: { type: 'string', minLength: 1, description: '已登记的相对 Markdown 路径,省略时读取 SKILL.md。' }
|
|
339
|
+
}, ['skill']),
|
|
340
|
+
output: { type: 'string', description: 'stdout 为原始 Markdown 文本,不是 JSON 字符串或 JSON 信封。' },
|
|
341
|
+
notes: [
|
|
342
|
+
'指引名称与文件路径分别传入,不放在同一对引号内;只能读取当前安装版登记的文件。',
|
|
343
|
+
'指引解释对象、选路和使用边界,不能新增可执行能力或授予权限。'
|
|
344
|
+
]
|
|
345
|
+
},
|
|
346
|
+
api: {
|
|
347
|
+
input: object({
|
|
348
|
+
method: { const: 'POST' },
|
|
349
|
+
path: { type: 'string', pattern: '^/?[a-z][a-z0-9._-]*/[0-9]+\\.[0-9]+\\.[0-9]+$' },
|
|
350
|
+
data: { type: 'object', additionalProperties: true, description: '--data JSON 文本解析后的对象,省略选项时为空对象;不接受数组、null 或非对象。' },
|
|
351
|
+
yes: { const: true, description: '发送前须显式 --yes 确认未知副作用。' }
|
|
352
|
+
}),
|
|
353
|
+
output: object({
|
|
354
|
+
sent: { const: true }, httpStatus: { type: 'integer', minimum: 200, maximum: 299 },
|
|
355
|
+
body: { type: 'string', description: '脱敏后的原始响应文本,不自动解析 JSON,不判断业务成功。' }
|
|
356
|
+
}),
|
|
357
|
+
notes: [
|
|
358
|
+
'按明确的 API 名和版本发送 POST,可以是已登记或未登记的接口。风险未知,发送需 --yes;不继承领域命令的对象校验、风险分级和业务结果保证。',
|
|
359
|
+
'默认发送到生产开放 API 网关,ZAN_SAAS_API_BASE_URL 可覆盖;默认使用 auth login 保存的凭证,受控环境配置 ZAN_SAAS_USER_ACCESS_TOKEN 时使用环境凭证;不接受任意目标 URL。',
|
|
360
|
+
'退出0、HTTP 2xx 和 sent=true 只表示收到响应;即使正文报告业务失败也不改变退出码,不证明业务成功或写入完成。超时或断连时结果未知,不自动重试。',
|
|
361
|
+
'不提供 --dry-run,不自动应用业务金额或日期转换。'
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
function withSystemSchema(command, name) {
|
|
367
|
+
const schema = { schemaVersion: 1, error, ...contracts[name] };
|
|
368
|
+
schema.error = { anyOf: [schema.error, structuredClone(parserErrorModel)] };
|
|
369
|
+
attachCommandSchema(command, schema);
|
|
370
|
+
return command;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = { withSystemSchema };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Command, Option } = require('commander');
|
|
4
|
+
const { version } = require('../../../package.json');
|
|
5
|
+
const { withSystemSchema } = require('./contracts');
|
|
6
|
+
|
|
7
|
+
function createDoctorCommand(businessCommandCount) {
|
|
8
|
+
return withSystemSchema(new Command('doctor')
|
|
9
|
+
.description('检查本地安装,不检查登录状态或网络')
|
|
10
|
+
.addOption(new Option('--format <format>', 'json 或 pretty,仅改变缩进')
|
|
11
|
+
.choices(['json', 'pretty']).default('json'))
|
|
12
|
+
.action(options => {
|
|
13
|
+
const result = {
|
|
14
|
+
version,
|
|
15
|
+
node: process.version,
|
|
16
|
+
networkProbes: false,
|
|
17
|
+
authentication: 'not_checked',
|
|
18
|
+
businessCommands: businessCommandCount
|
|
19
|
+
};
|
|
20
|
+
process.stdout.write(`${JSON.stringify(result, null, options.format === 'pretty' ? 2 : 0)}\n`);
|
|
21
|
+
}), 'doctor');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { createDoctorCommand };
|