@vercel/cli-support 65.10.0-snapshot-c80a43d75dc7 → 65.10.0
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/dist/chunks/{add-RFPJCD35.js → add-4MTUAI2X.js} +1 -1
- package/dist/chunks/{add-A3SN7KWS.js → add-JYLWBVVQ.js} +1 -1
- package/dist/chunks/{add-TOKTANDW.js → add-T5ERJ62Z.js} +1 -1
- package/dist/chunks/{build-25I6XOV7.js → build-OGWTTFLG.js} +1 -1
- package/dist/chunks/{chunk-SHY7P3C6.js → chunk-36XHIIUT.js} +1 -1
- package/dist/chunks/{chunk-3AX7ZZJ7.js → chunk-3PKL2PTF.js} +1 -1
- package/dist/chunks/{chunk-2HLYY3KF.js → chunk-3QFBVAHL.js} +1 -1
- package/dist/chunks/chunk-3QYXC4PH.js +11 -0
- package/dist/chunks/{chunk-BT5C6MCY.js → chunk-42J2KCDF.js} +1 -1
- package/dist/chunks/{chunk-NLKM5LR5.js → chunk-4E6BGQWR.js} +1 -1
- package/dist/chunks/{chunk-FAVZ6WVS.js → chunk-4U6ACPA4.js} +1 -1
- package/dist/chunks/{chunk-NAEY6HG5.js → chunk-4WXVKR43.js} +1 -1
- package/dist/chunks/{chunk-MKYFAYCD.js → chunk-52X4YVSG.js} +1 -1
- package/dist/chunks/{chunk-3WYSEF57.js → chunk-5IZ33FNL.js} +1 -1
- package/dist/chunks/{chunk-GPISCZ2P.js → chunk-5MPZSRYG.js} +1 -1
- package/dist/chunks/{chunk-AX3V7N44.js → chunk-5RSSGN2Q.js} +1 -1
- package/dist/chunks/{chunk-7ULLVH2G.js → chunk-5RYQH3XW.js} +1 -1
- package/dist/chunks/{chunk-KNAOHPOI.js → chunk-6NUH6ZPE.js} +6 -6
- package/dist/chunks/{chunk-EJ34DLV4.js → chunk-6RMW63GB.js} +1 -1
- package/dist/chunks/{chunk-BB5UMZMF.js → chunk-6UMVJ7RL.js} +1 -1
- package/dist/chunks/{chunk-HR462OEG.js → chunk-6YAYCEDT.js} +2 -2
- package/dist/chunks/{chunk-VYRMHE34.js → chunk-75DUL7VI.js} +1 -1
- package/dist/chunks/{chunk-TNNEKGGF.js → chunk-7LSH3MEB.js} +1 -1
- package/dist/chunks/{chunk-LQAQD5LG.js → chunk-7TR6H6NE.js} +3 -3
- package/dist/chunks/chunk-7VGBGUX7.js +7 -0
- package/dist/chunks/chunk-A3C67PNC.js +7 -0
- package/dist/chunks/{chunk-CKTLP5GX.js → chunk-AMPJ6RWT.js} +1 -1
- package/dist/chunks/{chunk-IZ4GFKGR.js → chunk-AO6AMYBF.js} +1 -1
- package/dist/chunks/chunk-AUIV7YJO.js +11 -0
- package/dist/chunks/chunk-AY523IYR.js +7 -0
- package/dist/chunks/{chunk-DBPN2VGJ.js → chunk-B4XGTAFO.js} +1 -1
- package/dist/chunks/{chunk-DRLGZXAL.js → chunk-BIRSJSAO.js} +1 -1
- package/dist/chunks/{chunk-WHUZRBYL.js → chunk-BK3TNLWO.js} +5 -5
- package/dist/chunks/chunk-BZIC36KP.js +7 -0
- package/dist/chunks/chunk-CBMWPDOL.js +8 -0
- package/dist/chunks/{chunk-G2BNLRPU.js → chunk-CHIYT3V4.js} +1 -1
- package/dist/chunks/chunk-CJ6XPDSS.js +11 -0
- package/dist/chunks/{chunk-T7HE23VK.js → chunk-CU3LNJZV.js} +1 -1
- package/dist/chunks/chunk-DIHB5BUY.js +8 -0
- package/dist/chunks/{chunk-TZ6MSTOI.js → chunk-DNLE3KPC.js} +1 -1
- package/dist/chunks/{chunk-R3D4M5RE.js → chunk-DQASZDQT.js} +1 -1
- package/dist/chunks/{chunk-TNUQME3L.js → chunk-DQNP7OQ5.js} +1 -1
- package/dist/chunks/{chunk-NGHFFOD2.js → chunk-DUCCC22H.js} +1 -1
- package/dist/chunks/{chunk-QCIIUQV3.js → chunk-E7WULWMX.js} +1 -1
- package/dist/chunks/{chunk-CZOD25BC.js → chunk-ECFLIVEE.js} +1 -1
- package/dist/chunks/{chunk-FI5O4WHD.js → chunk-ECGY67NB.js} +1 -1
- package/dist/chunks/{chunk-S5JBQCBA.js → chunk-EJN55YDV.js} +1 -1
- package/dist/chunks/{chunk-3PXE5BLH.js → chunk-ER7L7HYA.js} +1 -1
- package/dist/chunks/{chunk-7UFI6OYF.js → chunk-FOWAVQOH.js} +1 -1
- package/dist/chunks/{chunk-4JWAXIIN.js → chunk-FPREO4VS.js} +1 -1
- package/dist/chunks/{chunk-PXQAWH7E.js → chunk-FSJFWVUY.js} +1 -1
- package/dist/chunks/{chunk-Y57DK3ZN.js → chunk-FXHB7TTL.js} +1 -1
- package/dist/chunks/chunk-FYE7ILRR.js +7 -0
- package/dist/chunks/{chunk-V32V4SXB.js → chunk-GQ4CQDIL.js} +1 -1
- package/dist/chunks/{chunk-W634OFUG.js → chunk-GZLYNPNU.js} +6 -5
- package/dist/chunks/chunk-HTPNGLPH.js +7 -0
- package/dist/chunks/{chunk-W5GHMAHP.js → chunk-IHTWZEQW.js} +1 -1
- package/dist/chunks/{chunk-4LGAV2WN.js → chunk-IIGNDMLO.js} +1 -1
- package/dist/chunks/{chunk-F2LNGJOZ.js → chunk-JA6IGJ67.js} +1 -1
- package/dist/chunks/chunk-JBU72NEQ.js +7 -0
- package/dist/chunks/{chunk-U6XJYQJS.js → chunk-JLNF2QTR.js} +1 -1
- package/dist/chunks/{chunk-SODZSOOL.js → chunk-K5KVSA2A.js} +1 -1
- package/dist/chunks/{chunk-VD2FK6OF.js → chunk-K7ARJYAG.js} +1 -1
- package/dist/chunks/{chunk-5TI3ZXW3.js → chunk-KBVYN635.js} +1 -1
- package/dist/chunks/chunk-KKE55LTK.js +7 -0
- package/dist/chunks/{chunk-G5T6UQAL.js → chunk-KV3QXTRZ.js} +1 -1
- package/dist/chunks/{chunk-3F5BOG4A.js → chunk-KZLRIAAT.js} +1 -1
- package/dist/chunks/{chunk-L4QQTYUI.js → chunk-LE72YLCC.js} +2 -2
- package/dist/chunks/{chunk-HJ45TAM4.js → chunk-LIEWXPUC.js} +1 -1
- package/dist/chunks/{chunk-ANCWFEOM.js → chunk-M3R7F2N4.js} +1 -1
- package/dist/chunks/{chunk-MUNGQJGO.js → chunk-M3Z6NSC2.js} +1 -1
- package/dist/chunks/{chunk-VPUYTI5E.js → chunk-MBXLL2HF.js} +1 -1
- package/dist/chunks/{chunk-C24IKNZN.js → chunk-MKTZDEOJ.js} +1 -1
- package/dist/chunks/{chunk-VZGTL3YC.js → chunk-N5AN4JZL.js} +1 -1
- package/dist/chunks/{chunk-EJZNJN4N.js → chunk-N65H4JMJ.js} +1 -1
- package/dist/chunks/{chunk-LC4BSDDG.js → chunk-NAN5IE3B.js} +1 -1
- package/dist/chunks/{chunk-TD7AL545.js → chunk-NE6PKR2J.js} +1 -1
- package/dist/chunks/{chunk-2EFBGKRC.js → chunk-NLTZGQB2.js} +1 -1
- package/dist/chunks/{chunk-TL4ZLZ2Q.js → chunk-NMIZSQDN.js} +1 -1
- package/dist/chunks/{chunk-22UXDTCE.js → chunk-NZRBJB6T.js} +1 -1
- package/dist/chunks/chunk-OII46FDN.js +12 -0
- package/dist/chunks/{chunk-YOWMZJPA.js → chunk-OJWI6QJ3.js} +1 -1
- package/dist/chunks/{chunk-GKALP6QD.js → chunk-OYIDWI5Y.js} +1 -1
- package/dist/chunks/{chunk-QVMU7ZKQ.js → chunk-P24YBQ6H.js} +1 -1
- package/dist/chunks/{chunk-2JCJP43W.js → chunk-P6J22UJQ.js} +1 -1
- package/dist/chunks/{chunk-E7DYXF6G.js → chunk-PFZCGMJ7.js} +1 -1
- package/dist/chunks/{chunk-64UCX5JM.js → chunk-PKNEWN4W.js} +1 -1
- package/dist/chunks/{chunk-Q4GX2UTG.js → chunk-PKZK23PR.js} +1 -1
- package/dist/chunks/{chunk-56UH6NQ3.js → chunk-PL6IOUKW.js} +1 -1
- package/dist/chunks/{chunk-MBL7MWP5.js → chunk-Q2JNATRP.js} +1 -1
- package/dist/chunks/{chunk-KMPDQE4Q.js → chunk-Q46Q4Y4R.js} +1 -1
- package/dist/chunks/{chunk-NAJCRBQB.js → chunk-QXL353OB.js} +1 -1
- package/dist/chunks/{chunk-VR5ROBAO.js → chunk-RI6DJAFI.js} +1 -1
- package/dist/chunks/{chunk-2DHDKWYN.js → chunk-RIJ45N7C.js} +1 -1
- package/dist/chunks/{chunk-6AJWRPTY.js → chunk-RUBMVRT6.js} +1 -1
- package/dist/chunks/{chunk-6RUAAORO.js → chunk-S535FZTA.js} +1 -1
- package/dist/chunks/{chunk-5I7CC45F.js → chunk-SBEWZ65N.js} +1 -1
- package/dist/chunks/{chunk-GJCVOEC2.js → chunk-SDYCW5JM.js} +1 -1
- package/dist/chunks/chunk-SNGM6NSO.js +7 -0
- package/dist/chunks/chunk-SNNJRKD6.js +10 -0
- package/dist/chunks/{chunk-DX2OHM56.js → chunk-SR7BZA6N.js} +1 -1
- package/dist/chunks/{chunk-QXYCICNB.js → chunk-STKZTUGE.js} +1 -1
- package/dist/chunks/{chunk-XGOELSYA.js → chunk-SXYA7JSX.js} +1 -1
- package/dist/chunks/chunk-TILI4QBL.js +11 -0
- package/dist/chunks/{chunk-PMEYNJU2.js → chunk-TZE4CS4K.js} +1 -1
- package/dist/chunks/{chunk-BZV2S4H6.js → chunk-U2TQFAO3.js} +1 -1
- package/dist/chunks/chunk-U2ZNF6HN.js +8 -0
- package/dist/chunks/chunk-UIMJNASP.js +7 -0
- package/dist/chunks/{chunk-FQVIWQ6M.js → chunk-UQNBUXQA.js} +1 -1
- package/dist/chunks/{chunk-C4N4LKPM.js → chunk-US7YJ2RQ.js} +1 -1
- package/dist/chunks/{chunk-JVNIDSP5.js → chunk-V5TOMLK4.js} +1 -1
- package/dist/chunks/{chunk-L6CZL2IQ.js → chunk-VQSSVB6V.js} +1 -1
- package/dist/chunks/chunk-VTITZEPW.js +9 -0
- package/dist/chunks/chunk-WFS3XBAA.js +8 -0
- package/dist/chunks/{chunk-XEXOSJIU.js → chunk-WKIDCYKL.js} +1 -1
- package/dist/chunks/{chunk-UVHKXCR5.js → chunk-WKSXBH7X.js} +1 -2
- package/dist/chunks/{chunk-77LSSWXL.js → chunk-WTFD4MDT.js} +1 -1
- package/dist/chunks/{chunk-YO6SJCZP.js → chunk-WYGOYE6C.js} +1 -1
- package/dist/chunks/{chunk-UNASHUWQ.js → chunk-XFUUALZS.js} +1 -1
- package/dist/chunks/{chunk-SXEOJSZQ.js → chunk-XS3MNHFR.js} +1 -1
- package/dist/chunks/{chunk-ZGQNI22M.js → chunk-YGYUPPAL.js} +2 -2
- package/dist/chunks/{chunk-Q25FOMOX.js → chunk-YKLTNKQJ.js} +1 -1
- package/dist/chunks/{chunk-WFCGAYP5.js → chunk-YUHKH4TK.js} +1 -1
- package/dist/chunks/{chunk-D4TD2KWH.js → chunk-YZKARZ2B.js} +1 -1
- package/dist/chunks/chunk-ZCUUCYID.js +13 -0
- package/dist/chunks/{chunk-FPYHB76H.js → chunk-ZEUREQ4E.js} +1 -1
- package/dist/chunks/chunk-ZIC7VWFC.js +14 -0
- package/dist/chunks/{chunk-QGZZL6YY.js → chunk-ZPJHONCZ.js} +1 -1
- package/dist/chunks/{chunk-R2P4PNRQ.js → chunk-ZQXXESKG.js} +1 -1
- package/dist/chunks/{clear-B764MEAM.js → clear-Z5OO6URI.js} +1 -1
- package/dist/chunks/cli-embedded-Y74S252S.js +66 -0
- package/dist/chunks/{compile-vercel-config-EWCB7SIC.js → compile-vercel-config-ZKUTSDWV.js} +1 -1
- package/dist/chunks/{config-D4WY2HG4.js → config-AUTTHXQO.js} +1 -1
- package/dist/chunks/config-RJ7PLFQD.js +7 -0
- package/dist/chunks/{connect-git-provider-AWEOEEOO.js → connect-git-provider-YIX5ZOU6.js} +1 -1
- package/dist/chunks/{delete-W3GMFAKA.js → delete-QQOC63HV.js} +1 -1
- package/dist/chunks/{delete-SKYFHUWD.js → delete-WIGSOZR3.js} +1 -1
- package/dist/chunks/{detect-services-3TRRRLES.js → detect-services-J4LGLAHC.js} +1 -1
- package/dist/chunks/{disable-EBPZ2LYC.js → disable-L54VGY5A.js} +1 -1
- package/dist/chunks/{discard-SMVPENSP.js → discard-Q5UPTZR2.js} +1 -1
- package/dist/chunks/{edit-CGTNGJKY.js → edit-FRGMYBW3.js} +1 -1
- package/dist/chunks/{edit-A3M3GBGP.js → edit-QEDNQ33T.js} +1 -1
- package/dist/chunks/{enable-6A5THWDP.js → enable-TGQZWSBG.js} +1 -1
- package/dist/chunks/engine-DkdkXQEy-FDYYIXY7.js +17 -0
- package/dist/chunks/{exec-UV73J2ZB.js → exec-H3X7U4P3.js} +1 -1
- package/dist/chunks/{export-R5IUWAW5.js → export-YYQ2J5AT.js} +1 -1
- package/dist/chunks/{future-CPJUUZTU.js → future-RYEYLPCG.js} +1 -1
- package/dist/chunks/{generate-cert-for-deploy-Z3HCAR5H.js → generate-cert-for-deploy-Y4KDIFF2.js} +1 -1
- package/dist/chunks/image-PDPRURMH.js +7 -0
- package/dist/chunks/{import-builders-GPJQAMXT.js → import-builders-H54QCMQ4.js} +1 -1
- package/dist/chunks/{inspect-WC2C4MDP.js → inspect-6IBPIBIJ.js} +1 -1
- package/dist/chunks/{inspect-WWOHWQKS.js → inspect-AHHXHIIP.js} +1 -1
- package/dist/chunks/{inspect-BO2ACFEG.js → inspect-Q7FYDFL7.js} +1 -1
- package/dist/chunks/{inspect-6FEL5DYV.js → inspect-RPCDGIP3.js} +1 -1
- package/dist/chunks/{inspect-TEPB7KVC.js → inspect-SFM3NWDF.js} +1 -1
- package/dist/chunks/{list-C5GUZ35P.js → list-7WWUYCE6.js} +1 -1
- package/dist/chunks/{list-EZWZSVM4.js → list-GSVERG4S.js} +1 -1
- package/dist/chunks/{list-4KSJEP23.js → list-NXZXKKAR.js} +1 -1
- package/dist/chunks/{login-ZNCCVA4H.js → login-HEHHQO3F.js} +1 -1
- package/dist/chunks/{login-PDZT4PQR.js → login-XKLPSZNN.js} +1 -1
- package/dist/chunks/{logs-YKQMUTMG.js → logs-NJ46ZSNZ.js} +1 -1
- package/dist/chunks/{ls-3HUS6KS6.js → ls-5SKRXNSI.js} +1 -1
- package/dist/chunks/{ls-WX4BJRDV.js → ls-FOFJU2JJ.js} +1 -1
- package/dist/chunks/{ls-TASQJDLF.js → ls-ITWXAWSZ.js} +1 -1
- package/dist/chunks/{ls-LYVPEIIT.js → ls-LCUOBWY2.js} +1 -1
- package/dist/chunks/{ls-KQJYKADC.js → ls-MWGIT2YH.js} +1 -1
- package/dist/chunks/{ls-RBPRE6BF.js → ls-OY5YTZGN.js} +1 -1
- package/dist/chunks/{ls-ZP7OBTTV.js → ls-TZN53EJL.js} +1 -1
- package/dist/chunks/{mutate-5N6DMBYS.js → mutate-ZYXVL22A.js} +1 -1
- package/dist/chunks/{native-self-update-7OAY4AVR.js → native-self-update-DEVBYOH2.js} +1 -1
- package/dist/chunks/{notifications-OVFMQLHL.js → notifications-XAPB2F2T.js} +1 -1
- package/dist/chunks/{open-DOBOKUEK.js → open-WPRHI6ND.js} +1 -1
- package/dist/chunks/{openapi-YAZA3237.js → openapi-UUFKDKGC.js} +1 -1
- package/dist/chunks/{permissions-PHCOVDWK.js → permissions-FU5OET67.js} +1 -1
- package/dist/chunks/{plugin-offer-M7KJSJU6.js → plugin-offer-E5JOW442.js} +1 -1
- package/dist/chunks/{prompt-missing-credentials-RTNCHH6Y.js → prompt-missing-credentials-ZZVTEQQB.js} +1 -1
- package/dist/chunks/{publish-JIFRJNDV.js → publish-R5IV2BH2.js} +1 -1
- package/dist/chunks/pull-CQVHIIAN.js +7 -0
- package/dist/chunks/{purchase-domain-if-available-3EBONBX3.js → purchase-domain-if-available-VMWCMYC2.js} +1 -1
- package/dist/chunks/{push-ZN6CBJLJ.js → push-Y5CUOISM.js} +1 -1
- package/dist/chunks/{query-MT6ID6C7.js → query-VMKIOI3W.js} +1 -1
- package/dist/chunks/{reauthenticate-OXFB64PS.js → reauthenticate-5K7LBTOW.js} +1 -1
- package/dist/chunks/{reorder-N2L43LXD.js → reorder-W3MXHSGZ.js} +1 -1
- package/dist/chunks/{reply-WBR3XXBJ.js → reply-YH7HRIWQ.js} +1 -1
- package/dist/chunks/{resolve-NMR24Y67.js → resolve-FWVMWJTG.js} +1 -1
- package/dist/chunks/{resolve-agent-team-KRTLBC5D.js → resolve-agent-team-6L7AQUA2.js} +1 -1
- package/dist/chunks/{restore-AKAVSLAK.js → restore-I7L3EOS4.js} +1 -1
- package/dist/chunks/{rm-Q3ZXEMGI.js → rm-AWUMNWQU.js} +1 -1
- package/dist/chunks/{rm-DGMHU3CU.js → rm-HVGUK55W.js} +1 -1
- package/dist/chunks/{rm-ERBTFH3G.js → rm-M3EMCKSN.js} +1 -1
- package/dist/chunks/{rm-H2T7HB3F.js → rm-RFCTWU5R.js} +1 -1
- package/dist/chunks/{rm-TNGYCAFJ.js → rm-VREBRM5D.js} +1 -1
- package/dist/chunks/{routes-4WGST7VN.js → routes-BP54NO3Z.js} +1 -1
- package/dist/chunks/{rule-inspect-ZVHPS2PL.js → rule-inspect-SQUCRRGA.js} +1 -1
- package/dist/chunks/{schema-TSHU5NN6.js → schema-AKVIKCFT.js} +1 -1
- package/dist/chunks/{schema-YUXTIJSB.js → schema-S36QQNJP.js} +1 -1
- package/dist/chunks/{select-org-35NFUKXO.js → select-org-PRWYXAWW.js} +1 -1
- package/dist/chunks/serving-DKny38LK-UNINNTL5.js +8 -0
- package/dist/chunks/{set-PP5K4NBO.js → set-ZJAC44LV.js} +1 -1
- package/dist/chunks/{setup-and-link-ACSPPTME.js → setup-and-link-VD53JFQI.js} +1 -1
- package/dist/chunks/{skills-X5TPVYXW.js → skills-JG4MGVKX.js} +1 -1
- package/dist/chunks/{tags-6QB3FP3B.js → tags-4Y4UBA5S.js} +1 -1
- package/dist/chunks/token-3ILJZPAZ.js +7 -0
- package/dist/chunks/token-JJLQVXPI.js +7 -0
- package/dist/chunks/{token-util-JDQI37VN.js → token-util-A4BSB3KP.js} +1 -1
- package/dist/chunks/token-util-KL35LHLQ.js +7 -0
- package/dist/chunks/{types-SENK43U4.js → types-DSN6Q6HW.js} +1 -1
- package/dist/chunks/{update-QA3NLSTV.js → update-R42CXSVE.js} +1 -1
- package/dist/commands/build/index.js +1 -1
- package/dist/commands/deploy/index.js +9 -9
- package/dist/commands/dev/index.js +2 -2
- package/dist/commands/env/index.js +12 -12
- package/dist/commands/link/index.js +4 -4
- package/dist/commands/list/index.js +1 -1
- package/dist/commands/project/index.js +8 -14
- package/dist/commands-account.js +2 -2
- package/dist/commands-ai.js +4 -4
- package/dist/commands-blob.js +7 -7
- package/dist/commands-core.js +62 -32
- package/dist/commands-deployments.js +4 -4
- package/dist/commands-flags.js +3 -3
- package/dist/commands-integrations.js +1 -1
- package/dist/commands-observability.js +2 -2
- package/dist/commands-routing.js +5 -5
- package/dist/help.js +1 -1
- package/dist/index.js +5 -5
- package/dist/internal-build.js +1 -1
- package/dist/launcher-errors.js +1 -1
- package/dist/package.json +31 -30
- package/dist/version.mjs +1 -1
- package/package.json +22 -21
- package/dist/chunks/chunk-5KE67O45.js +0 -7
- package/dist/chunks/chunk-CFYTFXX6.js +0 -7
- package/dist/chunks/chunk-CUBRYWA2.js +0 -7
- package/dist/chunks/chunk-FOQD62WW.js +0 -17
- package/dist/chunks/chunk-FTACGWII.js +0 -14
- package/dist/chunks/chunk-FY4ORPZH.js +0 -7
- package/dist/chunks/chunk-G7H33IKQ.js +0 -10
- package/dist/chunks/chunk-KWXJIVBG.js +0 -7
- package/dist/chunks/chunk-MOF4QNZH.js +0 -9
- package/dist/chunks/chunk-O4CQRV2G.js +0 -7
- package/dist/chunks/chunk-UWJ3UPS5.js +0 -11
- package/dist/chunks/chunk-ZUI5EMXD.js +0 -11
- package/dist/chunks/config-CW4JO67Q.js +0 -7
- package/dist/chunks/image-VNSN2SKE.js +0 -7
- package/dist/chunks/pull-HLJSW3W4.js +0 -7
- package/dist/chunks/token-W6P2KNI5.js +0 -7
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath as __fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __dirname_ } from 'node:path';
|
|
4
|
+
const require = __createRequire(import.meta.url);
|
|
5
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = __dirname_(__filename);
|
|
7
|
+
import{require_auth_errors,require_token_error,require_token_util}from"./chunk-DIHB5BUY.js";import{DERIVED_ID,DroppedError,ExternalAuthor,ExternalEvent,ExternalTarget,Link,Origin,derivedId,displayText,extractMentions,isThreadTarget,keyId,replaceMentions,runMiddleware,sha256,waitUntil}from"./chunk-CJ6XPDSS.js";import{compileAccess,createAuthority,decodeJwt,fromTokens,hostPrincipal,issuersOf,parseScope,verifiesTokens}from"./chunk-A3C67PNC.js";import{ActorId,AttachmentId,BoardError,ChannelPath,Emoji,HttpError,LabelKey,LabelValue,Labels,MESSAGE_TEXT_MAX,MessageText,RejectedError,RemoteWatcherId,badRequest,createBoardClient,displayEmoji,duration,fileSize,forbidden,json,normalizeEmoji,notFound,platformAvatar,storable,toErrorResponse,unauthorized}from"./chunk-WFS3XBAA.js";import{require_cjs}from"./chunk-AY523IYR.js";import"./chunk-WKSXBH7X.js";import"./chunk-GD7AV7BO.js";import{external_exports}from"./chunk-2AMO2VMV.js";import{commands,filters,globalOptions,groups,options,usage}from"./chunk-SNGM6NSO.js";import"./chunk-OPORM35X.js";import"./chunk-BLUPZPAS.js";import{__commonJS,__toESM}from"./chunk-BZENIQ7N.js";var require_version=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/version.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),version_exports={};__export(version_exports,{version:()=>version});module.exports=__toCommonJS(version_exports);var version="3.8.10"}});var require_exchange_vercel_oidc_token=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/exchange-vercel-oidc-token.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),exchange_vercel_oidc_token_exports={};__export(exchange_vercel_oidc_token_exports,{exchangeVercelOidcToken:()=>exchangeVercelOidcToken3});module.exports=__toCommonJS(exchange_vercel_oidc_token_exports);var import_version=require_version(),TokenCache=class{constructor(maxEntries){this.maxEntries=maxEntries,this.entries=new Map}get(key){let entry=this.entries.get(key);if(entry!==void 0){if(entry.expiresAt<=Date.now()){this.entries.delete(key);return}return this.entries.delete(key),this.entries.set(key,entry),entry.token}}set({key,token:token2,expiresAt}){for(this.entries.delete(key),this.entries.set(key,{token:token2,expiresAt});this.entries.size>this.maxEntries;){let oldest=this.entries.keys().next().value;if(oldest===void 0)break;this.entries.delete(oldest)}}},MAX_CACHE_ENTRIES=1e3,tokenCache=new TokenCache(MAX_CACHE_ENTRIES);async function getCacheKey(options2){let input=JSON.stringify([options2.token,options2.audience,options2.jti]),digest=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(input));return Array.from(new Uint8Array(digest)).map(byte=>byte.toString(16).padStart(2,"0")).join("")}async function exchangeVercelOidcToken3(options2){let cacheKey=await getCacheKey(options2);if(!options2.skipCache){let cached=tokenCache.get(cacheKey);if(cached!==void 0)return cached}let response=await fetch("https://oidc.vercel.com/~token",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json","User-Agent":`@vercel/oidc@${import_version.version}`},body:JSON.stringify({token:options2.token,aud:options2.audience,...options2.jti?{jti:options2.jti}:void 0})});if(!response.ok)throw new Error(`Failed to exchange token: ${await readErrorMessage(response)}`);let data;try{data=await response.json()}catch{throw new Error("Failed to exchange token: response was not valid JSON")}if(!data||typeof data!="object"||!("token"in data)||typeof data.token!="string")throw new Error("Failed to exchange token: response did not contain a token");let{token:token2}=data,expiry2="expiry"in data&&typeof data.expiry=="number"?data.expiry:void 0;if(expiry2!==void 0){let expiresAt=expiry2*1e3;expiresAt>Date.now()&&tokenCache.set({key:cacheKey,token:token2,expiresAt})}return token2}async function readErrorMessage(response){try{let data=await response.json();if(data&&typeof data=="object"&&"error"in data&&typeof data.error=="string")return data.error}catch{}return response.statusText||`HTTP ${response.status}`}}});var require_get_context=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/get-context.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),get_context_exports={};__export(get_context_exports,{SYMBOL_FOR_REQ_CONTEXT:()=>SYMBOL_FOR_REQ_CONTEXT,getContext:()=>getContext2});module.exports=__toCommonJS(get_context_exports);var SYMBOL_FOR_REQ_CONTEXT=Symbol.for("@vercel/request-context");function getContext2(){return globalThis[SYMBOL_FOR_REQ_CONTEXT]?.get?.()??{}}}});var require_get_vercel_oidc_token_sync=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/get-vercel-oidc-token-sync.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),get_vercel_oidc_token_sync_exports={};__export(get_vercel_oidc_token_sync_exports,{getVercelOidcTokenSync:()=>getVercelOidcTokenSync2});module.exports=__toCommonJS(get_vercel_oidc_token_sync_exports);var import_get_context=require_get_context();function getVercelOidcTokenSync2(){let token2=(0,import_get_context.getContext)().headers?.["x-vercel-oidc-token"]??process.env.VERCEL_OIDC_TOKEN;if(!token2)throw new Error("The 'x-vercel-oidc-token' header is missing from the request.");return token2}}});var require_get_vercel_oidc_token_with_refresh=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/get-vercel-oidc-token-with-refresh.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),get_vercel_oidc_token_with_refresh_exports={};__export(get_vercel_oidc_token_with_refresh_exports,{getVercelOidcToken:()=>getVercelOidcToken4});module.exports=__toCommonJS(get_vercel_oidc_token_with_refresh_exports);var import_exchange_vercel_oidc_token=require_exchange_vercel_oidc_token(),import_get_vercel_oidc_token_sync=require_get_vercel_oidc_token_sync(),import_token_error=require_token_error();async function getVercelOidcToken4(options2){let token2="",err;try{token2=(0,import_get_vercel_oidc_token_sync.getVercelOidcTokenSync)()}catch(error){err=error}try{let[{getTokenPayload,isExpired},{refreshToken}]=await Promise.all([await import("./token-util-A4BSB3KP.js"),await import("./token-JJLQVXPI.js")]);(!token2||isExpired(getTokenPayload(token2),options2?.expirationBufferMs))&&(await refreshToken(options2),token2=(0,import_get_vercel_oidc_token_sync.getVercelOidcTokenSync)())}catch(error){let message=err instanceof Error?err.message:"";throw error instanceof Error&&(message=`${message}
|
|
8
|
+
${error.message}`),message?new import_token_error.VercelOidcTokenError(message):error}return options2?.audience&&(token2=await(0,import_exchange_vercel_oidc_token.exchangeVercelOidcToken)({token:token2,audience:options2.audience,jti:options2.jti,skipCache:options2.skipCache})),token2}}});var require_verify_vercel_oidc_token=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/verify-vercel-oidc-token.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),verify_vercel_oidc_token_exports={};__export(verify_vercel_oidc_token_exports,{verifyVercelOidcToken:()=>verifyVercelOidcToken2});module.exports=__toCommonJS(verify_vercel_oidc_token_exports);var import_jose3=require_cjs(),VERCEL_OIDC_ISSUER="https://oidc.vercel.com",VERCEL_OIDC_JWKS_URL=new URL("https://oidc.vercel.com/.well-known/jwks"),DEFAULT_ALGORITHMS=["RS256"],VERCEL_OIDC_JWKS=(0,import_jose3.createRemoteJWKSet)(VERCEL_OIDC_JWKS_URL);async function verifyVercelOidcToken2(token2,options2){let{algorithms,projectId=process.env.VERCEL_PROJECT_ID,environment:environment3=process.env.VERCEL_TARGET_ENV||process.env.VERCEL_ENV,ownerId,...verifyOptions}=options2??{};if(projectId==="*"&&ownerId===void 0&&!hasAudienceVerification(verifyOptions.audience))throw new TypeError("Expected ownerId or audience to be provided when projectId is '*'.");let result2=await(0,import_jose3.jwtVerify)(token2,VERCEL_OIDC_JWKS,{...verifyOptions,algorithms:algorithms??DEFAULT_ALGORITHMS});return validateIssuer(result2.payload.iss),validateClaim({actual:result2.payload.project_id,claim:"project_id",env:"VERCEL_PROJECT_ID",expected:projectId,option:"projectId"}),validateClaim({actual:result2.payload.environment,claim:"environment",env:"VERCEL_TARGET_ENV or VERCEL_ENV",expected:environment3,option:"environment"}),validateOptionalClaim({actual:result2.payload.owner_id,claim:"owner_id",expected:ownerId}),result2}function hasAudienceVerification(audience){return Array.isArray(audience)?audience.length>0:audience!==void 0}function validateIssuer(actual){if(actual!==VERCEL_OIDC_ISSUER&&(typeof actual!="string"||!actual.startsWith(`${VERCEL_OIDC_ISSUER}/`)))throw new TypeError(`Expected Vercel OIDC token iss claim to be "${VERCEL_OIDC_ISSUER}" or to start with "${VERCEL_OIDC_ISSUER}/".`)}function validateClaim({actual,claim,env,expected,option}){if(expected!=="*"){if(expected===void 0||expected.length===0)throw new TypeError(`Expected ${env} to be set or ${option} to be provided. Pass ${option}: '*' to allow any ${claim} claim.`);if(!(Array.isArray(expected)&&typeof actual=="string"&&expected.includes(actual))&&actual!==expected)throw new TypeError(Array.isArray(expected)?`Expected Vercel OIDC token ${claim} claim to be one of: ${expected.map(value=>`"${value}"`).join(", ")}.`:`Expected Vercel OIDC token ${claim} claim to be "${expected}".`)}}function validateOptionalClaim({actual,claim,expected}){if(expected!==void 0&&actual!==expected)throw new TypeError(`Expected Vercel OIDC token ${claim} claim to be "${expected}".`)}}});var require_dist=__commonJS({"../../node_modules/.pnpm/@vercel+oidc@3.8.10/node_modules/@vercel/oidc/dist/index.js"(exports,module){"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),src_exports={};__export(src_exports,{AccessTokenMissingError:()=>import_auth_errors.AccessTokenMissingError,RefreshAccessTokenFailedError:()=>import_auth_errors.RefreshAccessTokenFailedError,exchangeVercelOidcToken:()=>import_exchange_vercel_oidc_token.exchangeVercelOidcToken,getContext:()=>import_get_context.getContext,getVercelOidcToken:()=>import_get_vercel_oidc_token_with_refresh.getVercelOidcToken,getVercelOidcTokenSync:()=>import_get_vercel_oidc_token_sync.getVercelOidcTokenSync,getVercelToken:()=>import_token_util.getVercelToken,verifyVercelOidcToken:()=>import_verify_vercel_oidc_token.verifyVercelOidcToken});module.exports=__toCommonJS(src_exports);var import_get_vercel_oidc_token_with_refresh=require_get_vercel_oidc_token_with_refresh(),import_get_vercel_oidc_token_sync=require_get_vercel_oidc_token_sync(),import_get_context=require_get_context(),import_verify_vercel_oidc_token=require_verify_vercel_oidc_token(),import_auth_errors=require_auth_errors(),import_exchange_vercel_oidc_token=require_exchange_vercel_oidc_token(),import_token_util=require_token_util()}});var Ref=external_exports.string().min(1).max(128),Revision=external_exports.number().int().positive(),id=Ref,repeated=external_exports.union([external_exports.string(),external_exports.array(external_exports.string())]).transform(value=>Array.isArray(value)?value:[value]),LabelFilter=external_exports.string().regex(/^[a-z0-9][a-z0-9_.-]*(=.*)?$/,"label filters look like key, key=value, or key=a|b").transform(value=>{let at=value.indexOf("=");return at===-1?[value,null]:[value.slice(0,at),value.slice(at+1).split("|")]}),ResolvedSyncTarget=external_exports.object({target:ExternalTarget,kind:external_exports.enum(["channel","thread"])}),Filters=external_exports.object({channel:external_exports.string().optional(),re:id.optional(),by:external_exports.string().optional(),mentions:external_exports.string().optional(),label:repeated.pipe(external_exports.array(LabelFilter)).optional(),from:external_exports.string().min(1).max(64).optional(),text:external_exports.string().min(1).max(200).optional()}),MessagesQuery=Filters.extend({after:Ref.optional(),before:id.optional(),limit:external_exports.coerce.number().int().min(1).max(500).optional()}),ThreadsQuery=Filters.extend({after:Ref.optional(),limit:external_exports.coerce.number().int().min(1).max(500).optional()}),EventsQuery=Filters.extend({after:Ref.optional(),wait:external_exports.coerce.number().min(0).max(300).optional(),all:external_exports.coerce.boolean().optional()}),PostBody=external_exports.object({channel:ChannelPath.optional(),re:Ref.optional(),text:external_exports.string().max(MESSAGE_TEXT_MAX).default(""),labels:Labels.optional(),noSync:external_exports.boolean().optional(),sync:external_exports.array(external_exports.union([external_exports.string().min(3).max(300),ResolvedSyncTarget])).max(10).optional(),id:external_exports.string().min(1).max(200).refine(storable,"id has a NUL character or a lone surrogate").optional(),attachments:external_exports.array(AttachmentId).max(10).optional(),after:Ref.optional()}).refine(body=>body.text.trim().length>0||(body.attachments?.length??0)>0,{message:"message text is empty",path:["text"]}).refine(body=>body.channel===void 0!=(body.re===void 0),{message:"a post needs `channel`; a reply needs `re`"}).refine(body=>!(body.noSync&&body.sync?.length),{message:"`noSync` and `sync` contradict each other"}).refine(body=>body.after===void 0||body.re!==void 0,{message:"`after` is for replies: it names the newest message in `re`'s thread",path:["after"]}),Duration=external_exports.string().regex(/^\d+(\.\d+)?(s|m|h|d)$/,"durations look like 30s, 30m, 12h, or 1d"),Instant=external_exports.string().datetime({offset:!0}),ScheduleBody=external_exports.object({channel:ChannelPath.optional(),re:Ref.optional(),text:external_exports.string().max(MESSAGE_TEXT_MAX).default(""),labels:Labels.optional(),noSync:external_exports.boolean().optional(),attachments:external_exports.array(AttachmentId).max(10).optional(),in:Duration.optional(),at:Instant.optional(),every:Duration.optional(),until:Instant.optional(),for:Duration.optional(),times:external_exports.number().int().positive().optional()}).refine(body=>body.text.trim().length>0||(body.attachments?.length??0)>0,{message:"message text is empty",path:["text"]}).refine(body=>body.channel===void 0!=(body.re===void 0),{message:"a schedule needs `channel` for a new thread, or `re` for a reply"}).refine(body=>body.in!==void 0||body.at!==void 0||body.every!==void 0,{message:"say when: `in`, `at`, or `every`"}).refine(body=>!(body.in!==void 0&&body.at!==void 0),{message:"`in` and `at` contradict each other"}).refine(body=>[body.until,body.for,body.times].filter(v=>v!==void 0).length<=1,{message:"pick one stop: `until`, `for`, or `times`"}).refine(body=>body.every!==void 0||body.until===void 0&&body.for===void 0&&body.times===void 0,{message:"`until`, `for`, and `times` stop a repeat: add `every`"}),SchedulesQuery=external_exports.object({re:id.optional(),channel:external_exports.string().optional(),by:external_exports.string().optional(),all:external_exports.coerce.boolean().optional()}),SyncBody=external_exports.object({target:external_exports.string().min(3).max(300)}),InboxQuery=external_exports.object({channel:external_exports.string().optional(),limit:external_exports.coerce.number().int().min(1).max(500).optional()}),Registering=external_exports.object({watch:external_exports.record(external_exports.string().max(100),external_exports.object({tail:external_exports.string().max(1e3),when:external_exports.enum(["matches","enters"]).default("matches")})).refine(watch=>Object.keys(watch).length<=32,"a board registers at most 32 watchers"),sync:external_exports.array(external_exports.object({key:external_exports.string().min(1).max(64),platform:external_exports.string().min(1).max(64),signIn:external_exports.boolean().default(!1)})).max(32).optional()}),RegisterBody=external_exports.union([Registering.extend({url:external_exports.string().url().max(500).refine(url=>/^https:\/\//.test(url),"wake-ups go over https").transform(url=>url.replace(/\/+$/,"")).nullable().optional(),path:external_exports.null({error:"register a url or a path, not both"}).optional(),branch:external_exports.null({error:"a branch goes with a path"}).optional()}),Registering.extend({url:external_exports.null({error:"register a url or a path, not both"}).optional(),path:external_exports.string().max(300).regex(/^\/[^?#]*$/,"a path starts with / and has no query or fragment").transform(path=>path.replace(/(.)\/+$/,"$1")),branch:external_exports.string().min(1).max(255).nullable().optional()})]),MatchesQuery=external_exports.object({limit:external_exports.coerce.number().int().min(1).max(50).optional(),wait:external_exports.coerce.number().min(0).max(25).optional()}),AckBody=external_exports.object({lease:external_exports.string().min(1).max(64),error:external_exports.string().min(1).max(500).optional(),skip:external_exports.boolean().optional()}),SourceReadQuery=external_exports.object({target:external_exports.string().min(1).max(4e3),limit:external_exports.coerce.number().int().min(1).max(200).optional(),after:external_exports.string().min(1).max(400).optional()}),SourceJoinBody=external_exports.object({target:external_exports.string().min(1).max(4e3)}),LinkedQuery=external_exports.object({url:external_exports.string().url().max(4e3)}),IdentityUnlinkBody=external_exports.object({actor:external_exports.string().min(1).max(256)}),SyncKey=external_exports.string().min(1).max(64),ImportOrigin=external_exports.object({thread:ExternalTarget,message:external_exports.string().min(1).max(300).optional()}),ImportLink=Link.extend({message:Ref.nullable()}),PlatformUser=ExternalAuthor.extend({avatar:external_exports.string().nullable().transform(platformAvatar)}),Imported={author:PlatformUser,from:SyncKey.nullable()},ImportSource=external_exports.object({key:SyncKey,channel:ExternalTarget,thread:ExternalTarget}),Changed={origin:ImportOrigin.optional(),message:Ref.optional()},SyncImportBody=external_exports.discriminatedUnion("op",[external_exports.object({op:external_exports.literal("post"),...Imported,origin:Origin.nullable().default(null),source:ImportSource.optional(),channel:ChannelPath.optional(),re:Ref.optional(),text:external_exports.string().max(MESSAGE_TEXT_MAX).default(""),labels:Labels.optional(),noSync:external_exports.boolean().optional(),attachments:external_exports.array(AttachmentId).max(10).optional(),links:external_exports.array(ImportLink).max(10).optional(),id:external_exports.string().min(1).max(200).refine(storable,"id has a NUL character or a lone surrogate").optional(),after:Ref.optional()}),external_exports.object({op:external_exports.literal("edit"),...Imported,...Changed,text:MessageText,source:ImportSource.optional(),revision:Revision.optional()}),external_exports.object({op:external_exports.literal("delete"),...Imported,...Changed,revision:Revision.optional()}),external_exports.object({op:external_exports.literal("react"),...Imported,...Changed,emoji:Emoji,remove:external_exports.boolean().optional()}),external_exports.object({op:external_exports.literal("labels"),...Imported,...Changed,set:Labels.optional(),unset:external_exports.array(LabelKey).optional(),if:external_exports.record(LabelKey,LabelValue.nullable()).optional()}),external_exports.object({op:external_exports.literal("links"),...Imported,...Changed,links:external_exports.array(ImportLink).max(10)}),external_exports.object({op:external_exports.literal("seen"),author:PlatformUser})]),PlatformFiles=external_exports.array(external_exports.string().min(1).max(300)).max(10).nullable(),SyncReceiptBody=external_exports.discriminatedUnion("kind",[external_exports.object({kind:external_exports.literal("message"),message:Ref,target:ExternalTarget,external:external_exports.object({thread:ExternalTarget,message:external_exports.string().min(1).max(300),asUser:external_exports.literal(!0).optional()}),files:PlatformFiles.optional()}),external_exports.object({kind:external_exports.literal("files"),message:Ref,target:ExternalTarget,files:PlatformFiles}),external_exports.object({kind:external_exports.literal("failed"),message:Ref,target:ExternalTarget,error:external_exports.string().min(1).max(500)}),external_exports.object({kind:external_exports.literal("thread"),message:Ref,target:ExternalTarget,targetKind:external_exports.enum(["channel","thread"]),cause:external_exports.enum(["config","request"])})]),SyncFindQuery=external_exports.object({code:external_exports.string().min(1).max(64).optional(),post:external_exports.string().min(1).max(200).optional(),thread:ExternalTarget.optional(),message:external_exports.string().min(1).max(300).optional(),account:ActorId.optional(),person:ActorId.optional()}),SyncCopiesQuery=external_exports.object({message:repeated.pipe(external_exports.array(Ref).min(1).max(100))}),SyncRootsQuery=external_exports.object({by:ActorId,since:external_exports.coerce.number().int().nonnegative()}),InboxPushBody=external_exports.object({events:external_exports.array(external_exports.object({id:external_exports.string().min(1).max(300),event:ExternalEvent,raw:external_exports.unknown()})).min(1).max(50)}),InboxNextQuery=external_exports.object({limit:external_exports.coerce.number().int().min(1).max(50).optional()}),InboxAckBody=external_exports.object({lease:external_exports.string().min(1).max(64),error:external_exports.string().min(1).max(500).optional()}),SyncCodeBody=external_exports.object({sync:SyncKey}),SyncAccountBody=external_exports.object({sync:SyncKey,account:PlatformUser,code:external_exports.string().min(1).max(64),message:external_exports.string().max(1024).optional(),method:external_exports.enum(["code","consent"]).optional()}),SignInQuery=external_exports.object({sync:SyncKey,code:external_exports.string().min(1).max(64)}),SyncUnlinkBody=external_exports.object({account:ActorId}),UploadStartBody=external_exports.object({name:external_exports.string().max(1024).nullable().optional(),type:external_exports.string().max(1024).nullable().optional(),size:external_exports.number().int().positive().max(104857600,`direct uploads are at most ${104857600/1024/1024} MB`),sha256:external_exports.string().regex(/^[A-Za-z0-9+/]{43}=$/,"sha256 is the base64 SHA-256 of the bytes").optional()}),EditBody=external_exports.object({text:MessageText,revision:Revision.optional()}),DeleteBody=external_exports.object({revision:Revision.optional()}).optional(),TypingBody=external_exports.object({status:external_exports.string().trim().min(1).max(100).optional()}).optional(),ReactBody=external_exports.object({emoji:Emoji,remove:external_exports.boolean().optional()}),LabelBody=external_exports.object({set:Labels.optional(),unset:external_exports.array(LabelKey).optional(),if:external_exports.record(LabelKey,LabelValue.nullable()).refine(expected=>Object.keys(expected).length<=16,"at most 16 labels").optional()}).refine(body=>body.set!==void 0||body.unset!==void 0,{message:"nothing to change: pass `set` or `unset`"}),routes={discovery:{method:"GET",path:"/"},me:{method:"GET",path:"/me"},channels:{method:"GET",path:"/channels"},actors:{method:"GET",path:"/actors"},messages:{method:"GET",path:"/messages",query:MessagesQuery},message:{method:"GET",path:"/messages/:id"},threads:{method:"GET",path:"/threads",query:ThreadsQuery},post:{method:"POST",path:"/messages",body:PostBody},edit:{method:"POST",path:"/messages/:id/edit",body:EditBody},delete:{method:"POST",path:"/messages/:id/delete",body:DeleteBody},react:{method:"POST",path:"/messages/:id/reactions",body:ReactBody},label:{method:"POST",path:"/messages/:id/labels",body:LabelBody},sync:{method:"POST",path:"/messages/:id/sync",body:SyncBody},synced:{method:"GET",path:"/messages/:id/sync"},linked:{method:"GET",path:"/links",query:LinkedQuery},typing:{method:"POST",path:"/messages/:id/typing",body:TypingBody},schedule:{method:"POST",path:"/schedules",body:ScheduleBody},schedules:{method:"GET",path:"/schedules",query:SchedulesQuery},cancelSchedule:{method:"POST",path:"/schedules/:id/cancel"},posted:{method:"GET",path:"/posts/:id"},inbox:{method:"GET",path:"/inbox/:key",query:InboxQuery},inboxPush:{method:"POST",path:"/inbox/:key",body:InboxPushBody},inboxNext:{method:"GET",path:"/inbox/:key/next",query:InboxNextQuery},inboxAck:{method:"POST",path:"/inbox/:key/:id/ack",body:InboxAckBody},syncImport:{method:"POST",path:"/sync/imports",body:SyncImportBody},syncReceipt:{method:"POST",path:"/sync/receipts",body:SyncReceiptBody},syncThread:{method:"GET",path:"/sync/threads/:thread"},syncFind:{method:"GET",path:"/sync/find",query:SyncFindQuery},syncCopies:{method:"GET",path:"/sync/copies",query:SyncCopiesQuery},syncRoots:{method:"GET",path:"/sync/roots",query:SyncRootsQuery},syncCode:{method:"POST",path:"/sync/codes",body:SyncCodeBody},syncAccount:{method:"POST",path:"/sync/accounts",body:SyncAccountBody},syncUnlink:{method:"POST",path:"/sync/accounts/unlink",body:SyncUnlinkBody},readSource:{method:"GET",path:"/sources/:key/read",query:SourceReadQuery},joinSource:{method:"POST",path:"/sources/:key/join",body:SourceJoinBody},webhook:{method:"POST",path:"/webhooks/:key"},events:{method:"GET",path:"/events",query:EventsQuery},avatar:{method:"GET",path:"/avatars/:name"},upload:{method:"POST",path:"/attachments"},attachment:{method:"GET",path:"/attachments/:id"},uploadStart:{method:"POST",path:"/attachments/uploads",body:UploadStartBody},uploadFinish:{method:"POST",path:"/attachments/uploads/:id"},openapi:{method:"GET",path:"/openapi.json"},identity:{method:"GET",path:"/identity"},signIn:{method:"GET",path:"/identity/sign-in",query:SignInQuery},signedIn:{method:"GET",path:"/identity/signed-in",query:SignInQuery},identityUnlink:{method:"POST",path:"/identity/unlink",body:IdentityUnlinkBody},watchers:{method:"GET",path:"/watchers"},register:{method:"POST",path:"/watchers",body:RegisterBody},matches:{method:"GET",path:"/watchers/matches",query:MatchesQuery},ack:{method:"POST",path:"/watchers/matches/:id/ack",body:AckBody},wake:{method:"POST",path:"/wake"}},SYNC="@sync",VERSION="0.30.0",BOARD_ROUTES=["avatar","openapi","webhook","signIn","signedIn","identityUnlink","sync"],METHODS=["discovery","me","channels","actors","identity","messages","threads","message","post","posted","edit","delete","react","label","schedule","schedules","cancelSchedule","upload","attachment","uploadStart","uploadFinish","watchers","register","matches","ack","events","syncImport","syncReceipt","syncThread","syncFind","syncCopies","syncRoots","inboxPush","inboxNext","inboxAck","inbox","syncCode","syncAccount","syncUnlink","typing","readSource","joinSource","synced","linked"],escape=literal=>literal.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function compile(method,path,target,own2){let parts=path.split("/").filter(Boolean),source=parts.map(part=>part.startsWith(":")?"/([^/]+)":`/${escape(part)}`).join("");return{method,pattern:new RegExp(`${source}$`),names:parts.filter(part=>part.startsWith(":")).map(part=>part.slice(1)),target,segments:parts.length,literals:parts.filter(part=>!part.startsWith(":")).length,own:own2}}function createRouter(backendRoutes,basePath){let patterns=[...BOARD_ROUTES.map(name=>compile(routes[name].method,routes[name].path,{kind:"board",name},!1)),...METHODS.filter(name=>name!=="discovery").map(name=>compile(routes[name].method,routes[name].path,{kind:"protocol",name},!1)),...backendRoutes.map(route=>compile(route.method,route.path,{kind:"backend",route},!0))].sort((a,b)=>b.segments-a.segments||b.literals-a.literals||Number(b.own)-Number(a.own)),found=(candidate,base,values)=>{let params={};try{candidate.names.forEach((name,i)=>params[name]=decodeURIComponent(values[i]))}catch{return null}let first=candidate.names[0];return{target:candidate.target,base,param:first===void 0?null:params[first],params}},discovery=base=>({target:{kind:"protocol",name:"discovery"},base,param:null,params:{},root:!0});return function(method,pathname){let path=pathname.length>1?pathname.replace(/\/+$/,""):pathname;if(basePath!==void 0){let base=basePath.replace(/\/+$/,"");if(path!==base&&!path.startsWith(`${base}/`))return null;let rest=path.slice(base.length)||"/";if(rest==="/")return method==="GET"?discovery(base):null;for(let candidate of patterns){let hit=candidate.method===method?rest.match(new RegExp(`^${candidate.pattern.source}`)):null;if(hit)return found(candidate,base,hit.slice(1))}return null}for(let candidate of patterns){if(candidate.method!==method)continue;let hit=path.match(candidate.pattern);if(hit)return found(candidate,path.slice(0,hit.index),hit.slice(1))}return method==="GET"?discovery(path==="/"?"":path):null}}function queryObject(url){let out2={};for(let key of new Set(url.searchParams.keys())){let values=url.searchParams.getAll(key);out2[key]=values.length>1?values:values[0]}return out2}async function readJson(request){let text2=await request.text();if(text2.trim()!=="")try{return JSON.parse(text2)}catch{throw badRequest("request body is not valid JSON")}}var tooLarge=()=>new HttpError(413,"too_large",`attachments are at most ${4194304/1024/1024} MB`);async function readCapped(request,max){if(Number(request.headers.get("content-length")??0)>max)throw tooLarge();let chunks=[],size=0;for await(let chunk of request.body??[]){if(size+=chunk.byteLength,size>max)throw tooLarge();chunks.push(chunk)}let body=new Uint8Array(size),at=0;for(let chunk of chunks)body.set(chunk,at),at+=chunk.byteLength;return body}function openapi(){let paths={};for(let[name,definition]of Object.entries(routes)){let path=definition.path.replace(/:(\w+)/g,"{$1}"),operation={operationId:name};"query"in definition&&(operation["x-query-schema"]=external_exports.toJSONSchema(definition.query,{io:"input"})),"body"in definition&&(operation.requestBody={content:{"application/json":{schema:external_exports.toJSONSchema(definition.body,{io:"input"})}}}),(paths[path]??={})[definition.method.toLowerCase()]=operation}return{openapi:"3.1.0",info:{title:"board",version:VERSION},paths}}var FILTER_FLAGS={re:options.re,by:options.by,mentions:options.mentions,label:options.label,source:options.source,text:options.text};function withMentions(args){let out2=[];for(let i=0;i<args.length;i++){let arg=args[i];arg!=="--mentions"?out2.push(arg):args[i+1]?.startsWith("@")?out2.push(`--mentions=${args[++i]}`):out2.push("--mentions=me")}return out2}function words(expression){let out2=[],word=null,quote=null;for(let i=0;i<expression.length;i++){let char=expression[i];quote?char===quote?quote=null:char==="\\"&"e==='"'&&i+1<expression.length?word+=expression[++i]:word+=char:/\s/.test(char)?(word!==null&&out2.push(word),word=null):char==='"'||char==="'"?(quote=char,word??=""):char==="\\"&&i+1<expression.length?word=(word??"")+expression[++i]:word=(word??"")+char}if(quote)throw new Error(`unterminated ${quote}`);return word!==null&&out2.push(word),out2}var TAIL_FLAGS={...FILTER_FLAGS,all:{type:"boolean"}};function parseFilterFlags(args){let values={},positionals2=[];for(let i=0;i<args.length;i++){let arg=args[i];if(arg==="--"){positionals2.push(...args.slice(i+1));break}if(!arg.startsWith("-")||arg==="-"){positionals2.push(arg);continue}let eq=arg.indexOf("="),name=arg.startsWith("--")?arg.slice(2,eq===-1?void 0:eq):null,spec=name!==null&&Object.hasOwn(TAIL_FLAGS,name)?TAIL_FLAGS[name]:null;if(!spec)throw new Error(`${name!==null?`--${name}`:arg.slice(0,2)} isn't a filter`);if(spec.type==="boolean"){if(eq!==-1)throw new Error(`--${name} takes no value`);values[name]=!0;continue}let value;if(eq!==-1)value=arg.slice(eq+1);else{let next=args[i+1];if(next===void 0||next.length>1&&next.startsWith("-"))throw new Error(`--${name} needs a value`);value=next,i++}"multiple"in spec?values[name]=[...values[name]??[],value]:values[name]=value}return{values,positionals:positionals2}}function parseWatchTail(expression){let{values,positionals:positionals2}=parseFilterFlags(withMentions(words(expression)));if(positionals2.length>1)throw new Error(`expected one channel, got: ${positionals2.join(" ")}`);let channel=positionals2[0],parsed=Filters.safeParse({...channel?{channel}:{},...values.re?{re:values.re.replace(/^#/,"")}:{},...values.by?{by:values.by.replace(/^@/,"")}:{},...values.mentions?{mentions:values.mentions.replace(/^@/,"")}:{},...values.label?.length?{label:values.label}:{},...values.source?{from:values.source}:{},...values.text?{text:values.text}:{}});if(!parsed.success){let issue=parsed.error.issues[0];throw new Error(`${issue?.path.length?`--${String(issue.path[0])}: `:""}${issue?.message??"invalid filters"}`)}return{filters:parsed.data,all:values.all===!0}}var hasCriteria=filters3=>filters3.channel!==void 0||filters3.re!==void 0||filters3.by!==void 0||filters3.mentions!==void 0||filters3.from!==void 0||filters3.text!==void 0||(filters3.label?.length??0)>0;function compileWatcher(id2,spec){let filters3,all;try{({filters:filters3,all}=parseWatchTail(spec.tail))}catch(error){throw new Error(`watch ${id2}: ${error.message}`)}if(filters3.mentions==="me")throw new Error(`watch ${id2}: a watcher has no "me": use --mentions @handle`);let criteria=hasCriteria(filters3);if(spec.when==="enters"&&!criteria)throw new Error(`watch ${id2}: \`when: enters\` needs a filter to enter`);return{filters:filters3,criteria,enters:spec.when==="enters",all}}var byCodeUnit=(a,b)=>a<b?-1:a>b?1:0;function canonicalWatchers(watch,sync=[]){let canonical2=Object.keys(watch).sort(byCodeUnit).map(id2=>[id2,watch[id2].tail,watch[id2].when]);return sync.length>0&&canonical2.push(["sync",[...sync].sort((a,b)=>byCodeUnit(a.key,b.key)).map(entry=>[entry.key,entry.platform,entry.signIn])]),JSON.stringify(canonical2)}import{execFile}from"node:child_process";import{createHash}from"node:crypto";import{mkdir,readFile,readdir,stat,unlink,writeFile}from"node:fs/promises";import{existsSync,readFileSync,statSync}from"node:fs";import{homedir}from"node:os";import{dirname,join,resolve}from"node:path";import{promisify}from"node:util";var import_oidc=__toESM(require_dist(),1),configDir=()=>join(process.env.XDG_CONFIG_HOME||join(homedir(),".config"),"board");function isCheckout(directory){let git=join(directory,".git");try{return statSync(git).isDirectory()?existsSync(join(git,"HEAD")):readFileSync(git,"utf8").startsWith("gitdir:")}catch{return!1}}function vercelProjectFile(cwd=process.cwd()){let directory=resolve(cwd);for(;;){let file=join(directory,".vercel","project.json");if(existsSync(file))return file;let parent=dirname(directory);if(isCheckout(directory)||parent===directory)return null;directory=parent}}var LOOPBACK=new Set(["127.0.0.1","localhost","[::1]"]),PROJECT_ID=/^prj_[A-Za-z0-9]{1,64}$/,URL_SHAPE=/^https?:\/\/[A-Za-z0-9.-]{1,253}(:\d{1,5})?(\/[A-Za-z0-9._~%/-]{0,2000})?$/,shown=(value,shape)=>typeof value=="string"&&shape.test(value)?value:void 0;function localTokenFile(url){let normalized=url.replace(/\/+$/,"");return join(configDir(),"local",`${createHash("sha256").update(normalized).digest("hex").slice(0,32)}.json`)}function localToken(url){try{if(!LOOPBACK.has(new URL(url).hostname))return null;let saved=JSON.parse(readFileSync(localTokenFile(url),"utf8"));return saved.url!==url.replace(/\/+$/,"")||typeof saved.token!="string"?null:{token:saved.token,handle:saved.handle??null}}catch{return null}}function detectAgent(env=process.env){let found=[];(env.CLAUDECODE==="1"||env.CLAUDE_CODE_SESSION_ID)&&found.push({profile:"claude",session:env.CLAUDE_CODE_SESSION_ID||null}),(env.CODEX_THREAD_ID||env.CODEX_SESSION_ID)&&found.push({profile:"codex",session:env.CODEX_THREAD_ID||env.CODEX_SESSION_ID||null}),env.GROK_SESSION_ID&&found.push({profile:"grok",session:env.GROK_SESSION_ID}),env.CURSOR_AGENT==="1"&&found.push({profile:"cursor",session:null}),(env.PI_CODING_AGENT==="true"||env.AI_AGENT==="pi")&&found.push({profile:"pi",session:env.PI_SESSION_ID||null});let named2=env.AI_AGENT?.trim().toLowerCase();if(named2&&/^[a-z][a-z0-9-]{0,31}$/.test(named2)&&!found.some(agent=>agent.profile===named2)&&found.push({profile:named2,session:null}),found.length>1)throw new BoardError("ambiguous_agent",`Several coding agents' environments are set (${found.map(a=>a.profile).join(", ")}), so it can't tell which one is writing.`,0,{hint:"Pick one with a profile, or none to write as yourself."});return found[0]??null}var expiry=token2=>{try{let{exp}=decodeJwt(token2);return exp?exp*1e3:Date.now()+3e5}catch{return Date.now()+3e5}},cacheFile=(audience,login)=>{let key=login?`${audience}
|
|
9
|
+
${login}`:audience;return join(configDir(),"tokens",`${createHash("sha256").update(key).digest("hex").slice(0,32)}.json`)};async function readCache(audience,login){try{let cached=JSON.parse(await readFile(cacheFile(audience,login),"utf8"));return cached.expiresAt-3e5>Date.now()?cached:null}catch{return null}}async function writeCache(audience,login,cached){let tokens=join(configDir(),"tokens");await mkdir(tokens,{recursive:!0,mode:448}),await writeFile(cacheFile(audience,login),JSON.stringify(cached),{mode:384});let dead=Date.now()-864e5;for(let name of await readdir(tokens).catch(()=>[])){let file=join(tokens,name),written=(await stat(file).catch(()=>null))?.mtimeMs;written!==void 0&&written<dead&&await unlink(file).catch(()=>{})}}var handed=null;function handLogin(login){handed=login}var handedFetch=null;function handFetch(send){handedFetch=send}function asYou(input,init,login,send=fetch){if(handedFetch&&loginHanded())return handedFetch(input,init);let headers=new Headers(input instanceof Request?input.headers:void 0);return new Headers(init.headers).forEach((value,name)=>headers.set(name,value)),headers.set("authorization",`Bearer ${login}`),send(input,{...init,headers})}var loginHanded=()=>handed!==null||!!process.env.VERCEL_AUTH_TOKEN,vercelLogin=()=>handed?handed():process.env.VERCEL_AUTH_TOKEN?Promise.resolve(process.env.VERCEL_AUTH_TOKEN):(0,import_oidc.getVercelToken)();async function handedToken(){if(!loginHanded())return null;try{return await vercelLogin()}catch{throw new BoardError("not_logged_in","You're not logged in to Vercel.",0,{hint:"Run vercel login."})}}var apiLogin=async handed3=>handed3??await(0,import_oidc.getVercelToken)().catch(()=>null);async function personToken(audience,discovery){let login=await handedToken(),cached=await readCache(audience,login);if(cached&&cached.name===void 0){let authToken2=await apiLogin(login),user2=authToken2?await vercelUser(authToken2):null,filled={...cached,name:user2?.name??null,avatar:user2?.avatar??null};return await writeCache(audience,login,filled).catch(()=>{}),filled}if(cached)return cached;if(!discovery.project_id)throw new BoardError("no_project",`The board at ${audience} doesn't know its Vercel project, so there's no Vercel login token to mint for it.`,0,{hint:"Pass a token it accepts in BOARD_TOKEN."});let authToken=await apiLogin(login),project=await boardProject(audience,discovery,authToken),development;try{if(!authToken)throw new Error("no Vercel CLI credentials file");let response=await asYou(`https://api.vercel.com/v1/projects/${project.id}/token?source=board${project.team?`&teamId=${project.team}`:""}`,{method:"POST"},authToken);if(response.status===403||response.status===404)throw new BoardError("no_access","Your Vercel account can't access this board's project.",response.status,{hint:"Ask someone on the project's team for access to it."});if(!response.ok)throw new Error(`${response.status} ${response.statusText}`);development=(await response.json()).token}catch(error){if(error instanceof BoardError||login)throw error;development=await tokenFromVercelCli(project)}let token2=await(0,import_oidc.exchangeVercelOidcToken)({token:development,audience}),user=authToken?await vercelUser(authToken):null,fresh={token:token2,expiresAt:expiry(token2),handle:user?.handle??null,name:user?.name??null,avatar:user?.avatar??null};return await writeCache(audience,login,fresh).catch(()=>{}),fresh}async function boardProject(url,discovery,authToken){let{host,hostname}=new URL(url);if(LOOPBACK.has(hostname))return{id:discovery.project_id,team:discovery.team_id};let found=await findProject(authToken?async(method,path)=>{let response=await asYou(`https://api.vercel.com${path}`,{method},authToken);return response.ok?await response.json():null}:vercelCliApi,host);if(!found)throw new BoardError("no_access",`Your Vercel login can't see the project ${host} belongs to.`,0,{hint:"Log in as someone on the project's team (vercel login), or ask for access to it."});if(found.projectId!==discovery.project_id)throw new BoardError("wrong_project",`The board at ${url} says its project is ${shown(discovery.project_id,PROJECT_ID)??"another one"}, but it's a deployment of ${found.projectId}, so no token is minted for it.`,0);return{id:found.projectId,team:found.ownerId.startsWith("team_")?found.ownerId:null}}async function tokenFromVercelCli(project){let args=["project","token",project.id,"--format=json"];project.team&&args.push("--scope",project.team);try{let{stdout}=await promisify(execFile)("vercel",args,{timeout:3e4});return JSON.parse(stdout).token}catch{throw new BoardError("not_logged_in","You're not logged in to Vercel.",0,{hint:"Run vercel login."})}}async function vercelUser(authToken){try{let response=await asYou("https://api.vercel.com/v2/user",{},authToken);if(!response.ok)return null;let{user}=await response.json();return{handle:user?.username?.toLowerCase()??null,name:user?.name?.trim()||null,avatar:user?.avatar&&/^[0-9a-f]+$/i.test(user.avatar)?`https://vercel.com/api/www/avatar/${user.avatar}?s=128`:null}}catch{return null}}async function githubActionsToken(audience){let url=new URL(process.env.ACTIONS_ID_TOKEN_REQUEST_URL);url.searchParams.set("audience",audience);let response=await fetch(url,{headers:{authorization:`bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`}});if(!response.ok)throw new BoardError("no_token",`GitHub Actions refused an OIDC token (${response.status}).`,0,{hint:"Give the job `permissions: id-token: write`."});return(await response.json()).value}function boardUrl(){let url=process.env.BOARD_URL?.replace(/\/+$/,"");if(!url)throw new BoardError("no_board","There's no board URL: BOARD_URL isn't set.",0,{hint:"Set BOARD_URL, or pass the URL to http()."});return url}function connect(url,options2={}){url=url.replace(/\/+$/,"");let doFetch=guarded(url,options2.fetch??((input,init)=>globalThis.fetch(input,init)),options2.token),discovery,discover=()=>discovery??=doFetch(url).then(async response=>{if(!response.ok)throw new BoardError("no_board",`There's no board at ${url}: it answered ${response.status}.`,response.status);let found=await response.json();if(found.audience!==url)throw new BoardError("wrong_audience",`The board at ${url} says its URL is ${shown(found.audience,URL_SHAPE)??"another one"}.`,0,{hint:shown(found.audience,URL_SHAPE)?`Use ${found.audience}.`:"Use the URL its discovery names."});return found}),cached=null,source=null,person=null,mint=async()=>{if(options2.token!==void 0){let token3=typeof options2.token=="function"?await options2.token():options2.token;return{token:token3,expiresAt:expiry(token3)}}if(process.env.BOARD_TOKEN){let token3=process.env.BOARD_TOKEN;return source="BOARD_TOKEN",{token:token3,expiresAt:expiry(token3)}}let local=localToken(url);if(local)return source="vb serve",{token:local.token,expiresAt:expiry(local.token),handle:local.handle,name:null,avatar:null};let discovered=await discover();if(process.env.ACTIONS_ID_TOKEN_REQUEST_URL&&process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN){let token3=await githubActionsToken(url);return source="GitHub Actions OIDC",{token:token3,expiresAt:expiry(token3)}}if(process.env.VERCEL==="1"){let token3=await hostedToken(url);return source="Vercel OIDC",{token:token3,expiresAt:expiry(token3)}}person??=personToken(url,discovered).finally(()=>{person=null});let minted=await person;return source="vercel login",minted},token2=async()=>((options2.token!==void 0||!cached||cached.expiresAt-6e4<Date.now())&&(cached=await mint()),cached.token);return{fetch:async(input,init={})=>{let href=input instanceof Request?input.url:String(input),method=(init.method??(input instanceof Request?input.method:"GET")).toUpperCase(),path=(href.startsWith(url)?href.slice(url.length):new URL(href).pathname).split("?")[0]||"/",headers=new Headers(init.headers??(input instanceof Request?input.headers:void 0));if(!(method==="GET"&&path==="/")){headers.has("authorization")||headers.set("authorization",`Bearer ${await token2()}`);let handle=options2.handle??(process.env.BOARD_HANDLE||(options2.token===void 0?cached?.handle:null));handle&&!headers.has("x-board-handle")&&headers.set("x-board-handle",handle),!headers.has("x-board-profile")&&options2.token===void 0&&(cached?.name&&!headers.has("x-board-name")&&headers.set("x-board-name",encodeURIComponent(cached.name)),cached?.avatar&&!headers.has("x-board-avatar")&&headers.set("x-board-avatar",cached.avatar))}return doFetch(input,{...init,headers})},source:()=>source}}function environmentProfile(){let detected=detectAgent(),name=process.env.BOARD_AS||detected?.profile,avatar=process.env.BOARD_AVATAR;return{profile:name?avatar?{name,avatar}:name:null,session:detected?.session??null}}var WHERE="Project \u2192 Settings \u2192 Deployment Protection \u2192 Protection Bypass for Automation",Wall=class extends Error{status;constructor(status){super("protected"),this.status=status}},Moved=class extends Error{origin;status;constructor(origin,status){super("moved"),this.origin=origin,this.status=status}};async function hostedToken(url){try{return await(0,import_oidc.getVercelOidcToken)({audience:url})}catch(cause){throw Object.assign(new BoardError("no_token",`This deployment can't get a Vercel OIDC token for ${url}.`,0,{hint:"Enable OIDC on its project, or pass a token (BOARD_TOKEN), and add its project to the board project's Deployment Protection Trusted Sources."}),{cause})}}var ownHost=host=>[process.env.VERCEL_URL,process.env.VERCEL_BRANCH_URL,process.env.VERCEL_PROJECT_PRODUCTION_URL].some(own2=>!!own2&&own2.toLowerCase()===host.toLowerCase());function guarded(url,doFetch,suppliedToken){let host=new URL(url).host,secret,looked,envRejected=!1;return(input,init)=>attempt(input,init).catch(error=>{if(!(error instanceof Moved))throw error;let there=shown(`${error.origin}${new URL(url).pathname}`,URL_SHAPE);throw new BoardError("redirected",`${url} redirects to ${there?new URL(there).origin:"another origin"}, and its credentials don't follow it there, so Deployment Protection turns it away.`,error.status,{hint:there?`Use ${there}.`:"Use the URL it redirects to."})});async function attempt(input,init={}){let headers=new Headers(init.headers??(input instanceof Request?input.headers:void 0));init={...init,headers};let fromEnv=process.env.VERCEL_AUTOMATION_BYPASS_SECRET;if(fromEnv&&!envRejected&&(process.env.VERCEL!=="1"||ownHost(host)))try{return await through(doFetch,input,init,fromEnv)}catch(error){if(!(error instanceof Wall))throw error;if(process.env.VERCEL!=="1")throw new BoardError("protected",`${url} is behind Vercel Deployment Protection, and it rejected VERCEL_AUTOMATION_BYPASS_SECRET.`,error.status,{hint:`Check VERCEL_AUTOMATION_BYPASS_SECRET against ${WHERE}.`});envRejected=!0}if(process.env.VERCEL==="1"){if(!headers.has("x-vercel-trusted-oidc-idp-token")){let token2=headers.get("authorization")?.match(/^Bearer (.+)$/i)?.[1]??(typeof suppliedToken=="function"?await suppliedToken():suppliedToken)??(process.env.BOARD_TOKEN||await hostedToken(url));headers.set("x-vercel-trusted-oidc-idp-token",token2)}return through(doFetch,input,init,null).catch(error=>{throw error instanceof Wall?new BoardError("protected",`${url} is behind Vercel Deployment Protection, and it rejected this deployment's Vercel OIDC token.`,error.status,{hint:"Add this project and environment to the board project's Deployment Protection Trusted Sources."}):error})}let used=await(secret??=readBypass(host));try{return await through(doFetch,input,init,used)}catch(error){if(!(error instanceof Wall))throw error}looked??=projectBypass(url,host,doFetch).then(async found2=>(await writeBypass(host,found2.secret).catch(()=>{}),found2));let{secret:found,created}=await looked;secret=Promise.resolve(found);try{return await through(doFetch,input,init,found).catch(async error=>{if(!(error instanceof Wall)||!created)throw error;return await new Promise(resolve2=>setTimeout(resolve2,1e3)),through(doFetch,input,init,found)})}catch(error){throw error instanceof Wall?new BoardError("protected",`${url} is behind Vercel Deployment Protection, and its project's automation bypass didn't get through.`,error.status,{hint:`Set VERCEL_AUTOMATION_BYPASS_SECRET (${WHERE}).`}):error}}}async function through(doFetch,input,init,bypass){let target=typeof input=="string"?input:input instanceof URL?input.href:input.url,method=(init.method??(input instanceof Request?input.method:"GET")).toUpperCase(),headers=new Headers(init.headers??(input instanceof Request?input.headers:void 0));bypass&&headers.set("x-vercel-protection-bypass",bypass);let moved=null,wall=status=>moved?new Moved(moved,status):new Wall(status);for(let hops=0;;hops++){let response=await doFetch(hops===0?input:target,{...init,headers,redirect:"manual"}),location=response.status>=300&&response.status<400?response.headers.get("location"):null;if(location){let next=new URL(location,target);if(next.pathname.startsWith("/sso-api"))throw wall(response.status);if(method==="GET"||method==="HEAD"){if(hops>=20)throw new BoardError("too_many_redirects",`${target} redirected more than 20 times.`,response.status);next.origin!==new URL(target).origin&&(moved=next.origin,headers=new Headers(headers),headers.delete("authorization"),headers.delete("x-vercel-trusted-oidc-idp-token"),headers.delete("x-vercel-protection-bypass")),target=next.href;continue}}let{status}=response;if(/^text\/html\b/i.test(response.headers.get("content-type")??"")&&!response.headers.has("content-disposition")&&(status>=200&&status<300||status===401||status===403)||status===401&&response.headers.has("x-vercel-error"))throw wall(status);return response}}var bypassFile=host=>join(configDir(),"bypass",`${createHash("sha256").update(host).digest("hex").slice(0,32)}.json`);async function readBypass(host){try{return JSON.parse(await readFile(bypassFile(host),"utf8")).secret||null}catch{return null}}async function writeBypass(host,secret){await mkdir(join(configDir(),"bypass"),{recursive:!0,mode:448}),await writeFile(bypassFile(host),JSON.stringify({host,secret}),{mode:384})}var automationBypass=bypasses=>Object.entries(bypasses??{}).find(([,value])=>value.scope==="automation-bypass")?.[0]??null;async function projectBypass(url,host,doFetch){let blocked=(why,fix)=>new BoardError("protected",`${url} is behind Vercel Deployment Protection, and your Vercel login ${why}.`,0,{hint:`${fix}, or set VERCEL_AUTOMATION_BYPASS_SECRET (${WHERE}).`}),token2=await apiLogin(await handedToken()),api=token2?async(method,path)=>{let response=await asYou(`https://api.vercel.com${path}`,{method,...method==="PATCH"?{headers:{"content-type":"application/json"},body:"{}"}:{}},token2,doFetch);return response.ok?await response.json():null}:vercelCliApi,project=await findProject(api,host);if(!project)throw blocked("can't see its project","Log in as someone on its project's team (vercel login), ask for access");let scope=project.ownerId.startsWith("team_")?`?teamId=${project.ownerId}`:"",details=await api("GET",`/v9/projects/${project.projectId}${scope}`);if(!details)throw blocked("can't read its project's settings","Ask for access to its project");let existing=automationBypass(details.protectionBypass);if(existing)return{secret:existing,created:!1};if(!details.ssoProtection&&!details.passwordProtection&&!details.passport)throw new BoardError("no_board",`There's no board at ${url}: it answered with a web page, and its Vercel project isn't protected.`,0);let created=await api("PATCH",`/v1/projects/${project.projectId}/protection-bypass${scope}`),secret=automationBypass(created?.protectionBypass);if(!secret)throw blocked("can't read or create its project's automation bypass","Ask a project admin to create one");return process.stderr.write(`created a Protection Bypass for Automation on the Vercel project ${details?.name??project.projectId}
|
|
10
|
+
`),{secret,created:!0}}async function findProject(api,host){let lookup=async(kind,team2)=>{let found=await api("GET",`${kind==="deployment"?`/v13/deployments/${encodeURIComponent(host)}`:`/now/aliases/${encodeURIComponent(host)}`}${team2?`?teamId=${team2}`:""}`).catch(()=>null),owner=found?.ownerId??found?.team?.id??team2;return found?.projectId&&owner?{projectId:found.projectId,ownerId:owner}:null},own2=await lookup("deployment",null)??await lookup("alias",null);if(own2)return own2;let{teams}=await api("GET","/v2/teams?limit=100").catch(()=>null)??{},ids=(teams??[]).filter(team2=>!team2.limited).map(team2=>team2.id);for(let kind of["deployment","alias"])for(let i=0;i<ids.length;i+=8){let found=(await Promise.all(ids.slice(i,i+8).map(id2=>lookup(kind,id2)))).find(Boolean);if(found)return found}return null}var vercelCli,vercelCliApi=async(method,path)=>{if(vercelCli??=promisify(execFile)("vercel",["whoami","--non-interactive"],{timeout:1e4}).then(()=>!0,()=>!1),!await vercelCli)throw new BoardError("not_logged_in","You're not logged in to Vercel.",0,{hint:"Run vercel login."});let args=["api",path,"-X",method,"--raw","--non-interactive"];method==="PATCH"&&args.push("--input","-");try{let run2=promisify(execFile)("vercel",args,{timeout:3e4});return run2.child.stdin?.on("error",()=>{}),run2.child.stdin?.end(method==="PATCH"?"{}":""),JSON.parse((await run2).stdout)}catch(error){if(error.code==="ENOENT")throw new BoardError("not_logged_in","You're not logged in to Vercel.",0,{hint:"Run vercel login."});return null}};var REGISTER_EVERY_MS=1e4,DRAIN_MS=24e4,WAKE_ANSWER_MS=2e3,POLL_WAIT_S=25,describe$1=error=>((error instanceof Error?error.message:String(error))||"unknown error").slice(0,500),wakeable=here=>here?.startsWith("https://")?here:null;function wakePath(here){let env=typeof process<"u"&&process.env.VERCEL==="1"?process.env:null,environment3=env?.VERCEL_TARGET_ENV||env?.VERCEL_ENV,branch=environment3==="preview"&&env?.VERCEL_GIT_COMMIT_REF||null;return!here||environment3!=="production"&&!branch?null:{path:new URL(here).pathname,...branch?{branch}:{}}}function registrant(options2){let{registry,rethrow:rethrow2}=options2,bound=options2.binding,last=null,registering=null;async function register(here){let current2=await options2.hash();return last&&last.url===here&&last.result.hash===current2&&Date.now()-last.at<REGISTER_EVERY_MS?last.result:(registering??=options2.wakeAt(here).then(where=>registry.register(where)).then(result2=>(last={url:here,at:Date.now(),result:result2},result2)).finally(()=>{registering=null}),registering)}async function runOne(match){let outcome={};if(!bound().subscriptions[match.watcher])outcome={skip:!0};else try{await bound().run(match.watcher,match.event,{trigger:`watch:${match.watcher}:${match.id}`,attempt:match.attempt+1})}catch(error){console.error(`[board] watcher ${match.watcher} failed:`,error),outcome={error:describe$1(error)}}await registry.ack(match.id,{lease:match.lease,...outcome}).catch(error=>console.error("[board] couldn't ack a match:",describe$1(error)))}async function drain(){let ran=0,until2=Date.now()+DRAIN_MS;for(;Date.now()<until2;){let matches=await registry.matches({limit:10});if(matches.length===0)break;await Promise.all(matches.map(runOne)),ran+=matches.length}return ran+await bound().pull()}let polling=null;function poll(){if(polling)return;let controller=polling=new AbortController;(async()=>{for(let pause=1e3;!controller.signal.aborted;)try{await register(null);let matches=await registry.matches({limit:10,wait:POLL_WAIT_S,signal:controller.signal});await Promise.all(matches.map(runOne)),await bound().pull(),pause=1e3}catch(error){if(controller.signal.aborted)break;console.error(`[board] couldn't pull matches from ${options2.remote}:`,describe$1(error)),await new Promise(resolve2=>setTimeout(resolve2,pause)),pause=Math.min(pause*2,6e4)}})()}let started=null,failedAt=0;function start(){if(started)return started;if(!options2.hasWork()||Date.now()-failedAt<REGISTER_EVERY_MS)return Promise.resolve();let here=wakeable(bound().url());return!here&&typeof process<"u"&&process.env.VERCEL==="1"?Promise.resolve():(started=register(here).then(()=>{},error=>{started=null,failedAt=Date.now(),console.error(`[board] couldn't register with ${options2.remote}:`,describe$1(error))}),here||poll(),started)}return{start,routes:[{method:"POST",path:"/wake",async handle(request){started??=Promise.resolve();let stale=request.headers.get("x-board-watchers")!==await options2.hash(),running=(async()=>(stale&&await register(wakeable(bound().url())),drain()))();waitUntil(running.catch(error=>console.error("[board] couldn't run what woke it:",describe$1(error))));let timer,waited=new Promise(resolve2=>timer=setTimeout(()=>resolve2(null),WAKE_ANSWER_MS));try{let ran=await Promise.race([running.catch(rethrow2),waited]);return ran===null?new Response(null,{status:202}):json({ran})}finally{clearTimeout(timer)}}},{method:"POST",path:"/watchers",async handle(request,{caller}){if(caller)return options2.registerFor(caller,request);started??=Promise.resolve();let here=wakeable(bound().url()),registration=await register(here).catch(rethrow2);return here||poll(),json(registration)}}],registerNow:()=>register(wakeable(bound().url())),close:()=>{polling?.abort(),polling=null}}}var PROFILE=/^[A-Za-z][A-Za-z0-9-]{0,31}$/,PROFILE_RULE="ids start with a letter and use up to 32 letters, numbers, and hyphens",PROJECTS_OWN=new Set(["identity","edit","delete","schedule","cancelSchedule","upload","uploadStart","uploadFinish","watchers","register","matches","ack","syncImport","syncReceipt","syncThread","syncFind","syncCopies","syncRoots","inboxPush","inboxNext","inboxAck","inbox","syncCode","syncAccount","syncUnlink"]);function profileOf(handle){let name=handle.replace(/[^A-Za-z0-9-]+/g,"-").replace(/^-+|-+$/g,"");return/^[A-Za-z]/.test(name)||(name=`u-${name}`),name.slice(0,32).replace(/-+$/,"")}function callerProfile(handle,own2){let taken=new Set([...own2,"middleware","client"].map(name2=>name2.toLowerCase())),base=profileOf(handle),name=base;for(let n=2;taken.has(name.toLowerCase());n++)name=`${base.slice(0,31-String(n).length)}-${n}`;return name}function scopeOf(identity2){let{access}=identity2.principal;if(access.everywhere("manage"))return null;let entries=Object.entries(access.summary()).map(([pattern,permissions])=>`${pattern}=${permissions.join(",")}`);return entries.length>0?entries.join(";"):"*="}var describe=error=>((error instanceof Error?error.message:String(error))||"unknown error").slice(0,500),rethrow=error=>{throw error instanceof BoardError?new HttpError(error.status>=400?error.status:502,error.code,error.message,{...error.hint!==void 0?{hint:error.hint}:{},origin:error.origin}):error instanceof HttpError||error instanceof Error&&error.name==="AbortError"?error:new HttpError(502,"unreachable",`the board didn't answer: ${describe(error)}`)};function queryString(input){let params=new URLSearchParams;for(let[key,value]of Object.entries(input))if(!(value==null||value===!1))if(key==="label"&&Array.isArray(value))for(let[name,values]of value)params.append("label",values===null?name:`${name}=${values.join("|")}`);else if(Array.isArray(value))for(let item of value)params.append(key,String(item));else params.set(key,String(value));let text2=params.toString();return text2?`?${text2}`:""}function attachmentOf(id2,response){let encoded=(response.headers.get("content-disposition")??"").match(/filename\*=UTF-8''([^;]+)/)?.[1],name="file";try{encoded&&(name=decodeURIComponent(encoded))}catch{}return{attachment:{id:id2,name,type:response.headers.get("content-type")??"application/octet-stream",size:Number(response.headers.get("content-length")??0)},data:response.body??new Uint8Array}}function createHttp(url,options2,environment3){if(url!==void 0&&!/^https?:\/\/[^/]/.test(url))throw new Error(`http: ${url} isn't a URL: pass the board's, like https://board.example.com/api/board`);if(url===void 0&&!environment3)throw new Error("http: pass the board's URL: outside Node there's no environment to find it in");let remote=(url??environment3.url()).replace(/\/+$/,""),doFetch=options2.fetch??environment3?.fetch(remote)??((input,init)=>globalThis.fetch(input,init)),binding=null,specs={},sync=[],hash=null,watchersHash=()=>hash??=sha256(canonicalWatchers(specs,sync)).then(hex=>hex.slice(0,32)),bound=()=>{if(!binding)throw new Error("http: this backend isn't serving a board yet: pass it to board({ backend })");return binding},sdk=(profile,quiet)=>createBoardClient({url:remote,fetch:doFetch,profile:profile!==void 0?profile:()=>environment3?.profile().profile??null,session:quiet||profile===null?null:()=>environment3?.profile().session??null}),own2=sdk(null,!0),profiles=new Map;function asProfile(profile){if(!PROFILE.test(profile))throw new HttpError(400,"invalid_request",`as: ${profile}: profiles start with a letter and use up to 32 letters, numbers, and hyphens`);let client2=profiles.get(profile);return client2||profiles.set(profile,client2=sdk(profile,!0)),client2}let holders=new Map;function holder(profile){let key=JSON.stringify(profile??(profile===null?null:"\0")),client2=holders.get(key);return client2||holders.set(key,client2=sdk(profile,!1)),client2}function clientOf(caller){return caller.origin.via?.kind==="engine"?own2:caller.origin.via?.kind==="client"?(caller.self!==null&&asProfile(caller.self),holder("profile"in caller?caller.profile:caller.self??void 0)):asProfile(caller.self??"client")}let discovered=null,discovery=()=>(discovered??=doFetch(remote).then(async response=>{if(!response.ok)throw new HttpError(response.status>=400?response.status:502,"no_board",`There's no board at ${remote}: it answered ${response.status}.`,{origin:"handle"});return await response.json()},rethrow)).catch(error=>{throw discovered=null,error}),principal=null,self=()=>principal??=own2.me().then(me=>me.principal).catch(error=>{throw principal=null,error}),ownKey=async derived=>keyId(await self(),derived);async function call(client2,method,input,signal){let route=routes[method],fields={...input},path=route.path.replace(/:(\w+)/g,(_,name)=>{let value=fields[name];return delete fields[name],encodeURIComponent(String(value))}),response;if(method==="upload"){let{data,name,type}=input;if(data.byteLength>4194304){let attachment=await client2.upload({data,name:name??"file",...type?{type}:{}}).catch(rethrow);return Response.json({attachment})}response=await client2.request(path,{method:"POST",body:data,headers:{...type?{"content-type":type}:{},...name!==null?{"x-board-filename":encodeURIComponent(name)}:{}},signal}).catch(rethrow)}else route.method==="GET"?response=await client2.request(`${path}${queryString(fields)}`,{signal}).catch(rethrow):response=await client2.request(path,{method:"POST",body:new TextEncoder().encode(JSON.stringify(fields)),headers:{"content-type":"application/json"},signal}).catch(rethrow);if(response.ok)return response;try{let body=await response.json();if(body.error?.message)throw new HttpError(response.status,body.error.code??"error",body.error.message,typeof body.error.hint=="string"?{hint:body.error.hint}:{})}catch(error){if(error instanceof HttpError)throw error}throw new HttpError(502,"remote_error",`${remote} answered ${response.status}`)}async function asProject(method,identity2,input,signal){if(method==="me"){let{principal:principal2,author,actor}=identity2;return{actor:actor.id,principal:principal2.id,kind:actor.kind,handle:actor.handle,name:actor.name,avatar:actor.avatar,profile:author.profile,session:author.session,expiresAt:principal2.expiresAt?.toISOString()??null,can:principal2.access.summary()}}if(PROJECTS_OWN.has(method))throw forbidden(`this board's callers act as its project on ${remote}, which can't tell them apart: ${method} is the project's own to do there`);let scope=scopeOf(identity2),avatar=identity2.actor.avatar?.startsWith("https://")?identity2.actor.avatar:void 0,client2=createBoardClient({url:remote,fetch:doFetch,profile:{name:callerProfile(identity2.actor.handle,Object.keys(specs)),...avatar?{avatar}:{}},session:null,...scope!==null?{headers:{"x-board-scope":scope}}:{}});(method==="post"||method==="posted")&&typeof input.id=="string"&&(input={...input,id:await ownKey(await derivedId("board-gateway",identity2.principal.id,input.id)).catch(rethrow)});let response=await call(client2,method,input,signal);return method==="attachment"?attachmentOf(String(input.id),response):response.json()}async function remoteCall(method,caller,input,signal){let registering=start();if("identity"in caller)return asProject(method,caller.identity,input,signal);let client2=clientOf(caller);"self"in caller&&caller.origin.via?.kind==="engine"&&await registering,(method==="post"||method==="posted")&&"self"in caller&&typeof input.id=="string"&&DERIVED_ID.test(input.id)&&(input={...input,id:await ownKey(input.id).catch(rethrow)});let response=await call(client2,method,input,signal);return method==="attachment"?attachmentOf(String(input.id),response):response.json()}let methods={...Object.fromEntries(METHODS.map(method=>[method,(caller,input,signal)=>remoteCall(method,caller,input,signal)])),async discovery(){return discovery()},async readSource(caller,input,signal){return"self"in caller&&bound().sync.some(entry=>entry.key===input.key)?bound().readSource(input.key,input.target,input.limit??50,input.after??null):remoteCall("readSource",caller,input,signal)},async joinSource(caller,input,signal){return"self"in caller&&bound().sync.some(entry=>entry.key===input.key)?bound().joinSource(input.key,input.target):remoteCall("joinSource",caller,input,signal)},async synced(caller,input,signal){let{targets}=await remoteCall("synced",caller,input,signal);return{targets:await Promise.all(targets.map(async row=>({...row,url:row.url??(row.thread!==null&&row.message!==null?await bound().linkOf(row.key,row.thread,row.message):null)})))}},async linked(caller,input,signal){let missing2=()=>new HttpError(404,"message_not_found","no message on this board for that link"),id2=await(async()=>{for(let{platform,thread,message}of bound().locate(input.url))for(let entry of bound().sync.filter(candidate=>candidate.platform===platform)){let at=await own2.syncFind({thread:`${entry.key}:${thread}`,...message!==null?{message}:{}},{signal}).catch(rethrow),found=message!==null?at.message:at.thread===null?null:(await own2.syncThread(at.thread).catch(rethrow)).root;if(found!==null)return found}return null})();if(id2===null)return remoteCall("linked",caller,input,signal);try{return await methods.message(caller,{id:id2},signal)}catch(error){throw error instanceof HttpError&&error.status===404?missing2():error}},async typing(caller,input,signal){let{targets}=await remoteCall("typing",caller,input,signal);if(!("self"in caller)||bound().sync.length===0)return{targets};let{message,replies}=await methods.message(caller,{id:input.id},signal),here=await bound().typing([message,...replies],input.status).catch(error=>(console.warn("[board] skipped typing where this board syncs:",describe(error)),[]));return{targets:[...targets,...here]}}};async function wakeAt(here){let path=wakePath(here);return path&&(await discovery()).wake_by_path?path:{url:here}}let runner=registrant({remote,registry:{register:where=>own2.register({...where,watch:specs,...sync.length>0?{sync}:{}}),matches:query=>own2.matches(query),ack:(id2,body)=>own2.ack(id2,body)},wakeAt,binding:bound,hash:watchersHash,hasWork:()=>Object.keys(specs).length>0||sync.length>0,rethrow,registerFor:async(caller,request)=>json(await methods.register(caller,RegisterBody.parse(await request.json()),request.signal))}),start=runner.start;return{...methods,routes:runner.routes,keeps:!1,bind(next){if(binding)throw new Error("http: this backend already serves a board: create one http() per board()");for(let[id2,spec]of Object.entries(next.subscriptions)){if(spec.kind==="sync"){id2==="@sync"&&(sync=next.sync.map(entry=>({...entry})));continue}if(!PROFILE.test(id2))throw new Error(`watch ${id2}: ${PROFILE_RULE}`);compileWatcher(id2,spec),specs[id2]={tail:spec.tail,when:spec.when}}hash=null,binding=next},registerWatchers:runner.registerNow,close:runner.close}}var environment={url:boardUrl,fetch:url=>connect(url).fetch,profile:environmentProfile};function http(url,options2={}){return createHttp(url,options2,environment)}var KNOWN_AGENTS=["claude","codex","grok","pi"],PROFILE2=/^[A-Za-z][A-Za-z0-9-]{0,31}$/,HANDLE=/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;function claimsOf(headers){let profile=headers.get("x-board-profile")?.trim()||null;if(profile!==null&&!PROFILE2.test(profile))throw badRequest("profile names start with a letter and use up to 32 letters, numbers, and hyphens");let avatar=headers.get("x-board-avatar")?.trim()||null;if(avatar!==null&&!isAvatarUrl(avatar))throw badRequest("avatars must be https URLs (http only on localhost)");let session=headers.get("x-board-session")?.trim().slice(0,200)||null,handle=headers.get("x-board-handle")?.trim().toLowerCase()||null;if(handle!==null&&!HANDLE.test(handle))throw badRequest(`invalid handle: ${handle}`);let encoded=headers.get("x-board-name"),name=null;if(encoded)try{name=decodeURIComponent(encoded).replace(/[\u0000-\u001f\u007f]/g,"").trim().slice(0,128)||null}catch{throw badRequest("x-board-name must be URI-encoded")}let scope=headers.get("x-board-scope");if(scope!==null)try{parseScope(scope)}catch(error){throw badRequest(error.message,"invalid_scope")}return{profile,avatar,session,handle,name,scope}}function resolveActor(principal,claims,audience){let baseHandle=principal.kind==="person"&&principal.facts?.issuer!=="host"&&claims.handle?claims.handle:principal.handle,personName=principal.kind==="person"?claims.name:null,profile=claims.profile?.toLowerCase()??null,id2=profile===null?principal.id:`${principal.id}+${profile}`,known=profile!==null&&KNOWN_AGENTS.includes(profile),actor={id:id2,kind:profile===null?principal.kind:"agent",handle:profile===null?baseHandle:`${baseHandle}/${profile}`,name:claims.profile??personName??baseHandle,avatar:claims.avatar??(known?`${audience}/avatars/${profile}`:null)};return{author:{actor:id2,principal:principal.id,profile,session:claims.session},actor}}function isAvatarUrl(value){try{let url=new URL(value);return url.protocol==="https:"||url.protocol==="http:"&&["localhost","127.0.0.1"].includes(url.hostname)}catch{return!1}}var syncTarget=target=>typeof target=="string"?{target,kind:isThreadTarget(target)?"thread":"channel"}:target,mentionsEmails=text2=>extractMentions(text2).some(token2=>token2.kind==="actor"&&token2.actor.includes("@"));async function withEmailMentions(text2,find){let emails=extractMentions(text2).flatMap(token2=>token2.kind==="actor"&&token2.actor.includes("@")?[token2.actor]:[]);if(emails.length===0)return{text:text2,found:[]};let byEmail=new Map;for(let email of emails)byEmail.set(email,await find?.(email).catch(()=>null)??null);return{text:replaceMentions(text2,token2=>{if(token2.kind!=="actor"||!byEmail.has(token2.actor))return null;let actor=byEmail.get(token2.actor);return actor?`@[${actor.id}]`:token2.actor}),found:[...byEmail.values()].filter(actor=>actor!==null)}}function createPolicy(options2,keeps,serving){let open=keeps||options2.access!==void 0,access=options2.access??[],own2=null,project=()=>(own2??=(async()=>{if(!serving)return{id:options2.project?.id??null,teamId:options2.project?.teamId??null};let found=await serving(),[id2,teamId]=await Promise.all([found.ownProjectId(options2.project),found.ownTeamId(options2.project)]);return{id:id2,teamId}})()).catch(error=>{throw own2=null,error});async function discoveryIdentity(headers){return serving?(await serving()).discoveryIdentity(headers,options2.project):{projectId:options2.project?.id??null,teamId:options2.project?.teamId??null}}let tokens=null,verifier=()=>options2.authenticate?Promise.resolve(options2.authenticate):tokens??=serving?serving().then(found=>found.oidcTokens()):Promise.reject(new HttpError(501,"no_verifier","this build of board verifies no tokens: pass board({ authenticate })")),authority=createAuthority({access,project,hosts:options2.authenticate!==void 0&&!verifiesTokens(options2.authenticate)}),context2={project,issuers:typeof access=="function"?[]:issuersOf(access)},verified=new Map,perRequest=new WeakMap;async function principalOf(request){let live=principal2=>principal2.expiresAt===null||principal2.expiresAt.getTime()>Date.now(),known=perRequest.get(request);if(known){let principal2=await known;if(live(principal2))return principal2}let key=request.credential===null?null:[request.audience,request.credential,request.headers.get("x-vercel-oidc-token")??""].join(""),cached=key===null?void 0:verified.get(key);if(cached&&cached.until>Date.now())return perRequest.set(request,cached.principal),cached.principal;let principal=verify(request);return perRequest.set(request,principal),key!==null&&principal.then(found=>{found.facts?.issuer==="host"||found.expiresAt===null||(verified.size>=1e3&&verified.delete(verified.keys().next().value),verified.set(key,{principal,until:found.expiresAt.getTime()}))},()=>perRequest.delete(request)),principal}async function verify(request){let authenticate=await verifier(),result2;try{result2=await authenticate({...request,board:context2})}catch(error){throw error instanceof Response?new RejectedError(error):error}if(result2 instanceof Response)throw new RejectedError(result2);if(result2==null)throw unauthorized("missing bearer token");return fromTokens(result2)?result2:hostPrincipal(result2)}return{open,project,discoveryIdentity,async identify(request){let found=await principalOf(request),principal=request.claims.scope===null?found:{...found,scope:request.claims.scope},{author,actor}=resolveActor(principal,request.claims,request.audience);return{principal:{...principal,access:await authority.grant(principal)},author,actor}},accessNow:principal=>authority.accessNow(principal)}}var LOGOS={claude:"m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z",openai:"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z",x:"M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z",slack:"M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z",github:"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",whatsapp:"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"},STYLES={claude:{background:"#D97757",mark:{path:LOGOS.claude}},codex:{background:"#000000",mark:{path:LOGOS.openai}},grok:{background:"#000000",mark:{path:LOGOS.x}},pi:{background:"#1f1f1f",mark:{glyph:"\u03C0"}}};function avatarSvg(name){let style=STYLES[name]??{background:"#666666",mark:{glyph:name.slice(0,1).toUpperCase()}},mark="path"in style.mark?`<path transform="translate(30 30) scale(2.8333)" fill="#ffffff" d="${style.mark.path}"/>`:`<text x="64" y="86" font-family="ui-sans-serif,system-ui,sans-serif" font-size="66" font-weight="600" text-anchor="middle" fill="#ffffff">${style.mark.glyph}</text>`;return`<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"><rect width="128" height="128" rx="28" fill="${style.background}"/>${mark}</svg>`}var CREATED=new Set(["post","schedule","upload","uploadFinish"]),INLINE=/^(image\/(png|jpeg|gif|webp|avif)|video\/(mp4|webm)|text\/plain)$/;function attachmentResponse({attachment,data}){let disposition=INLINE.test(attachment.type)?"inline":"attachment",body=data instanceof Uint8Array?new Blob([data]):data;return new Response(body,{headers:{"content-type":attachment.type,"content-length":String(attachment.size),"content-disposition":`${disposition}; filename*=UTF-8''${encodeURIComponent(attachment.name)}`,"content-security-policy":"sandbox; default-src 'none'","x-content-type-options":"nosniff","cache-control":"private, max-age=31536000, immutable"}})}async function inputOf(name,request,url,params){let id2=params.id??"",key=params.key??"",body=()=>readJson(request);switch(name){case"discovery":case"me":case"channels":case"actors":case"identity":case"watchers":return{};case"message":case"posted":case"synced":case"cancelSchedule":case"attachment":case"uploadFinish":return{id:id2};case"messages":return MessagesQuery.parse(queryObject(url));case"threads":return ThreadsQuery.parse(queryObject(url));case"schedules":return SchedulesQuery.parse(queryObject(url));case"matches":return MatchesQuery.parse(queryObject(url));case"events":{let query=EventsQuery.parse(queryObject(url)),last=request.headers.get("last-event-id");return query.after===void 0&&last&&Ref.safeParse(last).success&&(query.after=last),query}case"post":return PostBody.parse(await body());case"schedule":return ScheduleBody.parse(await body());case"register":return RegisterBody.parse(await body());case"edit":return{id:id2,...EditBody.parse(await body())};case"delete":return{id:id2,...DeleteBody.parse(await body())};case"react":return{id:id2,...ReactBody.parse(await body())};case"label":return{id:id2,...LabelBody.parse(await body())};case"ack":return{id:id2,...AckBody.parse(await body())};case"upload":{let header=request.headers.get("x-board-filename"),name2=null;try{name2=header===null?null:decodeURIComponent(header)}catch{throw badRequest("`x-board-filename` is not URI-encoded")}return{data:await readCapped(request,4194304),name:name2,type:request.headers.get("content-type")}}case"uploadStart":return UploadStartBody.parse(await body());case"syncImport":return SyncImportBody.parse(await body());case"syncReceipt":return SyncReceiptBody.parse(await body());case"syncThread":return{thread:params.thread??""};case"syncFind":return SyncFindQuery.parse(queryObject(url));case"syncCopies":return SyncCopiesQuery.parse(queryObject(url));case"syncRoots":return SyncRootsQuery.parse(queryObject(url));case"inboxPush":return{key,...InboxPushBody.parse(await body())};case"inboxNext":return{key,...InboxNextQuery.parse(queryObject(url))};case"inboxAck":return{key,id:id2,...InboxAckBody.parse(await body())};case"inbox":return{key,...InboxQuery.parse(queryObject(url))};case"syncCode":return SyncCodeBody.parse(await body());case"syncAccount":return SyncAccountBody.parse(await body());case"syncUnlink":return SyncUnlinkBody.parse(await body());case"typing":return{id:id2,...TypingBody.parse(await body())??{}};case"readSource":return{key,...SourceReadQuery.parse(queryObject(url))};case"joinSource":return{key,...SourceJoinBody.parse(await body())};case"linked":return LinkedQuery.parse(queryObject(url))}}var externalActor=author=>({id:author.id,kind:"external",handle:author.handle,name:author.name,avatar:author.avatar});function sourceOf(caller){let via=caller.origin.via;return via?.kind==="middleware"||via?.kind==="engine"?null:via??{kind:"api"}}var MIDDLEWARE={self:"middleware",origin:{via:{kind:"middleware"}}},OWN={self:null,origin:{via:{kind:"client",name:null}}};async function createServe(context2){let{options:options2,backend,runtime,policy}=context2,entries=options2.sync??{},engine=Object.keys(entries).length>0?(await runtime.sync()).createSyncEngine(entries):null,streamMaxMs=options2.streamMaxMs??28e4,route=createRouter(backend.routes,options2.basePath),{state,ownUrl,clientAs,handleOf}=context2,watch=options2.watch??{},middleware=options2.middleware??[];function requestOf(request,audience){return{credential:(request.headers.get("authorization")??"").match(/^Bearer\s+(.+)$/i)?.[1]??null,claims:claimsOf(request.headers),audience,headers:request.headers}}async function callerOf(request){return{identity:await policy.identify(request),origin:{via:{kind:"api"}}}}let servesOnly=()=>new HttpError(404,"not_found",`${ownUrl()??"this board"} answers only wake-ups and webhooks`);async function withEmails(text2){return(await withEmailMentions(text2,engine?async email=>{let actor=await engine.findByEmail(email);return actor&&await backend.syncImport(engine.caller,{op:"seen",author:{...actor,isBot:!1}},new AbortController().signal),actor}:void 0)).text}async function resolveTarget(target){if(typeof target!="string")return target;let at=target.indexOf(":");if(at<=0)throw badRequest(`sync targets look like <key>:<channel or thread>, got: ${target}`);let key=target.slice(0,at);if(engine&&entries[key])return engine.normalizeTarget(target);if(engine)throw badRequest(`no sync entry named ${key}`);return syncTarget(target)}let accessOf2=caller=>"identity"in caller?caller.identity.principal.access:null;async function mayPost(caller,where,signal){let access=accessOf2(caller);if(access===null)return{access:null,channel:where.channel??""};let channel=where.channel??(where.re===void 0?void 0:(await lookup(caller,where.re,signal))?.channel);return channel===void 0?null:access.can("post",channel)?{access,channel}:null}let selves=new Map;async function whoIs(caller,signal){if("identity"in caller)return{principal:caller.identity.principal.id,actor:caller.identity.actor};let key=JSON.stringify([caller.origin.via?.kind??"",caller.self,caller.profile??null]),found2=selves.get(key);return found2||(selves.set(key,found2=backend.me(caller,{},signal).then(me=>({principal:me.principal,actor:{id:me.actor,kind:me.kind,handle:me.handle,name:me.name,avatar:me.avatar}}))),found2.catch(()=>selves.delete(key))),found2}let middlewareBoard=null,screen=(write,author,source,commit)=>runMiddleware(middleware,{write,author,source,board:()=>middlewareBoard??=handleOf(MIDDLEWARE,null)},commit);async function found(lookup2){try{return await lookup2(),!0}catch(error){if(error instanceof HttpError&&error.status===404)return!1;throw error}}async function attachmentsOf(caller,ids,signal){let attachments2=[];for(let id2 of new Set(ids??[])){let{attachment,data}=await backend.attachment(caller,{id:id2},signal);data instanceof ReadableStream&&await data.cancel().catch(()=>{}),attachments2.push(attachment)}return attachments2}async function screenPost(caller,input,signal,send){let source=sourceOf(caller);if(middleware.length===0||source===null||input.id!==void 0&&await found(()=>backend.posted(caller,{id:input.id},signal)))return send(input);let channel=input.channel,re=null;if(input.re!==void 0){let root=await lookup(caller,input.re,signal);if(!root)return send(input);channel=root.channel,re=root.re??root.id}let access=accessOf2(caller),labels2=Object.keys(input.labels??{}).length>0,syncs=input.re===void 0&&(input.sync?.length??0)>0;if(channel===void 0||access&&(!access.can("post",channel)||labels2&&!access.can("label",channel)||syncs&&!access.can("sync",channel)))return send(input);let attachments2=await attachmentsOf(caller,input.attachments,signal),{actor}=await whoIs(caller,signal);return screen({kind:"post",channel,re,text:input.text,labels:input.labels??{},attachments:attachments2},actor,source,write=>send({...input,text:write.text,labels:write.labels}))}function screenSchedule(caller,input,signal){let{channel,re,text:text2,labels:labels2,noSync,attachments:attachments2}=input;return screenPost(caller,{...channel!==void 0?{channel}:{},...re!==void 0?{re}:{},text:text2,...labels2?{labels:labels2}:{},...noSync!==void 0?{noSync}:{},...attachments2?{attachments:attachments2}:{}},signal,screened=>backend.schedule(caller,{...input,text:screened.text,...screened.labels?{labels:screened.labels}:{}},signal))}async function ownMessage(caller,id2,signal){let access=accessOf2(caller),readable=await lookup(caller,id2,signal),message=readable??(access!==null?await lookup(OWN,id2,signal):null);return!message||message.deleted||access&&!access.can("post",message.channel)||!readable&&!message.blind?null:message.by.principal===(await whoIs(caller,signal)).principal?message:null}async function screenEdit(caller,input,signal,send){let source=sourceOf(caller);if(middleware.length===0||source===null)return send(input);let message=await ownMessage(caller,input.id,signal);if(!message)return send(input);let{actor}=await whoIs(caller,signal);return screen({kind:"edit",id:message.id,channel:message.channel,re:message.re,text:input.text},actor,source,write=>send({...input,text:write.text}))}async function importSource(caller,body,found2,signal){let key=body.source?.key??body.from;if(body.source&&!("identity"in caller?(await backend.watchers(caller,{},signal)).watchers.some(registration=>registration.owner===caller.identity.principal.id&®istration.sync.some(entry=>entry.key===key)):entries[body.source.key]!==void 0))return null;let origin=(body.op==="post"?body.origin:null)??found2?.origin??null;return{kind:"sync",key:key??"",platform:body.author.id.slice(0,Math.max(0,body.author.id.indexOf(":"))),channel:body.source?.channel??found2?.origin?.channel??origin?.channel??"",thread:body.source?.thread??(origin?origin.subthread??origin.thread:"")}}async function importTarget(caller,body,signal){let id2=body.message??null;if(body.origin!==void 0){let{thread,message}=await backend.syncFind(caller,{thread:body.origin.thread,...body.origin.message!==void 0?{message:body.origin.message}:{}},signal);id2=body.origin.message!==void 0?message:thread===null?null:(await backend.syncThread(caller,{thread},signal)).root}return id2===null?null:lookup(caller,id2,signal)}async function syncImport(caller,body,signal){let send=patched=>backend.syncImport(caller,patched,signal);if(middleware.length===0||caller.origin.via?.kind==="middleware"||body.op!=="post"&&body.op!=="edit")return send(body);let access=accessOf2(caller);if(access&&(!access.anywhere("sync")||"identity"in caller&&caller.identity.principal.scope!==void 0))return send(body);let dropped=async commit=>{try{return await commit()}catch(error){if(!(error instanceof DroppedError))throw error;return{message:null,dropped:[...error.responses]}}};if(body.op==="post"){if(body.id!==void 0&&(await backend.syncFind(caller,{post:body.id},signal)).message!==null)return send(body);let channel=body.channel,re=null;if(body.re!==void 0){let root=await lookup(caller,body.re,signal);if(!root)return send(body);channel=root.channel,re=root.re??root.id}if(channel===void 0||access&&!access.can("sync",channel))return send(body);let source2=await importSource(caller,body,null,signal);if(!source2)return send(body);let attachments2=await attachmentsOf(caller,body.attachments,signal);return dropped(async()=>screen({kind:"post",channel,re,text:body.text,labels:body.labels??{},attachments:attachments2},externalActor(body.author),source2,async write=>send({...body,text:write.text,labels:write.labels})))}if(body.origin===void 0==(body.message===void 0))return send(body);let found2=await importTarget(caller,body,signal);if(!found2||found2.deleted||access&&!access.can("sync",found2.channel)||body.message!==void 0&&found2.by.principal!==body.author.id)return send(body);let source=await importSource(caller,body,found2,signal);return source?dropped(async()=>screen({kind:"edit",id:found2.id,channel:found2.channel,re:found2.re,text:body.text},externalActor(body.author),source,async write=>send({...body,text:write.text}))):send(body)}async function lookup(caller,id2,signal){try{let{message,replies}=await backend.message(caller,{id:id2},signal);return[message,...replies].find(candidate=>candidate.id===id2)??null}catch(error){if(error instanceof HttpError&&error.status===404)return null;throw error}}async function post2(caller,input,signal){let send=(poster,body2)=>screenPost(poster,body2,signal,patched=>backend.post(poster,patched,signal));if(!engine)return send(caller,input.sync?.length?{...input,sync:await Promise.all(input.sync.map(resolveTarget))}:input);let allowed=(input.sync?.length??0)>0||mentionsEmails(input.text)?await mayPost(caller,input,signal):null;if(!allowed)return send(caller,input);let{access,channel}=allowed,body=input.re===void 0&&(input.sync?.length??0)>0&&(access===null||access.can("sync",channel))?{...input,sync:await Promise.all(input.sync.map(resolveTarget))}:input,text2=await withEmails(body.text),answer=await send(caller,{...body,text:text2});return text2!==input.text&&access!==null&&!access.readable(answer.message.channel)?{...answer,message:{...answer.message,text:input.text}}:answer}async function landed(caller,answer){if(!answer.dropped)return answer.message;for(let text2 of answer.dropped)await caller.platform.respond(text2);throw new DroppedError(answer.dropped)}async function asPlatform(name,caller,input,signal){let me=engine.caller,{author,key}=caller.platform,{via}=caller.origin,source={key,channel:via.channel,thread:via.thread},change=async body=>{if(await landed(caller,await syncImport(me,body,signal))===null)throw notFound(`message not found: #${body.message}`,"message_not_found");return{ok:!0}};switch(name){case"post":return importPost(caller,input,signal);case"edit":{let{id:id2,text:text2,revision}=input;return change({op:"edit",author,from:null,message:id2,text:await withEmails(text2),source,...revision!==void 0?{revision}:{}})}case"delete":{let{id:id2,revision}=input;return change({op:"delete",author,from:null,message:id2,...revision!==void 0?{revision}:{}})}case"react":{let{id:id2,emoji,remove}=input;return change({op:"react",author,from:null,message:id2,emoji,...remove!==void 0?{remove}:{}})}case"label":{let{id:id2,set,unset,if:expected}=input;return change({op:"labels",author,from:null,message:id2,...set?{set}:{},...unset?{unset}:{},...expected?{if:expected}:{}})}default:return invoke(name,me,input,signal)}}async function importPost(caller,input,signal){let me=engine.caller,{author,key}=caller.platform,{via}=caller.origin,read=async id2=>{let message2=await lookup(me,id2,signal);if(!message2)throw notFound(`message not found: #${id2}`,"message_not_found");return message2};if(input.id!==void 0){let found2=(await backend.syncFind(me,{post:input.id},signal)).message;if(found2!==null)return{message:await read(found2)}}let targets=await Promise.all((input.sync??[]).map(resolveTarget)),message=await landed(caller,await syncImport(me,{op:"post",author,from:null,origin:null,source:{key,channel:via.channel,thread:via.thread},...input.channel!==void 0?{channel:input.channel}:{re:input.re},text:await withEmails(input.text),...input.labels?{labels:input.labels}:{},...input.noSync?{noSync:!0}:{},...input.attachments?.length?{attachments:input.attachments}:{},...input.id!==void 0?{id:input.id}:{},...input.after!==void 0?{after:input.after}:{}},signal));for(let target of targets)await backend.syncReceipt(me,{kind:"thread",message,target:target.target,targetKind:target.kind,cause:"request"},signal);return{message:await read(message)}}async function invoke(name,caller,input,signal){switch(name){case"post":return post2(caller,input,signal);case"edit":{let edit=input,send=body=>screenEdit(caller,body,signal,patched=>backend.edit(caller,patched,signal));return!engine||!mentionsEmails(edit.text)||!await mayPost(caller,{re:edit.id},signal)?send(edit):send({...edit,text:await withEmails(edit.text)})}case"syncImport":return syncImport(caller,input,signal);case"schedule":{let body=input,schedule=made=>backend.sendsSchedulesHere===!1?screenSchedule(caller,made,signal):backend.schedule(caller,made,signal),allowed=engine&&mentionsEmails(body.text)?await mayPost(caller,body,signal):null;if(!allowed)return schedule(body);let text2=await withEmails(body.text),answer=await schedule({...body,text:text2}),{access}=allowed;return text2!==body.text&&access!==null&&!access.readable(answer.schedule.channel)?{...answer,schedule:{...answer.schedule,text:body.text}}:answer}case"inbox":{let query=input;if(engine&&entries[query.key]&&query.channel!==void 0){let access=accessOf2(caller);if(access===null||access.anywhere("sync"))return backend.inbox(caller,{...query,channel:await engine.inboxChannel(query.key,query.channel)},signal)}return backend.inbox(caller,query,signal)}default:{let method=backend[name];if(typeof method!="function")throw notFound(`no route: ${name}`);return method.call(backend,caller,input,signal)}}}async function signingIn(name,url,signal){let{sync:key,code}=SignInQuery.parse(queryObject(url));if(!engine||!entries[key])throw notFound(`no sync entry named ${key}`);let page=(status,text2)=>new Response(`${text2}
|
|
11
|
+
`,{status,headers:{"content-type":"text/plain; charset=utf-8"}}),{holder}=await backend.syncFind(engine.caller,{code},signal);if(holder===null)return page(404,"That link has expired or been used. Run `vb identity link` again for a new one.");if(name==="signIn"){let back=ownUrl(),at=await engine.signIn(key,holder,back?`${back}/identity/signed-in?${new URLSearchParams({sync:key,code})}`:void 0);return new Response(null,{status:302,headers:{location:at}})}let linked=await engine.redeemSignIn(key,holder,code);return linked?page(200,`Linked ${linked.handle?`@${linked.handle}`:linked.actor}. You can close this tab.`):page(409,`${entries[key].platform} hasn't said you approved yet: finish signing in, then open this page again.`)}async function serve(name,caller,request,url,params){let signal=request.signal;switch(name){case"sync":{let body=SyncBody.parse(await readJson(request)),id2=params.id??"",access=accessOf2(caller);if(access&&engine){let message=await lookup(caller,id2,signal);if(!message)throw notFound(`message not found: #${id2}`,"message_not_found");if(!access.can("sync",message.channel))throw forbidden(`this token can't sync in ${message.channel}`)}let target=await resolveTarget(body.target),{attached}=await backend.syncReceipt(caller,{kind:"thread",message:id2,target:target.target,targetKind:target.kind,cause:"request"},signal);return json({target:target.target,attached:attached??!0})}case"identityUnlink":{let body=IdentityUnlinkBody.parse(await readJson(request)),{links}=await backend.identity(caller,{},signal),wanted=body.actor.replace(/^@/,"").toLowerCase(),found2=links.filter(link=>link.actor===body.actor||link.handle?.toLowerCase()===wanted).map(link=>link.actor);if(found2.length===0)throw notFound(`${body.actor} isn't linked to you`,"not_linked");if(found2.length>1)throw badRequest(`${body.actor} names several of your accounts: use ${found2.join(" or ")}`,"ambiguous_actor");return await backend.syncUnlink(caller,{account:found2[0]},signal),json({ok:!0,actor:found2[0]})}default:throw notFound(`no route: ${request.method} ${url.pathname}`)}}let synced=Object.fromEntries(METHODS.map(name=>[name,name==="syncImport"?syncImport:(caller,input,signal)=>backend[name].call(backend,caller,input,signal)]));engine?.bind({backend:synced,client:(platform,trigger)=>clientAs({platform:{author:platform.author,key:platform.key,respond:platform.respond},origin:{via:platform.via}},trigger)});async function sse(caller,query,request,identify){let first=await backend.events(caller,{...query,wait:0},request.signal),encoder=new TextEncoder,controller=new AbortController,deadline=Date.now()+streamMaxMs,signal=AbortSignal.any([request.signal,controller.signal,AbortSignal.timeout(streamMaxMs)]),body=new ReadableStream({async start(stream){let send=text2=>{try{stream.enqueue(encoder.encode(text2))}catch{}},ping=setInterval(()=>send(`: ping
|
|
12
|
+
|
|
13
|
+
`),15e3);try{send(`: connected
|
|
14
|
+
|
|
15
|
+
`);let page=first,after;for(;;){for(let event of page.events)send(`id: ${event.cursor}
|
|
16
|
+
event: board
|
|
17
|
+
data: ${JSON.stringify(event)}
|
|
18
|
+
|
|
19
|
+
`);page.cursor!==(page.events.at(-1)?.cursor??after)&&send(`id: ${page.cursor}
|
|
20
|
+
|
|
21
|
+
`),after=page.cursor;let left=deadline-Date.now();if(signal.aborted||left<=0)break;page=await backend.events(identify?await identify():caller,{...query,after,wait:Math.min(300,left/1e3)},signal)}}catch(error){signal.aborted||console.error("[board] feed failed",error)}finally{clearInterval(ping);try{stream.close()}catch{}}},cancel(){controller.abort()}});return new Response(body,{headers:{"content-type":"text/event-stream","cache-control":"no-cache, no-transform",connection:"keep-alive"}})}async function discovery(request,url,signal){let{projectId,teamId}=await policy.discoveryIdentity(request.headers),sync=backend.keeps?(await backend.discovery(OWN,{},signal)).sync:Object.entries(entries).map(([key,entry])=>({key,platform:entry.platform}));return{version:VERSION,url,audience:url,team_id:teamId,project_id:projectId,sync}}async function dispatch(request,injected){try{let url=new URL(request.url),match=route(request.method,url.pathname);if(!match)throw notFound(`no route: ${request.method} ${url.pathname}`);let{target}=match;!injected&&!match.root&&(state.learned===null||state.learned.startsWith(`${match.base}/`))&&(state.learned=match.base);let boardUrl2=(options2.url??`${url.origin}${match.base}`).replace(/\/+$/,"");if(target.kind==="board"&&target.name==="webhook"){if(!engine||!entries[match.params.key??""])throw notFound(`no sync entry named ${match.params.key}`);return await engine.handleWebhook(match.params.key,request)}if(target.kind==="board"&&(target.name==="signIn"||target.name==="signedIn"))return await signingIn(target.name,url,request.signal);if(target.kind==="board"&&target.name==="avatar"){let name=match.param??"";if(!KNOWN_AGENTS.includes(name))throw notFound(`no avatar: ${name}`);return new Response(avatarSvg(name),{headers:{"content-type":"image/svg+xml","cache-control":"public, max-age=86400"}})}if(target.kind==="board"&&target.name==="openapi")return json(openapi());if(target.kind==="backend")return await target.route.handle(request,{caller:injected&&"platform"in injected?engine.caller:injected,param:match.param});if(!injected&&!policy.open)throw servesOnly();if(!injected&&target.kind==="protocol"&&target.name==="discovery")return json(await discovery(request,boardUrl2,request.signal));if(target.kind==="protocol"&&typeof backend[target.name]!="function")throw notFound(`no route: ${request.method} ${url.pathname}`);let own2=injected&&"platform"in injected?engine.caller:injected,asked=own2?null:requestOf(request,boardUrl2),caller=own2??await callerOf(asked);if(target.kind==="board")return await serve(target.name,caller,request,url,match.params);let input=await inputOf(target.name,request,url,match.params);if(target.name==="events"&&(request.headers.get("accept")??"").includes("text/event-stream"))return await sse(caller,input,request,asked?()=>callerOf(asked):null);let output=injected&&"platform"in injected?await asPlatform(target.name,injected,input,request.signal):await invoke(target.name,caller,input,request.signal);return target.name==="attachment"?attachmentResponse(output):json(output,CREATED.has(target.name)?201:200)}catch(error){return toErrorResponse(error)}}return{dispatch,async inject(request,caller,trigger,writes){if(trigger!==null&&request.method==="POST"&&new URL(request.url).pathname.endsWith("/messages")){let body=await request.json()??{};body.id??=await derivedId("board-inbound",trigger,"client",String(writes.made++)),request=new Request(request.url,{method:"POST",headers:request.headers,body:JSON.stringify(body)})}return dispatch(request,caller)},async run(id2,event,{trigger,attempt}){if(id2==="@sync"&&engine)return engine.deliver(event,{attempt});let watcher=watch[id2];if(!watcher)throw new Error(`this board has no watcher ${id2}`);await watcher.run({event,board:handleOf({self:id2,origin:{via:{kind:"watcher",id:id2}}},trigger)})},async receive(key,item,{id:id2,attempt}){if(!engine)throw new Error(`this board has no sync, so nothing reads ${key}'s inbox`);await engine.receive(key,item,{id:id2,attempt})},pull:()=>engine?engine.pull():Promise.resolve(0),async readSource(key,target,limit,after){if(!engine||!entries[key])throw notFound(`this board doesn't run sync entry ${key}: read it where its sync runs`,"not_synced_here");return engine.readSource(key,target,limit,after)},async joinSource(key,target){if(!engine||!entries[key])throw notFound(`this board doesn't run sync entry ${key}: join it where its sync runs`,"not_synced_here");return engine.joinSource(key,target)},typing:async(messages,status)=>engine?engine.typing(messages,status):[]}}var EVENTS_WAIT_S=25;function createBoard(options2,runtime){let backend=options2.backend??runtime.http(),policy=createPolicy(options2,backend.keeps,runtime.serving),watch=options2.watch??{},entries=options2.sync??{},state={learned:null},inProcess=`http://in-process.board${(options2.basePath??"").replace(/\/+$/,"")}`;function ownUrl(){if(options2.url)return options2.url.replace(/\/+$/,"");let env=typeof process>"u"?void 0:process.env,domain=env?.VERCEL_ENV==="production"?env.VERCEL_PROJECT_PRODUCTION_URL:env?.VERCEL_URL,base=options2.basePath??state.learned;return domain&&base!==null?`https://${domain}${base}`.replace(/\/+$/,""):null}let loading=null;function server(){return loading??=createServe(context2),loading.catch(()=>loading=null),loading}function clientAs(caller,trigger){let writes={made:0};return createBoardClient({url:inProcess,token:"in-process",fetch:async(input,init)=>(await server()).inject(new Request(input,init),caller,trigger,writes)})}let serve=async request=>(await server()).dispatch(request,null);function handle(client2,url){let made={...client2,fetch:serve,as};return url&&Object.defineProperty(made,"url",{get:url,enumerable:!0}),made}function handleOf(caller,trigger){let client2=clientAs(caller,trigger);if("identity"in caller)return handle({...client2,events:(query,opts)=>events(client2,query,opts)},()=>ownUrl()??inProcess);let via=caller.origin.via?.kind??"client",id2=given=>derivedId(`board-${via==="watcher"?"watch":via}`,caller.self??"",given),owned=async input=>typeof input=="string"||input.id===void 0?input:{...input,id:await id2(input.id)};return handle({...client2,post:async(channel,input)=>client2.post(channel,await owned(input)),reply:async(re,input)=>client2.reply(re,await owned(input)),posted:async given=>client2.posted(await id2(given)),request:(path,init={})=>{let{as:profile,...rest}=init;return profile!==void 0?as(profile).request(path,rest):client2.request(path,rest)},events:(query,opts)=>events(client2,query,opts)},()=>ownUrl()??inProcess)}let profileCaller=profile=>{let name=profile===null?null:typeof profile=="string"?profile:profile.name;return{self:name,profile,origin:{via:{kind:"client",name}}}};function as(profile){return handleOf(profileCaller(profile),null)}let context2={options:options2,backend,runtime,policy,state,ownUrl,clientAs,handleOf},subscriptions=Object.fromEntries(Object.entries(watch).map(([id2,watcher])=>[id2,{kind:"tail",tail:watcher.tail,when:watcher.when??"matches"}]));Object.keys(entries).length>0&&(subscriptions[SYNC]={kind:"sync"});let ownEntries=Object.entries(entries).map(([key,entry])=>({key,platform:entry.platform,signIn:entry.users!==null})),binding={subscriptions,run:async(id2,event,meta)=>(await server()).run(id2,event,meta),receive:async(key,item,meta)=>(await server()).receive(key,item,meta),pull:async()=>ownEntries.length>0?(await server()).pull():0,sync:ownEntries,readSource:async(key,target,limit,after)=>(await server()).readSource(key,target,limit,after),joinSource:async(key,target)=>(await server()).joinSource(key,target),locate:url=>{let read=new Map;for(let entry of Object.values(entries)){let found=entry.locate(url);found&&read.set(JSON.stringify([entry.platform,found.thread,found.message]),{platform:entry.platform,...found})}return[...read.values()]},linkOf:async(key,thread,message)=>{let entry=Object.hasOwn(entries,key)?entries[key]:void 0;return!entry||!thread.startsWith(`${key}:`)?null:entry.url(thread.slice(key.length+1),message).catch(()=>null)},typing:async(messages,status)=>(await server()).typing(messages,status),accessNow:principal=>policy.accessNow(principal),project:()=>policy.project(),handle:caller=>handleOf(caller,null),url:ownUrl};return backend.bind(binding),handleOf({self:null,origin:{via:{kind:"client",name:null}}},null)}async function*events(client2,query={},opts={}){let after=query.after,failures=0;for(;!opts.signal?.aborted;){let page;try{page=await client2.poll({...query,...after!==void 0?{after}:{},wait:EVENTS_WAIT_S},opts.signal)}catch(error){if(opts.signal?.aborted)return;if(error instanceof BoardError&&error.status>=400&&error.status<500)throw error;await new Promise(resolve2=>setTimeout(resolve2,Math.min(3e4,500*2**failures++)));continue}failures=0;for(let event of page.events)if(yield event,opts.signal?.aborted)return;after=page.cursor}}function board(options2={}){return createBoard(options2,{http:()=>http(),sync:()=>import("./engine-DkdkXQEy-FDYYIXY7.js"),serving:()=>import("./serving-DKny38LK-UNINNTL5.js")})}var Mismatch=class extends Error{},show=value=>JSON.stringify(value)??String(value);function check(condition,message){if(!condition)throw new Mismatch(message)}function same(actual,expected,what){if(actual!==expected)throw new Mismatch(`${what}: expected ${show(expected)}, got ${show(actual)}`)}async function rejects(attempt,status,code,what){try{await attempt}catch(error){if(error instanceof BoardError&&error.status===status&&error.code===code)return;throw new Mismatch(`${what}: expected ${status} ${code}, got ${error instanceof BoardError?`${error.status} ${error.code}`:String(error)}`)}throw new Mismatch(`${what}: expected ${status} ${code}, and it succeeded`)}function wokenAt(registered){return registered?.path?{path:registered.path,...registered.branch?{branch:registered.branch}:{}}:{url:registered?.url??null}}function isRef(value,what){check(typeof value=="string"&&value.length>=1&&value.length<=128,`${what} should be a string of 1 to 128 characters, got ${show(value)}`)}var OPTIONAL=new Set(["schedules","attachments"]),needRoot=ctx=>(check(ctx.root&&ctx.reply,"posting failed, so there's nothing to read"),{root:ctx.root,reply:ctx.reply}),SYNC_KEY="conformance";async function registerSync(ctx){let registered=(await ctx.board.watchers()).find(registration=>registration.owner===ctx.me.principal);if(registered&&(Object.keys(registered.watch).length>0||(registered.sync?.length??0)>0))return{skip:"this token has watchers or a sync registered here, which registering would replace"};let where=wokenAt(registered);try{await ctx.board.register({url:null,watch:{},sync:[{key:SYNC_KEY,platform:SYNC_KEY}]})}catch(error){if(error instanceof BoardError&&error.status===403)return{skip:"this token can't register a sync here"};throw error}return{where}}async function post(ctx,text2,extra={}){let message=await ctx.board.post(ctx.channel,{text:text2,labels:ctx.labels,noSync:!0,...extra});return ctx.posted.push(message.id),message}async function reply(ctx,re,text2,extra={}){let message=await ctx.board.reply(re,{text:text2,noSync:!0,...extra});return ctx.posted.push(message.id),message}var CHECKS=[{group:"core",name:"posts a message, and reads it back by its ID",run:async ctx=>{let root=await post(ctx,"conformance: a root");isRef(root.id,"a message's id"),check(typeof root.version=="number",`a message's version should be a number, got ${show(root.version)}`);let thread=await ctx.board.thread(root.id);same(thread.message.id,root.id,"the thread read by that id"),same(thread.message.text,"conformance: a root","its text"),ctx.root=root;let reply2=await ctx.board.reply(root.id,{text:"conformance: a reply",noSync:!0});ctx.posted.push(reply2.id),ctx.reply=reply2}},{group:"core",name:"puts a reply in its root's thread",run:async ctx=>{let{root,reply:reply2}=needRoot(ctx);same(reply2.re,root.id,"the reply's re"),check((await ctx.board.thread(root.id)).replies.some(m=>m.id===reply2.id),`the thread's replies should include ${reply2.id}`)}},{group:"core",name:"replies after a message only while it's the thread's newest, one of any that race",run:async ctx=>{let root=await post(ctx,"conformance: a thread to reply after"),first=await reply(ctx,root.id,"conformance: after the root",{after:root.id});await rejects(reply(ctx,root.id,"conformance: late",{after:root.id}),409,"newer_message","a reply after the root, once another came after it"),same((await ctx.board.thread(root.id)).replies.length,1,"the replies after a refused one");let deleted=await reply(ctx,root.id,"conformance: deleted",{after:first.id});await ctx.board.delete(deleted.id),await rejects(reply(ctx,root.id,"conformance: after a deleted one",{after:first.id}),409,"newer_message","a reply after a message a deleted one came after");let racing=await Promise.allSettled([1,2,3].map(n=>reply(ctx,root.id,`conformance: racing ${n}`,{after:deleted.id})));same(racing.filter(result2=>result2.status==="fulfilled").length,1,"the replies that landed of three racing after the same message");for(let result2 of racing){if(result2.status==="fulfilled")continue;let error=result2.reason;check(error instanceof BoardError&&error.status===409&&error.code==="newer_message",`a reply that lost the race should be 409 newer_message, got ${error instanceof BoardError?`${error.status} ${error.code}`:String(error)}`)}same((await ctx.board.thread(root.id)).replies.length,3,"the replies after the race, a deleted one included")}},{group:"core",name:"posts once for a client ID, however many times it is sent",run:async ctx=>{let id2=`conformance-${ctx.labels.conformance}`,first=await post(ctx,"conformance: once",{id:id2});same((await post(ctx,"conformance: once",{id:id2})).id,first.id,"the second post with the same client id"),same((await ctx.board.posted(id2)).id,first.id,"the post found by its client id"),await rejects(ctx.board.posted(`${id2}-never`),404,"post_not_found","a client id nothing was posted with")}},{group:"core",name:"raises the version with each edit, reaction, and label",run:async ctx=>{let{root}=needRoot(ctx),version=async()=>(await ctx.board.thread(root.id)).message,before=await version();for(let[what,change]of[["an edit",()=>ctx.board.edit(root.id,"conformance: an edited root")],["a reaction",()=>ctx.board.react(root.id,"eyes")],["a label",()=>ctx.board.label(root.id,{set:{state:"open"}})]]){await change();let after=await version();check(after.version>before.version,`${what} should raise the version past ${before.version}, got ${after.version}`),before=after}same(before.text,"conformance: an edited root","the edited text"),check(before.editedAt!==null,"an edited message should have editedAt"),check(before.reactions.eyes?.includes(ctx.me.actor),`reactions.eyes should include ${ctx.me.actor}, got ${show(before.reactions)}`),same(before.labels.state,"open","the label set"),await ctx.board.label(root.id,{unset:["state"]}),same((await version()).labels.state,void 0,"the label unset")}},{group:"core",name:"labels a message only while its labels are as the write says, one of any that race",run:async ctx=>{let message=await post(ctx,"conformance: a message to claim"),labels2=async()=>(await ctx.board.thread(message.id)).message.labels,racing=await Promise.allSettled([1,2,3].map(n=>ctx.board.label(message.id,{set:{owner:`agent-${n}`},if:{owner:null}}))),landed=racing.flatMap((result2,n)=>result2.status==="fulfilled"?[`agent-${n+1}`]:[]);same(landed.length,1,"the claims that landed of three racing where no one had one");for(let result2 of racing){if(result2.status==="fulfilled")continue;let error=result2.reason;check(error instanceof BoardError&&error.status===409&&error.code==="label_conflict",`a claim that lost the race should be 409 label_conflict, got ${error instanceof BoardError?`${error.status} ${error.code}`:String(error)}`)}same((await labels2()).owner,landed[0],"the owner, after the race"),await rejects(ctx.board.label(message.id,{set:{owner:"someone"},if:{owner:"no-one"}}),409,"label_conflict","a label write from a value the label doesn't have"),same((await labels2()).owner,landed[0],"the owner, after a write from the wrong value"),await ctx.board.label(message.id,{unset:["owner"],if:{owner:landed[0]}}),same((await labels2()).owner,void 0,"the owner, released from the value it had")}},{group:"core",name:"edits and deletes a message only at the revision it was read at, which only edits and deletes change",run:async ctx=>{let message=await post(ctx,"conformance: a message to edit"),read=async()=>(await ctx.board.thread(message.id)).message,first=(await read()).revision;check(typeof first=="number",`a message's revision should be a number, got ${show(first)}`),await ctx.board.react(message.id,"eyes"),await ctx.board.label(message.id,{set:{state:"open"}}),same((await read()).revision,first,"the revision after a reaction and a label"),await ctx.board.edit(message.id,"conformance: edited",{revision:first});let second=(await read()).revision;check(second!==first,`an edit should change the revision from ${first}`),await rejects(ctx.board.edit(message.id,"conformance: stale",{revision:first}),409,"revision_conflict","an edit at a revision an edit came after"),await rejects(ctx.board.delete(message.id,{revision:first}),409,"revision_conflict","a delete at a revision an edit came after");let after=await read();check(after.text==="conformance: edited"&&!after.deleted,`refused writes should leave it as it was, got ${show({text:after.text,deleted:after.deleted})}`);let racing=await Promise.allSettled([1,2,3].map(n=>ctx.board.edit(message.id,`conformance: racing ${n}`,{revision:second})));same(racing.filter(result2=>result2.status==="fulfilled").length,1,"the edits that landed of three racing at the same revision");for(let result2 of racing){if(result2.status==="fulfilled")continue;let error=result2.reason;check(error instanceof BoardError&&error.status===409&&error.code==="revision_conflict",`an edit that lost the race should be 409 revision_conflict, got ${error instanceof BoardError?`${error.status} ${error.code}`:String(error)}`)}let last=(await read()).revision;await ctx.board.delete(message.id,{revision:last}),check((await read()).deleted,"a delete at the revision it has should delete it"),await rejects(ctx.board.edit(message.id,"conformance: too late",{revision:last}),409,"revision_conflict","an edit at a revision a delete came after")}},{group:"core",name:"matches a label on the message or its thread's root",run:async ctx=>{let{reply:reply2}=needRoot(ctx),{messages}=await ctx.board.messages({channel:ctx.channel,label:ctx.labels});check(messages.some(m=>m.id===reply2.id),"a reply should match its root's label")}},{group:"core",name:"reads a channel after a cursor or a message ID, oldest first",run:async ctx=>{let{root,reply:reply2}=needRoot(ctx),page=await ctx.board.messages({channel:ctx.channel,label:ctx.labels,after:ctx.start});isRef(page.cursor,"a read's cursor");let ids=page.messages.map(m=>m.id);check(ids.indexOf(root.id)!==-1&&ids.indexOf(root.id)<ids.indexOf(reply2.id),`the root should come before its reply, got ${show(ids)}`);let later=await ctx.board.messages({channel:ctx.channel,label:ctx.labels,after:root.id});check(!later.messages.some(m=>m.id===root.id),"reading after a message should leave it out"),check(later.messages.some(m=>m.id===reply2.id),"reading after a message should include what came after it")}},{group:"core",name:"lists threads, each with its reply count and latest reply",run:async ctx=>{let{root,reply:reply2}=needRoot(ctx),{threads}=await ctx.board.threads({channel:ctx.channel,label:ctx.labels}),found=threads.find(t=>t.message.id===root.id);check(found,`the threads should include ${root.id}`),same(found.replies,1,"its reply count"),same(found.last?.id,reply2.id,"its latest reply")}},{group:"core",name:"reads events after a cursor, each with a cursor of its own",run:async ctx=>{let{root}=needRoot(ctx),page=await ctx.board.poll({channel:ctx.channel,after:ctx.start});isRef(page.cursor,"the events' cursor");let posted=page.events.find(event=>event.type==="message.posted"&&event.message?.id===root.id);check(posted,`the events should include ${root.id} being posted`),isRef(posted.cursor,"an event's cursor"),check(!(await ctx.board.poll({channel:ctx.channel,after:page.cursor})).events.some(event=>event.message?.id===root.id),"reading on from the cursor should not repeat what it read")}},{group:"core",name:"holds a long-poll open until the next event",run:async ctx=>{let{cursor}=await ctx.board.poll({channel:ctx.channel,label:ctx.labels}),waiting=ctx.board.poll({channel:ctx.channel,label:ctx.labels,after:cursor,wait:10});await new Promise(resolve2=>setTimeout(resolve2,200));let later=await post(ctx,"conformance: while it waits");check((await waiting).events.some(event=>event.message?.id===later.id),"the long-poll should return the post made while it waited")}},{group:"core",name:"deletes a message, leaving a tombstone",run:async ctx=>{let message=await post(ctx,"conformance: to delete");await ctx.board.delete(message.id);let{message:after}=await ctx.board.thread(message.id);check(after.deleted&&after.text==="",`a deleted message should be deleted with no text, got ${show({deleted:after.deleted,text:after.text})}`)}},{group:"core",name:"lists the channel and you in it",run:async ctx=>{let found=(await ctx.board.channels()).find(c=>c.path===ctx.channel);check(found,`the channels should include ${ctx.channel}`),check(typeof found.lastIndex=="number",`a channel's lastIndex should be a number, got ${show(found.lastIndex)}`),check((await ctx.board.actors()).some(a=>a.id===ctx.me.actor),`the actors should include ${ctx.me.actor}`)}},{group:"core",name:"answers 404 for an ID that names nothing",run:async ctx=>{await rejects(ctx.board.thread(`no-such-message-${ctx.labels.conformance}`),404,"message_not_found","reading a message that was never posted")}},{group:"core",name:"answers 400 for a request that does not validate",run:async ctx=>{await rejects(ctx.board.post("Not A Channel",{text:"conformance",noSync:!0}),400,"invalid_request","posting to a malformed channel")}},{group:"schedules",name:"schedules a reply, lists it, and cancels it",run:async ctx=>{let{root}=needRoot(ctx),schedule=await ctx.board.schedule({re:root.id},{text:"conformance: later",noSync:!0,in:"1d"});isRef(schedule.id,"a schedule's id"),same(schedule.re,root.id,"the schedule's re"),same(schedule.state,"pending","the schedule's state"),check((await ctx.board.schedules({re:root.id})).some(s=>s.id===schedule.id),`the schedules should include ${schedule.id}`);let{schedule:canceled}=await ctx.board.cancelSchedule(schedule.id);same(canceled.state,"canceled","the canceled schedule")}},{group:"schedules",name:"keeps noSync on a schedule, so its sends stay off every platform",run:async ctx=>{let{root}=needRoot(ctx),schedule=await ctx.board.schedule({re:root.id},{text:"conformance: later, on the board only",noSync:!0,in:"1d"});try{same(schedule.noSync,!0,"the schedule's noSync"),same((await ctx.board.schedules({re:root.id})).find(s=>s.id===schedule.id)?.noSync,!0,"the listed schedule's noSync")}finally{await ctx.board.cancelSchedule(schedule.id).catch(()=>{})}}},{group:"attachments",name:"takes a file, posts it, and gives it back",run:async ctx=>{let bytes=new TextEncoder().encode(`conformance ${ctx.labels.conformance}
|
|
22
|
+
`),attachment=await ctx.board.upload({data:bytes,name:"conformance.txt",type:"text/plain"});check((await post(ctx,"conformance: a file",{attachments:[attachment.id]})).attachments.some(a=>a.id===attachment.id),"the post should carry the file");let response=await ctx.board.download(attachment.id);same(new TextDecoder().decode(await response.arrayBuffer()),new TextDecoder().decode(bytes),"the file's bytes")}},{group:"watchers",name:"matches a registered tail, hands the match to a pull, and takes its ack",run:async ctx=>{let registered=(await ctx.board.watchers()).find(r=>r.owner===ctx.me.principal);if(registered&&(Object.keys(registered.watch).length>0||(registered.sync?.length??0)>0))return"this token has watchers or a sync registered here, and checking would replace them";let where=wokenAt(registered);await ctx.board.register({url:null,watch:{conformance:{tail:`${ctx.channel} --label conformance=${ctx.labels.conformance}`}}});try{let message=await post(ctx,"conformance: for a watcher"),deadline=Date.now()+1e4,match;for(;!match&&Date.now()<deadline;)match=(await ctx.board.matches({wait:5})).find(m=>m.event.message?.id===message.id);check(match,"a pull should hand out the match for the post"),same(match.watcher,"conformance","the match's watcher"),await ctx.board.ack(match.id,{lease:match.lease}),await rejects(ctx.board.ack(match.id,{lease:match.lease}),404,"no_match","acking a match twice")}finally{await ctx.board.register({...where,watch:{}})}}},{group:"watchers",name:"wakes by path only where its discovery says so, and only a Vercel project",run:async ctx=>{let registered=(await ctx.board.watchers()).find(r=>r.owner===ctx.me.principal);if(registered&&(Object.keys(registered.watch).length>0||(registered.sync?.length??0)>0))return"this token has watchers or a sync registered here, and checking would replace them";let byPath=(await ctx.board.discovery()).wake_by_path,environment3=/^vercel:project:[^:]+:(.+)$/.exec(ctx.me.principal)?.[1]??null;if(byPath&&environment3!==null&&environment3!=="production"&&environment3!=="preview")return`a path wakes production or a preview, and this token is ${environment3}'s`;let where=wokenAt(registered),branch=environment3==="preview"?{branch:"conformance"}:{},attempt=()=>ctx.board.register({path:"/conformance",...branch,watch:{}});try{if(!byPath)await rejects(attempt(),501,"no_wake_by_path","a path, on a board that wakes only urls");else if(environment3===null)await rejects(attempt(),403,"not_a_project","a path, from a token that isn't a Vercel project's");else{let view=await attempt();same(view.path,"/conformance","the registration's path"),same(view.url,null,"the registration's url"),same(view.branch??null,environment3==="preview"?"conformance":null,"the registration's branch")}}finally{await ctx.board.register({...where,watch:{}})}}},{group:"sync",name:"keeps sync's records: an import as a platform author, its lookups by platform ID, a receipt, and where it's synced",run:async ctx=>{let key=SYNC_KEY,thread=`${key}:${ctx.labels.conformance}:1`,author={id:`${key}:user:${ctx.labels.conformance}`,handle:"conformance",name:"Conformance",avatar:null,isBot:!1},origin={thread,message:"1"},registered=await registerSync(ctx);if("skip"in registered)return registered.skip;let{where}=registered,imported;try{imported=await ctx.board.syncImport({op:"post",author,from:key,channel:ctx.channel,text:"conformance: from a platform",labels:ctx.labels,noSync:!0,origin:{channel:`${key}:${ctx.labels.conformance}`,thread,subthread:null,message:"1",context:null},id:`conformance-sync-${ctx.labels.conformance}`})}catch(error){if(await ctx.board.register({...where,watch:{}}).catch(()=>{}),error instanceof BoardError&&error.status===403)return`this token can't sync in ${ctx.channel}`;throw error}let id2=imported.message;isRef(id2,"an import's message");try{let message=(await ctx.board.thread(id2)).message;same(message.by.actor,author.id,"the import's author"),same(message.from,key,"the import's from"),same(message.origin?.thread,thread,"the import's origin");let found=await ctx.board.syncFind({thread,message:"1"});same(found.message,id2,"the message found by its platform IDs"),check(found.thread!==null,"the thread found by its platform thread"),same((await ctx.board.syncThread(found.thread)).root,id2,"the synced thread's root"),same((await ctx.board.syncImport({op:"post",author,from:key,channel:ctx.channel,text:"conformance: from a platform",noSync:!0,id:`conformance-sync-${ctx.labels.conformance}`})).message,id2,"the same import, sent again"),await rejects(ctx.board.syncImport({op:"post",author,from:key,channel:ctx.channel,text:"conformance: a second root",noSync:!0,origin:{thread,subthread:null,message:"3",context:null}}),409,"already_synced","an import from a platform thread another board thread syncs with");let external={thread:`${key}:${ctx.labels.conformance}:2`,message:"2"};await ctx.board.syncReceipt({kind:"thread",message:id2,target:external.thread,targetKind:"thread",cause:"request"}),await ctx.board.syncReceipt({kind:"message",message:id2,target:external.thread,external}),same(JSON.stringify((await ctx.board.syncCopies([id2]))[id2]?.[external.thread]),JSON.stringify(external),"the copy its receipt recorded");let{targets}=await ctx.board.sync(id2);check(targets.some(row=>row.target===external.thread&&row.message===external.message),`where the thread is synced should show the copy its receipt recorded, got ${show(targets)}`),await rejects(ctx.board.linked(`https://conformance.invalid/${ctx.labels.conformance}`),404,"message_not_found","a link no sync here knows")}finally{await ctx.board.syncImport({op:"delete",author,from:key,origin}).catch(()=>{}),await ctx.board.syncImport({op:"delete",author,from:key,origin:{thread,message:"3"}}).catch(()=>{}),await ctx.board.register({...where,watch:{}}).catch(()=>{})}}},{group:"sync",name:"queues a platform thread's events in its sync's inbox once, and leases each after the one before is acked",run:async ctx=>{let registered=await registerSync(ctx);if("skip"in registered)return registered.skip;try{let run2=ctx.labels.conformance,author={id:`${SYNC_KEY}:user:${run2}`,handle:"conformance",name:"Conformance",avatar:null,isBot:!1},thread=`${SYNC_KEY}:${run2}:inbox`,eventOf=message=>({id:`conformance-inbox-${run2}-${message}`,event:{kind:"message",channel:`${SYNC_KEY}:${run2}`,thread,subthread:null,message,isRoot:message==="1",text:`conformance: inbox event ${message}`,context:null,author},raw:{conformance:run2,message}}),pushed=[eventOf("1"),eventOf("2")];await ctx.board.inboxPush(SYNC_KEY,pushed),await ctx.board.inboxPush(SYNC_KEY,[pushed[0]]);let lease=async()=>(await ctx.board.inboxNext(SYNC_KEY,{limit:50})).filter(item=>item.thread===thread);for(let{event,raw}of pushed){let leased=await lease();same(leased.length,1,`the events leased from the platform thread, for message ${event.message}`);let item=leased[0];same(item.event.kind==="message"?item.event.message:null,event.message,"the platform message leased next"),same(JSON.stringify(item.raw),JSON.stringify(raw),"the leased event's raw payload"),await ctx.board.inboxAck(SYNC_KEY,item.id,{lease:item.lease})}same((await lease()).length,0,"the events leased once both are acked")}finally{await ctx.board.register({...registered.where,watch:{}}).catch(()=>{})}}},{group:"sync",name:"hands a sync each message change as a match of its @sync watcher",run:async ctx=>{let access=compileAccess(ctx.me.can);if(access.anywhere("sync")&&!access.can("sync",ctx.channel))return`this token can't sync in ${ctx.channel}`;let registered=await registerSync(ctx);if("skip"in registered)return registered.skip;try{let message=await post(ctx,"conformance: for a sync"),deadline=Date.now()+1e4,match;for(;!match&&Date.now()<deadline;)match=(await ctx.board.matches({wait:5})).find(m=>m.watcher==="@sync"&&m.event.message?.id===message.id);check(match,"a pull should hand the sync a match for the post, as its @sync watcher's"),same(match.event.type,"message.posted","the sync's match"),await ctx.board.ack(match.id,{lease:match.lease})}finally{await ctx.board.register({...registered.where,watch:{}}).catch(()=>{})}}},{group:"sync",name:"keeps noSync on a post and a reply, so sync sends neither",run:async ctx=>{let{root,reply:reply2}=needRoot(ctx),thread=await ctx.board.thread(root.id);same(thread.message.noSync,!0,"a post's noSync, read back"),same(thread.replies.find(m=>m.id===reply2.id)?.noSync,!0,"a reply's noSync, read back")}},{group:"sync",name:"finds no one for a platform account nobody linked",run:async ctx=>{let found;try{found=await ctx.board.syncFind({account:`${SYNC_KEY}:user:${ctx.labels.conformance}`})}catch(error){if(error instanceof BoardError&&error.status===403)return"this token can't look up a sync here";throw error}same(found.person,null,"who an account nobody linked is")}}];async function conformance(board2,options2={}){let checks=[],discovery=await board2.discovery().catch(error=>error),wrong=discovery instanceof Error?discovery.message:typeof discovery.audience!="string"||typeof discovery.url!="string"||!Array.isArray(discovery.sync)?`discovery should have url, audience, and sync, got ${show(discovery)}`:null;if(wrong!==null)return checks.push({group:"core",name:"says in its discovery where it is",status:"failed",detail:wrong}),{url:board2.url,checks,ok:!1};let url=discovery.url;checks.push({group:"core",name:"says in its discovery where it is",status:"passed"});let me=await board2.me(),channel=options2.channel??"conformance",ctx={board:board2,channel,labels:{conformance:crypto.randomUUID().slice(0,8)},start:(await board2.messages({channel,limit:1})).cursor,me,posted:[]};try{for(let{group,name,run:run2}of CHECKS)try{let skipped=await run2(ctx);checks.push(skipped?{group,name,status:"skipped",detail:skipped}:{group,name,status:"passed"})}catch(error){OPTIONAL.has(group)&&error instanceof BoardError&&error.status===501?checks.push({group,name,status:"skipped",detail:`not kept here: ${error.message}`}):checks.push({group,name,status:"failed",detail:error instanceof Error?error.message:String(error)})}}finally{for(let id2 of[...ctx.posted].reverse())await board2.delete(id2).catch(()=>{})}return{url,checks,ok:checks.every(c=>c.status!=="failed")}}function fileName(name){return(name??"").split(/[\\/]/).pop().replace(/[\u0000-\u001f\u007f]/g,"").trim().slice(0,255)||"file"}function mediaType(type){let value=(type??"").split(";")[0].trim().toLowerCase();return/^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/.test(value)&&value.length<=255?value:"application/octet-stream"}var PROFILE3=/^[A-Za-z][A-Za-z0-9-]{0,31}$/,SESSION=/^[A-Za-z0-9_.:-]{1,128}$/,CLIENT_ID=/^[A-Za-z0-9_.:-]{1,128}$/,MESSAGE_ID=/^msg_([0-9a-f-]{36})_([1-9]\d*)$/,CURSOR=/^([0-9a-z]+)(?:\.([0-9a-z]+))?$/,API_CURSOR=/^c1\./,PAGE_MAX=100,WAIT_MAX_S=25,SCAN_PAGES=10,fileOf=({id:id2,name,type,size})=>({id:id2,name,type,size}),notSupported=what=>new HttpError(501,"not_supported",`Vercel's boards don't support ${what} yet`),accessOf=caller=>"identity"in caller?caller.identity.principal.access:null;function need(access,permission,channel){if(access&&!access.can(permission,channel))throw forbidden(`this token can't ${permission} in ${channel}`)}var apiKind=kind=>kind==="app"?"workflow":kind;function operationId(){let bytes=crypto.getRandomValues(new Uint8Array(16)),now=BigInt(Date.now());for(let i=0;i<6;i++)bytes[i]=Number(now>>BigInt(8*(5-i))&255n);bytes[6]=bytes[6]&15|112,bytes[8]=bytes[8]&63|128;let hex=Array.from(bytes,byte=>byte.toString(16).padStart(2,"0")).join("");return`${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`}var iso=ms=>new Date(ms).toISOString(),idAt=(threadId,sequence)=>`msg_${threadId.slice(4)}_${sequence}`;function locate(id2){let found=MESSAGE_ID.exec(id2);return found?{threadId:`thr_${found[1]}`,sequence:Number(found[2])}:null}function principalId(principal){return principal.type==="user"?`vercel:user:${principal.userId}`:principal.type==="app"?`vercel:app:${principal.clientId}`:`vercel:project:${principal.projectId}:${principal.environment}`}var authorOf=actor=>({actor:actor.id,principal:principalId(actor.principal),profile:actor.profile?.name??null,session:actor.session??null}),SCHEDULER={actor:"board:schedule",principal:"board:schedule",profile:null,session:null};function actorView(actor){let{principal}=actor,base=principal.type==="user"?`user-${principal.userId.slice(0,6).toLowerCase()}`:principal.type==="app"?`app-${principal.clientId.slice(0,6).toLowerCase()}`:principal.environment==="production"?principal.projectId:`${principal.projectId}@${principal.environment}`,handle=actor.profile?.name??base,kind=actor.profile?"agent":principal.type==="user"?"person":principal.type==="project"?"deployment":"workflow";return{id:actor.id,kind,handle,name:handle,avatar:actor.profile?.avatar??null}}function createVercel(options2,environment3){let origin=(options2.api??"https://api.vercel.com").replace(/\/+$/,"");if(!/^https:\/\/[^/]+$|^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin))throw new Error(`vercel: ${origin} isn't Vercel's API: pass an https origin`);if(!options2.fetch&&!environment3)throw new Error("vercel: pass a fetch that sends credentials: outside Node there's no environment to find them in");let doFetch=options2.fetch??environment3.fetch(),filesFetch=options2.files??fetch,binding=null,own2=new Set,team2=null,teamId=()=>team2??=options2.team?Promise.resolve(options2.team):environment3?.team()??Promise.resolve(null);async function api(who,method,path,init={}){let url=new URL(`${origin}/v1/messageboard${path}`),id2=await teamId();id2&&url.searchParams.set(id2.startsWith("team_")?"teamId":"slug",id2);for(let[name,value]of Object.entries(init.query??{}))value!==void 0&&url.searchParams.set(name,value);let headers={"content-type":"application/json"};who.profile&&(headers["x-board-profile"]=who.profile.name),who.profile?.avatar?.startsWith("https://")&&(headers["x-board-avatar"]=who.profile.avatar),who.session&&SESSION.test(who.session)&&(headers["x-board-session"]=who.session);let response;try{response=await doFetch(url,{method,headers,redirect:"error",...init.body!==void 0?{body:JSON.stringify(init.body)}:{},...init.signal?{signal:init.signal}:{}})}catch(error2){throw error2 instanceof HttpError||error2 instanceof Error&&error2.name==="AbortError"?error2:new HttpError(502,"unreachable",`Vercel's API didn't answer: ${error2 instanceof Error?error2.message:String(error2)}`)}let text2=await response.text(),body;try{body=text2?JSON.parse(text2):void 0}catch{}if(response.ok)return body;let error=body?.error;throw new HttpError(response.status,error?.code??"remote_error",error?.message??`Vercel's API answered ${response.status}`)}async function all(who,path,key,query={},signal){let out2=[],cursor;do{let page=await api(who,"GET",path,{query:{...query,cursor},...signal?{signal}:{}});out2.push(...page[key]),cursor=page.cursor??void 0}while(cursor);return out2}let board2=null,boardId=who=>(board2??=options2.board?Promise.resolve(options2.board):api(who,"GET","/boards").then(async({defaultBoardId})=>{if(defaultBoardId)return defaultBoardId;if((await self(who)).actor.principal.type==="project")throw new HttpError(404,"no_board","Vercel's API names no board this project may use: ask someone on the team to grant it the team's board, or pass `board`");let{board:made}=await api(who,"POST","/boards/default",{body:{operationId:operationId()}});return made.id})).catch(error=>{throw board2=null,error}),onBoard=async(who,path="")=>`/boards/${encodeURIComponent(await boardId(who))}${path}`,channels=null,loadChannels=async(who,fresh=!1)=>(fresh&&(channels=null),channels??=onBoard(who,"/channels").then(path=>all(who,path,"channels")),channels.catch(error=>{throw channels=null,error})),named2=new Map;async function channelNamed(who,name){let known=named2.get(name);if(known)return known;let{channels:[found]}=await api(who,"GET",await onBoard(who,"/channels"),{query:{name}});return found&&named2.set(name,found),found??null}async function channelById(who,id2){let found=(await loadChannels(who)).find(channel=>channel.id===id2)??(await loadChannels(who,!0)).find(channel=>channel.id===id2);if(!found)throw new HttpError(502,"remote_error",`Vercel's API names a channel it doesn't list: ${id2}`);return found}async function channelToPost(who,name){let found=await channelNamed(who,name);if(found)return found;try{let{channel}=await api(who,"POST",await onBoard(who,"/channels"),{body:{operationId:operationId(),name}});return channels=null,named2.set(name,channel),channel}catch(error){if(error instanceof HttpError&&error.code==="channel_name_taken"){let made=await channelNamed(who,name);if(made)return made}throw error}}async function channelsFor(who,filter,access){let readable=channel=>access===null||access.readable(channel.name);if(filter!==void 0&&!filter.endsWith("/")){let found=await channelNamed(who,filter);return found&&readable(found)?[found]:[]}let listed2=(await loadChannels(who,!0)).filter(readable);if(filter===void 0)return listed2;let path=filter.slice(0,-1);return listed2.filter(channel=>channel.name===path||channel.name.startsWith(filter))}async function messageById(who,id2,signal){if(!locate(id2))throw notFound(`message not found: ${id2}`,"message_not_found");try{return await api(who,"GET",await onBoard(who,`/messages/${encodeURIComponent(id2)}`),signal?{signal}:{})}catch(error){throw error instanceof HttpError&&error.code==="thread_not_found"?notFound(`message not found: ${id2}`,"message_not_found"):error}}async function readableMessage(who,access,id2,signal){let found=await messageById(who,id2,signal),channel=await channelById(who,found.thread.channelId);if(access&&!access.readable(channel.name))throw notFound(`message not found: ${id2}`,"message_not_found");return{...found,channel}}function threadReader(who,signal){let read2=new Map;return id2=>{let thread=read2.get(id2);return thread||read2.set(id2,thread=onBoard(who,`/threads/${encodeURIComponent(id2)}`).then(path=>api(who,"GET",path,signal?{signal}:{})).then(found=>found.thread)),thread}}let threadMessages=async(who,threadId,signal)=>all(who,await onBoard(who,`/threads/${encodeURIComponent(threadId)}/messages`),"messages",{},signal);async function reactors(who,message){if(!message.reactions||Object.keys(message.reactions).length===0)return{};let named3=message.reactors??{};if(Object.entries(message.reactions).every(([emoji,count])=>(named3[emoji]?.length??0)>=count))return Object.fromEntries(Object.entries(named3).map(([emoji,actors])=>[emoji,[...actors]]));let path=await onBoard(who,`/threads/${encodeURIComponent(message.threadId)}/messages/${message.sequence}/reactions`),out2={};for(let reaction of await all(who,path,"reactions"))(out2[reaction.emoji]??=[]).push(reaction.actor.id);return out2}let actorNames=null,NAMES_FRESH_MS=3e5,unlisted=new Set,readNames=who=>{let views=(async()=>new Map((await all(who,await onBoard(who,"/actors"),"actors")).map(view2=>[view2.id,view2])))(),read2={at:Date.now(),views};return actorNames=read2,views.catch(()=>{actorNames===read2&&(actorNames=null)}),read2},inList=(read2,id2)=>read2.views.then(views=>views.get(id2),()=>{});async function nameOf(who,actor){let current2=!actorNames||Date.now()-actorNames.at>NAMES_FRESH_MS?readNames(who):actorNames,found=await inList(current2,actor.id);if(!found&&!unlisted.has(actor.id)){let again=actorNames&&actorNames!==current2?actorNames:readNames(who);found=await inList(again,actor.id),!found&&await again.views.then(()=>!0,()=>!1)&&unlisted.add(actor.id)}let kind=found&&apiKind(found.kind);return!found||!(kind==="person"||kind==="agent"&&actor.principal.type==="user")?actorView(actor):{id:actor.id,kind,handle:found.handle,name:found.name??found.handle,avatar:found.avatar??null}}async function view(who,message,channel){let root=message.sequence===1;return{id:message.id,channel,text:message.text,re:root?null:idAt(message.threadId,1),replyTo:message.replyTo!==void 0&&message.replyTo!==1?idAt(message.threadId,message.replyTo):null,thread:message.threadId,labels:{...message.labels},reactions:message.deletedAt===void 0?await reactors(who,message):{},mentions:[],by:authorOf(message.createdBy),noSync:!1,origin:null,from:null,createdAt:iso(message.createdAt),editedAt:message.editedAt===void 0?null:iso(message.editedAt),deleted:message.deletedAt!==void 0,undelivered:{},attachments:message.attachments??[],links:[],version:message.version,revision:message.revision,author:await nameOf(who,message.createdBy),...message.schedule?{schedule:{id:message.schedule.id,n:message.schedule.n}}:{}}}async function scheduleView(who,schedule){let{canceled}=schedule;return{id:schedule.id,channel:(await channelById(who,schedule.channelId)).name,re:schedule.threadId===void 0?null:idAt(schedule.threadId,1),text:schedule.text,labels:{...schedule.labels},noSync:!1,attachments:[],first:iso(schedule.firstAt),every:schedule.every??null,until:schedule.until===void 0?null:iso(schedule.until),times:schedule.times??null,sent:schedule.sent,next:schedule.nextAt===null?null:iso(schedule.nextAt),last:schedule.lastMessageId??null,state:schedule.state,canceled:canceled?{by:canceled.by?authorOf(canceled.by):SCHEDULER,reason:canceled.reason==="disabled"?"revoked":canceled.reason}:null,by:authorOf(schedule.createdBy),author:await nameOf(who,schedule.createdBy),createdAt:iso(schedule.createdAt)}}let positions=new Map;function cursorOf(position){let key=`${position.t.toString(36)}.${Math.floor(Math.random()*2821109907456).toString(36)}`;return positions.set(key,position),positions.size>1e4&&positions.delete(positions.keys().next().value),key}function positionOf(cursor){let kept=positions.get(cursor);if(kept)return kept;let found=CURSOR.exec(cursor);if(!found)throw new HttpError(400,"invalid_cursor",`after: ${cursor} isn't a cursor here or a message's ID`);return{t:parseInt(found[1],36),feeds:{}}}async function timeOf(who,after){return locate(after)?(await messageById(who,after)).message.createdAt:positionOf(after).t}async function heads(who,listed2){let path=await onBoard(who,"/changes"),found=await Promise.all(listed2.map(channel=>api(who,"GET",path,{query:{channelId:channel.id,from:"latest"}})));return Object.fromEntries(listed2.map((channel,i)=>[channel.id,{at:found[i].cursor,skip:0}]))}function matches(filters3,message,thread,me2){if(filters3.re!==void 0&&filters3.re!==message.id&&filters3.re!==idAt(thread.id,1))return!1;if(filters3.by!==void 0){let{createdBy}=message,names=[createdBy.id,principalId(createdBy.principal),createdBy.profile?.name];if(!(filters3.by==="me"?createdBy.id===me2:names.includes(filters3.by)))return!1}if(filters3.mentions!==void 0||filters3.from!==void 0&&filters3.from!=="board"||filters3.text!==void 0&&!message.text.toLowerCase().includes(filters3.text.toLowerCase()))return!1;for(let[key,values]of filters3.label??[]){let on=labels2=>labels2!==void 0&&Object.hasOwn(labels2,key)&&(values===null||values.includes(labels2[key]));if(!on(message.labels)&&!on(thread.labels))return!1}return!0}function indexed(filters3){let first=filters3.label?.find(([,values])=>values===null||values.length===1);return first&&(first[1]===null?first[0]:`${first[0]}=${first[1][0]}`)}async function threadsOf(who,channel,filters3,signal){let label=indexed(filters3);return all(who,await onBoard(who,"/threads"),"threads",{channelId:channel.id,label},signal)}async function latestThreads(who,channel,label,page,signal){let path=await onBoard(who,"/threads"),out2=[],cursor;do{let found=await api(who,"GET",path,{query:{channelId:channel.id,label,cursor},signal});out2.push(...found.threads),cursor=found.cursor??void 0;let last=out2.at(-1);if(label===void 0&&(page.after!==void 0?last!==void 0&&last.lastActivityAt<=page.after:page.limit!==void 0&&out2.length>=page.limit))break}while(cursor);return out2}async function summary(who,thread,channel,signal){let[root,last]=await Promise.all([messageById(who,idAt(thread.id,1),signal),thread.lastMessage&&thread.lastMessage.sequence>1?messageById(who,thread.lastMessage.id,signal):null]);return{message:await view(who,root.message,channel.name),replies:thread.messageCount-1,last:last&&await view(who,last.message,channel.name)}}let me=null,self=who=>(me??=api(who,"GET","/me")).catch(error=>{throw me=null,error}),actorOf=async who=>`${principalId((await self(who)).actor.principal)}${who.profile?`+${who.profile.name.toLowerCase()}`:""}`;function whoOf(caller){if("identity"in caller){let avatar=caller.identity.actor.avatar?.startsWith("https://")?caller.identity.actor.avatar:void 0;return{profile:{name:callerProfile(caller.identity.actor.handle,own2),...avatar?{avatar}:{}},session:null}}let named3=profile=>{if(profile===null)return null;let name=typeof profile=="string"?profile:profile.name;if(!PROFILE3.test(name))throw new HttpError(400,"invalid_request",`as: ${name}: profiles start with a letter and use up to 32 letters, numbers, and hyphens`);return typeof profile=="string"||!profile.avatar?{name}:{name,avatar:profile.avatar}};if(caller.origin.via?.kind==="client"){let asked="profile"in caller?caller.profile:caller.self??void 0,found=environment3?.profile()??{profile:null,session:null};return asked===void 0?{profile:named3(found.profile),session:found.session}:{profile:named3(asked),session:asked===null?null:found.session}}return{profile:named3(caller.self??"client"),session:null}}let clientIdOf=async id2=>CLIENT_ID.test(id2)?id2:`h.${(await sha256(id2)).slice(0,64)}`,postId=async(caller,id2)=>clientIdOf("identity"in caller?await derivedId("board-gateway",caller.identity.principal.id,id2):id2);async function collect(who,access,filters3,signal){let listed2,threads;if(filters3.re!==void 0){let found=await readableMessage(who,access,filters3.re,signal).catch(error=>{if(error instanceof HttpError&&error.status===404)return null;throw error});if(!found)return{messages:[],feeds:{},newest:0};let{channel}=found;if(filters3.channel!==void 0&&!(await channelsFor(who,filters3.channel,access)).some(c=>c.id===channel.id))return{messages:[],feeds:{},newest:0};listed2=[channel],threads=[{thread:found.thread,channel}]}else listed2=await channelsFor(who,filters3.channel,access),threads=(await Promise.all(listed2.map(async channel=>(await threadsOf(who,channel,filters3,signal)).map(thread=>({thread,channel}))))).flat();let feeds=await heads(who,listed2),mine=await actorOf(who),newest=0,messages=[];for(let{thread,channel}of threads)for(let message of await threadMessages(who,thread.id,signal))newest=Math.max(newest,message.createdAt),matches(filters3,message,thread,mine)&&messages.push({message,thread,channel});return messages.sort((a,b)=>a.message.createdAt-b.message.createdAt||a.thread.id.localeCompare(b.thread.id)||a.message.sequence-b.message.sequence),{messages,feeds,newest}}async function eventOf(who,change,channel,cursor,filters3,me2,given){if(!change.messageId||!change.type.startsWith("message."))return null;let found=given??await messageById(who,change.messageId).catch(error=>{if(error instanceof HttpError&&error.status===404)return null;throw error});if(!found||!matches(filters3,found.message,found.thread,me2))return null;let message=await view(who,found.message,channel),by=authorOf(change.actor),base={id:message.id,thread:message.thread},type,payload;switch(change.type){case"message.posted":type="message.posted",payload={channel,text:message.text,re:message.re,replyTo:message.replyTo,thread:message.thread,labels:message.labels,noSync:!1,mentions:[],by,origin:null,from:null,attachments:message.attachments,links:[]};break;case"message.edited":type="message.edited",payload={...base,text:message.text,mentions:[],by,from:null};break;case"message.deleted":type="message.deleted",payload={...base,by,from:null};break;case"message.reaction_added":case"message.reaction_removed":type=change.type==="message.reaction_added"?"reaction.added":"reaction.removed",payload={...base,emoji:change.emoji,by,from:null};break;case"message.labeled":{let changed=Object.entries(change.labels??{});type="labels.changed",payload={...base,set:Object.fromEntries(changed.flatMap(([key,{to}])=>to===void 0?[]:[[key,to]])),unset:changed.flatMap(([key,{to}])=>to===void 0?[key]:[]),by};break}default:return null}return{cursor,type,createdAt:iso(change.createdAt),payload,message,actor:await nameOf(who,change.actor)}}async function read(who,listed2,position,filters3,signal){let path=await onBoard(who,"/changes"),pages=await Promise.all(listed2.map(async channel=>{let from=position.feeds[channel.id],page=await api(who,"GET",path,{query:{channelId:channel.id,...from?.at?{cursor:from.at}:{}},signal}),skip=from?.skip??0,events3=page.events.map((change,i)=>({change,i})).slice(skip).filter(({change})=>from||change.createdAt>position.t);return{channel,at:from?.at??null,next:page.cursor,events:events3}})),mine=await actorOf(who),running={...position.feeds},merged=pages.flatMap(page=>page.events.map(event=>({...event,page}))).sort((a,b)=>a.change.createdAt-b.change.createdAt),events2=[];for(let{change,i,page}of merged){running[page.channel.id]={at:page.at,skip:i+1};let event=await eventOf(who,change,page.channel.name,cursorOf({t:change.createdAt,feeds:{...running}}),filters3,mine);event&&events2.push(event)}let after={...position.feeds};for(let page of pages)after[page.channel.id]={at:page.next,skip:0};return{events:events2,position:{t:Math.max(position.t,...merged.map(({change})=>change.createdAt)),feeds:after}}}let filtered=filters3=>filters3.by!==void 0||filters3.mentions!==void 0||filters3.text!==void 0||filters3.from!==void 0||(filters3.label?.length??0)>0;async function channelMessages(who,channel,query,signal){let path=await onBoard(who,"/messages"),limit=query.limit??50,thread=threadReader(who,signal),narrowed=filtered(query),me2=narrowed?await actorOf(who):null,keep=async message=>!narrowed||matches(query,message,query.label?.length?await thread(message.threadId):{id:message.threadId},me2),kept=[];if(query.after===void 0){let page=await api(who,"GET",path,{query:{channelId:channel.id,limit:String(narrowed?PAGE_MAX:Math.min(limit,PAGE_MAX))},signal});for(let message of page.messages)await keep(message)&&kept.push(message);return{messages:kept.slice(-limit),cursor:page.cursor}}let cursor=query.after;for(let pages=0;pages<SCAN_PAGES&&kept.length<limit;pages++){let page=await api(who,"GET",path,{query:{channelId:channel.id,after:cursor,limit:String(Math.min(limit-kept.length,PAGE_MAX))},signal});cursor=page.cursor;for(let message of page.messages)await keep(message)&&kept.push(message);if(page.messages.length===0)break}return{messages:kept,cursor}}async function channelEvents(who,channel,query,signal){let path=await onBoard(who,"/changes"),thread=threadReader(who,signal),me2=await actorOf(who),until2=Date.now()+(query.wait??0)*1e3,cursor=query.after;for(;;){let wait=Math.min(WAIT_MAX_S,Math.max(0,Math.ceil((until2-Date.now())/1e3))),page=await api(who,"GET",path,{query:{channelId:channel.id,include:"message",...cursor===void 0?{from:"latest"}:{cursor},...wait>0?{wait:String(wait)}:{}},signal});cursor=page.cursor;let events2=[];for(let change of page.events){let message=change.currentMessage,found=message&&change.threadId?{message,thread:query.label?.length?await thread(change.threadId):{id:change.threadId}}:null;if(!found)continue;let event=await eventOf(who,change,channel.name,change.cursor??page.cursor,query,me2,found);event&&events2.push(event)}if(events2.length>0||Date.now()>=until2||signal.aborted)return{events:events2,cursor}}}async function oneChannel(who,access,query){if(query.channel===void 0||query.channel.endsWith("/")||query.re!==void 0||query.after!==void 0&&!API_CURSOR.test(query.after))return null;let[channel]=await channelsFor(who,query.channel,access);return channel??null}async function actorsSeen(who,access,signal){let path=await onBoard(who,"/messages"),seen=new Set;for(let channel of await channelsFor(who,void 0,access)){let{messages}=await api(who,"GET",path,{query:{channelId:channel.id,limit:String(PAGE_MAX)},signal});for(let message of messages){seen.add(message.createdBy.id);for(let actors of Object.values(message.reactors??{}))for(let actor of actors)seen.add(actor)}}return seen}function checkCaller(method,caller){if("identity"in caller&&PROJECTS_OWN.has(method))throw forbidden(`this board's callers act as its project on Vercel's board, which can't tell them apart: ${method} is the project's own to do there`)}let isProject=async(caller,who)=>!("identity"in caller)||caller.identity.principal.id===principalId((await self(who)).actor.principal);async function checkOwner(what,caller,who){if(!await isProject(caller,who))throw forbidden(`this board's callers act as its project on Vercel's board, which owns what any of them wrote: only the project itself may ${what} there`)}async function checkUploader(caller,who){await checkOwner("upload",caller,who);let access=accessOf(caller);if(access&&!access.anywhere("post")&&!access.anywhere("sync"))throw forbidden("this token can't post anywhere, so it can't upload")}let meOf=identity2=>{let{principal,author,actor}=identity2;return{actor:actor.id,principal:principal.id,kind:actor.kind,handle:actor.handle,name:actor.name,avatar:actor.avatar,profile:author.profile,session:author.session,expiresAt:principal.expiresAt?.toISOString()??null,can:principal.access.summary()}},tagOf=async watch=>(await sha256(canonicalWatchers(watch))).slice(0,32);async function authorsOf(who,by){if(by==="me")return[await actorOf(who)];if(by.startsWith("vercel:"))return[by];let named3=(await all(who,await onBoard(who,"/actors"),"actors")).filter(actor=>actor.handle===by||actor.id.endsWith(`+${by.toLowerCase()}`)).map(actor=>actor.id);if(named3.length===0)throw new HttpError(400,"bad_watcher",`--by ${by}: nobody here goes by that yet: name them by their actor ID`);return named3}async function filterOf(who,id2,spec){let bad=message=>new HttpError(400,"bad_watcher",`watch ${id2}: ${message}`);if(!RemoteWatcherId.safeParse(id2).success)throw bad("ids start with a letter and use up to 32 letters, numbers, and hyphens");let filters3;try{({filters:filters3}=compileWatcher(id2,spec))}catch(error){throw new HttpError(400,"bad_watcher",error.message)}if(filters3.mentions!==void 0)throw bad("Vercel's boards can't match mentions yet");if(filters3.from!==void 0&&filters3.from!=="board")throw bad(`nothing comes from ${filters3.from} here: every message is written on the board`);let filter={};if(filters3.channel!==void 0)if(filters3.channel.endsWith("/"))filter.channelTree=filters3.channel.slice(0,-1);else{let found=await channelNamed(who,filters3.channel);found?filter.channelIds=[found.id]:filter.channelTree=filters3.channel}if(filters3.re!==void 0){let at=locate(filters3.re);if(!at)throw bad(`--re ${filters3.re} names no message here`);filter.threadId=at.threadId}return filters3.by!==void 0&&(filter.authors=await authorsOf(who,filters3.by)),filters3.text!==void 0&&(filter.text=filters3.text),filters3.label?.length&&(filter.labels=filters3.label.map(([key,values])=>values===null?{key}:{key,values:[...values]})),{filter,when:spec.when??"matches"}}async function tailOf(who,{filter}){let parts=[];filter.channelIds?.[0]?parts.push((await channelById(who,filter.channelIds[0])).name):filter.channelTree!==void 0&&parts.push(`${filter.channelTree}/`),filter.threadId!==void 0&&parts.push(`--re ${idAt(filter.threadId,1)}`),filter.authors?.[0]&&parts.push(`--by ${filter.authors[0]}`),filter.text!==void 0&&parts.push(`--text ${JSON.stringify(filter.text)}`);for(let{key,values}of filter.labels??[])parts.push(`--label ${values?`${key}=${values.join("|")}`:key}`);return parts.join(" ")}let whereOf=found=>found.path?{url:null,path:found.path,...found.branch?{branch:found.branch}:{}}:{url:found.url};async function registerAs(who,where,watch){let specs=Object.fromEntries(Object.entries(watch).map(([id2,spec])=>[id2,{tail:spec.tail,when:spec.when??"matches"}])),filters3=Object.fromEntries(await Promise.all(Object.entries(specs).map(async([id2,spec])=>[id2,await filterOf(who,id2,spec)]))),made=await api(who,"PUT",await onBoard(who,"/watchers"),{body:{...where,watch:filters3,tag:await tagOf(specs)}});return{owner:made.owner,...whereOf(made),watch:specs,sync:[],hash:made.hash}}async function matchOf(who,match,me2){let found=await messageById(who,match.event.messageId).catch(error=>{if(error instanceof HttpError&&error.status===404)return null;throw error}),channel=found&&await channelById(who,found.thread.channelId),event=found&&channel&&await eventOf(who,match.event,channel.name,match.id,{},me2,found);return event?.message?{id:match.id,watcher:match.watcher,thread:event.message.thread,lease:match.lease,attempt:match.attempt,event}:null}async function pullAs(who,query,signal){let{matches:matches2}=await api(who,"GET",await onBoard(who,"/watchers/matches"),{query:{limit:query.limit?.toString(),wait:query.wait?.toString()},...signal?{signal}:{}}),me2=await actorOf(who),shown3=await Promise.all(matches2.map(match=>matchOf(who,match,me2)));return await Promise.all(matches2.map((match,i)=>shown3[i]?null:ackAs(who,match.id,{lease:match.lease,skip:!0}).catch(()=>{}))),shown3.filter(match=>match!==null)}async function ackAs(who,id2,body){await api(who,"POST",await onBoard(who,`/watchers/matches/${encodeURIComponent(id2)}/ack`),{body:{lease:body.lease,...body.error!==void 0?{error:body.error}:{},...body.skip?{skip:!0}:{}}})}function ownWatchers(caller,what){if("identity"in caller)throw forbidden(`this board registers watchers as its own deployment, so it can't ${what} for you: run them in its config, or register with Vercel's API as yourself`)}let deployment={profile:null,session:null},tails={},runner=registrant({remote:"Vercel's API",registry:{register:where=>registerAs(deployment,where,tails),matches:({signal,...query})=>pullAs(deployment,query,signal),ack:(id2,body)=>ackAs(deployment,id2,body)},wakeAt:async here=>wakePath(here)??{url:here},binding:()=>{if(!binding)throw new Error("vercel: used before board() bound it");return binding},hash:()=>tagOf(tails),hasWork:()=>Object.keys(tails).length>0,rethrow:error=>{throw error},registerFor:async(caller,request)=>json(await methods.register(caller,RegisterBody.parse(await request.json()),request.signal))}),methods={...Object.fromEntries(METHODS.map(method=>[method,async()=>{throw notSupported(method==="register"||method==="watchers"||method==="matches"||method==="ack"?"watchers":method)}])),async discovery(){let who={profile:null,session:null},team3=await teamId();options2.board&&await api(who,"GET",await onBoard(who));let id2=team3?.startsWith("team_")?team3:(await self(who)).teamId;return{version:VERSION,url:`${origin}/v1/messageboard${await onBoard(who)}?teamId=${encodeURIComponent(id2)}`,audience:origin,team_id:id2,project_id:null,sync:[],wake_by_path:!0}},async me(caller){if("identity"in caller)return meOf(caller.identity);let found=await api(whoOf(caller),"GET","/me"),{view:named3}=found,shown3=named3?{kind:apiKind(named3.kind),handle:named3.handle,name:named3.name??named3.handle,avatar:named3.avatar??null}:actorView(found.actor);return{actor:found.actor.id,principal:principalId(found.actor.principal),kind:shown3.kind,handle:shown3.handle,name:shown3.name,avatar:shown3.avatar,profile:found.actor.profile?.name??null,session:found.actor.session??null,expiresAt:null,can:{"*":["read","post","react","label"]}}},async channels(caller){return{channels:(await channelsFor(whoOf(caller),void 0,accessOf(caller))).map(channel=>({path:channel.name,lastIndex:0,messages:0,threads:0})),cursor:cursorOf({t:Date.now(),feeds:{}})}},async actors(caller,_input,signal){let who=whoOf(caller),listed2=(await all(who,await onBoard(who,"/actors"),"actors",{},signal)).map(actor=>({id:actor.id,kind:apiKind(actor.kind),handle:actor.handle,name:actor.name??actor.handle,avatar:actor.avatar??null})),cursor=cursorOf({t:Date.now(),feeds:{}});if(!("identity"in caller)||caller.identity.principal.access.readsAll)return{actors:listed2,cursor};let{actor:self2,principal}=caller.identity,seen=await actorsSeen(who,principal.access,signal);return{actors:[...listed2.filter(actor=>seen.has(actor.id)&&actor.id!==self2.id),{id:self2.id,kind:self2.kind,handle:self2.handle,name:self2.name,avatar:self2.avatar}],cursor}},async messages(caller,query,signal){let who=whoOf(caller),access=accessOf(caller),channel=query.before===void 0?await oneChannel(who,access,query):null;if(channel){let page2=await channelMessages(who,channel,query,signal);return{messages:await Promise.all(page2.messages.map(message=>view(who,message,channel.name))),cursor:page2.cursor}}if(query.after!==void 0&&API_CURSOR.test(query.after))throw new HttpError(400,"invalid_request",`after: ${query.after} is one channel's cursor: read that channel`);let{messages,feeds,newest}=await collect(who,access,query,signal),after=query.after!==void 0?await timeOf(who,query.after):void 0,before=query.before!==void 0?await timeOf(who,query.before):void 0,limit=query.limit??50,bounded=messages.filter(({message})=>(after===void 0||message.createdAt>after)&&(before===void 0||message.createdAt<before)),page=after!==void 0?bounded.slice(0,limit):bounded.slice(-limit);return{messages:await Promise.all(page.map(({message,channel:channel2})=>view(who,message,channel2.name))),cursor:cursorOf({t:newest,feeds})}},async threads(caller,query,signal){let who=whoOf(caller),after=query.after!==void 0?await timeOf(who,query.after):void 0,limit=query.limit??50,pick=found2=>{let ordered=found2.filter(({at})=>after===void 0||at>after).sort((a,b)=>a.at-b.at);return after!==void 0?ordered.slice(0,limit):ordered.slice(-limit)};if(query.re!==void 0||query.by!==void 0||query.mentions!==void 0||query.text!==void 0||query.from!==void 0&&query.from!=="board"){let{messages,feeds:feeds2,newest:newest2}=await collect(who,accessOf(caller),query,signal),latest=new Map;for(let{message,thread,channel}of messages)latest.set(thread.id,{thread,channel,at:Math.max(latest.get(thread.id)?.at??0,message.createdAt)});return{threads:await Promise.all(pick([...latest.values()]).map(({thread,channel})=>summary(who,thread,channel,signal))),cursor:cursorOf({t:newest2,feeds:feeds2})}}let listed2=await channelsFor(who,query.channel,accessOf(caller)),feeds=await heads(who,listed2),label=indexed(query),found=(await Promise.all(listed2.map(async channel=>(await latestThreads(who,channel,label,{...after!==void 0?{after}:{},...query.label?.length?{}:{limit}},signal)).map(thread=>({thread,channel,at:thread.lastActivityAt}))))).flat(),rooted=found.filter(({thread})=>(query.label??[]).every(([key,values])=>thread.labels!==void 0&&Object.hasOwn(thread.labels,key)&&(values===null||values.includes(thread.labels[key])))),newest=found.reduce((max,{at})=>Math.max(max,at),0);return{threads:await Promise.all(pick(rooted).map(({thread,channel})=>summary(who,thread,channel,signal))),cursor:cursorOf({t:newest,feeds})}},async message(caller,{id:id2},signal){let who=whoOf(caller),{thread,channel}=await readableMessage(who,accessOf(caller),id2,signal),[root,...replies]=await threadMessages(who,thread.id,signal);if(!root)throw notFound(`message not found: ${id2}`,"message_not_found");return{message:await view(who,root,channel.name),replies:await Promise.all(replies.map(reply2=>view(who,reply2,channel.name))),cursor:cursorOf({t:Math.max(root.createdAt,...replies.map(reply2=>reply2.createdAt)),feeds:{}})}},async upload(caller,{data,name,type}){let who=whoOf(caller);if(await checkUploader(caller,who),data.byteLength===0)throw new HttpError(400,"bad_request","the file is empty");if(data.byteLength>26214400)throw new HttpError(413,"too_large",`attachments are at most ${26214400/1024/1024} MB`);let bytes=new Uint8Array(data),digest=new Uint8Array(await crypto.subtle.digest("SHA-256",bytes)),started=await api(who,"POST",await onBoard(who,"/attachments"),{body:{operationId:operationId(),name:fileName(name),type:mediaType(type),size:data.byteLength,sha256:btoa(String.fromCharCode(...digest))}}),put=await filesFetch(started.upload.url,{method:started.upload.method,headers:started.upload.headers,body:bytes,redirect:"error"});if(!put.ok)throw new HttpError(502,"upload_failed",`the upload to Vercel's storage answered ${put.status}`);let{attachment}=await api(who,"POST",await onBoard(who,`/attachments/${encodeURIComponent(started.attachment.id)}/complete`));return{attachment:fileOf(attachment)}},async uploadStart(caller,{name,type,size,sha256:sha2562}){let who=whoOf(caller);if(await checkUploader(caller,who),sha2562===void 0)throw new HttpError(400,"invalid_request","Vercel's API signs a direct upload for its bytes' SHA-256: send `sha256`, as board's client does");let started=await api(who,"POST",await onBoard(who,"/attachments"),{body:{operationId:operationId(),name:fileName(name??null),type:mediaType(type??null),size,sha256:sha2562}});return{id:started.attachment.id,url:started.upload.url,headers:started.upload.headers,expiresAt:iso(started.upload.expiresAt)}},async uploadFinish(caller,{id:id2}){let who=whoOf(caller);await checkUploader(caller,who);let{attachment}=await api(who,"POST",await onBoard(who,`/attachments/${encodeURIComponent(id2)}/complete`));return{attachment:fileOf(attachment)}},async attachment(caller,{id:id2},signal){let who=whoOf(caller),access=accessOf(caller),found=await api(who,"GET",await onBoard(who,`/attachments/${encodeURIComponent(id2)}`),signal?{signal}:{});if(access&&!found.attachment.messageId){if(!await isProject(caller,who))throw notFound(`no attachment: ${id2}`,"attachment_not_found")}else if(access){let{message}=await readableMessage(who,access,found.attachment.messageId,signal).catch(error=>{throw error instanceof HttpError&&error.status===404?notFound(`no attachment: ${id2}`,"attachment_not_found"):error});if(message.deletedAt!==void 0)throw notFound(`no attachment: ${id2}`,"attachment_not_found")}let response=await filesFetch(found.url,{redirect:"error",...signal?{signal}:{}});if(!response.ok||!response.body)throw new HttpError(502,"download_failed",`Vercel's storage answered ${response.status}`);return{attachment:fileOf(found.attachment),data:response.body}},async post(caller,input,signal){if(checkCaller("post",caller),input.sync?.length)throw notSupported("sync");let who=whoOf(caller),access=accessOf(caller);input.attachments?.length&&await checkOwner("post files",caller,who);let labeled2=Object.keys(input.labels??{}).length>0,clientId=input.id!==void 0?await postId(caller,input.id):void 0,made;if(input.channel!==void 0){need(access,"post",input.channel),labeled2&&need(access,"label",input.channel);let channel2=await channelToPost(who,input.channel);made=await api(who,"POST",await onBoard(who,"/threads"),{body:{operationId:operationId(),channelId:channel2.id,text:input.text,...input.labels&&Object.keys(input.labels).length?{labels:input.labels}:{},...clientId?{clientId}:{},...input.attachments?.length?{attachments:input.attachments}:{}},signal})}else{let{thread,message:to,channel:channel2}=await readableMessage(who,access,input.re,signal);need(access,"post",channel2.name),labeled2&&need(access,"label",channel2.name);let after=input.after===void 0?void 0:locate(input.after);if(after!==void 0&&after?.threadId!==thread.id){let landed=clientId?await api(who,"GET",await onBoard(who,`/posts/${encodeURIComponent(clientId)}`),{signal}).catch(()=>null):null;if(landed?.thread.id===thread.id)return{message:await view(who,landed.message,channel2.name)};throw badRequest(`after: ${input.after} isn't in ${input.re}'s thread`,"invalid_after")}if(made=await api(who,"POST",await onBoard(who,`/threads/${encodeURIComponent(thread.id)}/messages`),{body:{operationId:operationId(),text:input.text,...to.sequence>1?{replyTo:to.sequence}:{},...clientId?{clientId}:{},...input.attachments?.length?{attachments:input.attachments}:{},...after?{after:after.sequence}:{}},signal}).catch(error=>{throw error instanceof HttpError&&error.code==="newer_message"?new HttpError(409,"newer_message",`a message came after ${input.after}`):error}),input.labels&&Object.keys(input.labels).length){let labeled3=await api(who,"POST",await onBoard(who,`/threads/${encodeURIComponent(thread.id)}/messages/${made.message.sequence}/labels`),{body:{operationId:operationId(),set:input.labels},signal});made={...made,message:{...made.message,...labeled3.meta.labels?{labels:labeled3.meta.labels}:{},version:labeled3.meta.version}}}}let channel=await channelById(who,made.thread.channelId);return{message:await view(who,made.message,channel.name)}},async posted(caller,{id:id2},signal){checkCaller("posted",caller);let who=whoOf(caller),found=await api(who,"GET",await onBoard(who,`/posts/${encodeURIComponent(await postId(caller,id2))}`),{signal}),channel=await channelById(who,found.thread.channelId),access=accessOf(caller);if(access&&!access.readable(channel.name))throw notFound(`no post of yours has id ${id2}`,"post_not_found");return{message:await view(who,found.message,channel.name)}},async edit(caller,{id:id2,text:text2,revision},signal){let who=whoOf(caller);await checkOwner("edit",caller,who);let at=locate(id2);if(!at)throw notFound(`message not found: ${id2}`,"message_not_found");let access=accessOf(caller);return access&&need(access,"post",(await readableMessage(who,access,id2,signal)).channel.name),await api(who,"PATCH",await onBoard(who,`/threads/${encodeURIComponent(at.threadId)}/messages/${at.sequence}`),{body:{operationId:operationId(),text:text2,...revision!==void 0?{revision}:{}},signal}),{ok:!0}},async delete(caller,{id:id2,revision},signal){let who=whoOf(caller);await checkOwner("delete",caller,who);let at=locate(id2);if(!at)throw notFound(`message not found: ${id2}`,"message_not_found");let access=accessOf(caller);return access&&need(access,"post",(await readableMessage(who,access,id2,signal)).channel.name),await api(who,"POST",await onBoard(who,`/threads/${encodeURIComponent(at.threadId)}/messages/${at.sequence}/delete`),{body:{operationId:operationId(),...revision!==void 0?{revision}:{}},signal}),{ok:!0}},async react(caller,{id:id2,emoji,remove},signal){checkCaller("react",caller);let who=whoOf(caller),at=locate(id2);if(!at)throw notFound(`message not found: ${id2}`,"message_not_found");let access=accessOf(caller);return access&&need(access,"react",(await readableMessage(who,access,id2,signal)).channel.name),await api(who,"POST",await onBoard(who,`/threads/${encodeURIComponent(at.threadId)}/messages/${at.sequence}/reactions`),{body:{operationId:operationId(),emoji,...remove?{remove:!0}:{}},signal}),{ok:!0}},async label(caller,{id:id2,set,unset,if:expected},signal){checkCaller("label",caller);let who=whoOf(caller),at=locate(id2);if(!at)throw notFound(`message not found: ${id2}`,"message_not_found");let access=accessOf(caller);return access&&need(access,"label",(await readableMessage(who,access,id2,signal)).channel.name),await api(who,"POST",await onBoard(who,`/threads/${encodeURIComponent(at.threadId)}/messages/${at.sequence}/labels`),{body:{operationId:operationId(),...set?{set}:{},...unset?{unset}:{},...expected&&Object.keys(expected).length?{if:expected}:{}},signal}),{ok:!0}},async schedule(caller,input,signal){if(input.attachments?.length)throw notSupported("files on scheduled messages");let who=whoOf(caller);await checkOwner("schedule",caller,who);let access=accessOf(caller),labels2=input.labels&&Object.keys(input.labels).length?input.labels:void 0,target;if(input.channel!==void 0)need(access,"post",input.channel),labels2&&need(access,"label",input.channel),target={channelId:(await channelToPost(who,input.channel)).id};else{if(labels2)throw notSupported("labels on a scheduled reply");let{thread,channel}=await readableMessage(who,access,input.re,signal);need(access,"post",channel.name),target={threadId:thread.id}}let ms=value=>Math.round(duration(value)),firstAt=input.at!==void 0?Date.parse(input.at):Date.now()+ms(input.in??input.every),until2=input.until!==void 0?Date.parse(input.until):input.for!==void 0?firstAt+ms(input.for):void 0,{schedule}=await api(who,"POST",await onBoard(who,"/schedules"),{body:{operationId:operationId(),...target,text:input.text,...labels2?{labels:labels2}:{},firstAt,...input.every!==void 0?{every:ms(input.every)}:{},...until2!==void 0?{until:until2}:{},...input.times!==void 0?{times:input.times}:{}},signal});return{schedule:await scheduleView(who,schedule)}},async schedules(caller,query,signal){let who=whoOf(caller),access=accessOf(caller),filter={state:query.all?void 0:"pending"};if(query.re!==void 0){let found=await readableMessage(who,access,query.re,signal).catch(error=>{if(error instanceof HttpError&&error.status===404)return null;throw error});if(!found)return{schedules:[]};filter.threadId=found.thread.id}if(query.channel!==void 0){let channel=await channelNamed(who,query.channel);if(!channel)return{schedules:[]};filter.channelId=channel.id}let me2=query.by==="me"?await actorOf(who):void 0,by=me2??query.by;by?.startsWith("vercel:")&&(filter.createdBy=by);let created=actor=>by===void 0||(me2?actor.id===me2:[actor.id,principalId(actor.principal),actor.profile?.name].includes(by)),listed2=await all(who,await onBoard(who,"/schedules"),"schedules",filter,signal);return{schedules:(await Promise.all(listed2.filter(schedule=>created(schedule.createdBy)).map(schedule=>scheduleView(who,schedule)))).filter(schedule=>access===null||access.readable(schedule.channel))}},async cancelSchedule(caller,{id:id2},signal){let who=whoOf(caller);await checkOwner("cancelSchedule",caller,who);let access=accessOf(caller);if(access){let found=(await all(who,await onBoard(who,"/schedules"),"schedules",{},signal)).find(schedule2=>schedule2.id===id2),channel=found&&await channelById(who,found.channelId);if(!channel||!access.readable(channel.name))throw notFound(`no schedule: ${id2}`,"schedule_not_found");need(access,"post",channel.name)}let{schedule,canceled}=await api(who,"POST",await onBoard(who,`/schedules/${encodeURIComponent(id2)}/cancel`),{body:{operationId:operationId()},signal});return{schedule:await scheduleView(who,schedule),canceled}},async events(caller,query,signal){checkCaller("events",caller);let who=whoOf(caller),access=accessOf(caller),channel=await oneChannel(who,access,query);if(channel)return channelEvents(who,channel,query,signal);if(query.after!==void 0&&API_CURSOR.test(query.after))throw new HttpError(400,"invalid_request",`after: ${query.after} is one channel's cursor: read that channel`);let listed2=await channelsFor(who,query.channel,access),position=query.after===void 0?{t:Date.now(),feeds:await heads(who,listed2)}:locate(query.after)?{t:await timeOf(who,query.after),feeds:{}}:positionOf(query.after),until2=Date.now()+(query.wait??0)*1e3;for(;;){let page=await read(who,listed2,position,query,signal);if(position=page.position,page.events.length>0||Date.now()>=until2||signal.aborted)return{events:page.events,cursor:cursorOf(position)};await new Promise(resolve2=>setTimeout(resolve2,Math.min(1e3,until2-Date.now())))}},async typing(){return{targets:[]}},async readSource(_caller,{key}){throw notFound(`this board doesn't run sync entry ${key}`,"not_synced_here")},async watchers(caller){ownWatchers(caller,"list watchers");let who=whoOf(caller),{watchers}=await api(who,"GET",await onBoard(who,"/watchers"));return{watchers:await Promise.all(watchers.map(async found=>({owner:found.owner,...whereOf(found),watch:Object.fromEntries(await Promise.all(Object.entries(found.watch).map(async([id2,spec])=>[id2,{tail:await tailOf(who,spec),when:spec.when}]))),sync:[],hash:found.hash})))}},async register(caller,body){if(ownWatchers(caller,"register watchers"),(body.sync?.length??0)>0)throw notSupported("sync");return registerAs(whoOf(caller),typeof body.path=="string"?{path:body.path,...body.branch?{branch:body.branch}:{}}:{url:body.url??null},body.watch)},async matches(caller,query,signal){return ownWatchers(caller,"pull matches"),{matches:await pullAs(whoOf(caller),query,signal)}},async ack(caller,{id:id2,...body}){return ownWatchers(caller,"ack matches"),await ackAs(whoOf(caller),id2,body),{ok:!0}}};return{...Object.fromEntries(Object.entries(methods).map(([name,method])=>[name,(...args)=>(binding&&runner.start(),method(...args))])),routes:runner.routes,keeps:!1,sendsSchedulesHere:!1,bind(next){if(binding)throw new Error("vercel: this backend already serves a board: create one vercel() per board()");if(Object.values(next.subscriptions).map(subscription=>subscription.kind).includes("sync"))throw new Error("vercel: Vercel's boards don't run sync yet: leave `sync` out");for(let id2 of Object.keys(next.subscriptions))own2.add(id2);tails=Object.fromEntries(Object.entries(next.subscriptions).flatMap(([id2,subscription])=>subscription.kind==="tail"?[[id2,{tail:subscription.tail,when:subscription.when}]]:[])),binding=next},registerWatchers:runner.registerNow,close:runner.close}}import{readFile as readFile2}from"node:fs/promises";var import_oidc2=__toESM(require_dist(),1),deployed=()=>process.env.VERCEL==="1"||!!process.env.VERCEL_OIDC_TOKEN;async function token(){return process.env.VERCEL_TOKEN?process.env.VERCEL_TOKEN:deployed()?(0,import_oidc2.getVercelOidcToken)():vercelLogin().catch(()=>{throw new HttpError(401,"not_logged_in","You're not logged in to Vercel.",{hint:"Run vercel login.",origin:"handle"})})}function credentialSource(){return process.env.VERCEL_TOKEN?"VERCEL_TOKEN":deployed()?"Vercel OIDC":loginHanded()?"the Vercel CLI":"vercel login"}var handed2=null;function handTeam(resolve2){handed2=resolve2}async function team(){if(handed2)try{return await handed2()}catch(error){throw new HttpError(502,"no_team",error instanceof Error?error.message:String(error),{origin:"handle"})}if(process.env.VERCEL_ORG_ID)return process.env.VERCEL_ORG_ID;if(!process.env.VERCEL_TOKEN&&deployed())try{let owner=decodeJwt(await(0,import_oidc2.getVercelOidcToken)()).owner_id;if(typeof owner=="string")return owner}catch{}let file=vercelProjectFile();if(!file)return null;try{let linked=JSON.parse(await readFile2(file,"utf8"));if(typeof linked.orgId=="string")return linked.orgId}catch{}return null}var environment2={fetch:()=>async(input,init={})=>{let credential=await token();if(!process.env.VERCEL_TOKEN&&!deployed())return asYou(input,init,credential);let headers=new Headers(init.headers);return headers.set("authorization",`Bearer ${credential}`),globalThis.fetch(input,{...init,headers})},team,profile:environmentProfile};function vercel(options2={}){return createVercel(options2,environment2)}import{spawn}from"node:child_process";import{readFile as readFile3,stat as stat2,writeFile as writeFile2}from"node:fs/promises";import{mkdirSync,readFileSync as readFileSync2,renameSync,unlinkSync,writeFileSync}from"node:fs";import{homedir as homedir2}from"node:os";import{basename,dirname as dirname2,join as join2}from"node:path";import{parseArgs}from"node:util";var terminalText=text2=>text2.replace(/\r\n/g,`
|
|
23
|
+
`).replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g,char=>{let code=char.charCodeAt(0);return code<32?String.fromCharCode(9216+code):code===127?"\u2421":"\uFFFD"});function authorName(message){let author=message.author;if(!author)return message.by.actor;let slash=author.handle.indexOf("/");return message.by.profile!==null&&slash!==-1?`${author.name} via ${author.handle.slice(0,slash)}`:author.person&&author.person.handle!==author.handle?`${author.name} (${author.person.handle})`:author.name}function ago(iso2,now=Date.now()){let seconds=Math.max(0,Math.round((now-Date.parse(iso2))/1e3));return seconds<60?`${seconds}s`:seconds<3600?`${Math.floor(seconds/60)}m`:seconds<86400?`${Math.floor(seconds/3600)}h`:`${Math.floor(seconds/86400)}d`}var indent=(text2,prefix)=>text2.split(`
|
|
24
|
+
`).map(line2=>`${prefix}${line2}`).join(`
|
|
25
|
+
`);function labels(message){let entries=Object.entries(message.labels);return entries.length?` [${entries.map(([k,v])=>`${k}=${v}`).join(" ")}]`:""}function reactions(message,names){let groups2=Object.entries(message.reactions);return groups2.length===0?"":groups2.map(([emoji,actors])=>{let shown3=actors.slice(0,3).map(id2=>names.get(id2)??id2).join(", ");return`${displayEmoji(emoji)} ${shown3}${actors.length>3?` +${actors.length-3}`:""}`}).join(" ")}function reactionNames(messages){return[...new Set(messages.flatMap(m=>Object.values(m.reactions).flatMap(actors=>actors.slice(0,3))))]}function actorName(actor){let slash=actor.handle.indexOf("/");return slash===-1?actor.name:`${actor.name} via ${actor.handle.slice(0,slash)}`}var EMBED_LINES=12;function embed(link,pad){if(!link.snapshot)return[`${pad}\u21AA ${link.url} (couldn't read it: ${link.unavailable??"unknown"})`];let{author,text:text2,files}=link.snapshot,lines=text2.trim()?text2.trim().split(`
|
|
26
|
+
`):[],shown3=lines.length>EMBED_LINES?[...lines.slice(0,EMBED_LINES),"\u2026"]:lines;return[`${pad}\u21AA ${author}${link.message!==null?` \xB7 #${link.message}`:""} \xB7 ${link.url}`,...shown3.map(line2=>`${pad}> ${line2}`),...files.map(file=>`${pad}> file ${file.name}${file.url?` ${file.url}`:""}`)]}function formatMessage(message,options2={}){let pad=options2.reply?" ":" ",head=`${options2.reply?" \u21B3 ":""}#${message.id} ${authorName(message)} \xB7 ${ago(message.createdAt)}${message.editedAt?" (edited)":""}${message.schedule?` \xB7 scheduled ${message.schedule.id}`:""}${labels(message)}`,body=message.deleted?`${pad}(message removed)`:indent(displayText(message.text,message.by.actor),pad),files=(message.attachments??[]).map(a=>`${pad}file ${a.name} \xB7 ${a.type} \xB7 ${fileSize(a.size)} \xB7 ${a.id}`),links=message.deleted?[]:(message.links??[]).flatMap(link=>embed(link,pad)),reacted=reactions(message,options2.names??new Map),undelivered=Object.entries(message.undelivered??{}).map(([target,error])=>`${pad}\u2717 didn't reach ${target}: ${error}`);return[head,body,...files,...links,...reacted?[`${pad}${reacted}`]:[],...undelivered].join(`
|
|
27
|
+
`)}function formatThreads(messages,known=new Map){let names=new Map([...known,...messages.map(m=>[m.by.actor,authorName(m)])]),byRoot=new Map,roots=[],orphans=[];for(let message of messages)message.re===null?roots.push(message):(byRoot.get(message.re)??byRoot.set(message.re,[]).get(message.re)).push(message);let rootIds=new Set(roots.map(r=>r.id));for(let[root,replies]of byRoot)rootIds.has(root)||orphans.push(...replies);let blocks=roots.map(root=>[formatMessage(root,{names}),...(byRoot.get(root.id)??[]).map(r=>formatMessage(r,{reply:!0,names}))].join(`
|
|
28
|
+
`));for(let reply2 of orphans)blocks.push(`(in thread #${reply2.re})
|
|
29
|
+
${formatMessage(reply2,{reply:!0,names})}`);return blocks.join(`
|
|
30
|
+
|
|
31
|
+
`)}function firstLine(message){if(message.deleted)return"(message removed)";let text2=displayText(message.text,message.by.actor).trim(),files=message.attachments?.length??0;if(!text2)return files?`(${files} ${files===1?"file":"files"})`:"(no text)";let[line2="",...rest]=text2.split(/\r?\n/),shown3=rest.length?`${line2.trimEnd()} \u2026`:line2;return shown3.length>74?`${line2.slice(0,73)}\u2026`:shown3}function formatThreadSummaries(threads,options2={}){let now=options2.now??Date.now();return threads.map(({message,replies,last})=>{let where=options2.channel?`${message.channel} `:"",head=`#${message.id} ${where}${authorName(message)} \xB7 ${ago(message.createdAt,now)}${labels(message)}`,activity=last?`${replies} ${replies===1?"reply":"replies"} \xB7 last #${last.id} ${authorName(last)} \xB7 ${ago(last.createdAt,now)}`:"no replies",counted=Object.entries(message.reactions).filter(([,actors])=>actors.length>0).map(([emoji,actors])=>`${displayEmoji(emoji)} ${actors.length}`).join(" ");return[head,` ${firstLine(message)}`,...counted?[` ${counted}`]:[],` ${activity}`].join(`
|
|
32
|
+
`)}).join(`
|
|
33
|
+
|
|
34
|
+
`)}function formatAccess(access){let patterns=Object.keys(access);if(patterns.length===1&&patterns[0]==="*"&&access["*"].includes("manage"))return[];if(patterns.length===0)return["nothing"];let width=Math.max(...patterns.map(pattern=>pattern.length));return patterns.map(pattern=>{let permissions=access[pattern];return`${pattern.padEnd(width)} ${permissions.includes("manage")?"all":permissions.length===0?"nothing":permissions.join(" ")}`})}function span(ms){let parts=[],rest=Math.round(ms/1e3);for(let[unit,size]of[["d",86400],["h",3600],["m",60],["s",1]])rest>=size&&parts.push(`${Math.floor(rest/size)}${unit}`),rest%=size;return parts.join("")||"0s"}var until=(iso2,now)=>{let ms=Date.parse(iso2)-now;return ms<=0?"due":`in ${span(ms<6e4?Math.ceil(ms/1e3)*1e3:Math.ceil(ms/6e4)*6e4)}`};function formatSchedule(schedule,options2={}){let now=options2.now??Date.now(),pad=options2.pad??"",where=schedule.re===null?`new thread in ${schedule.channel}`:`#${schedule.re}`,repeat=schedule.every===null?"once":`every ${span(schedule.every)}`,stop=schedule.every===null?"":schedule.times!==null?` \xB7 ${schedule.times} times`:schedule.until!==null?` \xB7 until ${schedule.until}`:" \xB7 until canceled",when=schedule.state==="pending"?`next ${until(schedule.next,now)} (${schedule.next})`:schedule.state==="canceled"?schedule.canceled?.reason==="thread_deleted"?"ended: the thread was deleted":schedule.canceled?.reason==="dropped"?"ended: the board dropped it":schedule.canceled?.reason==="revoked"?"ended: its creator can no longer post it":"canceled":"done",sent=schedule.sent>0?` \xB7 ${schedule.sent} sent`:"",files=schedule.attachments.length?` \xB7 ${schedule.attachments.length} ${schedule.attachments.length===1?"file":"files"}`:"",text2=displayText(schedule.text,schedule.by.actor);return[`${pad}${schedule.id} ${where} \xB7 ${repeat}${stop} \xB7 ${when}${sent}${files} \xB7 by ${authorName(schedule)}`,indent(text2||"(no text)",`${pad} `)].join(`
|
|
35
|
+
`)}function formatEvent(event){let message=event.message;if(!message)return`#${event.cursor} ${event.type}`;let where=message.re===null?message.channel:`${message.channel} \u21B3 #${message.re}`,who=authorName(message);switch(event.type){case"message.posted":{let files=(message.attachments??[]).map(a=>` \xB7 file ${a.name}`).join("");return`#${message.id} ${where} ${who}: ${displayText(message.text,message.by.actor)}${files}`}case"message.edited":return`#${message.id} ${where} ${who} (edited): ${displayText(message.text,message.by.actor)}`;case"message.deleted":return`#${message.id} ${where} (message removed)`;case"message.linked":return`#${message.id} ${where} \u21AA links ${(message.links??[]).map(link=>link.message!==null?`#${link.message}`:link.url).join(", ")||"nothing"}`;case"reaction.added":case"reaction.removed":return`#${message.id} ${where} ${displayEmoji(event.payload.emoji)} ${event.type==="reaction.added"?"added":"removed"} by ${event.actor?.name??event.payload.by.actor}`;case"labels.changed":{let set=Object.entries(event.payload.set).map(([k,v])=>`${k}=${v}`),unset=event.payload.unset.map(k=>`-${k}`);return`#${message.id} ${where} labels ${[...set,...unset].join(" ")}${event.actor?` by ${event.actor.name}`:""}`}case"message.sync_failed":return`#${message.id} ${where} \u2717 didn't reach ${event.payload.target}: ${event.payload.error}`;default:return`#${event.cursor} ${event.type}`}}function formatSynced(targets){let where=row=>row.target.slice(row.key.length+1),keys=Math.max(...targets.map(row=>row.key.length)),wheres=Math.max(...targets.map(row=>where(row).length));return targets.map(row=>{let copy=row.undelivered!==null?`didn't reach it: ${row.undelivered}`:row.url??(row.message!==null?`message ${row.message}`:"no copy yet");return`${row.key.padEnd(keys)} ${where(row).padEnd(wheres)} ${terminalText(copy)}`}).join(`
|
|
36
|
+
`)}function formatSourceRead(key,read,now=Date.now(),cli2="vb"){let where=`${key}:${read.channel}${read.name?` (#${read.name})`:""}${read.thread?`, thread ${read.thread}`:""}`;if(read.messages.length===0)return`${where}
|
|
37
|
+
no messages`;let blocks=read.messages.map(message=>{let reply2=read.thread!==null&&message.id!==read.thread,pad=reply2?" ":" ",when=message.createdAt?` \xB7 ${ago(message.createdAt,now)}`:"",replies=message.replies>0&&read.thread===null?` (${message.replies} ${message.replies===1?"reply":"replies"}: ${cli2} sources ${key} read ${message.thread})`:"";return[`${reply2?" \u21B3 ":""}${message.id} ${message.author}${when}${replies}`,indent(message.text||"(no text)",pad),...message.files.map(file=>`${pad}file ${file.name}${file.url?` ${file.url}`:""}`)].join(`
|
|
38
|
+
`)}),more=read.next?[`(more not shown: ${cli2} sources ${key} read ${read.thread??read.channel} --after ${read.next})`]:read.more?[`(${read.thread?"later replies":"older messages"} not shown: raise --limit, up to 200)`]:[];return[where,...blocks,...more].join(`
|
|
39
|
+
`)}function formatConformance(report2){let blocks=[];for(let group of[...new Set(report2.checks.map(check2=>check2.group))]){let lines=[group];for(let check2 of report2.checks.filter(c=>c.group===group))lines.push(` ${check2.status.padEnd(7)} ${check2.name}`),check2.detail&&lines.push(` ${check2.detail}`);blocks.push(lines.join(`
|
|
40
|
+
`))}let count=status=>report2.checks.filter(check2=>check2.status===status).length,counts=[`${count("passed")} passed`,...count("skipped")?[`${count("skipped")} skipped`]:[],...count("failed")?[`${count("failed")} failed`]:[]];return`${blocks.join(`
|
|
41
|
+
|
|
42
|
+
`)}
|
|
43
|
+
|
|
44
|
+
${counts.join(", ")}`}var AGENTS={claude:{name:"Claude Code",shape:"nested",event:"Stop",file:()=>join2(process.env.CLAUDE_CONFIG_DIR||join2(homedir2(),".claude"),"settings.json")},codex:{name:"Codex",shape:"nested",event:"Stop",file:()=>join2(process.env.CODEX_HOME||join2(homedir2(),".codex"),"hooks.json")},cursor:{name:"Cursor",shape:"flat",event:"stop",file:()=>join2(homedir2(),".cursor","hooks.json")},pi:{name:"Pi",shape:"extension",event:"agent_before_settle",file:()=>join2(process.env.PI_CODING_AGENT_DIR||join2(homedir2(),".pi","agent"),"extensions","board.ts")}},ours=entry=>/ hook stop --agent /.test(entry.command??"");function readConfig(path){try{return JSON.parse(readFileSync2(path,"utf8"))}catch(error){if(error.code==="ENOENT")return{};throw new Error(`can't read ${path}: ${error.message}`)}}function writeConfig(path,config){mkdirSync(dirname2(path),{recursive:!0}),writeFileSync(`${path}.board-tmp`,`${JSON.stringify(config,null,2)}
|
|
45
|
+
`),renameSync(`${path}.board-tmp`,path)}function without(entries,shape){return shape==="flat"?entries.filter(entry=>!ours(entry)):entries.map(group=>({...group,hooks:(group.hooks??[]).filter(h=>!ours(h))})).filter(group=>group.hooks.length>0)}var PI_COMMAND=/^const COMMAND = (".*")$/m;function uninstallHook(agent){let spec=AGENTS[agent];if(installedHook(agent)===null)return!1;if(spec.shape==="extension")return unlinkSync(spec.file()),!0;let config=readConfig(spec.file()),hooks={...config.hooks},kept=without(hooks[spec.event]??[],spec.shape);return kept.length>0?hooks[spec.event]=kept:delete hooks[spec.event],Object.keys(config).filter(key=>key!=="hooks"&&key!=="version").length===0&&Object.keys(hooks).length===0?unlinkSync(spec.file()):writeConfig(spec.file(),{...config,hooks}),!0}function installedHook(agent){let spec=AGENTS[agent];if(spec.shape==="extension")try{let line2=PI_COMMAND.exec(readFileSync2(spec.file(),"utf8"));return line2?JSON.parse(line2[1]):null}catch{return null}let entries=readConfig(spec.file()).hooks?.[spec.event]??[];return(spec.shape==="flat"?entries:entries.flatMap(group=>group.hooks??[])).find(ours)?.command??null}function tilde(path){let home=homedir2();return home&&(path===home||path.startsWith(`${home}/`))?`~${path.slice(home.length)}`:path}var cliOf=command=>command.slice(0,command.indexOf(" hook stop "));function allowRules(agent,cli2){return agent==="claude"?{file:AGENTS.claude.file(),rules:["reply","post","read","channels","update"].map(sub=>`Bash(${cli2} ${sub}:*)`)}:agent==="cursor"&&cli2==="vb"?{file:join2(homedir2(),".cursor","cli-config.json"),rules:["Shell(vb)"]}:null}function disallow(file,rules){let config=readConfig(file),allowed=config.permissions?.allow;return allowed?.some(rule=>rules.includes(rule))?(writeConfig(file,{...config,permissions:{...config.permissions,allow:allowed.filter(rule=>!rules.includes(rule))}}),!0):!1}var SUBJECT_MAX=34,COLUMNS=80,padOf=entries=>Math.min(SUBJECT_MAX,Math.max(0,...entries.map(([subject])=>subject.length)));function wrap(text2,width,gap=" "){let lines=[],line2="";for(let word of text2.split(gap))line2&&line2.length+gap.length+word.length>width?(lines.push(line2),line2=word):line2=line2?`${line2}${gap}${word}`:word;return line2?[...lines,line2]:lines}var labeled=(label,value,gap=" ")=>{let indent2=" ".repeat(10);return wrap(value,COLUMNS-indent2.length,gap).map((line2,index)=>`${index===0?label.padEnd(indent2.length):indent2}${line2}`)};function rows(entries,pad=padOf(entries)){let indent2=" ".repeat(2+pad+2);return entries.map(([subject,value])=>{let[first="",...rest]=wrap(value,Math.max(20,COLUMNS-indent2.length));return[subject.length>pad?` ${subject}
|
|
46
|
+
${indent2}${first}`:` ${subject.padEnd(pad)} ${first}`.trimEnd(),...rest.map(line2=>`${indent2}${line2}`)].join(`
|
|
47
|
+
`)})}var flag=option=>`${option.short?`-${option.short}, `:""}--${option.name}${option.value?option.optionalValue?` [${option.value}]`:` <${option.value}>`:""}${option.multiple?"...":""}`,shown2=list=>list.filter(option=>!option.hidden);function listed(command){return[...!command.subcommands||command.runsWithoutSubcommand?[[usage([command]),command.description]]:[],...(command.subcommands??[]).filter(sub=>!sub.hidden).map(sub=>[usage([command,sub]),sub.description])]}function rootHelp(app2,version){let sections=[...groups.map(group=>commands.filter(command=>command.group===group)),app2.commands].map(list=>list.filter(command=>!command.hidden)),lines=[`${app2.name} ${version}: a message board you mount in one route`,"",`usage: ${app2.name} <command> [options]${app2.name==="board"?" (also installed as `vb`)":""}`],listedSections=sections.filter(list=>list.length>0).map(list=>list.flatMap(listed)),pad=padOf(listedSections.flat());for(let section of listedSections)lines.push("",...rows(section,pad));let everywhere=shown2(globalOptions).filter(option=>option.name!=="help"&&option.name!=="version");return lines.push("",...labeled("filters:",shown2(filters).map(flag).join(" ")," "),...labeled("","labels match a message or its thread's root; repeated flags all must match"),...labeled("global:",everywhere.map(flag).join(" ")," "),...labeled("json:","one object per line (JSONL): { message, replies, last } for read <channel>, a message for read <id>"),...labeled("text:",'pass "-" to read it from stdin; it may be left out when you --attach'),...labeled("env:","BOARD_URL BOARD_TOKEN BOARD_AS BOARD_AVATAR BOARD_HANDLE"," "),"",`a command's own help: ${app2.name} <command> --help`),lines.join(`
|
|
48
|
+
`)}function commandHelp(name,path){let command=path.at(-1),options2=shown2(command.options),subcommands=(command.subcommands??[]).filter(sub=>!sub.hidden),lines=[`usage: ${name} ${usage(path)}${options2.length||command.filters?" [options]":""}`,"",...wrap(command.description,COLUMNS)];command.aliases?.length&&lines.push("",`also: ${command.aliases.map(alias=>`${name} ${alias}`).join(", ")}`);let section=(title,entries)=>{entries.length&&lines.push("",`${title}:`,...rows(entries))};return section("arguments",command.arguments.filter(argument=>argument.description).map(argument=>[argument.name,argument.description])),section("commands",subcommands.map(sub=>[usage([...path,sub]),sub.description])),section("options",options2.map(option=>[flag(option),option.description])),command.filters&&(section("filters",shown2(filters).map(option=>[flag(option),option.description])),lines.push(" labels match a message or its thread's root; repeated flags all must match")),command.examples.length&&lines.push("","examples:",...command.examples.flatMap((example,index)=>[...index?[""]:[],` ${example.name}`,` ${name} ${example.value}`])),lines.push("",`options every command takes: ${name} help`),lines.join(`
|
|
49
|
+
`)}function commandPath(words2,all){let command=all.find(candidate=>candidate.name===words2[0]||candidate.aliases?.includes(words2[0]));if(!command)return null;let at=command.argumentsBeforeSubcommand?command.arguments.length+1:1,sub=command.subcommands?.find(candidate=>candidate.name===words2[at]);return sub?[command,sub]:[command]}function exitCode(failure){return failure.class==="usage"||failure.class==="missing_input"?2:failure.class==="canceled"?130:1}var shellWord=arg=>/^<[A-Z_]+>$/.test(arg)||/^[\w@%+=:,./-]+$/.test(arg)?arg:`'${arg.replace(/'/g,"'\\''")}'`,commandLine=argv=>argv.map(shellWord).join(" ");function failureLines(failure){let lines=[failure.message,...failure.details??[]];for(let[label,value]of failure.context??[])lines.push(`${label.padEnd(14)} ${value}`);if(failure.next?.length){lines.push("");for(let{instruction,argv}of failure.next)argv?lines.push(`${instruction}:`,` ${commandLine(argv)}`):lines.push(instruction)}return lines}function formatFailure(failure){return failureLines(failure).map((line2,index)=>index===0?`error: ${line2}`:line2?` ${line2}`:"").join(`
|
|
50
|
+
`)}function failureDocument(failure){let next=(failure.next??[]).flatMap(({instruction,argv})=>argv?[{command:commandLine(argv),argv,when:instruction}]:[]),hint=(failure.next??[]).flatMap(({instruction,argv})=>argv?[]:[instruction]);return{status:failure.actionRequired?"action_required":"error",reason:failure.reason,message:failure.message,...next.length?{next}:{},...hint.length?{hint:hint.join(" ")}:{},...failure.data?{data:failure.data}:{}}}function closest(word,known){let best,distance=1/0;for(let candidate of known){let d=edits(word.toLowerCase(),candidate.toLowerCase());d<distance&&([best,distance]=[candidate,d])}return best!==void 0&&distance>0&&distance<=(word.length>=6?2:1)?best:void 0}function edits(a,b){let d=Array.from({length:a.length+1},(_,i)=>Array.from({length:b.length+1},(_2,j)=>i===0?j:j===0?i:0));for(let i=1;i<=a.length;i++)for(let j=1;j<=b.length;j++)d[i][j]=Math.min(d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+(a[i-1]===b[j-1]?0:1)),i>1&&j>1&&a[i-1]===b[j-2]&&a[i-2]===b[j-1]&&(d[i][j]=Math.min(d[i][j],d[i-2][j-2]+1));return d[a.length][b.length]}var Failed=class extends Error{failure;constructor(failure){super(failure.message),this.failure=failure}};function usage2(message,options2={}){return new Failed({class:"usage",reason:options2.reason??"invalid_arguments",message,next:options2.next??[helpRecovery()]})}var helpRecovery=(command=current.command)=>({instruction:"See how to use it",argv:[...cli(),...command?[command]:[],"--help"]}),cli=()=>named().split(" ");function rerun(...extra){let at=typed.indexOf("--");return[...cli(),...at===-1?[...typed,...extra]:[...typed.slice(0,at),...extra,...typed.slice(at)]]}var OPTIONS=options,SHORT=Object.fromEntries(Object.entries(OPTIONS).flatMap(([name,option])=>"short"in option?[[option.short,name]]:[]));function takesValue(arg){if(arg.includes("="))return!1;let string=name=>name!==void 0&&OPTIONS[name]?.type==="string";if(arg.startsWith("--"))return string(arg.slice(2));let letters=arg.slice(1);return[...letters].findIndex(letter=>string(SHORT[letter]))===letters.length-1}var canonical=word=>word===void 0?word:commands.find(command=>command.aliases?.includes(word))?.name??word,MARK="\0";function markAttachmentId(args){let positionals2=[];for(let i=0;i<args.length;i++){let arg=args[i];if(arg==="--")break;if(!arg.startsWith("-")||arg==="-")positionals2.push(arg);else{if(positionals2.length===1&&positionals2[0]==="download"&&AttachmentId.safeParse(arg).success)return[...args.slice(0,i),`${MARK}${arg}`,...args.slice(i+1)];takesValue(arg)&&i++}}return args}function removedCommand(word){let gone=(next,why="")=>usage2(`\`${named()} ${word}\` is gone.${why}`,{reason:"unknown_command",next}),url=[{instruction:"Pass the board's URL",argv:[...cli(),"read","dev","--url","<URL>"]},{instruction:"Or, in a Vercel project, its team's board needs no URL"}],removed={update:()=>gone([{instruction:"Reply in the thread you're working in",argv:[...cli(),"reply","<ID>","<TEXT>"]},{instruction:"Or post where it fits",argv:[...cli(),"post","<CHANNEL>","<TEXT>"]}]),stuck:()=>gone([]," A board on a2() keeps its dead letters in A2, on the backend's `servers`."),link:()=>gone(url),use:()=>gone(url)};return Object.hasOwn(removed,word)?removed[word]():void 0}function commandWord(argv){let{positionals:positionals2}=parseArgs({args:markAttachmentId(withMentions(argv)),allowPositionals:!0,options:OPTIONS,strict:!1});return canonical(positionals2[0])}var parseAll=args=>parseArgs({args,allowPositionals:!0,options:OPTIONS}),app={name:"board",commands:{}},flags={},positionals=[],current={command:void 0,args:[]},typed=[],warnings=[],reached=null;function help(words2=[]){let own2=Object.values(app.commands).map(command=>command.spec),path=commandPath(words2,[...commands,...own2]);return path?commandHelp(app.name,path):rootHelp({name:app.name,commands:own2},VERSION)}var named=()=>app.name==="board"?"vb":app.name,TEXT=new Set(["post","reply","edit","schedule"]);function parse(argv){let args=withMentions(argv),word=commandWord(args);current={command:word,args:[]};let removed=removedCommand(word??"");if(removed)throw removed;try{let parsed=parseAll(markAttachmentId(args));flags=parsed.values,positionals=parsed.positionals.map(arg=>arg.startsWith(MARK)?arg.slice(1):arg)}catch(error){throw parseFailure(error,argv,word)}}function parseFailure(error,argv,word){let option=error.message.match(/--[a-z][\w-]*/)?.[0]??/'(-[^' ,]+)/.exec(error.message)?.[1];if(error.code==="ERR_PARSE_ARGS_UNKNOWN_OPTION"&&option){let guess=option.startsWith("--")?closest(option.slice(2),optionsOf(word)):void 0,next=[{instruction:"See all options",argv:[...cli(),...word?[word]:[],"--help"]}],at=argv.indexOf(option);if(word&&TEXT.has(word)&&at!==-1){let rest=[...argv.slice(0,at),...argv.slice(at+1)],dash=rest.indexOf("--"),text2=dash===-1?[...rest,"--",option]:[...rest.slice(0,dash+1),option,...rest.slice(dash+1)];next.push({instruction:"Or, for text that starts with -, put it after --",argv:[...cli(),...text2]})}return usage2(`Unknown option ${option} for ${`${named()}${word?` ${word}`:""}`}.${guess?` Did you mean --${guess}?`:""}`,{reason:"unknown_option",next})}return error.code==="ERR_PARSE_ARGS_INVALID_OPTION_VALUE"&&option?usage2(`${option} needs a value.`,{reason:"missing_value"}):usage2(sentence(error.message))}function optionsOf(word){let command=[...commands,...Object.values(app.commands).map(own2=>own2.spec)].find(known=>known.name===word);return[...command?[command,...command.subcommands??[]].flatMap(c=>[...c.options,...c.filters?filters:[]]):[],...globalOptions].map(option=>option.name)}var sentence=text2=>{let trimmed=text2.trim();return`${trimmed.charAt(0).toUpperCase()}${trimmed.slice(1)}${/[.!?]$/.test(trimmed)?"":"."}`},TERMINAL={write:chunk=>void process.stdout.write(chunk),error:chunk=>void process.stderr.write(chunk),async readFile(path){let info=await stat2(path).catch(()=>null);if(!info?.isFile())return null;if(info.size>104857600)throw tooLarge2(path);return new Uint8Array(await readFile3(path))},writeFile:(path,data)=>writeFile2(path,data),stdin:readStdin,tty:process.stdout.isTTY===!0},io=TERMINAL,out=text2=>io.write(`${terminalText(text2)}
|
|
51
|
+
`),result=value=>io.write(`${JSON.stringify(warnings.length?{...value,warnings}:value,null,io.tty?2:void 0)}
|
|
52
|
+
`),line=value=>io.write(`${JSON.stringify(value)}
|
|
53
|
+
`);function messageId(value,what="message ID"){let id2=value?.replace(/^#/,"")??"";if(value===void 0)throw usage2(`Missing the ${what}.`,{reason:"missing_arguments"});if(!/^[^\s/]+$/.test(id2))throw usage2(`${value} isn't a ${what}.`);return id2}var tooLarge2=path=>new Failed({class:"limit",reason:"payload_too_large",message:`Can't attach ${path}. It's over ${104857600/1024/1024} MB, the most an attachment can be.`}),isMessageRef=value=>/^#[^\s/]+$/.test(value)||/^\d+$/.test(value),messageRef=id2=>/^\d+$/.test(id2)?id2:`#${id2}`;function ifRevision(){let given=flags["if-revision"];if(given===void 0)return{};if(!/^[1-9]\d{0,15}$/.test(given))throw usage2(`--if-revision takes the message's revision, a number, and ${given} isn't one.`);return{revision:Number(given)}}function labelPairs(values,wrong="Labels look like key=value, and {value} isn't one."){let pairs={};for(let value of values??[]){let at=value.indexOf("=");if(at<=0)throw usage2(wrong.replace("{value}",value));pairs[value.slice(0,at)]=value.slice(at+1)}return pairs}async function text(value){if(value===void 0)throw usage2("Missing the message's text: pass it, or - to read it from stdin.",{reason:"missing_arguments"});return value!=="-"?value:(await io.stdin()).replace(/\n$/,"")}async function attachments(board2){let files=[];for(let path of flags.attach??[]){let data=await io.readFile(path);if(!data)throw usage2(`Can't attach ${path}. There's no such file.`,{reason:"file_not_found",next:[]});if(data.byteLength>104857600)throw tooLarge2(path);files.push({path,data})}let uploaded=[];for(let{path,data}of files)uploaded.push(await board2.upload({data:new Uint8Array(data),name:basename(path)}));return uploaded}function scheduleId(value){if(value===void 0)throw usage2("Missing the schedule ID, like s12.",{reason:"missing_arguments"});if(!/^[^\s/]+$/.test(value))throw usage2(`${value} isn't a schedule ID like s12.`);return value}function instant(flag2,value){let time=new Date(value);if(Number.isNaN(time.getTime()))throw usage2(`--${flag2} takes a time like 2026-10-08T09:00:00Z, and ${value} isn't one.`);return time.toISOString()}function filters2(channel){return{...channel?{channel}:{},...flags.re?{re:messageId(flags.re)}:{},...flags.by?{by:flags.by.replace(/^@/,"")}:{},...flags.mentions?{mentions:flags.mentions.replace(/^@/,"")}:{},...flags.label?.length?{label:flags.label}:{},...flags.source?{from:flags.source}:{},...flags.text?{text:flags.text}:{},...flags.after!==void 0?{after:flags.after.replace(/^#/,"")}:{}}}function configuredUrl(){try{return boardUrl()}catch(error){if(!(error instanceof BoardError)||error.code!=="no_board")throw error;if(app.teamBoard||process.env.VERCEL_ORG_ID||vercelProjectFile())return null;throw new Failed({class:"missing_input",reason:"missing_scope",actionRequired:!0,message:"No board to use. Nothing sets one, and this directory isn't in a Vercel project.",next:[{instruction:"Use a team's board",argv:rerun("--scope","<TEAM>")},{instruction:"Or a board at a URL",argv:rerun("--url","<URL>")}]})}}var outdated=message=>new Failed({class:"platform",reason:"board_outdated",message:`${message} It's older than this CLI.`,next:[{instruction:"Update @vercel/board where the board is deployed"}]}),ref=value=>value===void 0?"a message":`#${value.replace(/^#/,"")}`;function doing(){let{command,args}=current,[a,b,c]=args,message=a!==void 0&&isMessageRef(a);switch(command){case"read":return a===void 0?"read the board":/^https?:\/\//i.test(a)?"read that link":`read ${message?ref(a):a}`;case"post":return message?`reply to ${ref(a)}`:`post to ${a??"a channel"}`;case"reply":return`reply to ${ref(a)}`;case"edit":case"delete":case"label":return`${command} ${ref(a)}`;case"react":return`react to ${ref(a)}`;case"typing":return`show typing in ${ref(a)}`;case"sync":return b===void 0?`list where ${ref(a)} is synced`:`sync ${ref(a)} with ${b}`;case"schedule":return a==="cancel"?`cancel schedule ${b}`:`schedule a message in ${message?ref(a):a??"a channel"}`;case"schedules":case"channels":case"actors":return`list ${command}`;case"sources":return b===void 0?"list sources":`${b} ${c??"it"} on ${a}`;case"identity":return a==="link"?`link your account on ${b}`:a==="unlink"?`unlink ${b}`:"list your linked accounts";case"download":return`download ${a??"the attachment"}`;case"inbox":return`read ${a}'s inbox`;case"tail":return`follow ${a??"the board"}`;case"wait":return`wait on ${a??"the board"}`;case"whoami":return"reach the board";case"conformance":return"check the board";default:return`run ${named()}${command?` ${command}`:""}`}}var ON_MESSAGE=new Set(["reply","edit","delete","react","label","typing","sync","schedules"]);function missing(){let{command="",args}=current,[a,b]=args;if(a!==void 0){if(command==="read"&&/^https?:\/\//i.test(a))return"that link's message on this board";if(ON_MESSAGE.has(command)||["read","post","schedule"].includes(command)&&isMessageRef(a))return`message ${ref(a)}`;if(command==="schedule"&&a==="cancel"&&b!==void 0)return`schedule ${b}`;if(command==="download")return`attachment ${a}`}}function failureOf(error){if(error instanceof Failed)return error.failure;let message=error instanceof Error?error.message:String(error),{code,status,hint,origin}=error;if(typeof code!="string"||typeof status!="number")return{class:"unknown",reason:"unexpected_error",message:`Failed to ${doing()}.`,details:[message]};let said={error:{code,message,status,...typeof hint=="string"?{hint}:{}}};if(origin==="handle"){let fix=typeof hint=="string"?[{instruction:hint}]:[],own2=(kind,reason,next=fix)=>({class:kind,reason,message,...next.length?{next}:{},data:said});switch(code){case"not_logged_in":return{class:"auth",reason:"login_required",message:"You're not logged in to Vercel.",next:[{instruction:"Log in",argv:["vercel","login"]}],data:said};case"no_access":return own2("permission","permission_denied");case"wrong_project":return own2("permission","wrong_project");case"protected":return own2("permission","deployment_protected");case"no_project":return own2("platform","no_project");case"no_token":return own2("auth","token_unavailable");case"no_board":return own2("not_found","board_not_found");case"too_many_redirects":return own2("network","network_error");case"invalid_duration":return own2("usage","invalid_arguments",[helpRecovery()]);case"wrong_audience":case"redirected":return own2("usage","wrong_url");case"ambiguous_agent":return own2("usage","ambiguous_agent",[{instruction:"Write as one of them",argv:rerun("--as","<PROFILE>")},{instruction:"Or as yourself",argv:rerun("--human")}]);case"too_large":return{...own2("limit","payload_too_large"),message:`Can't ${doing()}. ${sentence(message)}`};case"no_team":return{...own2("platform","api_error"),message:"Couldn't tell which team's board to use.",details:[message]};case"upload_failed":return{...own2("platform","upload_failed"),message:`Failed to ${doing()}.`,details:[message]};case"port_taken":return own2("conflict","port_taken",[{instruction:"Pick another port",argv:[...cli(),current.command??"serve","--port","<PORT>"]}])}}let theirs=(kind,reason,headline,next=[])=>({class:kind,reason,message:headline,details:[message],...reached&&["auth","permission","network","platform"].includes(kind)?{context:[["Board",reached]]}:{},...next.length?{next}:{},data:said}),specific=fallback=>/^[a-z]+(_[a-z]+)+$/.test(code)&&code!=="invalid_request"&&code!=="remote_error"?code:fallback;if(status===400)return theirs("usage",specific("invalid_arguments"),`Can't ${doing()}.`);if(status===401)return theirs("auth","login_required","The board didn't accept your credentials.",[{instruction:"Log in again",argv:["vercel","login"]}]);if(status===403)return theirs("permission","permission_denied",`You can't ${doing()}.`);if(status===404){let thing=missing();return thing?{...theirs("not_found",specific("not_found"),`Couldn't find ${thing}.`),details:[]}:theirs("not_found",specific("not_found"),`Can't ${doing()}.`)}if(status===409){let{args}=current,newer=code==="newer_message"&&flags.after!==void 0&&args[0]!==void 0,labeled2=code==="label_conflict"&&(flags.if!==void 0||flags["if-unset"]!==void 0)&&args[0]!==void 0,changed=code==="revision_conflict"&&flags["if-revision"]!==void 0&&args[0]!==void 0;return theirs("conflict",specific("conflict"),`Can't ${doing()}.`,newer?[{instruction:`Read what came after ${ref(flags.after)}`,argv:[...cli(),"read",args[0].replace(/^#/,""),"--after",flags.after.replace(/^#/,"")]}]:labeled2?[{instruction:"See its labels now",argv:[...cli(),"read",args[0].replace(/^#/,"")]}]:changed?[{instruction:"Read it as it is now",argv:[...cli(),"read",args[0].replace(/^#/,""),"--json"]}]:[])}return status===413?theirs("limit","payload_too_large",`Can't ${doing()}. It's too large for the board.`):status===429?theirs("rate_limit","rate_limited","Too many requests to the board. Try again in a minute."):status===501?theirs("platform",specific("not_supported"),`Can't ${doing()}. This board doesn't support it.`):code==="unreachable"?theirs("network","network_error","Couldn't reach the board."):theirs("platform","api_error",`Failed to ${doing()}.`)}function report(failure,json2){return app.onError?app.onError(failure,{json:json2}):json2?io.error(`${JSON.stringify(failureDocument(failure))}
|
|
54
|
+
`):io.error(`${terminalText(formatFailure(failure))}
|
|
55
|
+
`),exitCode(failure)}function vercelBoard(url){let parsed;try{parsed=new URL(url)}catch{return null}let found=/^\/v1\/messageboard\/boards\/([^/]+)\/?$/.exec(parsed.pathname);if(parsed.origin!=="https://api.vercel.com"||!found)return null;let team2=parsed.searchParams.get("teamId");return{board:decodeURIComponent(found[1]),...team2?{team:team2}:{}}}function profileFlag(){if(flags.human)return null;if(flags.as)return flags.avatar?{name:flags.as,avatar:flags.avatar}:flags.as}function connection(url=flags.url,options2={}){let profile=profileFlag(),team2=flags.scope??flags.team,configured=url??(team2===void 0?configuredUrl():null);if(configured===null){let board$1=board({backend:vercel(team2!==void 0?{team:team2}:{})});return{url:null,transport:{source:credentialSource},board:profile!==void 0?board$1.as(profile):board$1}}let at=configured.replace(/\/+$/,"");reached=at;let onVercel=vercelBoard(at);if(onVercel){let board$3=board({backend:vercel(onVercel)});return{url:at,transport:{source:credentialSource},board:profile!==void 0?board$3.as(profile):board$3}}let handle=options2.handle??flags.handle,transport=connect(at,{...options2.token!==void 0?{token:options2.token}:{},...handle?{handle}:{}}),board$2=board({backend:http(at,{fetch:transport.fetch})});return{url:at,transport,board:profile!==void 0?board$2.as(profile):board$2}}function client(){return io.board?flags.human?io.board.as(null):flags.as?io.board.as(flags.avatar?{name:flags.as,avatar:flags.avatar}:flags.as):io.board:connection().board}async function readStdin(){let chunks=[];for await(let chunk of process.stdin)chunks.push(chunk);return Buffer.concat(chunks).toString("utf8")}function hookAgents(names){let known=Object.keys(AGENTS);for(let name of names)if(!known.includes(name))throw usage2(`Unknown agent ${name}. The agents are ${known.join(", ")}.`);return names.length?names:known}function warn(warning){flags.json?warnings.push(warning):app.onWarning?app.onWarning(warning):io.error(`${terminalText(`warning: ${warning.message}`)}
|
|
56
|
+
`)}function warnUnknownMentions(handles){if(!handles?.length)return;let names=handles.map(handle=>`@${handle}`),list=names.length===1?names[0]:`${names.slice(0,-1).join(", ")} and ${names.at(-1)}`,one=names.length===1;warn({reason:"unknown_mention",message:`${list} ${one?"matches":"match"} no one on the board, so ${one?"it stays":"they stay"} plain text and ${one?"notifies":"notify"} no one.`})}function printMessage(message){flags.json?result({message,outcome:"posted"}):out(messageRef(message.id))}function printDone(id2,outcome){flags.json&&result({message:{id:id2},outcome})}function browsable(url){try{let parsed=new URL(url);return parsed.protocol==="https:"||parsed.protocol==="http:"?parsed.href:null}catch{return null}}function openBrowser(url){let at=browsable(url);if(at===null)return;let[command,...args]=process.platform==="darwin"?["open",at]:process.platform==="win32"?["rundll32","url.dll,FileProtocolHandler",at]:["xdg-open",at],child=spawn(command,args,{detached:!0,stdio:"ignore"});child.on("error",()=>{}),child.unref()}function portFlag(){let port=flags.port===void 0?void 0:Number(flags.port);if(port!==void 0&&!(Number.isInteger(port)&&port>=0&&port<=65535))throw usage2(`${flags.port} isn't a port.`,{reason:"invalid_port"});return port}var LINK_WAIT_MS=6e5;async function identity(args){let board2=client(),[sub,target]=args;if(sub===void 0){let view=await board2.identity();if(flags.json)return result(view),0;view.links.length===0&&out("no linked accounts");for(let link of view.links)out(`${link.handle?`@${link.handle} `:""}${link.actor} by ${link.method==="code"?"code":"sign-in"} \xB7 ${link.at}`);return view.unlinked?.length&&out(`not linked ${view.unlinked.join(", ")} (${named()} identity link <key>)`),0}if(sub==="unlink"){if(!target)throw usage2(`Missing the account to unlink: a handle or actor ID from \`${named()} identity\`.`,{reason:"missing_arguments"});if(args.length!==2)throw usage2("identity unlink takes one account.");let unlinked=await board2.unlinkAccount(target);return flags.json?result({...unlinked,outcome:"unlinked"}):out(`unlinked ${unlinked.actor}`),0}if(sub!=="link")throw usage2(`Unknown command ${sub} for ${named()} identity.`,{reason:"unknown_command"});if(!target)throw usage2("Missing the sync entry to link your account on, such as slack.",{reason:"missing_arguments"});if(args.length!==2)throw usage2("identity link takes one sync entry.");let before=new Set((await board2.identity()).links.map(link=>`${link.actor}|${link.at}`)),platform=(await board2.discovery().catch(()=>null))?.sync.find(entry=>entry.key===target)?.platform??target,{code,expiresAt,signIn}=await board2.syncCode(target),consent=signIn!==void 0&&!flags.code;if(consent&&browsable(signIn)===null)throw new Failed({class:"platform",reason:"invalid_sign_in",message:"The board's sign-in isn't an http or https URL.",details:[JSON.stringify(signIn)],data:{signIn}});flags.json&&line({type:"code",sync:target,platform,code,expiresAt,...signIn!==void 0?{signIn}:{}});let until2=Date.now()+LINK_WAIT_MS,sleep=()=>new Promise(resolve2=>setTimeout(resolve2,2e3)),linkedSince=async()=>(await board2.identity()).links.find(link=>link.actor.startsWith(`${platform}:user:`)&&!before.has(`${link.actor}|${link.at}`));if(consent)flags.json||(out(`Sign in to ${platform} as yourself to link it (and to post as you there):
|
|
57
|
+
|
|
58
|
+
${signIn}
|
|
59
|
+
`),out("Only open it yourself: whoever completes it links their account to you.")),!flags["no-open"]&&!flags.json&&openBrowser(signIn);else if(!flags.json){let minutes=Math.round((Date.parse(expiresAt)-Date.now())/6e4);out(`From your ${platform} account, post this as a message anywhere the board syncs, within ${minutes} minutes:
|
|
60
|
+
|
|
61
|
+
${code}
|
|
62
|
+
`)}if(flags["no-wait"])return 0;for(;Date.now()<Math.min(until2,consent?1/0:Date.parse(expiresAt));){await sleep();let linked=await linkedSince();if(linked)return flags.json?line({type:"linked",account:linked}):out(`linked ${linked.handle?`@${linked.handle} `:""}(${linked.actor})`),0}throw new Failed({class:"timeout",reason:"timeout",message:consent?"Nothing linked an account within 10 minutes.":"The code expired before a message carried it.",next:[{instruction:"Try again",argv:[...cli(),"identity","link",target]}]})}async function run(command,args){switch(command){case void 0:case"help":return out(help(args)),0;case"whoami":{let{board:board2,url:given,transport}=connection(),[me,discovered]=await Promise.all([board2.me(),board2.discovery()]),url=given??discovered.url,source=transport.source();if(flags.json)return result({...me,board:url,source}),0;let name=me.profile!==null?`${me.name} via ${me.handle.split("/")[0]}`:me.name;out(`${name} (${me.actor})`);let rows2=[["board",url]];if(source&&rows2.push(["source",source]),me.expiresAt){let minutes=Math.max(0,Math.round((Date.parse(me.expiresAt)-Date.now())/6e4));rows2.push(["token",`expires in ${Math.floor(minutes/60)}h ${minutes%60}m`])}me.session&&rows2.push(["session",me.session]),(me.can===void 0?[]:formatAccess(me.can)).forEach((line2,index)=>rows2.push([index===0?"can":"",line2]));let width=Math.max(...rows2.map(([subject])=>subject.length));for(let[subject,value]of rows2)out(`${subject.padEnd(width)} ${value}`);return 0}case"conformance":{if(args.length>0)throw usage2("conformance takes no arguments: pass its channel with --channel.");let report2=await conformance(client(),flags.channel?{channel:flags.channel}:{});return flags.json?result(report2):out(formatConformance(report2)),report2.ok?0:1}case"channels":{let channels=await client().channels();if(flags.json)return result({channels}),0;if(channels.length===0)return out("no channels yet"),0;let group="";for(let channel of channels){let prefix=channel.path.includes("/")?channel.path.slice(0,channel.path.lastIndexOf("/")+1):"";prefix!==group&&(prefix&&out(prefix),group=prefix);let count=(n,one,many)=>`${n} ${n===1?one:many}`;out(`${prefix?" ":""}${channel.path} ${count(channel.threads,"thread","threads")} \xB7 ${count(channel.messages,"message","messages")} \xB7 last #${channel.lastIndex}`)}return 0}case"actors":{let actors=await client().actors();if(flags.json)return result({actors}),0;for(let actor of actors)out(`@${actor.handle} ${actor.name} ${actor.id}${actor.person?` \u2192 @${actor.person.handle}`:""}`);return 0}case"identity":return identity(args);case"read":{let target=args[0],board2=client(),link=target!==void 0&&/^https?:\/\//i.test(target);if(target&&(link||isMessageRef(target))){let thread=link?await board2.linked(target):await board2.thread(messageId(target));if(link&&!Array.isArray(thread.replies))throw outdated("This board can't read links.");let narrowed=Object.keys(filters2()).length>0,replies=narrowed?(await board2.messages({...filters2(),re:thread.message.id,limit:500})).messages.filter(m=>m.re!==null):thread.replies;if(flags.json)return result({...thread,replies}),0;let authors=new Set([thread.message,...replies].map(m=>m.by.actor)),unnamed=reactionNames([thread.message,...replies]).filter(id2=>!authors.has(id2)),known=unnamed.length?new Map((await board2.actors().catch(()=>[])).filter(a=>unnamed.includes(a.id)).map(a=>[a.id,actorName(a)])):new Map;if(out(formatThreads([thread.message,...replies],known)),!narrowed)for(let schedule of thread.schedules??[])out(formatSchedule(schedule,{pad:" "}));return 0}let query={...filters2(target),...flags.limit?{limit:Number(flags.limit)}:{}},old=outdated("This board can't list threads."),page=await board2.threads(query).catch(error=>{throw error instanceof BoardError&&error.status===404&&/^no route: /.test(error.message)?old:error});if(!Array.isArray(page.threads))throw old;if(!page.threads.length&&target){let channels=(await board2.channels()).map(c=>c.path);if(!channels.some(path=>path===target||path.startsWith(target.replace(/\/?$/,"/")))){let guess=closest(target,channels);throw new Failed({class:"not_found",reason:"channel_not_found",message:`Couldn't find channel ${target}.${guess?` Did you mean ${guess}?`:""}`,next:[{instruction:"See its channels",argv:[...cli(),"channels"]}]})}}if(flags.json)return result(page),0;let count=`${page.threads.length} ${page.threads.length===1?"thread":"threads"}`;return out(`${target??"all channels"} \xB7 ${count} \xB7 cursor ${page.cursor}${page.threads.length?`
|
|
63
|
+
`:""}`),page.threads.length&&out(formatThreadSummaries(page.threads,{channel:!target||target.endsWith("/")})),0}case"post":{let[target,body]=args;if(!target)throw usage2("Missing the channel or message to post to.",{reason:"missing_arguments"});let re=isMessageRef(target)?messageId(target):void 0;if(re!==void 0&&flags.sync?.length)throw usage2("Can't use --sync on a reply. It syncs whole threads.",{next:[{instruction:"Sync the thread instead",argv:[...cli(),"sync",re,"<TARGET>"]}]});if(flags.sync?.length&&flags["no-sync"])throw usage2("Can't use --sync with --no-sync.");if(re===void 0&&flags.after!==void 0)throw usage2("Can't use --after on a new thread. It's for replies: the thread's newest message.");let board2=client(),content=body===void 0&&flags.attach?.length?"":await text(body),files=await attachments(board2),options2={text:content,...files.length?{attachments:files}:{},...flags.label?.length?{labels:labelPairs(flags.label)}:{},...flags["idempotency-key"]?{id:flags["idempotency-key"]}:{},...flags["no-sync"]?{noSync:!0}:{}},message=re===void 0?await board2.post(target,{...options2,...flags.sync?.length?{sync:flags.sync}:{}}):await board2.reply(re,{...options2,...flags.after!==void 0?{after:messageId(flags.after)}:{}});return warnUnknownMentions(message.unknownMentions),printMessage(message),0}case"reply":{let id2=messageId(args[0]),board2=client(),content=args[1]===void 0&&flags.attach?.length?"":await text(args[1]),files=await attachments(board2),message=await board2.reply(id2,{text:content,...files.length?{attachments:files}:{},...flags["idempotency-key"]?{id:flags["idempotency-key"]}:{},...flags["no-sync"]?{noSync:!0}:{},...flags.after!==void 0?{after:messageId(flags.after)}:{}});return warnUnknownMentions(message.unknownMentions),printMessage(message),0}case"schedule":{if(args[0]==="cancel"&&args.length===2){let{schedule:schedule2,canceled}=await client().cancelSchedule(scheduleId(args[1]));return flags.json?result({schedule:schedule2,outcome:canceled?"canceled":"unchanged"}):out(canceled?`canceled ${schedule2.id}`:`${schedule2.id} is already ${schedule2.state}`),0}let[target,body]=args;if(!target)throw usage2("Missing the channel or message to schedule a message in.",{reason:"missing_arguments"});if(!flags.in&&!flags.at&&!flags.every)throw usage2("Can't schedule a message without a time: pass --in 1d, --at <time>, or --every 30m.",{reason:"missing_arguments"});if(flags.sync?.length)throw usage2("Can't use --sync with schedule. A schedule posts where you point it.");let times=flags.times===void 0?void 0:Number(flags.times);if(times!==void 0&&!(Number.isInteger(times)&×>0))throw usage2(`--times takes a count, and ${flags.times} isn't one.`);let board2=client(),content=body===void 0&&flags.attach?.length?"":await text(body),files=await attachments(board2),where=isMessageRef(target)?{re:messageId(target)}:target,schedule=await board2.schedule(where,{text:content,...files.length?{attachments:files}:{},...flags.label?.length?{labels:labelPairs(flags.label)}:{},...flags["no-sync"]?{noSync:!0}:{},...flags.in?{in:flags.in}:{},...flags.at?{at:instant("at",flags.at)}:{},...flags.every?{every:flags.every}:{},...flags.until?{until:instant("until",flags.until)}:{},...flags.for?{for:flags.for}:{},...times!==void 0?{times}:{}});return warnUnknownMentions(schedule.unknownMentions),flags.json?result({schedule,outcome:"scheduled"}):out(formatSchedule(schedule)),0}case"schedules":{let list=await client().schedules({...args[0]?{re:messageId(args[0])}:{},...flags.by?{by:flags.by.replace(/^@/,"")}:{},...flags.channel?{channel:flags.channel}:{},...flags.all?{all:!0}:{}});return flags.json?(result({schedules:list}),0):list.length===0?(out(flags.all?"no schedules":"no pending schedules"),0):(out(list.map(schedule=>formatSchedule(schedule)).join(`
|
|
64
|
+
`)),0)}case"hooks":{let[action,...names]=args;if(action!=="uninstall")throw usage2(`\`${`${named()} hooks ${action??""}`.trimEnd()}\` is gone. Turn-end hooks were removed.`,{reason:"unknown_command",next:[{instruction:"Clear an earlier install",argv:[...cli(),"hooks","uninstall"]}]});let agents=hookAgents(names),width=Math.max(...agents.map(agent=>AGENTS[agent].name.length)),hooks=[];for(let agent of agents){let installed=installedHook(agent),by=installed?cliOf(installed):null,rules=by?allowRules(agent,by):null;rules&&disallow(rules.file,rules.rules);let removed=uninstallHook(agent);hooks.push({agent,outcome:removed?"removed":"unchanged",file:installed?AGENTS[agent].file():null}),flags.json||out([AGENTS[agent].name.padEnd(width),removed?"removed":"not installed",installed?tilde(AGENTS[agent].file()):""].join(" ").trimEnd())}return flags.json&&result({hooks}),0}case"hook":return 0;case"edit":{let id2=messageId(args[0]),{unknownMentions}=await client().edit(id2,await text(args[1]),ifRevision());return warnUnknownMentions(unknownMentions),printDone(id2,"edited"),0}case"delete":{let id2=messageId(args[0]);return await client().delete(id2,ifRevision()),printDone(id2,"deleted"),0}case"react":{let id2=messageId(args[0]);if(!args[1])throw usage2("Missing the emoji to react with, like eyes or thumbs_up.",{reason:"missing_arguments"});let emoji;try{emoji=normalizeEmoji(args[1])}catch{throw usage2(`Unknown emoji ${args[1]}. Use a short name, like eyes or thumbs_up.`)}return await client().react(id2,emoji,flags.remove?{remove:!0}:{}),printDone(id2,flags.remove?"unreacted":"reacted"),0}case"label":{let id2=messageId(args[0]),set=labelPairs(args.slice(1)),unset=flags.unset??[];if(Object.keys(set).length===0&&unset.length===0)throw usage2("Missing the labels: k=v to set one, --unset k to remove one.",{reason:"missing_arguments"});let expected=labelPairs(flags.if,"--if takes key=value, and {value} isn't one. For a label that mustn't be set, --if-unset key.");for(let key of flags["if-unset"]??[]){if(Object.hasOwn(expected,key))throw usage2(`Can't use --if ${key}=\u2026 with --if-unset ${key}.`);expected[key]=null}return await client().label(id2,{set,unset,...Object.keys(expected).length?{if:expected}:{}}),printDone(id2,"labeled"),0}case"typing":{let id2=messageId(args[0]),status=args.slice(1).join(" ").trim(),targets=await client().typing(id2,status||void 0);return flags.json&&result({targets}),0}case"sync":{let id2=messageId(args[0]);if(!args[1]){let{targets}=await client().sync(id2);return flags.json?(result({targets}),0):(out(targets.length?formatSynced(targets):"not synced anywhere"),0)}let synced=await client().sync(id2,args[1]);return flags.json?result({...synced,outcome:synced.attached?"synced":"unchanged"}):out(synced.attached?`synced with ${synced.target}`:`already synced with ${synced.target}`),0}case"download":{let id2=args[0];if(!id2)throw usage2(`Missing the attachment to download: the ID \`${named()} read\` shows on a file line.`,{reason:"missing_arguments"});let response=await client().download(id2),data=new Uint8Array(await response.arrayBuffer());if(args[1]==="-")return io.write(data),0;let disposition=response.headers.get("content-disposition")??"",filename=/filename\*=UTF-8''([^;]+)/.exec(disposition)?.[1],path=args[1]??basename(filename?decodeURIComponent(filename):id2);return await io.writeFile(path,data),flags.json?result({attachment:{id:id2},path,outcome:"downloaded"}):out(path),0}case"inbox":{let key=args[0];if(!key)throw usage2("Missing the sync entry whose inbox to read, such as slack.",{reason:"missing_arguments"});let events2=await client().inbox(key,{...args[1]?{channel:args[1]}:{},...flags.limit?{limit:Number(flags.limit)}:{}});if(flags.json)return result({events:events2}),0;if(events2.length===0)return out("no events"),0;for(let entry of events2){let event=entry.event,detail=event.text??event.type??event.emoji??"";out(`${entry.createdAt} ${entry.channel} ${event.kind} ${event.author?.handle??""} ${detail.split(`
|
|
65
|
+
`)[0]}`)}return 0}case"sources":{let[key,sub,target]=args;if(!key){let{sync}=await client().discovery();if(flags.json)return result({sources:sync}),0;if(sync.length===0)return out("no sources: this board has no sync configured"),0;for(let entry of sync)out(`${entry.key} ${entry.platform}`);return 0}if(sub==="join"&&target){let joined=await client().joinSource(key,target);if(flags.json)return result({...joined,outcome:joined.joined?"joined":"unchanged"}),0;let label=joined.name?`#${joined.name}`:joined.channel;return out(joined.joined?`joined ${label}`:`already in ${label}`),0}if(sub!=="read"&&sub!=="join")throw usage2(sub===void 0?`Missing what to do with ${key}: read or join.`:`Unknown command ${sub} for ${named()} sources.`,{reason:sub===void 0?"missing_arguments":"unknown_command"});if(!target)throw usage2(`Missing the channel, thread, or link to ${sub}.`,{reason:"missing_arguments"});let read=await client().readSource(key,target,{...flags.limit?{limit:Number(flags.limit)}:{},...flags.after?{after:String(flags.after)}:{}});return flags.json?(result(read),0):(out(formatSourceRead(key,read,Date.now(),named())),0)}case"tail":{let controller=new AbortController,stop=stopOn(controller);try{for await(let event of client().events({...filters2(args[0]),...flags.all?{all:!0}:{}},{signal:controller.signal}))flags.json?line(event):out(formatEvent(event))}finally{stop.release()}return stop.interrupted()?130:0}case"wait":{let controller=new AbortController,stop=stopOn(controller),timeout=flags.timeout??"30m",event=await client().wait(filters2(args[0]),{timeout,signal:controller.signal}).finally(stop.release);if(!event){if(controller.signal.aborted)return stop.interrupted()?130:0;throw new Failed({class:"timeout",reason:"timeout",message:`Nothing matched within ${timeout}.`})}return flags.json?result({event}):out(formatEvent(event)),0}default:{let own2=app.commands[command];if(own2)return own2.run(args,context());let guess=closest(command,[...commands.flatMap(known=>[known.name,...known.aliases??[]]),...Object.keys(app.commands)]);throw usage2(`Unknown command ${command} for ${named()}.${guess?` Did you mean ${guess}?`:""}`,{reason:"unknown_command",next:[{instruction:"See all commands",argv:[...cli(),"--help"]}]})}}}function stopOn(controller){let stop=()=>controller.abort();if(io.signal){io.signal.aborted&&stop(),io.signal.addEventListener("abort",stop,{once:!0});let signal=io.signal;return{release:()=>signal.removeEventListener("abort",stop),interrupted:()=>!1}}return process.once("SIGINT",stop),{release:()=>process.off("SIGINT",stop),interrupted:()=>controller.signal.aborted}}var context=()=>({flags,out,usage:message=>usage2(message),portFlag,connection,client,openBrowser});async function main(argv,options2){handLogin(options2.login??null),handTeam(options2.team??null),handFetch(options2.fetch??null);try{return await runOnce(argv,options2)}finally{handLogin(null),handTeam(null),handFetch(null)}}async function runOnce(argv,options2){app=options2,typed=argv,flags={},positionals=[],warnings=[],reached=null;try{if(parse(argv),flags.version)return flags.json?result({version:VERSION}):out(VERSION),0;if(flags.help)return out(help(positionals)),0;let[word,...args]=positionals,command=canonical(word);if(current={command,args},io.board){if(flags.url||flags.handle)throw usage2(`--${flags.url?"url":"handle"} doesn't apply in-process: the board handle you pass says which board and who.`,{next:[]});if(command!==void 0&&MACHINE.has(command))throw usage2(`${command} isn't available in-process: it's about the machine ${named()} runs on.`,{reason:"not_available",next:[]})}return await run(command,args)}catch(error){let json2=flags.json??argv.slice(0,argv.includes("--")?argv.indexOf("--"):void 0).includes("--json");return report(failureOf(error),json2)}}var MACHINE=new Set(["whoami","identity","hooks","hook","serve","inspect"]);async function mainWith(argv,options2,with_){io=with_;try{return await main(argv,options2)}finally{io=TERMINAL}}function createApp(options2){let{appName,onError,onWarning,login,team:team2,fetch:fetch2}=options2;return{run:args=>main(args,{name:appName,commands:{},teamBoard:!0,...onError?{onError}:{},...onWarning?{onWarning}:{},...login?{login}:{},...team2?{team:team2}:{},...fetch2?{fetch:fetch2}:{}})}}var lacking=(what,option)=>async()=>{throw new Failed({class:"usage",reason:"not_available",message:`${what} here: pass ${option} in vb()'s options.`})};function refused(argv,failure){let stderr=argv.slice(0,argv.includes("--")?argv.indexOf("--"):void 0).includes("--json")?JSON.stringify(failureDocument(failure)):formatFailure(failure);return Promise.resolve({code:exitCode(failure),stdout:"",stderr:`${stderr}
|
|
66
|
+
`})}var queue=Promise.resolve();function vb(board2,argv,options2={}){let word=commandWord(argv);if(word!==void 0&&options2.refuse?.includes(word))return refused(argv,{class:"usage",reason:"not_available",message:`${word} isn't available here.`});if((word==="tail"||word==="wait")&&!options2.signal)return refused(argv,{class:"usage",reason:"not_available",message:`${word} runs until it's stopped, and vb() runs one call at a time: pass a signal.`});let result2=queue.then(async()=>{let stdout="",stderr="",decoder=new TextDecoder;return{code:await mainWith(argv,{name:"board",commands:{}},{board:board2,write:chunk=>void(stdout+=typeof chunk=="string"?chunk:decoder.decode(chunk,{stream:!0})),error:chunk=>void(stderr+=chunk),readFile:options2.readFile?async path=>options2.readFile(path):lacking("--attach can't read files","readFile"),writeFile:options2.writeFile?async(path,data)=>options2.writeFile(path,data):lacking("download can't save files","writeFile"),stdin:options2.stdin!==void 0?async()=>options2.stdin:lacking("- can't read stdin","stdin"),...options2.signal?{signal:options2.signal}:{}}),stdout,stderr}});return queue=result2.catch(()=>{}),result2}export{commandLine,commandWord,createApp,failureLines,vb};
|