@astralyn/sash 0.1.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/CHANGELOG.md +184 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/THIRD_PARTY_NOTICES.md +54 -0
- package/dist/api.js +103 -0
- package/dist/browser.js +25 -0
- package/dist/cli-errors.js +15 -0
- package/dist/cli.js +122 -0
- package/dist/commands/lifecycle.js +50 -0
- package/dist/commands/logs.js +54 -0
- package/dist/commands/shared.js +8 -0
- package/dist/commands/status.js +39 -0
- package/dist/commands/update.js +5 -0
- package/dist/commands/upgrade.js +43 -0
- package/dist/commands/web.js +33 -0
- package/dist/contracts.js +412 -0
- package/dist/core-config-validation.js +72 -0
- package/dist/core-install-record.js +55 -0
- package/dist/core-install-transaction.js +114 -0
- package/dist/core-update-coordination.js +105 -0
- package/dist/core-update-service.js +245 -0
- package/dist/core-update.js +804 -0
- package/dist/core-version.js +18 -0
- package/dist/core.js +206 -0
- package/dist/daemon/app.js +157 -0
- package/dist/daemon/context.js +40 -0
- package/dist/daemon/entry.js +67 -0
- package/dist/daemon/errors.js +47 -0
- package/dist/daemon/handlers/core.js +17 -0
- package/dist/daemon/handlers/daemon.js +66 -0
- package/dist/daemon/handlers/profiles.js +57 -0
- package/dist/daemon/handlers/proxy.js +12 -0
- package/dist/daemon/handlers/settings.js +48 -0
- package/dist/daemon/router.js +312 -0
- package/dist/daemon/scheduler.js +33 -0
- package/dist/daemon/server.js +121 -0
- package/dist/daemon-auth.js +71 -0
- package/dist/daemon-client.js +55 -0
- package/dist/daemon-entry.js +13 -0
- package/dist/daemon-http.js +93 -0
- package/dist/daemon-lifecycle.js +378 -0
- package/dist/daemon-proxy.js +176 -0
- package/dist/daemon-static.js +106 -0
- package/dist/daemon.js +3 -0
- package/dist/error-utils.js +14 -0
- package/dist/fs-atomic.js +123 -0
- package/dist/github.js +187 -0
- package/dist/http.js +282 -0
- package/dist/json-shape.js +17 -0
- package/dist/log-follow.js +280 -0
- package/dist/log.js +17 -0
- package/dist/managed-state-transaction.js +377 -0
- package/dist/mihomo-config.js +334 -0
- package/dist/node-version-guard.js +8 -0
- package/dist/offline-mutation.js +58 -0
- package/dist/paths.js +57 -0
- package/dist/process.js +572 -0
- package/dist/profile-migration.js +101 -0
- package/dist/profile-service.js +667 -0
- package/dist/profiles.js +182 -0
- package/dist/runtime-lifecycle.js +205 -0
- package/dist/runtime-owner.js +87 -0
- package/dist/runtime-recovery.js +31 -0
- package/dist/sash-client.js +163 -0
- package/dist/settings-service.js +242 -0
- package/dist/settings.js +314 -0
- package/dist/state-lock.js +331 -0
- package/dist/status.js +279 -0
- package/dist/supervisor.js +301 -0
- package/dist/sysproxy/common.js +99 -0
- package/dist/sysproxy/darwin.js +287 -0
- package/dist/sysproxy/factory.js +71 -0
- package/dist/sysproxy/gnome.js +181 -0
- package/dist/sysproxy/legacy.js +58 -0
- package/dist/sysproxy/snapshot.js +284 -0
- package/dist/sysproxy/types.js +24 -0
- package/dist/sysproxy/windows.js +216 -0
- package/dist/sysproxy.js +8 -0
- package/dist/system-proxy-manager.js +460 -0
- package/dist/tun-guidance.js +11 -0
- package/dist/ui/assets/05129d5ad91a6181d272cc3d031363d6-DJkDXMrK.woff2 +0 -0
- package/dist/ui/assets/0526ebf94418638dfd5b6cd9b06c07d4-BzWe4Lji.woff2 +0 -0
- package/dist/ui/assets/053f2647a78f659f6f8a3ae8436bd265-BocXYDl5.woff2 +0 -0
- package/dist/ui/assets/078f666863424d3fd2d98e3ea528704c-DQ_QCvOt.woff2 +0 -0
- package/dist/ui/assets/07f3ff379663ffa0222c12f241421dd9-Cma7oDlr.woff2 +0 -0
- package/dist/ui/assets/08427b8d865853bbd7c439321b26495f-CJmC8fWx.woff2 +0 -0
- package/dist/ui/assets/095224e4db29d40c54098da02b266b0a-DjsP0nQn.woff2 +0 -0
- package/dist/ui/assets/0ada18ca45940b08accde5c61ee871e3-E1BZD3RX.woff2 +0 -0
- package/dist/ui/assets/0be242294f7d791af850c6df38ac78a0-2cL6Ntwf.woff2 +0 -0
- package/dist/ui/assets/0bf18c938d0c27cce06c39f855405fdb-CruLZZZJ.woff2 +0 -0
- package/dist/ui/assets/0cfb3e9f4edf60839c37dc83e6f75ea3-DzAtRHvi.woff2 +0 -0
- package/dist/ui/assets/0fbbd340d0467dfe3101d58846f30cb0-gtFcO250.woff2 +0 -0
- package/dist/ui/assets/1063fa2dc7334d1f383e30824e016eae-B7U-kD-0.woff2 +0 -0
- package/dist/ui/assets/109e26e62ddc85d946e699464afbece7-BLnUjAEV.woff2 +0 -0
- package/dist/ui/assets/12a443b0ae9748a993b5395f8fad412b-Btjff2Lz.woff2 +0 -0
- package/dist/ui/assets/135c4621de7a4d85d852e03d416f13ee-DdSuW36e.woff2 +0 -0
- package/dist/ui/assets/13aa415469ee761c968749b1c0c56ec6-Dwe-Wa_w.woff2 +0 -0
- package/dist/ui/assets/1435b1ad447bd9c0e8bc52035c9dcfa8-C05ldXtw.woff2 +0 -0
- package/dist/ui/assets/175e0915aa874e7446b79b6bc9b00721-Dp0-l77L.woff2 +0 -0
- package/dist/ui/assets/1876e7d02187f006e2b7f769391c3089-CNjpRdap.woff2 +0 -0
- package/dist/ui/assets/1886dc284b3b845e3b5fe04aa2107276-Bh_wHwL1.woff2 +0 -0
- package/dist/ui/assets/19a3dda6611aaf5d787403bd1159fe15-zoEBvdpA.woff2 +0 -0
- package/dist/ui/assets/1afa74e14da193125e45d4783d358a4f-BgjSTENX.woff2 +0 -0
- package/dist/ui/assets/1cd26a6a6d784720b4a43053e9058bcf-TAP3dvNi.woff2 +0 -0
- package/dist/ui/assets/1ce19bddc3beab385a863b1e05685678-BCdXitZw.woff2 +0 -0
- package/dist/ui/assets/1d3459ac6578c80502bdb876a4824a93-CSwdnEGP.woff2 +0 -0
- package/dist/ui/assets/1d479e364dab4af01c539f0a6a7ac036-ZFI-djGt.woff2 +0 -0
- package/dist/ui/assets/1dcea2408d17fcec4197adce50f8b32d-CnK2xXR-.woff2 +0 -0
- package/dist/ui/assets/1e855385699999e24a33f7df8aed0489-DGFHkDVQ.woff2 +0 -0
- package/dist/ui/assets/1f55944b8f7972580e3029694462a6b0-CB7ogVJ9.woff2 +0 -0
- package/dist/ui/assets/1f96eda70929089762dc3c4feb33f9c8-DtSQ6YOf.woff2 +0 -0
- package/dist/ui/assets/1fb275532caadc746455881b31da27c4-BHFTsg74.woff2 +0 -0
- package/dist/ui/assets/20e679bdc00d06f802ea71fd9154b284-D9gRjq_v.woff2 +0 -0
- package/dist/ui/assets/25aff66c4a9f8cb801cbd525f4a923e4-Dgi87hP7.woff2 +0 -0
- package/dist/ui/assets/28e6700380b023b23e77f2d75aee624f-b19D855Q.woff2 +0 -0
- package/dist/ui/assets/2a1c50aa9cd99ce3a0197f5d49948e63-DrztdXCB.woff2 +0 -0
- package/dist/ui/assets/2b4e0ae0bf3af610b97717dfd66a012d-CBoapvha.woff2 +0 -0
- package/dist/ui/assets/2be2b7a672b616bc2f07059a67bfaad8-BUR0aNML.woff2 +0 -0
- package/dist/ui/assets/2e0a358ada78ea98ec8ba86002a27eac-DvgbSr9v.woff2 +0 -0
- package/dist/ui/assets/2e1cf63a2a22a50516c49b1dc521ea8a-CmltIxig.woff2 +0 -0
- package/dist/ui/assets/2e4481801925fcb6573c4337c73f15d7-DYOoCk-m.woff2 +0 -0
- package/dist/ui/assets/2fe5e634c568cd874f94d24b8f02bfd1-DcEHY35G.woff2 +0 -0
- package/dist/ui/assets/309c33f63f29887fb557115aa24d4bb5-CUgqrXIj.woff2 +0 -0
- package/dist/ui/assets/3158f45c959e1b346b0bbd6f3193dbf0-kimcClqp.woff2 +0 -0
- package/dist/ui/assets/316adbc8037a36f8d804637b053bbe5e-CjbZmJf9.woff2 +0 -0
- package/dist/ui/assets/319e9631bc29fc7235b70fc9aac43cd9-IV6yZuHZ.woff2 +0 -0
- package/dist/ui/assets/32054f9bb88d9431b1cdf023f770e614-CYrpCgqK.woff2 +0 -0
- package/dist/ui/assets/3245fa1aac6338c1d9d04befb7c346f8-CzvPhVPA.woff2 +0 -0
- package/dist/ui/assets/326c730f98afa476057957f2743c81e2-GPHJ3Fpu.woff2 +0 -0
- package/dist/ui/assets/33885558dc783bf9bae6c5d4668fef07-D33us0yR.woff2 +0 -0
- package/dist/ui/assets/33cd11f5e61556e4618801c8231f2744-CoR_Tv3X.woff2 +0 -0
- package/dist/ui/assets/34a170749cb7e6c5851acc510882af6b-BCZwsihk.woff2 +0 -0
- package/dist/ui/assets/3538de2d387591258d7e9908aad803b9-CewOkERZ.woff2 +0 -0
- package/dist/ui/assets/38d7b029c036080067a2e94211098171-CettR5Qv.woff2 +0 -0
- package/dist/ui/assets/393f47efeefc9f9e9cca83eb10a82c69-Brhz4z1y.woff2 +0 -0
- package/dist/ui/assets/396a51f2b274d01f6ba606a94f3e5fe2-DgRk-jtI.woff2 +0 -0
- package/dist/ui/assets/3991bf955d4c925e84b4a6542760d4d5-II4HtRkQ.woff2 +0 -0
- package/dist/ui/assets/39b6cb6ae8d1ea0e9832b88d2c21ef99-CbJm-q6b.woff2 +0 -0
- package/dist/ui/assets/3a1133157fbd259f1a51aaa9f8418a26-3FmUjjvi.woff2 +0 -0
- package/dist/ui/assets/3a34cff07dca7a65ed2486be19229290-B7ml2yBN.woff2 +0 -0
- package/dist/ui/assets/3c339dd7c27e6b8a06eea431733e076f-dGQqXaEj.woff2 +0 -0
- package/dist/ui/assets/3d074ea67f60953b816ce7cf5d87a980-B5VkPOmg.woff2 +0 -0
- package/dist/ui/assets/3d38f54dcf4a9f4a50165d5849becf71-CVDtFSZn.woff2 +0 -0
- package/dist/ui/assets/3e3fc9a19a27f94e4ff1b815b55b2671-Ci736GLi.woff2 +0 -0
- package/dist/ui/assets/3e8abb1f606f29e2f0a8a8a6b702d263-C5sOSf5L.woff2 +0 -0
- package/dist/ui/assets/3f0ab4e1cd99bdbfdf9acbc8f4bcb9ce-0DcdTLxV.woff2 +0 -0
- package/dist/ui/assets/3f30c0482650345c981d66920f56ab61-CTD2CsZI.woff2 +0 -0
- package/dist/ui/assets/41a9aa12ad9a60758b304c1a998fb781-BTQUDLf1.woff2 +0 -0
- package/dist/ui/assets/424010b2bc5cee540b92b28da49b61bf-DT8upW2M.woff2 +0 -0
- package/dist/ui/assets/42fbdafff2d44b76c6665d51bb2cee9d-_UrojhXD.woff2 +0 -0
- package/dist/ui/assets/4341db13c311b3cb0eaaf6b4c71b89e5-CzjA7MzG.woff2 +0 -0
- package/dist/ui/assets/434db5a796128131fde78d4389d14b71-AeNdWKua.woff2 +0 -0
- package/dist/ui/assets/43cb51fc70a15274d49e2d956c5ec3b4-BQbufd96.woff2 +0 -0
- package/dist/ui/assets/43e79de1c7815f21efbc966ff96fcbd6-DlCQvOsQ.woff2 +0 -0
- package/dist/ui/assets/43faf6a8a3cde1088bd5f7c830890a89-SvDzF3nA.woff2 +0 -0
- package/dist/ui/assets/44731ae21d7690ce538d0f8242755833-CEmu9Z88.woff2 +0 -0
- package/dist/ui/assets/45324b2a64cc0c0db797d5c09e239d34-BuFudtvM.woff2 +0 -0
- package/dist/ui/assets/4584df05d00aa75ae6e1f850fdc9df1d-GNHt6xwM.woff2 +0 -0
- package/dist/ui/assets/48d539539e98481fb511a761031c59fd-Cd9gdi8B.woff2 +0 -0
- package/dist/ui/assets/49a1fb9fc6a35796b2e0d3539088ede8-CD1cRDOy.woff2 +0 -0
- package/dist/ui/assets/4bec02eb0c2371e59bfb7d41a141ea1e-CmBnNPo0.woff2 +0 -0
- package/dist/ui/assets/4c5a81ef5683f6fbf9935e640de804e7-B4wQxzVs.woff2 +0 -0
- package/dist/ui/assets/4f11c9c75e86214b7f69ddee3a9f8076-DHsQ2xpE.woff2 +0 -0
- package/dist/ui/assets/50434b8ff88e27101d1c67ba953ee873-PH_hIqxu.woff2 +0 -0
- package/dist/ui/assets/5107fd5153d52c6a3f8d43ff214dd6c2-ls3eo5Qd.woff2 +0 -0
- package/dist/ui/assets/520cb7d3683dee99e767650efc0aa5b1-Lkd4z-9S.woff2 +0 -0
- package/dist/ui/assets/5455087df2f091b7bf3d38c5a102a2e7-QNkiOxwe.woff2 +0 -0
- package/dist/ui/assets/556450fdf0978d329cf8277e3b71dcdb-CtypGAPw.woff2 +0 -0
- package/dist/ui/assets/5a1b5c1b71cad50c51a79a7f4825e6e6-DQA407Ec.woff2 +0 -0
- package/dist/ui/assets/5ba02e809d2ef4f7639178e7a7d654c0-Dty0omtl.woff2 +0 -0
- package/dist/ui/assets/5bd654afe48e094f95e5142e4eaadc75-D9pUq71R.woff2 +0 -0
- package/dist/ui/assets/5c4c3ca0a3f4115879a61a96d02e3efc-RcVLBj31.woff2 +0 -0
- package/dist/ui/assets/5c737d740638e8d764f12c1e5c1cd144-D2Npjq-N.woff2 +0 -0
- package/dist/ui/assets/5e78019102102aa5741aa591313fd5b5-V7Azrw-2.woff2 +0 -0
- package/dist/ui/assets/6287cacac64e1849941f7b7d7e6316c5-DzkbPOID.woff2 +0 -0
- package/dist/ui/assets/643df7194160d54edc48d16d76e5ba74-BKmmIRjw.woff2 +0 -0
- package/dist/ui/assets/65d3b214c69ff6786fd1cab06f8394b7-B0cRUJAr.woff2 +0 -0
- package/dist/ui/assets/6698103c11bfe260d44fcf00c6d8b623-DVWYOeyb.woff2 +0 -0
- package/dist/ui/assets/692e07b3a609a8f1a21c15c45631f348-DJW5LcZH.woff2 +0 -0
- package/dist/ui/assets/694e87456af181279c2bfd551aa3c6e3-Cu7kRuJB.woff2 +0 -0
- package/dist/ui/assets/6b88066bdc7a6e8bc823128dbd0f6f3b-BG38UZSq.woff2 +0 -0
- package/dist/ui/assets/6d6f36f15381826d113832bfdd41b021-Bgh7YPiq.woff2 +0 -0
- package/dist/ui/assets/6e22361162bd0a5cd189ca3c98d23d52-DqnptHqF.woff2 +0 -0
- package/dist/ui/assets/6e884dd501b8da24ecd37425a9b3d51d-BVNqra8s.woff2 +0 -0
- package/dist/ui/assets/71200dbc7497fbafa28db5cfeae74acb-B6xMqoC6.woff2 +0 -0
- package/dist/ui/assets/72a85441a9757146440e3985f3b02075-CzTSlCPZ.woff2 +0 -0
- package/dist/ui/assets/7472d7e02045dea3ff288eac850e38eb-CboJEy4X.woff2 +0 -0
- package/dist/ui/assets/795ffa953bea666763ae79ac88aa7e5a-C6UsoDmi.woff2 +0 -0
- package/dist/ui/assets/7aa8647d6c52d249169b73417aa8e444-kJejhQ0-.woff2 +0 -0
- package/dist/ui/assets/7be6b794fc233ccd8b5769496301313a-Ba2iZC0a.woff2 +0 -0
- package/dist/ui/assets/7c0748f04d52086809c9f551c5aaded7-DSzO_ybF.woff2 +0 -0
- package/dist/ui/assets/7c858bb53a7652227fab8cf9753d3e1f-Bi9fbvIF.woff2 +0 -0
- package/dist/ui/assets/7e7026091b4e6061b05963bdb3a7f944-CrmRDJpc.woff2 +0 -0
- package/dist/ui/assets/7f80369a7479d568ab8bf492000282bd-CqcI5ZOp.woff2 +0 -0
- package/dist/ui/assets/834ce2f2571aaa26e47a618c4ad55e58-DZfWJrxo.woff2 +0 -0
- package/dist/ui/assets/834faab602d9c795341bc7dfd6578fe5-B1VeBVWh.woff2 +0 -0
- package/dist/ui/assets/8513a8eea35b460a047a16a87c36f8bb-H259w_6b.woff2 +0 -0
- package/dist/ui/assets/8699e8a36465bf46671dc28ce24e188c-YlkPpQ6h.woff2 +0 -0
- package/dist/ui/assets/86ae8881eae9eac7e9c0182c8ce6c627-Ch2r3LNs.woff2 +0 -0
- package/dist/ui/assets/86b3ae1be58d76f7147793f09264451b-CpgTfcn-.woff2 +0 -0
- package/dist/ui/assets/8769f8d7d45adcfcbd3f80460d22720d-DV_yqcTo.woff2 +0 -0
- package/dist/ui/assets/87bb0ceea9f5515490c832bdf6ebf641-CJpxd8e6.woff2 +0 -0
- package/dist/ui/assets/8928b9a50b5efbaaafa039674a3c7301-C-42WRD6.woff2 +0 -0
- package/dist/ui/assets/899f2c2a36cbeac473a7caeeaabc46b1-xrIcXJXU.woff2 +0 -0
- package/dist/ui/assets/8a075d4043e6bf66740808b492186438-Dq0346Qm.woff2 +0 -0
- package/dist/ui/assets/8b86676936792154c53205ab5d90b797-BGH_CGsg.woff2 +0 -0
- package/dist/ui/assets/8c63ca479653200a0e12f428242e1171-CsBTcPwY.woff2 +0 -0
- package/dist/ui/assets/8c9c7ff8342f5fed60e73c4a7ad67325-CGSYpt52.woff2 +0 -0
- package/dist/ui/assets/8ca655fcc5d9cf33d49815e231f3f356-b8PyEeZg.woff2 +0 -0
- package/dist/ui/assets/8e96f0f9385ef826f900587d0fc18b3d-D7GIG6Uq.woff2 +0 -0
- package/dist/ui/assets/8ea917252cf2c1e664f06ee5badffeea-CDZ1AFr0.woff2 +0 -0
- package/dist/ui/assets/8f42f890fc37e292017446af85af2745-Qx6oGI0z.woff2 +0 -0
- package/dist/ui/assets/8f9589e71c53bb753749a9c412f15c01-BVR5mPJl.woff2 +0 -0
- package/dist/ui/assets/90e70b52a3cc2491bbb2b4c0caf0ae7e-BdwPu67j.woff2 +0 -0
- package/dist/ui/assets/92ac4ca42fe050c1e680c585551abd9c-CXLyuj7l.woff2 +0 -0
- package/dist/ui/assets/92c12bb794c491930fd06f3f97299807-u_0ognJ0.woff2 +0 -0
- package/dist/ui/assets/92f0b4d2bb274a801aac10fc34c1e0cc-DFHB6dyu.woff2 +0 -0
- package/dist/ui/assets/93247aebd628367f5518eafdb2770ce9-o1N4zvTO.woff2 +0 -0
- package/dist/ui/assets/940446ea160995af66a9ee223cc3d4d1-C-_7aPYO.woff2 +0 -0
- package/dist/ui/assets/94be430de618453b6ae27bd366827ba2-1YYf1n5N.woff2 +0 -0
- package/dist/ui/assets/98cd7d62095e466e55885b34f63c86f1-z33YCk0X.woff2 +0 -0
- package/dist/ui/assets/99049fd5cec5894cafe2a1e5e13d1e03-DNrg8nIk.woff2 +0 -0
- package/dist/ui/assets/99e2752b7351aaa7681b4d91db80e89a-b3hi7miY.woff2 +0 -0
- package/dist/ui/assets/9d1b670cbfd64716ac685dea26237d95-DAZ9U6es.woff2 +0 -0
- package/dist/ui/assets/CodeEditorModal-6m0TJWyF.css +1 -0
- package/dist/ui/assets/CodeEditorModal-CFEnWsyh.js +14 -0
- package/dist/ui/assets/ConnectionsView-DHo6yvU6.css +1 -0
- package/dist/ui/assets/ConnectionsView-DNGmZBSU.js +1 -0
- package/dist/ui/assets/LogsView-BoeE92VF.css +1 -0
- package/dist/ui/assets/LogsView-fSiaxQ13.js +1 -0
- package/dist/ui/assets/PaginationFooter-B3kHzRfB.js +1 -0
- package/dist/ui/assets/PaginationFooter-BNkpcNoB.css +1 -0
- package/dist/ui/assets/ProfileEditorDialog-BydoZthX.js +1 -0
- package/dist/ui/assets/ProfilesView-Btc1DOxE.js +2 -0
- package/dist/ui/assets/ProfilesView-ByqdFNHN.css +1 -0
- package/dist/ui/assets/RulesView-CHyynVqA.css +1 -0
- package/dist/ui/assets/RulesView-D9vZBiJ1.js +1 -0
- package/dist/ui/assets/SettingsFileDialog-CNFEAVs4.js +1 -0
- package/dist/ui/assets/SettingsView-BlDhZkXQ.js +2 -0
- package/dist/ui/assets/SettingsView-CngS3vBM.css +1 -0
- package/dist/ui/assets/a0f2d41645a113d821e7ff0891daad1e-XK0rDAci.woff2 +0 -0
- package/dist/ui/assets/a1d1345d8407dd6169f39c28ff6b1708-Bn7hMmv8.woff2 +0 -0
- package/dist/ui/assets/a29f51b4ebb6776e43e7492a036be28e-CrAKKz8u.woff2 +0 -0
- package/dist/ui/assets/a3252ac86b2c4e88bfb9baecabcad817-qnA_3LNE.woff2 +0 -0
- package/dist/ui/assets/a3d63a61e7eb2bb497dbc2da65bd0873-BRMJAm0m.woff2 +0 -0
- package/dist/ui/assets/a45eea933252dbe3529f30a0323c2661-D4kBOrXR.woff2 +0 -0
- package/dist/ui/assets/a4886c4ebed8c8b1f8a87b5b491a39aa-CBQdRqpF.woff2 +0 -0
- package/dist/ui/assets/a50b4ab745c7676d9d3ceb3c0a023a3d-CW990pgI.woff2 +0 -0
- package/dist/ui/assets/a5851ae4d7e58996d8bd681f80d4727c-BBytdb0t.woff2 +0 -0
- package/dist/ui/assets/a5cdaa96bf7d1db6c671432f9ab7bccb-slKJjgfX.woff2 +0 -0
- package/dist/ui/assets/a640dd2e962cf72c7fcf76234c973303-CUCWUDBY.woff2 +0 -0
- package/dist/ui/assets/a67e457bbd366aef5104ce743a2cf6d4-DkE8fLlE.woff2 +0 -0
- package/dist/ui/assets/a6c3f2257315d45aa212bd2e86a02852-CxDbXnZV.woff2 +0 -0
- package/dist/ui/assets/a73399a40a5f81fb7314b8b9a10cd221-DRAvJPKI.woff2 +0 -0
- package/dist/ui/assets/a73a64b0941803d9d807b9ec082df641-BbPpPyy5.woff2 +0 -0
- package/dist/ui/assets/a73d1a0a1dfd4f4234f7a0649ece4da4-BHeqktY9.woff2 +0 -0
- package/dist/ui/assets/a740044e77a400a450e71fcdea55523f-Co5hpBNK.woff2 +0 -0
- package/dist/ui/assets/a7669b29f404662c179c71b87e67c8d2-CVnuBgHy.woff2 +0 -0
- package/dist/ui/assets/a964a4def15811e91b05a91740a1cf37-CzcDfCRn.woff2 +0 -0
- package/dist/ui/assets/aac3f69132da164921fda75d2d90bfb7-B6rUZQ4_.woff2 +0 -0
- package/dist/ui/assets/aacd263c3bb4ba163a4d5079c159680a-DtrDLFlk.woff2 +0 -0
- package/dist/ui/assets/ac62ec881ded2cdb450e542b396e24d0-CRlVJvPa.woff2 +0 -0
- package/dist/ui/assets/aebcb9b7d4b174ab5a4b911206f24036-BuleSpgP.woff2 +0 -0
- package/dist/ui/assets/af70712dadc05c7b18d248996a5ab0a9-gCZSZGzb.woff2 +0 -0
- package/dist/ui/assets/afbd3a8a4cfaa4561b7848c28bc6c4f8-DgnqceEw.woff2 +0 -0
- package/dist/ui/assets/b02d3867e6e2ea921d2845b44c4a5a31-C9v4FrqD.woff2 +0 -0
- package/dist/ui/assets/b1b22825b930688403337d4fd7bbba62-vmMs2pJf.woff2 +0 -0
- package/dist/ui/assets/b3206c979bfb28d12d7ad6df58e74b9f-X1PqLaCR.woff2 +0 -0
- package/dist/ui/assets/b6280e42d4f11dcab70594f9d6774936-CtezImLG.woff2 +0 -0
- package/dist/ui/assets/b71b8cd1a70405c3dbc0e7a44055ca9a-Cd6cIOEr.woff2 +0 -0
- package/dist/ui/assets/b9198c4f220a4ca075f8daa4db529e50-Bgc7zlEt.woff2 +0 -0
- package/dist/ui/assets/baad304a58d7d42d0af29235c5e9490b-BQZYwARB.woff2 +0 -0
- package/dist/ui/assets/bb99e34f8784cc44f0eeadbe735dd238-UHFYI827.woff2 +0 -0
- package/dist/ui/assets/bc1cb0f523c8a2ff566a047aeddf90fd-BSlXQn97.woff2 +0 -0
- package/dist/ui/assets/bd3171e975d62612e50148bf38b2158f-CtZKzCzQ.woff2 +0 -0
- package/dist/ui/assets/be10640c403263dd5917ad65c4d453a6-B-rbuuja.woff2 +0 -0
- package/dist/ui/assets/be764e101e78b50b749aa1414d4810a7-yIPaQQih.woff2 +0 -0
- package/dist/ui/assets/bfa682a7f5f12945b41230c35eb859f6-CzagcI5f.woff2 +0 -0
- package/dist/ui/assets/bfa9753ed26f2e1f81ab7020167bd1a0-C6ZNQQ2p.woff2 +0 -0
- package/dist/ui/assets/branding/sash-cat.png +0 -0
- package/dist/ui/assets/c0e78c6ad709b19ce437677c91033489-BtwtlApg.woff2 +0 -0
- package/dist/ui/assets/c5a59409c610cc8c265d1691eb3f71eb-DmI_ky2X.woff2 +0 -0
- package/dist/ui/assets/c5ff66a1a57f2a29424e8de5d4ef3756-C9pOsaV1.woff2 +0 -0
- package/dist/ui/assets/c607bdee84a0e5b69721367ccc0c9c31-eoHbdXeg.woff2 +0 -0
- package/dist/ui/assets/c8095033941d18db730496eb4c863757-DoexTYks.woff2 +0 -0
- package/dist/ui/assets/c8295519e16f4cf3a350e9729f07653a-C8bAtIO_.woff2 +0 -0
- package/dist/ui/assets/c91f9c9f4db81afeba37c8e73afb0bfd-BGIl2iKW.woff2 +0 -0
- package/dist/ui/assets/c9ad9caa4afdd40766add91d321b0350-HfuiRq1k.woff2 +0 -0
- package/dist/ui/assets/cc4f271c90f9ea5157db9c0a4edc976b-MAjuDvHE.woff2 +0 -0
- package/dist/ui/assets/cdf7c3d96ba172214d7e1e9fe5e6ba8b-CUx4fWQE.woff2 +0 -0
- package/dist/ui/assets/cf70d5821ea7c3ca34653169ac763606-CE0T0Svc.woff2 +0 -0
- package/dist/ui/assets/d0d5efe92496b1ceea38e180b0ac97d3-7G71Z6v3.woff2 +0 -0
- package/dist/ui/assets/d1977142eceb878b06440375e64c4372-mr-7LWm-.woff2 +0 -0
- package/dist/ui/assets/d42feb13392b4dd9214c61b61a6e5ae8-DjnKADf_.woff2 +0 -0
- package/dist/ui/assets/d46a696ec6a077c6873fc39043c3c082-C62E-13k.woff2 +0 -0
- package/dist/ui/assets/d4c0bbf1605a8a9c36fff5a2ead81bb5-C2F6tzOj.woff2 +0 -0
- package/dist/ui/assets/d918d4e47697b7c752ea223e0d0d4c81-twovU2ty.woff2 +0 -0
- package/dist/ui/assets/d988c05da0b5624267dd825fedd133d1-Bgbc9SVH.woff2 +0 -0
- package/dist/ui/assets/da3a071df79531f4f033b7c8499651d0-0tgZJL_N.woff2 +0 -0
- package/dist/ui/assets/da8eba4acbf93048645043006de1c5f0-Ce0dmHn_.woff2 +0 -0
- package/dist/ui/assets/dc3f16892e20a85a8fcad2d3097e622b-8PRrFPJt.woff2 +0 -0
- package/dist/ui/assets/dd5f808ec691f911483a2eb60b5049bd-BhVupSbT.woff2 +0 -0
- package/dist/ui/assets/e2b04534cc697a76afd70e885119cb8c-DO303iCb.woff2 +0 -0
- package/dist/ui/assets/e307280eb2fb7809adee14ff0d247d32-DHMLXvSs.woff2 +0 -0
- package/dist/ui/assets/e3b484bec850c669094f713570870b3a-BI_9kRw8.woff2 +0 -0
- package/dist/ui/assets/e7b1d196949b7530cbda0ad956fbb2cc-i2m2Id0q.woff2 +0 -0
- package/dist/ui/assets/e7c2e5fb387880508811cadc2926e13a-BfROXX4U.woff2 +0 -0
- package/dist/ui/assets/e8a5fe283c8b9e173097bee0b5757ab6-BvfjDjYP.woff2 +0 -0
- package/dist/ui/assets/e8e3465d48435612e9c3509be3984188-BeUPU5-9.woff2 +0 -0
- package/dist/ui/assets/e93903f4604b007fc1b7134a74f5c534-B1M4h46T.woff2 +0 -0
- package/dist/ui/assets/eb0994906efd969102d522b6d8b3aff2-i9SHU5CJ.woff2 +0 -0
- package/dist/ui/assets/eb32eee23ceadd3fc4eb5763d3573f57-B3h9kgf3.woff2 +0 -0
- package/dist/ui/assets/ebadbc005027fb3e2b1c2d15be38c66b-qVQSBwBu.woff2 +0 -0
- package/dist/ui/assets/ecbbb464ff6934b5012cf33c80c7a907-CBEPqZfS.woff2 +0 -0
- package/dist/ui/assets/ed79ac25c014b6434f22fe1e2549bc26-mWhz7hkU.woff2 +0 -0
- package/dist/ui/assets/eed64f40443554cb9f65edaca176abb0-Cqw29XTm.woff2 +0 -0
- package/dist/ui/assets/efc749471476cce09e12e7038c757950-BhSWPf1Q.woff2 +0 -0
- package/dist/ui/assets/f2a7c2cb375662aa93d8f31116d39c20-D1zsMYCW.woff2 +0 -0
- package/dist/ui/assets/f5ba0fb4332ccceadc481e47134023d3-CAKMPiu5.woff2 +0 -0
- package/dist/ui/assets/f6326eb5031e18478aa4588598f96965-Dj3uu55n.woff2 +0 -0
- package/dist/ui/assets/f8678cbaab9caef95e1986731dcbd92f-CnIuR3EN.woff2 +0 -0
- package/dist/ui/assets/f8d9d55e2a811e9839c9af5f8f2d62ac-DIUQRBsH.woff2 +0 -0
- package/dist/ui/assets/f98b117e363eb51f4d7c7d1056da7688-O9MtqjIW.woff2 +0 -0
- package/dist/ui/assets/f9ab5d9163148ceb211e5b4f15a3d8e0-Dtqse-AT.woff2 +0 -0
- package/dist/ui/assets/fa69ce224ff0a6c0ff97b10a829779c2-pX45dCNY.woff2 +0 -0
- package/dist/ui/assets/fc38d1da3930d39dd8e286f708105765-DmbOHwTI.woff2 +0 -0
- package/dist/ui/assets/fe03c1dfaffc1cad3521fe55f1541d6b-BW8aEqCj.woff2 +0 -0
- package/dist/ui/assets/fe0b2421eb4d1f4f8949c602871a317c-BsBLV6jH.woff2 +0 -0
- package/dist/ui/assets/fe9c75d0cb3d808023412d26dfc9d388-eXjcgUPI.woff2 +0 -0
- package/dist/ui/assets/index-B61V60w_.js +19 -0
- package/dist/ui/assets/index-bkyxJG8J.css +1 -0
- package/dist/ui/assets/theme-BNq4FkXS.js +1 -0
- package/dist/ui/index.html +52 -0
- package/dist/webui.js +21 -0
- package/docs/backend.md +260 -0
- package/docs/frontend.md +125 -0
- package/docs/remix-icon-license.txt +186 -0
- package/docs/usage.md +227 -0
- package/package.json +82 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveUiDir } from "./webui.js";
|
|
4
|
+
const UI_SECURITY_HEADERS = {
|
|
5
|
+
"Content-Security-Policy": "frame-ancestors 'none'",
|
|
6
|
+
"X-Frame-Options": "DENY",
|
|
7
|
+
"X-Content-Type-Options": "nosniff",
|
|
8
|
+
"Referrer-Policy": "no-referrer",
|
|
9
|
+
};
|
|
10
|
+
const FINGERPRINTED_ASSET = /^assets\/.+-[A-Za-z0-9_-]{8}\.[^/\\]+$/;
|
|
11
|
+
const MIME_TYPES = {
|
|
12
|
+
".html": "text/html; charset=utf-8",
|
|
13
|
+
".js": "application/javascript; charset=utf-8",
|
|
14
|
+
".css": "text/css; charset=utf-8",
|
|
15
|
+
".json": "application/json; charset=utf-8",
|
|
16
|
+
".svg": "image/svg+xml",
|
|
17
|
+
".png": "image/png",
|
|
18
|
+
".jpg": "image/jpeg",
|
|
19
|
+
".jpeg": "image/jpeg",
|
|
20
|
+
".webp": "image/webp",
|
|
21
|
+
".ico": "image/x-icon",
|
|
22
|
+
".wasm": "application/wasm",
|
|
23
|
+
".woff": "font/woff",
|
|
24
|
+
".woff2": "font/woff2",
|
|
25
|
+
};
|
|
26
|
+
export function serveStaticUi(req, res, pathname, layout) {
|
|
27
|
+
if (pathname !== "/ui" && !pathname.startsWith("/ui/")) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const uiRoot = resolveUiDir(layout);
|
|
31
|
+
if (!uiRoot) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
let relative = pathname.startsWith("/ui/") ? pathname.slice("/ui/".length) : "";
|
|
35
|
+
if (!relative || relative === "ui")
|
|
36
|
+
relative = "index.html";
|
|
37
|
+
if (relative.split(/[\\/]/).includes("..")) {
|
|
38
|
+
res.writeHead(403, { ...UI_SECURITY_HEADERS, "Content-Type": "text/plain" });
|
|
39
|
+
res.end("Forbidden");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const candidate = path.join(uiRoot, relative);
|
|
43
|
+
const hasExt = Boolean(path.extname(relative));
|
|
44
|
+
const statFile = (file) => {
|
|
45
|
+
try {
|
|
46
|
+
const stats = fs.statSync(file);
|
|
47
|
+
return stats.isFile() ? stats : null;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
let targetFile = null;
|
|
54
|
+
let targetStats = statFile(candidate);
|
|
55
|
+
if (targetStats) {
|
|
56
|
+
targetFile = candidate;
|
|
57
|
+
}
|
|
58
|
+
else if (!hasExt) {
|
|
59
|
+
targetFile = path.join(uiRoot, "index.html");
|
|
60
|
+
targetStats = statFile(targetFile);
|
|
61
|
+
}
|
|
62
|
+
if (targetFile && targetStats) {
|
|
63
|
+
const ext = path.extname(targetFile);
|
|
64
|
+
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
65
|
+
const headers = {
|
|
66
|
+
...UI_SECURITY_HEADERS,
|
|
67
|
+
"Content-Type": mime,
|
|
68
|
+
"Content-Length": String(targetStats.size),
|
|
69
|
+
};
|
|
70
|
+
if (ext === ".html") {
|
|
71
|
+
headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
|
|
72
|
+
headers.Pragma = "no-cache";
|
|
73
|
+
headers.Expires = "0";
|
|
74
|
+
}
|
|
75
|
+
else if (FINGERPRINTED_ASSET.test(relative)) {
|
|
76
|
+
headers["Cache-Control"] = "public, max-age=31536000, immutable";
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
headers["Cache-Control"] = "public, max-age=3600";
|
|
80
|
+
}
|
|
81
|
+
if (req.method === "HEAD") {
|
|
82
|
+
res.writeHead(200, headers);
|
|
83
|
+
res.end();
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const stream = fs.createReadStream(targetFile);
|
|
87
|
+
stream.once("open", () => {
|
|
88
|
+
res.writeHead(200, headers);
|
|
89
|
+
stream.pipe(res);
|
|
90
|
+
});
|
|
91
|
+
stream.once("error", () => {
|
|
92
|
+
if (!res.headersSent) {
|
|
93
|
+
res.writeHead(500, {
|
|
94
|
+
...UI_SECURITY_HEADERS,
|
|
95
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
96
|
+
});
|
|
97
|
+
res.end("Failed to read dashboard asset");
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
res.destroy();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function errorMessage(error) {
|
|
2
|
+
return error instanceof Error ? error.message : String(error);
|
|
3
|
+
}
|
|
4
|
+
export function errnoCode(error) {
|
|
5
|
+
if (typeof error !== "object" || error === null)
|
|
6
|
+
return undefined;
|
|
7
|
+
try {
|
|
8
|
+
const code = error.code;
|
|
9
|
+
return typeof code === "string" ? code : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { errnoCode } from "./error-utils.js";
|
|
5
|
+
const WINDOWS_TRANSIENT_FILE_CODES = new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
6
|
+
const DEFAULT_RETRY_DELAYS = [0, 50, 100, 200, 400];
|
|
7
|
+
function shouldRetryFileError(error, platform) {
|
|
8
|
+
const code = error.code;
|
|
9
|
+
return platform === "win32" && Boolean(code && WINDOWS_TRANSIENT_FILE_CODES.has(code));
|
|
10
|
+
}
|
|
11
|
+
export function renameWithRetrySync(from, to, options = {}) {
|
|
12
|
+
const platform = options.platform ?? process.platform;
|
|
13
|
+
const delays = options.delays?.length ? options.delays : DEFAULT_RETRY_DELAYS;
|
|
14
|
+
const sleep = options.sleep ?? sleepSync;
|
|
15
|
+
const rename = options.rename ?? fs.renameSync;
|
|
16
|
+
let lastError;
|
|
17
|
+
for (const delay of delays) {
|
|
18
|
+
if (delay > 0)
|
|
19
|
+
sleep(delay);
|
|
20
|
+
try {
|
|
21
|
+
rename(from, to);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
lastError = err;
|
|
26
|
+
if (!shouldRetryFileError(err, platform))
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw lastError;
|
|
31
|
+
}
|
|
32
|
+
export function removeWithRetrySync(target, options = {}) {
|
|
33
|
+
const platform = options.platform ?? process.platform;
|
|
34
|
+
const delays = options.delays?.length ? options.delays : DEFAULT_RETRY_DELAYS;
|
|
35
|
+
const sleep = options.sleep ?? sleepSync;
|
|
36
|
+
const unlink = options.unlink ?? fs.unlinkSync;
|
|
37
|
+
let lastError;
|
|
38
|
+
for (const delay of delays) {
|
|
39
|
+
if (delay > 0)
|
|
40
|
+
sleep(delay);
|
|
41
|
+
try {
|
|
42
|
+
unlink(target);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err.code === "ENOENT")
|
|
47
|
+
return false;
|
|
48
|
+
lastError = err;
|
|
49
|
+
if (!shouldRetryFileError(err, platform))
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw lastError;
|
|
54
|
+
}
|
|
55
|
+
export function pathEntryExists(target) {
|
|
56
|
+
try {
|
|
57
|
+
fs.lstatSync(target);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (errnoCode(err) === "ENOENT")
|
|
62
|
+
return false;
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function atomicWriteFileSync(target, data, mode = 0o600) {
|
|
67
|
+
const dir = path.dirname(target);
|
|
68
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
69
|
+
const tmp = path.join(dir, `.${path.basename(target)}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`);
|
|
70
|
+
const fd = fs.openSync(tmp, "wx", mode);
|
|
71
|
+
try {
|
|
72
|
+
fs.writeFileSync(fd, data);
|
|
73
|
+
fs.fsyncSync(fd);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
try {
|
|
77
|
+
fs.closeSync(fd);
|
|
78
|
+
fs.rmSync(tmp, { force: true });
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
fs.closeSync(fd);
|
|
85
|
+
try {
|
|
86
|
+
renameWithRetrySync(tmp, target);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
try {
|
|
90
|
+
fs.rmSync(tmp, { force: true });
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
fsyncParentDirectory(dir);
|
|
97
|
+
}
|
|
98
|
+
export function durableRemoveFileSync(target) {
|
|
99
|
+
if (removeWithRetrySync(target))
|
|
100
|
+
fsyncParentDirectory(path.dirname(target));
|
|
101
|
+
}
|
|
102
|
+
export function durableRenameSync(from, to) {
|
|
103
|
+
renameWithRetrySync(from, to);
|
|
104
|
+
const sourceDir = path.dirname(from);
|
|
105
|
+
const destinationDir = path.dirname(to);
|
|
106
|
+
fsyncParentDirectory(destinationDir);
|
|
107
|
+
if (sourceDir !== destinationDir)
|
|
108
|
+
fsyncParentDirectory(sourceDir);
|
|
109
|
+
}
|
|
110
|
+
function fsyncParentDirectory(dir) {
|
|
111
|
+
if (process.platform === "win32")
|
|
112
|
+
return;
|
|
113
|
+
const fd = fs.openSync(dir, "r");
|
|
114
|
+
try {
|
|
115
|
+
fs.fsyncSync(fd);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
fs.closeSync(fd);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function sleepSync(ms) {
|
|
122
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
123
|
+
}
|
package/dist/github.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { downloadToFile, ERROR_BODY_LIMIT, fetchWithRetry, USER_AGENT } from "./http.js";
|
|
4
|
+
export const MIHOMO_REPO = "MetaCubeX/mihomo";
|
|
5
|
+
export const GITHUB_MIRRORS = [
|
|
6
|
+
"",
|
|
7
|
+
"https://ghfast.top/",
|
|
8
|
+
"https://gh-proxy.com/",
|
|
9
|
+
];
|
|
10
|
+
export const GITHUB_DOWNLOAD_HOSTS = new Set([
|
|
11
|
+
"github.com",
|
|
12
|
+
"release-assets.githubusercontent.com",
|
|
13
|
+
"objects.githubusercontent.com",
|
|
14
|
+
"github-releases.githubusercontent.com",
|
|
15
|
+
...GITHUB_MIRRORS.filter(Boolean).map((mirror) => new URL(mirror).hostname.toLowerCase()),
|
|
16
|
+
]);
|
|
17
|
+
function mirrorize(githubUrl) {
|
|
18
|
+
return GITHUB_MIRRORS.map((prefix) => (prefix ? `${prefix}${githubUrl}` : githubUrl));
|
|
19
|
+
}
|
|
20
|
+
function ghTokenHeaders() {
|
|
21
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
22
|
+
return token ? { authorization: `Bearer ${token}` } : {};
|
|
23
|
+
}
|
|
24
|
+
export async function resolveLatestTag(repo) {
|
|
25
|
+
const latestUrl = `https://github.com/${repo}/releases/latest`;
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetchWithRetry(latestUrl, {
|
|
28
|
+
manualRedirect: true,
|
|
29
|
+
attempts: 2,
|
|
30
|
+
deadlineMs: 15_000,
|
|
31
|
+
});
|
|
32
|
+
if (res.statusCode >= 300 && res.statusCode < 400) {
|
|
33
|
+
const location = res.headers.location;
|
|
34
|
+
const loc = Array.isArray(location) ? location[0] : location;
|
|
35
|
+
await res.discard();
|
|
36
|
+
if (loc) {
|
|
37
|
+
const target = new URL(loc, latestUrl);
|
|
38
|
+
if (target.protocol === "https:" && target.hostname.toLowerCase() === "github.com") {
|
|
39
|
+
const tag = target.pathname.match(/\/releases\/tag\/([^/]+)\/?$/)?.[1];
|
|
40
|
+
if (tag)
|
|
41
|
+
return decodeURIComponent(tag);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
await res.discard();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
}
|
|
51
|
+
const apiUrl = `https://api.github.com/repos/${repo}/releases/latest`;
|
|
52
|
+
const res = await fetchWithRetry(apiUrl, {
|
|
53
|
+
headers: {
|
|
54
|
+
accept: "application/vnd.github+json",
|
|
55
|
+
...ghTokenHeaders(),
|
|
56
|
+
},
|
|
57
|
+
attempts: 2,
|
|
58
|
+
deadlineMs: 15_000,
|
|
59
|
+
manualRedirect: true,
|
|
60
|
+
});
|
|
61
|
+
if (res.statusCode !== 200) {
|
|
62
|
+
await res.text(ERROR_BODY_LIMIT);
|
|
63
|
+
throw new Error(`Failed to resolve latest release for ${repo}: HTTP ${res.statusCode}`);
|
|
64
|
+
}
|
|
65
|
+
const text = await res.text(2 * 1024 * 1024);
|
|
66
|
+
let data;
|
|
67
|
+
try {
|
|
68
|
+
data = JSON.parse(text);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
}
|
|
72
|
+
if (!data?.tag_name) {
|
|
73
|
+
throw new Error(`GitHub release response for ${repo} missing tag_name`);
|
|
74
|
+
}
|
|
75
|
+
return data.tag_name;
|
|
76
|
+
}
|
|
77
|
+
export async function listReleaseAssets(repo, tag) {
|
|
78
|
+
const apiUrl = `https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`;
|
|
79
|
+
const res = await fetchWithRetry(apiUrl, {
|
|
80
|
+
headers: {
|
|
81
|
+
accept: "application/vnd.github+json",
|
|
82
|
+
...ghTokenHeaders(),
|
|
83
|
+
},
|
|
84
|
+
attempts: 2,
|
|
85
|
+
deadlineMs: 15_000,
|
|
86
|
+
manualRedirect: true,
|
|
87
|
+
});
|
|
88
|
+
if (res.statusCode !== 200) {
|
|
89
|
+
await res.text(ERROR_BODY_LIMIT);
|
|
90
|
+
throw new Error(`Failed to list release assets for ${repo}@${tag}: HTTP ${res.statusCode}`);
|
|
91
|
+
}
|
|
92
|
+
const text = await res.text(8 * 1024 * 1024);
|
|
93
|
+
let data;
|
|
94
|
+
try {
|
|
95
|
+
data = JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
}
|
|
99
|
+
if (!Array.isArray(data?.assets)) {
|
|
100
|
+
throw new Error(`GitHub release response for ${repo}@${tag} is missing assets`);
|
|
101
|
+
}
|
|
102
|
+
return data.assets.flatMap((value) => {
|
|
103
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
104
|
+
return [];
|
|
105
|
+
const asset = value;
|
|
106
|
+
if (typeof asset.name !== "string" ||
|
|
107
|
+
typeof asset.browser_download_url !== "string" ||
|
|
108
|
+
typeof asset.size !== "number" ||
|
|
109
|
+
!Number.isSafeInteger(asset.size) ||
|
|
110
|
+
asset.size <= 0 ||
|
|
111
|
+
typeof asset.digest !== "string") {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
name: asset.name,
|
|
117
|
+
browser_download_url: asset.browser_download_url,
|
|
118
|
+
size: asset.size,
|
|
119
|
+
digest: asset.digest,
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
export const RELEASE_ASSET_SIZE_LIMIT = 128 * 1024 * 1024;
|
|
125
|
+
export function parseSha256Digest(value) {
|
|
126
|
+
const match = value.match(/^sha256:([0-9a-f]{64})$/i);
|
|
127
|
+
if (!match?.[1])
|
|
128
|
+
throw new Error(`Release asset has an invalid SHA-256 digest: ${value}`);
|
|
129
|
+
return match[1].toLowerCase();
|
|
130
|
+
}
|
|
131
|
+
export async function sha256File(file) {
|
|
132
|
+
const hash = crypto.createHash("sha256");
|
|
133
|
+
for await (const chunk of fs.createReadStream(file))
|
|
134
|
+
hash.update(chunk);
|
|
135
|
+
return hash.digest("hex");
|
|
136
|
+
}
|
|
137
|
+
export function selectReleaseAsset(assets, candidates) {
|
|
138
|
+
return candidates
|
|
139
|
+
.map((candidate) => assets.find((asset) => asset.name.toLowerCase() === candidate.toLowerCase()))
|
|
140
|
+
.find((asset) => asset !== undefined);
|
|
141
|
+
}
|
|
142
|
+
export async function downloadReleaseAsset(opts) {
|
|
143
|
+
const chosen = selectReleaseAsset(opts.assets, opts.candidates);
|
|
144
|
+
if (!chosen) {
|
|
145
|
+
throw new Error(`No trusted release asset matched ${opts.candidates.join(", ")} for ${opts.repo}@${opts.tag}`);
|
|
146
|
+
}
|
|
147
|
+
if (chosen.size > RELEASE_ASSET_SIZE_LIMIT) {
|
|
148
|
+
throw new Error(`Release asset ${chosen.name} exceeds the ${RELEASE_ASSET_SIZE_LIMIT} byte safety limit`);
|
|
149
|
+
}
|
|
150
|
+
const expectedDigest = parseSha256Digest(chosen.digest);
|
|
151
|
+
const directUrl = `https://github.com/${opts.repo}/releases/download/${opts.tag}/${chosen.name}`;
|
|
152
|
+
const urls = mirrorize(directUrl);
|
|
153
|
+
const deadlineMs = opts.deadlineMs ?? 15 * 60_000;
|
|
154
|
+
if (!Number.isSafeInteger(deadlineMs) || deadlineMs <= 0) {
|
|
155
|
+
throw new Error("Core asset deadlineMs must be a positive safe integer");
|
|
156
|
+
}
|
|
157
|
+
const deadlineAt = Date.now() + deadlineMs;
|
|
158
|
+
let lastError;
|
|
159
|
+
for (const url of urls) {
|
|
160
|
+
const remainingMs = deadlineAt - Date.now();
|
|
161
|
+
if (remainingMs <= 0) {
|
|
162
|
+
lastError = new Error(`Core asset download deadline exceeded after ${deadlineMs}ms`);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
await downloadToFile(url, opts.dest, {
|
|
167
|
+
allowedHosts: GITHUB_DOWNLOAD_HOSTS,
|
|
168
|
+
maxBytes: RELEASE_ASSET_SIZE_LIMIT,
|
|
169
|
+
requireHttps: true,
|
|
170
|
+
onProgress: opts.onProgress,
|
|
171
|
+
stallMs: 60_000,
|
|
172
|
+
deadlineMs: remainingMs,
|
|
173
|
+
});
|
|
174
|
+
const actualDigest = await sha256File(opts.dest);
|
|
175
|
+
if (actualDigest !== expectedDigest) {
|
|
176
|
+
throw new Error(`SHA-256 mismatch for ${chosen.name}: expected ${expectedDigest}, got ${actualDigest}`);
|
|
177
|
+
}
|
|
178
|
+
return chosen.name;
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
fs.rmSync(opts.dest, { force: true });
|
|
182
|
+
lastError = err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
throw new Error(`Failed to download and verify ${chosen.name} from all mirrors: ${lastError?.message ?? "unknown error"}`);
|
|
186
|
+
}
|
|
187
|
+
export { USER_AGENT };
|