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,132 @@
|
|
|
1
|
+
import { calendarValidationError } from '../errors.js';
|
|
2
|
+
/** 原型污染防护:禁止出现在输入对象中的键。 */
|
|
3
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
4
|
+
export function asObject(value) {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
6
|
+
throw calendarValidationError('input_not_object', '输入必须是 JSON 对象');
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function assertNoForbiddenKeys(input) {
|
|
11
|
+
for (const key of Object.keys(input)) {
|
|
12
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
13
|
+
throw calendarValidationError('forbidden_key', `输入包含非法字段:${key}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** 校验枚举取值。 */
|
|
18
|
+
export function assertEnum(value, allowed, field) {
|
|
19
|
+
const text = typeof value === 'string' ? value : String(value);
|
|
20
|
+
if (!allowed.includes(text)) {
|
|
21
|
+
throw calendarValidationError('enum_invalid', `${field} 取值必须是 ${allowed.join(' / ')} 之一`);
|
|
22
|
+
}
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
/** Switch 字段:必须传数字 0/1,禁止布尔值。 */
|
|
26
|
+
export function assertSwitch(value, field) {
|
|
27
|
+
if (value === undefined || value === null) {
|
|
28
|
+
throw calendarValidationError('switch_required', `${field} 必须传数字 0 或 1,不能传布尔值`);
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === 'boolean') {
|
|
31
|
+
throw calendarValidationError('switch_invalid', `${field} 必须传数字 0 或 1,不能传布尔值`);
|
|
32
|
+
}
|
|
33
|
+
const numeric = Number(value);
|
|
34
|
+
if (!Number.isFinite(numeric) || (numeric !== 0 && numeric !== 1)) {
|
|
35
|
+
throw calendarValidationError('switch_invalid', `${field} 必须传数字 0 或 1`);
|
|
36
|
+
}
|
|
37
|
+
return numeric;
|
|
38
|
+
}
|
|
39
|
+
/** 校验 yyyy-MM-dd HH:mm 时间格式。 */
|
|
40
|
+
export function assertDateTimeMinute(value, field) {
|
|
41
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(value)) {
|
|
42
|
+
throw calendarValidationError('datetime_invalid', `${field} 必须是 yyyy-MM-dd HH:mm 格式`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
/** 校验 yyyy-MM-dd 日期格式。 */
|
|
47
|
+
export function assertDate(value, field) {
|
|
48
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
49
|
+
throw calendarValidationError('date_invalid', `${field} 必须是 yyyy-MM-dd 格式`);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
/** 移除值为 undefined 的键。 */
|
|
54
|
+
export function compact(body) {
|
|
55
|
+
for (const key of Object.keys(body)) {
|
|
56
|
+
if (body[key] === undefined)
|
|
57
|
+
delete body[key];
|
|
58
|
+
}
|
|
59
|
+
return body;
|
|
60
|
+
}
|
|
61
|
+
export function pickDefined(value, keys) {
|
|
62
|
+
const result = {};
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
if (value[key] !== undefined)
|
|
65
|
+
result[key] = value[key];
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
/** 选项 ID 归一化:接受数字或数字字符串,转为字符串。 */
|
|
70
|
+
export function toIdString(value, field) {
|
|
71
|
+
if (value === undefined || value === null || value === '') {
|
|
72
|
+
throw calendarValidationError('id_invalid', `${field} 必须是日程数据 ID`);
|
|
73
|
+
}
|
|
74
|
+
const text = String(value);
|
|
75
|
+
if (!/^\d+$/.test(text)) {
|
|
76
|
+
throw calendarValidationError('id_invalid', `${field} 必须是日程数据 ID`);
|
|
77
|
+
}
|
|
78
|
+
return text;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 从详情接口的选项字段([{name,id}] 数组)提取选项 ID 标量。
|
|
82
|
+
* 详情返回 `[{name:"会议日程",id:"1147539996611665922"}]`,写入时需传 `"1147539996611665922"`。
|
|
83
|
+
*/
|
|
84
|
+
export function optionId(value) {
|
|
85
|
+
if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && value[0] !== null) {
|
|
86
|
+
const id = value[0].id;
|
|
87
|
+
if (id !== undefined && id !== null && id !== '')
|
|
88
|
+
return String(id);
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === 'string' && value)
|
|
92
|
+
return value;
|
|
93
|
+
if (typeof value === 'number')
|
|
94
|
+
return String(value);
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 参与人字段:详情返回人员 ID 字符串(多个逗号分隔),写入时也传字符串。
|
|
99
|
+
* 原样透传,不做拆分。
|
|
100
|
+
*/
|
|
101
|
+
export function participantIds(value) {
|
|
102
|
+
if (typeof value === 'string' && value)
|
|
103
|
+
return value;
|
|
104
|
+
if (Array.isArray(value))
|
|
105
|
+
return value.map((item) => String(item)).join(',');
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
/** 日程持续时间默认 1 小时:只给 start_time 时补 end_time。 */
|
|
109
|
+
export function defaultEndTime(startTime) {
|
|
110
|
+
const match = /^(\d{4}-\d{2}-\d{2}) (\d{2}):(\d{2})$/.exec(startTime);
|
|
111
|
+
if (!match)
|
|
112
|
+
return startTime;
|
|
113
|
+
const [, date, hour, minute] = match;
|
|
114
|
+
let nextHour = Number(hour) + 1;
|
|
115
|
+
let nextDay = date;
|
|
116
|
+
if (nextHour >= 24) {
|
|
117
|
+
nextHour -= 24;
|
|
118
|
+
const [y, m, d] = date.split('-').map(Number);
|
|
119
|
+
const tomorrow = new Date(Date.UTC(y, m - 1, d + 1));
|
|
120
|
+
nextDay = `${tomorrow.getUTCFullYear()}-${String(tomorrow.getUTCMonth() + 1).padStart(2, '0')}-${String(tomorrow.getUTCDate()).padStart(2, '0')}`;
|
|
121
|
+
}
|
|
122
|
+
return `${nextDay} ${String(nextHour).padStart(2, '0')}:${minute}`;
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// 业务枚举(来自源资料单接口文档)
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
export const START_REMINDER_VALUES = ['NOW', 'M5', 'M15', 'H1', 'D1', 'CUSTOM'];
|
|
128
|
+
export const REMIND_TYPE_VALUES = ['system', 'message', 'email'];
|
|
129
|
+
export const REPEAT_TYPE_VALUES = ['NONE', 'D1', 'W1', 'W2', 'M1', 'CUSTOM'];
|
|
130
|
+
export const REPEAT_CUSTOM_TYPE_VALUES = ['D', 'W', 'M'];
|
|
131
|
+
export const REPEAT_WEEK_UNIT_VALUES = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'];
|
|
132
|
+
export const REPEAT_END_TYPE_VALUES = ['0', '1', '2'];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function calendarSuccess(action, data, meta) {
|
|
2
|
+
const payload = { ok: true, action, data };
|
|
3
|
+
if (meta && Object.keys(meta).length > 0)
|
|
4
|
+
payload.meta = meta;
|
|
5
|
+
return payload;
|
|
6
|
+
}
|
|
7
|
+
/** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
|
|
8
|
+
export function schemaOf(properties, options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'object',
|
|
11
|
+
additionalProperties: false,
|
|
12
|
+
properties,
|
|
13
|
+
...(options.required?.length ? { required: options.required } : {}),
|
|
14
|
+
...(options.description ? { description: options.description } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** 日程数据 ID:源资料返回字符串,请求也接受数字或数字字符串。 */
|
|
18
|
+
export const SCHEMA_ID = { oneOf: [{ type: 'integer' }, { type: 'string', pattern: '^\\d+$' }] };
|
|
19
|
+
export const SCHEMA_ID_LIST = { type: 'array', items: SCHEMA_ID };
|
|
20
|
+
/** 日程时间格式 yyyy-MM-dd HH:mm(不含秒)。 */
|
|
21
|
+
export const SCHEMA_DATETIME_MINUTE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$' };
|
|
22
|
+
/** 日期格式 yyyy-MM-dd。 */
|
|
23
|
+
export const SCHEMA_DATE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' };
|
|
24
|
+
/** Switch 字段:仅接受数字 0/1(布尔值会触发服务端报错)。 */
|
|
25
|
+
export const SCHEMA_SWITCH = { type: 'integer', enum: [0, 1] };
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { calendarConfirmationError, calendarPartialError, calendarValidationError, CalendarCliError } from '../errors.js';
|
|
3
|
+
import { calendarSuccess, schemaOf, SCHEMA_ID, SCHEMA_DATETIME_MINUTE, SCHEMA_SWITCH, SCHEMA_DATE } from './types.js';
|
|
4
|
+
import { assertDateTimeMinute, defaultEndTime, optionId, participantIds, pickDefined, } from './shared.js';
|
|
5
|
+
const CONTINUATION_TTL_SECONDS = 600;
|
|
6
|
+
// 日程 mainTable 可选业务字段(创建/更新共用)
|
|
7
|
+
const OPTIONAL_FIELDS = [
|
|
8
|
+
'next_start_remind',
|
|
9
|
+
'next_end_remind',
|
|
10
|
+
'all_day',
|
|
11
|
+
'agn_desc',
|
|
12
|
+
'location',
|
|
13
|
+
'place',
|
|
14
|
+
'part_receipt',
|
|
15
|
+
'remind',
|
|
16
|
+
'start_reminder',
|
|
17
|
+
'remind_type',
|
|
18
|
+
'start_hours',
|
|
19
|
+
'start_mins',
|
|
20
|
+
'end_reminder',
|
|
21
|
+
'end_hours',
|
|
22
|
+
'end_mins',
|
|
23
|
+
'repeat_type',
|
|
24
|
+
'repeat_custom_type',
|
|
25
|
+
'repeat_custom_cycle',
|
|
26
|
+
'repeat_week_unit',
|
|
27
|
+
'repeat_month_unit',
|
|
28
|
+
'repeat_end_date',
|
|
29
|
+
'repeat_end_type',
|
|
30
|
+
'repeat_end_num',
|
|
31
|
+
'repeat_desc',
|
|
32
|
+
];
|
|
33
|
+
const MAIN_TABLE_SCHEMA = {
|
|
34
|
+
agn_name: { type: 'string', minLength: 1, description: '日程名称' },
|
|
35
|
+
start_time: SCHEMA_DATETIME_MINUTE,
|
|
36
|
+
end_time: SCHEMA_DATETIME_MINUTE,
|
|
37
|
+
participants: { type: 'string', description: '参与人人员 ID(多个逗号分隔);缺省时自动带当前操作人' },
|
|
38
|
+
agn_type: { type: 'string', description: '日程类型选项 ID(先通过 calendar.type.list 获取)' },
|
|
39
|
+
priority: { type: 'string', description: '紧急程度选项 ID(先通过 calendar.priority.list 获取)' },
|
|
40
|
+
next_start_remind: { type: 'string', description: '下次提醒时间(开始前)yyyy-MM-dd HH:mm:ss' },
|
|
41
|
+
next_end_remind: { type: 'string', description: '下次提醒时间(结束前)yyyy-MM-dd HH:mm:ss' },
|
|
42
|
+
all_day: SCHEMA_SWITCH,
|
|
43
|
+
agn_desc: { type: 'string', description: '日程详情' },
|
|
44
|
+
location: { type: 'string', description: '位置' },
|
|
45
|
+
place: { type: 'string', description: '地点' },
|
|
46
|
+
part_receipt: SCHEMA_SWITCH,
|
|
47
|
+
remind: SCHEMA_SWITCH,
|
|
48
|
+
start_reminder: { type: 'string', enum: ['NOW', 'M5', 'M15', 'H1', 'D1', 'CUSTOM'], description: '开始前提醒' },
|
|
49
|
+
remind_type: { type: 'string', enum: ['system', 'message', 'email'], description: '提醒方式' },
|
|
50
|
+
start_hours: { type: 'integer', description: '提前小时(start_reminder=CUSTOM 时)' },
|
|
51
|
+
start_mins: { type: 'integer', description: '提前分钟(start_reminder=CUSTOM 时)' },
|
|
52
|
+
end_reminder: { type: 'string', enum: ['NOW', 'M5', 'M15', 'H1', 'D1', 'CUSTOM'], description: '结束前提醒' },
|
|
53
|
+
end_hours: { type: 'integer', description: '结束小时(end_reminder=CUSTOM 时)' },
|
|
54
|
+
end_mins: { type: 'integer', description: '结束分钟(end_reminder=CUSTOM 时)' },
|
|
55
|
+
repeat_type: { type: 'string', enum: ['NONE', 'D1', 'W1', 'W2', 'M1', 'CUSTOM'], description: '重复类型' },
|
|
56
|
+
repeat_custom_type: { type: 'string', enum: ['D', 'W', 'M'], description: '重复频率(repeat_type=CUSTOM 时)' },
|
|
57
|
+
repeat_custom_cycle: { type: 'integer', description: '重复间隔' },
|
|
58
|
+
repeat_week_unit: { type: 'string', enum: ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'], description: '每周的' },
|
|
59
|
+
repeat_month_unit: { type: 'string', description: '每月的(1~31)' },
|
|
60
|
+
repeat_end_date: SCHEMA_DATE,
|
|
61
|
+
repeat_end_type: { type: 'string', enum: ['0', '1', '2'], description: '结束类型:0 指定时间 / 1 指定次数 / 2 永不结束' },
|
|
62
|
+
repeat_end_num: { type: 'integer', description: '次数(repeat_end_type=1 时)' },
|
|
63
|
+
repeat_desc: { type: 'string', description: '重复规则描述' },
|
|
64
|
+
};
|
|
65
|
+
function fingerprint(value) {
|
|
66
|
+
return createHash('sha256').update(JSON.stringify(value ?? null)).digest('hex').slice(0, 32);
|
|
67
|
+
}
|
|
68
|
+
function requireConfirm(args, action) {
|
|
69
|
+
if (args.confirm !== true) {
|
|
70
|
+
throw calendarConfirmationError(action, { hint: '重新调用时传入 confirm=true 与 prepare 返回的 continuation' });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function verifyLease(args, action, env) {
|
|
74
|
+
const lease = await env.continuation.verify(args.continuation, action);
|
|
75
|
+
const session = await env.host.getSession();
|
|
76
|
+
if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
|
|
77
|
+
throw calendarValidationError('context_mismatch', '确认上下文已变化(租户或登录账号不同),请重新执行 prepare');
|
|
78
|
+
}
|
|
79
|
+
return lease;
|
|
80
|
+
}
|
|
81
|
+
function isNetworkError(error) {
|
|
82
|
+
return error instanceof CalendarCliError && error.type === 'network';
|
|
83
|
+
}
|
|
84
|
+
/** 构造写入接口 datajson 请求体(datas[].mainTable 结构)。 */
|
|
85
|
+
async function buildWriteDatajson(host, mainTableList) {
|
|
86
|
+
const session = await host.getSession();
|
|
87
|
+
return {
|
|
88
|
+
datajson: {
|
|
89
|
+
operationinfo: { printCost: false },
|
|
90
|
+
datas: mainTableList.map((mainTable) => ({ mainTable })),
|
|
91
|
+
header: { operator: session.userId },
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 归一化创建/更新的 mainTable:校验必填、补默认值(参与人默认当前人、时长默认 1 小时),
|
|
97
|
+
* 并拾取可选字段。返回完整 mainTable 与用于展示的 preview。
|
|
98
|
+
*/
|
|
99
|
+
async function normalizeMainTable(args, host) {
|
|
100
|
+
const session = await host.getSession();
|
|
101
|
+
const agnName = args.agn_name;
|
|
102
|
+
if (typeof agnName !== 'string' || !agnName.trim()) {
|
|
103
|
+
throw calendarValidationError('agn_name_required', 'agn_name(日程名称)必填');
|
|
104
|
+
}
|
|
105
|
+
const startTime = assertDateTimeMinute(args.start_time, 'start_time');
|
|
106
|
+
const endTime = args.end_time !== undefined
|
|
107
|
+
? assertDateTimeMinute(args.end_time, 'end_time')
|
|
108
|
+
: defaultEndTime(startTime);
|
|
109
|
+
const participants = args.participants !== undefined && args.participants !== ''
|
|
110
|
+
? args.participants
|
|
111
|
+
: session.userId;
|
|
112
|
+
const mainTable = {
|
|
113
|
+
agn_name: agnName.trim(),
|
|
114
|
+
start_time: startTime,
|
|
115
|
+
end_time: endTime,
|
|
116
|
+
participants,
|
|
117
|
+
...pickDefined(args, [...OPTIONAL_FIELDS]),
|
|
118
|
+
};
|
|
119
|
+
// 必填但允许在 create 里显式校验(update 走全量覆盖,字段来自详情合并,可不强制)
|
|
120
|
+
if (args.agn_type !== undefined)
|
|
121
|
+
mainTable.agn_type = args.agn_type;
|
|
122
|
+
if (args.priority !== undefined)
|
|
123
|
+
mainTable.priority = args.priority;
|
|
124
|
+
return mainTable;
|
|
125
|
+
}
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// 创建日程
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
const calendarCreatePrepare = {
|
|
130
|
+
name: 'calendar.create.prepare',
|
|
131
|
+
resource: 'calendar',
|
|
132
|
+
method: 'create.prepare',
|
|
133
|
+
action: 'calendar.create.prepare',
|
|
134
|
+
summary: '准备创建日程:校验字段、补默认值(参与人默认当前人、时长默认 1 小时)并生成确认预览,不写入',
|
|
135
|
+
capability: 'calendar',
|
|
136
|
+
risk: 'high-risk-write',
|
|
137
|
+
requiresConfirmation: true,
|
|
138
|
+
inputSchema: schemaOf({ ...MAIN_TABLE_SCHEMA }, { required: ['agn_name', 'start_time'] }),
|
|
139
|
+
handler: async (args, { host, continuation }) => {
|
|
140
|
+
if (args.agn_type === undefined) {
|
|
141
|
+
throw calendarValidationError('agn_type_required', 'agn_type(日程类型)必填,请先通过 calendar.type.list 获取选项 ID');
|
|
142
|
+
}
|
|
143
|
+
if (args.priority === undefined) {
|
|
144
|
+
throw calendarValidationError('priority_required', 'priority(紧急程度)必填,请先通过 calendar.priority.list 获取选项 ID');
|
|
145
|
+
}
|
|
146
|
+
const mainTable = await normalizeMainTable(args, host);
|
|
147
|
+
const session = await host.getSession();
|
|
148
|
+
const preview = { ...mainTable };
|
|
149
|
+
const token = await continuation.issue({
|
|
150
|
+
operation: 'calendar.create.apply',
|
|
151
|
+
baseUrl: session.baseUrl,
|
|
152
|
+
profile: session.profile,
|
|
153
|
+
userId: session.userId,
|
|
154
|
+
payload: mainTable,
|
|
155
|
+
fingerprint: fingerprint(mainTable),
|
|
156
|
+
}, CONTINUATION_TTL_SECONDS * 1000);
|
|
157
|
+
return calendarSuccess('calendar.create.prepare', {
|
|
158
|
+
status: 'AWAITING_CONFIRMATION',
|
|
159
|
+
preview,
|
|
160
|
+
continuation: token,
|
|
161
|
+
expiresInSeconds: CONTINUATION_TTL_SECONDS,
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
const calendarCreateApply = {
|
|
166
|
+
name: 'calendar.create.apply',
|
|
167
|
+
resource: 'calendar',
|
|
168
|
+
method: 'create.apply',
|
|
169
|
+
action: 'calendar.create.apply',
|
|
170
|
+
summary: '提交创建日程;必须在用户明确确认后调用,且要求 prepare 返回的 continuation',
|
|
171
|
+
capability: 'calendar',
|
|
172
|
+
risk: 'high-risk-write',
|
|
173
|
+
requiresConfirmation: true,
|
|
174
|
+
inputSchema: schemaOf({ ...MAIN_TABLE_SCHEMA, confirm: { const: true }, continuation: { type: 'string', minLength: 1 } }, { required: ['agn_name', 'start_time', 'confirm', 'continuation'] }),
|
|
175
|
+
handler: async (args, env) => {
|
|
176
|
+
requireConfirm(args, 'calendar.create.apply');
|
|
177
|
+
const lease = await verifyLease(args, 'calendar.create.apply', env);
|
|
178
|
+
const mainTable = await normalizeMainTable(args, env.host);
|
|
179
|
+
if (lease.fingerprint !== fingerprint(mainTable)) {
|
|
180
|
+
throw calendarValidationError('target_changed', '日程内容与 prepare 时的快照不一致,请重新执行 prepare');
|
|
181
|
+
}
|
|
182
|
+
const body = await buildWriteDatajson(env.host, [mainTable]);
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await env.host.request({
|
|
186
|
+
method: 'POST',
|
|
187
|
+
path: '/api/ebuilder/form/formdata/v2/saveFormData/createAgenda',
|
|
188
|
+
operation: 'calendar.create.apply',
|
|
189
|
+
body,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
if (isNetworkError(error)) {
|
|
194
|
+
throw calendarPartialError('创建请求发送后连接中断,是否已创建不确定,禁止自动重试,请通过 calendar.list 复核', { attempted: { agn_name: mainTable.agn_name } }, { stage: 'create' });
|
|
195
|
+
}
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
return calendarSuccess('calendar.create.apply', {
|
|
199
|
+
status: 'COMPLETE',
|
|
200
|
+
result: response,
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// 更新日程(全量覆盖)
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
/**
|
|
208
|
+
* 查询当前日程详情并把详情字段转换为更新接口要求的标量形态。
|
|
209
|
+
* 详情接口选项字段返回 [{name,id}] 数组,更新接口需传选项 ID 标量。
|
|
210
|
+
*/
|
|
211
|
+
async function fetchCurrentMainTable(id, host) {
|
|
212
|
+
const session = await host.getSession();
|
|
213
|
+
const response = await host.request({
|
|
214
|
+
method: 'POST',
|
|
215
|
+
path: '/api/ebuilder/form/formdata/v2/getFormDataByPk/queryAgenda',
|
|
216
|
+
operation: 'calendar.update.prepare',
|
|
217
|
+
body: {
|
|
218
|
+
datajson: {
|
|
219
|
+
mainTable: { id },
|
|
220
|
+
header: { operator: session.userId },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const datas = response?.datajson?.datas;
|
|
225
|
+
const mainTable = Array.isArray(datas) && datas[0]?.mainTable ? datas[0].mainTable : undefined;
|
|
226
|
+
if (!mainTable) {
|
|
227
|
+
throw calendarValidationError('not_found', `日程 ${id} 不存在或无权查看,无法更新`);
|
|
228
|
+
}
|
|
229
|
+
return mainTable;
|
|
230
|
+
}
|
|
231
|
+
function mainTableToUpdateFields(mainTable) {
|
|
232
|
+
const result = { id: mainTable.id };
|
|
233
|
+
const scalarKeys = [
|
|
234
|
+
'agn_name', 'start_time', 'end_time', 'agn_desc', 'place', 'location',
|
|
235
|
+
'next_start_remind', 'next_end_remind', 'all_day', 'part_receipt', 'remind',
|
|
236
|
+
'start_hours', 'start_mins', 'end_hours', 'end_mins', 'repeat_custom_cycle',
|
|
237
|
+
'repeat_end_date', 'repeat_end_num', 'repeat_desc',
|
|
238
|
+
];
|
|
239
|
+
for (const key of scalarKeys) {
|
|
240
|
+
if (mainTable[key] !== undefined)
|
|
241
|
+
result[key] = mainTable[key];
|
|
242
|
+
}
|
|
243
|
+
// 选项字段数组 -> 标量 ID
|
|
244
|
+
const optionKeys = ['agn_type', 'priority', 'start_reminder', 'remind_type', 'end_reminder', 'repeat_type', 'repeat_custom_type', 'repeat_week_unit', 'repeat_month_unit', 'repeat_end_type'];
|
|
245
|
+
for (const key of optionKeys) {
|
|
246
|
+
const id = optionId(mainTable[key]);
|
|
247
|
+
if (id !== undefined)
|
|
248
|
+
result[key] = id;
|
|
249
|
+
}
|
|
250
|
+
// 参与人 ID 字符串
|
|
251
|
+
const participants = participantIds(mainTable.participants);
|
|
252
|
+
if (participants !== undefined)
|
|
253
|
+
result.participants = participants;
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
const calendarUpdatePrepare = {
|
|
257
|
+
name: 'calendar.update.prepare',
|
|
258
|
+
resource: 'calendar',
|
|
259
|
+
method: 'update.prepare',
|
|
260
|
+
action: 'calendar.update.prepare',
|
|
261
|
+
summary: '准备更新日程(全量覆盖):先查当前详情,合并用户修改并生成确认预览,不写入',
|
|
262
|
+
capability: 'calendar',
|
|
263
|
+
risk: 'high-risk-write',
|
|
264
|
+
requiresConfirmation: true,
|
|
265
|
+
inputSchema: schemaOf({
|
|
266
|
+
id: SCHEMA_ID,
|
|
267
|
+
...MAIN_TABLE_SCHEMA,
|
|
268
|
+
}, { required: ['id'] }),
|
|
269
|
+
handler: async (args, { host, continuation }) => {
|
|
270
|
+
const id = String(args.id);
|
|
271
|
+
const current = await fetchCurrentMainTable(id, host);
|
|
272
|
+
const base = mainTableToUpdateFields(current);
|
|
273
|
+
// 用户传入的字段覆盖当前值
|
|
274
|
+
const overrides = {};
|
|
275
|
+
for (const key of ['agn_name', 'start_time', 'end_time', 'participants', 'agn_type', 'priority', ...OPTIONAL_FIELDS]) {
|
|
276
|
+
if (args[key] !== undefined)
|
|
277
|
+
overrides[key] = args[key];
|
|
278
|
+
}
|
|
279
|
+
// start_time 变化而 end_time 未指定时,默认 end_time = 新 start_time + 1 小时
|
|
280
|
+
if (overrides.start_time !== undefined && overrides.end_time === undefined) {
|
|
281
|
+
overrides.end_time = defaultEndTime(String(overrides.start_time));
|
|
282
|
+
}
|
|
283
|
+
const merged = { ...base, ...overrides };
|
|
284
|
+
const session = await host.getSession();
|
|
285
|
+
const token = await continuation.issue({
|
|
286
|
+
operation: 'calendar.update.apply',
|
|
287
|
+
baseUrl: session.baseUrl,
|
|
288
|
+
profile: session.profile,
|
|
289
|
+
userId: session.userId,
|
|
290
|
+
payload: merged,
|
|
291
|
+
fingerprint: fingerprint(merged),
|
|
292
|
+
}, CONTINUATION_TTL_SECONDS * 1000);
|
|
293
|
+
return calendarSuccess('calendar.update.prepare', {
|
|
294
|
+
status: 'AWAITING_CONFIRMATION',
|
|
295
|
+
preview: {
|
|
296
|
+
before: base,
|
|
297
|
+
after: merged,
|
|
298
|
+
changedFields: Object.keys(overrides),
|
|
299
|
+
},
|
|
300
|
+
continuation: token,
|
|
301
|
+
expiresInSeconds: CONTINUATION_TTL_SECONDS,
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
const calendarUpdateApply = {
|
|
306
|
+
name: 'calendar.update.apply',
|
|
307
|
+
resource: 'calendar',
|
|
308
|
+
method: 'update.apply',
|
|
309
|
+
action: 'calendar.update.apply',
|
|
310
|
+
summary: '提交更新日程;必须在用户明确确认后调用,且要求 prepare 返回的 continuation',
|
|
311
|
+
capability: 'calendar',
|
|
312
|
+
risk: 'high-risk-write',
|
|
313
|
+
requiresConfirmation: true,
|
|
314
|
+
inputSchema: schemaOf({
|
|
315
|
+
id: SCHEMA_ID,
|
|
316
|
+
...MAIN_TABLE_SCHEMA,
|
|
317
|
+
confirm: { const: true },
|
|
318
|
+
continuation: { type: 'string', minLength: 1 },
|
|
319
|
+
}, { required: ['id', 'confirm', 'continuation'] }),
|
|
320
|
+
handler: async (args, env) => {
|
|
321
|
+
requireConfirm(args, 'calendar.update.apply');
|
|
322
|
+
const lease = await verifyLease(args, 'calendar.update.apply', env);
|
|
323
|
+
const id = String(args.id);
|
|
324
|
+
const payload = (lease.payload || {});
|
|
325
|
+
if (String(payload.id ?? '') !== id) {
|
|
326
|
+
throw calendarValidationError('target_changed', '日程 ID 与 prepare 时的快照不一致,请重新执行 prepare');
|
|
327
|
+
}
|
|
328
|
+
const body = await buildWriteDatajson(env.host, [payload]);
|
|
329
|
+
let response;
|
|
330
|
+
try {
|
|
331
|
+
response = await env.host.request({
|
|
332
|
+
method: 'POST',
|
|
333
|
+
path: '/api/ebuilder/form/formdata/v2/updateFormData/updateAgenda',
|
|
334
|
+
operation: 'calendar.update.apply',
|
|
335
|
+
body,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (isNetworkError(error)) {
|
|
340
|
+
throw calendarPartialError('更新请求发送后连接中断,是否已更新不确定,禁止自动重试,请通过 calendar.get 复核', { attempted: { id } }, { stage: 'update' });
|
|
341
|
+
}
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
return calendarSuccess('calendar.update.apply', {
|
|
345
|
+
status: 'COMPLETE',
|
|
346
|
+
result: response,
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
// 删除日程
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
const calendarDeletePrepare = {
|
|
354
|
+
name: 'calendar.delete.prepare',
|
|
355
|
+
resource: 'calendar',
|
|
356
|
+
method: 'delete.prepare',
|
|
357
|
+
action: 'calendar.delete.prepare',
|
|
358
|
+
summary: '准备删除日程:查当前详情并生成确认预览,不删除',
|
|
359
|
+
capability: 'calendar',
|
|
360
|
+
risk: 'high-risk-write',
|
|
361
|
+
requiresConfirmation: true,
|
|
362
|
+
inputSchema: schemaOf({ id: SCHEMA_ID }, { required: ['id'] }),
|
|
363
|
+
handler: async (args, { host, continuation }) => {
|
|
364
|
+
const id = String(args.id);
|
|
365
|
+
const current = await fetchCurrentMainTable(id, host);
|
|
366
|
+
const session = await host.getSession();
|
|
367
|
+
const preview = {
|
|
368
|
+
id,
|
|
369
|
+
agn_name: current.agn_name,
|
|
370
|
+
start_time: current.start_time,
|
|
371
|
+
end_time: current.end_time,
|
|
372
|
+
};
|
|
373
|
+
const token = await continuation.issue({
|
|
374
|
+
operation: 'calendar.delete.apply',
|
|
375
|
+
baseUrl: session.baseUrl,
|
|
376
|
+
profile: session.profile,
|
|
377
|
+
userId: session.userId,
|
|
378
|
+
payload: { id },
|
|
379
|
+
fingerprint: fingerprint({ id }),
|
|
380
|
+
}, CONTINUATION_TTL_SECONDS * 1000);
|
|
381
|
+
return calendarSuccess('calendar.delete.prepare', {
|
|
382
|
+
status: 'AWAITING_CONFIRMATION',
|
|
383
|
+
preview,
|
|
384
|
+
continuation: token,
|
|
385
|
+
expiresInSeconds: CONTINUATION_TTL_SECONDS,
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
const calendarDeleteApply = {
|
|
390
|
+
name: 'calendar.delete.apply',
|
|
391
|
+
resource: 'calendar',
|
|
392
|
+
method: 'delete.apply',
|
|
393
|
+
action: 'calendar.delete.apply',
|
|
394
|
+
summary: '提交删除日程;不可逆,必须在用户明确确认后调用',
|
|
395
|
+
capability: 'calendar',
|
|
396
|
+
risk: 'high-risk-write',
|
|
397
|
+
requiresConfirmation: true,
|
|
398
|
+
inputSchema: schemaOf({
|
|
399
|
+
id: SCHEMA_ID,
|
|
400
|
+
confirm: { const: true },
|
|
401
|
+
continuation: { type: 'string', minLength: 1 },
|
|
402
|
+
}, { required: ['id', 'confirm', 'continuation'] }),
|
|
403
|
+
handler: async (args, env) => {
|
|
404
|
+
requireConfirm(args, 'calendar.delete.apply');
|
|
405
|
+
const lease = await verifyLease(args, 'calendar.delete.apply', env);
|
|
406
|
+
const id = String(args.id);
|
|
407
|
+
if (lease.fingerprint !== fingerprint({ id })) {
|
|
408
|
+
throw calendarValidationError('target_changed', '日程 ID 与 prepare 时的快照不一致,请重新执行 prepare');
|
|
409
|
+
}
|
|
410
|
+
const body = await buildWriteDatajson(env.host, [{ id }]);
|
|
411
|
+
let response;
|
|
412
|
+
try {
|
|
413
|
+
response = await env.host.request({
|
|
414
|
+
method: 'POST',
|
|
415
|
+
path: '/api/ebuilder/form/formdata/v2/deleteFormData/deleteAgenda',
|
|
416
|
+
operation: 'calendar.delete.apply',
|
|
417
|
+
body,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
if (isNetworkError(error)) {
|
|
422
|
+
throw calendarPartialError('删除请求发送后连接中断,是否已删除不确定,禁止自动重试,请通过 calendar.get 复核', { attempted: { id } }, { stage: 'delete' });
|
|
423
|
+
}
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
return calendarSuccess('calendar.delete.apply', {
|
|
427
|
+
status: 'COMPLETE',
|
|
428
|
+
result: response,
|
|
429
|
+
});
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
export const calendarWriteOperations = [
|
|
433
|
+
calendarCreatePrepare,
|
|
434
|
+
calendarCreateApply,
|
|
435
|
+
calendarUpdatePrepare,
|
|
436
|
+
calendarUpdateApply,
|
|
437
|
+
calendarDeletePrepare,
|
|
438
|
+
calendarDeleteApply,
|
|
439
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getCalendarOperation } from './operations/registry.js';
|
|
2
|
+
import { assertNoForbiddenKeys, asObject } from './operations/shared.js';
|
|
3
|
+
export async function executeCalendarOperation(operation, rawInput, context) {
|
|
4
|
+
const definition = getCalendarOperation(operation);
|
|
5
|
+
const input = asObject(rawInput);
|
|
6
|
+
assertNoForbiddenKeys(input);
|
|
7
|
+
return definition.handler(input, context);
|
|
8
|
+
}
|
|
@@ -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 { ehrValidationError } from './errors.js';
|
|
7
|
+
function stateRoot(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(stateRoot(ctx), 'keys');
|
|
15
|
+
const filename = resolve(keysDir, 'ehr-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 createEhrContinuationSigner(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 ehrValidationError('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 ehrValidationError('continuation_invalid', 'continuation 签名校验失败');
|
|
54
|
+
let value;
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw ehrValidationError('continuation_invalid', 'continuation 内容无法解析');
|
|
60
|
+
}
|
|
61
|
+
if (value.operation !== expectedOperation) {
|
|
62
|
+
throw ehrValidationError('continuation_mismatch', 'continuation 不属于当前操作');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
|
|
65
|
+
throw ehrValidationError('continuation_expired', 'continuation 已过期');
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|