excaliagent 0.1.0 → 0.3.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/README.md +40 -9
- package/dist/server/args.js +6 -2
- package/dist/server/cli.js +35 -8
- package/dist/server/desktop/desktop.js +106 -64
- package/dist/server/desktop/exec.js +27 -0
- package/dist/server/desktop/process-table.js +167 -0
- package/dist/server/http.js +27 -5
- package/dist/server/instance.js +52 -0
- package/dist/server/panes/herdr-clients.js +28 -49
- package/dist/server/panes/herdr.js +5 -3
- package/dist/server/scene-store.js +58 -7
- package/dist/server/token.js +23 -0
- package/dist/web/assets/{abnfDiagram-VCTEODGH-lVjPevGa.js → abnfDiagram-VCTEODGH-DV-5IMmB.js} +1 -1
- package/dist/web/assets/{ar-SA-G6X2FPQ2-CD5aVmDx.js → ar-SA-G6X2FPQ2-n3XdOipE.js} +1 -1
- package/dist/web/assets/{architecture-7GRP2DOG-9pDjotLm.js → architecture-7GRP2DOG-C497sWO2.js} +1 -1
- package/dist/web/assets/{architectureDiagram-5GKGNRK7-D0Yl-_Yc.js → architectureDiagram-5GKGNRK7-DuCJZflG.js} +1 -1
- package/dist/web/assets/{az-AZ-76LH7QW2-D4vT9Alz.js → az-AZ-76LH7QW2-DSYFdGPZ.js} +1 -1
- package/dist/web/assets/{bg-BG-XCXSNQG7-MMTI5wg3.js → bg-BG-XCXSNQG7-aq-bPVBx.js} +1 -1
- package/dist/web/assets/{blockDiagram-I7D4REHJ-DhrilPMV.js → blockDiagram-I7D4REHJ-D6rbmy9X.js} +1 -1
- package/dist/web/assets/{bn-BD-2XOGV67Q-CCHqTpZW.js → bn-BD-2XOGV67Q-BsnX_gLN.js} +1 -1
- package/dist/web/assets/{c4Diagram-7LVT6UL2-DGXbRPw_.js → c4Diagram-7LVT6UL2-bkZh0r6f.js} +1 -1
- package/dist/web/assets/{ca-ES-6MX7JW3Y-ChGGIAut.js → ca-ES-6MX7JW3Y-CvtjFAUU.js} +1 -1
- package/dist/web/assets/channel-B_LXUtkZ.js +1 -0
- package/dist/web/assets/{chunk-4HAMMTFA-Cw9s83q8.js → chunk-4HAMMTFA-DZJ_s-sA.js} +1 -1
- package/dist/web/assets/{chunk-75Z2AOVW-BC0CaRUB.js → chunk-75Z2AOVW-DU74RPBJ.js} +1 -1
- package/dist/web/assets/{chunk-DU6HZSFF-W9nFVr4b.js → chunk-DU6HZSFF-CChYnq-U.js} +1 -1
- package/dist/web/assets/{chunk-EIO257PC-DG_y2xQ1.js → chunk-EIO257PC-DIGaCD3q.js} +1 -1
- package/dist/web/assets/{chunk-F27PBJKO-yfVK-Hr6.js → chunk-F27PBJKO-C65b5F8o.js} +1 -1
- package/dist/web/assets/{chunk-GMAD6QVW-C2Qn052r.js → chunk-GMAD6QVW-CJy4srfe.js} +1 -1
- package/dist/web/assets/{chunk-GVQU2GXP-IFaX5U4g.js → chunk-GVQU2GXP-CedQyfJw.js} +1 -1
- package/dist/web/assets/{chunk-IMKFNOWR-M1ZEBqas.js → chunk-IMKFNOWR-DPwApfTy.js} +1 -1
- package/dist/web/assets/{chunk-L3NEJ4N5-oLQiZ5i-.js → chunk-L3NEJ4N5-DXku7BIn.js} +1 -1
- package/dist/web/assets/{chunk-OSK3NFVY-D-OfNZzS.js → chunk-OSK3NFVY-BstcDrHK.js} +1 -1
- package/dist/web/assets/{chunk-P2QGCYS3-DJerQdnp.js → chunk-P2QGCYS3-BHerElni.js} +1 -1
- package/dist/web/assets/{chunk-POPQ4Y6H-BC0OyYJc.js → chunk-POPQ4Y6H-ZS2hGTJJ.js} +1 -1
- package/dist/web/assets/{chunk-PWAF6VOD-CjU6T4Vk.js → chunk-PWAF6VOD-BUUBZvSR.js} +1 -1
- package/dist/web/assets/{chunk-SHT3W25Y-DBMa5yhK.js → chunk-SHT3W25Y-CMdySuQW.js} +1 -1
- package/dist/web/assets/{chunk-SVP7TREG-Bbvoxr5d.js → chunk-SVP7TREG-E_6g4L6l.js} +1 -1
- package/dist/web/assets/{chunk-TICWLB2K-DIPq7Qti.js → chunk-TICWLB2K-CBTJSW3m.js} +1 -1
- package/dist/web/assets/classDiagram-ZZMXUADV-zYihe46b.js +1 -0
- package/dist/web/assets/classDiagram-v2-VYDZK3BY-zYihe46b.js +1 -0
- package/dist/web/assets/{cs-CZ-2BRQDIVT-Bax3wFiz.js → cs-CZ-2BRQDIVT-DLt9XJMu.js} +1 -1
- package/dist/web/assets/{cynefin-OW5HDTMX-y_XDTnaT.js → cynefin-OW5HDTMX-BYqJDvxe.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-5FMLGOSQ-BeJtQvjE.js → cynefinDiagram-5FMLGOSQ-iwuMBkRk.js} +1 -1
- package/dist/web/assets/{da-DK-5WZEPLOC-i66Qepbp.js → da-DK-5WZEPLOC-BnzwheRI.js} +1 -1
- package/dist/web/assets/{dagre-GXQ25YYZ-CzBP3X6H.js → dagre-GXQ25YYZ-DGskQLMq.js} +1 -1
- package/dist/web/assets/{de-DE-XR44H4JA-yjhmbrBK.js → de-DE-XR44H4JA-B05-7snG.js} +1 -1
- package/dist/web/assets/{diagram-S7CK7UJ4-CL5OmME1.js → diagram-S7CK7UJ4-Sclpr_lb.js} +1 -1
- package/dist/web/assets/{diagram-UQ7AKVKN-6_lscQb4.js → diagram-UQ7AKVKN-CkRKjDjq.js} +1 -1
- package/dist/web/assets/{diagram-VSXAHHWV-C3E4-nJG.js → diagram-VSXAHHWV-DKAzSk6r.js} +1 -1
- package/dist/web/assets/{diagram-VX7I27RA-51lQ_NN2.js → diagram-VX7I27RA-Bjdeg2uG.js} +1 -1
- package/dist/web/assets/{diagram-Z3DM3KII-D86k1y9Z.js → diagram-Z3DM3KII-4jN9UD5P.js} +1 -1
- package/dist/web/assets/{dist-BPnbDAwD.js → dist-BQE0NGB-.js} +4 -4
- package/dist/web/assets/{ebnfDiagram-PWID7BFC-DDjKtPWY.js → ebnfDiagram-PWID7BFC-BSFSSJ77.js} +1 -1
- package/dist/web/assets/{el-GR-BZB4AONW-Deizd0yM.js → el-GR-BZB4AONW-ShhNmsmN.js} +1 -1
- package/dist/web/assets/{en-B4ZKOASM-Dvqqxrua.js → en-B4ZKOASM-BNw5UiKn.js} +1 -1
- package/dist/web/assets/{erDiagram-RLTQ6QDP-Dju3XIzd.js → erDiagram-RLTQ6QDP-CXr09bBy.js} +1 -1
- package/dist/web/assets/{es-ES-U4NZUMDT-Bz7tNFv9.js → es-ES-U4NZUMDT-2jCdm2lH.js} +1 -1
- package/dist/web/assets/{eu-ES-A7QVB2H4-B3BHiHTa.js → eu-ES-A7QVB2H4-BhBUGvkL.js} +1 -1
- package/dist/web/assets/{eventmodeling-NTZA5JFV-MeBWwPID.js → eventmodeling-NTZA5JFV-DLX5id4n.js} +1 -1
- package/dist/web/assets/{fa-IR-HGAKTJCU-B9t2EOWw.js → fa-IR-HGAKTJCU-B-Z0NFoQ.js} +1 -1
- package/dist/web/assets/{fi-FI-Z5N7JZ37-BRW-fC7d.js → fi-FI-Z5N7JZ37-C3a93Lzr.js} +1 -1
- package/dist/web/assets/flowDiagram-HODETNUW-CnV5VPNb.js +1 -0
- package/dist/web/assets/{fr-FR-RHASNOE6-C1x7RIuS.js → fr-FR-RHASNOE6-BfJc7jwO.js} +1 -1
- package/dist/web/assets/{ganttDiagram-EL5Y4UJY-CUQyfHZV.js → ganttDiagram-EL5Y4UJY-V2sHQ0x1.js} +1 -1
- package/dist/web/assets/{gitGraph-4MIJSDKK-77imLp1D.js → gitGraph-4MIJSDKK-BGpxF8pu.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-WWUBYQGX-CC4ZOu_u.js → gitGraphDiagram-WWUBYQGX-asoaR4Hx.js} +1 -1
- package/dist/web/assets/{gl-ES-HMX3MZ6V-DSChLEXM.js → gl-ES-HMX3MZ6V-C8eeUPra.js} +1 -1
- package/dist/web/assets/{he-IL-6SHJWFNN-DqrJHras.js → he-IL-6SHJWFNN-DB0HanYn.js} +1 -1
- package/dist/web/assets/{hi-IN-IWLTKZ5I-scDn95Bz.js → hi-IN-IWLTKZ5I-B3Gf328f.js} +1 -1
- package/dist/web/assets/{hu-HU-A5ZG7DT2-2BB7ywaE.js → hu-HU-A5ZG7DT2-7jNfqM7f.js} +1 -1
- package/dist/web/assets/{id-ID-SAP4L64H-BCD-rITt.js → id-ID-SAP4L64H-DZd1jLNQ.js} +1 -1
- package/dist/web/assets/{image-GAAHSSAO-BHEttEFL.js → image-GAAHSSAO-DKJMzS7d.js} +1 -1
- package/dist/web/assets/{index-C6RFmwp_.js → index-BLscnX3-.js} +13 -13
- package/dist/web/assets/{info-A6RAGUB7-Ee6NHVyn.js → info-A6RAGUB7-Dw0JaEzD.js} +1 -1
- package/dist/web/assets/{infoDiagram-27XIBGKW-bwB50WOT.js → infoDiagram-27XIBGKW-DhivBaFe.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-5VMMS53U-6CilUSFW.js → ishikawaDiagram-5VMMS53U-DHIyFGlH.js} +1 -1
- package/dist/web/assets/{it-IT-JPQ66NNP-D4MSuNp_.js → it-IT-JPQ66NNP-BF7oahO7.js} +1 -1
- package/dist/web/assets/{ja-JP-DBVTYXUO-BkR3NP86.js → ja-JP-DBVTYXUO-CtGU47sH.js} +1 -1
- package/dist/web/assets/{journeyDiagram-3NMN7TZE-C-woTmG9.js → journeyDiagram-3NMN7TZE-wwS9CODt.js} +1 -1
- package/dist/web/assets/{kaa-6HZHGXH3-C_EQTf78.js → kaa-6HZHGXH3-CyohUVIM.js} +1 -1
- package/dist/web/assets/{kab-KAB-ZGHBKWFO-P-qeHBkl.js → kab-KAB-ZGHBKWFO-2acNr90z.js} +1 -1
- package/dist/web/assets/{kanban-definition-UXKFOSKX-CL-EUtyf.js → kanban-definition-UXKFOSKX-vqJ11RID.js} +1 -1
- package/dist/web/assets/{kk-KZ-P5N5QNE5-DwZmyItd.js → kk-KZ-P5N5QNE5-C56zTJ0v.js} +1 -1
- package/dist/web/assets/{km-KH-HSX4SM5Z-3SN2ksDz.js → km-KH-HSX4SM5Z-Bg7FoxwJ.js} +1 -1
- package/dist/web/assets/{ko-KR-MTYHY66A--YzrPq0D.js → ko-KR-MTYHY66A-wlZa8Fb7.js} +1 -1
- package/dist/web/assets/{ku-TR-6OUDTVRD-BV1o1ulU.js → ku-TR-6OUDTVRD-DpMT6JcJ.js} +1 -1
- package/dist/web/assets/{line-DGLpHp6u.js → line-Cy2R8zKE.js} +1 -1
- package/dist/web/assets/{lt-LT-XHIRWOB4-BuU5cssW.js → lt-LT-XHIRWOB4-0nDOQqUM.js} +1 -1
- package/dist/web/assets/{lv-LV-5QDEKY6T-Z9bSAiFr.js → lv-LV-5QDEKY6T-DGNAIt-v.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-DA_7CZUN.js → mermaid-parser.core-C-IWeUrh.js} +3 -3
- package/dist/web/assets/{mindmap-definition-YA3MSWOX-B58mvQws.js → mindmap-definition-YA3MSWOX-DDX8XN8u.js} +1 -1
- package/dist/web/assets/{mr-IN-CRQNXWMA-Cz6h_4fN.js → mr-IN-CRQNXWMA-CHXpujpK.js} +1 -1
- package/dist/web/assets/{my-MM-5M5IBNSE-n2l8bHxH.js → my-MM-5M5IBNSE-Bg0H7WJb.js} +1 -1
- package/dist/web/assets/{nb-NO-T6EIAALU-r9C_Vo0N.js → nb-NO-T6EIAALU-BE_hPlhF.js} +1 -1
- package/dist/web/assets/{nl-NL-IS3SIHDZ-z43DJLFx.js → nl-NL-IS3SIHDZ-fdiiEQqB.js} +1 -1
- package/dist/web/assets/{nn-NO-6E72VCQL-Dc6CXJ79.js → nn-NO-6E72VCQL-C7j1gMVo.js} +1 -1
- package/dist/web/assets/{oc-FR-POXYY2M6-BgJXbWuu.js → oc-FR-POXYY2M6-Q5CdkT-D.js} +1 -1
- package/dist/web/assets/{pa-IN-N4M65BXN-CNGXNDOU.js → pa-IN-N4M65BXN-DJyQmbX0.js} +1 -1
- package/dist/web/assets/{packet-AYTQ26CC-DBS9PBUE.js → packet-AYTQ26CC-D4ouOVez.js} +1 -1
- package/dist/web/assets/{pegDiagram-XKGWAZYB-DA7TGmky.js → pegDiagram-XKGWAZYB-DAWOkGLy.js} +1 -1
- package/dist/web/assets/percentages-BXMCSKIN-B3ZxlSu3.js +1 -0
- package/dist/web/assets/{pie-WAS4IAKB-Cx2pnjZr.js → pie-WAS4IAKB-MCWueKc-.js} +1 -1
- package/dist/web/assets/{pieDiagram-E7YTZNPT-q7SgBOc3.js → pieDiagram-E7YTZNPT-BLlQT9t_.js} +1 -1
- package/dist/web/assets/{pl-PL-T2D74RX3-NeLUNwLZ.js → pl-PL-T2D74RX3-CcWFE9AN.js} +1 -1
- package/dist/web/assets/{pt-BR-5N22H2LF-D5X0RFzx.js → pt-BR-5N22H2LF-D6QCOM38.js} +1 -1
- package/dist/web/assets/{pt-PT-UZXXM6DQ-_ZsKyBBN.js → pt-PT-UZXXM6DQ-ByJh7ZNa.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-AXDQQJYC-CqjGVnZh.js → quadrantDiagram-AXDQQJYC-CIinawCS.js} +1 -1
- package/dist/web/assets/{radar-RG4KPBEZ-BWJANoHa.js → radar-RG4KPBEZ-Cm5qT3Hl.js} +1 -1
- package/dist/web/assets/{railroad-74A4TZTK-BTAfdSvV.js → railroad-74A4TZTK-DO3FGMCu.js} +1 -1
- package/dist/web/assets/{railroad-abnf-HS5TGJTU-NfS3EnaM.js → railroad-abnf-HS5TGJTU-DqC68RR5.js} +1 -1
- package/dist/web/assets/{railroad-ebnf-LZEXJU2U-Ce5m2P1B.js → railroad-ebnf-LZEXJU2U-Cby7H3Dj.js} +1 -1
- package/dist/web/assets/{railroad-peg-WCYAUIDC-CyXkeDDy.js → railroad-peg-WCYAUIDC-CJpoUW06.js} +1 -1
- package/dist/web/assets/{railroadDiagram-O6MQD6OU-C4wOSBs0.js → railroadDiagram-O6MQD6OU-CIJ1E8KE.js} +1 -1
- package/dist/web/assets/{requirementDiagram-BXWQKSXE-dC2Q9_dM.js → requirementDiagram-BXWQKSXE-BTDHXcSN.js} +1 -1
- package/dist/web/assets/{ro-RO-JPDTUUEW-B0TUgaSO.js → ro-RO-JPDTUUEW-BlhbQCSw.js} +1 -1
- package/dist/web/assets/{ru-RU-B4JR7IUQ-FyI4SBb1.js → ru-RU-B4JR7IUQ-Cd8b9tGe.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-P5KCCOFB-czlewm_g.js → sankeyDiagram-P5KCCOFB-DFx8uxV2.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-WJ2MYXX4-rtSwZGtV.js → sequenceDiagram-WJ2MYXX4-Dr8vTa3e.js} +1 -1
- package/dist/web/assets/{si-LK-N5RQ5JYF-q-KyMvoF.js → si-LK-N5RQ5JYF-Y7h5xFBD.js} +1 -1
- package/dist/web/assets/{sk-SK-C5VTKIMK-tX-HO0L6.js → sk-SK-C5VTKIMK-DU5G4wEe.js} +1 -1
- package/dist/web/assets/{sl-SI-NN7IZMDC-Dy8vWC0s.js → sl-SI-NN7IZMDC-4olCD7lc.js} +1 -1
- package/dist/web/assets/{stateDiagram-D77RDMKH-CWDH4s-t.js → stateDiagram-D77RDMKH-CRqebLR8.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-MP3YSRHH-DWV16uKx.js +1 -0
- package/dist/web/assets/subset-shared.chunk-1t88pPR-.js +1 -0
- package/dist/web/assets/{subset-worker.chunk-CBZq9OSE.js → subset-worker.chunk-Bqgy-Y1W.js} +1 -1
- package/dist/web/assets/{sv-SE-XGPEYMSR-D1ErsBn-.js → sv-SE-XGPEYMSR-Cp0rVzLX.js} +1 -1
- package/dist/web/assets/{swimlanes-42K2YHIH-CM2OkOmc.js → swimlanes-42K2YHIH-BYd8YoU8.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-VR7AAH4N-CACbHpLI.js +8 -0
- package/dist/web/assets/{ta-IN-2NMHFXQM-PSu4eqYe.js → ta-IN-2NMHFXQM-DPDOL5cy.js} +1 -1
- package/dist/web/assets/{th-TH-HPSO5L25-BVu-PlXo.js → th-TH-HPSO5L25-CN82X-G0.js} +1 -1
- package/dist/web/assets/{timeline-definition-24CTP7MA-Bo6sYnPJ.js → timeline-definition-24CTP7MA-DeQFCevv.js} +1 -1
- package/dist/web/assets/{tr-TR-DEFEU3FU-BtC_O7fA.js → tr-TR-DEFEU3FU-DZkONFun.js} +1 -1
- package/dist/web/assets/{treeView-Q6P3EWNA-B3NS1SBg.js → treeView-Q6P3EWNA-C4dlZskR.js} +1 -1
- package/dist/web/assets/{treemap-WGGIJYW6-Du_VkW6s.js → treemap-WGGIJYW6-oSQNu9HW.js} +1 -1
- package/dist/web/assets/{uk-UA-QMV73CPH-RwWQcIwP.js → uk-UA-QMV73CPH-da8znbfC.js} +1 -1
- package/dist/web/assets/{vennDiagram-4TSXK5OY-R1EH0pXq.js → vennDiagram-4TSXK5OY-e4T6zK4z.js} +1 -1
- package/dist/web/assets/{vi-VN-M7AON7JQ-CUEI6eHH.js → vi-VN-M7AON7JQ-QWCjquXm.js} +1 -1
- package/dist/web/assets/{wardley-WFR3VGLG-9B3cKl8K.js → wardley-WFR3VGLG-BzQw11Sd.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-VM6X3IG4-XZ18gIOz.js → wardleyDiagram-VM6X3IG4-DyPpKV2H.js} +1 -1
- package/dist/web/assets/{xychartDiagram-S5SC5T6Z-BLzXnvba.js → xychartDiagram-S5SC5T6Z-DKjIes2P.js} +1 -1
- package/dist/web/assets/{zh-CN-LNUGB5OW-C3asHvVp.js → zh-CN-LNUGB5OW-BbT-kvRj.js} +1 -1
- package/dist/web/assets/{zh-HK-E62DVLB3-BO6FsJfO.js → zh-HK-E62DVLB3-BPw2AWZD.js} +1 -1
- package/dist/web/assets/{zh-TW-RAJ6MFWO-DGtaI2hh.js → zh-TW-RAJ6MFWO-BE9F3OdK.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +3 -2
- package/dist/web/assets/channel-fspI7jZI.js +0 -1
- package/dist/web/assets/classDiagram-ZZMXUADV-Dr-7S1cu.js +0 -1
- package/dist/web/assets/classDiagram-v2-VYDZK3BY-Dr-7S1cu.js +0 -1
- package/dist/web/assets/flowDiagram-HODETNUW-kdP64-E7.js +0 -1
- package/dist/web/assets/percentages-BXMCSKIN-btxXaPp4.js +0 -1
- package/dist/web/assets/stateDiagram-v2-MP3YSRHH-DdSAvpNH.js +0 -1
- package/dist/web/assets/subset-shared.chunk-D1bYkpAa.js +0 -1
- package/dist/web/assets/swimlanesDiagram-VR7AAH4N-CVzVUDpQ.js +0 -8
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-019d66dcad46dc156b162d267f981c20.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-04b718e5623574919c8b0dea5f301444.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-069e77aac84590e2e991d0a0176d34f2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-06c77b8c66e51ed6c63ccb502dd8b8af.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-08e0dc436ad0ad61ba5558db0674d762.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-093b9ef39a46ceae95a1df18a0a3a326.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-095c169f3314805276f603a362766abd.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-09850c4077f3fffe707905872e0e2460.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0986d134c05864f5025962eef9f994a0.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0b5d723fdc4e249c140f0909e87d03b4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0f626226ba1272e832aea87bafd9720e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0f7fb1e0d5015bb1371343153ecf7ce3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0fa55a080fcd0f9dc2e0b0058b793df8.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-0facdf1ea213ba40261022f5d5ed4493.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-10a7ae9a371830a80c3d844acf1c02d7.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-112c051027b2d766c19a519f6ee1f4f7.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-11c345711937f0ba4b8f7b6b919c8440.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-12b52b58eb3df36804b9a654ec9ee194.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-13ae07ed2e272d26d59bc0691cd7117a.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-13d2887ec8ee73c43acdabc52a05af7b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-145aa02cdd91946e67dc934e1acffe75.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-15dc6d811c9cd078f9086a740d5a1038.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-173945821411c09f70c95f98d590e697.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-1b611157cd46bb184d4fa4dae2d6a2b8.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-1e6fd68f1f3902ce48ce8c69df385622.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-1ee544f0f1dac422545c505baa788992.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-1fdc0c67ed57263a80fd108c1f6ccf24.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-203b0e569e3b14aac86a003dc3fa523e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-20cc1bbf50e7efb442756cb605672c1f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-20e7bf72fa05de9adf7dbcc7bf51dde6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-21430ee05a1248901da8d0de08744d47.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-23686f7f29da6e8008c36dd3a80c83d6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-23ad2d71b280f00b1363b95b7bea94eb.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-23f228f3999c01983860012330e4be08.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-24476a126f129212beb33f66853ea151.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-24a21c1e4449222e8d1898d69ff3a404.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-25b7f38e18f035f96cb5e547bd2bd08c.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-29cec36cd205b211da97acabaa62f055.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2a26d20a23b00898ce82f09d2ee47c3f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2adbc89c11e65905393d3dfc468b9d5b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2b7441d46298788ac94e610ffcc709b6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2b77e8ebfb2367ab2662396a60e7d320.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2cf96d082d35ea3d8106851223ad0d16.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2d43040e86ff03ba677f6f9c04cd0805.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-2e33e8dc771ef5e1d9127d60a6b73679.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-33432927cd87d40cfe393c7482bf221f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-353f33792a8f60dc69323ddf635a269e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-36925dfe329a45086cbb7fc5c20d45ac.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3717077e38f98d89eae729b6c14e56dc.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3756e81d3e149cf6099163ee79944fec.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-395c35dd584b56b0789f58a0559beaf1.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3c9de2ae0ea4bc91a510942dfa4be8d2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3cc70dbb64df5b21f1326cc24dee2195.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3e1f8f654357353bf0e04ba5c34b5f7f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3e63ed8162808a9e425ed80a8bc79114.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-3eaa538115d76932653c21d8dc28f207.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4095eb84ef3874e2600247bee0b04026.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-41521fade99856108931b4768b1b2648.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-450da755d5bcb70906e1295e559b9602.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-452225341522a7942f0f6aab1a5c91a3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4535823663ad81405188a528d8f2b1a2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4806e761d750087c2d734fc64596eaff.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4a0fdb40036e87b40aa08dd30584cb85.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4a38cc3e9cf104e69ba246d37f8cf135.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4aca6a43e59aceee2166b0c7e4e85ef1.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4bfaa8ffa64c5ee560aa2daba7c9cbd3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4dc6d5f188d5c96d44815cd1e81aa885.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4ddc14ed3eb0c3e46364317dfc0144a3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4ee10ae43505e2e0bc62656ced49c0fa.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-4f50e5136e136527280bc902c5817561.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-51502f1206be09c565f1547c406e9558.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-51a0e808bbc8361236ac521a119758a3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-52a84a22fd1369bffeaf21da2d6158dc.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5330a2119a716e4e7224ed108b085dac.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-543fa46ace099a7099dad69123399400.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-544fc28abe2c5c30e62383fd4dac255f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-54acdfc2166ad7fcbd074f75fd4a56ba.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5572b3513ba8df57a3d5d7303ee6b11b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-56467a5c8840c4d23a60b2f935114848.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-56a32a7689abd0326e57c10c6c069bb4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-57862b464a55b18c7bf234ce22907d73.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-583d166e56ba0de4b77eabb47ef67839.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5882ffa04f32584d26109137e2da4352.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-58fd02350d0bc52cf1ca3bb32ce9766e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5935a5775af3d5c6307ac667bd9ae74e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-59e9ff77b0efaf684bc09274fb6908c9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5a1ce3117cfe90c48e8fb4a9a00f694d.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5a45d991244d4c7140217e1e5f5ca4f4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5a7fac4b8b23a6e4e5ba0c9bf1756c91.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5b0ed6971aaab9c8ad563230bd5471a7.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-5d2898fbc097a7e24c6f38d80587621e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-603aefd23e350ba7eb124273e3c9bcf1.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-60a3089806700d379f11827ee9843b6b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-60a41c7e1c68f22424e6d22df544bc82.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-642b26e2e5f5fb780b51b593dbc8c851.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-66493ba5a8367f2928812f446f47b56a.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-670ba603758d94268e8606f240a42e12.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-671a2c20b1eb9e4ef8a192833940e319.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-684d65f1793cac449dde5d59cb3c47fb.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-69c09cc5fa3e55c74fc4821f76909cc3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-6ae5b42180ad70b971c91e7eefb8eba2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-6e092f71c1e634059ada0e52abadce67.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-6f3256af8454371776bc46670d33cc65.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-6fe5c5973cc06f74b2387a631ea36b88.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-70c2eb8d64e71a42a834eb857ea9df51.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-70e811fd7994e61f408c923de6ddd078.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7197d6fda6cba7c3874c53d6381ca239.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-72252d73220fa3cd856677888cee1635.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-72536a3d71b694a0d53dd90ddceae41e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-726303e0774b4e678bff8c2deb6ca603.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-733171b4ffcd17ea1fe1c0ba627173bf.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-739bc1a567439c7cffcd1614644593d2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-73e309718fd16cea44b4d54a33581811.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7494dc504ae00ee9cd0505f990f88c5d.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-74e2263a91439c25b91d5132ce9f4d62.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-761d05e3cd968cf574166867998ef06a.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7718fe60986d8b42b1be9c5ace5ccf25.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-774d4f764a1299da5d28ec2f2ffe0d69.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-79d494361ae093b69e74ee9dbe65bfd4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-79f007c1c6d07557120982951ea67998.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7a07ddc0f0c0f5f4a9bad6ee3dda66b5.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7ab2bed91166a9dca83a5ebfbe2a7f38.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7ccce86603f80a099ddb0cb21d4ae3e3.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7e4bde7e9c7f84cd34d8a845e384c746.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7e929f262f30c8ee78bf398150b1a7cd.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7eb9fffd1aa890d07d0f88cc82e6cfe4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-7f855356ab893b0d2b9c1c83b8116f0e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-829615148e6357d826b9242eb7fbbd1e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-866fa7613df6b3fd272bcfd4530c0bb9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-87599f94b6cc129d505b375798d0d751.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-8c2f33cee3993174f7e87c28e4bf42ee.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-8d3bcabb847b56243b16afe62adaaf21.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-8de5b863cb50dfefdd07cb11c774d579.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-8e9f97f01034820170065b2921b4fb5e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-8f476c4c99813d57cbe6eca4727388ad.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-903bb6865f3452e2fda42e3a25547bc5.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-91ddb2969bf2d31ba02ad82998d1314c.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-938d90c10ff8c20386af7f242c05d6b0.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-93fc8f28a33234bcadf1527cafabd502.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-95429962233afd82db1c27df1500a28c.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-9544732d2e62d1a429674f8ee41b5d3a.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-9592bfc861f07bcb8d75c196b370e548.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-95bfd249da4902577b4b7d76ebdd0b44.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-968cffdc8ee679da094e77ebf50f58ef.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-97f7f48ce90c9429bf32ae51469db74d.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-982b630266d87db93d2539affb1275c6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-98f2ad84457de7f3740d9920b8fa8667.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-99a16ef6a64934d5781933dbd9c46b2e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-9cfb2a77a4e45025105ad29a1748b90d.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-9d81066dd2b337c938df6e90380a00dc.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-9eb5a99df4e76ac3363453ac9ca288b1.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a004ddfcb26e67bd6e678c8ed19e25ce.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a0ca5df4258213d7fc9fce80f65ce760.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a1f916d6039285c4ffb900cd654e418f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a203b91dad570bf05a58c3c3ddb529bf.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a365e82ed54697a52f27adcea1315fe8.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a4c34be6d42152e64b0df90bc4607f64.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-a7accba310e821da5505f71c03b76bdb.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-aa0d470430e6391eca720c7cfa44446f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-aa5c9ca6cf4fba00433b7aa3fa10671a.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-ac9ceb44437becc3e9c4dbfebab7fc2d.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b1220a3c61f85cc0408deedb4c5f57a2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b358f7a51ece39a3247942b1feabdb29.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b57aaedfd8ebdf3931f25119dc6a5eb2.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b5c1596551c256e0e9cf02028595b092.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b6d128682ee29e471486354d486a1b90.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b6fd38ca30869792244804b04bc058da.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b7d203b051eff504ff59ddca7576b6a9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-b96d9226ce77ec94ceca043d712182e6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-ba3de316d63c7e339987b16f41a0b879.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-bafff7a14c27403dcc6cf1432e8ea836.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-bd77e3c7f9e0b072d96af37f73d1aa32.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-be549ab72f0719d606a5c01e2c0219b6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c16ed9740b85badf16e86ea782a3062f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c1f94158256bb1f3bf665b053d895af9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c40533fdf4cc57177b12803598af7e59.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c4a687ac4f0c2766eefc9f77ed99cddf.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c69f61a4ab18d0488c8d1fc12e7028e8.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c8b71798409ccc126ee264a00aadcf21.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-c99eda15fc26a2941579560f76c3a5cf.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cb17fc3db95f6d139afc9d31a8e93293.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cbaaefaaf326668277aa24dfa93c4d28.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cd145ce4a0ea18469358df53c207bc1b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cdbce89e82cc1ab53a2decbf5819278f.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-ce4884f96f11589608b76b726a755803.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cf2cc71752631e579e35b0e423bf2638.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cf6ff4e0f491ca0cf3038187a997b9b4.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-cfb211578629b7e8153b37240de6a9d5.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-d0cf73942fea1c74edbdf0b3011f4656.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-d2666cbed13462c5dc36fa2f15c202ca.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-d3716376641d615e2995605b29bca7b6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-dac48066b5883d8b4551fc584f0c2a3e.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-dbea1af6dcd9860be40c3d18254338f5.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e11567fd2accf9957cd0d3c2be937d87.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e2ead7ea7da0437f085f42ffc05f8d13.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e3fcf5180fd466c8915c4e8069491054.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e480d9c614742d05f0e78f274f1e69e6.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e4bca6cfa53e499cae0a6be4894a90e9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e51ef413167c6e14e0c0fdcc585f2fc9.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e5f453bb04da18eed01675eeebd88bf8.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-e656f091b9dc4709722c9f4b84d3c797.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-ec181b795ac1fb5a50d700b6e996d745.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-ee8bae97908d5147b423f77ad0d3c1bb.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f0f13b5c60e0af5553bd359f5513be1b.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f2b54d4e7be0eaefe1c2c56836fa5368.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f56414bf9bced67990def8660e306759.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f5d079153c99a25b9be5b8583c4cc8a7.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f6032fc06eb20480f096199713f70885.woff2 +0 -0
- package/dist/web/fonts/Xiaolai/Xiaolai-Regular-f8ee5d36068a42b51d0e4a1116cfcec1.woff2 +0 -0
package/README.md
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
A spatial scratchboard for your Claude Code sessions, on an Excalidraw canvas.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
Every session is a card. You drag cards wherever they make sense to you (parked,
|
|
6
8
|
blocked on a colleague, come back to, done) and draw anything around them. The
|
|
7
9
|
app keeps each card's **color and label** in sync with the session and never
|
|
8
10
|
moves, resizes, or edits anything else.
|
|
9
11
|
|
|
10
12
|
```
|
|
11
|
-
Fix flaky checkout test
|
|
13
|
+
Fix flaky checkout test ✳ ← title (handwritten, larger), harness logo
|
|
12
14
|
demo-app · fix/flaky ← repo · branch
|
|
13
15
|
needs user: input needed · 2m ← status · what it waits for · last activity
|
|
14
16
|
cache warm 41m ← prompt-cache warmth
|
|
@@ -19,24 +21,34 @@ PR #12 ← last PR link
|
|
|
19
21
|
| Color | Meaning |
|
|
20
22
|
|---|---|
|
|
21
23
|
| red | needs you (input, permission, trust, blocked) |
|
|
22
|
-
|
|
|
24
|
+
| yellow | busy |
|
|
23
25
|
| green | idle, prompt cache still warm: review it before it goes cold |
|
|
24
26
|
| grey | idle, cache cold |
|
|
25
27
|
| dashed, pale | dormant (not running) |
|
|
26
28
|
|
|
29
|
+

|
|
30
|
+
|
|
27
31
|
## Run
|
|
28
32
|
|
|
29
33
|
```sh
|
|
30
34
|
npx excaliagent
|
|
31
35
|
```
|
|
32
36
|
|
|
33
|
-
It starts a server on `127.0.0.1`
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
It starts a server on `127.0.0.1:7357` (a random free port if that one is
|
|
38
|
+
busy; choose with `--port`), prints the URL with its access token, and opens it
|
|
39
|
+
in your browser. After that first visit, `http://127.0.0.1:7357/` works as a
|
|
40
|
+
bookmark. Running it again while a board is up just opens that board.
|
|
41
|
+
|
|
42
|
+
- **Live sessions** appear in the *inbox* by themselves. A new board comes with
|
|
43
|
+
lanes next to it (To Do, In Progress, Review, Blocked, Parked, Done, and two
|
|
44
|
+
blank ones to name yourself); they are plain shapes you can move, resize,
|
|
45
|
+
rename (double-click) or delete. **+ Lane** (top right) adds one more at the
|
|
46
|
+
right end. On an older
|
|
47
|
+
board, add the whole set from the main menu: *Add starter lanes*.
|
|
36
48
|
- **Dormant sessions** are added from the **Sessions** panel (top right).
|
|
37
49
|
- **Double-click a card** to jump to it: a live session focuses its
|
|
38
50
|
[herdr](https://herdr.dev) pane and raises the terminal window (Hyprland,
|
|
39
|
-
Sway). A dormant session is resumed with `claude --resume` in a new herdr
|
|
51
|
+
Sway, macOS). A dormant session is resumed with `claude --resume` in a new herdr
|
|
40
52
|
tab, in the workspace for that repo. Without herdr, the resume command is
|
|
41
53
|
copied instead.
|
|
42
54
|
- **Right-click a card** for an excaliagent section on top of the Excalidraw
|
|
@@ -45,7 +57,11 @@ It starts a server on `127.0.0.1` and opens the board in your browser.
|
|
|
45
57
|
- A **PR link** (GitHub, Bitbucket, GitLab) shows on the card; the card's link
|
|
46
58
|
icon opens it.
|
|
47
59
|
- The board is saved to `$XDG_STATE_HOME/excaliagent/scene.json`
|
|
48
|
-
(default `~/.local/state/excaliagent/`)
|
|
60
|
+
(default `~/.local/state/excaliagent/`), pasted images under `files/` next
|
|
61
|
+
to it.
|
|
62
|
+
- **Start over**: main menu → *Reset the canvas* (undoable) lays out a fresh
|
|
63
|
+
board with the live sessions. Or try a clean board without touching yours:
|
|
64
|
+
`npx excaliagent --state-dir /tmp/excaliagent-fresh`.
|
|
49
65
|
|
|
50
66
|
Options: `--port`, `--no-open`, `--state-dir`, `--claude-dir`, `--no-herdr`, `--no-raise`.
|
|
51
67
|
See `npx excaliagent --help`.
|
|
@@ -56,12 +72,25 @@ See `npx excaliagent --help`.
|
|
|
56
72
|
`~/.claude/projects/` (only metadata lines: title, branch, timestamps, token
|
|
57
73
|
usage, PR links; never message content). Installs no hooks, writes nothing
|
|
58
74
|
outside its state directory.
|
|
59
|
-
- Listens on loopback only. A random token
|
|
60
|
-
|
|
75
|
+
- Listens on loopback only. A random token (created once, kept in the state
|
|
76
|
+
directory, readable only by you) is put into the URL it opens and exchanged
|
|
77
|
+
for an HttpOnly cookie; every request needs it, so other users on the same
|
|
78
|
+
machine stay out. Requests with a
|
|
61
79
|
foreign `Host` (DNS rebinding) or cross-origin writes are refused.
|
|
62
80
|
- Its only actions: focus a herdr pane, raise the terminal window, and start
|
|
63
81
|
`claude --resume <id>` in a new herdr pane. It never types into a running
|
|
64
82
|
session.
|
|
83
|
+
- Images you paste onto the board are stored next to the scene, in the
|
|
84
|
+
state directory.
|
|
85
|
+
- The only outside request: Chinese, Japanese or Korean text on the board loads
|
|
86
|
+
Excalidraw's CJK font from esm.sh (it is not bundled, to keep the package
|
|
87
|
+
small). Everything else, including all other fonts, is served locally.
|
|
88
|
+
|
|
89
|
+
## Platforms
|
|
90
|
+
|
|
91
|
+
Linux and macOS. Raising the terminal window after a jump works on Hyprland,
|
|
92
|
+
Sway and macOS. Windows is not supported yet
|
|
93
|
+
([#1](https://github.com/martinkozle/excaliagent/issues/1), contributions welcome).
|
|
65
94
|
|
|
66
95
|
## herdr (optional)
|
|
67
96
|
|
|
@@ -89,3 +118,5 @@ A privacy test fails if any fixture contains this machine's home path or user
|
|
|
89
118
|
name.
|
|
90
119
|
|
|
91
120
|
Docs: [product](docs/product.md), [design](docs/design.md), [roadmap](docs/roadmap.md). Agents start at [AGENTS.md](AGENTS.md).
|
|
121
|
+
|
|
122
|
+
MIT licensed. The harness logos on cards (from [simple-icons](https://simpleicons.org), CC0) are trademarks of their owners and only identify which agent a card belongs to.
|
package/dist/server/args.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
|
+
/** Used when --port is not given; a busy default falls back to a random port. */
|
|
3
|
+
export const DEFAULT_PORT = 7357;
|
|
2
4
|
export const USAGE = `Usage: excaliagent [options]
|
|
3
5
|
|
|
4
6
|
A spatial Excalidraw board for your Claude Code sessions.
|
|
5
7
|
|
|
6
8
|
Options:
|
|
7
|
-
--port <n> Port on 127.0.0.1 (default: random free port
|
|
9
|
+
--port <n> Port on 127.0.0.1 (default: 7357, or a random free port
|
|
10
|
+
if that is busy; 0 = always random)
|
|
8
11
|
--no-open Do not open the browser, just print the URL
|
|
9
12
|
--state-dir <dir> Where scene.json lives (default: $XDG_STATE_HOME/excaliagent)
|
|
10
13
|
--claude-dir <dir> Claude Code home (default: ~/.claude)
|
|
11
14
|
--no-herdr Do not link sessions to herdr panes
|
|
12
15
|
--no-raise Focus the herdr pane but do not raise its terminal window
|
|
16
|
+
(raising supports Hyprland, Sway and macOS)
|
|
13
17
|
--dev Serve the frontend through Vite with hot reload
|
|
14
18
|
-h, --help Show this help
|
|
15
19
|
-v, --version Show the version
|
|
@@ -31,7 +35,7 @@ export function parseCliArgs(argv) {
|
|
|
31
35
|
version: { type: "boolean", short: "v", default: false },
|
|
32
36
|
},
|
|
33
37
|
});
|
|
34
|
-
let port
|
|
38
|
+
let port;
|
|
35
39
|
if (values.port !== undefined) {
|
|
36
40
|
port = Number(values.port);
|
|
37
41
|
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
package/dist/server/cli.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { randomBytes } from "node:crypto";
|
|
3
2
|
import { readFileSync } from "node:fs";
|
|
4
3
|
import { dirname, join } from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { parseCliArgs, USAGE } from "./args.js";
|
|
5
|
+
import { DEFAULT_PORT, parseCliArgs, USAGE } from "./args.js";
|
|
7
6
|
import { createHub } from "./hub.js";
|
|
8
7
|
import { createAppServer } from "./http.js";
|
|
9
8
|
import { createDesktop } from "./desktop/desktop.js";
|
|
10
9
|
import { createHerdrLink, createNoPaneLink } from "./panes/herdr.js";
|
|
11
10
|
import { createSceneStore, defaultStateDir } from "./scene-store.js";
|
|
12
11
|
import { createClaudeSource } from "./sources/claude.js";
|
|
12
|
+
import { loadOrCreateToken } from "./token.js";
|
|
13
|
+
import { clearRunning, findRunning, recordRunning } from "./instance.js";
|
|
13
14
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
// src/server or dist/server → package root
|
|
15
16
|
const root = join(here, "..", "..");
|
|
@@ -26,14 +27,24 @@ function openBrowser(url) {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
async function main(args) {
|
|
29
|
-
const
|
|
30
|
+
const stateDir = args.stateDir ?? defaultStateDir();
|
|
31
|
+
const token = await loadOrCreateToken(stateDir);
|
|
32
|
+
// One board per state dir: a second server would overwrite the first one's saves.
|
|
33
|
+
const running = await findRunning(stateDir, token);
|
|
34
|
+
if (running !== undefined) {
|
|
35
|
+
const url = `http://127.0.0.1:${running}/?token=${token}`;
|
|
36
|
+
console.log(`excaliagent is already running for ${stateDir}`);
|
|
37
|
+
console.log(`open: ${url}`);
|
|
38
|
+
if (args.open)
|
|
39
|
+
openBrowser(url);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
30
42
|
const source = createClaudeSource({ claudeDir: args.claudeDir });
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
: createNoPaneLink();
|
|
43
|
+
const desktop = args.herdr && args.raise ? createDesktop({ env: process.env }) : undefined;
|
|
44
|
+
const panes = args.herdr ? createHerdrLink({ desktop }) : createNoPaneLink();
|
|
34
45
|
const hub = createHub({ source, panes });
|
|
35
46
|
await hub.start();
|
|
36
|
-
const scenes = createSceneStore(
|
|
47
|
+
const scenes = createSceneStore(stateDir);
|
|
37
48
|
let middleware;
|
|
38
49
|
let closeVite = async () => { };
|
|
39
50
|
const server = createAppServer({
|
|
@@ -55,12 +66,27 @@ async function main(args) {
|
|
|
55
66
|
middleware = vite.middlewares;
|
|
56
67
|
closeVite = () => vite.close();
|
|
57
68
|
}
|
|
58
|
-
|
|
69
|
+
if (args.port !== undefined)
|
|
70
|
+
await server.listen(args.port);
|
|
71
|
+
else {
|
|
72
|
+
try {
|
|
73
|
+
await server.listen(DEFAULT_PORT);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err.code !== "EADDRINUSE")
|
|
77
|
+
throw err;
|
|
78
|
+
console.log(`port ${DEFAULT_PORT} is busy (another excaliagent?); using a random port`);
|
|
79
|
+
await server.listen(0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
59
82
|
const url = `http://127.0.0.1:${server.port}/?token=${token}`;
|
|
60
83
|
console.log(`excaliagent ${args.dev ? "(dev) " : ""}listening on http://127.0.0.1:${server.port}`);
|
|
61
84
|
console.log(`scene: ${scenes.path}`);
|
|
62
85
|
console.log(`herdr: ${panes.available() ? "connected" : args.herdr ? "not running (will retry)" : "disabled"}`);
|
|
86
|
+
if (args.herdr)
|
|
87
|
+
console.log(`raise window on focus: ${!desktop ? "off (--no-raise)" : desktop.kind === "none" ? "unsupported here" : desktop.kind}`);
|
|
63
88
|
console.log(`open: ${url}`);
|
|
89
|
+
await recordRunning(stateDir, server.port);
|
|
64
90
|
if (args.open)
|
|
65
91
|
openBrowser(url);
|
|
66
92
|
let stopping = false;
|
|
@@ -72,6 +98,7 @@ async function main(args) {
|
|
|
72
98
|
panes.close();
|
|
73
99
|
await closeVite();
|
|
74
100
|
await server.close();
|
|
101
|
+
await clearRunning(stateDir).catch(() => { });
|
|
75
102
|
process.exit(0);
|
|
76
103
|
};
|
|
77
104
|
process.on("SIGINT", stop);
|
|
@@ -2,102 +2,144 @@
|
|
|
2
2
|
* Raise the desktop window that hosts a process (e.g. the terminal running a
|
|
3
3
|
* herdr client), so focusing a pane also brings its terminal to the front.
|
|
4
4
|
*
|
|
5
|
-
* Walks each candidate pid's ancestors
|
|
6
|
-
* first ancestor that
|
|
7
|
-
*
|
|
5
|
+
* Walks each candidate pid's ancestors through a ProcessTable and acts on the
|
|
6
|
+
* first ancestor that is a window / app:
|
|
7
|
+
* - Linux + Hyprland: `hyprctl clients -j` → `hyprctl dispatch focuswindow address:<addr>`
|
|
8
|
+
* - Linux + Sway: `swaymsg -t get_tree` → `swaymsg [pid=N] focus`
|
|
9
|
+
* - macOS: first ancestor whose executable lives in an `.app` bundle →
|
|
10
|
+
* `open -a <bundle>` (activates the app; no Accessibility permission).
|
|
11
|
+
* Unverified on real hardware.
|
|
12
|
+
* - anything else: no-op.
|
|
13
|
+
* Never throws; raise() resolves false when nothing was raised.
|
|
8
14
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (err)
|
|
15
|
-
reject(err);
|
|
16
|
-
else
|
|
17
|
-
resolve({ stdout });
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
/** ppid from /proc/<pid>/stat; comm (field 2) may contain spaces and parens. */
|
|
21
|
-
export async function parentPid(procRoot, pid) {
|
|
22
|
-
try {
|
|
23
|
-
const stat = await readFile(join(procRoot, String(pid), "stat"), "utf8");
|
|
24
|
-
const rest = stat.slice(stat.lastIndexOf(")") + 1).trim().split(/\s+/);
|
|
25
|
-
const ppid = Number(rest[1]);
|
|
26
|
-
return Number.isInteger(ppid) && ppid > 0 ? ppid : undefined;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/** pid, then its parents up to (excluding) init. */
|
|
33
|
-
async function ancestry(procRoot, pid) {
|
|
15
|
+
import { defaultExec, defaultLog, describeExecFailure, logOnce } from "./exec.js";
|
|
16
|
+
import { createProcessTable } from "./process-table.js";
|
|
17
|
+
export { defaultExec };
|
|
18
|
+
/** pid, then its parents up to (excluding) init/launchd. */
|
|
19
|
+
async function ancestry(table, pid) {
|
|
34
20
|
const chain = [];
|
|
35
21
|
const seen = new Set();
|
|
36
22
|
let cur = pid;
|
|
37
23
|
while (cur !== undefined && cur > 1 && !seen.has(cur) && chain.length < 64) {
|
|
38
24
|
seen.add(cur);
|
|
39
25
|
chain.push(cur);
|
|
40
|
-
cur = await
|
|
26
|
+
cur = await table.parent(cur);
|
|
41
27
|
}
|
|
42
28
|
return chain;
|
|
43
29
|
}
|
|
44
|
-
function
|
|
30
|
+
function windowManager(listWindows, focus) {
|
|
45
31
|
return {
|
|
46
|
-
async
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
for (const c of JSON.parse(stdout)) {
|
|
50
|
-
if (typeof c.pid === "number" && typeof c.address === "string" && !out.has(c.pid))
|
|
51
|
-
out.set(c.pid, c.address);
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
},
|
|
55
|
-
async focus(address) {
|
|
56
|
-
await exec("hyprctl", ["dispatch", "focuswindow", `address:${address}`], { timeout });
|
|
32
|
+
async prepare() {
|
|
33
|
+
const windows = await listWindows();
|
|
34
|
+
return async (pid) => windows.get(pid);
|
|
57
35
|
},
|
|
36
|
+
focus,
|
|
58
37
|
};
|
|
59
38
|
}
|
|
39
|
+
function hyprland(exec, timeout) {
|
|
40
|
+
return windowManager(async () => {
|
|
41
|
+
const { stdout } = await exec("hyprctl", ["clients", "-j"], { timeout });
|
|
42
|
+
const out = new Map();
|
|
43
|
+
for (const c of JSON.parse(stdout)) {
|
|
44
|
+
if (typeof c.pid === "number" && typeof c.address === "string" && !out.has(c.pid))
|
|
45
|
+
out.set(c.pid, c.address);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}, async (address) => {
|
|
49
|
+
await exec("hyprctl", ["dispatch", "focuswindow", `address:${address}`], { timeout });
|
|
50
|
+
});
|
|
51
|
+
}
|
|
60
52
|
function sway(exec, timeout) {
|
|
53
|
+
return windowManager(async () => {
|
|
54
|
+
const { stdout } = await exec("swaymsg", ["-t", "get_tree"], { timeout });
|
|
55
|
+
const out = new Map();
|
|
56
|
+
const walk = (n) => {
|
|
57
|
+
if (typeof n.pid === "number")
|
|
58
|
+
out.set(n.pid, String(n.pid));
|
|
59
|
+
for (const c of [...(n.nodes ?? []), ...(n.floating_nodes ?? [])])
|
|
60
|
+
walk(c);
|
|
61
|
+
};
|
|
62
|
+
walk(JSON.parse(stdout));
|
|
63
|
+
return out;
|
|
64
|
+
}, async (pid) => {
|
|
65
|
+
await exec("swaymsg", [`[pid=${pid}]`, "focus"], { timeout });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Outermost `.app` bundle containing an executable path, e.g. /Applications/kitty.app. */
|
|
69
|
+
export function appBundleOf(exe) {
|
|
70
|
+
const m = /^(\/.*?\.app)\//.exec(exe);
|
|
71
|
+
return m?.[1];
|
|
72
|
+
}
|
|
73
|
+
function macos(exec, timeout, table, log) {
|
|
74
|
+
const once = logOnce(log);
|
|
61
75
|
return {
|
|
62
|
-
async
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (typeof n.pid === "number")
|
|
67
|
-
out.set(n.pid, String(n.pid));
|
|
68
|
-
for (const c of [...(n.nodes ?? []), ...(n.floating_nodes ?? [])])
|
|
69
|
-
walk(c);
|
|
76
|
+
async prepare() {
|
|
77
|
+
return async (pid) => {
|
|
78
|
+
const exe = await table.exe(pid);
|
|
79
|
+
return exe ? appBundleOf(exe) : undefined;
|
|
70
80
|
};
|
|
71
|
-
walk(JSON.parse(stdout));
|
|
72
|
-
return out;
|
|
73
81
|
},
|
|
74
|
-
async focus(
|
|
75
|
-
|
|
82
|
+
async focus(bundle) {
|
|
83
|
+
const args = ["-a", bundle];
|
|
84
|
+
try {
|
|
85
|
+
await exec("open", args, { timeout });
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
once(`open:${bundle}`, `raise: ${describeExecFailure("open", args, e)}`);
|
|
89
|
+
throw e;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
async notFound(chains) {
|
|
93
|
+
const shown = await Promise.all(chains.map(async (chain) => (await Promise.all(chain.map(async (p) => `${p} ${(await table.exe(p)) ?? "?"}`))).join(" <- ")));
|
|
94
|
+
once("no-app", `raise: no .app bundle among the ancestors of herdr client(s): ${shown.join("; ")}`);
|
|
95
|
+
},
|
|
96
|
+
noPids() {
|
|
97
|
+
once("no-pids", "raise: no herdr client process found (is herdr running in a terminal on this Mac?)");
|
|
76
98
|
},
|
|
77
99
|
};
|
|
78
100
|
}
|
|
79
101
|
export function createDesktop(opts = {}) {
|
|
80
|
-
const {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
const { platform = process.platform, env = process.env, exec = defaultExec, timeoutMs = 2000 } = opts;
|
|
103
|
+
const table = opts.table ?? createProcessTable({ platform, procRoot: opts.procRoot, exec, timeoutMs, log: opts.log });
|
|
104
|
+
const log = opts.log ?? defaultLog;
|
|
105
|
+
const kind = platform === "darwin"
|
|
106
|
+
? "macos"
|
|
107
|
+
: platform === "linux" && env.HYPRLAND_INSTANCE_SIGNATURE
|
|
108
|
+
? "hyprland"
|
|
109
|
+
: platform === "linux" && env.SWAYSOCK
|
|
110
|
+
? "sway"
|
|
111
|
+
: "none";
|
|
112
|
+
const raiser = kind === "macos"
|
|
113
|
+
? macos(exec, timeoutMs, table, log)
|
|
114
|
+
: kind === "hyprland"
|
|
115
|
+
? hyprland(exec, timeoutMs)
|
|
116
|
+
: kind === "sway"
|
|
117
|
+
? sway(exec, timeoutMs)
|
|
118
|
+
: undefined;
|
|
86
119
|
return {
|
|
120
|
+
kind,
|
|
87
121
|
async raise(pids) {
|
|
88
|
-
if (!
|
|
122
|
+
if (!raiser)
|
|
89
123
|
return false;
|
|
124
|
+
if (pids.length === 0) {
|
|
125
|
+
raiser.noPids?.();
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
90
128
|
try {
|
|
91
|
-
const
|
|
129
|
+
const targetOf = await raiser.prepare();
|
|
130
|
+
const chains = [];
|
|
92
131
|
for (const pid of pids) {
|
|
93
|
-
|
|
94
|
-
|
|
132
|
+
const chain = await ancestry(table, pid);
|
|
133
|
+
chains.push(chain);
|
|
134
|
+
for (const p of chain) {
|
|
135
|
+
const target = await targetOf(p);
|
|
95
136
|
if (target !== undefined) {
|
|
96
|
-
await
|
|
137
|
+
await raiser.focus(target);
|
|
97
138
|
return true;
|
|
98
139
|
}
|
|
99
140
|
}
|
|
100
141
|
}
|
|
142
|
+
await raiser.notFound?.(chains);
|
|
101
143
|
}
|
|
102
144
|
catch {
|
|
103
145
|
// no compositor, timeout, bad output: just don't raise
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
export const defaultExec = (file, args, opts) => new Promise((resolve, reject) => {
|
|
3
|
+
execFile(file, args, { timeout: opts.timeout, maxBuffer: 16 << 20, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
4
|
+
if (err)
|
|
5
|
+
reject(Object.assign(err, { stderr }));
|
|
6
|
+
else
|
|
7
|
+
resolve({ stdout });
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
/** "`cmd args` failed: <stderr, else message>" for logs. */
|
|
11
|
+
export function describeExecFailure(file, args, err) {
|
|
12
|
+
const e = err;
|
|
13
|
+
const stderr = typeof e?.stderr === "string" ? e.stderr.trim() : "";
|
|
14
|
+
const detail = stderr || (typeof e?.message === "string" ? e.message : String(err));
|
|
15
|
+
return `\`${[file, ...args].join(" ")}\` failed: ${detail}`;
|
|
16
|
+
}
|
|
17
|
+
export const defaultLog = (m) => console.warn(`excaliagent: ${m}`);
|
|
18
|
+
/** Wraps a logger so each key is reported at most once. */
|
|
19
|
+
export function logOnce(log) {
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
return (key, message) => {
|
|
22
|
+
if (seen.has(key))
|
|
23
|
+
return;
|
|
24
|
+
seen.add(key);
|
|
25
|
+
log(message);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal process information, portable across Linux and macOS.
|
|
3
|
+
*
|
|
4
|
+
* - linux: /proc (stat, comm, cmdline, environ, exe). No subprocesses.
|
|
5
|
+
* - darwin: `ps -axww -o pid=,ppid=,comm=` (comm is the full executable path
|
|
6
|
+
* on macOS), `ps -ww -o command= -p N` for argv and `ps -E -ww -o command= -p N`
|
|
7
|
+
* for the environment (own-user processes only; -E = "display the environment"). argv/env are split on
|
|
8
|
+
* whitespace, so arguments or values containing spaces are approximate.
|
|
9
|
+
* Unverified on real hardware; CI runs the parsers against fixtures.
|
|
10
|
+
* - anything else: an empty table.
|
|
11
|
+
* Every method fails soft (empty / undefined).
|
|
12
|
+
*/
|
|
13
|
+
import { readdir, readFile, readlink } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { defaultExec, defaultLog, describeExecFailure, logOnce } from "./exec.js";
|
|
16
|
+
const read = (p) => readFile(p, "utf8").catch(() => undefined);
|
|
17
|
+
function parseStat(stat) {
|
|
18
|
+
const open = stat.indexOf("(");
|
|
19
|
+
const close = stat.lastIndexOf(")");
|
|
20
|
+
if (open < 0 || close < open)
|
|
21
|
+
return undefined;
|
|
22
|
+
const ppid = Number(stat.slice(close + 1).trim().split(/\s+/)[1]);
|
|
23
|
+
return Number.isInteger(ppid) ? { comm: stat.slice(open + 1, close), ppid } : undefined;
|
|
24
|
+
}
|
|
25
|
+
function linuxTable(procRoot) {
|
|
26
|
+
const stat = async (pid) => {
|
|
27
|
+
const s = await read(join(procRoot, String(pid), "stat"));
|
|
28
|
+
return s === undefined ? undefined : parseStat(s);
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
async list() {
|
|
32
|
+
let entries;
|
|
33
|
+
try {
|
|
34
|
+
entries = await readdir(procRoot);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const out = await Promise.all(entries
|
|
40
|
+
.filter((e) => /^\d+$/.test(e))
|
|
41
|
+
.map(async (e) => {
|
|
42
|
+
const s = await stat(Number(e));
|
|
43
|
+
return s ? { pid: Number(e), ppid: s.ppid, comm: s.comm } : undefined;
|
|
44
|
+
}));
|
|
45
|
+
return out.filter((p) => p !== undefined);
|
|
46
|
+
},
|
|
47
|
+
async parent(pid) {
|
|
48
|
+
const ppid = (await stat(pid))?.ppid;
|
|
49
|
+
return ppid !== undefined && ppid > 0 ? ppid : undefined;
|
|
50
|
+
},
|
|
51
|
+
async argv(pid) {
|
|
52
|
+
return (await read(join(procRoot, String(pid), "cmdline")))?.split("\0").filter(Boolean);
|
|
53
|
+
},
|
|
54
|
+
async env(pid) {
|
|
55
|
+
const environ = await read(join(procRoot, String(pid), "environ"));
|
|
56
|
+
if (environ === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
const env = {};
|
|
59
|
+
for (const kv of environ.split("\0")) {
|
|
60
|
+
const i = kv.indexOf("=");
|
|
61
|
+
if (i > 0)
|
|
62
|
+
env[kv.slice(0, i)] = kv.slice(i + 1);
|
|
63
|
+
}
|
|
64
|
+
return env;
|
|
65
|
+
},
|
|
66
|
+
async exe(pid) {
|
|
67
|
+
return readlink(join(procRoot, String(pid), "exe")).catch(() => undefined);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Lines of `ps -o pid=,ppid=,comm=`; comm runs to end of line (may contain spaces). */
|
|
72
|
+
export function parsePsList(out) {
|
|
73
|
+
const list = [];
|
|
74
|
+
for (const line of out.split("\n")) {
|
|
75
|
+
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
76
|
+
if (m)
|
|
77
|
+
list.push({ pid: Number(m[1]), ppid: Number(m[2]), comm: m[3].trim() });
|
|
78
|
+
}
|
|
79
|
+
return list;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Environment from `ps -E -ww -o command=`: the command line followed by
|
|
83
|
+
* KEY=VALUE pairs. The known argv prefix is skipped; a token that is not
|
|
84
|
+
* KEY=VALUE continues the previous value (values with spaces).
|
|
85
|
+
*/
|
|
86
|
+
export function parsePsEnv(out, argv = []) {
|
|
87
|
+
let rest = out.trim();
|
|
88
|
+
const cmd = argv.join(" ");
|
|
89
|
+
if (cmd && rest.startsWith(cmd))
|
|
90
|
+
rest = rest.slice(cmd.length);
|
|
91
|
+
else if (argv.length > 0)
|
|
92
|
+
return {};
|
|
93
|
+
const env = {};
|
|
94
|
+
let last;
|
|
95
|
+
for (const tok of rest.split(/\s+/).filter(Boolean)) {
|
|
96
|
+
const m = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(tok);
|
|
97
|
+
if (m) {
|
|
98
|
+
last = m[1];
|
|
99
|
+
env[last] = m[2];
|
|
100
|
+
}
|
|
101
|
+
else if (last !== undefined) {
|
|
102
|
+
env[last] += " " + tok;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return env;
|
|
106
|
+
}
|
|
107
|
+
function darwinTable(exec, timeout, cacheMs, log) {
|
|
108
|
+
const once = logOnce(log);
|
|
109
|
+
const ps = async (args, key) => {
|
|
110
|
+
try {
|
|
111
|
+
return (await exec("ps", args, { timeout })).stdout;
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
once(key, `process table: ${describeExecFailure("ps", args, e)}`);
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
let cached;
|
|
119
|
+
const list = () => {
|
|
120
|
+
if (!cached || Date.now() - cached.at > cacheMs) {
|
|
121
|
+
const listing = ps(["-axww", "-o", "pid=,ppid=,comm="], "list").then((out) => (out === undefined ? [] : parsePsList(out)));
|
|
122
|
+
cached = { at: Date.now(), list: listing };
|
|
123
|
+
}
|
|
124
|
+
return cached.list;
|
|
125
|
+
};
|
|
126
|
+
// A process that exited meanwhile also makes ps fail: not worth a log line each.
|
|
127
|
+
const argv = async (pid) => {
|
|
128
|
+
const out = await ps(["-ww", "-o", "command=", "-p", String(pid)], "argv");
|
|
129
|
+
const parts = out?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
130
|
+
return parts.length > 0 ? parts : undefined;
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
list,
|
|
134
|
+
async parent(pid) {
|
|
135
|
+
const ppid = (await list()).find((p) => p.pid === pid)?.ppid;
|
|
136
|
+
return ppid !== undefined && ppid > 0 ? ppid : undefined;
|
|
137
|
+
},
|
|
138
|
+
argv,
|
|
139
|
+
async env(pid) {
|
|
140
|
+
const args = await argv(pid);
|
|
141
|
+
if (!args)
|
|
142
|
+
return undefined;
|
|
143
|
+
const out = await ps(["-E", "-ww", "-o", "command=", "-p", String(pid)], "env");
|
|
144
|
+
return out === undefined ? undefined : parsePsEnv(out, args);
|
|
145
|
+
},
|
|
146
|
+
async exe(pid) {
|
|
147
|
+
const comm = (await list()).find((p) => p.pid === pid)?.comm;
|
|
148
|
+
return comm?.startsWith("/") ? comm : undefined;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const emptyTable = {
|
|
153
|
+
list: async () => [],
|
|
154
|
+
parent: async () => undefined,
|
|
155
|
+
argv: async () => undefined,
|
|
156
|
+
env: async () => undefined,
|
|
157
|
+
exe: async () => undefined,
|
|
158
|
+
};
|
|
159
|
+
export function createProcessTable(opts = {}) {
|
|
160
|
+
const { platform = process.platform, procRoot = "/proc", exec = defaultExec, timeoutMs = 2000, cacheMs = 1000 } = opts;
|
|
161
|
+
const log = opts.log ?? defaultLog;
|
|
162
|
+
if (platform === "linux")
|
|
163
|
+
return linuxTable(procRoot);
|
|
164
|
+
if (platform === "darwin")
|
|
165
|
+
return darwinTable(exec, timeoutMs, cacheMs, log);
|
|
166
|
+
return emptyTable;
|
|
167
|
+
}
|