weaver-work-cli 0.1.42 → 0.1.44
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 +17 -0
- package/dist/chunks/{archive-J4I6MCKF.js → archive-P6TBQGKL.js} +13 -5
- package/dist/chunks/{archive-oa-BRZKKTO4.js → archive-oa-7TQ2NXOH.js} +12 -5
- package/dist/chunks/{asset-L7D4FAOZ.js → asset-3ELRG37G.js} +11 -4
- package/dist/chunks/{asset-idle-DADLLV75.js → asset-idle-WIGMUFMD.js} +7 -3
- package/dist/chunks/{attachment-content-K4C7VLWQ.js → attachment-content-E3276UOV.js} +5 -4
- package/dist/chunks/{blog-BVGQ7NZL.js → blog-C5NMZAAC.js} +11 -4
- package/dist/chunks/{caizhilian-SAADNFD6.js → caizhilian-5XSM2DFP.js} +13 -6
- package/dist/chunks/{calendar-Q7NQY5Z2.js → calendar-FI2WNSBO.js} +12 -5
- package/dist/chunks/{chunk-YRSZQUD2.js → chunk-3YBDYOC4.js} +3 -3
- package/dist/chunks/{chunk-OOUTBLZ4.js → chunk-56EZERK4.js} +1 -1
- package/dist/chunks/chunk-6N4JPNFL.js +76 -0
- package/dist/chunks/{chunk-ZUX3P6BQ.js → chunk-727FH2NI.js} +1 -1
- package/dist/chunks/chunk-7BDPM5ST.js +11 -0
- package/dist/chunks/{chunk-EKWAQSLU.js → chunk-BCVYBYYT.js} +432 -2248
- package/dist/chunks/{chunk-W7BEHOSR.js → chunk-CWYGFU5B.js} +2 -2
- package/dist/chunks/{chunk-NOODWMCL.js → chunk-D4ADSZ7H.js} +1 -1
- package/dist/chunks/{chunk-SA6VBIHY.js → chunk-ETNJSKMM.js} +214 -16
- package/dist/chunks/{chunk-BBKDXQEC.js → chunk-F4E4EM35.js} +3 -3
- package/dist/chunks/{chunk-IZ7S7QUS.js → chunk-FAZTGX7S.js} +6 -6
- package/dist/chunks/{chunk-NKZRQXST.js → chunk-FRZKBMSW.js} +52 -36
- package/dist/chunks/{chunk-BYBSCN4X.js → chunk-G7PQWD4L.js} +6 -3
- package/dist/chunks/{chunk-GEWYMQME.js → chunk-GAMZLAG2.js} +1 -1
- package/dist/chunks/{chunk-R4CHUSKC.js → chunk-GM6J7Q4B.js} +4 -4
- package/dist/chunks/{chunk-APVFJUQH.js → chunk-GRDRG6HP.js} +3 -3
- package/dist/chunks/{chunk-LDZQPUIA.js → chunk-HIT4Y6OI.js} +1 -1
- package/dist/chunks/{chunk-EZALDOQH.js → chunk-HP7JIYMT.js} +5 -2
- package/dist/chunks/{chunk-LADPYOWC.js → chunk-IAZ3UPY6.js} +3 -3
- package/dist/chunks/{chunk-ZEDWSH32.js → chunk-IC62NFM5.js} +1 -1
- package/dist/chunks/{chunk-P26ZWSKU.js → chunk-KIHNAS25.js} +5 -2
- package/dist/chunks/{chunk-BIPYLLVS.js → chunk-N4M2DDFE.js} +15 -15
- package/dist/chunks/{chunk-QKNKR4RH.js → chunk-OF4S23UB.js} +4 -4
- package/dist/chunks/{chunk-JPRO6ER2.js → chunk-PC7VKEB5.js} +57 -60
- package/dist/chunks/{chunk-4PD5NWAX.js → chunk-PUQS5MWL.js} +2 -2
- package/dist/chunks/{chunk-2LV74IWP.js → chunk-Q4OYAQYH.js} +1 -1
- package/dist/chunks/{chunk-72CKAUAG.js → chunk-QVSWRLLU.js} +7 -5
- package/dist/chunks/{chunk-4RAE6JXP.js → chunk-TA7FXXJD.js} +6 -3
- package/dist/chunks/{chunk-GPMFD4RD.js → chunk-TXUG266F.js} +1 -1
- package/dist/chunks/{chunk-FOAA27U5.js → chunk-UFIT2UU3.js} +16 -4
- package/dist/chunks/{chunk-LUSPKZKY.js → chunk-UG5G54VL.js} +1 -0
- package/dist/chunks/{chunk-NIAJ5ZCG.js → chunk-UL5XUQ25.js} +5 -2
- package/dist/chunks/{chunk-FMPJJYYI.js → chunk-WRXSAVKO.js} +1 -1
- package/dist/chunks/{chunk-G4UR6NXN.js → chunk-WSF4NNMR.js} +3 -3
- package/dist/chunks/chunk-XHK4MDR5.js +2497 -0
- package/dist/chunks/{chunk-3KTFS4IA.js → chunk-XNIK66I4.js} +19 -9
- package/dist/chunks/{chunk-3SO6QL6U.js → chunk-YWX377BY.js} +5 -2
- package/dist/chunks/{chunk-BYATLLEF.js → chunk-Z3GDFVJR.js} +1 -1
- package/dist/chunks/{chunk-GHXCDPTM.js → chunk-Z7VYWPZV.js} +1 -1
- package/dist/chunks/{conflict-F2OMZRTO.js → conflict-ARLSZJ6V.js} +4 -3
- package/dist/chunks/{continuation-2CKSBLA6.js → continuation-3S37AUSH.js} +4 -3
- package/dist/chunks/{continuation-RN6PZN2M.js → continuation-3XLYYTKV.js} +4 -3
- package/dist/chunks/{continuation-HZIZRZBX.js → continuation-HFW2YT5S.js} +4 -3
- package/dist/chunks/{continuation-NLECEJPX.js → continuation-KI7DRRZN.js} +4 -3
- package/dist/chunks/{contract-5BETHRSG.js → contract-DNIIW5VJ.js} +13 -5
- package/dist/chunks/{cowork-3MN6EQQO.js → cowork-GMLRGVIW.js} +11 -4
- package/dist/chunks/{create-flow-5FQNNMVO.js → create-flow-J22BO65E.js} +8 -7
- package/dist/chunks/{ebuilder-form-SBHOYBUD.js → ebuilder-form-ANV2FHYV.js} +12 -7
- package/dist/chunks/{ehr-KMOJULKG.js → ehr-GSTGAIY3.js} +266 -24
- package/dist/chunks/{employee-SAK2QYXX.js → employee-22BGJ3QS.js} +3 -3
- package/dist/chunks/{env-LB7NBL2O.js → env-BX3VRFS7.js} +4 -3
- package/dist/chunks/{esb-W2PJCFCX.js → esb-ATAIASX2.js} +12 -7
- package/dist/chunks/{factory-6ZFQXQ46.js → factory-PETTFME6.js} +3 -2
- package/dist/chunks/{fna-ZQPCUV4R.js → fna-YEKBSFSX.js} +14 -8
- package/dist/chunks/{group-chat-2HHAJN46.js → group-chat-BFVXAYFX.js} +5 -4
- package/dist/chunks/{host-ATKVNZAT.js → host-HRBXEPDQ.js} +5 -5
- package/dist/chunks/{host-BQH22NTQ.js → host-LR55CTHP.js} +5 -5
- package/dist/chunks/{host-X2CSLFM6.js → host-SUOY6VYQ.js} +5 -5
- package/dist/chunks/{host-4CBHGOHT.js → host-SZGV4UNT.js} +5 -5
- package/dist/chunks/{host-YTODKMSL.js → host-WIKN2EJ5.js} +5 -5
- package/dist/chunks/{hrm-FFM4UJFS.js → hrm-NRDO4W5G.js} +11 -7
- package/dist/chunks/{im-VEHKDQPB.js → im-IQ2HN23S.js} +8 -7
- package/dist/chunks/{invoice-LVIKSIIF.js → invoice-MNRW76PP.js} +636 -1768
- package/dist/chunks/{jiuchuanhui-X6DINO5P.js → jiuchuanhui-DTRBAX4N.js} +14 -6
- package/dist/chunks/{lazy-dispatch-KSWTN5L6.js → lazy-dispatch-ZVWAI25B.js} +7 -6
- package/dist/chunks/{mail-VDKRGUD3.js → mail-ZVAUXOJA.js} +16 -14
- package/dist/chunks/{meeting-CLC6HP3Z.js → meeting-FEVPZ67S.js} +21 -14
- package/dist/chunks/{okr-CHG4OO3U.js → okr-SDH3E5RA.js} +12 -5
- package/dist/chunks/{operations-5QL3AECO.js → operations-62N6EP3C.js} +4 -3
- package/dist/chunks/{plan-XWR75667.js → plan-E35JFDI7.js} +12 -5
- package/dist/chunks/{project-5BXOOKMG.js → project-73QXBAB2.js} +11 -4
- package/dist/chunks/reception-UWZVACBN.js +1899 -0
- package/dist/chunks/{recruit-stage-DGMFNNDE.js → recruit-stage-4MAOCKFY.js} +320 -57
- package/dist/chunks/registry-IM5G23LS.js +24 -0
- package/dist/chunks/{registry-RHMW6ANW.js → registry-JDUS4E7D.js} +7 -6
- package/dist/chunks/{registry-7FFOXIAW.js → registry-KNKSCJNT.js} +7 -6
- package/dist/chunks/{registry-Z43434VG.js → registry-PO7SI256.js} +4 -4
- package/dist/chunks/{registry-OZSMDVPP.js → registry-PVUZQDDQ.js} +3 -3
- package/dist/chunks/{room-recommend-62H7ZQQ6.js → room-recommend-H7KLXA5R.js} +6 -5
- package/dist/chunks/{schedule-FNXKH3DQ.js → schedule-ESZIBAUO.js} +6 -5
- package/dist/chunks/{workflow-ADHKCAJK.js → workflow-CWJFJLQU.js} +13 -8
- package/dist/cmd/action.js +6 -0
- package/dist/cmd/identity/index.js +45 -0
- package/dist/cmd/index.js +2 -0
- package/dist/core/continuation-ttl.js +18 -0
- package/dist/core/error-codes.js +85 -0
- package/dist/core/logger.js +11 -0
- package/dist/index.js +37 -31
- package/dist/internal/e10/auth/commands.js +163 -8
- package/dist/internal/e10/auth/error-log.js +182 -0
- package/dist/internal/e10/auth/session.js +17 -3
- package/dist/internal/e10/auth-recovery.js +39 -12
- package/dist/internal/e10/context.js +17 -2
- package/dist/internal/skills/install.js +9 -1
- package/dist/shortcuts/archive/continuation.js +2 -1
- package/dist/shortcuts/archive/index.js +3 -0
- package/dist/shortcuts/archive/operations/confirm.js +2 -1
- package/dist/shortcuts/archive-oa/continuation.js +2 -1
- package/dist/shortcuts/archive-oa/index.js +2 -0
- package/dist/shortcuts/archive-oa/operations/confirm.js +2 -1
- package/dist/shortcuts/asset/continuation.js +2 -1
- package/dist/shortcuts/asset/index.js +2 -0
- package/dist/shortcuts/asset-idle/index.js +2 -0
- package/dist/shortcuts/blog/continuation.js +2 -1
- package/dist/shortcuts/blog/index.js +2 -0
- package/dist/shortcuts/caizhilian/continuation.js +2 -1
- package/dist/shortcuts/caizhilian/index.js +2 -0
- package/dist/shortcuts/caizhilian/operations/confirm.js +3 -2
- package/dist/shortcuts/calendar/continuation.js +2 -1
- package/dist/shortcuts/calendar/index.js +2 -0
- package/dist/shortcuts/calendar/operations/write.js +1 -1
- package/dist/shortcuts/contract/continuation.js +2 -1
- package/dist/shortcuts/contract/index.js +2 -0
- package/dist/shortcuts/contract/operations/write.js +2 -1
- package/dist/shortcuts/cowork/continuation.js +2 -1
- package/dist/shortcuts/cowork/index.js +2 -0
- package/dist/shortcuts/ebuilder-form/continuation.js +2 -1
- package/dist/shortcuts/ebuilder-form/index.js +2 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +3 -2
- package/dist/shortcuts/ehr/continuation.js +2 -1
- package/dist/shortcuts/ehr/index.js +2 -0
- package/dist/shortcuts/ehr/operations/attend-write.js +1 -1
- package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +182 -3
- package/dist/shortcuts/ehr/operations/recruit-position.js +134 -2
- package/dist/shortcuts/ehr/operations/recruit-write.js +1 -1
- package/dist/shortcuts/esb/continuation.js +2 -1
- package/dist/shortcuts/esb/index.js +2 -0
- package/dist/shortcuts/esb/operations/trigger.js +2 -1
- package/dist/shortcuts/fna/continuation.js +2 -1
- package/dist/shortcuts/fna/index.js +2 -0
- package/dist/shortcuts/fna/operations/invoice-writeoff.js +1 -1
- package/dist/shortcuts/fna/operations/loan.js +1 -1
- package/dist/shortcuts/hrm/index.js +2 -0
- package/dist/shortcuts/im/continuation.js +2 -1
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/invoice/continuation.js +2 -1
- package/dist/shortcuts/invoice/index.js +14 -15
- package/dist/shortcuts/invoice/invoice-record.js +93 -0
- package/dist/shortcuts/invoice/manifest.js +190 -393
- package/dist/shortcuts/invoice/operations/add.js +2 -1
- package/dist/shortcuts/invoice/operations/delete.js +2 -1
- package/dist/shortcuts/invoice/operations/import.js +2 -1
- package/dist/shortcuts/invoice/operations/issuing-contract-detail.js +86 -0
- package/dist/shortcuts/invoice/operations/issuing-contracts.js +210 -0
- package/dist/shortcuts/invoice/operations/registry.js +17 -17
- package/dist/shortcuts/invoice/operations/reim-auto.js +4 -569
- package/dist/shortcuts/invoice/operations/reim.js +52 -341
- package/dist/shortcuts/invoice/operations/shared.js +21 -4
- package/dist/shortcuts/invoice/operations/update.js +2 -1
- package/dist/shortcuts/invoice/projection.js +10 -62
- package/dist/shortcuts/jiuchuanhui/continuation.js +2 -1
- package/dist/shortcuts/jiuchuanhui/index.js +2 -0
- package/dist/shortcuts/jiuchuanhui/operations/datajson.js +2 -1
- package/dist/shortcuts/jiuchuanhui/operations/esb.js +2 -1
- package/dist/shortcuts/mail/continuation.js +2 -1
- package/dist/shortcuts/mail/index.js +2 -0
- package/dist/shortcuts/mail/operations/mail-blacklist.js +1 -1
- package/dist/shortcuts/mail/operations/mail-contacts.js +1 -1
- package/dist/shortcuts/mail/operations/mail-send.js +1 -1
- package/dist/shortcuts/mail/operations/mail-sign.js +1 -1
- package/dist/shortcuts/mail/operations/mail-template.js +1 -1
- package/dist/shortcuts/meeting/continuation.js +2 -1
- package/dist/shortcuts/meeting/index.js +2 -0
- package/dist/shortcuts/meeting/operations/change.js +1 -1
- package/dist/shortcuts/meeting/operations/create.js +1 -1
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +16 -7
- package/dist/shortcuts/meeting/operations/eb/decision.js +1 -1
- package/dist/shortcuts/meeting/operations/eb/factory.js +1 -1
- package/dist/shortcuts/meeting/operations/lifecycle.js +1 -1
- package/dist/shortcuts/meeting/operations/receipts.js +1 -1
- package/dist/shortcuts/meeting/operations/signs.js +1 -1
- package/dist/shortcuts/okr/continuation.js +2 -1
- package/dist/shortcuts/okr/index.js +2 -0
- package/dist/shortcuts/okr/operations/write.js +1 -1
- package/dist/shortcuts/plan/continuation.js +2 -1
- package/dist/shortcuts/plan/index.js +2 -0
- package/dist/shortcuts/plan/operations/write.js +1 -1
- package/dist/shortcuts/project/continuation.js +2 -1
- package/dist/shortcuts/project/index.js +2 -0
- package/dist/shortcuts/reception/constants.js +53 -0
- package/dist/shortcuts/reception/errors.js +54 -0
- package/dist/shortcuts/reception/host.js +125 -0
- package/dist/shortcuts/reception/index.js +198 -0
- package/dist/shortcuts/reception/manifest.js +177 -0
- package/dist/shortcuts/reception/operations/ask.js +71 -0
- package/dist/shortcuts/reception/operations/browsertool.js +58 -0
- package/dist/shortcuts/reception/operations/compose.js +129 -0
- package/dist/shortcuts/reception/operations/customparams.js +74 -0
- package/dist/shortcuts/reception/operations/doc.js +238 -0
- package/dist/shortcuts/reception/operations/docrender.js +36 -0
- package/dist/shortcuts/reception/operations/panels.js +179 -0
- package/dist/shortcuts/reception/operations/registry.js +26 -0
- package/dist/shortcuts/reception/operations/render-assets.js +184 -0
- package/dist/shortcuts/reception/operations/render.js +123 -0
- package/dist/shortcuts/reception/operations/runs.js +38 -0
- package/dist/shortcuts/reception/operations/schedule.js +127 -0
- package/dist/shortcuts/reception/operations/shared.js +196 -0
- package/dist/shortcuts/reception/operations/snapshot.js +68 -0
- package/dist/shortcuts/reception/operations/types.js +1 -0
- package/dist/shortcuts/reception/operations.js +33 -0
- package/dist/shortcuts/reception/upload.js +218 -0
- package/dist/shortcuts/recruit-stage/context.js +145 -33
- package/dist/shortcuts/recruit-stage/continuation.js +2 -1
- package/dist/shortcuts/recruit-stage/operations/jd-create.js +106 -17
- package/dist/shortcuts/recruit-stage/operations/resume-import.js +166 -10
- package/dist/shortcuts/workflow/continuation.js +2 -1
- package/dist/shortcuts/workflow/index.js +2 -0
- package/dist/shortcuts/workflow/operations/form.js +32 -8
- package/dist/shortcuts/workflow/operations/write.js +1 -1
- package/docs/_catalog.md +3 -0
- package/docs/agent-invoice.md +1 -1
- package/docs/archive-oa.md +1 -1
- package/docs/caizhilian.md +1 -1
- package/docs/calendar.md +1 -1
- package/docs/contract.md +1 -1
- package/docs/e10-auth.md +16 -0
- package/docs/ehr.md +2 -2
- package/docs/esb.md +1 -1
- package/docs/im.md +1 -1
- package/docs/invoice.md +8 -12
- package/docs/okr.md +1 -1
- package/docs/reception.md +119 -0
- package/package.json +1 -1
- package/scripts/clean.mjs +43 -2
- package/skill-template/business-info.json +18 -2
- package/skill-template/domains/caizhilian.md +1 -1
- package/skill-template/domains/reception-flow.md +18 -0
- package/skill-template/domains/yepiaotong.md +4 -7
- package/skills/weaver-e10-archive-oa/references/source-manifest.json +14 -14
- package/skills/weaver-e10-blog/references/blog-publish.md +1 -1
- package/skills/weaver-e10-blog/references/source-manifest.json +7 -7
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +21 -21
- package/skills/weaver-e10-caizhilian/references/write-protocol.md +2 -2
- package/skills/weaver-e10-calendar/references/calendar-write.md +1 -1
- package/skills/weaver-e10-cowork/SKILL.md +1 -1
- package/skills/weaver-e10-ebuilder-form/SKILL.md +1 -1
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -1
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -1
- package/skills/weaver-e10-esb/SKILL.md +2 -2
- package/skills/weaver-e10-esb/references/source-manifest.json +22 -22
- package/skills/weaver-e10-esb/references/trigger.md +2 -2
- package/skills/weaver-e10-jinchengda/references/contract-clause-library-crud.md +1 -1
- package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +1 -1
- package/skills/weaver-e10-jinchengda/references/contract-request-write.md +1 -1
- package/skills/weaver-e10-jinchengda/references/source-manifest.json +29 -29
- package/skills/weaver-e10-jucailin/SKILL.md +1 -1
- package/skills/weaver-e10-jucailin/references/attend-write.md +2 -2
- package/skills/weaver-e10-jucailin/references/recruit-create.md +12 -3
- package/skills/weaver-e10-jucailin/references/recruit-write.md +1 -1
- package/skills/weaver-e10-meeting/SKILL.md +8 -6
- package/skills/weaver-e10-meeting/references/meeting-change.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-create.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +1 -1
- package/skills/weaver-e10-meeting/references/meeting-verification.md +1 -1
- package/skills/weaver-e10-okr/references/okr-align.md +1 -1
- package/skills/weaver-e10-okr/references/okr-keyresult.md +1 -1
- package/skills/weaver-e10-okr/references/okr-write.md +1 -1
- package/skills/weaver-e10-okr/references/source-manifest.json +8 -8
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/references/loan-management.md +1 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +18 -18
- package/skills/weaver-e10-reception-flow/SKILL.md +173 -0
- package/skills/weaver-e10-reception-flow/assets//350/241/214/347/250/213/345/215/225/346/250/241/346/235/277-/346/263/233/345/276/256.docx +0 -0
- package/skills/weaver-e10-reception-flow/data/fields-snapshot.json +213 -0
- package/skills/weaver-e10-reception-flow/references/browsertool.md +68 -0
- package/skills/weaver-e10-reception-flow/references/env-pitfalls.md +51 -0
- package/skills/weaver-e10-reception-flow/references/fields-snapshot.md +227 -0
- package/skills/weaver-e10-reception-flow/references/guide.md +179 -0
- package/skills/weaver-e10-reception-flow/scripts/locate.sh +14 -0
- package/skills/weaver-e10-reception-flow/scripts/render.py +724 -0
- package/skills/weaver-e10-recruit-stage/SKILL.md +9 -5
- package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +1 -1
- package/skills/weaver-e10-recruit-stage/lib/xiaoe-session.mjs +66 -0
- package/skills/weaver-e10-recruit-stage/references/position-templates.md +202 -0
- package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +22 -0
- package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +14 -1
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +6 -0
- package/skills/weaver-e10-shijingran/SKILL.md +5 -5
- package/skills/weaver-e10-shijingran/references/source-manifest.json +37 -36
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +2 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
- package/skills/weaver-e10-workflow/references/source-manifest.json +32 -32
- package/skills/weaver-e10-yepiaotong/SKILL.md +19 -26
- package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +13 -10
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +2 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +194 -155
- package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +28 -32
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +5 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +8 -10
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +211 -616
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +149 -117
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +17 -20
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +20 -39
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +55 -22
- package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +7 -17
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +88 -92
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +144 -354
- package/skills/weaver-e10-yimiaoban/SKILL.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +1 -1
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +7 -7
- package/dist/chunks/chunk-TTPJJJC4.js +0 -465
- package/dist/chunks/registry-BKBZPBAJ.js +0 -23
- package/dist/shortcuts/invoice/markdown.js +0 -172
- package/dist/shortcuts/invoice/operations/issuing-qys.js +0 -424
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +0 -131
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +0 -179
|
@@ -0,0 +1,2497 @@
|
|
|
1
|
+
import { createRequire as __esbuildCreateRequire } from 'node:module';
|
|
2
|
+
const require = __esbuildCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_RESULT_THRESHOLD_BYTES,
|
|
5
|
+
Output,
|
|
6
|
+
RefError,
|
|
7
|
+
bytesOf,
|
|
8
|
+
listRefs,
|
|
9
|
+
pickValue,
|
|
10
|
+
pruneRefs,
|
|
11
|
+
readRef,
|
|
12
|
+
refMaxBytes,
|
|
13
|
+
refMaxCount,
|
|
14
|
+
resolveRefInput,
|
|
15
|
+
resultThresholdBytes
|
|
16
|
+
} from "./chunk-727FH2NI.js";
|
|
17
|
+
import {
|
|
18
|
+
refsDir
|
|
19
|
+
} from "./chunk-AJ62BGAW.js";
|
|
20
|
+
import {
|
|
21
|
+
Command,
|
|
22
|
+
clearInvoiceTokenForSession
|
|
23
|
+
} from "./chunk-BCVYBYYT.js";
|
|
24
|
+
import {
|
|
25
|
+
shouldReportPlatformError,
|
|
26
|
+
toPlatformError
|
|
27
|
+
} from "./chunk-6N4JPNFL.js";
|
|
28
|
+
import {
|
|
29
|
+
appendXiaoEAuthErrorLog,
|
|
30
|
+
clearXiaoEAuthErrorLog,
|
|
31
|
+
getXiaoEAuthErrorLogPath,
|
|
32
|
+
normalizeXiaoEAuthErrorLogLimit,
|
|
33
|
+
readXiaoEAuthErrorLog,
|
|
34
|
+
xiaoEAuthLogSecrets
|
|
35
|
+
} from "./chunk-ETNJSKMM.js";
|
|
36
|
+
import {
|
|
37
|
+
E10AuthSession,
|
|
38
|
+
XIAOE_AGENT_TYPE,
|
|
39
|
+
buildE10CookieHeader,
|
|
40
|
+
clearProfileAuthFile,
|
|
41
|
+
createXiaoEUnifiedLoginUnavailableError,
|
|
42
|
+
extractRawSetCookie,
|
|
43
|
+
extractSessionTicket,
|
|
44
|
+
findAuthFile,
|
|
45
|
+
getActiveProfile,
|
|
46
|
+
getE10AuthRoot,
|
|
47
|
+
getProfileAuthPath,
|
|
48
|
+
getProfileDir,
|
|
49
|
+
hasXiaoEEnv,
|
|
50
|
+
inspectXiaoEUnifiedLoginSources,
|
|
51
|
+
listProfiles,
|
|
52
|
+
normalizeGlobalOptions,
|
|
53
|
+
packageFileUrl,
|
|
54
|
+
packagePath,
|
|
55
|
+
profileNameFromUrl,
|
|
56
|
+
readProfileConfig,
|
|
57
|
+
readTeamsCheckUserInfo,
|
|
58
|
+
resolveXiaoEPreferredBaseUrl,
|
|
59
|
+
saveAuthFile,
|
|
60
|
+
saveProfileConfig,
|
|
61
|
+
setActiveProfile,
|
|
62
|
+
switchActiveProfile,
|
|
63
|
+
tryXiaoEUnifiedLogin
|
|
64
|
+
} from "./chunk-UFIT2UU3.js";
|
|
65
|
+
import {
|
|
66
|
+
CliError,
|
|
67
|
+
isCliError,
|
|
68
|
+
toErrorEnvelope
|
|
69
|
+
} from "./chunk-UG5G54VL.js";
|
|
70
|
+
|
|
71
|
+
// src/cmd/argv.ts
|
|
72
|
+
var VALUE_TAKING_GLOBAL_OPTIONS = /* @__PURE__ */ new Set([
|
|
73
|
+
"--config",
|
|
74
|
+
"--auth",
|
|
75
|
+
"--profile",
|
|
76
|
+
"--runtime-agent",
|
|
77
|
+
"--base-url",
|
|
78
|
+
"--passport-url"
|
|
79
|
+
]);
|
|
80
|
+
function firstPositionalToken(argv) {
|
|
81
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
82
|
+
const token = argv[index];
|
|
83
|
+
if (token === void 0) return void 0;
|
|
84
|
+
if (token === "--") return argv[index + 1];
|
|
85
|
+
if (token.startsWith("-")) {
|
|
86
|
+
if (VALUE_TAKING_GLOBAL_OPTIONS.has(token)) index += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
return token;
|
|
90
|
+
}
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
function toCommandMetadata(commands) {
|
|
94
|
+
return commands.map((command) => ({
|
|
95
|
+
name: command.name,
|
|
96
|
+
description: command.description,
|
|
97
|
+
category: command.category
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/cmd/commands/index.ts
|
|
102
|
+
var commandsCommand = {
|
|
103
|
+
name: "commands",
|
|
104
|
+
description: "Registered command discovery",
|
|
105
|
+
category: "system",
|
|
106
|
+
register({ program, action }) {
|
|
107
|
+
const commands = program.command("commands").alias("module").description("Inspect registered commands");
|
|
108
|
+
commands.command("list").description("List registered commands").action(action((ctx) => {
|
|
109
|
+
ctx.output.write({ commands: ctx.commands, modules: ctx.modules });
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/cmd/config/index.ts
|
|
115
|
+
function parseValue(value) {
|
|
116
|
+
if (value === "true") return true;
|
|
117
|
+
if (value === "false") return false;
|
|
118
|
+
if (value === "null") return null;
|
|
119
|
+
if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
|
|
120
|
+
try {
|
|
121
|
+
return JSON.parse(value);
|
|
122
|
+
} catch {
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
var configCommand = {
|
|
127
|
+
name: "config",
|
|
128
|
+
description: "Generic local configuration commands",
|
|
129
|
+
category: "system",
|
|
130
|
+
register({ program, action }) {
|
|
131
|
+
const config = program.command("config").description("Manage weaver-work-cli config");
|
|
132
|
+
config.command("path").description("Print the active config file path").action(action((ctx) => {
|
|
133
|
+
ctx.output.write({ path: ctx.config.path() }, { text: ctx.config.path() });
|
|
134
|
+
}));
|
|
135
|
+
config.command("list").description("Print all config values").action(action((ctx) => {
|
|
136
|
+
ctx.output.write(ctx.config.all());
|
|
137
|
+
}));
|
|
138
|
+
config.command("get").description("Print one config value").argument("<key>", "config key").action(action((ctx, key) => {
|
|
139
|
+
if (typeof key !== "string") throw new CliError("key must be a string", 2 /* UsageError */);
|
|
140
|
+
ctx.output.write({ key, value: ctx.config.get(key) });
|
|
141
|
+
}));
|
|
142
|
+
config.command("set").description("Set one config value").argument("<key>", "config key").argument("<value>", "config value").action(action((ctx, key, value) => {
|
|
143
|
+
if (typeof key !== "string" || typeof value !== "string") {
|
|
144
|
+
throw new CliError("key and value must be strings", 2 /* UsageError */);
|
|
145
|
+
}
|
|
146
|
+
const parsed = parseValue(value);
|
|
147
|
+
ctx.config.set(key, parsed);
|
|
148
|
+
ctx.output.write({ key, value: parsed, path: ctx.config.path() }, { text: "Saved " + key });
|
|
149
|
+
}));
|
|
150
|
+
config.command("delete").description("Delete one config value").argument("<key>", "config key").action(action((ctx, key) => {
|
|
151
|
+
if (typeof key !== "string") throw new CliError("key must be a string", 2 /* UsageError */);
|
|
152
|
+
const deleted = ctx.config.delete(key);
|
|
153
|
+
ctx.output.write({ key, deleted, path: ctx.config.path() }, { text: deleted ? "Deleted " + key : "No value for " + key });
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/cmd/docs/index.ts
|
|
159
|
+
import { cpSync, existsSync, readdirSync, readFileSync } from "node:fs";
|
|
160
|
+
import { resolve } from "node:path";
|
|
161
|
+
var DOCS_DIR = packagePath("docs");
|
|
162
|
+
var PKG_JSON_URL = packageFileUrl("package.json");
|
|
163
|
+
function packageVersion() {
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(readFileSync(PKG_JSON_URL, "utf-8")).version;
|
|
166
|
+
} catch {
|
|
167
|
+
return "unknown";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function countFiles(dir) {
|
|
171
|
+
let count = 0;
|
|
172
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
173
|
+
const entryPath = resolve(dir, entry.name);
|
|
174
|
+
count += entry.isDirectory() ? countFiles(entryPath) : 1;
|
|
175
|
+
}
|
|
176
|
+
return count;
|
|
177
|
+
}
|
|
178
|
+
function ensureDocs() {
|
|
179
|
+
if (!existsSync(DOCS_DIR)) {
|
|
180
|
+
throw new CliError("docs directory does not exist: " + DOCS_DIR);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
var docsCommand = {
|
|
184
|
+
name: "docs",
|
|
185
|
+
description: "Packaged docs and template copy commands",
|
|
186
|
+
category: "system",
|
|
187
|
+
register({ program, action }) {
|
|
188
|
+
const docs = program.command("docs").description("Read or copy packaged docs");
|
|
189
|
+
docs.command("root").description("Print packaged docs root path").action(action((ctx) => {
|
|
190
|
+
ensureDocs();
|
|
191
|
+
const root = DOCS_DIR.replace(/[\\/]+$/, "");
|
|
192
|
+
ctx.output.write({ root, version: packageVersion() }, { text: root + "\nversion: " + packageVersion() });
|
|
193
|
+
}));
|
|
194
|
+
docs.command("copy").description("Copy packaged docs to <target>/weaver-work-cli-docs").argument("[target]", "target directory", process.cwd()).action(action((ctx, target) => {
|
|
195
|
+
ensureDocs();
|
|
196
|
+
const dest = resolve(String(target || process.cwd()), "weaver-work-cli-docs");
|
|
197
|
+
const sourceRoot = DOCS_DIR.replace(/[\\/]+$/, "");
|
|
198
|
+
if (dest === sourceRoot || dest.startsWith(sourceRoot + "/") || dest.startsWith(sourceRoot + "\\")) {
|
|
199
|
+
throw new CliError("target directory is inside docs source: " + dest);
|
|
200
|
+
}
|
|
201
|
+
cpSync(DOCS_DIR, dest, { recursive: true });
|
|
202
|
+
const files = countFiles(dest);
|
|
203
|
+
ctx.output.write({ files, dest, version: packageVersion() }, { text: "Copied " + files + " files to " + dest });
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/cmd/doctor/index.ts
|
|
209
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
210
|
+
function versionMajor(version) {
|
|
211
|
+
return Number(version.replace(/^v/, "").split(".")[0] || 0);
|
|
212
|
+
}
|
|
213
|
+
var doctorCommand = {
|
|
214
|
+
name: "doctor",
|
|
215
|
+
description: "Environment diagnostics",
|
|
216
|
+
category: "system",
|
|
217
|
+
register({ program, action }) {
|
|
218
|
+
program.command("doctor").description("Check local CLI environment").option("--e10", "also check E10 auth file discovery").action(action((ctx, options) => {
|
|
219
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
220
|
+
const checks = [
|
|
221
|
+
{
|
|
222
|
+
name: "node",
|
|
223
|
+
ok: versionMajor(process.version) >= 18,
|
|
224
|
+
detail: process.version
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "package",
|
|
228
|
+
ok: Boolean(ctx.packageInfo.version),
|
|
229
|
+
detail: ctx.packageInfo.name + "@" + ctx.packageInfo.version
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "configPath",
|
|
233
|
+
ok: true,
|
|
234
|
+
detail: ctx.config.path()
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
if (opts.e10) {
|
|
238
|
+
const authFile = findAuthFile(ctx.cwd);
|
|
239
|
+
checks.push({
|
|
240
|
+
name: "e10AuthFile",
|
|
241
|
+
ok: Boolean(authFile && existsSync2(authFile)),
|
|
242
|
+
detail: authFile || "not found"
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const ok = checks.every((check) => check.ok);
|
|
246
|
+
ctx.output.write({ ok, checks });
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// src/internal/e10/auth/commands.ts
|
|
252
|
+
import { execFile } from "node:child_process";
|
|
253
|
+
import { randomBytes } from "node:crypto";
|
|
254
|
+
import { createServer } from "node:http";
|
|
255
|
+
var DEFAULT_AGENT_TYPE = "weaver-work-cli";
|
|
256
|
+
var EXPLICIT_AGENT_TYPE_ENV_KEYS = [
|
|
257
|
+
"WORKBUDDY_AGENT_TYPE",
|
|
258
|
+
"CODEX_AGENT_TYPE",
|
|
259
|
+
"WEAVER_AGENT_TYPE"
|
|
260
|
+
];
|
|
261
|
+
var AGENT_RETURN_URL_ENV_KEY = "WEAVER_AGENT_RETURN_URL";
|
|
262
|
+
var AGENT_RETURN_PATH = "/agent-return";
|
|
263
|
+
var AGENT_RETURN_TOKEN_HEADER = "x-weaver-agent-return-token";
|
|
264
|
+
var AGENT_RETURN_TIMEOUT_MS = 9e5;
|
|
265
|
+
var OAUTH_CALLBACK_PROTOCOL_MISMATCH_GRACE_MS = 3e4;
|
|
266
|
+
var UNSAFE_AGENT_RETURN_PROTOCOLS = /* @__PURE__ */ new Set([
|
|
267
|
+
"about:",
|
|
268
|
+
"blob:",
|
|
269
|
+
"data:",
|
|
270
|
+
"file:",
|
|
271
|
+
"ftp:",
|
|
272
|
+
"http:",
|
|
273
|
+
"https:",
|
|
274
|
+
"javascript:",
|
|
275
|
+
"mailto:",
|
|
276
|
+
"sms:",
|
|
277
|
+
"tel:",
|
|
278
|
+
"vbscript:",
|
|
279
|
+
"ws:",
|
|
280
|
+
"wss:"
|
|
281
|
+
]);
|
|
282
|
+
var BUILT_IN_AGENT_RETURN_TARGETS = [
|
|
283
|
+
{
|
|
284
|
+
aliases: ["workbuddy"],
|
|
285
|
+
label: "WorkBuddy",
|
|
286
|
+
url: "workbuddy://"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
aliases: ["xiaoe", "xiaoeclient", "\u5C0Fe", "\u667A\u80FD\u5C0Fe"],
|
|
290
|
+
label: "\u667A\u80FD\u5C0F E",
|
|
291
|
+
url: "xiaoe-client://"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
aliases: ["codex", "openaicodex"],
|
|
295
|
+
label: "Codex",
|
|
296
|
+
url: "codex://"
|
|
297
|
+
}
|
|
298
|
+
];
|
|
299
|
+
function asOptions(value) {
|
|
300
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
301
|
+
}
|
|
302
|
+
function optionString(options, ...keys) {
|
|
303
|
+
for (const key of keys) {
|
|
304
|
+
const value = options[key];
|
|
305
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
306
|
+
}
|
|
307
|
+
return "";
|
|
308
|
+
}
|
|
309
|
+
function envString(env, ...keys) {
|
|
310
|
+
for (const key of keys) {
|
|
311
|
+
const value = env[key];
|
|
312
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
313
|
+
}
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
function hasEnvPrefix(env, prefix) {
|
|
317
|
+
return Object.keys(env).some((key) => key === prefix || key.startsWith(`${prefix}_`));
|
|
318
|
+
}
|
|
319
|
+
function inferAgentTypeFromEnv(env) {
|
|
320
|
+
if (hasXiaoEEnv(env)) return XIAOE_AGENT_TYPE;
|
|
321
|
+
if (hasEnvPrefix(env, "WORKBUDDY")) return "workbuddy";
|
|
322
|
+
if (hasEnvPrefix(env, "CODEX")) return "codex";
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
function resolveAgentType(options = {}, env = process.env) {
|
|
326
|
+
return optionString(options, "agentType", "agent_type") || envString(env, ...EXPLICIT_AGENT_TYPE_ENV_KEYS) || inferAgentTypeFromEnv(env) || DEFAULT_AGENT_TYPE;
|
|
327
|
+
}
|
|
328
|
+
function normalizeAgentKey(value) {
|
|
329
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/gu, "");
|
|
330
|
+
}
|
|
331
|
+
function normalizeAgentAlias(value) {
|
|
332
|
+
return value.trim().toLowerCase().replace(/[^\p{Letter}\p{Number}]+/gu, "");
|
|
333
|
+
}
|
|
334
|
+
function builtInAgentReturnTarget(agentType) {
|
|
335
|
+
const alias = normalizeAgentAlias(agentType);
|
|
336
|
+
const target = BUILT_IN_AGENT_RETURN_TARGETS.find((candidate) => candidate.aliases.includes(alias));
|
|
337
|
+
return target ? { label: target.label, url: target.url } : null;
|
|
338
|
+
}
|
|
339
|
+
function conventionalAgentReturnUrl(agentType) {
|
|
340
|
+
const scheme = agentType.trim().toLowerCase();
|
|
341
|
+
if (normalizeAgentAlias(agentType) === normalizeAgentAlias(DEFAULT_AGENT_TYPE) || scheme.length > 64 || !/^[a-z][a-z0-9+.-]*$/u.test(scheme) || UNSAFE_AGENT_RETURN_PROTOCOLS.has(`${scheme}:`)) {
|
|
342
|
+
return "";
|
|
343
|
+
}
|
|
344
|
+
return `${scheme}://`;
|
|
345
|
+
}
|
|
346
|
+
function validateAgentReturnUrl(value) {
|
|
347
|
+
if (value.length > 2048 || /[\u0000-\u001f\u007f\s]/u.test(value) || !/^[a-z][a-z0-9+.-]*:\/\//iu.test(value)) {
|
|
348
|
+
throw new CliError("Invalid --agent-return-url value", 2 /* UsageError */);
|
|
349
|
+
}
|
|
350
|
+
let url;
|
|
351
|
+
try {
|
|
352
|
+
url = new URL(value);
|
|
353
|
+
} catch {
|
|
354
|
+
throw new CliError("Invalid --agent-return-url value", 2 /* UsageError */);
|
|
355
|
+
}
|
|
356
|
+
if (UNSAFE_AGENT_RETURN_PROTOCOLS.has(url.protocol)) {
|
|
357
|
+
throw new CliError("--agent-return-url must use a registered desktop application URL scheme.", 2 /* UsageError */);
|
|
358
|
+
}
|
|
359
|
+
return value;
|
|
360
|
+
}
|
|
361
|
+
function resolveAgentReturnTarget(options = {}, env = process.env, agentType = resolveAgentType(options, env)) {
|
|
362
|
+
const builtInTarget = builtInAgentReturnTarget(agentType);
|
|
363
|
+
const explicitUrl = optionString(options, "agentReturnUrl") || envString(env, AGENT_RETURN_URL_ENV_KEY);
|
|
364
|
+
if (explicitUrl) {
|
|
365
|
+
return {
|
|
366
|
+
label: builtInTarget?.label || agentType,
|
|
367
|
+
url: validateAgentReturnUrl(explicitUrl)
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (builtInTarget) return builtInTarget;
|
|
371
|
+
const conventionalUrl = conventionalAgentReturnUrl(agentType);
|
|
372
|
+
return conventionalUrl ? { label: agentType, url: conventionalUrl } : null;
|
|
373
|
+
}
|
|
374
|
+
function normalizeUrl(value) {
|
|
375
|
+
return value.replace(/\/+$/, "");
|
|
376
|
+
}
|
|
377
|
+
function readString(data, ...keys) {
|
|
378
|
+
for (const key of keys) {
|
|
379
|
+
const value = data[key];
|
|
380
|
+
if (typeof value === "string" && value) return value;
|
|
381
|
+
if (typeof value === "number") return String(value);
|
|
382
|
+
}
|
|
383
|
+
return "";
|
|
384
|
+
}
|
|
385
|
+
function cookieString(value) {
|
|
386
|
+
if (!value) return "";
|
|
387
|
+
if (typeof value === "string") return value;
|
|
388
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
389
|
+
return Object.entries(value).filter(([, cookieValue2]) => cookieValue2 !== void 0 && cookieValue2 !== null && cookieValue2 !== "").map(([name, cookieValue2]) => `${name}=${String(cookieValue2)}`).join("; ");
|
|
390
|
+
}
|
|
391
|
+
return "";
|
|
392
|
+
}
|
|
393
|
+
function cookieValue(rawCookie, name) {
|
|
394
|
+
const match = rawCookie.match(new RegExp(`${name}=([^;]+)`));
|
|
395
|
+
return match?.[1] || "";
|
|
396
|
+
}
|
|
397
|
+
function formatAuthStatus(status) {
|
|
398
|
+
if (!status.loggedIn) {
|
|
399
|
+
return "Not logged in. Select the appropriate authentication method for the current runtime before retrying.";
|
|
400
|
+
}
|
|
401
|
+
const lines = [
|
|
402
|
+
"Logged in.",
|
|
403
|
+
"userId: " + status.userId,
|
|
404
|
+
"tenantKey: " + (status.tenantKey || ""),
|
|
405
|
+
"tenantName: " + (status.tenantName || ""),
|
|
406
|
+
"department: " + (status.department || ""),
|
|
407
|
+
"subcompany: " + (status.subcompany || ""),
|
|
408
|
+
"position: " + (status.position || ""),
|
|
409
|
+
"version: " + (status.version || ""),
|
|
410
|
+
"agentType: " + (status.agentType || ""),
|
|
411
|
+
"baseUrl: " + status.baseUrl,
|
|
412
|
+
"profile: " + status.profile,
|
|
413
|
+
"authFile: " + status.authFile,
|
|
414
|
+
..."loginSource" in status && status.loginSource ? ["loginSource: " + status.loginSource] : [],
|
|
415
|
+
..."credentialSourceStep" in status && status.credentialSourceStep ? ["credentialSourceStep: " + status.credentialSourceStep] : [],
|
|
416
|
+
..."credentialSourceType" in status && status.credentialSourceType ? ["credentialSourceType: " + status.credentialSourceType] : [],
|
|
417
|
+
..."credentialSourceLabel" in status && status.credentialSourceLabel ? ["credentialSourceLabel: " + status.credentialSourceLabel] : [],
|
|
418
|
+
..."expiryTimes" in status && status.expiryTimes ? ["expiryTimes: " + status.expiryTimes] : []
|
|
419
|
+
];
|
|
420
|
+
if ("valid" in status) {
|
|
421
|
+
lines.push("valid: " + String(status.valid));
|
|
422
|
+
}
|
|
423
|
+
return lines.join("\n");
|
|
424
|
+
}
|
|
425
|
+
function formatLoginResult(result) {
|
|
426
|
+
return [
|
|
427
|
+
"Saved E10 auth.",
|
|
428
|
+
"userId: " + result.userId,
|
|
429
|
+
"tenantKey: " + result.tenantKey,
|
|
430
|
+
"tenantName: " + result.tenantName,
|
|
431
|
+
"baseUrl: " + result.baseUrl,
|
|
432
|
+
"profile: " + result.profile,
|
|
433
|
+
"authFile: " + result.authFile,
|
|
434
|
+
...result.loginSource ? ["loginSource: " + result.loginSource] : [],
|
|
435
|
+
...result.credentialSourceStep ? ["credentialSourceStep: " + result.credentialSourceStep] : [],
|
|
436
|
+
...result.credentialSourceType ? ["credentialSourceType: " + result.credentialSourceType] : [],
|
|
437
|
+
...result.credentialSourceLabel ? ["credentialSourceLabel: " + result.credentialSourceLabel] : [],
|
|
438
|
+
...result.expiryTimes ? ["expiryTimes: " + result.expiryTimes] : []
|
|
439
|
+
].join("\n");
|
|
440
|
+
}
|
|
441
|
+
async function clearInvoiceTokenCache(profile, authData) {
|
|
442
|
+
if (!authData.baseUrl || !authData.userId) return;
|
|
443
|
+
await clearInvoiceTokenForSession({
|
|
444
|
+
baseUrl: normalizeUrl(authData.baseUrl),
|
|
445
|
+
profile,
|
|
446
|
+
userId: authData.userId
|
|
447
|
+
}).catch(() => false);
|
|
448
|
+
}
|
|
449
|
+
function posixShellQuote(value) {
|
|
450
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
451
|
+
}
|
|
452
|
+
function powershellQuote(value) {
|
|
453
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
454
|
+
}
|
|
455
|
+
function resolveWeaverEnvShell(shell, platform = process.platform) {
|
|
456
|
+
if (shell === "auto") return platform === "win32" ? "powershell" : "posix";
|
|
457
|
+
if (shell === "posix" || shell === "powershell") return shell;
|
|
458
|
+
throw new CliError("Invalid --shell value. Use auto, posix, or powershell.", 2 /* UsageError */);
|
|
459
|
+
}
|
|
460
|
+
function formatWeaverEnvExports(value, shell = "auto", platform = process.platform) {
|
|
461
|
+
const resolved = resolveWeaverEnvShell(shell, platform);
|
|
462
|
+
return Object.entries(value).map(([name, envValue2]) => resolved === "powershell" ? `$env:${name} = ${powershellQuote(envValue2)}` : `export ${name}=${posixShellQuote(envValue2)}`).join("\n");
|
|
463
|
+
}
|
|
464
|
+
function esc(value) {
|
|
465
|
+
return String(value ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
466
|
+
}
|
|
467
|
+
function renderE10LoginSuccessPage(userId, returnTarget = null, launchToken = "") {
|
|
468
|
+
const safeReturnTarget = returnTarget ? { ...returnTarget, url: validateAgentReturnUrl(returnTarget.url) } : null;
|
|
469
|
+
const agentKey = safeReturnTarget ? normalizeAgentKey(safeReturnTarget.label) || "agent" : "";
|
|
470
|
+
const returnAction = safeReturnTarget && launchToken ? `<div class="overlay" data-agent-return data-token="${esc(launchToken)}" data-agent-key="${esc(agentKey)}" data-agent-label="${esc(safeReturnTarget.label)}" data-fallback-url="${esc(safeReturnTarget.url)}"><section class="prompt" role="dialog" aria-modal="true" aria-labelledby="agent-return-title"><div class="app-icon" aria-hidden="true"><svg viewBox="0 0 64 64"><defs><linearGradient id="app-gradient" x1="8" y1="4" x2="56" y2="60" gradientUnits="userSpaceOnUse"><stop stop-color="#24d3b5"/><stop offset="1" stop-color="#08aa82"/></linearGradient></defs><rect width="64" height="64" rx="16" fill="url(#app-gradient)"/><path d="M18 21.5 32 13l14 8.5v17L32 47 18 38.5v-17Z" fill="#fff" fill-opacity=".96"/><rect x="23" y="25" width="18" height="15" rx="6" fill="#12b991"/><circle cx="28.5" cy="32.5" r="1.8" fill="#fff"/><circle cx="35.5" cy="32.5" r="1.8" fill="#fff"/></svg></div><h1 id="agent-return-title">\u662F\u5426\u6253\u5F00\u201C${esc(safeReturnTarget.label)}\u201D\uFF1F</h1><p>\u767B\u5F55\u6388\u6743\u5DF2\u5B8C\u6210\uFF0C\u8FD4\u56DE ${esc(safeReturnTarget.label)} \u7EE7\u7EED\u4F7F\u7528\u3002</p><button class="primary" type="button" data-action="open">\u6253\u5F00\u201C${esc(safeReturnTarget.label)}\u201D</button><button class="secondary" type="button" data-action="always">\u59CB\u7EC8\u6253\u5F00\u201C${esc(safeReturnTarget.label)}\u201D</button><button class="secondary" type="button" data-action="cancel">\u53D6\u6D88</button><div class="status" role="status" aria-live="polite"></div></section></div>` : "";
|
|
471
|
+
const returnScript = safeReturnTarget && launchToken ? `<script nonce="${esc(launchToken)}">(()=>{const root=document.querySelector('[data-agent-return]');if(!root)return;const status=root.querySelector('.status');const buttons=[...root.querySelectorAll('button')];const agentLabel=root.dataset.agentLabel||'\u5E94\u7528';const fallbackUrl=root.dataset.fallbackUrl||'';const storageKey='weaver-work-cli.agent-return.'+root.dataset.agentKey;let busy=false;const setBusy=value=>{busy=value;buttons.forEach(button=>{button.disabled=value;});};const closePrompt=()=>{root.hidden=true;};const savePreference=()=>{try{localStorage.setItem(storageKey,'1');}catch{}};const clearPreference=()=>{try{localStorage.removeItem(storageKey);}catch{}};const hasPreference=()=>{try{return localStorage.getItem(storageKey)==='1';}catch{return false;}};const post=async action=>{let response;try{response=await fetch('${AGENT_RETURN_PATH}?action='+encodeURIComponent(action),{method:'POST',headers:{'${AGENT_RETURN_TOKEN_HEADER}':root.dataset.token}});}catch{return false;}if(!response.ok)throw new Error(await response.text()||'\u8BF7\u6C42\u5931\u8D25');return true;};const openAgent=async persist=>{if(busy)return;setBusy(true);status.textContent='\u6B63\u5728\u6253\u5F00 '+agentLabel+'\u2026';try{const openedLocally=await post('open');if(persist)savePreference();if(!openedLocally){closePrompt();window.location.href=fallbackUrl;return;}closePrompt();}catch(error){if(persist)clearPreference();status.textContent=error instanceof Error?error.message:'\u65E0\u6CD5\u6253\u5F00\u5E94\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002';setBusy(false);}};root.querySelector('[data-action="open"]').addEventListener('click',()=>openAgent(false));root.querySelector('[data-action="always"]').addEventListener('click',()=>openAgent(true));root.querySelector('[data-action="cancel"]').addEventListener('click',()=>{if(busy)return;clearPreference();closePrompt();post('cancel').catch(()=>{});});if(hasPreference())openAgent(false);})();</script>` : "";
|
|
472
|
+
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>weaver-work-cli \u767B\u5F55\u6210\u529F</title><style>:root{color-scheme:light}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;background:radial-gradient(circle at 50% 18%,#eefbf8 0,#f6f7f9 42%,#eef1f5 100%);color:#20242a;display:grid;place-items:center;min-height:100vh;margin:0}.card{background:rgba(255,255,255,.88);border:1px solid #e6e8ec;border-radius:18px;padding:34px 38px;box-shadow:0 18px 55px rgba(18,24,40,.09);width:min(560px,calc(100vw - 40px));text-align:center}.title{font-size:24px;font-weight:700;margin-bottom:10px}.desc{color:#667085;font-size:15px;line-height:24px}.user{color:#98a2b3;font-size:12px;margin-top:18px}.overlay{position:fixed;inset:0;z-index:10;display:grid;place-items:center;padding:20px;background:rgba(28,34,41,.36);backdrop-filter:blur(5px)}.overlay[hidden]{display:none}.prompt{width:min(410px,calc(100vw - 32px));padding:32px 28px 26px;border:1px solid rgba(255,255,255,.75);border-radius:24px;background:rgba(250,250,251,.97);box-shadow:0 24px 70px rgba(15,23,42,.28);text-align:center}.app-icon{width:74px;height:74px;margin:0 auto 24px;filter:drop-shadow(0 8px 14px rgba(8,170,130,.22))}.app-icon svg{display:block;width:100%;height:100%}.prompt h1{margin:0;font-size:20px;line-height:30px;font-weight:700;letter-spacing:-.2px}.prompt p{margin:10px 0 24px;color:#667085;font-size:14px;line-height:22px}.prompt button{display:block;width:100%;min-height:40px;border:0;border-radius:12px;font:600 16px/22px inherit;cursor:pointer;transition:transform .12s ease,filter .12s ease}.prompt button+button{margin-top:10px}.prompt button:hover{filter:brightness(.98)}.prompt button:active{transform:scale(.99)}.prompt button:disabled{cursor:wait;opacity:.62}.primary{background:linear-gradient(180deg,#2f91ff,#0b74e5);color:#fff;box-shadow:0 8px 18px rgba(11,116,229,.2)}.secondary{background:#e9eaec;color:#22252a}.status{min-height:18px;margin-top:14px;color:#667085;font-size:12px;line-height:18px}</style></head><body><main class="card"><div class="title">\u767B\u5F55\u6388\u6743\u6210\u529F</div><div class="desc">\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\uFF0C\u8FD4\u56DE\u667A\u80FD\u4F53\u6216\u7EC8\u7AEF\u7EE7\u7EED\u4F7F\u7528\u3002</div><div class="user">${esc(userId)}</div></main>${returnAction}${returnScript}</body></html>`;
|
|
473
|
+
}
|
|
474
|
+
function resolveBrowserOpenCommand(url, platform = process.platform) {
|
|
475
|
+
if (platform === "win32") {
|
|
476
|
+
return { file: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] };
|
|
477
|
+
}
|
|
478
|
+
if (platform === "darwin") return { file: "open", args: [url] };
|
|
479
|
+
return { file: "xdg-open", args: [url] };
|
|
480
|
+
}
|
|
481
|
+
function openUrlInBrowser(url, ctx) {
|
|
482
|
+
const command = resolveBrowserOpenCommand(url);
|
|
483
|
+
execFile(command.file, command.args, (error) => {
|
|
484
|
+
if (error) {
|
|
485
|
+
ctx.logger.warn("Could not open browser automatically. Copy the login URL manually.");
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
function openUrlWithSystemHandler(url) {
|
|
490
|
+
const command = resolveBrowserOpenCommand(url);
|
|
491
|
+
return new Promise((resolve8, reject) => {
|
|
492
|
+
execFile(command.file, command.args, (error) => {
|
|
493
|
+
if (error) reject(error);
|
|
494
|
+
else resolve8();
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
function buildE10AuthorizeUrl(baseUrl, redirectUri, agentType) {
|
|
499
|
+
const authorizeParams = new URLSearchParams({
|
|
500
|
+
redirect_uri: redirectUri,
|
|
501
|
+
response_type: "code",
|
|
502
|
+
access_type: "agent",
|
|
503
|
+
agent_type: agentType,
|
|
504
|
+
from: agentType
|
|
505
|
+
});
|
|
506
|
+
return `${baseUrl}/papi/sso/oauth2.0/authorize?${authorizeParams.toString()}`;
|
|
507
|
+
}
|
|
508
|
+
function isTlsClientHello(error) {
|
|
509
|
+
if (!error || typeof error !== "object") return false;
|
|
510
|
+
const rawPacket = error.rawPacket;
|
|
511
|
+
return Buffer.isBuffer(rawPacket) && rawPacket.length >= 3 && rawPacket[0] === 22 && rawPacket[1] === 3;
|
|
512
|
+
}
|
|
513
|
+
function createOAuthCallbackProtocolMismatchError(port) {
|
|
514
|
+
return Object.assign(new CliError(
|
|
515
|
+
`OAuth callback protocol mismatch: the browser connected to https://127.0.0.1:${port}, but the CLI callback listener uses HTTP. Retry the login and, on the callback error page, change only the URL scheme to http:// before reloading; keep the path and query unchanged. The E10 authorization service should preserve HTTP loopback redirect URIs.`,
|
|
516
|
+
3 /* AuthError */
|
|
517
|
+
), {
|
|
518
|
+
type: "authentication",
|
|
519
|
+
subtype: "oauth_callback_protocol_mismatch",
|
|
520
|
+
retryable: false,
|
|
521
|
+
stage: "oauth_callback"
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async function readJsonObject(resp) {
|
|
525
|
+
const data = await resp.json().catch(() => ({}));
|
|
526
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : {};
|
|
527
|
+
}
|
|
528
|
+
async function resolveUserInfo(baseUrl, eteamsId, rawCookie = "", agentType = "") {
|
|
529
|
+
const resp = await fetch(`${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`, {
|
|
530
|
+
method: "POST",
|
|
531
|
+
headers: {
|
|
532
|
+
accept: "application/json",
|
|
533
|
+
"content-type": "application/json",
|
|
534
|
+
eteamsid: eteamsId,
|
|
535
|
+
cookie: buildE10CookieHeader(rawCookie, eteamsId, agentType),
|
|
536
|
+
"user-agent": agentType ? `AgentType=${agentType},IsAgent=true` : DEFAULT_AGENT_TYPE,
|
|
537
|
+
origin: baseUrl
|
|
538
|
+
},
|
|
539
|
+
redirect: "manual"
|
|
540
|
+
});
|
|
541
|
+
const data = await resp.json().catch(() => ({}));
|
|
542
|
+
return { userInfo: readTeamsCheckUserInfo(resp, data), setCookie: extractRawSetCookie(resp) };
|
|
543
|
+
}
|
|
544
|
+
async function saveEteamsIdLogin(input) {
|
|
545
|
+
const baseUrl = normalizeUrl(input.baseUrl);
|
|
546
|
+
const passportUrl = normalizeUrl(input.passportUrl || baseUrl);
|
|
547
|
+
const { userInfo, setCookie } = await resolveUserInfo(baseUrl, input.eteamsId, input.rawCookie || "", input.agentType);
|
|
548
|
+
if (!userInfo.userId) {
|
|
549
|
+
throw new CliError("Could not resolve userId. Check whether ETEAMSID is valid for this E10 domain.", 3 /* AuthError */);
|
|
550
|
+
}
|
|
551
|
+
const tenantKey = userInfo.tenantKey || input.tenantKeyFallback;
|
|
552
|
+
const profile = input.profileHint || profileNameFromUrl(baseUrl);
|
|
553
|
+
setActiveProfile(profile);
|
|
554
|
+
const cookies = { ETEAMSID: input.eteamsId };
|
|
555
|
+
if (input.rawCookie) cookies.raw = input.rawCookie;
|
|
556
|
+
const ztSession = extractSessionTicket(setCookie, input.rawCookie || "");
|
|
557
|
+
const authData = {
|
|
558
|
+
cookies,
|
|
559
|
+
userId: userInfo.userId,
|
|
560
|
+
tenantKey,
|
|
561
|
+
tenantName: userInfo.tenantName,
|
|
562
|
+
department: userInfo.department,
|
|
563
|
+
subcompany: userInfo.subcompany,
|
|
564
|
+
position: userInfo.position,
|
|
565
|
+
agentType: input.agentType,
|
|
566
|
+
version: input.version || "",
|
|
567
|
+
baseUrl,
|
|
568
|
+
passportUrl,
|
|
569
|
+
cookieSavedAt: Date.now(),
|
|
570
|
+
...ztSession ? { ztSession } : {}
|
|
571
|
+
};
|
|
572
|
+
await clearInvoiceTokenCache(profile, authData);
|
|
573
|
+
const authFile = saveAuthFile(authData, profile);
|
|
574
|
+
saveProfileConfig(baseUrl, passportUrl, profile, tenantKey);
|
|
575
|
+
return {
|
|
576
|
+
...userInfo,
|
|
577
|
+
tenantKey,
|
|
578
|
+
eteamsId: input.eteamsId,
|
|
579
|
+
baseUrl,
|
|
580
|
+
passportUrl,
|
|
581
|
+
profile,
|
|
582
|
+
authFile,
|
|
583
|
+
agentType: input.agentType,
|
|
584
|
+
version: input.version || ""
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
async function tryXiaoEUnifiedLogin2(ctx, baseUrl, tenantKeyFallback, profileHint, allowTokenScript = false, diagnostics) {
|
|
588
|
+
const login = await tryXiaoEUnifiedLogin({
|
|
589
|
+
env: ctx.env,
|
|
590
|
+
baseUrl,
|
|
591
|
+
allowBundledEnvScript: true,
|
|
592
|
+
tenantKeyFallback,
|
|
593
|
+
profileHint,
|
|
594
|
+
allowTokenScript,
|
|
595
|
+
logger: ctx.logger,
|
|
596
|
+
diagnostics
|
|
597
|
+
});
|
|
598
|
+
return login?.result || null;
|
|
599
|
+
}
|
|
600
|
+
async function oidcExchange(code, redirectUri, baseUrl, tenantKeyFallback, agentTypeFallback, profileHint) {
|
|
601
|
+
const base = normalizeUrl(baseUrl);
|
|
602
|
+
const tokenParams = new URLSearchParams({
|
|
603
|
+
grant_type: "authorization_code",
|
|
604
|
+
code,
|
|
605
|
+
redirect_uri: redirectUri
|
|
606
|
+
});
|
|
607
|
+
const tokenResp = await fetch(`${base}/papi/sso/oauth2.0/accessToken`, {
|
|
608
|
+
method: "POST",
|
|
609
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
610
|
+
body: tokenParams.toString(),
|
|
611
|
+
redirect: "manual"
|
|
612
|
+
});
|
|
613
|
+
const tokenData = await readJsonObject(tokenResp);
|
|
614
|
+
if (tokenResp.status >= 400) {
|
|
615
|
+
const message = readString(tokenData, "message", "msg", "error_description", "error") || `OAuth token exchange failed: ${tokenResp.status}`;
|
|
616
|
+
throw new CliError(message, 3 /* AuthError */);
|
|
617
|
+
}
|
|
618
|
+
let eteamsId = readString(tokenData, "eteamsId", "eteamsid");
|
|
619
|
+
let userId = readString(tokenData, "id", "userId", "employeeId");
|
|
620
|
+
let rawCookies = cookieString(tokenData.cookies || tokenData.cookie) || extractRawSetCookie(tokenResp);
|
|
621
|
+
let agentType = agentTypeFallback || readString(tokenData, "agentType", "agent_type");
|
|
622
|
+
let version = readString(tokenData, "version", "baselineVersion");
|
|
623
|
+
const accessToken = readString(tokenData, "access_token", "accessToken");
|
|
624
|
+
if (!eteamsId) eteamsId = cookieValue(rawCookies, "ETEAMSID");
|
|
625
|
+
if (accessToken && (!eteamsId || !userId || !rawCookies || !version || !agentType)) {
|
|
626
|
+
const profileResp = await fetch(`${base}/papi/sso/oauth2.0/profile?access_token=${encodeURIComponent(accessToken)}`, {
|
|
627
|
+
method: "POST",
|
|
628
|
+
headers: { accept: "application/json" },
|
|
629
|
+
redirect: "manual"
|
|
630
|
+
});
|
|
631
|
+
const profileData = await readJsonObject(profileResp);
|
|
632
|
+
const profileRawCookies = cookieString(profileData.cookies || profileData.cookie) || extractRawSetCookie(profileResp);
|
|
633
|
+
if (!eteamsId) eteamsId = readString(profileData, "eteamsId", "eteamsid") || cookieValue(profileRawCookies, "ETEAMSID");
|
|
634
|
+
if (!userId) userId = readString(profileData, "id", "userId", "employeeId");
|
|
635
|
+
if (!rawCookies) rawCookies = profileRawCookies;
|
|
636
|
+
if (!agentType) agentType = readString(profileData, "agentType", "agent_type");
|
|
637
|
+
if (!version) version = readString(profileData, "version", "baselineVersion");
|
|
638
|
+
}
|
|
639
|
+
if (!eteamsId) {
|
|
640
|
+
throw new CliError("Could not resolve ETEAMSID from OAuth response.", 3 /* AuthError */);
|
|
641
|
+
}
|
|
642
|
+
const { userInfo, setCookie } = await resolveUserInfo(base, eteamsId, rawCookies, agentType).catch(() => ({
|
|
643
|
+
userInfo: {
|
|
644
|
+
userId: "",
|
|
645
|
+
tenantKey: "",
|
|
646
|
+
tenantName: "",
|
|
647
|
+
department: "",
|
|
648
|
+
subcompany: "",
|
|
649
|
+
position: ""
|
|
650
|
+
},
|
|
651
|
+
setCookie: ""
|
|
652
|
+
}));
|
|
653
|
+
const tenantKey = readString(tokenData, "tenant_key", "tenantKey") || userInfo.tenantKey || tenantKeyFallback;
|
|
654
|
+
const ztSession = extractSessionTicket(setCookie, rawCookies);
|
|
655
|
+
const authData = {
|
|
656
|
+
cookies: { ETEAMSID: eteamsId, raw: rawCookies },
|
|
657
|
+
userId: userInfo.userId || userId || "unknown",
|
|
658
|
+
tenantKey,
|
|
659
|
+
tenantName: userInfo.tenantName,
|
|
660
|
+
department: userInfo.department,
|
|
661
|
+
subcompany: userInfo.subcompany,
|
|
662
|
+
position: userInfo.position,
|
|
663
|
+
agentType,
|
|
664
|
+
version,
|
|
665
|
+
baseUrl: base,
|
|
666
|
+
passportUrl: base,
|
|
667
|
+
cookieSavedAt: Date.now(),
|
|
668
|
+
...ztSession ? { ztSession } : {}
|
|
669
|
+
};
|
|
670
|
+
const profile = profileHint || profileNameFromUrl(base);
|
|
671
|
+
setActiveProfile(profile);
|
|
672
|
+
await clearInvoiceTokenCache(profile, authData);
|
|
673
|
+
const authFile = saveAuthFile(authData, profile);
|
|
674
|
+
saveProfileConfig(base, base, profile, tenantKey);
|
|
675
|
+
return {
|
|
676
|
+
userId: authData.userId,
|
|
677
|
+
tenantKey,
|
|
678
|
+
tenantName: authData.tenantName || "",
|
|
679
|
+
department: authData.department || "",
|
|
680
|
+
subcompany: authData.subcompany || "",
|
|
681
|
+
position: authData.position || "",
|
|
682
|
+
eteamsId,
|
|
683
|
+
baseUrl: base,
|
|
684
|
+
passportUrl: base,
|
|
685
|
+
profile,
|
|
686
|
+
authFile,
|
|
687
|
+
agentType,
|
|
688
|
+
version
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function xiaoEUnifiedLoginUnavailableMessage(baseUrl, attempted) {
|
|
692
|
+
return [
|
|
693
|
+
attempted ? "XiaoE \u7EDF\u4E00\u767B\u5F55\u5DF2\u5C1D\u8BD5\uFF0C\u4F46\u672A\u83B7\u53D6\u5230\u53EF\u7528\u4F1A\u8BDD\u3002" : "\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684 XiaoE \u7EDF\u4E00\u767B\u5F55\u51ED\u636E\u6E90\uFF0C\u672C\u6B21\u672A\u5C1D\u8BD5\u7EDF\u4E00\u767B\u5F55\u3002",
|
|
694
|
+
"`auth xiaoe` \u4E0D\u4F1A\u964D\u7EA7\u5230\u6D4F\u89C8\u5668 OAuth\u3002",
|
|
695
|
+
baseUrl ? `\u5F53\u524D E10 baseUrl: ${baseUrl}\u3002` : "\u672A\u4ECE XiaoE \u51ED\u8BC1\u4E2D\u89E3\u6790\u5230 E10 baseUrl\u3002",
|
|
696
|
+
attempted ? "CLI \u5DF2\u4F9D\u6B21\u5C1D\u8BD5\u5E73\u53F0\u6CE8\u5165\u51ED\u8BC1\u7684 Node \u6362\u7968\u3001CLI \u5185\u7F6E xiaoe_env.py \u517C\u5BB9\u6362\u7968\u3001\u5BBF\u4E3B xiaoeUserData/tokens.py\u3001\u684C\u9762 XiaoE \u5BA2\u6237\u7AEF\u51ED\u8BC1\uFF08XiaoE/Robots/*/user_settings.json\uFF09\u548C WEAVER_XIAOE_* \u73AF\u5883\u6620\u5C04\u3002" : "CLI \u5DF2\u68C0\u67E5\u5E73\u53F0\u6CE8\u5165\u51ED\u8BC1\u3001\u5BBF\u4E3B xiaoeUserData/tokens.py\u3001\u684C\u9762 XiaoE \u5BA2\u6237\u7AEF\u51ED\u8BC1\uFF08XiaoE/Robots/*/user_settings.json\uFF09\u548C WEAVER_XIAOE_* \u73AF\u5883\u6620\u5C04\u3002",
|
|
697
|
+
"CLI \u4F1A\u4F18\u5148\u4F7F\u7528 XiaoE \u5BA2\u6237\u7AEF\u5185\u7F6E Python \u6267\u884C tokens.py\uFF1B\u81EA\u52A8\u53D1\u73B0\u4E0D\u53EF\u7528\u65F6\uFF0C\u53EF\u7528 WEAVER_XIAOE_TOKEN_SCRIPT_PYTHON \u6307\u5B9A\u517C\u5BB9\u89E3\u91CA\u5668\u3002"
|
|
698
|
+
].join(" ");
|
|
699
|
+
}
|
|
700
|
+
async function runXiaoELogin(ctx, options) {
|
|
701
|
+
const commandOpts = asOptions(options);
|
|
702
|
+
const globalOptions = ctx.getGlobalOptions();
|
|
703
|
+
const profileFromGlobal = globalOptions.profile;
|
|
704
|
+
const cfg = readProfileConfig(profileFromGlobal || getActiveProfile());
|
|
705
|
+
const explicitBaseUrl = optionString(commandOpts, "baseUrl") || globalOptions.baseUrl || "";
|
|
706
|
+
const preferredXiaoEBaseUrl = resolveXiaoEPreferredBaseUrl({
|
|
707
|
+
explicitBaseUrl,
|
|
708
|
+
profileBaseUrl: cfg.baseUrl,
|
|
709
|
+
env: ctx.env
|
|
710
|
+
});
|
|
711
|
+
const xiaoESources = inspectXiaoEUnifiedLoginSources({
|
|
712
|
+
env: ctx.env,
|
|
713
|
+
baseUrl: preferredXiaoEBaseUrl,
|
|
714
|
+
profileBaseUrl: cfg.baseUrl,
|
|
715
|
+
allowTokenScript: true
|
|
716
|
+
});
|
|
717
|
+
const useXiaoELogin = xiaoESources.any;
|
|
718
|
+
const baseUrl = preferredXiaoEBaseUrl;
|
|
719
|
+
const tenantKey = optionString(commandOpts, "tenantKey");
|
|
720
|
+
const diagnostics = {
|
|
721
|
+
attemptedSteps: [],
|
|
722
|
+
baseUrl,
|
|
723
|
+
clientType: xiaoESources.clientType,
|
|
724
|
+
platformCredentialDetected: xiaoESources.platformCredential
|
|
725
|
+
};
|
|
726
|
+
if (!useXiaoELogin) {
|
|
727
|
+
diagnostics.attemptedSteps.push(
|
|
728
|
+
"platform_token:missing",
|
|
729
|
+
"bundled_env_script:missing",
|
|
730
|
+
xiaoESources.clientType === "cloud" ? "token_script:disabled" : "token_script:missing",
|
|
731
|
+
xiaoESources.clientType === "cloud" ? "client_session:disabled" : "client_session:missing",
|
|
732
|
+
"environment_map:empty"
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
ctx.logger.info([
|
|
736
|
+
"XiaoE unified login preflight:",
|
|
737
|
+
"authMode=xiaoe",
|
|
738
|
+
`credentialSourceDetected=${String(xiaoESources.any)}`,
|
|
739
|
+
`attempted=${String(useXiaoELogin)}`,
|
|
740
|
+
`platformCredentialDetected=${String(xiaoESources.platformCredential)}`,
|
|
741
|
+
`tokenScriptDetected=${String(xiaoESources.tokenScript)}`,
|
|
742
|
+
`clientSettingsDetected=${String(xiaoESources.clientCredential)}`,
|
|
743
|
+
`environmentCredentialDetected=${String(xiaoESources.environmentCredential)}`,
|
|
744
|
+
`clientType=${xiaoESources.clientType || "(unset)"}`,
|
|
745
|
+
`baseUrl=${baseUrl || "(unresolved)"}`
|
|
746
|
+
].join(" "));
|
|
747
|
+
if (useXiaoELogin) {
|
|
748
|
+
try {
|
|
749
|
+
const xiaoEResult = await tryXiaoEUnifiedLogin2(
|
|
750
|
+
ctx,
|
|
751
|
+
baseUrl,
|
|
752
|
+
tenantKey,
|
|
753
|
+
profileFromGlobal,
|
|
754
|
+
true,
|
|
755
|
+
diagnostics
|
|
756
|
+
);
|
|
757
|
+
if (xiaoEResult) return xiaoEResult;
|
|
758
|
+
} catch (error2) {
|
|
759
|
+
await appendXiaoEAuthErrorLog({
|
|
760
|
+
source: "login",
|
|
761
|
+
profile: profileFromGlobal || getActiveProfile(),
|
|
762
|
+
baseUrl,
|
|
763
|
+
diagnostics,
|
|
764
|
+
error: error2,
|
|
765
|
+
secrets: xiaoEAuthLogSecrets(ctx.env)
|
|
766
|
+
}).catch((logError) => ctx.logger.debug(
|
|
767
|
+
`Could not persist XiaoE auth error log: ${logError instanceof Error ? logError.message : String(logError)}`
|
|
768
|
+
));
|
|
769
|
+
throw error2;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
ctx.logger.warn(xiaoEUnifiedLoginUnavailableMessage(baseUrl, useXiaoELogin));
|
|
773
|
+
const error = createXiaoEUnifiedLoginUnavailableError(baseUrl);
|
|
774
|
+
await appendXiaoEAuthErrorLog({
|
|
775
|
+
source: "login",
|
|
776
|
+
profile: profileFromGlobal || getActiveProfile(),
|
|
777
|
+
baseUrl,
|
|
778
|
+
diagnostics,
|
|
779
|
+
error,
|
|
780
|
+
secrets: xiaoEAuthLogSecrets(ctx.env)
|
|
781
|
+
}).catch((logError) => ctx.logger.debug(
|
|
782
|
+
`Could not persist XiaoE auth error log: ${logError instanceof Error ? logError.message : String(logError)}`
|
|
783
|
+
));
|
|
784
|
+
throw error;
|
|
785
|
+
}
|
|
786
|
+
async function runBrowserLogin(ctx, options) {
|
|
787
|
+
const commandOpts = asOptions(options);
|
|
788
|
+
const globalOptions = ctx.getGlobalOptions();
|
|
789
|
+
const profileFromGlobal = globalOptions.profile;
|
|
790
|
+
const cfg = readProfileConfig(profileFromGlobal || getActiveProfile());
|
|
791
|
+
const explicitBaseUrl = optionString(commandOpts, "baseUrl") || globalOptions.baseUrl || "";
|
|
792
|
+
const baseUrl = normalizeUrl(explicitBaseUrl || cfg.baseUrl || "");
|
|
793
|
+
const tenantKey = optionString(commandOpts, "tenantKey");
|
|
794
|
+
const portText = optionString(commandOpts, "port") || "19800";
|
|
795
|
+
const port = Number.parseInt(portText, 10);
|
|
796
|
+
const explicitAgentType = optionString(commandOpts, "agentType", "agent_type");
|
|
797
|
+
const agentType = resolveAgentType(commandOpts, ctx.env);
|
|
798
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
799
|
+
throw new CliError("Invalid --port value", 2 /* UsageError */);
|
|
800
|
+
}
|
|
801
|
+
if (!baseUrl) throw new CliError("Missing --base-url", 2 /* UsageError */);
|
|
802
|
+
const agentReturnTarget = resolveAgentReturnTarget(commandOpts, ctx.env, agentType);
|
|
803
|
+
const agentReturnToken = agentReturnTarget ? randomBytes(24).toString("base64url") : "";
|
|
804
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
805
|
+
const loginUrl = buildE10AuthorizeUrl(baseUrl, redirectUri, agentType);
|
|
806
|
+
ctx.logger.info("Open browser login: " + loginUrl);
|
|
807
|
+
ctx.logger.info("Waiting for E10 authorization callback...");
|
|
808
|
+
return await new Promise((resolve8, reject) => {
|
|
809
|
+
let settled = false;
|
|
810
|
+
let timer = null;
|
|
811
|
+
let pendingLoginResult = null;
|
|
812
|
+
let tlsCallbackDetected = false;
|
|
813
|
+
const finish = (error, result) => {
|
|
814
|
+
if (settled) return;
|
|
815
|
+
settled = true;
|
|
816
|
+
if (timer) clearTimeout(timer);
|
|
817
|
+
const complete = () => {
|
|
818
|
+
if (error) reject(error);
|
|
819
|
+
else resolve8(result);
|
|
820
|
+
};
|
|
821
|
+
try {
|
|
822
|
+
server.close(complete);
|
|
823
|
+
} catch {
|
|
824
|
+
complete();
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
const server = createServer(async (req, res) => {
|
|
828
|
+
const callbackUrl = new URL(req.url || "/", `http://127.0.0.1:${port}`);
|
|
829
|
+
if (callbackUrl.pathname === AGENT_RETURN_PATH) {
|
|
830
|
+
const requestToken = req.headers[AGENT_RETURN_TOKEN_HEADER];
|
|
831
|
+
if (req.method !== "POST" || !agentReturnTarget || !pendingLoginResult || requestToken !== agentReturnToken) {
|
|
832
|
+
res.writeHead(403, { "cache-control": "no-store", "content-type": "text/plain; charset=utf-8" });
|
|
833
|
+
res.end("Forbidden");
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const action = callbackUrl.searchParams.get("action");
|
|
837
|
+
if (action === "cancel") {
|
|
838
|
+
const result = pendingLoginResult;
|
|
839
|
+
res.writeHead(204, { "cache-control": "no-store" });
|
|
840
|
+
res.end();
|
|
841
|
+
finish(null, result);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (action !== "open") {
|
|
845
|
+
res.writeHead(400, { "cache-control": "no-store", "content-type": "text/plain; charset=utf-8" });
|
|
846
|
+
res.end("Invalid action");
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
try {
|
|
850
|
+
await openUrlWithSystemHandler(agentReturnTarget.url);
|
|
851
|
+
const result = pendingLoginResult;
|
|
852
|
+
res.writeHead(200, { "cache-control": "no-store", "content-type": "application/json; charset=utf-8" });
|
|
853
|
+
res.end('{"ok":true}');
|
|
854
|
+
finish(null, result);
|
|
855
|
+
} catch {
|
|
856
|
+
ctx.logger.warn(`Could not open ${agentReturnTarget.label} automatically.`);
|
|
857
|
+
res.writeHead(500, { "cache-control": "no-store", "content-type": "text/plain; charset=utf-8" });
|
|
858
|
+
res.end(`\u65E0\u6CD5\u6253\u5F00 ${agentReturnTarget.label}\uFF0C\u8BF7\u786E\u8BA4\u5E94\u7528\u5DF2\u5B89\u88C5\u3002`);
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (callbackUrl.pathname !== "/callback") {
|
|
863
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
864
|
+
res.end("Not found");
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
const error = callbackUrl.searchParams.get("error");
|
|
868
|
+
const code = callbackUrl.searchParams.get("code");
|
|
869
|
+
const redirectAgentType = explicitAgentType || callbackUrl.searchParams.get("agent_type") || callbackUrl.searchParams.get("agentType") || callbackUrl.searchParams.get("from") || agentType;
|
|
870
|
+
if (error) {
|
|
871
|
+
const message = callbackUrl.searchParams.get("error_description") || error;
|
|
872
|
+
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
873
|
+
res.end(`<h1>Authorization failed</h1><p>${esc(message)}</p>`);
|
|
874
|
+
finish(new CliError("OAuth authorization failed: " + message, 3 /* AuthError */));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (!code) {
|
|
878
|
+
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
879
|
+
res.end("<h1>Login failed</h1><p>Missing authorization code.</p>");
|
|
880
|
+
finish(new CliError("No authorization code received.", 3 /* AuthError */));
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
if (tlsCallbackDetected && timer) {
|
|
884
|
+
clearTimeout(timer);
|
|
885
|
+
timer = null;
|
|
886
|
+
}
|
|
887
|
+
try {
|
|
888
|
+
const result = await oidcExchange(code, redirectUri, baseUrl, tenantKey, redirectAgentType, profileFromGlobal);
|
|
889
|
+
const responseHeaders = {
|
|
890
|
+
"cache-control": "no-store",
|
|
891
|
+
"content-security-policy": agentReturnTarget ? `default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${agentReturnToken}'; connect-src 'self'` : `default-src 'none'; style-src 'unsafe-inline'`,
|
|
892
|
+
"content-type": "text/html; charset=utf-8",
|
|
893
|
+
"referrer-policy": "no-referrer",
|
|
894
|
+
"x-content-type-options": "nosniff",
|
|
895
|
+
"x-frame-options": "DENY"
|
|
896
|
+
};
|
|
897
|
+
res.writeHead(200, responseHeaders);
|
|
898
|
+
res.end(renderE10LoginSuccessPage(result.userId, agentReturnTarget, agentReturnToken));
|
|
899
|
+
if (!agentReturnTarget) {
|
|
900
|
+
finish(null, result);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
pendingLoginResult = result;
|
|
904
|
+
if (timer) clearTimeout(timer);
|
|
905
|
+
timer = setTimeout(() => finish(null, result), AGENT_RETURN_TIMEOUT_MS);
|
|
906
|
+
} catch (exchangeError) {
|
|
907
|
+
const message = exchangeError instanceof Error ? exchangeError.message : String(exchangeError);
|
|
908
|
+
res.writeHead(500, { "content-type": "text/html; charset=utf-8" });
|
|
909
|
+
res.end(`<h1>Login failed</h1><p>${esc(message)}</p>`);
|
|
910
|
+
finish(exchangeError);
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
server.on("error", (error) => {
|
|
914
|
+
const message = error.code === "EADDRINUSE" ? `Port ${port} is already in use. Retry with --port ${port + 1}.` : error.code === "EACCES" ? `Port ${port} is not accessible. Retry with another --port.` : error.message;
|
|
915
|
+
finish(new CliError(message, 2 /* UsageError */));
|
|
916
|
+
});
|
|
917
|
+
server.on("clientError", (error, socket) => {
|
|
918
|
+
if (isTlsClientHello(error)) {
|
|
919
|
+
if (!tlsCallbackDetected) {
|
|
920
|
+
tlsCallbackDetected = true;
|
|
921
|
+
ctx.logger.warn([
|
|
922
|
+
`OAuth callback protocol mismatch: the browser used HTTPS for 127.0.0.1:${port}, while the CLI listener is HTTP.`,
|
|
923
|
+
"\u5728 30 \u79D2\u5185\u628A\u6D4F\u89C8\u5668\u5F53\u524D\u5730\u5740\u5F00\u5934\u660E\u786E\u6539\u4E3A http://\uFF0C\u4FDD\u7559\u540E\u9762\u7684\u5B8C\u6574\u8DEF\u5F84\u548C\u53C2\u6570\uFF0C\u7136\u540E\u56DE\u8F66\u91CD\u8BD5\u3002",
|
|
924
|
+
"\u4E0D\u8981\u628A\u5730\u5740\u4E2D\u7684\u6388\u6743\u7801\u590D\u5236\u5230\u804A\u5929\u6216\u65E5\u5FD7\u3002"
|
|
925
|
+
].join(" "));
|
|
926
|
+
if (timer) clearTimeout(timer);
|
|
927
|
+
timer = setTimeout(() => {
|
|
928
|
+
finish(createOAuthCallbackProtocolMismatchError(port));
|
|
929
|
+
}, OAUTH_CALLBACK_PROTOCOL_MISMATCH_GRACE_MS);
|
|
930
|
+
}
|
|
931
|
+
socket.destroy();
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
socket.destroy();
|
|
935
|
+
});
|
|
936
|
+
server.listen(port, "127.0.0.1", () => {
|
|
937
|
+
ctx.logger.info("Local callback server: " + redirectUri);
|
|
938
|
+
openUrlInBrowser(loginUrl, ctx);
|
|
939
|
+
});
|
|
940
|
+
timer = setTimeout(() => {
|
|
941
|
+
finish(new CliError("Login timed out after 5 minutes.", 3 /* AuthError */));
|
|
942
|
+
}, 3e5);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
function sessionToCachedUser(session, profile) {
|
|
946
|
+
return {
|
|
947
|
+
employeeId: session.userId,
|
|
948
|
+
tenantKey: session.tenantKey,
|
|
949
|
+
tenantName: session.tenantName,
|
|
950
|
+
department: session.department,
|
|
951
|
+
subcompany: session.subcompany,
|
|
952
|
+
position: session.position,
|
|
953
|
+
version: session.version,
|
|
954
|
+
baseUrl: session.getBaseUrl(),
|
|
955
|
+
profile,
|
|
956
|
+
eteamsId: session.eteamsId,
|
|
957
|
+
agentType: session.agentType,
|
|
958
|
+
cookie: session.cookieHeader(),
|
|
959
|
+
userAgent: session.uaHeader()
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function formatWhoami(title, data) {
|
|
963
|
+
return [
|
|
964
|
+
title,
|
|
965
|
+
"employeeId: " + (data.employeeId || ""),
|
|
966
|
+
"tenantKey: " + (data.tenantKey || ""),
|
|
967
|
+
"tenantName: " + (data.tenantName || ""),
|
|
968
|
+
"department: " + (data.department || ""),
|
|
969
|
+
"subcompany: " + (data.subcompany || ""),
|
|
970
|
+
"position: " + (data.position || ""),
|
|
971
|
+
"version: " + (data.version || ""),
|
|
972
|
+
"baseUrl: " + (data.baseUrl || ""),
|
|
973
|
+
"profile: " + (data.profile || ""),
|
|
974
|
+
"ETEAMSID: " + (data.eteamsId || ""),
|
|
975
|
+
"cookie: " + (data.cookie || ""),
|
|
976
|
+
"User-Agent: " + (data.userAgent || "")
|
|
977
|
+
].join("\n");
|
|
978
|
+
}
|
|
979
|
+
function formatXiaoEAuthErrorLogEntry(entry) {
|
|
980
|
+
const lines = [
|
|
981
|
+
`[${entry.timestamp}] ${entry.source} id=${entry.id}`,
|
|
982
|
+
...entry.profile ? [`profile: ${entry.profile}`] : [],
|
|
983
|
+
...entry.baseUrl ? [`baseUrl: ${entry.baseUrl}`] : [],
|
|
984
|
+
...entry.clientType ? [`clientType: ${entry.clientType}`] : [],
|
|
985
|
+
...entry.attemptedSteps.length ? [`attemptedSteps: ${entry.attemptedSteps.join(", ")}`] : [],
|
|
986
|
+
...entry.lastError ? [`lastError: ${entry.lastError}`] : [],
|
|
987
|
+
...entry.resultSessionExpired ? ["resultSessionExpired: true"] : [],
|
|
988
|
+
...entry.error ? [`error: ${entry.error.type}/${entry.error.subtype}: ${entry.error.message}`] : []
|
|
989
|
+
];
|
|
990
|
+
for (const failure of entry.failures || []) {
|
|
991
|
+
lines.push([
|
|
992
|
+
`failure: ${failure.stage}/${failure.kind}`,
|
|
993
|
+
failure.causeCode ? `causeCode=${failure.causeCode}` : "",
|
|
994
|
+
failure.httpStatus === void 0 ? "" : `httpStatus=${failure.httpStatus}`,
|
|
995
|
+
failure.message
|
|
996
|
+
].filter(Boolean).join(" "));
|
|
997
|
+
}
|
|
998
|
+
return lines.join("\n");
|
|
999
|
+
}
|
|
1000
|
+
async function showXiaoEAuthErrorLogs(ctx, options) {
|
|
1001
|
+
const commandOpts = asOptions(options);
|
|
1002
|
+
let limit;
|
|
1003
|
+
try {
|
|
1004
|
+
limit = normalizeXiaoEAuthErrorLogLimit(commandOpts.limit);
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
throw new CliError(error instanceof Error ? error.message : String(error), 2 /* UsageError */);
|
|
1007
|
+
}
|
|
1008
|
+
const result = await readXiaoEAuthErrorLog(limit);
|
|
1009
|
+
const text = result.entries.length ? [
|
|
1010
|
+
`XiaoE auth error logs (${result.returned}/${result.total}, newest first)`,
|
|
1011
|
+
`logFile: ${result.path}`,
|
|
1012
|
+
...result.invalidLines ? [`invalidLines: ${result.invalidLines}`] : [],
|
|
1013
|
+
"",
|
|
1014
|
+
result.entries.map(formatXiaoEAuthErrorLogEntry).join("\n\n")
|
|
1015
|
+
].join("\n") : [
|
|
1016
|
+
"No XiaoE auth error logs.",
|
|
1017
|
+
`logFile: ${result.path}`,
|
|
1018
|
+
...result.invalidLines ? [`invalidLines: ${result.invalidLines}`] : []
|
|
1019
|
+
].join("\n");
|
|
1020
|
+
ctx.output.write(result, { text });
|
|
1021
|
+
}
|
|
1022
|
+
function registerXiaoEAuthLogCommands(auth, action) {
|
|
1023
|
+
const logs = auth.command("logs").description("View or clear persisted XiaoE authentication error logs").option("--limit <n>", "number of recent entries to show (1-500)", "50").action(action(showXiaoEAuthErrorLogs));
|
|
1024
|
+
logs.command("show").description("Show persisted XiaoE authentication error logs").option("--limit <n>", "number of recent entries to show (1-500)", "50").action(action(showXiaoEAuthErrorLogs));
|
|
1025
|
+
logs.command("path").description("Print the XiaoE authentication error log path").action(action((ctx) => {
|
|
1026
|
+
const path = getXiaoEAuthErrorLogPath();
|
|
1027
|
+
ctx.output.write({ path }, { text: path });
|
|
1028
|
+
}));
|
|
1029
|
+
logs.command("clear").description("Clear persisted XiaoE authentication error logs").action(action(async (ctx) => {
|
|
1030
|
+
const result = await clearXiaoEAuthErrorLog();
|
|
1031
|
+
const text = result.cleared ? `Cleared XiaoE auth error logs.
|
|
1032
|
+
logFile: ${result.path}` : `No XiaoE auth error logs to clear.
|
|
1033
|
+
logFile: ${result.path}`;
|
|
1034
|
+
ctx.output.write(result, { text });
|
|
1035
|
+
}));
|
|
1036
|
+
}
|
|
1037
|
+
function registerBrowserLoginCommand(auth, action, name, description, machine = false) {
|
|
1038
|
+
const command = auth.command(name).description(description).option("--base-url <url>", "E10 base URL").option("--tenant-key <key>", "Tenant key fallback after login").option("--port <n>", "Local OAuth callback port", "19800").option("--agent_type <value>", "Agent type sent to E10 authorize endpoint").option("--agent-type <value>", "Alias of --agent_type").option("--agent-return-url <url>", "Desktop application URL linked by the login success page");
|
|
1039
|
+
command.action(action(async (ctx, options) => {
|
|
1040
|
+
if (!machine) {
|
|
1041
|
+
const result = await runBrowserLogin(ctx, options);
|
|
1042
|
+
ctx.output.write(result, { text: formatLoginResult(result) });
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
ctx.logger.machineMode = true;
|
|
1046
|
+
try {
|
|
1047
|
+
const result = await runBrowserLogin(ctx, options);
|
|
1048
|
+
ctx.output.raw(JSON.stringify({
|
|
1049
|
+
status: "authorized",
|
|
1050
|
+
employeeId: result.userId || "",
|
|
1051
|
+
tenantKey: result.tenantKey || ""
|
|
1052
|
+
}));
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
ctx.output.raw(JSON.stringify({ status: "unauthorized" }));
|
|
1055
|
+
throw error;
|
|
1056
|
+
} finally {
|
|
1057
|
+
ctx.logger.machineMode = false;
|
|
1058
|
+
}
|
|
1059
|
+
}));
|
|
1060
|
+
}
|
|
1061
|
+
function registerXiaoELoginCommand(auth, action) {
|
|
1062
|
+
auth.command("xiaoe").description("Use XiaoE unified login without browser OAuth fallback").option("--base-url <url>", "E10 base URL").option("--tenant-key <key>", "Tenant key fallback after login").option("--port <n>", "Deprecated compatibility option; ignored by XiaoE unified login", "19800").option("--agent_type <value>", "Deprecated compatibility option; ignored by XiaoE unified login").option("--agent-type <value>", "Alias of --agent_type").option("--agent-return-url <url>", "Deprecated compatibility option; ignored by XiaoE unified login").action(action(async (ctx, options) => {
|
|
1063
|
+
const result = await runXiaoELogin(ctx, options);
|
|
1064
|
+
ctx.output.write(result, { text: formatLoginResult(result) });
|
|
1065
|
+
}));
|
|
1066
|
+
}
|
|
1067
|
+
function registerE10AuthCommands(parent, action) {
|
|
1068
|
+
const auth = parent.command("auth").description("E10 auth and profile management");
|
|
1069
|
+
auth.command("root").description("Print the E10 auth root directory").action(action((ctx) => {
|
|
1070
|
+
const root = getE10AuthRoot();
|
|
1071
|
+
ctx.output.write({ root }, { text: root });
|
|
1072
|
+
}));
|
|
1073
|
+
auth.command("status").description("Show E10 login status").option("--no-check", "skip remote session validity check").action(action(async (ctx, options) => {
|
|
1074
|
+
const opts = ctx.getGlobalOptions();
|
|
1075
|
+
const commandOpts = asOptions(options);
|
|
1076
|
+
const authFile = findAuthFile(ctx.cwd, opts);
|
|
1077
|
+
const session = authFile ? E10AuthSession.fromFile(authFile, opts.baseUrl, opts.passportUrl) : null;
|
|
1078
|
+
const valid = session && commandOpts.check !== false ? session.isExpired() ? false : await session.checkValid() : void 0;
|
|
1079
|
+
const status = session ? {
|
|
1080
|
+
loggedIn: true,
|
|
1081
|
+
userId: session.userId,
|
|
1082
|
+
tenantKey: session.tenantKey,
|
|
1083
|
+
tenantName: session.tenantName,
|
|
1084
|
+
department: session.department,
|
|
1085
|
+
subcompany: session.subcompany,
|
|
1086
|
+
position: session.position,
|
|
1087
|
+
version: session.version,
|
|
1088
|
+
agentType: session.agentType,
|
|
1089
|
+
baseUrl: session.getBaseUrl(),
|
|
1090
|
+
passportUrl: session.getPassportUrl(),
|
|
1091
|
+
profile: opts.profile || getActiveProfile(),
|
|
1092
|
+
authFile,
|
|
1093
|
+
hasEteamsId: Boolean(session.eteamsId),
|
|
1094
|
+
...session.loginSource ? { loginSource: session.loginSource } : {},
|
|
1095
|
+
...session.credentialSourceStep ? { credentialSourceStep: session.credentialSourceStep } : {},
|
|
1096
|
+
...session.credentialSourceType ? { credentialSourceType: session.credentialSourceType } : {},
|
|
1097
|
+
...session.credentialSourceLabel ? { credentialSourceLabel: session.credentialSourceLabel } : {},
|
|
1098
|
+
...session.expiryTimes ? { expiryTimes: session.expiryTimes } : {},
|
|
1099
|
+
valid
|
|
1100
|
+
} : {
|
|
1101
|
+
loggedIn: false,
|
|
1102
|
+
profile: opts.profile || getActiveProfile(),
|
|
1103
|
+
authFile
|
|
1104
|
+
};
|
|
1105
|
+
ctx.output.write(status, { text: formatAuthStatus(status) });
|
|
1106
|
+
}));
|
|
1107
|
+
auth.command("show").description('Platform contract: print login status as {"status":"authorized"|"unauthorized"}').option("--status", "print only the strict JSON status object").option("--no-check", "skip remote session validity check").action(action(async (ctx, options) => {
|
|
1108
|
+
const opts = ctx.getGlobalOptions();
|
|
1109
|
+
const commandOpts = asOptions(options);
|
|
1110
|
+
const authFile = findAuthFile(ctx.cwd, opts);
|
|
1111
|
+
const session = authFile ? E10AuthSession.fromFile(authFile, opts.baseUrl, opts.passportUrl) : null;
|
|
1112
|
+
const profile2 = opts.profile || getActiveProfile();
|
|
1113
|
+
const expired = session ? session.isExpired() : false;
|
|
1114
|
+
const probe = session && !expired && commandOpts.check !== false ? await session.probeValid() : void 0;
|
|
1115
|
+
const authorized = Boolean(session) && !expired && probe !== false;
|
|
1116
|
+
if (commandOpts.status === true) {
|
|
1117
|
+
ctx.output.raw(JSON.stringify({ status: authorized ? "authorized" : "unauthorized" }));
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
ctx.output.write({
|
|
1121
|
+
status: authorized ? "authorized" : "unauthorized",
|
|
1122
|
+
loggedIn: Boolean(session),
|
|
1123
|
+
expired,
|
|
1124
|
+
reachable: probe,
|
|
1125
|
+
profile: profile2,
|
|
1126
|
+
baseUrl: session ? session.getBaseUrl() : ""
|
|
1127
|
+
}, { json: true });
|
|
1128
|
+
}));
|
|
1129
|
+
auth.command("set").description("Save E10 auth from an ETEAMSID cookie").requiredOption("--eteamsid <id>", "ETEAMSID cookie value").option("--base-url <url>", "E10 base URL").option("--passport-url <url>", "E10 passport URL; defaults to base URL").option("--tenant-key <key>", "Tenant key fallback").option("--agent_type <value>", "Agent type used in cookie and User-Agent").option("--agent-type <value>", "Alias of --agent_type").action(action(async (ctx, options) => {
|
|
1130
|
+
const globalOptions = ctx.getGlobalOptions();
|
|
1131
|
+
const commandOpts = asOptions(options);
|
|
1132
|
+
const profileFromGlobal = globalOptions.profile;
|
|
1133
|
+
const cfg = readProfileConfig(profileFromGlobal || getActiveProfile());
|
|
1134
|
+
const baseUrl = normalizeUrl(optionString(commandOpts, "baseUrl") || globalOptions.baseUrl || cfg.baseUrl || "");
|
|
1135
|
+
const passportUrl = normalizeUrl(optionString(commandOpts, "passportUrl") || globalOptions.passportUrl || cfg.passportUrl || baseUrl);
|
|
1136
|
+
const tenantKeyFallback = optionString(commandOpts, "tenantKey");
|
|
1137
|
+
const agentType = resolveAgentType(commandOpts, ctx.env);
|
|
1138
|
+
const eteamsId = optionString(commandOpts, "eteamsid");
|
|
1139
|
+
if (!baseUrl) throw new CliError("Missing --base-url", 2 /* UsageError */);
|
|
1140
|
+
if (!eteamsId) throw new CliError("Missing --eteamsid", 2 /* UsageError */);
|
|
1141
|
+
ctx.logger.info("Resolving E10 user information...");
|
|
1142
|
+
const result = await saveEteamsIdLogin({
|
|
1143
|
+
baseUrl,
|
|
1144
|
+
passportUrl,
|
|
1145
|
+
eteamsId,
|
|
1146
|
+
tenantKeyFallback,
|
|
1147
|
+
agentType,
|
|
1148
|
+
profileHint: profileFromGlobal
|
|
1149
|
+
});
|
|
1150
|
+
ctx.output.write(result, { text: formatLoginResult(result) });
|
|
1151
|
+
}));
|
|
1152
|
+
registerBrowserLoginCommand(auth, action, "login", "Open browser OAuth2.0 login and save E10 auth");
|
|
1153
|
+
registerBrowserLoginCommand(auth, action, "oidc", "Alias of auth login for OAuth2.0 browser login");
|
|
1154
|
+
registerBrowserLoginCommand(
|
|
1155
|
+
auth,
|
|
1156
|
+
action,
|
|
1157
|
+
"init",
|
|
1158
|
+
'Platform contract alias of auth login: stdout is strict JSON {"status":"authorized"}',
|
|
1159
|
+
true
|
|
1160
|
+
);
|
|
1161
|
+
registerXiaoELoginCommand(auth, action);
|
|
1162
|
+
registerXiaoEAuthLogCommands(auth, action);
|
|
1163
|
+
auth.command("logout").alias("clear").description("Clear saved E10 auth for the current or selected profile").action(action(async (ctx) => {
|
|
1164
|
+
const opts = ctx.getGlobalOptions();
|
|
1165
|
+
const profile2 = opts.profile || getActiveProfile();
|
|
1166
|
+
const authFile = getProfileAuthPath(profile2);
|
|
1167
|
+
const session = E10AuthSession.fromFile(authFile, opts.baseUrl, opts.passportUrl);
|
|
1168
|
+
if (session) {
|
|
1169
|
+
await clearInvoiceTokenCache(profile2, {
|
|
1170
|
+
baseUrl: session.getBaseUrl(),
|
|
1171
|
+
userId: session.userId
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
const result = clearProfileAuthFile(profile2);
|
|
1175
|
+
const text = result.cleared ? "Cleared E10 auth.\nprofile: " + result.profile + "\nauthFile: " + result.authFile : "No E10 auth to clear.\nprofile: " + result.profile + "\nauthFile: " + result.authFile;
|
|
1176
|
+
ctx.output.write(result, { text });
|
|
1177
|
+
}));
|
|
1178
|
+
auth.command("whoami").description("Show cached or live E10 user information").option("--json", "print output as JSON").option("--live", "call teamsCheck for live user information").action(action(async (ctx, options) => {
|
|
1179
|
+
const opts = ctx.getGlobalOptions();
|
|
1180
|
+
const commandOpts = asOptions(options);
|
|
1181
|
+
const json = commandOpts.json === true;
|
|
1182
|
+
const live = commandOpts.live === true;
|
|
1183
|
+
const authFile = findAuthFile(ctx.cwd, opts);
|
|
1184
|
+
const session = authFile ? E10AuthSession.fromFile(authFile, opts.baseUrl, opts.passportUrl) : null;
|
|
1185
|
+
if (!session || !session.eteamsId) throw new CliError("E10 auth not found.", 3 /* AuthError */);
|
|
1186
|
+
const profile2 = opts.profile || getActiveProfile();
|
|
1187
|
+
const cached = sessionToCachedUser(session, profile2);
|
|
1188
|
+
if (!live) {
|
|
1189
|
+
ctx.output.write(cached, {
|
|
1190
|
+
json,
|
|
1191
|
+
text: formatWhoami("Current E10 user (cached)", cached)
|
|
1192
|
+
});
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const base = session.getBaseUrl();
|
|
1196
|
+
const resp = await fetch(`${base}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(base)}`, {
|
|
1197
|
+
method: "POST",
|
|
1198
|
+
headers: {
|
|
1199
|
+
accept: "application/json",
|
|
1200
|
+
"content-type": "application/json",
|
|
1201
|
+
eteamsid: session.eteamsId,
|
|
1202
|
+
cookie: session.cookieHeader(),
|
|
1203
|
+
"user-agent": session.uaHeader(),
|
|
1204
|
+
origin: base
|
|
1205
|
+
},
|
|
1206
|
+
redirect: "manual"
|
|
1207
|
+
});
|
|
1208
|
+
const raw = await resp.json().catch(() => ({}));
|
|
1209
|
+
if (json || ctx.getGlobalOptions().json) {
|
|
1210
|
+
ctx.output.write(raw, { json: true });
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const userInfo = readTeamsCheckUserInfo(resp, raw);
|
|
1214
|
+
const liveData = {
|
|
1215
|
+
...cached,
|
|
1216
|
+
employeeId: userInfo.userId || cached.employeeId,
|
|
1217
|
+
tenantKey: userInfo.tenantKey || cached.tenantKey,
|
|
1218
|
+
tenantName: userInfo.tenantName || cached.tenantName,
|
|
1219
|
+
department: userInfo.department || cached.department,
|
|
1220
|
+
subcompany: userInfo.subcompany || cached.subcompany,
|
|
1221
|
+
position: userInfo.position || cached.position
|
|
1222
|
+
};
|
|
1223
|
+
ctx.output.write(liveData, { text: formatWhoami("Current E10 user (live)", liveData) });
|
|
1224
|
+
}));
|
|
1225
|
+
auth.command("export-weaver-env").description("Print WEAVER_* shell exports for the active E10 auth").option("--shell <shell>", "Shell syntax: auto, posix, or powershell", "auto").action(action(async (ctx, options) => {
|
|
1226
|
+
const commandOpts = asOptions(options);
|
|
1227
|
+
const session = await E10AuthSession.fromOptions(ctx.getGlobalOptions(), ctx.cwd);
|
|
1228
|
+
if (!session) throw new CliError("E10 auth not found.", 3 /* AuthError */);
|
|
1229
|
+
const value = {
|
|
1230
|
+
WEAVER_BASE_URL: session.getBaseUrl(),
|
|
1231
|
+
WEAVER_ETEAMSID: session.eteamsId,
|
|
1232
|
+
WEAVER_COOKIE: session.cookieHeader(),
|
|
1233
|
+
WEAVER_USER_AGENT: session.uaHeader(),
|
|
1234
|
+
WEAVER_AGENT_TYPE: session.agentType
|
|
1235
|
+
};
|
|
1236
|
+
if (ctx.getGlobalOptions().json) {
|
|
1237
|
+
ctx.output.write(value);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const shell = optionString(commandOpts, "shell") || "auto";
|
|
1241
|
+
ctx.output.raw(formatWeaverEnvExports(value, shell));
|
|
1242
|
+
}));
|
|
1243
|
+
const profile = auth.command("profile").description("Manage E10 profiles");
|
|
1244
|
+
profile.command("list").description("List E10 profiles").action(action((ctx) => {
|
|
1245
|
+
const active = getActiveProfile();
|
|
1246
|
+
const profiles = listProfiles().map((name) => ({
|
|
1247
|
+
name,
|
|
1248
|
+
active: name === active,
|
|
1249
|
+
dir: getProfileDir(name)
|
|
1250
|
+
}));
|
|
1251
|
+
ctx.output.write({ active, profiles });
|
|
1252
|
+
}));
|
|
1253
|
+
profile.command("use").description("Switch active E10 profile; creates it if missing").argument("<name>", "profile name").action(action((ctx, name) => {
|
|
1254
|
+
if (typeof name !== "string") throw new CliError("profile name must be a string", 2 /* UsageError */);
|
|
1255
|
+
switchActiveProfile(name);
|
|
1256
|
+
const result = {
|
|
1257
|
+
active: name,
|
|
1258
|
+
dir: getProfileDir(name),
|
|
1259
|
+
authFile: getProfileAuthPath(name)
|
|
1260
|
+
};
|
|
1261
|
+
ctx.output.write(result, { text: "Active E10 profile: " + name + "\ndir: " + result.dir });
|
|
1262
|
+
}));
|
|
1263
|
+
profile.command("current").description("Print active E10 profile").action(action((ctx) => {
|
|
1264
|
+
const active = getActiveProfile();
|
|
1265
|
+
ctx.output.write({ active, dir: getProfileDir(active) }, { text: active });
|
|
1266
|
+
}));
|
|
1267
|
+
return auth;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// src/cmd/e10/index.ts
|
|
1271
|
+
var e10Command = {
|
|
1272
|
+
name: "e10",
|
|
1273
|
+
description: "E10 platform adapter: auth, profiles, and shared API client",
|
|
1274
|
+
category: "platform",
|
|
1275
|
+
register({ program, ctx, action }) {
|
|
1276
|
+
registerE10AuthCommands(program, action);
|
|
1277
|
+
const e10 = program.command("e10").description("E10 platform commands");
|
|
1278
|
+
registerE10AuthCommands(e10, action);
|
|
1279
|
+
ctx.hooks.register({
|
|
1280
|
+
beforeRun(runCtx) {
|
|
1281
|
+
const authFile = findAuthFile(runCtx.cwd);
|
|
1282
|
+
const session = authFile ? E10AuthSession.fromFile(authFile) : null;
|
|
1283
|
+
if (!session) return;
|
|
1284
|
+
runCtx.env.WEAVER_BASE_URL ||= session.getBaseUrl();
|
|
1285
|
+
runCtx.env.WEAVER_ETEAMSID ||= session.eteamsId;
|
|
1286
|
+
runCtx.env.WEAVER_COOKIE ||= session.cookieHeader();
|
|
1287
|
+
runCtx.env.WEAVER_USER_AGENT ||= session.uaHeader();
|
|
1288
|
+
runCtx.env.WEAVER_AGENT_TYPE ||= session.agentType;
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
// src/cmd/identity/index.ts
|
|
1295
|
+
function safeIdentity(session, profile) {
|
|
1296
|
+
const pick = (value) => typeof value === "string" ? value : "";
|
|
1297
|
+
return {
|
|
1298
|
+
employeeId: pick(session.userId),
|
|
1299
|
+
tenantKey: pick(session.tenantKey),
|
|
1300
|
+
tenantName: pick(session.tenantName),
|
|
1301
|
+
department: pick(session.department),
|
|
1302
|
+
subcompany: pick(session.subcompany),
|
|
1303
|
+
position: pick(session.position),
|
|
1304
|
+
version: pick(session.version),
|
|
1305
|
+
baseUrl: session.getBaseUrl(),
|
|
1306
|
+
profile,
|
|
1307
|
+
agentType: pick(session.agentType)
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
var identityCommand = {
|
|
1311
|
+
name: "identity",
|
|
1312
|
+
description: "Print the signed-in identity in a connector-safe JSON form",
|
|
1313
|
+
category: "platform",
|
|
1314
|
+
register({ program, action }) {
|
|
1315
|
+
const identity = program.command("identity").description("Connector contract: report who the current credential belongs to");
|
|
1316
|
+
identity.command("whoami").description("Print the signed-in E10 identity as JSON without any credential material").action(action((ctx) => {
|
|
1317
|
+
const opts = ctx.getGlobalOptions();
|
|
1318
|
+
const authFile = findAuthFile(ctx.cwd, opts);
|
|
1319
|
+
const session = authFile ? E10AuthSession.fromFile(authFile, opts.baseUrl, opts.passportUrl) : null;
|
|
1320
|
+
if (!session) throw new CliError("E10 auth not found.", 3 /* AuthError */);
|
|
1321
|
+
const profile = opts.profile || getActiveProfile();
|
|
1322
|
+
ctx.output.raw(JSON.stringify(safeIdentity(session, profile)));
|
|
1323
|
+
}));
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
// src/cmd/refs/index.ts
|
|
1328
|
+
function parseDuration(value) {
|
|
1329
|
+
const raw = value.trim().toLowerCase();
|
|
1330
|
+
const matched = /^(\d+)\s*([dhms])?$/.exec(raw);
|
|
1331
|
+
if (!matched) {
|
|
1332
|
+
throw new CliError(`\u65E0\u6CD5\u89E3\u6790\u65F6\u957F\uFF1A${value}\uFF08\u793A\u4F8B\uFF1A7d\u300124h\u300130m\uFF09`, 2 /* UsageError */);
|
|
1333
|
+
}
|
|
1334
|
+
const amount = Number(matched[1]);
|
|
1335
|
+
const unit = matched[2] ?? "ms";
|
|
1336
|
+
const factor = unit === "d" ? 864e5 : unit === "h" ? 36e5 : unit === "m" ? 6e4 : 1;
|
|
1337
|
+
return amount * factor;
|
|
1338
|
+
}
|
|
1339
|
+
function asOptions2(value) {
|
|
1340
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1341
|
+
}
|
|
1342
|
+
var refsCommand = {
|
|
1343
|
+
name: "refs",
|
|
1344
|
+
description: "Inspect and prune persisted large results",
|
|
1345
|
+
category: "system",
|
|
1346
|
+
register({ program, action }) {
|
|
1347
|
+
const refs = program.command("refs").description("Manage persisted large results");
|
|
1348
|
+
refs.command("path").description("Print the directory holding persisted results").action(action((ctx) => {
|
|
1349
|
+
const path = refsDir(ctx);
|
|
1350
|
+
ctx.output.write({ path }, { text: path });
|
|
1351
|
+
}));
|
|
1352
|
+
refs.command("list").description("List persisted results, newest first").action(action((ctx) => {
|
|
1353
|
+
const now = Date.now();
|
|
1354
|
+
const items = listRefs(ctx).filter((row) => Date.parse(row.expiresAt) > now).map((row) => ({
|
|
1355
|
+
refId: row.refId,
|
|
1356
|
+
operation: row.operation ?? null,
|
|
1357
|
+
bytes: row.bytes,
|
|
1358
|
+
createdAt: row.createdAt,
|
|
1359
|
+
expiresAt: row.expiresAt
|
|
1360
|
+
}));
|
|
1361
|
+
ctx.output.write({ count: items.length, items });
|
|
1362
|
+
}));
|
|
1363
|
+
refs.command("show").description("Print one persisted result in full").argument("<refId>", "reference id").action(action((ctx, refId) => {
|
|
1364
|
+
const id = String(refId ?? "").trim();
|
|
1365
|
+
if (!id) throw new CliError("refId \u4E0D\u80FD\u4E3A\u7A7A", 2 /* UsageError */);
|
|
1366
|
+
const record = readRef(ctx, id);
|
|
1367
|
+
const configuredLimit = resultThresholdBytes(ctx);
|
|
1368
|
+
const displayLimit = configuredLimit > 0 ? configuredLimit : DEFAULT_RESULT_THRESHOLD_BYTES;
|
|
1369
|
+
if (bytesOf(record.envelope) > displayLimit) {
|
|
1370
|
+
throw new CliError(
|
|
1371
|
+
`\u7ED3\u679C\u592A\u5927\uFF0C\u4E0D\u80FD\u5B89\u5168\u5730\u4E00\u6B21\u663E\u793A\u3002\u8BF7\u7528 refs get ${id} <json-path> \u8BFB\u53D6\u9700\u8981\u7684\u5B57\u6BB5\u3002`,
|
|
1372
|
+
2 /* UsageError */
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
ctx.output.write(record.envelope, { json: true, persist: false });
|
|
1376
|
+
}));
|
|
1377
|
+
refs.command("get").description("Print one value from a persisted result by JSON path").argument("<refId>", "reference id").argument("<path>", "JSON path, e.g. data.items[0].fid").action(action((ctx, refId, path) => {
|
|
1378
|
+
const id = String(refId ?? "").trim();
|
|
1379
|
+
const jsonPath = String(path ?? "").trim();
|
|
1380
|
+
if (!id || !jsonPath) throw new CliError("refId \u548C JSON \u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A", 2 /* UsageError */);
|
|
1381
|
+
const record = readRef(ctx, id);
|
|
1382
|
+
ctx.output.write(pickValue(record.envelope, jsonPath));
|
|
1383
|
+
}));
|
|
1384
|
+
refs.command("prune").description("Prune persisted results").option("--older-than <duration>", "remove results created before this age, e.g. 7d").option("--all", "remove every persisted result").action(action((ctx, options) => {
|
|
1385
|
+
const opts = asOptions2(options);
|
|
1386
|
+
if (opts.all === true) {
|
|
1387
|
+
const result2 = pruneRefs(ctx, { maxCount: 0, maxBytes: 0 });
|
|
1388
|
+
ctx.output.write({
|
|
1389
|
+
removed: result2.removed,
|
|
1390
|
+
kept: result2.kept,
|
|
1391
|
+
bytes: result2.bytes,
|
|
1392
|
+
cleanupIncomplete: result2.cleanupIncomplete
|
|
1393
|
+
});
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const olderThanMs = opts.olderThan ? parseDuration(String(opts.olderThan)) : void 0;
|
|
1397
|
+
const result = pruneRefs(ctx, {
|
|
1398
|
+
maxCount: refMaxCount(ctx),
|
|
1399
|
+
maxBytes: refMaxBytes(ctx),
|
|
1400
|
+
...olderThanMs === void 0 ? {} : { olderThanMs }
|
|
1401
|
+
});
|
|
1402
|
+
ctx.output.write({
|
|
1403
|
+
removed: result.removed,
|
|
1404
|
+
kept: result.kept,
|
|
1405
|
+
bytes: result.bytes,
|
|
1406
|
+
cleanupIncomplete: result.cleanupIncomplete
|
|
1407
|
+
});
|
|
1408
|
+
}));
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
// src/cmd/run/index.ts
|
|
1413
|
+
import { resolve as resolve2 } from "node:path";
|
|
1414
|
+
|
|
1415
|
+
// src/services/command-runner.ts
|
|
1416
|
+
import { spawn } from "node:child_process";
|
|
1417
|
+
var CommandRunner = class {
|
|
1418
|
+
constructor(ctx) {
|
|
1419
|
+
this.ctx = ctx;
|
|
1420
|
+
}
|
|
1421
|
+
ctx;
|
|
1422
|
+
async run(command, args = [], options = {}) {
|
|
1423
|
+
const runContext = {
|
|
1424
|
+
cwd: options.cwd || this.ctx.cwd,
|
|
1425
|
+
command,
|
|
1426
|
+
args,
|
|
1427
|
+
env: options.env || this.ctx.env,
|
|
1428
|
+
shell: options.shell
|
|
1429
|
+
};
|
|
1430
|
+
await this.ctx.hooks.beforeRun(runContext);
|
|
1431
|
+
const result = await new Promise((resolve8) => {
|
|
1432
|
+
const child = spawn(command, args, {
|
|
1433
|
+
cwd: runContext.cwd,
|
|
1434
|
+
env: runContext.env,
|
|
1435
|
+
shell: runContext.shell,
|
|
1436
|
+
stdio: options.stdio === "inherit" ? "inherit" : "pipe"
|
|
1437
|
+
});
|
|
1438
|
+
const stdout = [];
|
|
1439
|
+
const stderr = [];
|
|
1440
|
+
if (child.stdout) child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
1441
|
+
if (child.stderr) child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
1442
|
+
child.on("close", (code, signal) => {
|
|
1443
|
+
resolve8({
|
|
1444
|
+
command,
|
|
1445
|
+
args,
|
|
1446
|
+
cwd: runContext.cwd,
|
|
1447
|
+
code,
|
|
1448
|
+
signal,
|
|
1449
|
+
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
1450
|
+
stderr: Buffer.concat(stderr).toString("utf-8")
|
|
1451
|
+
});
|
|
1452
|
+
});
|
|
1453
|
+
});
|
|
1454
|
+
await this.ctx.hooks.afterRun(runContext, result);
|
|
1455
|
+
return result;
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
// src/cmd/run/index.ts
|
|
1460
|
+
var runCommand = {
|
|
1461
|
+
name: "run",
|
|
1462
|
+
description: "Generic external command runner with hooks",
|
|
1463
|
+
category: "system",
|
|
1464
|
+
register({ program, action }) {
|
|
1465
|
+
program.command("run").description("Run an external command through the shared command runner").argument("<command>", "command to run").argument("[args...]", "command arguments").option("--cwd <dir>", "working directory").option("--shell", "run through the platform shell").option("--inherit", "inherit stdio instead of capturing output").allowUnknownOption(true).action(action(async (ctx, command, args, options) => {
|
|
1466
|
+
if (typeof command !== "string") {
|
|
1467
|
+
throw new CliError("command must be a string", 2 /* UsageError */);
|
|
1468
|
+
}
|
|
1469
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
1470
|
+
const result = await new CommandRunner(ctx).run(command, Array.isArray(args) ? args.map(String) : [], {
|
|
1471
|
+
cwd: opts.cwd ? resolve2(opts.cwd) : void 0,
|
|
1472
|
+
shell: opts.shell,
|
|
1473
|
+
stdio: opts.inherit ? "inherit" : "pipe"
|
|
1474
|
+
});
|
|
1475
|
+
if (!opts.inherit && result.stdout) process.stdout.write(result.stdout);
|
|
1476
|
+
if (!opts.inherit && result.stderr) process.stderr.write(result.stderr);
|
|
1477
|
+
if (result.code && result.code !== 0) {
|
|
1478
|
+
throw new CliError("command exited with code " + result.code, result.code);
|
|
1479
|
+
}
|
|
1480
|
+
}));
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
// src/cmd/setup/index.ts
|
|
1485
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1486
|
+
|
|
1487
|
+
// src/internal/skills/install.ts
|
|
1488
|
+
import { cpSync as cpSync2, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync, writeFileSync } from "node:fs";
|
|
1489
|
+
import { homedir } from "node:os";
|
|
1490
|
+
import { basename, posix, relative, resolve as resolve3, sep, win32 } from "node:path";
|
|
1491
|
+
var SKILL_PREFIX = "weaver-e10-";
|
|
1492
|
+
var LEGACY_SKILL_PREFIX = "weaver-work-cli-";
|
|
1493
|
+
var SHARED_SKILL_NAME = "weaver-e10-shared";
|
|
1494
|
+
function envValue(env, name) {
|
|
1495
|
+
const direct = env[name];
|
|
1496
|
+
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
1497
|
+
const expected = name.toUpperCase();
|
|
1498
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1499
|
+
if (key.toUpperCase() === expected && typeof value === "string" && value.trim()) return value.trim();
|
|
1500
|
+
}
|
|
1501
|
+
return "";
|
|
1502
|
+
}
|
|
1503
|
+
function hasEnvFamily(env, prefix, ignoredNames = []) {
|
|
1504
|
+
const expected = `${prefix.toUpperCase()}_`;
|
|
1505
|
+
const ignored = new Set(ignoredNames.map((name) => name.toUpperCase()));
|
|
1506
|
+
return Object.entries(env).some(([key, value]) => key.toUpperCase().startsWith(expected) && !ignored.has(key.toUpperCase()) && typeof value === "string" && Boolean(value.trim()));
|
|
1507
|
+
}
|
|
1508
|
+
function normalizedAgentType(value) {
|
|
1509
|
+
const normalized = value.trim().toLowerCase();
|
|
1510
|
+
if (normalized === "codex-cli") return "codex";
|
|
1511
|
+
if (normalized === "work-buddy") return "workbuddy";
|
|
1512
|
+
return normalized || "unknown";
|
|
1513
|
+
}
|
|
1514
|
+
function isKnownSkillsHost(host) {
|
|
1515
|
+
return host === "codex" || host === "workbuddy";
|
|
1516
|
+
}
|
|
1517
|
+
function resolveSkillsTargetPath(value, options = {}) {
|
|
1518
|
+
const trimmed = value.trim();
|
|
1519
|
+
if (!trimmed) throw new Error("skills target path must not be empty");
|
|
1520
|
+
const platform = options.platform ?? process.platform;
|
|
1521
|
+
const pathApi = platform === "win32" ? win32 : posix;
|
|
1522
|
+
const userHome = options.homeDir ?? homedir();
|
|
1523
|
+
if (trimmed === "~") return pathApi.resolve(userHome);
|
|
1524
|
+
if (/^~[\\/]/u.test(trimmed)) {
|
|
1525
|
+
const segments = trimmed.slice(2).split(/[\\/]+/u).filter(Boolean);
|
|
1526
|
+
return pathApi.resolve(userHome, ...segments);
|
|
1527
|
+
}
|
|
1528
|
+
return pathApi.resolve(trimmed);
|
|
1529
|
+
}
|
|
1530
|
+
function hostSkillsRoot(host, env, options) {
|
|
1531
|
+
const platform = options.platform ?? process.platform;
|
|
1532
|
+
const pathApi = platform === "win32" ? win32 : posix;
|
|
1533
|
+
const userHome = options.homeDir ?? homedir();
|
|
1534
|
+
const envName = host === "codex" ? "CODEX_HOME" : "WORKBUDDY_HOME";
|
|
1535
|
+
const configuredHome = envValue(env, envName);
|
|
1536
|
+
const hostHome = configuredHome ? resolveSkillsTargetPath(configuredHome, options) : pathApi.resolve(userHome, host === "codex" ? ".codex" : ".workbuddy");
|
|
1537
|
+
return pathApi.resolve(hostHome, "skills");
|
|
1538
|
+
}
|
|
1539
|
+
function detectSkillsHost(env = process.env) {
|
|
1540
|
+
const explicitAgent = envValue(env, "WEAVER_AGENT_TYPE");
|
|
1541
|
+
if (explicitAgent) return normalizedAgentType(explicitAgent);
|
|
1542
|
+
const hasCodexRuntime = hasEnvFamily(env, "CODEX", ["CODEX_HOME"]);
|
|
1543
|
+
const hasWorkBuddyRuntime = hasEnvFamily(env, "WORKBUDDY", ["WORKBUDDY_HOME"]);
|
|
1544
|
+
if (hasCodexRuntime !== hasWorkBuddyRuntime) return hasCodexRuntime ? "codex" : "workbuddy";
|
|
1545
|
+
const hasCodexHome = Boolean(envValue(env, "CODEX_HOME"));
|
|
1546
|
+
const hasWorkBuddyHome = Boolean(envValue(env, "WORKBUDDY_HOME"));
|
|
1547
|
+
if (hasCodexHome !== hasWorkBuddyHome) return hasCodexHome ? "codex" : "workbuddy";
|
|
1548
|
+
return "unknown";
|
|
1549
|
+
}
|
|
1550
|
+
function resolveSkillsRoot(env = process.env, options = {}) {
|
|
1551
|
+
const configuredRoot = envValue(env, "WEAVER_SKILLS_ROOT");
|
|
1552
|
+
if (configuredRoot) {
|
|
1553
|
+
const explicitAgent = envValue(env, "WEAVER_AGENT_TYPE");
|
|
1554
|
+
const detectedHost = explicitAgent ? normalizedAgentType(explicitAgent) : detectSkillsHost(env);
|
|
1555
|
+
return {
|
|
1556
|
+
root: resolveSkillsTargetPath(configuredRoot, options),
|
|
1557
|
+
host: detectedHost === "unknown" ? "explicit" : detectedHost,
|
|
1558
|
+
source: "explicit-env",
|
|
1559
|
+
environmentVariable: "WEAVER_SKILLS_ROOT"
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
const host = detectSkillsHost(env);
|
|
1563
|
+
if (isKnownSkillsHost(host)) {
|
|
1564
|
+
const environmentVariable = envValue(env, host === "codex" ? "CODEX_HOME" : "WORKBUDDY_HOME") ? host === "codex" ? "CODEX_HOME" : "WORKBUDDY_HOME" : void 0;
|
|
1565
|
+
return {
|
|
1566
|
+
root: hostSkillsRoot(host, env, options),
|
|
1567
|
+
host,
|
|
1568
|
+
source: environmentVariable ? "explicit-env" : "host-detected",
|
|
1569
|
+
...environmentVariable ? { environmentVariable } : {}
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
const codexRoot = hostSkillsRoot("codex", env, options);
|
|
1573
|
+
const workBuddyRoot = hostSkillsRoot("workbuddy", env, options);
|
|
1574
|
+
const pathExists = options.pathExists ?? existsSync3;
|
|
1575
|
+
if (host === "unknown") {
|
|
1576
|
+
const existing = [
|
|
1577
|
+
{ root: codexRoot, host: "codex" },
|
|
1578
|
+
{ root: workBuddyRoot, host: "workbuddy" }
|
|
1579
|
+
].filter((candidate) => pathExists(candidate.root));
|
|
1580
|
+
if (existing.length === 1) {
|
|
1581
|
+
return { ...existing[0], source: "filesystem-detected" };
|
|
1582
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
root: codexRoot,
|
|
1585
|
+
host,
|
|
1586
|
+
source: "fallback",
|
|
1587
|
+
requiresExplicitTarget: true,
|
|
1588
|
+
reason: existing.length > 1 ? "ambiguous-host" : "unknown-host",
|
|
1589
|
+
candidates: [codexRoot, workBuddyRoot]
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
root: codexRoot,
|
|
1594
|
+
host,
|
|
1595
|
+
source: "fallback",
|
|
1596
|
+
requiresExplicitTarget: true,
|
|
1597
|
+
reason: "unsupported-host",
|
|
1598
|
+
candidates: [codexRoot, workBuddyRoot]
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
function unresolvedSkillsRootMessage(resolution, targetOption = "--target-dir") {
|
|
1602
|
+
const reason = resolution.reason === "ambiguous-host" ? "\u68C0\u6D4B\u5230\u591A\u4E2A\u53EF\u80FD\u7684\u5BBF\u4E3B\u76EE\u5F55" : resolution.reason === "unsupported-host" ? `\u68C0\u6D4B\u5230\u5F53\u524D\u667A\u80FD\u4F53 ${resolution.host}\uFF0C\u4F46\u4E0D\u77E5\u9053\u5B83\u7684 skills \u76EE\u5F55` : "\u672A\u68C0\u6D4B\u5230\u5F53\u524D\u667A\u80FD\u4F53\u53CA\u5176 skills \u76EE\u5F55";
|
|
1603
|
+
return [
|
|
1604
|
+
`${reason}\uFF0C\u4E3A\u907F\u514D\u88C5\u9519\u4F4D\u7F6E\uFF0C\u672C\u6B21\u672A\u6267\u884C\u5B89\u88C5\u3002`,
|
|
1605
|
+
`\u8BF7\u4F7F\u7528 ${targetOption} <\u5F53\u524D\u667A\u80FD\u4F53\u7684 skills \u6839\u76EE\u5F55>\uFF0C\u6216\u8BBE\u7F6E WEAVER_SKILLS_ROOT \u540E\u91CD\u8BD5\u3002`,
|
|
1606
|
+
...resolution.candidates?.length ? [`\u5DF2\u77E5\u5019\u9009\u76EE\u5F55\uFF1A${resolution.candidates.join("\uFF0C")}`] : []
|
|
1607
|
+
].join("\n");
|
|
1608
|
+
}
|
|
1609
|
+
function normalizePackagedSkillName(value) {
|
|
1610
|
+
const trimmed = value.trim();
|
|
1611
|
+
if (!trimmed || trimmed === "all") return trimmed;
|
|
1612
|
+
if (trimmed.includes("/") || trimmed.includes("\\") || trimmed === "." || trimmed === "..") {
|
|
1613
|
+
throw new Error(`invalid skill name: ${value}`);
|
|
1614
|
+
}
|
|
1615
|
+
if (!/^[A-Za-z0-9._-]+$/u.test(trimmed)) {
|
|
1616
|
+
throw new Error(`invalid skill name: ${value}`);
|
|
1617
|
+
}
|
|
1618
|
+
if (trimmed.startsWith(LEGACY_SKILL_PREFIX)) {
|
|
1619
|
+
return `${SKILL_PREFIX}${trimmed.slice(LEGACY_SKILL_PREFIX.length)}`;
|
|
1620
|
+
}
|
|
1621
|
+
return trimmed.startsWith(SKILL_PREFIX) ? trimmed : `${SKILL_PREFIX}${trimmed}`;
|
|
1622
|
+
}
|
|
1623
|
+
function resolvePackagedSkillNames(sourceRoot, requested = "all") {
|
|
1624
|
+
const available = readdirSync2(sourceRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync3(resolve3(sourceRoot, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
|
|
1625
|
+
const normalized = normalizePackagedSkillName(requested);
|
|
1626
|
+
if (!normalized || normalized === "all") return available;
|
|
1627
|
+
if (!available.includes(normalized)) {
|
|
1628
|
+
const shortName = normalized.slice(SKILL_PREFIX.length);
|
|
1629
|
+
const suggestions = available.filter((name) => editDistance(shortName, name.replace(/^weaver-e10-/u, "")) <= 2);
|
|
1630
|
+
throw new Error([
|
|
1631
|
+
`unknown packaged skill: ${requested}`,
|
|
1632
|
+
...suggestions.length ? [`Did you mean: ${suggestions.join(", ")}?`] : [],
|
|
1633
|
+
'Run "weaver-work-cli skills list" to see available skills.'
|
|
1634
|
+
].join("\n"));
|
|
1635
|
+
}
|
|
1636
|
+
const names = [];
|
|
1637
|
+
if (normalized !== SHARED_SKILL_NAME && available.includes(SHARED_SKILL_NAME)) {
|
|
1638
|
+
names.push(SHARED_SKILL_NAME);
|
|
1639
|
+
}
|
|
1640
|
+
names.push(normalized);
|
|
1641
|
+
return names;
|
|
1642
|
+
}
|
|
1643
|
+
function editDistance(left, right) {
|
|
1644
|
+
let row = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
1645
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
1646
|
+
const next = [i + 1];
|
|
1647
|
+
for (let j = 0; j < right.length; j += 1) {
|
|
1648
|
+
next.push(Math.min(next[j] + 1, row[j + 1] + 1, row[j] + (left[i] === right[j] ? 0 : 1)));
|
|
1649
|
+
}
|
|
1650
|
+
row = next;
|
|
1651
|
+
}
|
|
1652
|
+
return row[right.length];
|
|
1653
|
+
}
|
|
1654
|
+
function installPackagedSkills(sourceRoot, targetRoot, requested = "all") {
|
|
1655
|
+
const resolvedSourceRoot = resolve3(sourceRoot);
|
|
1656
|
+
const resolvedTargetRoot = resolve3(targetRoot);
|
|
1657
|
+
const skillNames = resolvePackagedSkillNames(resolvedSourceRoot, requested);
|
|
1658
|
+
const productContent = productMetadataContent(resolvedSourceRoot);
|
|
1659
|
+
mkdirSync(resolvedTargetRoot, { recursive: true, mode: 448 });
|
|
1660
|
+
const installed = skillNames.map((name) => {
|
|
1661
|
+
const source = resolveInside(resolvedSourceRoot, name);
|
|
1662
|
+
const target = resolve3(resolvedTargetRoot, basename(name));
|
|
1663
|
+
statSync(resolve3(source, "SKILL.md"));
|
|
1664
|
+
cpSync2(source, target, {
|
|
1665
|
+
recursive: true,
|
|
1666
|
+
force: true,
|
|
1667
|
+
filter: (entry) => shouldCopy(entry, source, Boolean(productContent))
|
|
1668
|
+
});
|
|
1669
|
+
writeProductMetadata(target, productContent);
|
|
1670
|
+
const current = {
|
|
1671
|
+
name,
|
|
1672
|
+
source,
|
|
1673
|
+
target,
|
|
1674
|
+
files: countFiles2(target)
|
|
1675
|
+
};
|
|
1676
|
+
return current;
|
|
1677
|
+
});
|
|
1678
|
+
return {
|
|
1679
|
+
sourceRoot: resolvedSourceRoot,
|
|
1680
|
+
targetRoot: resolvedTargetRoot,
|
|
1681
|
+
skillNames,
|
|
1682
|
+
installed
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
function productMetadataContent(sourceRoot) {
|
|
1686
|
+
const productPath = productMetadataPath(sourceRoot);
|
|
1687
|
+
if (!existsSync3(productPath)) return null;
|
|
1688
|
+
const product = JSON.parse(readFileSync2(productPath, "utf8"));
|
|
1689
|
+
const packageInfo = readPackageInfo(resolve3(sourceRoot, "..", "package.json"));
|
|
1690
|
+
const metadata = {
|
|
1691
|
+
schemaVersion: typeof product.schemaVersion === "number" ? product.schemaVersion : 1,
|
|
1692
|
+
product: stringOrEmpty(product.product),
|
|
1693
|
+
productVersion: stringOrEmpty(packageInfo?.version) || stringOrEmpty(product.productVersion),
|
|
1694
|
+
packageName: stringOrEmpty(packageInfo?.name) || stringOrEmpty(product.packageName),
|
|
1695
|
+
command: stringOrEmpty(product.command),
|
|
1696
|
+
nodeEngine: stringOrEmpty(packageInfo?.engines?.node) || stringOrEmpty(product.nodeEngine)
|
|
1697
|
+
};
|
|
1698
|
+
if (metadata.product !== "weaver-work-cli" || metadata.packageName !== "weaver-work-cli" || metadata.command !== "weaver-work-cli" || !metadata.productVersion || !metadata.nodeEngine) {
|
|
1699
|
+
throw new Error("skill-template/product.json or package.json is invalid for weaver-work-cli skills");
|
|
1700
|
+
}
|
|
1701
|
+
return `${JSON.stringify(metadata, null, 2)}
|
|
1702
|
+
`;
|
|
1703
|
+
}
|
|
1704
|
+
function productMetadataPath(sourceRoot) {
|
|
1705
|
+
const templatePath = resolve3(sourceRoot, "..", "skill-template", "product.json");
|
|
1706
|
+
if (existsSync3(templatePath)) return templatePath;
|
|
1707
|
+
return resolve3(sourceRoot, "product.json");
|
|
1708
|
+
}
|
|
1709
|
+
function readPackageInfo(packagePath2) {
|
|
1710
|
+
if (!existsSync3(packagePath2)) return null;
|
|
1711
|
+
return JSON.parse(readFileSync2(packagePath2, "utf8"));
|
|
1712
|
+
}
|
|
1713
|
+
function stringOrEmpty(value) {
|
|
1714
|
+
return typeof value === "string" ? value : "";
|
|
1715
|
+
}
|
|
1716
|
+
function writeProductMetadata(target, productContent) {
|
|
1717
|
+
if (!productContent) return;
|
|
1718
|
+
writeFileSync(resolve3(target, "product.json"), productContent, "utf8");
|
|
1719
|
+
}
|
|
1720
|
+
function shouldCopy(entry, sourceRoot, skipSkillProduct) {
|
|
1721
|
+
const relativePath = relative(sourceRoot, entry);
|
|
1722
|
+
const name = basename(entry);
|
|
1723
|
+
if (!relativePath) return true;
|
|
1724
|
+
if (skipSkillProduct && relativePath === "product.json") return false;
|
|
1725
|
+
if (name === ".DS_Store" || name.startsWith(".")) return false;
|
|
1726
|
+
const parts = relativePath.split(sep);
|
|
1727
|
+
return !parts.includes("node_modules");
|
|
1728
|
+
}
|
|
1729
|
+
function countFiles2(root) {
|
|
1730
|
+
let count = 0;
|
|
1731
|
+
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
1732
|
+
const child = resolve3(root, entry.name);
|
|
1733
|
+
if (entry.isDirectory()) count += countFiles2(child);
|
|
1734
|
+
if (entry.isFile()) count += 1;
|
|
1735
|
+
}
|
|
1736
|
+
return count;
|
|
1737
|
+
}
|
|
1738
|
+
function resolveInside(base, subpath = "") {
|
|
1739
|
+
if (subpath.startsWith("/") || subpath.split(/[\\/]/u).includes("..")) {
|
|
1740
|
+
throw new Error(`invalid skill path: ${subpath}`);
|
|
1741
|
+
}
|
|
1742
|
+
const target = resolve3(base, subpath || ".");
|
|
1743
|
+
const rel = relative(base, target);
|
|
1744
|
+
if (rel === ".." || rel.startsWith(`..${sep}`)) {
|
|
1745
|
+
throw new Error(`invalid skill path: ${subpath}`);
|
|
1746
|
+
}
|
|
1747
|
+
return target;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// src/cmd/setup/index.ts
|
|
1751
|
+
var SKILLS_DIR = packagePath("skills");
|
|
1752
|
+
function versionMajor2(version) {
|
|
1753
|
+
return Number(version.replace(/^v/, "").split(".")[0] || 0);
|
|
1754
|
+
}
|
|
1755
|
+
var setupCommand = {
|
|
1756
|
+
name: "setup",
|
|
1757
|
+
description: "One-step setup checks for business shortcuts",
|
|
1758
|
+
category: "system",
|
|
1759
|
+
register({ program, action }) {
|
|
1760
|
+
const setup = program.command("setup").description("One-step setup checks for business shortcuts");
|
|
1761
|
+
setup.command("invoice").description("Check invoice CLI readiness and optionally install Agent skills").option("--install-skill", "install packaged invoice skill into the current Agent skills directory").option("--skill-target-dir <path>", "override target Agent skills directory").action(action((ctx, options) => {
|
|
1762
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
1763
|
+
const authFile = findAuthFile(ctx.cwd, ctx.getGlobalOptions());
|
|
1764
|
+
const packagedSkills = resolvePackagedSkillNames(SKILLS_DIR, "yepiaotong");
|
|
1765
|
+
const checks = [
|
|
1766
|
+
{
|
|
1767
|
+
name: "node",
|
|
1768
|
+
ok: versionMajor2(process.version) >= 18,
|
|
1769
|
+
detail: process.version
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
name: "package",
|
|
1773
|
+
ok: true,
|
|
1774
|
+
detail: ctx.packageInfo.name + "@" + ctx.packageInfo.version
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
name: "invoiceSkillPackaged",
|
|
1778
|
+
ok: packagedSkills.includes("weaver-e10-yepiaotong"),
|
|
1779
|
+
detail: packagedSkills.join(", ")
|
|
1780
|
+
},
|
|
1781
|
+
{
|
|
1782
|
+
name: "e10Auth",
|
|
1783
|
+
ok: Boolean(authFile && existsSync4(authFile)),
|
|
1784
|
+
detail: authFile || "not found"
|
|
1785
|
+
}
|
|
1786
|
+
];
|
|
1787
|
+
const specifiedSkillRoot = typeof opts.skillTargetDir === "string" && opts.skillTargetDir.trim() ? resolveSkillsTargetPath(opts.skillTargetDir) : "";
|
|
1788
|
+
const skillResolution = opts.installSkill ? specifiedSkillRoot ? { root: specifiedSkillRoot, host: "explicit", source: "target-dir" } : resolveSkillsRoot(ctx.env) : null;
|
|
1789
|
+
if (skillResolution?.requiresExplicitTarget) {
|
|
1790
|
+
throw new CliError(
|
|
1791
|
+
unresolvedSkillsRootMessage(skillResolution, "--skill-target-dir"),
|
|
1792
|
+
2 /* UsageError */
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
const skillInstall = skillResolution ? {
|
|
1796
|
+
...installPackagedSkills(SKILLS_DIR, skillResolution.root, "yepiaotong"),
|
|
1797
|
+
skillsHost: skillResolution.host,
|
|
1798
|
+
skillsRootSource: skillResolution.source,
|
|
1799
|
+
warnings: skillResolution.source === "filesystem-detected" ? ["\u76EE\u6807\u76EE\u5F55\u4EC5\u6839\u636E\u672C\u673A\u5DF2\u6709\u76EE\u5F55\u63A8\u65AD\uFF1B\u5982\u4E0E\u5F53\u524D\u667A\u80FD\u4F53\u4E0D\u7B26\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A\u3002"] : []
|
|
1800
|
+
} : null;
|
|
1801
|
+
const nextActions = [];
|
|
1802
|
+
if (!checks.find((check) => check.name === "e10Auth")?.ok) {
|
|
1803
|
+
nextActions.push("Authenticate using the method required by the current runtime, then rerun setup.");
|
|
1804
|
+
}
|
|
1805
|
+
if (!skillInstall) {
|
|
1806
|
+
nextActions.push("Optional Agent setup: weaver-work-cli skills install yepiaotong");
|
|
1807
|
+
}
|
|
1808
|
+
nextActions.push("Verify invoice operations: weaver-work-cli invoice schema");
|
|
1809
|
+
const ok = checks.every((check) => check.ok);
|
|
1810
|
+
ctx.output.write({
|
|
1811
|
+
ok,
|
|
1812
|
+
module: "invoice",
|
|
1813
|
+
checks,
|
|
1814
|
+
skillInstall,
|
|
1815
|
+
nextActions
|
|
1816
|
+
}, {
|
|
1817
|
+
text: [
|
|
1818
|
+
ok ? "Invoice setup checks passed." : "Invoice setup needs attention.",
|
|
1819
|
+
...checks.map((check) => `- ${check.name}: ${check.ok ? "ok" : "missing"} (${check.detail})`),
|
|
1820
|
+
...skillInstall ? skillInstall.installed.map((entry) => `- installed skill: ${entry.name} -> ${entry.target}`) : [],
|
|
1821
|
+
...skillInstall ? skillInstall.warnings.map((warning) => `- warning: ${warning}`) : [],
|
|
1822
|
+
"Next:",
|
|
1823
|
+
...nextActions.map((actionText) => `- ${actionText}`)
|
|
1824
|
+
].join("\n")
|
|
1825
|
+
});
|
|
1826
|
+
}));
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
// src/cmd/skills/index.ts
|
|
1831
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1832
|
+
import { spawnSync } from "node:child_process";
|
|
1833
|
+
|
|
1834
|
+
// src/internal/skills/detector.ts
|
|
1835
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
1836
|
+
import { join, resolve as resolve4 } from "node:path";
|
|
1837
|
+
var PACKAGED_DETECTOR_VERSION = "1.0.13";
|
|
1838
|
+
var PACKAGED_DETECTOR_NAME = `weaver-skill-detector-${PACKAGED_DETECTOR_VERSION}`;
|
|
1839
|
+
function packagedDetectorDir() {
|
|
1840
|
+
return packagePath("tools", PACKAGED_DETECTOR_NAME);
|
|
1841
|
+
}
|
|
1842
|
+
function detectorScriptPath(detectorDir) {
|
|
1843
|
+
return join(detectorDir, "scripts", "validate_skill.py");
|
|
1844
|
+
}
|
|
1845
|
+
function legacySiblingDetectorDir() {
|
|
1846
|
+
return resolve4(packagedDetectorDir(), "..", "..", "..", PACKAGED_DETECTOR_NAME);
|
|
1847
|
+
}
|
|
1848
|
+
function detectorVersion(detectorDir) {
|
|
1849
|
+
let version = PACKAGED_DETECTOR_VERSION;
|
|
1850
|
+
const metaPath = join(detectorDir, "_meta.json");
|
|
1851
|
+
if (existsSync5(metaPath)) {
|
|
1852
|
+
try {
|
|
1853
|
+
const meta = JSON.parse(readFileSync3(metaPath, "utf8"));
|
|
1854
|
+
if (typeof meta.version === "string" && meta.version) version = meta.version;
|
|
1855
|
+
} catch {
|
|
1856
|
+
version = PACKAGED_DETECTOR_VERSION;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return version;
|
|
1860
|
+
}
|
|
1861
|
+
function resolveDetectorInfo(detectorDir) {
|
|
1862
|
+
const candidates = detectorDir ? [resolve4(detectorDir)] : [packagedDetectorDir(), legacySiblingDetectorDir()].map((candidate) => resolve4(candidate));
|
|
1863
|
+
for (const candidate of candidates) {
|
|
1864
|
+
const scriptPath = detectorScriptPath(candidate);
|
|
1865
|
+
if (existsSync5(scriptPath)) {
|
|
1866
|
+
return { detectorDir: candidate, scriptPath, version: detectorVersion(candidate) };
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
throw new CliError(`weaver skill detector is not available; checked: ${candidates.map(detectorScriptPath).join(", ")}`, 2 /* UsageError */);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// src/internal/skills/reader.ts
|
|
1873
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
1874
|
+
import { relative as relative2, resolve as resolve5, sep as sep2 } from "node:path";
|
|
1875
|
+
var SKILL_PREFIX2 = "weaver-e10-";
|
|
1876
|
+
var LEGACY_SKILL_PREFIX2 = "weaver-work-cli-";
|
|
1877
|
+
var SkillReader = class {
|
|
1878
|
+
constructor(root) {
|
|
1879
|
+
this.root = root;
|
|
1880
|
+
}
|
|
1881
|
+
root;
|
|
1882
|
+
list() {
|
|
1883
|
+
return readdirSync3(this.root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => this.skillInfo(entry.name)).filter((entry) => Boolean(entry)).sort((a, b) => a.name.localeCompare(b.name));
|
|
1884
|
+
}
|
|
1885
|
+
listPath(target) {
|
|
1886
|
+
const { name, subpath } = splitTarget(target);
|
|
1887
|
+
const skill = this.resolveSkill(name);
|
|
1888
|
+
const skillRoot = skill.root;
|
|
1889
|
+
const dir = this.resolveInside(skillRoot, subpath);
|
|
1890
|
+
const info = statSync2(dir);
|
|
1891
|
+
if (!info.isDirectory()) {
|
|
1892
|
+
throw new Error(`path is a file, not a directory: ${target}`);
|
|
1893
|
+
}
|
|
1894
|
+
const listed = cleanOutputPath(skill.name, subpath);
|
|
1895
|
+
const entries = readdirSync3(dir, { withFileTypes: true }).map((entry) => ({
|
|
1896
|
+
path: cleanOutputPath(listed, entry.name),
|
|
1897
|
+
isDir: entry.isDirectory()
|
|
1898
|
+
})).sort((a, b) => a.path.localeCompare(b.path));
|
|
1899
|
+
return { path: listed, entries };
|
|
1900
|
+
}
|
|
1901
|
+
read(target, pathArg = "") {
|
|
1902
|
+
const split = splitTarget(target);
|
|
1903
|
+
const skill = this.resolveSkill(split.name);
|
|
1904
|
+
const subpath = pathArg || split.subpath || "SKILL.md";
|
|
1905
|
+
const file = this.resolveInside(skill.root, subpath);
|
|
1906
|
+
const info = statSync2(file);
|
|
1907
|
+
if (info.isDirectory()) {
|
|
1908
|
+
throw new Error(`path is a directory, not a file: ${cleanOutputPath(skill.name, subpath)}`);
|
|
1909
|
+
}
|
|
1910
|
+
return {
|
|
1911
|
+
skill: skill.name,
|
|
1912
|
+
path: cleanOutputPath(subpath),
|
|
1913
|
+
content: readFileSync4(file, "utf-8")
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
skillInfo(name) {
|
|
1917
|
+
try {
|
|
1918
|
+
const skillRoot = this.ensureSkill(name);
|
|
1919
|
+
const content = readFileSync4(resolve5(skillRoot, "SKILL.md"), "utf-8");
|
|
1920
|
+
const frontmatter = parseFrontmatter(content);
|
|
1921
|
+
return {
|
|
1922
|
+
name,
|
|
1923
|
+
title: frontmatter.name,
|
|
1924
|
+
description: frontmatter.description,
|
|
1925
|
+
version: frontmatter.version
|
|
1926
|
+
};
|
|
1927
|
+
} catch {
|
|
1928
|
+
return null;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
ensureSkill(name) {
|
|
1932
|
+
return this.resolveSkill(name).root;
|
|
1933
|
+
}
|
|
1934
|
+
resolveSkill(name) {
|
|
1935
|
+
if (!name || name.includes("/") || name.includes("\\") || name === "." || name === "..") {
|
|
1936
|
+
throw new Error(`unknown skill: ${name}`);
|
|
1937
|
+
}
|
|
1938
|
+
const skillRoot = this.resolveInside(this.root, name);
|
|
1939
|
+
if (!existsDirectoryWithSkill(skillRoot)) {
|
|
1940
|
+
const normalized = normalizeLegacySkillName(name);
|
|
1941
|
+
if (normalized !== name) {
|
|
1942
|
+
const normalizedRoot = this.resolveInside(this.root, normalized);
|
|
1943
|
+
if (existsDirectoryWithSkill(normalizedRoot)) return { name: normalized, root: normalizedRoot };
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const info = statSync2(skillRoot);
|
|
1947
|
+
if (!info.isDirectory()) throw new Error(`unknown skill: ${name}`);
|
|
1948
|
+
statSync2(resolve5(skillRoot, "SKILL.md"));
|
|
1949
|
+
return { name, root: skillRoot };
|
|
1950
|
+
}
|
|
1951
|
+
resolveInside(base, subpath = "") {
|
|
1952
|
+
const cleaned = subpath.replace(/\\/g, "/");
|
|
1953
|
+
if (cleaned.startsWith("/") || cleaned.split("/").includes("..")) {
|
|
1954
|
+
throw new Error(`invalid skill path: ${subpath}`);
|
|
1955
|
+
}
|
|
1956
|
+
const target = resolve5(base, cleaned || ".");
|
|
1957
|
+
const rel = relative2(base, target);
|
|
1958
|
+
if (rel === ".." || rel.startsWith(`..${sep2}`) || resolve5(base) === target && cleaned === "..") {
|
|
1959
|
+
throw new Error(`invalid skill path: ${subpath}`);
|
|
1960
|
+
}
|
|
1961
|
+
return target;
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
function normalizeLegacySkillName(name) {
|
|
1965
|
+
return name.startsWith(LEGACY_SKILL_PREFIX2) ? `${SKILL_PREFIX2}${name.slice(LEGACY_SKILL_PREFIX2.length)}` : name;
|
|
1966
|
+
}
|
|
1967
|
+
function existsDirectoryWithSkill(skillRoot) {
|
|
1968
|
+
try {
|
|
1969
|
+
const info = statSync2(skillRoot);
|
|
1970
|
+
if (!info.isDirectory()) return false;
|
|
1971
|
+
statSync2(resolve5(skillRoot, "SKILL.md"));
|
|
1972
|
+
return true;
|
|
1973
|
+
} catch {
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
function splitTarget(target) {
|
|
1978
|
+
const index = target.indexOf("/");
|
|
1979
|
+
if (index === -1) return { name: target, subpath: "" };
|
|
1980
|
+
return {
|
|
1981
|
+
name: target.slice(0, index),
|
|
1982
|
+
subpath: target.slice(index + 1)
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
function cleanOutputPath(...parts) {
|
|
1986
|
+
return parts.filter(Boolean).join("/").replace(/\/+/g, "/").replace(/^\.\//, "");
|
|
1987
|
+
}
|
|
1988
|
+
function parseFrontmatter(content) {
|
|
1989
|
+
const lines = content.split(/\r?\n/u);
|
|
1990
|
+
if (lines[0] !== "---") return {};
|
|
1991
|
+
const result = {};
|
|
1992
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
1993
|
+
const line = lines[index];
|
|
1994
|
+
if (line === "---") return result;
|
|
1995
|
+
const match = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/u);
|
|
1996
|
+
if (!match) continue;
|
|
1997
|
+
result[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
|
|
1998
|
+
}
|
|
1999
|
+
return {};
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/cmd/skills/index.ts
|
|
2003
|
+
var SKILLS_DIR2 = packagePath("skills");
|
|
2004
|
+
function reader() {
|
|
2005
|
+
if (!existsSync6(SKILLS_DIR2)) {
|
|
2006
|
+
throw new CliError("skills directory does not exist: " + SKILLS_DIR2);
|
|
2007
|
+
}
|
|
2008
|
+
return new SkillReader(SKILLS_DIR2);
|
|
2009
|
+
}
|
|
2010
|
+
function commandOptions(value) {
|
|
2011
|
+
if (!value || typeof value !== "object" || !("opts" in value)) return {};
|
|
2012
|
+
const opts = value.opts();
|
|
2013
|
+
return opts && typeof opts === "object" ? opts : {};
|
|
2014
|
+
}
|
|
2015
|
+
function describeRoot(resolution) {
|
|
2016
|
+
if (resolution.source === "target-dir") return "\u7531 --target-dir \u6307\u5B9A";
|
|
2017
|
+
if (resolution.source === "explicit-env") {
|
|
2018
|
+
return `\u7531 ${resolution.environmentVariable || "\u5BBF\u4E3B\u73AF\u5883\u53D8\u91CF"} \u6307\u5B9A\uFF08${resolution.host}\uFF09`;
|
|
2019
|
+
}
|
|
2020
|
+
if (resolution.source === "host-detected") return `\u81EA\u52A8\u63A2\u6D4B\u5BBF\u4E3B ${resolution.host}`;
|
|
2021
|
+
if (resolution.source === "filesystem-detected") return `\u7531\u672C\u673A\u552F\u4E00\u5DF2\u5B58\u5728\u7684\u5BBF\u4E3B\u76EE\u5F55\u63A8\u65AD\uFF08${resolution.host}\uFF09`;
|
|
2022
|
+
return "\u65E0\u6CD5\u53EF\u9760\u5224\u65AD\u5BBF\u4E3B";
|
|
2023
|
+
}
|
|
2024
|
+
function pythonInvocation(opts) {
|
|
2025
|
+
if (opts.python) return { command: opts.python, args: [] };
|
|
2026
|
+
if (process.platform === "win32") return { command: "py", args: ["-3"] };
|
|
2027
|
+
return { command: "python3", args: [] };
|
|
2028
|
+
}
|
|
2029
|
+
function detectorArgs(target, opts, globalJson) {
|
|
2030
|
+
const args = [target];
|
|
2031
|
+
if (opts.json || globalJson) args.push("--json");
|
|
2032
|
+
if (opts.noHtml || opts.html === false) args.push("--no-html");
|
|
2033
|
+
if (opts.out) args.push("--out", opts.out);
|
|
2034
|
+
if (typeof opts.html === "string") args.push("--html", opts.html);
|
|
2035
|
+
if (opts.platform) args.push("--platform", opts.platform);
|
|
2036
|
+
if (opts.marketCatalog) args.push("--market-catalog", opts.marketCatalog);
|
|
2037
|
+
if (opts.marketLive) args.push("--market-live");
|
|
2038
|
+
return args;
|
|
2039
|
+
}
|
|
2040
|
+
var skillsCommand = {
|
|
2041
|
+
name: "skills",
|
|
2042
|
+
description: "Read packaged agent skill content",
|
|
2043
|
+
category: "skills",
|
|
2044
|
+
register({ program, action }) {
|
|
2045
|
+
const skills = program.command("skills").description("Read packaged agent skill content");
|
|
2046
|
+
skills.command("list").description("List skills, or list one layer under a skill path").argument("[target]", "skill name or name/path").option("--json", "accepted for symmetry; output is always JSON").action(action((ctx, target) => {
|
|
2047
|
+
const skillReader = reader();
|
|
2048
|
+
if (typeof target !== "string" || !target) {
|
|
2049
|
+
const listed2 = skillReader.list();
|
|
2050
|
+
ctx.output.write({ ok: true, skills: listed2, count: listed2.length }, { json: true });
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const listed = skillReader.listPath(target);
|
|
2054
|
+
ctx.output.write({ ok: true, ...listed, count: listed.entries.length }, { json: true });
|
|
2055
|
+
}));
|
|
2056
|
+
skills.command("read").description("Print a skill SKILL.md or a file under the skill").argument("<target>", "skill name or name/path").argument("[path]", "path under the skill").option("--json", "output as a JSON envelope instead of raw markdown").action(action((ctx, target, pathArg, command) => {
|
|
2057
|
+
if (typeof target !== "string") throw new CliError("target must be a string");
|
|
2058
|
+
const opts = commandOptions(command || pathArg);
|
|
2059
|
+
const result = reader().read(target, typeof pathArg === "string" ? pathArg : "");
|
|
2060
|
+
if (opts.json || ctx.getGlobalOptions().json) {
|
|
2061
|
+
ctx.output.write(result, { json: true });
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
ctx.output.raw(result.content);
|
|
2065
|
+
}));
|
|
2066
|
+
skills.command("install").description("Install packaged skills into the current Agent skills directory").argument("[target]", "skill name (weaver-e10-* or legacy weaver-work-cli-*), short name such as skill-maker, or all", "all").option("--target-dir <path>", "override target skills directory").action(action((ctx, target, options) => {
|
|
2067
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
2068
|
+
const specified = typeof opts.targetDir === "string" && opts.targetDir.trim() ? resolveSkillsTargetPath(opts.targetDir) : "";
|
|
2069
|
+
const resolution = specified ? { root: specified, host: "explicit", source: "target-dir" } : resolveSkillsRoot(ctx.env);
|
|
2070
|
+
if (resolution.requiresExplicitTarget) {
|
|
2071
|
+
throw new CliError(unresolvedSkillsRootMessage(resolution), 2 /* UsageError */);
|
|
2072
|
+
}
|
|
2073
|
+
const targetRoot = resolution.root;
|
|
2074
|
+
const result = installPackagedSkills(SKILLS_DIR2, targetRoot, typeof target === "string" ? target : "all");
|
|
2075
|
+
const warnings = resolution.source === "filesystem-detected" ? ["\u63D0\u793A\uFF1A\u8BE5\u76EE\u5F55\u4EC5\u6839\u636E\u672C\u673A\u5DF2\u6709\u76EE\u5F55\u63A8\u65AD\uFF1B\u5982\u4E0E\u5F53\u524D\u667A\u80FD\u4F53\u4E0D\u7B26\uFF0C\u8BF7\u7528 --target-dir \u6216 WEAVER_SKILLS_ROOT \u6307\u5B9A\u3002"] : [];
|
|
2076
|
+
ctx.output.write({
|
|
2077
|
+
...result,
|
|
2078
|
+
targetRoot,
|
|
2079
|
+
skillsHost: resolution.host,
|
|
2080
|
+
skillsRootSource: resolution.source,
|
|
2081
|
+
warnings
|
|
2082
|
+
}, {
|
|
2083
|
+
text: [
|
|
2084
|
+
`Skills root: ${targetRoot}\uFF08${describeRoot(resolution)}\uFF09`,
|
|
2085
|
+
"Installed packaged skills:",
|
|
2086
|
+
...result.installed.map((entry) => `- ${entry.name} -> ${entry.target}`),
|
|
2087
|
+
...warnings
|
|
2088
|
+
].join("\n")
|
|
2089
|
+
});
|
|
2090
|
+
}));
|
|
2091
|
+
skills.command("validate").description("Validate a generated skill using the packaged Weaver skill detector").argument("<target>", "skill directory, SKILL.md, or skill zip").option("--detector-dir <path>", "override detector directory; defaults to the packaged detector").option("--python <path>", "Python executable; defaults to python3, or py -3 on Windows").option("--json", "ask the detector to print JSON").option("--no-html", "disable detector HTML report output").option("--out <path>", "write detector markdown report").option("--html <path>", "write detector HTML report").option("--platform <platform>", "detector platform: weaver or qwenwork").option("--market-catalog <path>", "detector market catalog JSON").option("--market-live", "enable detector live market checks").action(action((ctx, target, options) => {
|
|
2092
|
+
if (typeof target !== "string") throw new CliError("target must be a string");
|
|
2093
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
2094
|
+
const detector = resolveDetectorInfo(opts.detectorDir);
|
|
2095
|
+
const python = pythonInvocation(opts);
|
|
2096
|
+
const child = spawnSync(python.command, [
|
|
2097
|
+
...python.args,
|
|
2098
|
+
detector.scriptPath,
|
|
2099
|
+
...detectorArgs(target, opts, Boolean(ctx.getGlobalOptions().json))
|
|
2100
|
+
], { stdio: "inherit" });
|
|
2101
|
+
if (child.error) throw new CliError(`failed to run Python detector: ${child.error.message}`);
|
|
2102
|
+
if (typeof child.status === "number") {
|
|
2103
|
+
process.exitCode = child.status;
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (child.signal) throw new CliError(`detector terminated by signal: ${child.signal}`);
|
|
2107
|
+
throw new CliError("detector exited without a status code");
|
|
2108
|
+
}));
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
|
|
2112
|
+
// src/cmd/index.ts
|
|
2113
|
+
var builtinCommands = [
|
|
2114
|
+
configCommand,
|
|
2115
|
+
docsCommand,
|
|
2116
|
+
doctorCommand,
|
|
2117
|
+
commandsCommand,
|
|
2118
|
+
runCommand,
|
|
2119
|
+
refsCommand,
|
|
2120
|
+
e10Command,
|
|
2121
|
+
identityCommand,
|
|
2122
|
+
setupCommand,
|
|
2123
|
+
skillsCommand
|
|
2124
|
+
];
|
|
2125
|
+
|
|
2126
|
+
// src/core/input.ts
|
|
2127
|
+
var FileInputPolicyError = class extends CliError {
|
|
2128
|
+
type = "policy";
|
|
2129
|
+
subtype = "file_input_denied";
|
|
2130
|
+
retryable = false;
|
|
2131
|
+
constructor() {
|
|
2132
|
+
super(
|
|
2133
|
+
"\u4E0D\u5141\u8BB8\u901A\u8FC7 --input <path> \u8BFB\u53D6 JSON \u6587\u4EF6\u3002\u8BF7\u6539\u7528 --input-json\u3001--input -\uFF0C\u6216\u7528 --input-ref \u4F20\u9012\u4E0A\u4E00\u6B65\u7684\u7ED3\u679C\u3002",
|
|
2134
|
+
6 /* PolicyError */
|
|
2135
|
+
);
|
|
2136
|
+
this.name = "FileInputPolicyError";
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
function configuredPolicy(ctx) {
|
|
2140
|
+
const io = ctx.config.get("io");
|
|
2141
|
+
if (io && typeof io === "object" && !Array.isArray(io) && "fileInput" in io) {
|
|
2142
|
+
return io.fileInput;
|
|
2143
|
+
}
|
|
2144
|
+
return ctx.config.get("io.fileInput");
|
|
2145
|
+
}
|
|
2146
|
+
function resolveFileInputPolicy(ctx, options) {
|
|
2147
|
+
const allow = options.allowFileInput === true;
|
|
2148
|
+
const deny = options.fileInput === false;
|
|
2149
|
+
if (allow && deny) {
|
|
2150
|
+
throw new CliError("\u4E0D\u80FD\u540C\u65F6\u4F20 --allow-file-input \u548C --no-file-input", 2 /* UsageError */);
|
|
2151
|
+
}
|
|
2152
|
+
if (allow) return "allow";
|
|
2153
|
+
if (deny) return "deny";
|
|
2154
|
+
const fromEnv = (ctx.env.WEAVER_WORK_CLI_FILE_INPUT ?? "").trim().toLowerCase();
|
|
2155
|
+
if (fromEnv === "allow" || fromEnv === "warn" || fromEnv === "deny") return fromEnv;
|
|
2156
|
+
const fromConfig = configuredPolicy(ctx);
|
|
2157
|
+
if (fromConfig === "allow" || fromConfig === "warn" || fromConfig === "deny") return fromConfig;
|
|
2158
|
+
return "warn";
|
|
2159
|
+
}
|
|
2160
|
+
function guardFileInput(ctx, options) {
|
|
2161
|
+
const input = options.input;
|
|
2162
|
+
if (input === void 0 || input === null || String(input).trim() === "" || input === "-") return;
|
|
2163
|
+
const policy = resolveFileInputPolicy(ctx, options);
|
|
2164
|
+
if (policy === "allow") return;
|
|
2165
|
+
if (policy === "deny") throw new FileInputPolicyError();
|
|
2166
|
+
process.stderr.write(
|
|
2167
|
+
"[weaver-work-cli] \u4F7F\u7528 --input <path> \u8BFB\u53D6 JSON \u6587\u4EF6\uFF1B\u591A\u6B65\u4F20\u9012\u8BF7\u4F18\u5148\u7528 --input-ref\uFF0C\u7B80\u5355\u6570\u636E\u8BF7\u7528 --input-json\u3002\n"
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
// src/cmd/action.ts
|
|
2172
|
+
function commandFromArgs(args) {
|
|
2173
|
+
const last = args[args.length - 1];
|
|
2174
|
+
return last && typeof last === "object" && "name" in last && "opts" in last ? last : void 0;
|
|
2175
|
+
}
|
|
2176
|
+
function optionsHolderFrom(args) {
|
|
2177
|
+
return args.find((arg) => Boolean(
|
|
2178
|
+
arg && typeof arg === "object" && !Array.isArray(arg) && !("opts" in arg) && ("inputRef" in arg || "pick" in arg || "pickAs" in arg || "allowFileInput" in arg || "fileInput" in arg)
|
|
2179
|
+
));
|
|
2180
|
+
}
|
|
2181
|
+
function applyRefInput(ctx, args) {
|
|
2182
|
+
const holder = optionsHolderFrom(args);
|
|
2183
|
+
if (!holder) return;
|
|
2184
|
+
const refId = String(holder.inputRef ?? "").trim();
|
|
2185
|
+
if (!refId) {
|
|
2186
|
+
if (holder.pick || holder.pickAs) {
|
|
2187
|
+
throw new RefError("input_ref_required", "--pick \u548C --pick-as \u5FC5\u987B\u4E0E --input-ref \u4E00\u8D77\u4F7F\u7528");
|
|
2188
|
+
}
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
if (holder.inputJson !== void 0 || holder.input !== void 0) {
|
|
2192
|
+
throw new RefError("input_conflict", "\u4E0D\u80FD\u540C\u65F6\u4F20 --input-ref \u4E0E --input/--input-json");
|
|
2193
|
+
}
|
|
2194
|
+
const pick = holder.pick === void 0 || holder.pick === null || holder.pick === "" ? void 0 : String(holder.pick);
|
|
2195
|
+
const pickAs = holder.pickAs === void 0 || holder.pickAs === null || holder.pickAs === "" ? void 0 : String(holder.pickAs);
|
|
2196
|
+
holder.inputJson = JSON.stringify(resolveRefInput(ctx, {
|
|
2197
|
+
refId,
|
|
2198
|
+
...pick ? { pick } : {},
|
|
2199
|
+
...pickAs ? { pickAs } : {}
|
|
2200
|
+
}));
|
|
2201
|
+
delete holder.inputRef;
|
|
2202
|
+
delete holder.pick;
|
|
2203
|
+
delete holder.pickAs;
|
|
2204
|
+
}
|
|
2205
|
+
function cleanupRefs(ctx) {
|
|
2206
|
+
try {
|
|
2207
|
+
pruneRefs(ctx, { maxCount: refMaxCount(ctx), maxBytes: refMaxBytes(ctx) });
|
|
2208
|
+
} catch {
|
|
2209
|
+
process.stderr.write("[weaver-work-cli] \u6682\u5B58\u7ED3\u679C\u6E05\u7406\u5931\u8D25\uFF1B\u672C\u6B21\u547D\u4EE4\u7EE7\u7EED\u6267\u884C\u3002\n");
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function isRefsShow(command) {
|
|
2213
|
+
const names = [];
|
|
2214
|
+
let current = command;
|
|
2215
|
+
while (current) {
|
|
2216
|
+
const name = current.name();
|
|
2217
|
+
if (name) names.unshift(name);
|
|
2218
|
+
current = current.parent;
|
|
2219
|
+
}
|
|
2220
|
+
return names.slice(-2).join(" ") === "refs show";
|
|
2221
|
+
}
|
|
2222
|
+
function createAction(ctx) {
|
|
2223
|
+
return (handler) => async (...args) => {
|
|
2224
|
+
const commandCtx = ctx.forCommand(commandFromArgs(args));
|
|
2225
|
+
try {
|
|
2226
|
+
await commandCtx.hooks.beforeCommand(commandCtx);
|
|
2227
|
+
applyRefInput(commandCtx, args);
|
|
2228
|
+
const options = commandCtx.activeCommand?.opts();
|
|
2229
|
+
if (options) guardFileInput(commandCtx, options);
|
|
2230
|
+
if (!isRefsShow(commandCtx.activeCommand)) cleanupRefs(commandCtx);
|
|
2231
|
+
const result = await handler(commandCtx, ...args);
|
|
2232
|
+
await commandCtx.hooks.afterCommand(commandCtx, result);
|
|
2233
|
+
} catch (error) {
|
|
2234
|
+
await commandCtx.hooks.onError(commandCtx, error);
|
|
2235
|
+
if (shouldReportPlatformError(error)) {
|
|
2236
|
+
process.stderr.write(JSON.stringify(toPlatformError(error)) + "\n");
|
|
2237
|
+
}
|
|
2238
|
+
if (commandCtx.getGlobalOptions().json) {
|
|
2239
|
+
console.error(JSON.stringify({
|
|
2240
|
+
schemaVersion: 1,
|
|
2241
|
+
ok: false,
|
|
2242
|
+
error: toErrorEnvelope(error)
|
|
2243
|
+
}, null, 2));
|
|
2244
|
+
process.exitCode = isCliError(error) ? error.exitCode : 1 /* GeneralError */;
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
if (isCliError(error)) {
|
|
2248
|
+
commandCtx.logger.error(error.message);
|
|
2249
|
+
process.exitCode = error.exitCode;
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2253
|
+
commandCtx.logger.error(message);
|
|
2254
|
+
process.exitCode = 1 /* GeneralError */;
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/cmd/register.ts
|
|
2260
|
+
var IO_CHANNEL_OPTIONS = [
|
|
2261
|
+
{ flags: "--input-ref <ref>", description: "consume a persisted large result reference" },
|
|
2262
|
+
{ flags: "--pick <path>", description: "pick a sub path from --input-ref, e.g. items[0].fid" },
|
|
2263
|
+
{ flags: "--pick-as <field>", description: "map a scalar picked value to this input field, e.g. fid" },
|
|
2264
|
+
{ flags: "--allow-file-input", description: "allow reading JSON input from --input <path>" },
|
|
2265
|
+
{ flags: "--no-file-input", description: "reject JSON input from --input <path>" }
|
|
2266
|
+
];
|
|
2267
|
+
function ensureRefInputOptions(command) {
|
|
2268
|
+
if (command.commands.length === 0) {
|
|
2269
|
+
for (const option of IO_CHANNEL_OPTIONS) {
|
|
2270
|
+
const long = option.flags.split(" ")[0];
|
|
2271
|
+
if (!command.options.some((existing) => existing.long === long)) {
|
|
2272
|
+
command.option(option.flags, option.description);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
for (const child of command.commands) {
|
|
2278
|
+
ensureRefInputOptions(child);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
async function registerCommands(program, ctx, commands) {
|
|
2282
|
+
ctx.commands = commands.map((command) => ({
|
|
2283
|
+
name: command.name,
|
|
2284
|
+
description: command.description,
|
|
2285
|
+
category: command.category
|
|
2286
|
+
}));
|
|
2287
|
+
ctx.modules = ctx.commands;
|
|
2288
|
+
for (const command of commands) {
|
|
2289
|
+
if (command.hooks) {
|
|
2290
|
+
ctx.hooks.register(command.hooks);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
const action = createAction(ctx);
|
|
2294
|
+
for (const command of commands) {
|
|
2295
|
+
await command.register({ program, ctx, action });
|
|
2296
|
+
}
|
|
2297
|
+
ensureRefInputOptions(program);
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// src/cmd/root.ts
|
|
2301
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2302
|
+
|
|
2303
|
+
// src/core/context.ts
|
|
2304
|
+
import { homedir as homedir2 } from "node:os";
|
|
2305
|
+
import { resolve as resolve7 } from "node:path";
|
|
2306
|
+
|
|
2307
|
+
// src/core/config-store.ts
|
|
2308
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2309
|
+
import { dirname } from "node:path";
|
|
2310
|
+
var ConfigStore = class {
|
|
2311
|
+
constructor(getPath) {
|
|
2312
|
+
this.getPath = getPath;
|
|
2313
|
+
}
|
|
2314
|
+
getPath;
|
|
2315
|
+
path() {
|
|
2316
|
+
return this.getPath();
|
|
2317
|
+
}
|
|
2318
|
+
all() {
|
|
2319
|
+
const filePath = this.path();
|
|
2320
|
+
if (!existsSync7(filePath)) return {};
|
|
2321
|
+
try {
|
|
2322
|
+
const value = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2323
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2324
|
+
} catch {
|
|
2325
|
+
return {};
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
get(key) {
|
|
2329
|
+
return this.all()[key];
|
|
2330
|
+
}
|
|
2331
|
+
set(key, value) {
|
|
2332
|
+
const data = this.all();
|
|
2333
|
+
data[key] = value;
|
|
2334
|
+
this.save(data);
|
|
2335
|
+
}
|
|
2336
|
+
delete(key) {
|
|
2337
|
+
const data = this.all();
|
|
2338
|
+
const existed = Object.prototype.hasOwnProperty.call(data, key);
|
|
2339
|
+
delete data[key];
|
|
2340
|
+
this.save(data);
|
|
2341
|
+
return existed;
|
|
2342
|
+
}
|
|
2343
|
+
save(data) {
|
|
2344
|
+
const filePath = this.path();
|
|
2345
|
+
mkdirSync2(dirname(filePath), { recursive: true });
|
|
2346
|
+
writeFileSync2(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
// src/core/logger.ts
|
|
2351
|
+
var Logger = class {
|
|
2352
|
+
constructor(getOptions) {
|
|
2353
|
+
this.getOptions = getOptions;
|
|
2354
|
+
}
|
|
2355
|
+
getOptions;
|
|
2356
|
+
/**
|
|
2357
|
+
* 机器模式:进度与提示改走 stderr,stdout 只留给结构化结果。
|
|
2358
|
+
*
|
|
2359
|
+
* 只有显式开启的平台型命令(如 `auth init`)会打开它,默认关闭,
|
|
2360
|
+
* 因此既有命令的 stdout 输出口径完全不变。
|
|
2361
|
+
*/
|
|
2362
|
+
machineMode = false;
|
|
2363
|
+
info(message) {
|
|
2364
|
+
if (this.machineMode) {
|
|
2365
|
+
console.error(message);
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
const opts = this.getOptions();
|
|
2369
|
+
if (!opts.quiet && !opts.json) {
|
|
2370
|
+
console.log(message);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
warn(message) {
|
|
2374
|
+
const opts = this.getOptions();
|
|
2375
|
+
if (!opts.quiet && !opts.json) {
|
|
2376
|
+
console.warn(message);
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
error(message) {
|
|
2380
|
+
console.error(message);
|
|
2381
|
+
}
|
|
2382
|
+
debug(message) {
|
|
2383
|
+
const opts = this.getOptions();
|
|
2384
|
+
if (opts.verbose && !opts.json) {
|
|
2385
|
+
console.error("[debug] " + message);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
// src/core/context.ts
|
|
2391
|
+
function createCliContext(program, hooks, packageInfo) {
|
|
2392
|
+
const getGlobalOptions = () => normalizeGlobalOptions(program.opts());
|
|
2393
|
+
const configStore = new ConfigStore(() => {
|
|
2394
|
+
const opts = getGlobalOptions();
|
|
2395
|
+
const home = process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve7(homedir2(), ".weaver-work-cli");
|
|
2396
|
+
return opts.config ? resolve7(opts.config) : resolve7(home, "config.json");
|
|
2397
|
+
});
|
|
2398
|
+
const partialCtx = {
|
|
2399
|
+
cwd: process.cwd(),
|
|
2400
|
+
env: process.env,
|
|
2401
|
+
program,
|
|
2402
|
+
hooks,
|
|
2403
|
+
packageInfo,
|
|
2404
|
+
commands: [],
|
|
2405
|
+
modules: [],
|
|
2406
|
+
getGlobalOptions,
|
|
2407
|
+
forCommand(command) {
|
|
2408
|
+
return { ...ctx, activeCommand: command };
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
const ctx = partialCtx;
|
|
2412
|
+
ctx.logger = new Logger(getGlobalOptions);
|
|
2413
|
+
ctx.output = new Output(ctx);
|
|
2414
|
+
ctx.config = configStore;
|
|
2415
|
+
return ctx;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/core/hooks.ts
|
|
2419
|
+
var HookManager = class {
|
|
2420
|
+
hooks = {
|
|
2421
|
+
beforeCommand: [],
|
|
2422
|
+
afterCommand: [],
|
|
2423
|
+
onError: [],
|
|
2424
|
+
beforeRun: [],
|
|
2425
|
+
afterRun: []
|
|
2426
|
+
};
|
|
2427
|
+
e10RequestInterceptors = [];
|
|
2428
|
+
register(hooks) {
|
|
2429
|
+
for (const [name, handler] of Object.entries(hooks)) {
|
|
2430
|
+
if (handler) {
|
|
2431
|
+
this.hooks[name].push(handler);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
async beforeCommand(ctx) {
|
|
2436
|
+
for (const hook of this.hooks.beforeCommand) {
|
|
2437
|
+
await hook(ctx);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
async afterCommand(ctx, result) {
|
|
2441
|
+
for (const hook of this.hooks.afterCommand) {
|
|
2442
|
+
await hook(ctx, result);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
async onError(ctx, error) {
|
|
2446
|
+
for (const hook of this.hooks.onError) {
|
|
2447
|
+
await hook(ctx, error);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
async beforeRun(ctx) {
|
|
2451
|
+
for (const hook of this.hooks.beforeRun) {
|
|
2452
|
+
await hook(ctx);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
async afterRun(ctx, result) {
|
|
2456
|
+
for (const hook of this.hooks.afterRun) {
|
|
2457
|
+
await hook(ctx, result);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
registerE10RequestInterceptor(interceptor) {
|
|
2461
|
+
this.e10RequestInterceptors.push(interceptor);
|
|
2462
|
+
}
|
|
2463
|
+
getE10RequestInterceptors() {
|
|
2464
|
+
return this.e10RequestInterceptors;
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
|
|
2468
|
+
// src/cmd/root.ts
|
|
2469
|
+
var CLI_COMMAND_NAME = "weaver-work-cli";
|
|
2470
|
+
function readPackageInfo2() {
|
|
2471
|
+
const pkg = JSON.parse(readFileSync6(packagePath("package.json"), "utf-8"));
|
|
2472
|
+
return {
|
|
2473
|
+
name: pkg.name || CLI_COMMAND_NAME,
|
|
2474
|
+
version: pkg.version || "0.0.0",
|
|
2475
|
+
description: pkg.description
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
function createProgram() {
|
|
2479
|
+
const packageInfo = readPackageInfo2();
|
|
2480
|
+
const program = new Command();
|
|
2481
|
+
const hooks = new HookManager();
|
|
2482
|
+
const ctx = createCliContext(program, hooks, packageInfo);
|
|
2483
|
+
program.name(CLI_COMMAND_NAME).description(packageInfo.description || "Extensible TypeScript CLI").version(packageInfo.version).option("--json", "print command output as JSON").option("--quiet", "suppress non-error logs").option("--verbose", "print debug logs").option("--config <path>", "use a custom weaver-work-cli config file").option("--auth <path>", "use a specific E10 auth file").option("--profile <name>", "use a specific E10 profile").option("--runtime-agent <name>", "identify the current agent runtime (for example: xiaoe)").option(
|
|
2484
|
+
"--is-connector [value]",
|
|
2485
|
+
"run as the WorkBuddy connector: use the fixed E10 profile managed by the connector"
|
|
2486
|
+
).option("--base-url <url>", "override E10 base URL").option("--passport-url <url>", "override E10 passport URL");
|
|
2487
|
+
program.showHelpAfterError();
|
|
2488
|
+
return { program, ctx };
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
export {
|
|
2492
|
+
firstPositionalToken,
|
|
2493
|
+
toCommandMetadata,
|
|
2494
|
+
builtinCommands,
|
|
2495
|
+
registerCommands,
|
|
2496
|
+
createProgram
|
|
2497
|
+
};
|