@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.
Files changed (339) hide show
  1. package/CHANGELOG.md +184 -0
  2. package/LICENSE +21 -0
  3. package/README.md +71 -0
  4. package/THIRD_PARTY_NOTICES.md +54 -0
  5. package/dist/api.js +103 -0
  6. package/dist/browser.js +25 -0
  7. package/dist/cli-errors.js +15 -0
  8. package/dist/cli.js +122 -0
  9. package/dist/commands/lifecycle.js +50 -0
  10. package/dist/commands/logs.js +54 -0
  11. package/dist/commands/shared.js +8 -0
  12. package/dist/commands/status.js +39 -0
  13. package/dist/commands/update.js +5 -0
  14. package/dist/commands/upgrade.js +43 -0
  15. package/dist/commands/web.js +33 -0
  16. package/dist/contracts.js +412 -0
  17. package/dist/core-config-validation.js +72 -0
  18. package/dist/core-install-record.js +55 -0
  19. package/dist/core-install-transaction.js +114 -0
  20. package/dist/core-update-coordination.js +105 -0
  21. package/dist/core-update-service.js +245 -0
  22. package/dist/core-update.js +804 -0
  23. package/dist/core-version.js +18 -0
  24. package/dist/core.js +206 -0
  25. package/dist/daemon/app.js +157 -0
  26. package/dist/daemon/context.js +40 -0
  27. package/dist/daemon/entry.js +67 -0
  28. package/dist/daemon/errors.js +47 -0
  29. package/dist/daemon/handlers/core.js +17 -0
  30. package/dist/daemon/handlers/daemon.js +66 -0
  31. package/dist/daemon/handlers/profiles.js +57 -0
  32. package/dist/daemon/handlers/proxy.js +12 -0
  33. package/dist/daemon/handlers/settings.js +48 -0
  34. package/dist/daemon/router.js +312 -0
  35. package/dist/daemon/scheduler.js +33 -0
  36. package/dist/daemon/server.js +121 -0
  37. package/dist/daemon-auth.js +71 -0
  38. package/dist/daemon-client.js +55 -0
  39. package/dist/daemon-entry.js +13 -0
  40. package/dist/daemon-http.js +93 -0
  41. package/dist/daemon-lifecycle.js +378 -0
  42. package/dist/daemon-proxy.js +176 -0
  43. package/dist/daemon-static.js +106 -0
  44. package/dist/daemon.js +3 -0
  45. package/dist/error-utils.js +14 -0
  46. package/dist/fs-atomic.js +123 -0
  47. package/dist/github.js +187 -0
  48. package/dist/http.js +282 -0
  49. package/dist/json-shape.js +17 -0
  50. package/dist/log-follow.js +280 -0
  51. package/dist/log.js +17 -0
  52. package/dist/managed-state-transaction.js +377 -0
  53. package/dist/mihomo-config.js +334 -0
  54. package/dist/node-version-guard.js +8 -0
  55. package/dist/offline-mutation.js +58 -0
  56. package/dist/paths.js +57 -0
  57. package/dist/process.js +572 -0
  58. package/dist/profile-migration.js +101 -0
  59. package/dist/profile-service.js +667 -0
  60. package/dist/profiles.js +182 -0
  61. package/dist/runtime-lifecycle.js +205 -0
  62. package/dist/runtime-owner.js +87 -0
  63. package/dist/runtime-recovery.js +31 -0
  64. package/dist/sash-client.js +163 -0
  65. package/dist/settings-service.js +242 -0
  66. package/dist/settings.js +314 -0
  67. package/dist/state-lock.js +331 -0
  68. package/dist/status.js +279 -0
  69. package/dist/supervisor.js +301 -0
  70. package/dist/sysproxy/common.js +99 -0
  71. package/dist/sysproxy/darwin.js +287 -0
  72. package/dist/sysproxy/factory.js +71 -0
  73. package/dist/sysproxy/gnome.js +181 -0
  74. package/dist/sysproxy/legacy.js +58 -0
  75. package/dist/sysproxy/snapshot.js +284 -0
  76. package/dist/sysproxy/types.js +24 -0
  77. package/dist/sysproxy/windows.js +216 -0
  78. package/dist/sysproxy.js +8 -0
  79. package/dist/system-proxy-manager.js +460 -0
  80. package/dist/tun-guidance.js +11 -0
  81. package/dist/ui/assets/05129d5ad91a6181d272cc3d031363d6-DJkDXMrK.woff2 +0 -0
  82. package/dist/ui/assets/0526ebf94418638dfd5b6cd9b06c07d4-BzWe4Lji.woff2 +0 -0
  83. package/dist/ui/assets/053f2647a78f659f6f8a3ae8436bd265-BocXYDl5.woff2 +0 -0
  84. package/dist/ui/assets/078f666863424d3fd2d98e3ea528704c-DQ_QCvOt.woff2 +0 -0
  85. package/dist/ui/assets/07f3ff379663ffa0222c12f241421dd9-Cma7oDlr.woff2 +0 -0
  86. package/dist/ui/assets/08427b8d865853bbd7c439321b26495f-CJmC8fWx.woff2 +0 -0
  87. package/dist/ui/assets/095224e4db29d40c54098da02b266b0a-DjsP0nQn.woff2 +0 -0
  88. package/dist/ui/assets/0ada18ca45940b08accde5c61ee871e3-E1BZD3RX.woff2 +0 -0
  89. package/dist/ui/assets/0be242294f7d791af850c6df38ac78a0-2cL6Ntwf.woff2 +0 -0
  90. package/dist/ui/assets/0bf18c938d0c27cce06c39f855405fdb-CruLZZZJ.woff2 +0 -0
  91. package/dist/ui/assets/0cfb3e9f4edf60839c37dc83e6f75ea3-DzAtRHvi.woff2 +0 -0
  92. package/dist/ui/assets/0fbbd340d0467dfe3101d58846f30cb0-gtFcO250.woff2 +0 -0
  93. package/dist/ui/assets/1063fa2dc7334d1f383e30824e016eae-B7U-kD-0.woff2 +0 -0
  94. package/dist/ui/assets/109e26e62ddc85d946e699464afbece7-BLnUjAEV.woff2 +0 -0
  95. package/dist/ui/assets/12a443b0ae9748a993b5395f8fad412b-Btjff2Lz.woff2 +0 -0
  96. package/dist/ui/assets/135c4621de7a4d85d852e03d416f13ee-DdSuW36e.woff2 +0 -0
  97. package/dist/ui/assets/13aa415469ee761c968749b1c0c56ec6-Dwe-Wa_w.woff2 +0 -0
  98. package/dist/ui/assets/1435b1ad447bd9c0e8bc52035c9dcfa8-C05ldXtw.woff2 +0 -0
  99. package/dist/ui/assets/175e0915aa874e7446b79b6bc9b00721-Dp0-l77L.woff2 +0 -0
  100. package/dist/ui/assets/1876e7d02187f006e2b7f769391c3089-CNjpRdap.woff2 +0 -0
  101. package/dist/ui/assets/1886dc284b3b845e3b5fe04aa2107276-Bh_wHwL1.woff2 +0 -0
  102. package/dist/ui/assets/19a3dda6611aaf5d787403bd1159fe15-zoEBvdpA.woff2 +0 -0
  103. package/dist/ui/assets/1afa74e14da193125e45d4783d358a4f-BgjSTENX.woff2 +0 -0
  104. package/dist/ui/assets/1cd26a6a6d784720b4a43053e9058bcf-TAP3dvNi.woff2 +0 -0
  105. package/dist/ui/assets/1ce19bddc3beab385a863b1e05685678-BCdXitZw.woff2 +0 -0
  106. package/dist/ui/assets/1d3459ac6578c80502bdb876a4824a93-CSwdnEGP.woff2 +0 -0
  107. package/dist/ui/assets/1d479e364dab4af01c539f0a6a7ac036-ZFI-djGt.woff2 +0 -0
  108. package/dist/ui/assets/1dcea2408d17fcec4197adce50f8b32d-CnK2xXR-.woff2 +0 -0
  109. package/dist/ui/assets/1e855385699999e24a33f7df8aed0489-DGFHkDVQ.woff2 +0 -0
  110. package/dist/ui/assets/1f55944b8f7972580e3029694462a6b0-CB7ogVJ9.woff2 +0 -0
  111. package/dist/ui/assets/1f96eda70929089762dc3c4feb33f9c8-DtSQ6YOf.woff2 +0 -0
  112. package/dist/ui/assets/1fb275532caadc746455881b31da27c4-BHFTsg74.woff2 +0 -0
  113. package/dist/ui/assets/20e679bdc00d06f802ea71fd9154b284-D9gRjq_v.woff2 +0 -0
  114. package/dist/ui/assets/25aff66c4a9f8cb801cbd525f4a923e4-Dgi87hP7.woff2 +0 -0
  115. package/dist/ui/assets/28e6700380b023b23e77f2d75aee624f-b19D855Q.woff2 +0 -0
  116. package/dist/ui/assets/2a1c50aa9cd99ce3a0197f5d49948e63-DrztdXCB.woff2 +0 -0
  117. package/dist/ui/assets/2b4e0ae0bf3af610b97717dfd66a012d-CBoapvha.woff2 +0 -0
  118. package/dist/ui/assets/2be2b7a672b616bc2f07059a67bfaad8-BUR0aNML.woff2 +0 -0
  119. package/dist/ui/assets/2e0a358ada78ea98ec8ba86002a27eac-DvgbSr9v.woff2 +0 -0
  120. package/dist/ui/assets/2e1cf63a2a22a50516c49b1dc521ea8a-CmltIxig.woff2 +0 -0
  121. package/dist/ui/assets/2e4481801925fcb6573c4337c73f15d7-DYOoCk-m.woff2 +0 -0
  122. package/dist/ui/assets/2fe5e634c568cd874f94d24b8f02bfd1-DcEHY35G.woff2 +0 -0
  123. package/dist/ui/assets/309c33f63f29887fb557115aa24d4bb5-CUgqrXIj.woff2 +0 -0
  124. package/dist/ui/assets/3158f45c959e1b346b0bbd6f3193dbf0-kimcClqp.woff2 +0 -0
  125. package/dist/ui/assets/316adbc8037a36f8d804637b053bbe5e-CjbZmJf9.woff2 +0 -0
  126. package/dist/ui/assets/319e9631bc29fc7235b70fc9aac43cd9-IV6yZuHZ.woff2 +0 -0
  127. package/dist/ui/assets/32054f9bb88d9431b1cdf023f770e614-CYrpCgqK.woff2 +0 -0
  128. package/dist/ui/assets/3245fa1aac6338c1d9d04befb7c346f8-CzvPhVPA.woff2 +0 -0
  129. package/dist/ui/assets/326c730f98afa476057957f2743c81e2-GPHJ3Fpu.woff2 +0 -0
  130. package/dist/ui/assets/33885558dc783bf9bae6c5d4668fef07-D33us0yR.woff2 +0 -0
  131. package/dist/ui/assets/33cd11f5e61556e4618801c8231f2744-CoR_Tv3X.woff2 +0 -0
  132. package/dist/ui/assets/34a170749cb7e6c5851acc510882af6b-BCZwsihk.woff2 +0 -0
  133. package/dist/ui/assets/3538de2d387591258d7e9908aad803b9-CewOkERZ.woff2 +0 -0
  134. package/dist/ui/assets/38d7b029c036080067a2e94211098171-CettR5Qv.woff2 +0 -0
  135. package/dist/ui/assets/393f47efeefc9f9e9cca83eb10a82c69-Brhz4z1y.woff2 +0 -0
  136. package/dist/ui/assets/396a51f2b274d01f6ba606a94f3e5fe2-DgRk-jtI.woff2 +0 -0
  137. package/dist/ui/assets/3991bf955d4c925e84b4a6542760d4d5-II4HtRkQ.woff2 +0 -0
  138. package/dist/ui/assets/39b6cb6ae8d1ea0e9832b88d2c21ef99-CbJm-q6b.woff2 +0 -0
  139. package/dist/ui/assets/3a1133157fbd259f1a51aaa9f8418a26-3FmUjjvi.woff2 +0 -0
  140. package/dist/ui/assets/3a34cff07dca7a65ed2486be19229290-B7ml2yBN.woff2 +0 -0
  141. package/dist/ui/assets/3c339dd7c27e6b8a06eea431733e076f-dGQqXaEj.woff2 +0 -0
  142. package/dist/ui/assets/3d074ea67f60953b816ce7cf5d87a980-B5VkPOmg.woff2 +0 -0
  143. package/dist/ui/assets/3d38f54dcf4a9f4a50165d5849becf71-CVDtFSZn.woff2 +0 -0
  144. package/dist/ui/assets/3e3fc9a19a27f94e4ff1b815b55b2671-Ci736GLi.woff2 +0 -0
  145. package/dist/ui/assets/3e8abb1f606f29e2f0a8a8a6b702d263-C5sOSf5L.woff2 +0 -0
  146. package/dist/ui/assets/3f0ab4e1cd99bdbfdf9acbc8f4bcb9ce-0DcdTLxV.woff2 +0 -0
  147. package/dist/ui/assets/3f30c0482650345c981d66920f56ab61-CTD2CsZI.woff2 +0 -0
  148. package/dist/ui/assets/41a9aa12ad9a60758b304c1a998fb781-BTQUDLf1.woff2 +0 -0
  149. package/dist/ui/assets/424010b2bc5cee540b92b28da49b61bf-DT8upW2M.woff2 +0 -0
  150. package/dist/ui/assets/42fbdafff2d44b76c6665d51bb2cee9d-_UrojhXD.woff2 +0 -0
  151. package/dist/ui/assets/4341db13c311b3cb0eaaf6b4c71b89e5-CzjA7MzG.woff2 +0 -0
  152. package/dist/ui/assets/434db5a796128131fde78d4389d14b71-AeNdWKua.woff2 +0 -0
  153. package/dist/ui/assets/43cb51fc70a15274d49e2d956c5ec3b4-BQbufd96.woff2 +0 -0
  154. package/dist/ui/assets/43e79de1c7815f21efbc966ff96fcbd6-DlCQvOsQ.woff2 +0 -0
  155. package/dist/ui/assets/43faf6a8a3cde1088bd5f7c830890a89-SvDzF3nA.woff2 +0 -0
  156. package/dist/ui/assets/44731ae21d7690ce538d0f8242755833-CEmu9Z88.woff2 +0 -0
  157. package/dist/ui/assets/45324b2a64cc0c0db797d5c09e239d34-BuFudtvM.woff2 +0 -0
  158. package/dist/ui/assets/4584df05d00aa75ae6e1f850fdc9df1d-GNHt6xwM.woff2 +0 -0
  159. package/dist/ui/assets/48d539539e98481fb511a761031c59fd-Cd9gdi8B.woff2 +0 -0
  160. package/dist/ui/assets/49a1fb9fc6a35796b2e0d3539088ede8-CD1cRDOy.woff2 +0 -0
  161. package/dist/ui/assets/4bec02eb0c2371e59bfb7d41a141ea1e-CmBnNPo0.woff2 +0 -0
  162. package/dist/ui/assets/4c5a81ef5683f6fbf9935e640de804e7-B4wQxzVs.woff2 +0 -0
  163. package/dist/ui/assets/4f11c9c75e86214b7f69ddee3a9f8076-DHsQ2xpE.woff2 +0 -0
  164. package/dist/ui/assets/50434b8ff88e27101d1c67ba953ee873-PH_hIqxu.woff2 +0 -0
  165. package/dist/ui/assets/5107fd5153d52c6a3f8d43ff214dd6c2-ls3eo5Qd.woff2 +0 -0
  166. package/dist/ui/assets/520cb7d3683dee99e767650efc0aa5b1-Lkd4z-9S.woff2 +0 -0
  167. package/dist/ui/assets/5455087df2f091b7bf3d38c5a102a2e7-QNkiOxwe.woff2 +0 -0
  168. package/dist/ui/assets/556450fdf0978d329cf8277e3b71dcdb-CtypGAPw.woff2 +0 -0
  169. package/dist/ui/assets/5a1b5c1b71cad50c51a79a7f4825e6e6-DQA407Ec.woff2 +0 -0
  170. package/dist/ui/assets/5ba02e809d2ef4f7639178e7a7d654c0-Dty0omtl.woff2 +0 -0
  171. package/dist/ui/assets/5bd654afe48e094f95e5142e4eaadc75-D9pUq71R.woff2 +0 -0
  172. package/dist/ui/assets/5c4c3ca0a3f4115879a61a96d02e3efc-RcVLBj31.woff2 +0 -0
  173. package/dist/ui/assets/5c737d740638e8d764f12c1e5c1cd144-D2Npjq-N.woff2 +0 -0
  174. package/dist/ui/assets/5e78019102102aa5741aa591313fd5b5-V7Azrw-2.woff2 +0 -0
  175. package/dist/ui/assets/6287cacac64e1849941f7b7d7e6316c5-DzkbPOID.woff2 +0 -0
  176. package/dist/ui/assets/643df7194160d54edc48d16d76e5ba74-BKmmIRjw.woff2 +0 -0
  177. package/dist/ui/assets/65d3b214c69ff6786fd1cab06f8394b7-B0cRUJAr.woff2 +0 -0
  178. package/dist/ui/assets/6698103c11bfe260d44fcf00c6d8b623-DVWYOeyb.woff2 +0 -0
  179. package/dist/ui/assets/692e07b3a609a8f1a21c15c45631f348-DJW5LcZH.woff2 +0 -0
  180. package/dist/ui/assets/694e87456af181279c2bfd551aa3c6e3-Cu7kRuJB.woff2 +0 -0
  181. package/dist/ui/assets/6b88066bdc7a6e8bc823128dbd0f6f3b-BG38UZSq.woff2 +0 -0
  182. package/dist/ui/assets/6d6f36f15381826d113832bfdd41b021-Bgh7YPiq.woff2 +0 -0
  183. package/dist/ui/assets/6e22361162bd0a5cd189ca3c98d23d52-DqnptHqF.woff2 +0 -0
  184. package/dist/ui/assets/6e884dd501b8da24ecd37425a9b3d51d-BVNqra8s.woff2 +0 -0
  185. package/dist/ui/assets/71200dbc7497fbafa28db5cfeae74acb-B6xMqoC6.woff2 +0 -0
  186. package/dist/ui/assets/72a85441a9757146440e3985f3b02075-CzTSlCPZ.woff2 +0 -0
  187. package/dist/ui/assets/7472d7e02045dea3ff288eac850e38eb-CboJEy4X.woff2 +0 -0
  188. package/dist/ui/assets/795ffa953bea666763ae79ac88aa7e5a-C6UsoDmi.woff2 +0 -0
  189. package/dist/ui/assets/7aa8647d6c52d249169b73417aa8e444-kJejhQ0-.woff2 +0 -0
  190. package/dist/ui/assets/7be6b794fc233ccd8b5769496301313a-Ba2iZC0a.woff2 +0 -0
  191. package/dist/ui/assets/7c0748f04d52086809c9f551c5aaded7-DSzO_ybF.woff2 +0 -0
  192. package/dist/ui/assets/7c858bb53a7652227fab8cf9753d3e1f-Bi9fbvIF.woff2 +0 -0
  193. package/dist/ui/assets/7e7026091b4e6061b05963bdb3a7f944-CrmRDJpc.woff2 +0 -0
  194. package/dist/ui/assets/7f80369a7479d568ab8bf492000282bd-CqcI5ZOp.woff2 +0 -0
  195. package/dist/ui/assets/834ce2f2571aaa26e47a618c4ad55e58-DZfWJrxo.woff2 +0 -0
  196. package/dist/ui/assets/834faab602d9c795341bc7dfd6578fe5-B1VeBVWh.woff2 +0 -0
  197. package/dist/ui/assets/8513a8eea35b460a047a16a87c36f8bb-H259w_6b.woff2 +0 -0
  198. package/dist/ui/assets/8699e8a36465bf46671dc28ce24e188c-YlkPpQ6h.woff2 +0 -0
  199. package/dist/ui/assets/86ae8881eae9eac7e9c0182c8ce6c627-Ch2r3LNs.woff2 +0 -0
  200. package/dist/ui/assets/86b3ae1be58d76f7147793f09264451b-CpgTfcn-.woff2 +0 -0
  201. package/dist/ui/assets/8769f8d7d45adcfcbd3f80460d22720d-DV_yqcTo.woff2 +0 -0
  202. package/dist/ui/assets/87bb0ceea9f5515490c832bdf6ebf641-CJpxd8e6.woff2 +0 -0
  203. package/dist/ui/assets/8928b9a50b5efbaaafa039674a3c7301-C-42WRD6.woff2 +0 -0
  204. package/dist/ui/assets/899f2c2a36cbeac473a7caeeaabc46b1-xrIcXJXU.woff2 +0 -0
  205. package/dist/ui/assets/8a075d4043e6bf66740808b492186438-Dq0346Qm.woff2 +0 -0
  206. package/dist/ui/assets/8b86676936792154c53205ab5d90b797-BGH_CGsg.woff2 +0 -0
  207. package/dist/ui/assets/8c63ca479653200a0e12f428242e1171-CsBTcPwY.woff2 +0 -0
  208. package/dist/ui/assets/8c9c7ff8342f5fed60e73c4a7ad67325-CGSYpt52.woff2 +0 -0
  209. package/dist/ui/assets/8ca655fcc5d9cf33d49815e231f3f356-b8PyEeZg.woff2 +0 -0
  210. package/dist/ui/assets/8e96f0f9385ef826f900587d0fc18b3d-D7GIG6Uq.woff2 +0 -0
  211. package/dist/ui/assets/8ea917252cf2c1e664f06ee5badffeea-CDZ1AFr0.woff2 +0 -0
  212. package/dist/ui/assets/8f42f890fc37e292017446af85af2745-Qx6oGI0z.woff2 +0 -0
  213. package/dist/ui/assets/8f9589e71c53bb753749a9c412f15c01-BVR5mPJl.woff2 +0 -0
  214. package/dist/ui/assets/90e70b52a3cc2491bbb2b4c0caf0ae7e-BdwPu67j.woff2 +0 -0
  215. package/dist/ui/assets/92ac4ca42fe050c1e680c585551abd9c-CXLyuj7l.woff2 +0 -0
  216. package/dist/ui/assets/92c12bb794c491930fd06f3f97299807-u_0ognJ0.woff2 +0 -0
  217. package/dist/ui/assets/92f0b4d2bb274a801aac10fc34c1e0cc-DFHB6dyu.woff2 +0 -0
  218. package/dist/ui/assets/93247aebd628367f5518eafdb2770ce9-o1N4zvTO.woff2 +0 -0
  219. package/dist/ui/assets/940446ea160995af66a9ee223cc3d4d1-C-_7aPYO.woff2 +0 -0
  220. package/dist/ui/assets/94be430de618453b6ae27bd366827ba2-1YYf1n5N.woff2 +0 -0
  221. package/dist/ui/assets/98cd7d62095e466e55885b34f63c86f1-z33YCk0X.woff2 +0 -0
  222. package/dist/ui/assets/99049fd5cec5894cafe2a1e5e13d1e03-DNrg8nIk.woff2 +0 -0
  223. package/dist/ui/assets/99e2752b7351aaa7681b4d91db80e89a-b3hi7miY.woff2 +0 -0
  224. package/dist/ui/assets/9d1b670cbfd64716ac685dea26237d95-DAZ9U6es.woff2 +0 -0
  225. package/dist/ui/assets/CodeEditorModal-6m0TJWyF.css +1 -0
  226. package/dist/ui/assets/CodeEditorModal-CFEnWsyh.js +14 -0
  227. package/dist/ui/assets/ConnectionsView-DHo6yvU6.css +1 -0
  228. package/dist/ui/assets/ConnectionsView-DNGmZBSU.js +1 -0
  229. package/dist/ui/assets/LogsView-BoeE92VF.css +1 -0
  230. package/dist/ui/assets/LogsView-fSiaxQ13.js +1 -0
  231. package/dist/ui/assets/PaginationFooter-B3kHzRfB.js +1 -0
  232. package/dist/ui/assets/PaginationFooter-BNkpcNoB.css +1 -0
  233. package/dist/ui/assets/ProfileEditorDialog-BydoZthX.js +1 -0
  234. package/dist/ui/assets/ProfilesView-Btc1DOxE.js +2 -0
  235. package/dist/ui/assets/ProfilesView-ByqdFNHN.css +1 -0
  236. package/dist/ui/assets/RulesView-CHyynVqA.css +1 -0
  237. package/dist/ui/assets/RulesView-D9vZBiJ1.js +1 -0
  238. package/dist/ui/assets/SettingsFileDialog-CNFEAVs4.js +1 -0
  239. package/dist/ui/assets/SettingsView-BlDhZkXQ.js +2 -0
  240. package/dist/ui/assets/SettingsView-CngS3vBM.css +1 -0
  241. package/dist/ui/assets/a0f2d41645a113d821e7ff0891daad1e-XK0rDAci.woff2 +0 -0
  242. package/dist/ui/assets/a1d1345d8407dd6169f39c28ff6b1708-Bn7hMmv8.woff2 +0 -0
  243. package/dist/ui/assets/a29f51b4ebb6776e43e7492a036be28e-CrAKKz8u.woff2 +0 -0
  244. package/dist/ui/assets/a3252ac86b2c4e88bfb9baecabcad817-qnA_3LNE.woff2 +0 -0
  245. package/dist/ui/assets/a3d63a61e7eb2bb497dbc2da65bd0873-BRMJAm0m.woff2 +0 -0
  246. package/dist/ui/assets/a45eea933252dbe3529f30a0323c2661-D4kBOrXR.woff2 +0 -0
  247. package/dist/ui/assets/a4886c4ebed8c8b1f8a87b5b491a39aa-CBQdRqpF.woff2 +0 -0
  248. package/dist/ui/assets/a50b4ab745c7676d9d3ceb3c0a023a3d-CW990pgI.woff2 +0 -0
  249. package/dist/ui/assets/a5851ae4d7e58996d8bd681f80d4727c-BBytdb0t.woff2 +0 -0
  250. package/dist/ui/assets/a5cdaa96bf7d1db6c671432f9ab7bccb-slKJjgfX.woff2 +0 -0
  251. package/dist/ui/assets/a640dd2e962cf72c7fcf76234c973303-CUCWUDBY.woff2 +0 -0
  252. package/dist/ui/assets/a67e457bbd366aef5104ce743a2cf6d4-DkE8fLlE.woff2 +0 -0
  253. package/dist/ui/assets/a6c3f2257315d45aa212bd2e86a02852-CxDbXnZV.woff2 +0 -0
  254. package/dist/ui/assets/a73399a40a5f81fb7314b8b9a10cd221-DRAvJPKI.woff2 +0 -0
  255. package/dist/ui/assets/a73a64b0941803d9d807b9ec082df641-BbPpPyy5.woff2 +0 -0
  256. package/dist/ui/assets/a73d1a0a1dfd4f4234f7a0649ece4da4-BHeqktY9.woff2 +0 -0
  257. package/dist/ui/assets/a740044e77a400a450e71fcdea55523f-Co5hpBNK.woff2 +0 -0
  258. package/dist/ui/assets/a7669b29f404662c179c71b87e67c8d2-CVnuBgHy.woff2 +0 -0
  259. package/dist/ui/assets/a964a4def15811e91b05a91740a1cf37-CzcDfCRn.woff2 +0 -0
  260. package/dist/ui/assets/aac3f69132da164921fda75d2d90bfb7-B6rUZQ4_.woff2 +0 -0
  261. package/dist/ui/assets/aacd263c3bb4ba163a4d5079c159680a-DtrDLFlk.woff2 +0 -0
  262. package/dist/ui/assets/ac62ec881ded2cdb450e542b396e24d0-CRlVJvPa.woff2 +0 -0
  263. package/dist/ui/assets/aebcb9b7d4b174ab5a4b911206f24036-BuleSpgP.woff2 +0 -0
  264. package/dist/ui/assets/af70712dadc05c7b18d248996a5ab0a9-gCZSZGzb.woff2 +0 -0
  265. package/dist/ui/assets/afbd3a8a4cfaa4561b7848c28bc6c4f8-DgnqceEw.woff2 +0 -0
  266. package/dist/ui/assets/b02d3867e6e2ea921d2845b44c4a5a31-C9v4FrqD.woff2 +0 -0
  267. package/dist/ui/assets/b1b22825b930688403337d4fd7bbba62-vmMs2pJf.woff2 +0 -0
  268. package/dist/ui/assets/b3206c979bfb28d12d7ad6df58e74b9f-X1PqLaCR.woff2 +0 -0
  269. package/dist/ui/assets/b6280e42d4f11dcab70594f9d6774936-CtezImLG.woff2 +0 -0
  270. package/dist/ui/assets/b71b8cd1a70405c3dbc0e7a44055ca9a-Cd6cIOEr.woff2 +0 -0
  271. package/dist/ui/assets/b9198c4f220a4ca075f8daa4db529e50-Bgc7zlEt.woff2 +0 -0
  272. package/dist/ui/assets/baad304a58d7d42d0af29235c5e9490b-BQZYwARB.woff2 +0 -0
  273. package/dist/ui/assets/bb99e34f8784cc44f0eeadbe735dd238-UHFYI827.woff2 +0 -0
  274. package/dist/ui/assets/bc1cb0f523c8a2ff566a047aeddf90fd-BSlXQn97.woff2 +0 -0
  275. package/dist/ui/assets/bd3171e975d62612e50148bf38b2158f-CtZKzCzQ.woff2 +0 -0
  276. package/dist/ui/assets/be10640c403263dd5917ad65c4d453a6-B-rbuuja.woff2 +0 -0
  277. package/dist/ui/assets/be764e101e78b50b749aa1414d4810a7-yIPaQQih.woff2 +0 -0
  278. package/dist/ui/assets/bfa682a7f5f12945b41230c35eb859f6-CzagcI5f.woff2 +0 -0
  279. package/dist/ui/assets/bfa9753ed26f2e1f81ab7020167bd1a0-C6ZNQQ2p.woff2 +0 -0
  280. package/dist/ui/assets/branding/sash-cat.png +0 -0
  281. package/dist/ui/assets/c0e78c6ad709b19ce437677c91033489-BtwtlApg.woff2 +0 -0
  282. package/dist/ui/assets/c5a59409c610cc8c265d1691eb3f71eb-DmI_ky2X.woff2 +0 -0
  283. package/dist/ui/assets/c5ff66a1a57f2a29424e8de5d4ef3756-C9pOsaV1.woff2 +0 -0
  284. package/dist/ui/assets/c607bdee84a0e5b69721367ccc0c9c31-eoHbdXeg.woff2 +0 -0
  285. package/dist/ui/assets/c8095033941d18db730496eb4c863757-DoexTYks.woff2 +0 -0
  286. package/dist/ui/assets/c8295519e16f4cf3a350e9729f07653a-C8bAtIO_.woff2 +0 -0
  287. package/dist/ui/assets/c91f9c9f4db81afeba37c8e73afb0bfd-BGIl2iKW.woff2 +0 -0
  288. package/dist/ui/assets/c9ad9caa4afdd40766add91d321b0350-HfuiRq1k.woff2 +0 -0
  289. package/dist/ui/assets/cc4f271c90f9ea5157db9c0a4edc976b-MAjuDvHE.woff2 +0 -0
  290. package/dist/ui/assets/cdf7c3d96ba172214d7e1e9fe5e6ba8b-CUx4fWQE.woff2 +0 -0
  291. package/dist/ui/assets/cf70d5821ea7c3ca34653169ac763606-CE0T0Svc.woff2 +0 -0
  292. package/dist/ui/assets/d0d5efe92496b1ceea38e180b0ac97d3-7G71Z6v3.woff2 +0 -0
  293. package/dist/ui/assets/d1977142eceb878b06440375e64c4372-mr-7LWm-.woff2 +0 -0
  294. package/dist/ui/assets/d42feb13392b4dd9214c61b61a6e5ae8-DjnKADf_.woff2 +0 -0
  295. package/dist/ui/assets/d46a696ec6a077c6873fc39043c3c082-C62E-13k.woff2 +0 -0
  296. package/dist/ui/assets/d4c0bbf1605a8a9c36fff5a2ead81bb5-C2F6tzOj.woff2 +0 -0
  297. package/dist/ui/assets/d918d4e47697b7c752ea223e0d0d4c81-twovU2ty.woff2 +0 -0
  298. package/dist/ui/assets/d988c05da0b5624267dd825fedd133d1-Bgbc9SVH.woff2 +0 -0
  299. package/dist/ui/assets/da3a071df79531f4f033b7c8499651d0-0tgZJL_N.woff2 +0 -0
  300. package/dist/ui/assets/da8eba4acbf93048645043006de1c5f0-Ce0dmHn_.woff2 +0 -0
  301. package/dist/ui/assets/dc3f16892e20a85a8fcad2d3097e622b-8PRrFPJt.woff2 +0 -0
  302. package/dist/ui/assets/dd5f808ec691f911483a2eb60b5049bd-BhVupSbT.woff2 +0 -0
  303. package/dist/ui/assets/e2b04534cc697a76afd70e885119cb8c-DO303iCb.woff2 +0 -0
  304. package/dist/ui/assets/e307280eb2fb7809adee14ff0d247d32-DHMLXvSs.woff2 +0 -0
  305. package/dist/ui/assets/e3b484bec850c669094f713570870b3a-BI_9kRw8.woff2 +0 -0
  306. package/dist/ui/assets/e7b1d196949b7530cbda0ad956fbb2cc-i2m2Id0q.woff2 +0 -0
  307. package/dist/ui/assets/e7c2e5fb387880508811cadc2926e13a-BfROXX4U.woff2 +0 -0
  308. package/dist/ui/assets/e8a5fe283c8b9e173097bee0b5757ab6-BvfjDjYP.woff2 +0 -0
  309. package/dist/ui/assets/e8e3465d48435612e9c3509be3984188-BeUPU5-9.woff2 +0 -0
  310. package/dist/ui/assets/e93903f4604b007fc1b7134a74f5c534-B1M4h46T.woff2 +0 -0
  311. package/dist/ui/assets/eb0994906efd969102d522b6d8b3aff2-i9SHU5CJ.woff2 +0 -0
  312. package/dist/ui/assets/eb32eee23ceadd3fc4eb5763d3573f57-B3h9kgf3.woff2 +0 -0
  313. package/dist/ui/assets/ebadbc005027fb3e2b1c2d15be38c66b-qVQSBwBu.woff2 +0 -0
  314. package/dist/ui/assets/ecbbb464ff6934b5012cf33c80c7a907-CBEPqZfS.woff2 +0 -0
  315. package/dist/ui/assets/ed79ac25c014b6434f22fe1e2549bc26-mWhz7hkU.woff2 +0 -0
  316. package/dist/ui/assets/eed64f40443554cb9f65edaca176abb0-Cqw29XTm.woff2 +0 -0
  317. package/dist/ui/assets/efc749471476cce09e12e7038c757950-BhSWPf1Q.woff2 +0 -0
  318. package/dist/ui/assets/f2a7c2cb375662aa93d8f31116d39c20-D1zsMYCW.woff2 +0 -0
  319. package/dist/ui/assets/f5ba0fb4332ccceadc481e47134023d3-CAKMPiu5.woff2 +0 -0
  320. package/dist/ui/assets/f6326eb5031e18478aa4588598f96965-Dj3uu55n.woff2 +0 -0
  321. package/dist/ui/assets/f8678cbaab9caef95e1986731dcbd92f-CnIuR3EN.woff2 +0 -0
  322. package/dist/ui/assets/f8d9d55e2a811e9839c9af5f8f2d62ac-DIUQRBsH.woff2 +0 -0
  323. package/dist/ui/assets/f98b117e363eb51f4d7c7d1056da7688-O9MtqjIW.woff2 +0 -0
  324. package/dist/ui/assets/f9ab5d9163148ceb211e5b4f15a3d8e0-Dtqse-AT.woff2 +0 -0
  325. package/dist/ui/assets/fa69ce224ff0a6c0ff97b10a829779c2-pX45dCNY.woff2 +0 -0
  326. package/dist/ui/assets/fc38d1da3930d39dd8e286f708105765-DmbOHwTI.woff2 +0 -0
  327. package/dist/ui/assets/fe03c1dfaffc1cad3521fe55f1541d6b-BW8aEqCj.woff2 +0 -0
  328. package/dist/ui/assets/fe0b2421eb4d1f4f8949c602871a317c-BsBLV6jH.woff2 +0 -0
  329. package/dist/ui/assets/fe9c75d0cb3d808023412d26dfc9d388-eXjcgUPI.woff2 +0 -0
  330. package/dist/ui/assets/index-B61V60w_.js +19 -0
  331. package/dist/ui/assets/index-bkyxJG8J.css +1 -0
  332. package/dist/ui/assets/theme-BNq4FkXS.js +1 -0
  333. package/dist/ui/index.html +52 -0
  334. package/dist/webui.js +21 -0
  335. package/docs/backend.md +260 -0
  336. package/docs/frontend.md +125 -0
  337. package/docs/remix-icon-license.txt +186 -0
  338. package/docs/usage.md +227 -0
  339. package/package.json +82 -0
