@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,184 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2026-09-05
8
+
9
+ ### Added
10
+
11
+ - The daemon HTTP API now has a shared browser-safe `SashClient` (`src/sash-client.ts`) covering the whole `/sash/*` surface with strict contract parsers; the CLI daemon client and the WebUI both build on it.
12
+ - The Settings page now has an "Edit settings file" button (top right, next to the header) that opens the full `sash.json` in a CodeMirror JSON editor. Saves are validated strictly — invalid JSON, unknown fields, port conflicts and non-loopback controllers are rejected without touching the disk — and changed managed keys are applied through the existing settings transaction machinery (core restart included where required). `daemonSecret` changes hot-swap immediately; `daemonPort` changes are persisted and the UI reminds the user that they take effect after a manual `sash restart`.
13
+
14
+ - Profiles can be renamed from the WebUI: a pencil button on each profile card opens a rename dialog backed by the new `PATCH /sash/profiles/:id` daemon endpoint. The rename touches only the profiles index (`profiles/index.json`) — the YAML file name is the timestamp id and the name never enters the generated core config, so no core reload is needed. Subscription updates keep the user-chosen name (matching Clash Verge Rev's behavior). The profile content editor button now uses a `</>` icon to distinguish it from rename.
15
+
16
+ - Cross-platform CLI lifecycle for installing, starting, stopping, restarting, inspecting and logging the managed runtime.
17
+ - First-run Core bootstrap, transactional Core updates, npm self-upgrade command and managed configuration commands.
18
+ - Platform-conventional private data directories with an absolute `SASH_HOME` override.
19
+ - Non-detached Core supervision with PID identity verification and conservative, fail-closed termination.
20
+ - Built-in Vue 3 dashboard is bundled in the npm package under `dist/ui/`, with no runtime dashboard download.
21
+ - WebUI includes Chinese and English localization with a Settings-page language switcher.
22
+ - Running `sash` with no arguments prints the same output as `sash status`.
23
+ - The system proxy is toggled from the dashboard or `PATCH /sash/settings`; there is no separate `sash proxy` command.
24
+ - Immutable `SettingsService` coordinates shared daemon/offline settings candidates, config validation and durable settings/config publication.
25
+ - Local profile library under `<root>/profiles/`: one timestamp-named YAML file per profile plus `index.json` metadata, active selection, provider update interval, quota/expiry data and persisted update errors.
26
+ - WebUI Profiles page with URL download, clipboard paste, local YAML import, Update All, profile cards, active selection, quota display and delete confirmation.
27
+ - Profile daemon API: list/add/import/activate/update/update-all/delete endpoints and scheduled due-profile refresh.
28
+ - Shared daemon/profile API contracts used by the server client and WebUI.
29
+ - State-changing daemon requests now accept the persistent CLI bearer or a per-boot WebUI token, with loopback Host validation.
30
+ - WebUI store/confirm regression tests and a minimal happy-dom Vue mount behavior harness are included in the normal Node test runner.
31
+ - Exact generated configurations are validated by the installed Core in an isolated temporary file before profile/config state is committed.
32
+ - One-time, crash-recoverable import of a qualifying pre-profile `config.yaml` into an active local profile, with conservative default-config detection and fail-closed validation.
33
+ - Versioned `sash.json` runtime schema with strict field validation and explicit v0 migration.
34
+ - Atomic daemon/start/runtime/mutation/settings leases for single-instance and cross-process state ownership.
35
+ - Durable first-install Core publication journal with publishing/committed crash recovery.
36
+ - Durable Core update journal recording previous/target install records and prepared/swapped/health-verified phases, including deferred health validation for stopped runtimes.
37
+ - Authenticated maintenance shutdown API returning an atomic Core-running snapshot for executable updates.
38
+ - Durable system-proxy ownership journal that snapshots and conditionally restores prior manual/PAC state.
39
+ - Windows/macOS/Linux and Node.js 24 CI matrix covering lint, tests, builds and actual tarball pack/install/CLI/UI smoke.
40
+ - Unified third-party notices for bundled Vue/Remix Icon assets and the separately downloaded runtime Core.
41
+
42
+ ### Changed
43
+
44
+ - Daemon HTTP API reorganized into three namespaces: `/sash/*` is implemented by sashd, `/core/api/*` is the authenticated reverse proxy to the Core controller, and `/ui/*` serves the dashboard. Root-level aliases and the `/core/start|stop|restart|config/reload` paths are removed: Core lifecycle moved to `/sash/core/*`, health/status to `/sash/daemon/*`, and the two shutdown endpoints merged into `POST /sash/daemon/shutdown` returning `{ coreWasRunning }`. Success bodies no longer carry `ok` envelopes (empty successes answer 204), errors use a unified `{ error: { code, message } }` envelope with a fixed code set (`invalid_input`, `not_found`, `conflict`, `core_unhealthy`, `shutting_down`, `unauthorized`, `http`, `internal`), and 405 responses derive `Allow` from the route table.
45
+ - `PATCH /sash/settings` now takes a typed partial object (`{ mixedPort?, allowLan?, tun?, systemProxy?, daemonPort?, daemonSecret? }`) instead of string key/value pairs, and the dedicated `POST /sash/proxy/enable|disable` endpoints are gone — the system proxy is toggled through the same settings transaction, with the Core health check enforced inside `SettingsService.apply()` and reported as `409 core_unhealthy`.
46
+ - Any request carrying a non-loopback `Origin` header is rejected with `403 unauthorized`, not only mutations; WebSocket upgrades already behaved this way.
47
+ - WebUI page headers are unified: every page now uses the shared `PageHeader` component — an 80px bar matching the sidebar traffic panel whose bottom rule spans the full content width via a `--page-gutter` CSS variable. The Logs and Connections pages migrated from their bespoke toolbars; page-specific controls (search, filters, buttons) go through the component's actions slot. The mixed-port input no longer right-aligns its value.
48
+ - WebUI typography now embeds a bundled WOFF2 font (declared via `@font-face` with system fallbacks) and uses a coarser 12/14/16/18/20/24/26px size scale for readability.
49
+ - The embedded LXGW WenKai Lite font is now split at build time into 232 unicode-range WOFF2 chunks (via `cn-font-split`, wired in as a Vite plugin): the browser downloads only the glyph slices a page actually renders (~470 KB on first load instead of the full 5.1 MB single file), which eliminates the visible fallback-to-LXGW font swap on dashboard open. Fingerprinted assets under `/ui/assets/` are served with `Cache-Control: public, max-age=31536000, immutable`; other static files get a bounded one-hour cache.
50
+ - WebUI first-load JS shrank from ~629 KB to ~187 KB: non-default views load as async route chunks and the CodeMirror editor stack (~416 KB) only loads when a code editor dialog opens. Static UI responses now carry `Content-Length`.
51
+ - WebUI rendering overhead is reduced across the board: the dashboard store is shallowly reactive with replacement-based updates; the log stream batches WebSocket frames (~100 ms) instead of pushing per line; connection polling and the log WebSocket only run while their pages are open; proxy group delay tests commit in a single store write; collapsed proxy groups unmount their node cards; and the rules table paginates (80 rows per page) through a shared pager component extracted from the connections page. The sidebar clock pauses in background tabs.
52
+ - WebUI consistency cleanup: dead locale keys removed, duration/relative-time strings and confirm-dialog defaults moved into the locale files, hardcoded colors and z-index values replaced by design tokens, and connection-close/profile-rename calls now go through store actions instead of views calling the API directly.
53
+ - WebUI accent color is now an indigo-violet scale (light `#5558dd`, dark `#8f91f3`) replacing the previous teal; the traffic chart's upload series is warm orange to stay distinguishable, and `scripts/contrast-check.mjs` tracks the new palette.
54
+ - Proxy node cards are fixed-width (240px) and wrap instead of stretching, and the desktop Overview page scrolls its general and proxy panes independently.
55
+ - WebUI now uses a classic compact desktop-console shell with a 25px title strip, 170px live-traffic sidebar, recessed active navigation, dense profile/proxy rows, stream-style logs, tagged connection rows and grouped settings. The intentional combined General + Proxies Overview workspace and responsive bottom navigation are preserved.
56
+ - `sash restart` now restarts the whole runtime: the daemon exits through its serialized maintenance shutdown boundary and a freshly spawned daemon (running the installed code) starts the Core. Core-only restarts remain available from the dashboard. The daemon/Core maintenance-shutdown orchestration is shared between restart and Core updates.
57
+ - TUN privilege guidance now points at an elevated full `sash restart` instead of a stop/start sequence, since an elevated restart replaces the unprivileged daemon.
58
+ - npm installation is documented as the primary install path alongside the source-checkout `npm link` flow, and the `0.1.0` history is consolidated in the changelog.
59
+ - amd64 Core downloads now prefer the upstream broadly compatible x86-64 build, with v1 and the x86-64-v3 plain asset used only as availability fallbacks.
60
+ - WebUI functional iconography now uses tree-shaken Remix Icon line components behind the existing semantic icon API.
61
+ - Built-in WebUI now uses a flat, neutral Light/Dark console with a wide text navigation sidebar, compact settings and data rows, accessible controls, responsive mobile navigation, and a two-column Overview workspace that keeps common controls beside its mode-driven proxy panel.
62
+ - Repeated `sash start` requests now always enter the daemon lifecycle reconciler, so desired runtime and system-proxy state are refreshed even when a Core is already running.
63
+ - Shutdown acknowledges success only after Core/proxy cleanup; listener closure follows the response, while failed cleanup keeps the daemon scheduler available for retry.
64
+ - Log tailing and follow-mode growth reads use bounded 64 KiB chunks instead of whole-file or whole-delta allocations.
65
+ - The internal Core controller address is restricted to loopback hosts so its bearer is never sent to a remote endpoint.
66
+ - HTTP requests use absolute deadlines, bounded body ownership and method-aware retry defaults; state-changing requests are not retried unless explicitly requested.
67
+ - Remote profile redirects are validated hop-by-hop, reject HTTPS downgrade and cannot cross from public origins into literal private/loopback targets.
68
+ - WebUI navigation is Overview, Profiles, Logs, Connections, Rules, Settings; the legacy `#/subscription` hash redirects to `#/profiles`.
69
+ - Profile behavior is owned by a single `ProfileService` used by daemon routes and offline CLI commands. Config activation/update transitions snapshot and restore prior state on failure.
70
+ - Remote profile refreshes use in-flight deduplication, bounded network concurrency and serialized state commits.
71
+ - The legacy `subscriptionUrl` setting migrates once into the profile index and is then removed instead of remaining as a second source of truth; it takes priority over unmanaged-config import.
72
+ - Runtime config generation now has only two canonical inputs: the active profile or the built-in DIRECT-only default. The dead existing-`config.yaml` fallback pipeline was removed.
73
+ - Every Core start recompiles and validates `config.yaml` from the active profile and current settings. Remote profile responses are limited to 8 MiB.
74
+ - The default mixed port is consistently `7890`. Installed core version is read from `state/install.json` instead of duplicated in settings.
75
+ - Core updates download and validate the staged binary before stopping the existing runtime, and commit install metadata only after health checks pass.
76
+ - Overview proxy groups share a reusable component; WebUI runtime refresh, profile mutations, mode/proxy intent and polling are organized behind a stable store facade with one reactive state source and per-domain generations. The Overview page now composes focused general/proxy panes, while latency requests own their busy sets and captured runtime generation in one composable.
77
+ - Daemon integration tests and WebUI store tests are split by domain. Shared daemon fixtures have no top-level I/O and retain centralized instance, socket, scheduler and temporary-directory cleanup.
78
+ - Daemon status exposes a per-boot monotonic profile revision so scheduled profile publications trigger one coherent WebUI runtime refresh without adding heavy requests to every poll.
79
+ - `npm test` now runs server TypeScript, `vue-tsc`, backend tests and WebUI tests. WebUI TypeScript is included in Biome checks.
80
+ - The minimum runtime is Node.js 24 across package metadata, the early CLI guard, documentation and CI. Vite continues to use its Node API; unused archive/version dependencies were removed, and published backend source maps are disabled.
81
+ - Core/proxy transitions now pass through one serialized runtime lifecycle; proxy restoration precedes deliberate Core shutdown and readiness precedes proxy apply.
82
+ - Offline mutations reload committed settings under lock and refuse uncertain daemon/orphan-Core ownership.
83
+ - Core release mirrors are transport-only: official GitHub metadata selects the release and supplies the mandatory SHA-256 digest.
84
+ - Core updates stage outside runtime ownership, then hold runtime ownership across the daemon's atomic maintenance snapshot, offline publication and runtime restoration. A second controller-vacancy check runs inside the final mutation boundary.
85
+ - Core updates retain managed profile/config rollback snapshots until the binary outcome is durable, and runtime restoration re-reads final settings while using the healthy daemon's observed port.
86
+ - CLI commands now distinguish healthy, confirmed-offline and unresponsive runtime owners. Daemon clients, status endpoints, dashboard URLs and lifecycle output use the healthy daemon's observed port, while runtime/proxy status share one local system-proxy fallback and deduplicated error aggregation path.
87
+ - Core ZIP extraction accepts only the expected upstream executable basename; npm self-upgrade versions are restricted to strict semver or safe dist-tags.
88
+ - Core updates temporarily stop the daemon, validate the staged binary/config and recover interrupted `.bak` states before publication.
89
+ - System-proxy backends preserve manual, automatic/PAC and authentication-mode fields they modify; Linux automation is explicitly GNOME `gsettings` only.
90
+ - System-proxy capture, apply, recovery and inspection now use asynchronous child processes behind one operation queue. Same-generation reads share in-flight work, normal polling uses a short cache, and `?fresh=1` bypasses only settled cache entries.
91
+ - Daemon clients and the WebUI now parse successful health, status and proxy responses from `unknown`; internal proxy observation flags are required, while missing flags from legacy daemons are normalized only at the network boundary.
92
+ - Profile and settings preparation now exposes one-shot opaque publication capabilities instead of mutable prepared-state objects or positional commit booleans. Weak settings-source snapshots and strict profile snapshots retain their distinct conflict semantics.
93
+ - Daemon HTTP and WebSocket routing now share one origin-form request-target parser and explicit route table. Known method mismatches return `405` with `Allow`, dashboard redirects preserve root queries, and WebSocket streams are explicitly GET-only.
94
+ - npm packages now include `docs/`, lint is part of `prepublishOnly`, and package self-upgrade requires the daemon to be stopped so runtime/schema versions cannot overlap.
95
+ - The CLI is trimmed to lifecycle and diagnostics: `start`, `stop`, `restart`, `status`, `logs`, `web`, `update`, `upgrade` and `version`. Profile management, the system-proxy toggle and runtime settings moved to the web dashboard exclusively; the `sub`, `proxy` and `config` command groups were removed along with the offline-mutation paths that only served them. The `controller` and `secret` keys are now edited directly in `sash.json`.
96
+
97
+ ### Fixed
98
+
99
+ - Package smoke verification now recurses into nested UI asset directories such as `dist/ui/assets/branding/` instead of asserting every top-level asset entry is a regular file.
100
+
101
+ - Proxy node delay buttons show only a spinner while testing (no "Loading…" text), and profile cards no longer wrap their meta line mid-phrase: the action buttons now overlay the card's top-right corner and the source/updated line is single-line with ellipsis plus a full-text tooltip, so it gets the whole card width.
102
+ - Profile card action buttons (rename/edit/update/delete) no longer also activate the profile through click bubbling; activation is restricted to the card's main body.
103
+ - All four WebUI dialogs now share a focus trap, Escape handling, focus return to the trigger, and a reference-counted scroll lock (previously nested dialogs could unlock page scrolling early). The two duplicated CodeMirror editor implementations are merged into a shared `CodeEditorModal` component.
104
+ - The in-app profile editor's content endpoints (`GET/PUT /sash/profiles/:id/content`) are now registered in the daemon routing table; previously the handler existed but the router returned 404 before the request reached it.
105
+
106
+ - WebUI polish: redundant RULE/current badges removed from the proxy pane and profile/node cards (the selection stripe already marks them), the group latency-test icon spins a loader instead of the lightning bolt, the Rules table is centered with column dividers and scrolls instead of paginating, and scrollbars are thin overlays that fade in while scrolling and hide when idle (including Firefox via `scrollbar-color`).
107
+ - Overview general pane: the identity header now carries version and PID, the mode buttons drop their heading, and system-proxy/LAN/TUN switches are a second row of mode-style toggle buttons (the TUN state text keeps its semantics); the port number input hides its native spinners, and the Overview restart button uses the same danger-outline warning style as Settings.
108
+ - The dark-theme Overview title now uses its intended light color: the scoped `:global()` override was being miscompiled, so `--general-title` moved into the shared theme variable blocks.
109
+ - WebUI polish: the off-state switch knob is neutral instead of red (state is now carried by the track color), Connections page pause/close buttons and connection tags use theme variables that adapt to the dark theme, the Logs page subtitle is localized, and small screens keep profile-card actions and the theme/language selectors in compact horizontal rows.
110
+ - Successful daemon health, status and proxy responses are now runtime-validated before CLI or WebUI state changes; malformed `200` payloads fail closed, and a failed WebUI initialization clears any stale per-boot session token.
111
+ - Slow Windows, macOS and GNOME proxy commands no longer block the daemon event loop. Health requests remain responsive while asynchronous status/proxy inspection is pending, and platform writes preserve their safety order.
112
+ - State-lock acquisition now treats disappearance between `lstat` and record read as a retryable missing observation instead of false corruption, with one shared decision path for synchronous and asynchronous callers.
113
+ - Core gateway routing no longer derives authentication from a parsed path but forwarding from a raw suffix. Query-only namespace roots, dot segments, encoded path data and WebSocket targets now use the same canonical representation without duplicated queries.
114
+ - Core and daemon child logs now use one private append-only descriptor helper that rejects non-regular paths, enforces POSIX `0600`, closes partial opens and cannot leak descriptors when spawn setup throws. Startup diagnostics include only bounded errors appended by the current attempt.
115
+ - Windows system-proxy ownership no longer fails on real `reg query` responses: the strict parser now accepts the flush-left subkey listings that follow a whole-key query, while still rejecting unrelated or malformed output.
116
+ - Windows system-proxy enable/restore no longer writes or verifies the legacy flat `AutoDetect` value. Windows rewrites it from the `DefaultConnectionSettings` blob on WinINet refreshes, so managing it made every enable fail verification and roll back; it is now observed but unmanaged, and excluded from ownership equivalence. PAC (`AutoConfigURL`) handling is unchanged.
117
+ - WebUI text and control contrast now meets WCAG AA in both themes: light-theme accent, selection, success, muted, info, danger and chart colors are deepened (white-on-accent text was ~3:1), dark-theme accent surfaces use dark text, and the off-state switch track keeps a >= 3:1 boundary against app backgrounds; `scripts/contrast-check.mjs` recomputes the ratios.
118
+ - The Logs page sizes its panel with flexbox instead of viewport arithmetic, eliminating the nested double scrollbar, and scrolls to the latest entry when the page opens.
119
+ - Proxy node cards no longer clip keyboard focus outlines, the profile quota bar exposes an accessible name, dialog/toast buttons declare `type="button"`, and network settings controls are disabled while the daemon is offline.
120
+ - WebUI core version display, TUN status badge and restart-with-confirmation logic now live in one shared composable instead of being copied across views; unused icons, types and exports were removed.
121
+ - Test subprocesses force loopback into `NO_PROXY`, preventing local HTTP fixtures from traversing a developer's configured proxy or live runtime.
122
+ - Package verification now rejects empty/missing UI output and forbidden source/test/user/secret/binary paths, installs the actual tarball, exercises its bin shims and resolves its installed UI; Windows force-termination identity revalidation is covered through injected signals instead of being skipped.
123
+ - `sash logs -f` now waits for delayed files, follows bounded appends across truncation and identity-changing rotation, and cleans up watchers/timers on SIGINT or SIGTERM; `-n` rejects non-canonical, fractional, prefixed and overflowing values.
124
+ - CLI status now preserves unknown daemon/Core/proxy/TUN values as `null`, exposes a versioned JSON observation contract, uses exit code 2 for incomplete reads, avoids success output for unresponsive daemons and limits TUN privilege guidance to verified runtime states; proxy status separates desired, daemon-applied and OS-observed values.
125
+ - WebUI now keeps daemon reachability, profile revisions and Core snapshot ownership independent: same-owner Core API failures preserve and mark stale data, new owners clear stale snapshots, stopped profile revisions still refresh, malformed stream frames are dropped and mixed-port drafts can be reverted/reset against committed settings.
126
+ - Release downloads now enforce one absolute budget across mirrors, redirects, headers and streaming bodies; continuously dripping responses cannot keep updates alive indefinitely.
127
+ - Core config reload now sends `force=true` in the upstream query contract instead of an ignored JSON field.
128
+ - Subscription redirect classification now handles IPv4-compatible/mapped/translated, NAT64, 6to4, ULA, link/site-local, multicast and documentation IPv6 ranges without overblocking unrelated public IPv4 `/16`s.
129
+ - OS proxy/process-inspection helpers now share the scrubbed child environment; Windows/macOS system tools use trusted absolute paths and Linux helper lookup ignores relative PATH entries.
130
+ - Durable rename/remove operations retry Windows sharing violations without deleting caller-owned sources, and startup restores an interrupted `.unlock-probe` or fails closed while preserving conflicting files.
131
+ - Profile and settings publications now recheck exact stored-content SHA-256 snapshots under the commit boundary, retry one settings preparation conflict, and reject stale fetches/errors instead of overwriting newer profile content.
132
+ - Profile/index reads now require bounded regular files, remote update intervals are bounded, and ID allocation avoids both metadata entries and orphan YAML files.
133
+ - Daemon JSON endpoints now reject malformed or non-object bodies with 400, oversized bodies with 413, and aborted streams without leaking TypeErrors or leaving parsers pending.
134
+ - Dashboard responses now deny framing through CSP and X-Frame-Options, preventing cross-origin UI redressing from driving authenticated local controls.
135
+ - WebSocket proxy ownership now starts before the upstream handshake, so a client disconnect aborts the pending Core request and transport instead of leaving an idle upstream stream.
136
+ - All HTTP requests entering the Core controller gateway now require daemon authentication, including GET/HEAD/OPTIONS, and browser mutations reject non-loopback Origins.
137
+ - Malformed HTTP and WebSocket request targets are rejected inside explicit daemon error boundaries instead of terminating sashd through an unhandled rejection.
138
+ - TUN state now reflects the Core's actual runtime `tun.enable`: online activation that remains inactive or unverified rolls settings/config/runtime back, while CLI and WebUI distinguish desired, active, inactive and unverified states and explain how to restart the whole Sash daemon with elevated privileges on each platform.
139
+ - WebUI normalizes an empty Core connection snapshot so Overview renders correctly with zero active connections.
140
+ - Controller status and system-proxy transitions now detect Core exit/replacement across asynchronous probes and release a just-applied proxy binding when ownership is lost.
141
+ - CLI stop now fails when daemon shutdown cannot be safely verified, and daemon-client shutdown errors are no longer discarded.
142
+ - Settings updates use immutable candidates and one shared online/offline service; validation rejects blank/control-character secrets and listener-port collisions, while failed runtime transitions restore prior settings/config/runtime state.
143
+ - Settings, Profile YAML, index and generated config publish through one fixed-role durable transaction; failed activations, missing-profile fetches, updates and deletes compensate immediately, while interrupted publication recovers on daemon or offline initialization.
144
+ - Profile request parsing, fetch, rendering and Core validation now run outside the short mutation lock; daemon and offline commits recheck profile identity/selection under the lock before publication.
145
+ - Profile index loading now rejects duplicate IDs, non-plain roots and unexpected root fields.
146
+ - Invalid unmanaged `config.yaml` migration candidates are left untouched and block migration instead of being silently replaced.
147
+ - System-proxy backends are split into focused platform modules; macOS empty fields and malformed Windows registry output now fail safely.
148
+ - System-proxy recovery persists a `restoring` phase so partial multi-field restoration can continue after a crash.
149
+ - Atomic state writes fsync the parent directory on POSIX after publication.
150
+ - Process termination now revalidates ownership before both graceful and force signals, and daemon shutdown verifies the current boot token in every ownership mode.
151
+ - WebUI traffic/log WebSockets now complete browser subprotocol negotiation while keeping private authentication protocols away from the Core.
152
+ - Release downloads now require the production host allowlist for both initial URLs and every redirect target.
153
+ - Failed Core updates restore both the previous binary and install record; updates performed while Core is stopped retain `.bak` until the next managed start passes health/version checks, then commit or roll back deterministically.
154
+ - Activating a missing/invalid local profile no longer silently keeps the previous config, and reload failures restore the previous active/config state.
155
+ - Changing `mixed-port` while system proxy is enabled now disables the old binding during restart and applies the new port afterward.
156
+ - Public daemon status/settings responses no longer expose controller or daemon secrets; unauthenticated mutation requests are rejected.
157
+ - Daemon bearer/boot-token headers are stripped before requests enter the core controller reverse proxy.
158
+ - WebSocket Core streams now require loopback Host/Origin validation and bearer/boot-token authentication; the private WebUI token subprotocol is removed upstream.
159
+ - Reverse-proxy path matching now rejects lookalike prefixes such as `/core/apiX`.
160
+ - Failed settings validation/Core restart restores previous settings, generated config and runtime where possible.
161
+ - Offline `sash proxy off` now persists the desired proxy state as disabled before OS cleanup.
162
+ - Corrupt `sash.json` and `profiles/index.json` files are rejected without being overwritten by defaults.
163
+ - Profile home-page metadata accepts only HTTP(S), and invalid provider container shapes are rejected during config validation.
164
+ - PID records now use atomic writes. Static dashboard streams handle read failures and support `HEAD`.
165
+ - `GET /ui` redirects to `/ui/` while preserving the query string, so relative dashboard assets resolve correctly.
166
+ - WebUI polling no longer overlaps, Settings polling no longer overwrites a dirty port input, and logs continue auto-scrolling after the 600-row cap.
167
+ - WebUI Core-owned snapshots and traffic are cleared when runtime ownership is lost; stopped polling skips Core endpoints and recovery/profile revisions refresh configs, proxies, rules, connections and profiles once.
168
+ - System proxy disable remains available for desired/applied/OS-observed recovery while Core is stopped, while enable requires a healthy Core and actions follow the switch target state.
169
+ - Stale runtime/profile/mode/proxy responses can no longer overwrite newer intent; same-domain controls are disabled during mutations, failed single-profile updates refresh persisted errors, and committed TUN/LAN toggles update immediately.
170
+ - Manual latency results survive ordinary proxy polling and are cleared only when Core/profile ownership changes; traffic WebSockets stop with unavailable sessions/runtime and reset stale rates on disconnect.
171
+ - Confirm dialogs no longer leave older Promises pending; Escape and route changes cancel the active dialog.
172
+ - Empty `204 No Content` responses are handled through typed void requests instead of JSON parsing/casts.
173
+ - A late exit event from a replaced core process no longer clears the new child handle or PID record.
174
+ - Concurrent daemon starts now converge on one singleton; stale PID metadata no longer authorizes replacement or deletion of a live owner.
175
+ - Failed Core/daemon termination preserves ownership records, and corrupt PID/lock records fail closed.
176
+ - System-proxy shutdown no longer destroys a user's prior proxy/PAC configuration or overwrites third-party changes made after takeover.
177
+ - Windows proxy refresh uses the correct WinINet refresh option; GNOME `uint16` ports and automatic mode are parsed correctly.
178
+ - Release downloads enforce HTTPS, redirect host boundaries, backpressure and compressed-size limits; ZIP extraction is streamed with a hard output cap.
179
+ - Core version checks use exact tokens instead of substring matching, and controller readiness requires a non-empty version across consecutive probes.
180
+ - Core update rollback slots remain available until daemon/runtime restoration succeeds; malformed install metadata and backup-only mismatch states are rejected before execution.
181
+ - Core updates no longer discard managed profile/config snapshots before the executable transaction is durable. Crash recovery rolls managed state back before binary/install metadata and keeps both journals when either side cannot be restored.
182
+ - `sash update --force` now journals malformed binary/install entries before moving them to fixed quarantine paths. Partial quarantine and restoration resume safely, while missing or unowned repair backups fail closed.
183
+ - Core startup fails closed when executable and install metadata are missing, malformed or inconsistent, with an explicit `sash update --force` repair path.
184
+ - npm upgrade and browser-launch children now receive the same scrubbed environment as managed runtime children.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ming-kang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Sash
2
+
3
+ > A lightweight command-line companion and web dashboard for a rule-based network core.
4
+
5
+ Sash is a **network toolbox for developers and advanced users**. It installs, runs, and maintains a rule-based network core on your machine: local HTTP/SOCKS endpoints, rule-driven traffic routing, remote profile management, live traffic inspection through a built-in web dashboard, and an optional TUN mode for device-level traffic takeover.
6
+
7
+ ## Features
8
+
9
+ - **Supervisor daemon (`sashd`)** — background supervisor on port `19090` managing the core lifecycle, recovery, and reverse proxying
10
+ - **Zero-download web dashboard** — built-in modern Vue 3 dashboard bundled with the package at `http://127.0.0.1:19090/ui/`
11
+ - **Reversible system proxy ownership** — snapshots and conditionally restores prior Windows, macOS, or GNOME proxy/PAC state after stop or crash
12
+ - **One-command lifecycle** — `sash start`, `stop`, `restart`, `status`, `logs`
13
+ - **Remote profiles** — fetch, validate, schedule, and hot-reload core-format network profiles from the dashboard
14
+ - **Verified upgrades** — SHA-256-verified downloads, bounded extraction, exact-version checks and atomic rollback (`sash update`)
15
+ - **TUN mode** — device-level traffic takeover (requires starting the whole Sash runtime with elevated privileges)
16
+ - **Credential hygiene** — child processes run with scrubbed environments; loopback traffic never traverses proxy dispatchers
17
+
18
+ ## Requirements
19
+
20
+ - Node.js **24 or newer**
21
+ - Windows 10+, macOS, or Linux — x64 and arm64
22
+
23
+ Automatic Linux system-proxy integration currently requires a GNOME desktop with `gsettings`; Core lifecycle and local endpoints do not have that desktop requirement.
24
+
25
+ ## Install
26
+
27
+ ```sh
28
+ npm install -g @astralyn/sash
29
+ sash --help
30
+ ```
31
+
32
+ From a source checkout instead:
33
+
34
+ ```sh
35
+ npm ci
36
+ npm run build
37
+ npm link
38
+ sash --help
39
+ ```
40
+
41
+ `npm link` installs the locally built checkout. Remove it with `npm unlink -g @astralyn/sash` when finished.
42
+
43
+ ## Quick Start
44
+
45
+ ```sh
46
+ sash start # downloads core if needed, launches sashd and core
47
+ sash web # open the web dashboard (profiles, nodes, system proxy, settings)
48
+ sash status # runtime state, endpoints, and proxy status
49
+ sash stop # restores prior proxy state, stops core and sashd
50
+ ```
51
+
52
+ ## Documentation
53
+
54
+ Comprehensive documentation is available in the [`docs/`](./docs) directory:
55
+
56
+ - [**User & Operations Guide**](./docs/usage.md) — complete CLI command reference, configuration parameters, TUN mode, and troubleshooting.
57
+ - [**Backend Architecture**](./docs/backend.md) — supervisor daemon model (`sashd`), API endpoints, lifecycle management, system proxy adapters, and safety invariants.
58
+ - [**Frontend Architecture**](./docs/frontend.md) — built-in Vue 3 + Vite dashboard, shared API contracts, reactive runtime state, and WebSocket streaming.
59
+ - [**Third-Party Notices**](./THIRD_PARTY_NOTICES.md) — licenses and attribution for code/assets embedded in the dashboard and the runtime-downloaded Core.
60
+
61
+ ## Disclaimer
62
+
63
+ Sash is a network tool created for **learning, research, and development debugging**. It runs on servers and profiles that you source and configure yourself. You are responsible for how you use it and for complying with the laws and regulations of your jurisdiction.
64
+
65
+ ## Upstream Components
66
+
67
+ Sash is MIT-licensed open source and an independent project. It does not bundle the upstream Core in this repository or npm package; at runtime it downloads an unmodified release artifact from [`MetaCubeX/mihomo`](https://github.com/MetaCubeX/mihomo).
68
+
69
+ The upstream project's working source branch is [`Meta`](https://github.com/MetaCubeX/mihomo/tree/Meta), and release artifacts are published on its [releases page](https://github.com/MetaCubeX/mihomo/releases). Licensing is determined by the selected upstream release and its accompanying notices. Sash's currently tested Core contract is `v1.19.30`, whose source tag carries the [GNU General Public License v3.0](https://github.com/MetaCubeX/mihomo/blob/v1.19.30/LICENSE).
70
+
71
+ The downloaded component remains the work of its respective authors; all credit belongs upstream. See [Third-Party Notices](./THIRD_PARTY_NOTICES.md) for the bundled dashboard notices and release-specific Core attribution.
@@ -0,0 +1,54 @@
1
+ # Third-Party Notices
2
+
3
+ Sash itself is licensed under the MIT License in [`LICENSE`](./LICENSE). The built WebUI distributed in the npm package embeds code and selected assets from the components below. Those portions remain subject to their respective terms. Backend npm dependencies are installed as separate packages and retain their own package metadata and license files.
4
+
5
+ ## Vue.js
6
+
7
+ - Package: `vue` 3.5.42 (including the bundled `@vue/runtime-*` modules)
8
+ - Project: <https://github.com/vuejs/core>
9
+ - License: MIT
10
+ - Copyright: Copyright (c) 2018-present, Yuxi (Evan) You
11
+
12
+ ### MIT License
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in
22
+ all copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
+ THE SOFTWARE.
31
+
32
+ ## Remix Icon
33
+
34
+ - Package: `@remixicon/vue` 4.9.0
35
+ - Project: <https://github.com/Remix-Design/remixicon>
36
+ - License: Remix Icon License v1.0
37
+ - Copyright: Copyright (c) 2017-2026 Remix Design
38
+
39
+ Sash embeds selected Remix Icon components as functional interface symbols. They are not used as the Sash logo or brand identity. The complete Remix Icon License v1.0 text is included in [`docs/remix-icon-license.txt`](./docs/remix-icon-license.txt).
40
+
41
+ ## LXGW WenKai Lite
42
+
43
+ - Font: LXGW WenKai Lite 1.522 (`LXGWWenKaiLite-Regular`, split into unicode-range WOFF2 chunks at build time)
44
+ - Project: <https://github.com/lxgw/LxgwWenKai-Lite>
45
+ - License: SIL Open Font License 1.1 (<https://openfontlicense.org>)
46
+ - Copyright: Copyright 2021-2026 LXGW; Copyright 2020 The Klee Project Authors (Fontworks Inc.)
47
+
48
+ The dashboard embeds this font for its UI typography. Redistribution and subsetting are permitted under the OFL 1.1; the font software remains the work of its authors and is not sold on its own.
49
+
50
+ ## Runtime-downloaded Core
51
+
52
+ The upstream Core is not embedded in the Sash source tree or npm tarball. Sash downloads an unmodified release artifact at runtime from the upstream release project identified in the README attribution section.
53
+
54
+ Licensing is release-specific. The source tag currently used for Sash's tested contract, `v1.19.30`, carries the GNU General Public License v3.0; see the upstream [tag license](https://github.com/MetaCubeX/mihomo/blob/v1.19.30/LICENSE) and [release](https://github.com/MetaCubeX/mihomo/releases/tag/v1.19.30). Users selecting another release should review the license and notices accompanying that release.
package/dist/api.js ADDED
@@ -0,0 +1,103 @@
1
+ import { ERROR_BODY_LIMIT, fetchWithRetry } from "./http.js";
2
+ import { parseControllerAddress } from "./settings.js";
3
+ export class MihomoApi {
4
+ baseUrl;
5
+ secret;
6
+ constructor(controller, secret) {
7
+ const raw = controller.trim() || "127.0.0.1:9090";
8
+ const address = parseControllerAddress(raw);
9
+ if (!address) {
10
+ throw new Error(`Invalid controller address: ${raw} (expected loopback host:port)`);
11
+ }
12
+ this.baseUrl = `http://${address.canonical}`;
13
+ this.secret = (secret || "").trim();
14
+ }
15
+ async request(endpoint, options = {}) {
16
+ const url = `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
17
+ const headers = {};
18
+ if (this.secret)
19
+ headers.Authorization = `Bearer ${this.secret}`;
20
+ if (options.body)
21
+ headers["Content-Type"] = "application/json";
22
+ return fetchWithRetry(url, {
23
+ method: options.method ?? "GET",
24
+ headers,
25
+ body: options.body,
26
+ direct: true,
27
+ attempts: options.attempts,
28
+ deadlineMs: options.deadlineMs ?? 5_000,
29
+ });
30
+ }
31
+ async isReachable() {
32
+ try {
33
+ return Boolean(await this.version());
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ async version() {
40
+ const res = await this.request("/version", { deadlineMs: 5_000, attempts: 2 });
41
+ if (res.statusCode < 200 || res.statusCode >= 300) {
42
+ const summary = (await res.text(ERROR_BODY_LIMIT)).slice(0, 200).trim();
43
+ throw new Error(`Mihomo API returned HTTP ${res.statusCode}: ${summary}`);
44
+ }
45
+ const text = await res.text(1024 * 1024);
46
+ let data;
47
+ try {
48
+ data = JSON.parse(text);
49
+ }
50
+ catch {
51
+ throw new Error(`Invalid JSON response from Mihomo /version: ${text.slice(0, 200).trim()}`);
52
+ }
53
+ if (typeof data.version === "string" && data.version.trim())
54
+ return data.version.trim();
55
+ throw new Error("Mihomo /version response is missing a non-empty version");
56
+ }
57
+ async getTunActive() {
58
+ const res = await this.request("/configs", { deadlineMs: 2_000, attempts: 1 });
59
+ if (res.statusCode < 200 || res.statusCode >= 300) {
60
+ const summary = (await res.text(ERROR_BODY_LIMIT)).slice(0, 200).trim();
61
+ throw new Error(`Mihomo API returned HTTP ${res.statusCode}: ${summary}`);
62
+ }
63
+ const text = await res.text(1024 * 1024);
64
+ let data;
65
+ try {
66
+ data = JSON.parse(text);
67
+ }
68
+ catch {
69
+ throw new Error(`Invalid JSON response from Mihomo /configs: ${text.slice(0, 200).trim()}`);
70
+ }
71
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
72
+ throw new Error("Mihomo /configs response is not an object");
73
+ }
74
+ const tun = data.tun;
75
+ if (typeof tun !== "object" || tun === null || Array.isArray(tun)) {
76
+ throw new Error("Mihomo /configs response is missing the TUN runtime state");
77
+ }
78
+ const enable = tun.enable;
79
+ if (typeof enable !== "boolean") {
80
+ throw new Error("Mihomo /configs response is missing boolean tun.enable");
81
+ }
82
+ return enable;
83
+ }
84
+ async reloadConfig(configPath) {
85
+ const body = JSON.stringify({ path: configPath });
86
+ const res = await this.request("/configs?force=true", {
87
+ method: "PUT",
88
+ body,
89
+ deadlineMs: 5_000,
90
+ });
91
+ if (res.statusCode < 200 || res.statusCode >= 300) {
92
+ let bodyText = "";
93
+ try {
94
+ bodyText = (await res.text(ERROR_BODY_LIMIT)).trim();
95
+ }
96
+ catch {
97
+ }
98
+ const summary = bodyText ? `: ${bodyText.slice(0, 300)}` : "";
99
+ throw new Error(`Failed to reload Mihomo config (HTTP ${res.statusCode})${summary}`);
100
+ }
101
+ await res.discard();
102
+ }
103
+ }
@@ -0,0 +1,25 @@
1
+ import { spawn } from "node:child_process";
2
+ import { buildSanitizedEnv, findExecutableOnPath, windowsSystemExecutable } from "./process.js";
3
+ export function buildBrowserSpawnOptions(sourceEnv = process.env) {
4
+ return {
5
+ detached: true,
6
+ stdio: "ignore",
7
+ windowsHide: true,
8
+ env: buildSanitizedEnv(sourceEnv),
9
+ };
10
+ }
11
+ export function openInBrowser(url) {
12
+ const command = process.platform === "win32"
13
+ ? { cmd: windowsSystemExecutable("rundll32.exe"), args: ["url.dll,FileProtocolHandler", url] }
14
+ : process.platform === "darwin"
15
+ ? { cmd: "/usr/bin/open", args: [url] }
16
+ : { cmd: findExecutableOnPath("xdg-open") ?? "xdg-open", args: [url] };
17
+ try {
18
+ const child = spawn(command.cmd, command.args, buildBrowserSpawnOptions());
19
+ child.on("error", () => {
20
+ });
21
+ child.unref();
22
+ }
23
+ catch {
24
+ }
25
+ }
@@ -0,0 +1,15 @@
1
+ export function withCliErrors(fn) {
2
+ return async (...args) => {
3
+ try {
4
+ await fn(...args);
5
+ }
6
+ catch (err) {
7
+ console.error(`[sash] ✗ ${err instanceof Error ? err.message : String(err)}`);
8
+ if (process.env.DEBUG === "1" || process.env.DEBUG === "true") {
9
+ if (err instanceof Error && err.stack)
10
+ console.error(err.stack);
11
+ }
12
+ process.exitCode = 1;
13
+ }
14
+ };
15
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ import "./node-version-guard.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { Command, CommanderError, InvalidArgumentError } from "commander";
7
+ import { withCliErrors } from "./cli-errors.js";
8
+ import { runRestart, runStart, runStop } from "./commands/lifecycle.js";
9
+ import { runLogs } from "./commands/logs.js";
10
+ import { runStatus } from "./commands/status.js";
11
+ import { runUpdate } from "./commands/update.js";
12
+ import { runUpgrade } from "./commands/upgrade.js";
13
+ import { runWeb } from "./commands/web.js";
14
+ import { parseLogLineCount } from "./log-follow.js";
15
+ function packageVersion() {
16
+ try {
17
+ const here = path.dirname(fileURLToPath(import.meta.url));
18
+ const pkg = JSON.parse(fs.readFileSync(path.join(here, "..", "package.json"), "utf8"));
19
+ return pkg.version ?? "0.0.0";
20
+ }
21
+ catch {
22
+ return "0.0.0";
23
+ }
24
+ }
25
+ const program = new Command();
26
+ program
27
+ .name("sash")
28
+ .description("A lightweight command-line companion for a rule-based network core and its web dashboard.")
29
+ .version(packageVersion(), "-v, --version", "print the Sash version")
30
+ .enablePositionalOptions()
31
+ .exitOverride()
32
+ .addHelpText("after", `
33
+ Examples:
34
+ $ sash start install components if needed and launch sash in the background
35
+ $ sash web open the web dashboard
36
+ $ sash status show runtime state, endpoints, and system proxy status
37
+ $ sash update upgrade the core binary
38
+ $ sash upgrade upgrade Sash itself via npm
39
+
40
+ Data directory: %LOCALAPPDATA%\\Sash (Windows), ~/Library/Application Support/Sash (macOS),
41
+ $XDG_DATA_HOME/sash (Linux). Override with the SASH_HOME environment variable.`);
42
+ program
43
+ .command("start")
44
+ .description("install components if needed and start sash in the background")
45
+ .action(withCliErrors(() => runStart()));
46
+ program
47
+ .command("stop")
48
+ .description("stop sash (shuts down core and disables system proxy)")
49
+ .action(withCliErrors(() => runStop()));
50
+ program
51
+ .command("restart")
52
+ .description("restart the daemon and core")
53
+ .action(withCliErrors(() => runRestart()));
54
+ program
55
+ .command("status")
56
+ .description("show runtime state, versions, endpoints, and system proxy status")
57
+ .option("--json", "output machine-readable JSON")
58
+ .action(withCliErrors((opts) => runStatus(opts)));
59
+ program
60
+ .command("logs")
61
+ .description("print runtime logs")
62
+ .option("-n, --lines <n>", "number of lines to print", parseLines)
63
+ .option("-f, --follow", "follow the log output")
64
+ .option("--errors", "read the stderr log instead of stdout")
65
+ .option("--daemon", "read sashd daemon logs instead of core logs")
66
+ .action(withCliErrors((opts) => runLogs({
67
+ lines: opts.lines ?? 50,
68
+ follow: opts.follow,
69
+ errors: opts.errors,
70
+ daemon: opts.daemon,
71
+ })));
72
+ program
73
+ .command("update")
74
+ .description("upgrade the core binary")
75
+ .option("--version <tag>", "install a specific core version, e.g. v1.19.30")
76
+ .option("--force", "reinstall the target version and repair inconsistent Core state")
77
+ .action(withCliErrors((opts) => runUpdate(opts)));
78
+ program
79
+ .command("upgrade")
80
+ .description("upgrade Sash itself via npm")
81
+ .option("--version <version>", "install a specific Sash version")
82
+ .action(withCliErrors((opts) => runUpgrade(opts)));
83
+ program
84
+ .command("web")
85
+ .description("open the web dashboard (installs/starts components as needed)")
86
+ .option("--no-open", "print the URL without opening a browser")
87
+ .action(withCliErrors((opts) => runWeb({ noOpen: !opts.open })));
88
+ program
89
+ .command("version")
90
+ .description("print the Sash version")
91
+ .action(() => {
92
+ console.log(packageVersion());
93
+ });
94
+ function parseLines(value) {
95
+ try {
96
+ return parseLogLineCount(value);
97
+ }
98
+ catch (err) {
99
+ throw new InvalidArgumentError(err instanceof Error ? err.message : String(err));
100
+ }
101
+ }
102
+ async function main() {
103
+ try {
104
+ if (process.argv.length <= 2) {
105
+ await withCliErrors(() => runStatus())();
106
+ return;
107
+ }
108
+ await program.parseAsync(process.argv);
109
+ }
110
+ catch (err) {
111
+ if (err instanceof CommanderError) {
112
+ process.exitCode =
113
+ err.exitCode === 0 || err.code === "commander.helpDisplayed" ? 0 : err.exitCode;
114
+ if (err.code === "commander.unknownCommand" || err.code === "commander.unknownOption") {
115
+ process.exitCode = 1;
116
+ }
117
+ return;
118
+ }
119
+ throw err;
120
+ }
121
+ }
122
+ await main();