weaver-work-cli 0.1.1 → 0.1.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/README.md +187 -76
- package/dist/cmd/setup/index.js +5 -5
- package/dist/cmd/skills/index.js +1 -1
- package/dist/core/hooks.js +7 -0
- package/dist/index.js +0 -0
- package/dist/internal/e10/auth/commands.js +160 -55
- package/dist/internal/e10/auth/crypto.js +7 -22
- package/dist/internal/e10/auth/session.js +1 -1
- package/dist/internal/e10/auth/xiaoe.js +268 -0
- package/dist/internal/e10/context.js +16 -2
- package/dist/internal/e10/index.js +1 -0
- package/dist/internal/e10/request-runtime.js +344 -0
- package/dist/internal/skills/install.js +48 -6
- package/dist/internal/skills/reader.js +27 -0
- package/dist/shortcuts/archive/continuation.js +70 -0
- package/dist/shortcuts/archive/errors.js +53 -0
- package/dist/shortcuts/archive/host.js +197 -0
- package/dist/shortcuts/archive/index.js +363 -0
- package/dist/shortcuts/archive/manifest.js +45 -0
- package/dist/shortcuts/archive/operations/archive-info.js +55 -0
- package/dist/shortcuts/archive/operations/borrow-car.js +146 -0
- package/dist/shortcuts/archive/operations/borrow-list.js +200 -0
- package/dist/shortcuts/archive/operations/confirm.js +46 -0
- package/dist/shortcuts/archive/operations/download.js +238 -0
- package/dist/shortcuts/archive/operations/flow-url.js +240 -0
- package/dist/shortcuts/archive/operations/fonds.js +95 -0
- package/dist/shortcuts/archive/operations/getPageUrl.js +137 -0
- package/dist/shortcuts/archive/operations/registry.js +43 -0
- package/dist/shortcuts/archive/operations/search.js +88 -0
- package/dist/shortcuts/archive/operations/shared.js +120 -0
- package/dist/shortcuts/archive/operations/types.js +23 -0
- package/dist/shortcuts/archive/operations/upload.js +182 -0
- package/dist/shortcuts/archive/operations.js +13 -0
- package/dist/shortcuts/archive/render/search-format.js +276 -0
- package/dist/shortcuts/archive/render/search-view.js +125 -0
- package/dist/shortcuts/asset/continuation.js +70 -0
- package/dist/shortcuts/asset/errors.js +67 -0
- package/dist/shortcuts/asset/host.js +438 -0
- package/dist/shortcuts/asset/index.js +255 -0
- package/dist/shortcuts/asset/manifest.js +471 -0
- package/dist/shortcuts/asset/operations/query.js +249 -0
- package/dist/shortcuts/asset/operations/registry.js +85 -0
- package/dist/shortcuts/asset/operations/shared.js +100 -0
- package/dist/shortcuts/asset/operations/type-cache.js +49 -0
- package/dist/shortcuts/asset/operations/types.js +1 -0
- package/dist/shortcuts/asset/operations/viewlink.js +117 -0
- package/dist/shortcuts/asset/operations/write.js +435 -0
- package/dist/shortcuts/asset/operations.js +9 -0
- package/dist/shortcuts/calendar/continuation.js +70 -0
- package/dist/shortcuts/calendar/errors.js +53 -0
- package/dist/shortcuts/calendar/host.js +148 -0
- package/dist/shortcuts/calendar/index.js +125 -0
- package/dist/shortcuts/calendar/manifest.js +26 -0
- package/dist/shortcuts/calendar/operations/read.js +259 -0
- package/dist/shortcuts/calendar/operations/registry.js +31 -0
- package/dist/shortcuts/calendar/operations/shared.js +132 -0
- package/dist/shortcuts/calendar/operations/types.js +25 -0
- package/dist/shortcuts/calendar/operations/write.js +439 -0
- package/dist/shortcuts/calendar/operations.js +8 -0
- package/dist/shortcuts/ehr/continuation.js +70 -0
- package/dist/shortcuts/ehr/errors.js +53 -0
- package/dist/shortcuts/ehr/host.js +213 -0
- package/dist/shortcuts/ehr/index.js +205 -0
- package/dist/shortcuts/ehr/manifest.js +47 -0
- package/dist/shortcuts/ehr/operations/attend-read.js +373 -0
- package/dist/shortcuts/ehr/operations/attend-write.js +514 -0
- package/dist/shortcuts/ehr/operations/hr-contact.js +363 -0
- package/dist/shortcuts/ehr/operations/hr-ds.js +298 -0
- package/dist/shortcuts/ehr/operations/hr-report.js +386 -0
- package/dist/shortcuts/ehr/operations/recruit-write.js +1629 -0
- package/dist/shortcuts/ehr/operations/recruit.js +803 -0
- package/dist/shortcuts/ehr/operations/registry.js +43 -0
- package/dist/shortcuts/ehr/operations/salary.js +319 -0
- package/dist/shortcuts/ehr/operations/shared.js +246 -0
- package/dist/shortcuts/ehr/operations/types.js +25 -0
- package/dist/shortcuts/ehr/operations.js +25 -0
- package/dist/shortcuts/esb/continuation.js +70 -0
- package/dist/shortcuts/esb/errors.js +65 -0
- package/dist/shortcuts/esb/host.js +187 -0
- package/dist/shortcuts/esb/index.js +112 -0
- package/dist/shortcuts/esb/manifest.js +91 -0
- package/dist/shortcuts/esb/operations/input-format.js +31 -0
- package/dist/shortcuts/esb/operations/registry.js +48 -0
- package/dist/shortcuts/esb/operations/shared.js +152 -0
- package/dist/shortcuts/esb/operations/trigger.js +76 -0
- package/dist/shortcuts/esb/operations/types.js +21 -0
- package/dist/shortcuts/esb/operations.js +15 -0
- package/dist/shortcuts/fna/continuation.js +70 -0
- package/dist/shortcuts/fna/errors.js +59 -0
- package/dist/shortcuts/fna/host.js +146 -0
- package/dist/shortcuts/fna/index.js +111 -0
- package/dist/shortcuts/fna/manifest.js +41 -0
- package/dist/shortcuts/fna/operations/budget.js +124 -0
- package/dist/shortcuts/fna/operations/expense-center.js +148 -0
- package/dist/shortcuts/fna/operations/invoice-writeoff.js +212 -0
- package/dist/shortcuts/fna/operations/loan.js +195 -0
- package/dist/shortcuts/fna/operations/registry.js +35 -0
- package/dist/shortcuts/fna/operations/shared.js +210 -0
- package/dist/shortcuts/fna/operations/types.js +25 -0
- package/dist/shortcuts/fna/operations.js +19 -0
- package/dist/shortcuts/hrm/errors.js +38 -0
- package/dist/shortcuts/hrm/host.js +156 -0
- package/dist/shortcuts/hrm/index.js +111 -0
- package/dist/shortcuts/hrm/manifest.js +32 -0
- package/dist/shortcuts/hrm/operations/employee.js +338 -0
- package/dist/shortcuts/hrm/operations/org.js +110 -0
- package/dist/shortcuts/hrm/operations/position.js +176 -0
- package/dist/shortcuts/hrm/operations/registry.js +33 -0
- package/dist/shortcuts/hrm/operations/shared.js +143 -0
- package/dist/shortcuts/hrm/operations/types.js +23 -0
- package/dist/shortcuts/hrm/operations.js +19 -0
- package/dist/shortcuts/index.js +20 -0
- package/dist/shortcuts/invoice/host.js +99 -94
- package/dist/shortcuts/invoice/index.js +65 -61
- package/dist/shortcuts/invoice/manifest.js +222 -14
- package/dist/shortcuts/invoice/operations/browse-field.js +17 -0
- package/dist/shortcuts/invoice/operations/issuing.js +198 -0
- package/dist/shortcuts/invoice/operations/ocr-preview.js +6 -3
- package/dist/shortcuts/invoice/operations/registry.js +17 -0
- package/dist/shortcuts/invoice/operations/reim.js +401 -0
- package/dist/shortcuts/invoice/operations/shared.js +271 -6
- package/dist/shortcuts/invoice/operations/validate-preview.js +1 -1
- package/dist/shortcuts/jiuchuanhui/cache.js +441 -0
- package/dist/shortcuts/jiuchuanhui/continuation.js +70 -0
- package/dist/shortcuts/jiuchuanhui/errors.js +59 -0
- package/dist/shortcuts/jiuchuanhui/host.js +159 -0
- package/dist/shortcuts/jiuchuanhui/index.js +109 -0
- package/dist/shortcuts/jiuchuanhui/manifest.js +469 -0
- package/dist/shortcuts/jiuchuanhui/operations/datajson.js +214 -0
- package/dist/shortcuts/jiuchuanhui/operations/esb.js +157 -0
- package/dist/shortcuts/jiuchuanhui/operations/generic.js +146 -0
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +133 -0
- package/dist/shortcuts/jiuchuanhui/operations/shared.js +158 -0
- package/dist/shortcuts/jiuchuanhui/operations/types.js +1 -0
- package/dist/shortcuts/jiuchuanhui/operations.js +8 -0
- package/dist/shortcuts/mail/continuation.js +70 -0
- package/dist/shortcuts/mail/errors.js +53 -0
- package/dist/shortcuts/mail/host.js +182 -0
- package/dist/shortcuts/mail/index.js +519 -0
- package/dist/shortcuts/mail/manifest.js +38 -0
- package/dist/shortcuts/mail/operations/mail-blacklist.js +79 -0
- package/dist/shortcuts/mail/operations/mail-contacts.js +195 -0
- package/dist/shortcuts/mail/operations/mail-manage.js +335 -0
- package/dist/shortcuts/mail/operations/mail-read.js +508 -0
- package/dist/shortcuts/mail/operations/mail-send.js +474 -0
- package/dist/shortcuts/mail/operations/mail-sign.js +203 -0
- package/dist/shortcuts/mail/operations/mail-template.js +170 -0
- package/dist/shortcuts/mail/operations/registry.js +41 -0
- package/dist/shortcuts/mail/operations/shared.js +64 -0
- package/dist/shortcuts/mail/operations/types.js +27 -0
- package/dist/shortcuts/mail/operations.js +19 -0
- package/dist/shortcuts/plan/continuation.js +70 -0
- package/dist/shortcuts/plan/errors.js +70 -0
- package/dist/shortcuts/plan/host.js +180 -0
- package/dist/shortcuts/plan/index.js +150 -0
- package/dist/shortcuts/plan/manifest.js +53 -0
- package/dist/shortcuts/plan/operations/link.js +256 -0
- package/dist/shortcuts/plan/operations/read.js +259 -0
- package/dist/shortcuts/plan/operations/registry.js +33 -0
- package/dist/shortcuts/plan/operations/shared.js +246 -0
- package/dist/shortcuts/plan/operations/types.js +49 -0
- package/dist/shortcuts/plan/operations/version.js +49 -0
- package/dist/shortcuts/plan/operations/write.js +710 -0
- package/dist/shortcuts/plan/operations.js +8 -0
- package/docs/SKILL.md +11 -7
- package/docs/_catalog.md +23 -5
- package/docs/agent-invoice.md +31 -10
- package/docs/agent-skill-install.md +211 -0
- package/docs/calendar.md +87 -0
- package/docs/e10-auth.md +28 -9
- package/docs/ehr.md +186 -0
- package/docs/esb.md +64 -0
- package/docs/fna.md +72 -0
- package/docs/hrm.md +89 -0
- package/docs/invoice.md +66 -20
- package/docs/jiuchuanhui.md +131 -0
- package/docs/operation-manual.md +443 -0
- package/docs/plan.md +82 -0
- package/docs/wenshuding.md +129 -0
- package/docs/ziguanjia.md +89 -0
- package/package.json +4 -4
- package/scripts/package-skill.mjs +127 -34
- package/scripts/postbuild.mjs +13 -0
- package/skill-template/business-info.json +13 -2
- package/skill-template/domains/calendar.md +15 -0
- package/skill-template/domains/esb.md +13 -0
- package/skill-template/domains/hrm.md +16 -0
- package/skill-template/domains/jiuchuanhui.md +23 -0
- package/skill-template/domains/jucailin.md +22 -0
- package/skill-template/domains/mail.md +21 -0
- package/skill-template/domains/plan.md +17 -0
- package/skill-template/domains/qiyecheng.md +17 -0
- package/skill-template/domains/shared.md +9 -3
- package/skill-template/domains/skill-maker.md +10 -0
- package/skill-template/domains/wenshuding.md +22 -0
- package/skill-template/domains/{invoice.md → yepiaotong.md} +4 -0
- package/skill-template/domains/ziguanjia.md +21 -0
- package/skill-template/master-skill-template.md +12 -6
- package/skill-template/skill-template.md +40 -9
- package/skills/weaver-e10-calendar/SKILL.md +101 -0
- package/skills/weaver-e10-calendar/product.json +8 -0
- package/skills/weaver-e10-calendar/references/calendar-app-check.md +65 -0
- package/skills/weaver-e10-calendar/references/calendar-dict.md +55 -0
- package/skills/weaver-e10-calendar/references/calendar-query.md +70 -0
- package/skills/weaver-e10-calendar/references/calendar-write.md +97 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +54 -0
- package/skills/weaver-e10-esb/SKILL.md +102 -0
- package/skills/weaver-e10-esb/product.json +8 -0
- package/skills/weaver-e10-esb/references/input-format.md +52 -0
- package/skills/weaver-e10-esb/references/source-manifest.json +242 -0
- package/skills/weaver-e10-esb/references/trigger.md +99 -0
- package/skills/weaver-e10-hrm/SKILL.md +87 -0
- package/skills/weaver-e10-hrm/product.json +8 -0
- package/skills/weaver-e10-hrm/references/employee.md +109 -0
- package/skills/weaver-e10-hrm/references/org.md +95 -0
- package/skills/weaver-e10-hrm/references/position.md +95 -0
- package/skills/weaver-e10-hrm/references/source-manifest.json +121 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +174 -0
- package/skills/weaver-e10-jiuchuanhui/product.json +8 -0
- package/skills/weaver-e10-jiuchuanhui/references/cache.md +110 -0
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +61 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact-plan.md +56 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact-records.md +48 -0
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +56 -0
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +69 -0
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +24 -0
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +60 -0
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +59 -0
- package/skills/weaver-e10-jiuchuanhui/references/jiuchuanhui-entry.md +60 -0
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +72 -0
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +2632 -0
- package/skills/weaver-e10-jucailin/SKILL.md +101 -0
- package/skills/weaver-e10-jucailin/product.json +8 -0
- package/skills/weaver-e10-jucailin/references/attend-read.md +73 -0
- package/skills/weaver-e10-jucailin/references/attend-write.md +82 -0
- package/skills/weaver-e10-jucailin/references/hr-contact.md +92 -0
- package/skills/weaver-e10-jucailin/references/hr-datasource.md +104 -0
- package/skills/weaver-e10-jucailin/references/hr-report.md +76 -0
- package/skills/weaver-e10-jucailin/references/recruit-read.md +76 -0
- package/skills/weaver-e10-jucailin/references/recruit-write.md +70 -0
- package/skills/weaver-e10-jucailin/references/safety-boundaries.md +35 -0
- package/skills/weaver-e10-jucailin/references/salary.md +110 -0
- package/skills/weaver-e10-jucailin/references/source-manifest.json +152 -0
- package/skills/weaver-e10-mail/SKILL.md +151 -0
- package/skills/weaver-e10-mail/product.json +8 -0
- package/skills/weaver-e10-mail/references/mail-agent-entry.md +47 -0
- package/skills/weaver-e10-mail/references/mail-attachment.md +30 -0
- package/skills/weaver-e10-mail/references/mail-blacklist.md +27 -0
- package/skills/weaver-e10-mail/references/mail-contacts.md +33 -0
- package/skills/weaver-e10-mail/references/mail-manage.md +44 -0
- package/skills/weaver-e10-mail/references/mail-read.md +76 -0
- package/skills/weaver-e10-mail/references/mail-send.md +43 -0
- package/skills/weaver-e10-mail/references/mail-sign.md +33 -0
- package/skills/weaver-e10-mail/references/mail-template.md +31 -0
- package/skills/weaver-e10-mail/references/safety-boundaries.md +32 -0
- package/skills/weaver-e10-plan/SKILL.md +139 -0
- package/skills/weaver-e10-plan/product.json +8 -0
- package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +123 -0
- package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +138 -0
- package/skills/weaver-e10-plan/references/plan-report-query.md +154 -0
- package/skills/weaver-e10-plan/references/plan-report-remind.md +104 -0
- package/skills/weaver-e10-plan/references/plan-report-write.md +162 -0
- package/skills/weaver-e10-plan/references/source-manifest.json +858 -0
- package/skills/weaver-e10-qiyecheng/SKILL.md +118 -0
- package/skills/weaver-e10-qiyecheng/product.json +8 -0
- package/skills/weaver-e10-qiyecheng/references/budget.md +65 -0
- package/skills/weaver-e10-qiyecheng/references/catalog.md +56 -0
- package/skills/weaver-e10-qiyecheng/references/expense-center.md +51 -0
- package/skills/weaver-e10-qiyecheng/references/invoice-writeoff.md +57 -0
- package/skills/weaver-e10-qiyecheng/references/loan-management.md +59 -0
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +698 -0
- package/skills/weaver-e10-shared/SKILL.md +86 -0
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +127 -0
- package/skills/weaver-e10-shared/references/json-output-contract.md +120 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +110 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +77 -0
- package/skills/weaver-e10-skill-maker/product.json +8 -0
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +124 -0
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +101 -0
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +230 -0
- package/skills/weaver-e10-skill-maker/references/post-generation-install.md +105 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +261 -0
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +141 -0
- package/skills/weaver-e10-wenshuding/SKILL.md +157 -0
- package/skills/weaver-e10-wenshuding/product.json +8 -0
- package/skills/weaver-e10-wenshuding/references/archive-agent-entry.md +63 -0
- package/skills/weaver-e10-wenshuding/references/archive-borrow-car.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-borrow-list.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-disabled-capabilities.md +22 -0
- package/skills/weaver-e10-wenshuding/references/archive-download.md +48 -0
- package/skills/weaver-e10-wenshuding/references/archive-flow.md +46 -0
- package/skills/weaver-e10-wenshuding/references/archive-fonds.md +34 -0
- package/skills/weaver-e10-wenshuding/references/archive-info.md +33 -0
- package/skills/weaver-e10-wenshuding/references/archive-search.md +52 -0
- package/skills/weaver-e10-wenshuding/references/archive-upload.md +41 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +517 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +184 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-add.md +1 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +47 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-browse-field-data.md +204 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-delete.md +1 -1
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-detail.md +13 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +28 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-download.md +31 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +45 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-file-upload.md +15 -4
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +45 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +120 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +161 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +99 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-ocr-preview.md +14 -4
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +58 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +261 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +97 -0
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-update.md +1 -1
- package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-validation-preview.md +10 -3
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +1340 -0
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +845 -0
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +94 -0
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +449 -0
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +266 -0
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +494 -0
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1103 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +162 -0
- package/skills/weaver-e10-ziguanjia/product.json +8 -0
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +147 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +65 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-create.md +45 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-depre.md +48 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-disabled-capabilities.md +22 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-purch.md +57 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-query.md +62 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-repair.md +83 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-resolve.md +49 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-return.md +70 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-update.md +45 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-use.md +60 -0
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-viewlink.md +81 -0
- package/skills/weaver-work-cli-invoice/SKILL.md +0 -103
- package/skills/weaver-work-cli-invoice/references/invoice-agent-entry.md +0 -38
- package/skills/weaver-work-cli-invoice/references/invoice-disabled-capabilities.md +0 -26
- package/skills/weaver-work-cli-invoice/references/invoice-download.md +0 -22
- package/skills/weaver-work-cli-invoice/references/invoice-enterprise-list.md +0 -35
- package/skills/weaver-work-cli-invoice/references/invoice-import.md +0 -28
- package/skills/weaver-work-cli-invoice/references/invoice-personal-list.md +0 -47
- package/skills/weaver-work-cli-shared/SKILL.md +0 -53
- package/skills/weaver-work-cli-shared/references/e10-auth-and-session.md +0 -83
- package/skills/weaver-work-cli-shared/references/json-output-contract.md +0 -61
- package/skills/weaver-work-cli-shared/references/weaver-work-cli-installation.md +0 -61
- /package/skills/{weaver-work-cli-shared → weaver-e10-shared}/references/high-risk-write.md +0 -0
- /package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/product.json +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { mailValidationError } from '../errors.js';
|
|
2
|
+
import { WRITE, mailSuccess, schemaOf, SCHEMA_STRING } from './types.js';
|
|
3
|
+
const CONTINUATION_TTL_MS = 600_000;
|
|
4
|
+
const TEMPLATE_SCHEMA = schemaOf({
|
|
5
|
+
id: SCHEMA_STRING,
|
|
6
|
+
templateType: SCHEMA_STRING,
|
|
7
|
+
subject: SCHEMA_STRING,
|
|
8
|
+
mailSubject: SCHEMA_STRING,
|
|
9
|
+
clobContent: SCHEMA_STRING,
|
|
10
|
+
templateDesc: SCHEMA_STRING,
|
|
11
|
+
defaultUse: SCHEMA_STRING,
|
|
12
|
+
contentId: SCHEMA_STRING,
|
|
13
|
+
confirm: { type: 'boolean' },
|
|
14
|
+
continuation: SCHEMA_STRING,
|
|
15
|
+
}, { description: '模板保存(prepare/apply)' });
|
|
16
|
+
function buildTemplateBody(args) {
|
|
17
|
+
const subject = String(args.subject || '').trim();
|
|
18
|
+
const clobContent = String(args.clobContent || '').trim();
|
|
19
|
+
if (!subject)
|
|
20
|
+
throw new Error('subject(模板主题)必填');
|
|
21
|
+
if (!clobContent)
|
|
22
|
+
throw new Error('clobContent(Base64 编码的 HTML 内容)必填');
|
|
23
|
+
const body = {
|
|
24
|
+
templateType: args.templateType ? String(args.templateType) : 'personal',
|
|
25
|
+
subject,
|
|
26
|
+
mailSubject: args.mailSubject ? String(args.mailSubject) : subject,
|
|
27
|
+
clobContent,
|
|
28
|
+
templateDesc: args.templateDesc ? String(args.templateDesc) : '',
|
|
29
|
+
defaultUse: args.defaultUse !== undefined ? Number(args.defaultUse) : 0,
|
|
30
|
+
contentId: args.contentId ? String(args.contentId) : 'null',
|
|
31
|
+
id: 'null',
|
|
32
|
+
templateFile: [],
|
|
33
|
+
};
|
|
34
|
+
if (args.id)
|
|
35
|
+
body.id = String(args.id);
|
|
36
|
+
return body;
|
|
37
|
+
}
|
|
38
|
+
const opTemplateSavePrepare = {
|
|
39
|
+
name: 'mail.template.save.prepare',
|
|
40
|
+
resource: 'mail/base/setting/template/saveMailTemplate',
|
|
41
|
+
method: 'POST',
|
|
42
|
+
action: 'mail.template.save.prepare',
|
|
43
|
+
summary: '新增模板(prepare,不保存)',
|
|
44
|
+
risk: WRITE,
|
|
45
|
+
requiresConfirmation: true,
|
|
46
|
+
inputSchema: TEMPLATE_SCHEMA,
|
|
47
|
+
async handler(args, env) {
|
|
48
|
+
const body = buildTemplateBody(args);
|
|
49
|
+
const session = await env.host.getSession();
|
|
50
|
+
const continuation = await env.continuation.issue({
|
|
51
|
+
operation: 'mail.template.save.apply',
|
|
52
|
+
baseUrl: session.baseUrl,
|
|
53
|
+
profile: session.profile,
|
|
54
|
+
userId: session.userId,
|
|
55
|
+
body,
|
|
56
|
+
}, CONTINUATION_TTL_MS);
|
|
57
|
+
return mailSuccess('mail.template.save.prepare', {
|
|
58
|
+
subject: body.subject,
|
|
59
|
+
templateType: body.templateType,
|
|
60
|
+
defaultUse: body.defaultUse,
|
|
61
|
+
tip: '确认后以 mail template save --apply --continuation <token> --yes 执行保存',
|
|
62
|
+
}, { continuation });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const opTemplateSaveApply = {
|
|
66
|
+
name: 'mail.template.save.apply',
|
|
67
|
+
resource: 'mail/base/setting/template/saveMailTemplate',
|
|
68
|
+
method: 'POST',
|
|
69
|
+
action: 'mail.template.save.apply',
|
|
70
|
+
summary: '执行新增模板',
|
|
71
|
+
risk: WRITE,
|
|
72
|
+
requiresConfirmation: true,
|
|
73
|
+
inputSchema: TEMPLATE_SCHEMA,
|
|
74
|
+
async handler(args, env) {
|
|
75
|
+
if (args.confirm !== true)
|
|
76
|
+
throw new Error('mail.template.save.apply 必须传入 confirm=true 与 prepare 返回的 continuation');
|
|
77
|
+
if (!args.continuation)
|
|
78
|
+
throw new Error('mail.template.save.apply 必须传入 prepare 返回的 continuation');
|
|
79
|
+
const lease = await env.continuation.verify(String(args.continuation), 'mail.template.save.apply');
|
|
80
|
+
const session = await env.host.getSession();
|
|
81
|
+
if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
|
|
82
|
+
throw mailValidationError('context_mismatch', '确认上下文已变化(租户或登录账号不同),请重新执行 prepare');
|
|
83
|
+
}
|
|
84
|
+
const value = (await env.host.request({ path: '/api/mail/base/setting/template/saveMailTemplate', method: 'POST', body: lease.body }));
|
|
85
|
+
return mailSuccess('mail.template.save.apply', { id: value?.data ?? 'SUCCESS' }, {});
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
const opTemplateUpdate = {
|
|
89
|
+
name: 'mail.template.update',
|
|
90
|
+
resource: 'mail/base/setting/template/updateMailTemplate',
|
|
91
|
+
method: 'POST',
|
|
92
|
+
action: 'mail.template.update',
|
|
93
|
+
summary: '编辑模板(GET 全量 + 保存)',
|
|
94
|
+
risk: WRITE,
|
|
95
|
+
inputSchema: schemaOf({
|
|
96
|
+
id: { type: 'string', description: '模板 id' },
|
|
97
|
+
subject: SCHEMA_STRING,
|
|
98
|
+
clobContent: SCHEMA_STRING,
|
|
99
|
+
templateDesc: SCHEMA_STRING,
|
|
100
|
+
defaultUse: SCHEMA_STRING,
|
|
101
|
+
templateType: SCHEMA_STRING,
|
|
102
|
+
}, { required: ['id'], description: '编辑模板' }),
|
|
103
|
+
async handler(args, env) {
|
|
104
|
+
const id = String(args.id || '').trim();
|
|
105
|
+
if (!id)
|
|
106
|
+
throw new Error('id(模板 id)必填');
|
|
107
|
+
const info = (await env.host.request({
|
|
108
|
+
path: '/api/mail/base/setting/template/getMailTemplateInfo',
|
|
109
|
+
method: 'POST',
|
|
110
|
+
form: { id, fromModule: 'mail' },
|
|
111
|
+
}));
|
|
112
|
+
const cur = info?.data || {};
|
|
113
|
+
const body = {
|
|
114
|
+
id,
|
|
115
|
+
templateType: args.templateType ? String(args.templateType) : (cur.templateType ?? 'personal'),
|
|
116
|
+
mailSubject: args.mailSubject ? String(args.mailSubject) : (cur.mailSubject ?? ''),
|
|
117
|
+
subject: args.subject ? String(args.subject) : (cur.subject ?? ''),
|
|
118
|
+
clobContent: args.clobContent ? String(args.clobContent) : (cur.clobContent ?? ''),
|
|
119
|
+
templateDesc: args.templateDesc ? String(args.templateDesc) : (cur.templateDesc ?? ''),
|
|
120
|
+
defaultUse: args.defaultUse !== undefined ? Number(args.defaultUse) : (cur.defaultUse ?? 0),
|
|
121
|
+
contentId: cur.contentId ?? 'null',
|
|
122
|
+
templateFile: cur.templateFile ?? [],
|
|
123
|
+
};
|
|
124
|
+
if (!body.subject)
|
|
125
|
+
throw new Error('模板主题为空(原值缺失且未提供 subject)');
|
|
126
|
+
if (!body.clobContent)
|
|
127
|
+
throw new Error('模板内容为空(原值缺失且未提供 clobContent)');
|
|
128
|
+
const value = (await env.host.request({ path: '/api/mail/base/setting/template/updateMailTemplate', method: 'POST', body }));
|
|
129
|
+
return mailSuccess('mail.template.update', value?.data ?? 'SUCCESS', { id });
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const opTemplateDelete = {
|
|
133
|
+
name: 'mail.template.delete',
|
|
134
|
+
resource: 'mail/base/setting/template/deleteMailTemplate',
|
|
135
|
+
method: 'POST',
|
|
136
|
+
action: 'mail.template.delete',
|
|
137
|
+
summary: '删除模板',
|
|
138
|
+
risk: WRITE,
|
|
139
|
+
inputSchema: schemaOf({ id: { type: 'string', description: '模板 id' } }, { required: ['id'], description: '删除模板' }),
|
|
140
|
+
async handler(args, env) {
|
|
141
|
+
const id = String(args.id || '').trim();
|
|
142
|
+
if (!id)
|
|
143
|
+
throw new Error('id(模板 id)必填');
|
|
144
|
+
const value = (await env.host.request({ path: '/api/mail/base/setting/template/deleteMailTemplate', method: 'POST', body: { id } }));
|
|
145
|
+
return mailSuccess('mail.template.delete', value?.data ?? 'SUCCESS', { id });
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
const opTemplateDefault = {
|
|
149
|
+
name: 'mail.template.default',
|
|
150
|
+
resource: 'mail/base/setting/template/defaultMailTemplate',
|
|
151
|
+
method: 'POST',
|
|
152
|
+
action: 'mail.template.default',
|
|
153
|
+
summary: '设为默认模板',
|
|
154
|
+
risk: WRITE,
|
|
155
|
+
inputSchema: schemaOf({ id: { type: 'string', description: '模板 id' } }, { required: ['id'], description: '设默认模板' }),
|
|
156
|
+
async handler(args, env) {
|
|
157
|
+
const id = String(args.id || '').trim();
|
|
158
|
+
if (!id)
|
|
159
|
+
throw new Error('id(模板 id)必填');
|
|
160
|
+
const value = (await env.host.request({ path: '/api/mail/base/setting/template/defaultMailTemplate', method: 'POST', body: { id } }));
|
|
161
|
+
return mailSuccess('mail.template.default', value?.data ?? 'SUCCESS', { id });
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
export const mailTemplateOperations = [
|
|
165
|
+
opTemplateSavePrepare,
|
|
166
|
+
opTemplateSaveApply,
|
|
167
|
+
opTemplateUpdate,
|
|
168
|
+
opTemplateDelete,
|
|
169
|
+
opTemplateDefault,
|
|
170
|
+
];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { mailValidationError } from '../errors.js';
|
|
2
|
+
import { mailReadOperations } from './mail-read.js';
|
|
3
|
+
import { mailSendOperations } from './mail-send.js';
|
|
4
|
+
import { mailManageOperations } from './mail-manage.js';
|
|
5
|
+
import { mailContactsOperations } from './mail-contacts.js';
|
|
6
|
+
import { mailTemplateOperations } from './mail-template.js';
|
|
7
|
+
import { mailSignOperations } from './mail-sign.js';
|
|
8
|
+
import { mailBlacklistOperations } from './mail-blacklist.js';
|
|
9
|
+
function buildRegistry() {
|
|
10
|
+
const registry = new Map();
|
|
11
|
+
const definitions = [
|
|
12
|
+
...mailReadOperations,
|
|
13
|
+
...mailSendOperations,
|
|
14
|
+
...mailManageOperations,
|
|
15
|
+
...mailContactsOperations,
|
|
16
|
+
...mailTemplateOperations,
|
|
17
|
+
...mailSignOperations,
|
|
18
|
+
...mailBlacklistOperations,
|
|
19
|
+
];
|
|
20
|
+
for (const definition of definitions) {
|
|
21
|
+
if (registry.has(definition.name)) {
|
|
22
|
+
throw new Error(`duplicate mail operation: ${definition.name}`);
|
|
23
|
+
}
|
|
24
|
+
registry.set(definition.name, definition);
|
|
25
|
+
}
|
|
26
|
+
return registry;
|
|
27
|
+
}
|
|
28
|
+
const mailOperationRegistry = buildRegistry();
|
|
29
|
+
export function mailOperationDefinitions() {
|
|
30
|
+
return [...mailOperationRegistry.values()];
|
|
31
|
+
}
|
|
32
|
+
export function getMailOperation(operation) {
|
|
33
|
+
const definition = mailOperationRegistry.get(operation);
|
|
34
|
+
if (!definition) {
|
|
35
|
+
throw mailValidationError('operation_unknown', `邮件模块不支持操作:${operation}`);
|
|
36
|
+
}
|
|
37
|
+
return definition;
|
|
38
|
+
}
|
|
39
|
+
export function hasMailOperation(operation) {
|
|
40
|
+
return mailOperationRegistry.has(operation);
|
|
41
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { mailValidationError } from '../errors.js';
|
|
2
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
3
|
+
export function asObject(value) {
|
|
4
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
5
|
+
throw mailValidationError('input_not_object', '输入必须是 JSON 对象');
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
export function assertNoForbiddenKeys(input) {
|
|
10
|
+
for (const key of Object.keys(input)) {
|
|
11
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
12
|
+
throw mailValidationError('forbidden_key', `输入包含非法字段:${key}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** 移除值为 undefined 的键,避免把空值写进请求体。 */
|
|
17
|
+
export function compact(body) {
|
|
18
|
+
for (const key of Object.keys(body)) {
|
|
19
|
+
if (body[key] === undefined)
|
|
20
|
+
delete body[key];
|
|
21
|
+
}
|
|
22
|
+
return body;
|
|
23
|
+
}
|
|
24
|
+
export function toNumber(value, field) {
|
|
25
|
+
if (value === undefined || value === null || value === '')
|
|
26
|
+
return undefined;
|
|
27
|
+
const numeric = Number(value);
|
|
28
|
+
if (!Number.isFinite(numeric)) {
|
|
29
|
+
throw mailValidationError('number_invalid', `${field} 必须是数字`);
|
|
30
|
+
}
|
|
31
|
+
return numeric;
|
|
32
|
+
}
|
|
33
|
+
export function toPositiveInt(value, field) {
|
|
34
|
+
if (value === undefined || value === null || value === '')
|
|
35
|
+
return undefined;
|
|
36
|
+
const numeric = Number(value);
|
|
37
|
+
if (!Number.isFinite(numeric)) {
|
|
38
|
+
throw mailValidationError('number_invalid', `${field} 必须是数字`);
|
|
39
|
+
}
|
|
40
|
+
const truncated = Math.trunc(numeric);
|
|
41
|
+
if (truncated !== numeric || truncated <= 0) {
|
|
42
|
+
throw mailValidationError('number_invalid', `${field} 必须是正整数`);
|
|
43
|
+
}
|
|
44
|
+
return truncated;
|
|
45
|
+
}
|
|
46
|
+
/** ID 归一化为 string[](部分接口要求字符串 id,19 位 id 必须保持字符串防精度丢失)。 */
|
|
47
|
+
export function toStringIdList(value, field) {
|
|
48
|
+
if (value === undefined || value === null)
|
|
49
|
+
return undefined;
|
|
50
|
+
if (!Array.isArray(value))
|
|
51
|
+
throw mailValidationError('id_list_invalid', `${field} 必须是 ID 数组`);
|
|
52
|
+
return value.map((item, index) => {
|
|
53
|
+
if (item === undefined || item === null || item === '') {
|
|
54
|
+
throw mailValidationError('id_list_invalid', `${field}[${index}] 不能为空`);
|
|
55
|
+
}
|
|
56
|
+
return String(item);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** 取响应中的业务数据。 */
|
|
60
|
+
export function unwrapData(response) {
|
|
61
|
+
if (!response || typeof response !== 'object')
|
|
62
|
+
return response;
|
|
63
|
+
return response.data;
|
|
64
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const READ = 'read';
|
|
2
|
+
export const WRITE = 'write';
|
|
3
|
+
export function mailSuccess(action, data, meta) {
|
|
4
|
+
const payload = { ok: true, action, data };
|
|
5
|
+
if (meta && Object.keys(meta).length > 0)
|
|
6
|
+
payload.meta = meta;
|
|
7
|
+
return payload;
|
|
8
|
+
}
|
|
9
|
+
/** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
|
|
10
|
+
export function schemaOf(properties, options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'object',
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
properties,
|
|
15
|
+
...(options.required?.length ? { required: options.required } : {}),
|
|
16
|
+
...(options.description ? { description: options.description } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export const SCHEMA_STRING = { type: 'string' };
|
|
20
|
+
export const SCHEMA_STRING_LIST = { type: 'array', items: { type: 'string' } };
|
|
21
|
+
export const SCHEMA_INTEGER = { type: 'integer' };
|
|
22
|
+
export const SCHEMA_BOOLEAN = { type: 'boolean' };
|
|
23
|
+
export const SCHEMA_POSITIVE_INT = { type: 'integer', minimum: 1 };
|
|
24
|
+
export const SCHEMA_ID = {
|
|
25
|
+
oneOf: [{ type: 'integer' }, { type: 'string', pattern: '^\\d+$' }],
|
|
26
|
+
};
|
|
27
|
+
export const SCHEMA_ID_LIST = { type: 'array', items: SCHEMA_ID };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getMailOperation } from './operations/registry.js';
|
|
2
|
+
import { assertNoForbiddenKeys, asObject } from './operations/shared.js';
|
|
3
|
+
export async function executeMailOperation(operation, rawInput, context) {
|
|
4
|
+
const definition = getMailOperation(operation);
|
|
5
|
+
const input = asObject(rawInput);
|
|
6
|
+
assertNoForbiddenKeys(input);
|
|
7
|
+
const host = {
|
|
8
|
+
request(request) {
|
|
9
|
+
return context.host.request({
|
|
10
|
+
...request,
|
|
11
|
+
operation: request.operation || operation,
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
getSession() {
|
|
15
|
+
return context.host.getSession();
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return definition.handler(input, { ...context, host });
|
|
19
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { planValidationError } from './errors.js';
|
|
7
|
+
export function planStateRoot(ctx) {
|
|
8
|
+
const configured = ctx?.config?.path();
|
|
9
|
+
if (configured)
|
|
10
|
+
return resolve(configured, '..');
|
|
11
|
+
return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
|
|
12
|
+
}
|
|
13
|
+
async function loadKey(ctx) {
|
|
14
|
+
const keysDir = resolve(planStateRoot(ctx), 'keys');
|
|
15
|
+
const filename = resolve(keysDir, 'plan-continuation.key');
|
|
16
|
+
await mkdir(keysDir, { recursive: true, mode: 0o700 });
|
|
17
|
+
if (existsSync(filename))
|
|
18
|
+
return readFile(filename);
|
|
19
|
+
const key = randomBytes(32);
|
|
20
|
+
try {
|
|
21
|
+
await writeFile(filename, key, { flag: 'wx', mode: 0o600 });
|
|
22
|
+
if (process.platform !== 'win32')
|
|
23
|
+
await chmod(filename, 0o600).catch(() => undefined);
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return readFile(filename);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function encode(value) {
|
|
31
|
+
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
32
|
+
}
|
|
33
|
+
function signature(payload, key) {
|
|
34
|
+
return createHmac('sha256', key).update(payload).digest('base64url');
|
|
35
|
+
}
|
|
36
|
+
export function createPlanContinuationSigner(ctx) {
|
|
37
|
+
return {
|
|
38
|
+
async issue(payload, ttlMs = 10 * 60_000) {
|
|
39
|
+
const issuedAt = Date.now();
|
|
40
|
+
const body = encode({ ...payload, issuedAt, expiresAt: issuedAt + ttlMs });
|
|
41
|
+
const sig = signature(body, await loadKey(ctx));
|
|
42
|
+
return `${body}.${sig}`;
|
|
43
|
+
},
|
|
44
|
+
async verify(token, expectedOperation) {
|
|
45
|
+
if (typeof token !== 'string' || !token.includes('.')) {
|
|
46
|
+
throw planValidationError('continuation_invalid', 'continuation 无效');
|
|
47
|
+
}
|
|
48
|
+
const [body, provided] = token.split('.');
|
|
49
|
+
const expected = signature(body, await loadKey(ctx));
|
|
50
|
+
const valid = provided.length === expected.length
|
|
51
|
+
&& timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
|
|
52
|
+
if (!valid)
|
|
53
|
+
throw planValidationError('continuation_invalid', 'continuation 签名校验失败');
|
|
54
|
+
let value;
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw planValidationError('continuation_invalid', 'continuation 内容无法解析');
|
|
60
|
+
}
|
|
61
|
+
if (value.operation !== expectedOperation) {
|
|
62
|
+
throw planValidationError('continuation_mismatch', 'continuation 不属于当前操作');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
|
|
65
|
+
throw planValidationError('continuation_expired', 'continuation 已过期');
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
|
+
export class PlanCliError extends CliError {
|
|
3
|
+
type;
|
|
4
|
+
subtype;
|
|
5
|
+
code;
|
|
6
|
+
retryable;
|
|
7
|
+
stage;
|
|
8
|
+
details;
|
|
9
|
+
partialData;
|
|
10
|
+
constructor(type, subtype, message, options = {}) {
|
|
11
|
+
super(message, options.exitCode ?? ExitCode.InternalError);
|
|
12
|
+
this.name = 'PlanCliError';
|
|
13
|
+
this.type = type;
|
|
14
|
+
this.subtype = subtype;
|
|
15
|
+
this.code = options.code;
|
|
16
|
+
this.retryable = options.retryable ?? false;
|
|
17
|
+
this.stage = options.stage;
|
|
18
|
+
this.details = options.details;
|
|
19
|
+
this.partialData = options.partialData;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function planValidationError(subtype, message, details) {
|
|
23
|
+
return new PlanCliError('validation', subtype, message, {
|
|
24
|
+
details,
|
|
25
|
+
exitCode: ExitCode.UsageError,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function planAuthError(subtype, message, details) {
|
|
29
|
+
return new PlanCliError('authentication', subtype, message, {
|
|
30
|
+
details,
|
|
31
|
+
exitCode: ExitCode.AuthError,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function planNetworkError(subtype, message, options = {}) {
|
|
35
|
+
return new PlanCliError('network', subtype, message, {
|
|
36
|
+
...options,
|
|
37
|
+
retryable: options.retryable ?? true,
|
|
38
|
+
exitCode: ExitCode.NetworkError,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function planApiError(subtype, message, details) {
|
|
42
|
+
return new PlanCliError('api', subtype, message, {
|
|
43
|
+
details,
|
|
44
|
+
exitCode: ExitCode.GeneralError,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function planConfirmationError(action, details) {
|
|
48
|
+
return new PlanCliError('confirmation', 'required', '该操作需要明确确认', {
|
|
49
|
+
details: { action, ...details },
|
|
50
|
+
exitCode: ExitCode.ConfirmationRequired,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** 当前接口链路不支持该能力:不是输入错误,而是源资料未提供该链路的实现依据。 */
|
|
54
|
+
export function planUnsupportedError(subtype, message, details) {
|
|
55
|
+
return new PlanCliError('policy', subtype, message, {
|
|
56
|
+
details,
|
|
57
|
+
exitCode: ExitCode.PolicyError,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export function planPartialError(message, partialData, details) {
|
|
61
|
+
return new PlanCliError('partial', 'write_uncertain', message, {
|
|
62
|
+
details,
|
|
63
|
+
partialData,
|
|
64
|
+
exitCode: ExitCode.Partial,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** 按结构化错误的 type 判定类型(不依赖类实例,兼容包装过的错误对象)。 */
|
|
68
|
+
export function isPlanErrorType(error, type) {
|
|
69
|
+
return Boolean(error && typeof error === 'object' && error.type === type);
|
|
70
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
6
|
+
import { requireE10Session } from '../../internal/e10/context.js';
|
|
7
|
+
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
8
|
+
import { planStateRoot } from './continuation.js';
|
|
9
|
+
import { PlanCliError, planAuthError, planNetworkError, planValidationError } from './errors.js';
|
|
10
|
+
function inferProfile(ctx) {
|
|
11
|
+
const options = ctx.getGlobalOptions();
|
|
12
|
+
return options.profile || getActiveProfile();
|
|
13
|
+
}
|
|
14
|
+
function cacheFilename(root, key) {
|
|
15
|
+
const digest = createHash('sha256').update(key).digest('hex').slice(0, 40);
|
|
16
|
+
return resolve(root, 'cache', 'plan', `${digest}.json`);
|
|
17
|
+
}
|
|
18
|
+
async function readCacheFile(root, key) {
|
|
19
|
+
const file = cacheFilename(root, key);
|
|
20
|
+
if (!existsSync(file))
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(await readFile(file, 'utf8'));
|
|
24
|
+
if (parsed.key !== key)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof parsed.expiresAt === 'number' && parsed.expiresAt < Date.now())
|
|
27
|
+
return undefined;
|
|
28
|
+
return parsed.value;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function writeCacheFile(root, key, value, ttlMs) {
|
|
35
|
+
const file = cacheFilename(root, key);
|
|
36
|
+
await mkdir(resolve(file, '..'), { recursive: true });
|
|
37
|
+
const payload = {
|
|
38
|
+
key,
|
|
39
|
+
value,
|
|
40
|
+
expiresAt: ttlMs === undefined ? null : Date.now() + ttlMs,
|
|
41
|
+
updatedAt: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
await writeFile(file, `${JSON.stringify(payload)}\n`, 'utf8');
|
|
44
|
+
}
|
|
45
|
+
/** 统一业务错误消息提取。 */
|
|
46
|
+
export function businessMessage(value) {
|
|
47
|
+
if (!value || typeof value !== 'object')
|
|
48
|
+
return '泛微接口返回业务失败';
|
|
49
|
+
const candidates = [
|
|
50
|
+
value.msg,
|
|
51
|
+
value.message,
|
|
52
|
+
value.resultMsg,
|
|
53
|
+
value.datajson?.message,
|
|
54
|
+
value.datajson?.msg,
|
|
55
|
+
];
|
|
56
|
+
for (const item of candidates) {
|
|
57
|
+
if (typeof item === 'string' && item.trim())
|
|
58
|
+
return item;
|
|
59
|
+
}
|
|
60
|
+
return '泛微接口返回业务失败';
|
|
61
|
+
}
|
|
62
|
+
/** ebuilder datajson 结构成功判定:`datajson.status === true`(兼容字符串 "true")。 */
|
|
63
|
+
export function isDatajsonSuccess(value) {
|
|
64
|
+
const status = value?.datajson?.status;
|
|
65
|
+
return status === true || status === 'true';
|
|
66
|
+
}
|
|
67
|
+
/** 标准版标记型成功判定:`status` 为 "1" / 1 / true。 */
|
|
68
|
+
export function isFlagSuccess(value) {
|
|
69
|
+
const status = value?.status;
|
|
70
|
+
return status === '1' || status === 1 || status === true;
|
|
71
|
+
}
|
|
72
|
+
/** 提醒类接口成功判定:`code === 200` 且非 `status === false`。 */
|
|
73
|
+
export function isWeaverCodeSuccess(value) {
|
|
74
|
+
if (!value || typeof value !== 'object')
|
|
75
|
+
return false;
|
|
76
|
+
if (Number(value.code) !== 200)
|
|
77
|
+
return false;
|
|
78
|
+
return value.status !== false && value.status !== 'false';
|
|
79
|
+
}
|
|
80
|
+
function assertPath(requestPath) {
|
|
81
|
+
if (typeof requestPath !== 'string' || !requestPath.startsWith('/') || requestPath.startsWith('//')) {
|
|
82
|
+
throw planValidationError('path_invalid', '请求路径必须是相对绝对路径');
|
|
83
|
+
}
|
|
84
|
+
if (/^https?:/i.test(requestPath) || /[\r\n]/.test(requestPath) || requestPath.includes('/../')) {
|
|
85
|
+
throw planValidationError('path_invalid', '请求路径不允许跨域或路径逃逸');
|
|
86
|
+
}
|
|
87
|
+
if (!requestPath.startsWith('/api/')) {
|
|
88
|
+
throw planValidationError('path_not_allowed', '计划报告模块只能调用 /api/ 路径');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function omitEmptyQueryValues(query) {
|
|
92
|
+
if (!query)
|
|
93
|
+
return undefined;
|
|
94
|
+
return Object.fromEntries(Object.entries(query).filter(([, value]) => value !== '' && value !== undefined && value !== null));
|
|
95
|
+
}
|
|
96
|
+
function mapRuntimeNetworkError(error) {
|
|
97
|
+
if (error instanceof E10ApiError && error.type === 'network') {
|
|
98
|
+
throw planNetworkError(error.subtype, error.message, {
|
|
99
|
+
retryable: error.retryable,
|
|
100
|
+
details: error.details,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
export function createPlanHost(ctx, dependencies = {}) {
|
|
106
|
+
let e10Session = null;
|
|
107
|
+
let sessionContext = null;
|
|
108
|
+
const requireSession = dependencies.requireSession || requireE10Session;
|
|
109
|
+
const cacheRoot = planStateRoot(ctx);
|
|
110
|
+
async function ensureSession() {
|
|
111
|
+
if (!e10Session) {
|
|
112
|
+
e10Session = await requireSession(ctx);
|
|
113
|
+
sessionContext = {
|
|
114
|
+
baseUrl: e10Session.getBaseUrl(),
|
|
115
|
+
profile: inferProfile(ctx),
|
|
116
|
+
userId: e10Session.userId,
|
|
117
|
+
tenantKey: e10Session.tenantKey,
|
|
118
|
+
eteamsId: e10Session.eteamsId,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return e10Session;
|
|
122
|
+
}
|
|
123
|
+
async function getSession() {
|
|
124
|
+
await ensureSession();
|
|
125
|
+
return sessionContext;
|
|
126
|
+
}
|
|
127
|
+
async function runtime() {
|
|
128
|
+
return createE10RequestRuntime(ctx, await ensureSession(), {
|
|
129
|
+
interceptors: dependencies.interceptors,
|
|
130
|
+
fetch: dependencies.fetch,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async function request(request) {
|
|
134
|
+
const { method, path, query, body, operation, timeoutMs = 30_000 } = request;
|
|
135
|
+
assertPath(path);
|
|
136
|
+
let response;
|
|
137
|
+
try {
|
|
138
|
+
response = await (await runtime()).requestJson({
|
|
139
|
+
module: 'plan',
|
|
140
|
+
operation,
|
|
141
|
+
method: (method || 'POST').toUpperCase(),
|
|
142
|
+
path,
|
|
143
|
+
query: omitEmptyQueryValues(query),
|
|
144
|
+
body,
|
|
145
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
146
|
+
timeoutMs,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
mapRuntimeNetworkError(error);
|
|
151
|
+
}
|
|
152
|
+
if (response.status === 401 || response.status === 302) {
|
|
153
|
+
throw planAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status });
|
|
154
|
+
}
|
|
155
|
+
if (response.parseError) {
|
|
156
|
+
throw planNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
|
|
157
|
+
}
|
|
158
|
+
if (response.status === 404) {
|
|
159
|
+
throw new PlanCliError('api', 'endpoint_not_found', `接口返回 404,当前环境可能未安装「计划报告」相关应用或接口(${path})`, {
|
|
160
|
+
code: response.status,
|
|
161
|
+
details: { path },
|
|
162
|
+
exitCode: 1,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (response.status < 200 || response.status >= 300) {
|
|
166
|
+
throw new PlanCliError('api', 'http_error', `泛微接口 HTTP ${response.status}`, {
|
|
167
|
+
code: response.status,
|
|
168
|
+
details: { status: response.status, message: businessMessage(response.value) },
|
|
169
|
+
exitCode: 1,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return response.value;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
request,
|
|
176
|
+
getSession,
|
|
177
|
+
getCache: (key) => readCacheFile(cacheRoot, key),
|
|
178
|
+
setCache: (key, value, ttlMs) => writeCacheFile(cacheRoot, key, value, ttlMs),
|
|
179
|
+
};
|
|
180
|
+
}
|