@@ -0,0 +1,334 @@
1
+ import { isIP } from "node:net";
2
+ import YAML from "yaml";
3
+ import { ERROR_BODY_LIMIT, fetchWithRetry } from "./http.js";
4
+ export function buildDefaultConfig() {
5
+ return {
6
+ mode: "rule",
7
+ "log-level": "info",
8
+ ipv6: true,
9
+ proxies: [],
10
+ "proxy-groups": [{ name: "PROXY", type: "select", proxies: ["DIRECT"] }],
11
+ rules: ["MATCH,PROXY"],
12
+ };
13
+ }
14
+ export function isValidMihomoConfig(doc) {
15
+ if (typeof doc !== "object" || doc === null || Array.isArray(doc))
16
+ return false;
17
+ const rec = doc;
18
+ return (("proxies" in rec && Array.isArray(rec.proxies)) ||
19
+ ("proxy-providers" in rec &&
20
+ typeof rec["proxy-providers"] === "object" &&
21
+ rec["proxy-providers"] !== null &&
22
+ !Array.isArray(rec["proxy-providers"])) ||
23
+ ("rules" in rec && Array.isArray(rec.rules)));
24
+ }
25
+ function firstHeader(value) {
26
+ if (Array.isArray(value))
27
+ return value[0];
28
+ return value;
29
+ }
30
+ export function parseSubscriptionUserinfo(header) {
31
+ if (!header)
32
+ return undefined;
33
+ const nums = {};
34
+ const knownKeys = new Set(["upload", "download", "total", "expire"]);
35
+ for (const pair of header.split(";")) {
36
+ const [k, v] = pair.split("=", 2);
37
+ const key = k?.trim() ?? "";
38
+ if (!knownKeys.has(key))
39
+ continue;
40
+ const n = Number(v?.trim());
41
+ if (Number.isFinite(n) && n >= 0)
42
+ nums[key] = n;
43
+ }
44
+ if (nums.upload === undefined || nums.download === undefined || nums.total === undefined) {
45
+ return undefined;
46
+ }
47
+ return {
48
+ upload: nums.upload,
49
+ download: nums.download,
50
+ total: nums.total,
51
+ ...(nums.expire !== undefined && nums.expire > 0 ? { expire: nums.expire } : {}),
52
+ };
53
+ }
54
+ export function parseContentDispositionFilename(header) {
55
+ if (!header)
56
+ return undefined;
57
+ const ext = header.match(/filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/);
58
+ if (ext?.[1]) {
59
+ try {
60
+ const decoded = sanitizeFilename(decodeURIComponent(ext[1].trim()));
61
+ if (decoded)
62
+ return stripYamlExt(decoded);
63
+ }
64
+ catch {
65
+ }
66
+ }
67
+ const plain = header.match(/filename\s*=\s*"?([^";]+)"?/);
68
+ const value = plain?.[1] ? sanitizeFilename(plain[1]) : undefined;
69
+ return value ? stripYamlExt(value) : undefined;
70
+ }
71
+ const C0_OR_DEL = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCode(31)}${String.fromCharCode(127)}]`, "g");
72
+ function sanitizeFilename(name) {
73
+ return name.replace(C0_OR_DEL, "").trim();
74
+ }
75
+ function stripYamlExt(name) {
76
+ return name.replace(/\.(ya?ml)$/i, "");
77
+ }
78
+ function parseIntervalHours(header) {
79
+ if (!header)
80
+ return undefined;
81
+ const n = Number(header.trim());
82
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined;
83
+ }
84
+ export function parseSafeHttpUrl(value) {
85
+ if (!value?.trim())
86
+ return undefined;
87
+ try {
88
+ const parsed = new URL(value.trim());
89
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.href : undefined;
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ export const PROFILE_DOWNLOAD_SIZE_LIMIT = 8 * 1024 * 1024;
96
+ const MAX_SUBSCRIPTION_REDIRECTS = 5;
97
+ const PROFILE_FETCH_DEADLINE_MS = 30_000;
98
+ function isRestrictedIpv4Address(parts) {
99
+ const [a = 0, b = 0, c = 0] = parts;
100
+ return (a === 0 ||
101
+ a === 10 ||
102
+ a === 127 ||
103
+ (a === 100 && b >= 64 && b <= 127) ||
104
+ (a === 169 && b === 254) ||
105
+ (a === 172 && b >= 16 && b <= 31) ||
106
+ (a === 192 && b === 0 && (c === 0 || c === 2)) ||
107
+ (a === 192 && b === 88 && c === 99) ||
108
+ (a === 192 && b === 168) ||
109
+ (a === 198 && (b === 18 || b === 19)) ||
110
+ (a === 198 && b === 51 && c === 100) ||
111
+ (a === 203 && b === 0 && c === 113) ||
112
+ a >= 224);
113
+ }
114
+ function parseIpv6Bytes(host) {
115
+ const pieces = host.toLowerCase().split("::");
116
+ if (pieces.length > 2)
117
+ return undefined;
118
+ const parseSide = (side) => {
119
+ if (!side)
120
+ return [];
121
+ const words = [];
122
+ for (const token of side.split(":")) {
123
+ if (token.includes(".")) {
124
+ const ipv4 = token.split(".").map(Number);
125
+ if (ipv4.length !== 4 ||
126
+ ipv4.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
127
+ return undefined;
128
+ }
129
+ words.push(((ipv4[0] ?? 0) << 8) | (ipv4[1] ?? 0));
130
+ words.push(((ipv4[2] ?? 0) << 8) | (ipv4[3] ?? 0));
131
+ continue;
132
+ }
133
+ if (!/^[0-9a-f]{1,4}$/.test(token))
134
+ return undefined;
135
+ words.push(Number.parseInt(token, 16));
136
+ }
137
+ return words;
138
+ };
139
+ const left = parseSide(pieces[0] ?? "");
140
+ const right = parseSide(pieces[1] ?? "");
141
+ if (!left || !right)
142
+ return undefined;
143
+ const omitted = 8 - left.length - right.length;
144
+ if ((pieces.length === 1 && omitted !== 0) || (pieces.length === 2 && omitted < 1)) {
145
+ return undefined;
146
+ }
147
+ const words = [...left, ...Array.from({ length: omitted }, () => 0), ...right];
148
+ if (words.length !== 8)
149
+ return undefined;
150
+ const bytes = new Uint8Array(16);
151
+ for (let index = 0; index < words.length; index++) {
152
+ const word = words[index] ?? 0;
153
+ bytes[index * 2] = word >> 8;
154
+ bytes[index * 2 + 1] = word & 0xff;
155
+ }
156
+ return bytes;
157
+ }
158
+ function isRestrictedIpv6Address(host) {
159
+ const bytes = parseIpv6Bytes(host);
160
+ if (!bytes)
161
+ return true;
162
+ const allZero = bytes.every((byte) => byte === 0);
163
+ const loopback = bytes.slice(0, 15).every((byte) => byte === 0) && bytes[15] === 1;
164
+ if (allZero || loopback)
165
+ return true;
166
+ const compatiblePrefix = bytes.slice(0, 12).every((byte) => byte === 0);
167
+ const mappedPrefix = bytes.slice(0, 10).every((byte) => byte === 0) && bytes[10] === 0xff && bytes[11] === 0xff;
168
+ const translatedPrefix = bytes.slice(0, 8).every((byte) => byte === 0) &&
169
+ bytes[8] === 0xff &&
170
+ bytes[9] === 0xff &&
171
+ bytes[10] === 0 &&
172
+ bytes[11] === 0;
173
+ const wellKnownNat64Prefix = bytes[0] === 0 &&
174
+ bytes[1] === 0x64 &&
175
+ bytes[2] === 0xff &&
176
+ bytes[3] === 0x9b &&
177
+ bytes.slice(4, 12).every((byte) => byte === 0);
178
+ if (compatiblePrefix || mappedPrefix || translatedPrefix || wellKnownNat64Prefix) {
179
+ return isRestrictedIpv4Address(Array.from(bytes.slice(12)));
180
+ }
181
+ if (bytes[0] === 0x20 && bytes[1] === 0x02) {
182
+ return isRestrictedIpv4Address(Array.from(bytes.slice(2, 6)));
183
+ }
184
+ if (((bytes[0] ?? 0) & 0xfe) === 0xfc)
185
+ return true;
186
+ if (bytes[0] === 0xfe && ((bytes[1] ?? 0) & 0xc0) >= 0x80)
187
+ return true;
188
+ if (bytes[0] === 0xff)
189
+ return true;
190
+ if (bytes[0] === 0x01 && bytes.slice(1, 8).every((byte) => byte === 0))
191
+ return true;
192
+ if (bytes[0] === 0x20 && bytes[1] === 0x01 && bytes[2] === 0x0d && bytes[3] === 0xb8) {
193
+ return true;
194
+ }
195
+ return false;
196
+ }
197
+ function isRestrictedSubscriptionHost(hostname) {
198
+ const host = hostname
199
+ .toLowerCase()
200
+ .replace(/^\[|\]$/g, "")
201
+ .replace(/\.$/, "");
202
+ if (host === "localhost" || host.endsWith(".localhost"))
203
+ return true;
204
+ if (isIP(host) === 4) {
205
+ return isRestrictedIpv4Address(host.split(".").map(Number));
206
+ }
207
+ if (isIP(host) === 6)
208
+ return isRestrictedIpv6Address(host);
209
+ return false;
210
+ }
211
+ export function resolveSubscriptionRedirect(initial, current, location) {
212
+ const target = new URL(location, current);
213
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
214
+ throw new Error(`Refusing subscription redirect to non-http(s) URL: ${target.href}`);
215
+ }
216
+ if (current.protocol === "https:" && target.protocol === "http:") {
217
+ throw new Error(`Refusing HTTPS-to-HTTP subscription redirect: ${target.href}`);
218
+ }
219
+ const initialRestricted = isRestrictedSubscriptionHost(initial.hostname);
220
+ const targetRestricted = isRestrictedSubscriptionHost(target.hostname);
221
+ if (initialRestricted && target.origin !== initial.origin) {
222
+ throw new Error(`Refusing subscription redirect away from restricted origin: ${target.href}`);
223
+ }
224
+ if (!initialRestricted && targetRestricted) {
225
+ throw new Error(`Refusing subscription redirect to restricted host: ${target.hostname}`);
226
+ }
227
+ return target;
228
+ }
229
+ export async function fetchSubscriptionProfile(url) {
230
+ let initial;
231
+ try {
232
+ initial = new URL(url);
233
+ }
234
+ catch {
235
+ throw new Error(`Invalid subscription URL: ${url}`);
236
+ }
237
+ if (initial.protocol !== "https:" && initial.protocol !== "http:") {
238
+ throw new Error(`Subscription URL must be http(s): ${url}`);
239
+ }
240
+ const deadlineAt = Date.now() + PROFILE_FETCH_DEADLINE_MS;
241
+ let current = initial;
242
+ let res;
243
+ for (let redirects = 0;; redirects += 1) {
244
+ const remainingDeadlineMs = deadlineAt - Date.now();
245
+ if (remainingDeadlineMs <= 0) {
246
+ throw new Error(`Subscription fetch deadline exceeded after ${PROFILE_FETCH_DEADLINE_MS}ms`);
247
+ }
248
+ res = await fetchWithRetry(current.href, {
249
+ attempts: 3,
250
+ deadlineMs: remainingDeadlineMs,
251
+ manualRedirect: true,
252
+ headers: { "user-agent": "clash.meta; mihomo; sash" },
253
+ });
254
+ if (res.statusCode < 300 || res.statusCode >= 400)
255
+ break;
256
+ const locationHeader = firstHeader(res.headers.location);
257
+ await res.discard();
258
+ if (!locationHeader)
259
+ throw new Error(`Subscription fetch failed: HTTP ${res.statusCode}`);
260
+ if (redirects >= MAX_SUBSCRIPTION_REDIRECTS) {
261
+ throw new Error(`Too many subscription redirects (>${MAX_SUBSCRIPTION_REDIRECTS})`);
262
+ }
263
+ current = resolveSubscriptionRedirect(initial, current, locationHeader);
264
+ }
265
+ if (res.statusCode !== 200) {
266
+ await res.text(ERROR_BODY_LIMIT);
267
+ throw new Error(`Subscription fetch failed: HTTP ${res.statusCode}`);
268
+ }
269
+ const text = await res.text(PROFILE_DOWNLOAD_SIZE_LIMIT);
270
+ let doc;
271
+ try {
272
+ doc = YAML.parse(text);
273
+ }
274
+ catch (err) {
275
+ throw new Error(`Subscription is not valid YAML (Clash/mihomo format required): ${err.message}`);
276
+ }
277
+ if (!isValidMihomoConfig(doc)) {
278
+ throw new Error("Subscription content is not a Clash/mihomo config (missing proxies/rules). " +
279
+ "Convert non-Clash subscriptions with a subconverter first.");
280
+ }
281
+ return {
282
+ doc,
283
+ yamlText: text,
284
+ name: parseContentDispositionFilename(firstHeader(res.headers["content-disposition"])),
285
+ subInfo: parseSubscriptionUserinfo(firstHeader(res.headers["subscription-userinfo"])),
286
+ homePage: parseSafeHttpUrl(firstHeader(res.headers["profile-web-page-url"])),
287
+ intervalHours: parseIntervalHours(firstHeader(res.headers["profile-update-interval"])),
288
+ };
289
+ }
290
+ const MANAGED_KEYS = new Set([
291
+ "mixed-port",
292
+ "port",
293
+ "socks-port",
294
+ "redir-port",
295
+ "tproxy-port",
296
+ "authentication",
297
+ "external-controller",
298
+ "external-controller-tls",
299
+ "external-ui",
300
+ "external-ui-url",
301
+ "external-ui-name",
302
+ "secret",
303
+ "tun",
304
+ "allow-lan",
305
+ ]);
306
+ export function stripManagedKeys(base) {
307
+ return Object.fromEntries(Object.entries(base).filter(([key]) => !MANAGED_KEYS.has(key)));
308
+ }
309
+ export function overlayManagedKeys(base, settings) {
310
+ const out = stripManagedKeys(base);
311
+ out["mixed-port"] = settings.mixedPort;
312
+ out["allow-lan"] = settings.allowLan;
313
+ out["external-controller"] = settings.controller;
314
+ out.secret = settings.secret;
315
+ if (settings.tun) {
316
+ out.tun = {
317
+ enable: true,
318
+ stack: "mixed",
319
+ "auto-route": true,
320
+ "auto-detect-interface": true,
321
+ "dns-hijack": ["any:53"],
322
+ };
323
+ }
324
+ return out;
325
+ }
326
+ export function renderConfig(base, settings, source) {
327
+ const merged = overlayManagedKeys(base, settings);
328
+ const proxies = merged.proxies;
329
+ return {
330
+ yaml: YAML.stringify(merged, { indent: 2 }),
331
+ proxyCount: Array.isArray(proxies) ? proxies.length : 0,
332
+ source,
333
+ };
334
+ }
@@ -0,0 +1,8 @@
1
+ const REQUIRED_NODE_MAJOR = 24;
2
+ const major = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
3
+ if (Number.isNaN(major) || major < REQUIRED_NODE_MAJOR) {
4
+ console.error(`Sash requires Node.js ${REQUIRED_NODE_MAJOR} or newer (found ${process.versions.node}).`);
5
+ console.error("Please upgrade Node.js: https://nodejs.org/");
6
+ process.exit(1);
7
+ }
8
+ export {};
@@ -0,0 +1,58 @@
1
+ import fs from "node:fs";
2
+ import { assertCoreInstallationConsistent } from "./core.js";
3
+ import { recoverCoreInstallTransaction } from "./core-install-transaction.js";
4
+ import { recoverCoordinatedCoreUpdate } from "./core-update-coordination.js";
5
+ import { evaluateDaemon } from "./daemon-lifecycle.js";
6
+ import { readManagedStateTransactionStatus, recoverManagedStateTransaction, } from "./managed-state-transaction.js";
7
+ import { isProcessAlive, readPidRecord } from "./process.js";
8
+ import { migrateProfileState } from "./profile-migration.js";
9
+ import { reconcileOrphanedRuntime } from "./runtime-recovery.js";
10
+ import { loadSettings } from "./settings.js";
11
+ import { withStateLock } from "./state-lock.js";
12
+ export async function runOfflineMutation(ctx, purpose, action, options = {}) {
13
+ return withStateLock(ctx.layout.mutationLockFile, { purpose, timeoutMs: 30_000 }, async () => {
14
+ ctx.settings = loadSettings(ctx.layout);
15
+ const daemon = await evaluateDaemon(ctx.layout, ctx.settings);
16
+ if (daemon.kind !== "stopped") {
17
+ const owner = daemon.pid ? ` (PID=${daemon.pid})` : "";
18
+ throw new Error(`sashd owns the data directory${owner} but is not accepting this operation; stop or recover it before retrying`);
19
+ }
20
+ const reconciliation = options.reconcileRuntime;
21
+ if (!reconciliation) {
22
+ const core = readPidRecord(ctx.layout.pidFile);
23
+ if (!core && fs.existsSync(ctx.layout.pidFile)) {
24
+ throw new Error(`Core PID record is corrupt: ${ctx.layout.pidFile}; reconcile it before modifying state`);
25
+ }
26
+ if (core && isProcessAlive(core.pid)) {
27
+ throw new Error(`Core PID ${core.pid} is still alive without sashd; run \`sash stop\` to reconcile it before modifying state`);
28
+ }
29
+ }
30
+ recoverCoreInstallTransaction(ctx.layout);
31
+ if (reconciliation) {
32
+ const managed = readManagedStateTransactionStatus(ctx.layout);
33
+ if (managed?.coordination !== "core-update") {
34
+ recoverManagedStateTransaction(ctx.layout);
35
+ }
36
+ ctx.settings = loadSettings(ctx.layout);
37
+ await reconcileOrphanedRuntime({
38
+ layout: ctx.layout,
39
+ settings: ctx.settings,
40
+ ...(reconciliation.legacyDaemon ? { legacyDaemon: reconciliation.legacyDaemon } : {}),
41
+ ...(reconciliation.verifyControllerVacant
42
+ ? { verifyControllerVacant: reconciliation.verifyControllerVacant }
43
+ : {}),
44
+ }, reconciliation.deps);
45
+ }
46
+ else {
47
+ recoverCoordinatedCoreUpdate(ctx.layout);
48
+ if (options.allowInconsistentCore !== "force-update") {
49
+ assertCoreInstallationConsistent(ctx.layout);
50
+ }
51
+ }
52
+ ctx.settings = loadSettings(ctx.layout);
53
+ if (options.migrateProfiles) {
54
+ await migrateProfileState(ctx.settings, ctx.layout);
55
+ }
56
+ return action();
57
+ });
58
+ }
package/dist/paths.js ADDED
@@ -0,0 +1,57 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ function envPathOr(fallback, value) {
4
+ const trimmed = value?.trim();
5
+ return trimmed && path.isAbsolute(trimmed) ? trimmed : fallback;
6
+ }
7
+ export function sashRoot() {
8
+ const override = process.env.SASH_HOME?.trim();
9
+ if (override) {
10
+ if (!path.isAbsolute(override)) {
11
+ throw new Error(`SASH_HOME must be an absolute path, got: ${override}`);
12
+ }
13
+ return override;
14
+ }
15
+ if (process.platform === "win32") {
16
+ const base = envPathOr(path.join(os.homedir(), "AppData", "Local"), process.env.LOCALAPPDATA);
17
+ return path.join(base, "Sash");
18
+ }
19
+ if (process.platform === "darwin") {
20
+ return path.join(os.homedir(), "Library", "Application Support", "Sash");
21
+ }
22
+ const xdgData = envPathOr(path.join(os.homedir(), ".local", "share"), process.env.XDG_DATA_HOME);
23
+ return path.join(xdgData, "sash");
24
+ }
25
+ export function sashLayout(root = sashRoot()) {
26
+ const exeName = process.platform === "win32" ? "mihomo.exe" : "mihomo";
27
+ return {
28
+ root,
29
+ binDir: path.join(root, "bin"),
30
+ coreExe: path.join(root, "bin", exeName),
31
+ configFile: path.join(root, "config.yaml"),
32
+ settingsFile: path.join(root, "sash.json"),
33
+ profilesDir: path.join(root, "profiles"),
34
+ profilesIndexFile: path.join(root, "profiles", "index.json"),
35
+ uiDir: path.join(root, "ui"),
36
+ stateDir: path.join(root, "state"),
37
+ pidFile: path.join(root, "state", "sash.pid"),
38
+ daemonPidFile: path.join(root, "state", "sashd.pid"),
39
+ daemonLeaseFile: path.join(root, "state", "sashd.lock"),
40
+ daemonStartLockFile: path.join(root, "state", "sashd-start.lock"),
41
+ runtimeOperationLockFile: path.join(root, "state", "runtime.lock"),
42
+ mutationLockFile: path.join(root, "state", "mutation.lock"),
43
+ settingsLockFile: path.join(root, "state", "settings.lock"),
44
+ systemProxyStateFile: path.join(root, "state", "system-proxy.json"),
45
+ managedStateTransactionFile: path.join(root, "state", "managed-state-transaction.json"),
46
+ installFile: path.join(root, "state", "install.json"),
47
+ coreInstallTransactionFile: path.join(root, "state", "core-install-transaction.json"),
48
+ coreUpdateTransactionFile: path.join(root, "state", "core-update-transaction.json"),
49
+ logsDir: path.join(root, "logs"),
50
+ coreLogFile: path.join(root, "logs", "mihomo.log"),
51
+ coreErrLogFile: path.join(root, "logs", "mihomo.err.log"),
52
+ sashLogFile: path.join(root, "logs", "sash.log"),
53
+ daemonLogFile: path.join(root, "logs", "sashd.log"),
54
+ daemonErrLogFile: path.join(root, "logs", "sashd.err.log"),
55
+ tempDir: path.join(root, "temp"),
56
+ };
57
+ }