@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,572 @@
1
+ import { execFile, execFileSync, spawn } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { atomicWriteFileSync, durableRemoveFileSync, durableRenameSync } from "./fs-atomic.js";
6
+ function sleep(ms) {
7
+ return new Promise((resolve) => setTimeout(resolve, ms));
8
+ }
9
+ export function isProcessAlive(pid) {
10
+ if (!Number.isInteger(pid) || pid <= 0)
11
+ return false;
12
+ try {
13
+ process.kill(pid, 0);
14
+ return true;
15
+ }
16
+ catch (err) {
17
+ const code = err.code;
18
+ if (code === "EPERM")
19
+ return true;
20
+ return false;
21
+ }
22
+ }
23
+ function normalizePath(filePath) {
24
+ const cleaned = filePath.replace(/\s+\(deleted\)$/i, "").trim();
25
+ try {
26
+ return fs.realpathSync.native(cleaned);
27
+ }
28
+ catch {
29
+ return path.resolve(cleaned);
30
+ }
31
+ }
32
+ function exePathsMatch(observedPath, expectedPath) {
33
+ try {
34
+ const a = normalizePath(observedPath);
35
+ const b = normalizePath(expectedPath);
36
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ function basenameLower(filePath) {
43
+ return path.posix.basename(filePath.replace(/\\/g, "/")).toLowerCase();
44
+ }
45
+ function stripExeSuffix(name) {
46
+ return name.toLowerCase().replace(/\.exe$/, "");
47
+ }
48
+ function imageMatchesExpectedExe(imageOrComm, expectedExe) {
49
+ const expected = stripExeSuffix(basenameLower(expectedExe));
50
+ if (!expected)
51
+ return false;
52
+ const observed = stripExeSuffix(basenameLower(imageOrComm.trim()));
53
+ if (!observed)
54
+ return false;
55
+ if (observed === expected)
56
+ return true;
57
+ if (observed.length === 15 && expected.startsWith(observed) && expected.length > 15) {
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+ let cachedPowerShell;
63
+ function runPowerShell(script, timeoutMs = 5000) {
64
+ const windowsPowerShell = windowsSystemExecutable(path.join("WindowsPowerShell", "v1.0", "powershell.exe"));
65
+ const pwsh = findExecutableOnPath("pwsh.exe");
66
+ const candidates = [windowsPowerShell, ...(pwsh ? [pwsh] : [])];
67
+ const shells = cachedPowerShell
68
+ ? [cachedPowerShell, ...candidates.filter((shell) => shell !== cachedPowerShell)]
69
+ : candidates;
70
+ for (const shell of shells) {
71
+ try {
72
+ const output = runSanitizedCommand(shell, ["-NoProfile", "-NonInteractive", "-Command", script], { timeoutMs }).trim();
73
+ cachedPowerShell = shell;
74
+ if (output)
75
+ return output;
76
+ }
77
+ catch {
78
+ }
79
+ }
80
+ return undefined;
81
+ }
82
+ function readWindowsExecutablePath(pid) {
83
+ const script = [
84
+ `$process = Get-Process -Id ${pid} -ErrorAction Stop`,
85
+ "if ($process.Path) { [Console]::Out.Write($process.Path) }",
86
+ ].join("; ");
87
+ return runPowerShell(script);
88
+ }
89
+ function parseTasklistImageName(tasklistOutput) {
90
+ const line = tasklistOutput.trim().split(/\r?\n/)[0] ?? "";
91
+ if (!line || /^INFO:/i.test(line))
92
+ return undefined;
93
+ const quoted = line.match(/^"([^"]+)"/);
94
+ if (quoted?.[1])
95
+ return quoted[1];
96
+ const first = line.split(",")[0]?.replace(/^"|"$/g, "").trim();
97
+ return first || undefined;
98
+ }
99
+ function classifyDarwinComm(commOutput, expectedExe) {
100
+ const out = commOutput.trim();
101
+ if (!out)
102
+ return "mismatch";
103
+ if (path.isAbsolute(out)) {
104
+ if (exePathsMatch(out, expectedExe))
105
+ return "match";
106
+ if (expectedExe.startsWith(out) && out.length < expectedExe.length)
107
+ return "unknown";
108
+ return "mismatch";
109
+ }
110
+ return imageMatchesExpectedExe(out, expectedExe) ? "unknown" : "mismatch";
111
+ }
112
+ export function classifyProcessIdentity(pid, expectedExe) {
113
+ if (!isProcessAlive(pid))
114
+ return "mismatch";
115
+ const expected = expectedExe || "";
116
+ if (!expected)
117
+ return "unknown";
118
+ try {
119
+ if (process.platform === "linux") {
120
+ try {
121
+ const exeLink = fs.readlinkSync(`/proc/${pid}/exe`);
122
+ return exePathsMatch(exeLink, expected) ? "match" : "mismatch";
123
+ }
124
+ catch (err) {
125
+ const code = err.code;
126
+ return code === "ENOENT" ? "mismatch" : "unknown";
127
+ }
128
+ }
129
+ if (process.platform === "win32") {
130
+ const executable = readWindowsExecutablePath(pid);
131
+ if (executable) {
132
+ return exePathsMatch(executable, expected) ? "match" : "mismatch";
133
+ }
134
+ try {
135
+ const out = runSanitizedCommand(windowsSystemExecutable("tasklist.exe"), ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], { timeoutMs: 3000 }).trim();
136
+ const image = parseTasklistImageName(out);
137
+ if (!image)
138
+ return "mismatch";
139
+ return imageMatchesExpectedExe(image, expected) ? "unknown" : "mismatch";
140
+ }
141
+ catch {
142
+ return "unknown";
143
+ }
144
+ }
145
+ if (process.platform === "darwin") {
146
+ try {
147
+ const out = runSanitizedCommand("/bin/ps", ["-ww", "-p", String(pid), "-o", "comm="], {
148
+ timeoutMs: 3000,
149
+ });
150
+ return classifyDarwinComm(out, expected);
151
+ }
152
+ catch {
153
+ return "unknown";
154
+ }
155
+ }
156
+ }
157
+ catch {
158
+ return "unknown";
159
+ }
160
+ return "unknown";
161
+ }
162
+ export function readProcessCommandLine(pid) {
163
+ try {
164
+ if (process.platform === "linux") {
165
+ const raw = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8");
166
+ const parts = raw.split("\0").filter((s) => s.length > 0);
167
+ return parts.length > 0 ? parts.join(" ") : undefined;
168
+ }
169
+ if (process.platform === "win32") {
170
+ const script = [
171
+ `$p = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" -ErrorAction Stop`,
172
+ "if ($p.CommandLine) { [Console]::Out.Write($p.CommandLine) }",
173
+ ].join("; ");
174
+ return runPowerShell(script);
175
+ }
176
+ if (process.platform === "darwin") {
177
+ const out = runSanitizedCommand("/bin/ps", ["-ww", "-p", String(pid), "-o", "command="], {
178
+ timeoutMs: 3000,
179
+ }).trim();
180
+ return out || undefined;
181
+ }
182
+ }
183
+ catch {
184
+ return undefined;
185
+ }
186
+ return undefined;
187
+ }
188
+ export function commandLineContains(pid, marker) {
189
+ const cmdline = readProcessCommandLine(pid);
190
+ if (!cmdline)
191
+ return false;
192
+ const normalize = (s) => s.replace(/\\/g, "/").toLowerCase();
193
+ return normalize(cmdline).includes(normalize(marker));
194
+ }
195
+ const STRIPPED_ENV_KEYS = new Set([
196
+ "GITHUB_TOKEN",
197
+ "GH_TOKEN",
198
+ "GH_ENTERPRISE_TOKEN",
199
+ "GITHUB_PAT",
200
+ "GITHUB_ACCESS_TOKEN",
201
+ "GH_PAT",
202
+ "NPM_TOKEN",
203
+ "NPM_AUTH_TOKEN",
204
+ "NODE_AUTH_TOKEN",
205
+ "NPM_ID_TOKEN",
206
+ "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
207
+ "ACTIONS_ID_TOKEN_REQUEST_URL",
208
+ ]);
209
+ export function buildSanitizedEnv(sourceEnv = process.env) {
210
+ const childEnv = { ...sourceEnv };
211
+ for (const key of Object.keys(childEnv)) {
212
+ const upper = key.toUpperCase();
213
+ const lower = key.toLowerCase();
214
+ const isNpmAuthConfig = lower.startsWith("npm_config_") &&
215
+ (lower.includes("authtoken") ||
216
+ lower.includes("auth_token") ||
217
+ lower.endsWith("_auth") ||
218
+ lower.includes("password") ||
219
+ lower.includes("username") ||
220
+ lower === "npm_config_userconfig" ||
221
+ lower === "npm_config_globalconfig");
222
+ if (STRIPPED_ENV_KEYS.has(upper) || isNpmAuthConfig) {
223
+ delete childEnv[key];
224
+ }
225
+ }
226
+ return childEnv;
227
+ }
228
+ export function runSanitizedCommand(command, args, options = {}) {
229
+ const output = execFileSync(command, args, {
230
+ encoding: "utf8",
231
+ env: buildSanitizedEnv(options.sourceEnv),
232
+ maxBuffer: options.maxBuffer,
233
+ stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
234
+ timeout: options.timeoutMs ?? 5000,
235
+ windowsHide: true,
236
+ });
237
+ return output ?? "";
238
+ }
239
+ export function runSanitizedCommandAsync(command, args, options = {}) {
240
+ return new Promise((resolve, reject) => {
241
+ const child = execFile(command, args, {
242
+ encoding: "utf8",
243
+ env: buildSanitizedEnv(options.sourceEnv),
244
+ maxBuffer: options.maxBuffer,
245
+ shell: false,
246
+ timeout: options.timeoutMs ?? 5000,
247
+ windowsHide: true,
248
+ }, (error, stdout) => {
249
+ if (error) {
250
+ reject(error);
251
+ return;
252
+ }
253
+ resolve(options.stdio === "ignore" ? "" : (stdout ?? ""));
254
+ });
255
+ child.stdin?.end();
256
+ });
257
+ }
258
+ export function windowsSystemExecutable(relativePath) {
259
+ const root = process.env.SystemRoot?.trim() || process.env.WINDIR?.trim();
260
+ return root && path.isAbsolute(root)
261
+ ? path.join(root, "System32", relativePath)
262
+ : path.basename(relativePath);
263
+ }
264
+ export function findExecutableOnPath(command, sourceEnv = process.env) {
265
+ for (const directory of (sourceEnv.PATH ?? sourceEnv.Path ?? "").split(path.delimiter)) {
266
+ if (!directory || !path.isAbsolute(directory))
267
+ continue;
268
+ const candidate = path.join(directory, command);
269
+ try {
270
+ if (!fs.lstatSync(candidate).isFile())
271
+ continue;
272
+ fs.accessSync(candidate, fs.constants.X_OK);
273
+ return candidate;
274
+ }
275
+ catch {
276
+ }
277
+ }
278
+ return undefined;
279
+ }
280
+ function openPrivateAppendLogFd(filePath) {
281
+ try {
282
+ if (!fs.lstatSync(filePath).isFile()) {
283
+ throw new Error(`Refusing to append to non-regular log file: ${filePath}`);
284
+ }
285
+ }
286
+ catch (err) {
287
+ if (err.code !== "ENOENT")
288
+ throw err;
289
+ }
290
+ const noFollow = process.platform === "win32" ? 0 : fs.constants.O_NOFOLLOW;
291
+ const fd = fs.openSync(filePath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND | noFollow, 0o600);
292
+ try {
293
+ if (!fs.fstatSync(fd).isFile()) {
294
+ throw new Error(`Refusing to append to non-regular log file: ${filePath}`);
295
+ }
296
+ if (process.platform !== "win32")
297
+ fs.fchmodSync(fd, 0o600);
298
+ return fd;
299
+ }
300
+ catch (err) {
301
+ try {
302
+ fs.closeSync(fd);
303
+ }
304
+ catch {
305
+ }
306
+ throw err;
307
+ }
308
+ }
309
+ export function withPrivateAppendLogFds(stdoutPath, stderrPath, use) {
310
+ const stdoutFd = openPrivateAppendLogFd(stdoutPath);
311
+ let stderrFd;
312
+ try {
313
+ stderrFd = openPrivateAppendLogFd(stderrPath);
314
+ }
315
+ catch (err) {
316
+ try {
317
+ fs.closeSync(stdoutFd);
318
+ }
319
+ catch {
320
+ }
321
+ throw err;
322
+ }
323
+ try {
324
+ return use({ stdoutFd, stderrFd });
325
+ }
326
+ finally {
327
+ for (const fd of [stdoutFd, stderrFd]) {
328
+ try {
329
+ fs.closeSync(fd);
330
+ }
331
+ catch {
332
+ }
333
+ }
334
+ }
335
+ }
336
+ export const TAIL_FILE_CHUNK_BYTES = 64 * 1024;
337
+ const MAX_TAIL_LINE_BYTES = 64 * 1024;
338
+ export function tailFile(filePath, lineCount = 20) {
339
+ if (!Number.isSafeInteger(lineCount) || lineCount <= 0)
340
+ return "";
341
+ let fd;
342
+ try {
343
+ const size = fs.statSync(filePath).size;
344
+ if (size <= 0)
345
+ return "";
346
+ fd = fs.openSync(filePath, "r");
347
+ let position = size;
348
+ let pending = "";
349
+ const lines = [];
350
+ while (position > 0 && lines.length < lineCount) {
351
+ const bytesToRead = Math.min(TAIL_FILE_CHUNK_BYTES, position);
352
+ position -= bytesToRead;
353
+ const chunk = Buffer.allocUnsafe(bytesToRead);
354
+ const bytesRead = fs.readSync(fd, chunk, 0, bytesToRead, position);
355
+ const parts = (chunk.subarray(0, bytesRead).toString("utf8") + pending).split(/\r?\n/);
356
+ pending = parts.shift() ?? "";
357
+ if (Buffer.byteLength(pending) > MAX_TAIL_LINE_BYTES) {
358
+ pending = pending.slice(-MAX_TAIL_LINE_BYTES);
359
+ }
360
+ for (let index = parts.length - 1; index >= 0 && lines.length < lineCount; index--) {
361
+ const line = parts[index];
362
+ if (line?.trim())
363
+ lines.push(line);
364
+ }
365
+ }
366
+ if (lines.length < lineCount && pending.trim())
367
+ lines.push(pending);
368
+ return lines.reverse().join("\n");
369
+ }
370
+ catch {
371
+ return "";
372
+ }
373
+ finally {
374
+ if (fd !== undefined) {
375
+ try {
376
+ fs.closeSync(fd);
377
+ }
378
+ catch {
379
+ }
380
+ }
381
+ }
382
+ }
383
+ async function runTaskkill(pid, force) {
384
+ const args = force ? ["/PID", String(pid), "/T", "/F"] : ["/PID", String(pid), "/T"];
385
+ return new Promise((resolve) => {
386
+ const killer = spawn(windowsSystemExecutable("taskkill.exe"), args, {
387
+ env: buildSanitizedEnv(),
388
+ windowsHide: true,
389
+ stdio: "ignore",
390
+ });
391
+ killer.on("close", (code) => resolve(code === 0));
392
+ killer.on("error", () => resolve(false));
393
+ });
394
+ }
395
+ async function processIdentityMatches(verify) {
396
+ try {
397
+ return (await verify()) === "match";
398
+ }
399
+ catch {
400
+ return false;
401
+ }
402
+ }
403
+ async function sendTerminationSignal(pid, force) {
404
+ if (process.platform === "win32")
405
+ return runTaskkill(pid, force);
406
+ try {
407
+ process.kill(pid, force ? "SIGKILL" : "SIGTERM");
408
+ return true;
409
+ }
410
+ catch (err) {
411
+ if (err.code === "ESRCH")
412
+ return true;
413
+ throw err;
414
+ }
415
+ }
416
+ export async function killProcessGracefully(pid, opts) {
417
+ const isAlive = opts.isAliveFn ?? isProcessAlive;
418
+ const signal = opts.signalFn ?? sendTerminationSignal;
419
+ const wait = opts.sleepFn ?? sleep;
420
+ if (!isAlive(pid))
421
+ return true;
422
+ if (!(await processIdentityMatches(opts.verify)))
423
+ return false;
424
+ const totalTimeout = opts.timeoutMs ?? 10_000;
425
+ const graceMs = Math.min(5000, Math.floor(totalTimeout / 2));
426
+ const forceMs = Math.max(1000, totalTimeout - graceMs);
427
+ const gracefulOk = await signal(pid, false);
428
+ if (gracefulOk) {
429
+ const deadline = Date.now() + graceMs;
430
+ while (Date.now() < deadline && isAlive(pid))
431
+ await wait(200);
432
+ }
433
+ if (isAlive(pid)) {
434
+ if (!(await processIdentityMatches(opts.verify)))
435
+ return false;
436
+ await signal(pid, true);
437
+ const hardDeadline = Date.now() + forceMs;
438
+ while (Date.now() < hardDeadline && isAlive(pid))
439
+ await wait(100);
440
+ }
441
+ return !isAlive(pid);
442
+ }
443
+ export function readPidRecord(pidFile) {
444
+ try {
445
+ if (!fs.existsSync(pidFile))
446
+ return undefined;
447
+ const raw = fs.readFileSync(pidFile, "utf8");
448
+ const parsed = JSON.parse(raw);
449
+ if (typeof parsed.pid === "number" &&
450
+ Number.isInteger(parsed.pid) &&
451
+ parsed.pid > 0 &&
452
+ typeof parsed.exe === "string" &&
453
+ typeof parsed.startedAt === "string") {
454
+ return {
455
+ pid: parsed.pid,
456
+ exe: parsed.exe,
457
+ startedAt: parsed.startedAt,
458
+ };
459
+ }
460
+ return undefined;
461
+ }
462
+ catch {
463
+ return undefined;
464
+ }
465
+ }
466
+ export function writePidRecord(pidFile, record) {
467
+ atomicWriteFileSync(pidFile, `${JSON.stringify(record, null, 2)}\n`);
468
+ }
469
+ export function clearPidRecord(pidFile) {
470
+ try {
471
+ fs.rmSync(pidFile, { force: true });
472
+ }
473
+ catch {
474
+ }
475
+ }
476
+ export function binaryUnlockProbePath(target) {
477
+ return path.join(path.dirname(target), `.${path.basename(target)}.unlock-probe`);
478
+ }
479
+ function regularFileStat(file) {
480
+ try {
481
+ const stat = fs.lstatSync(file);
482
+ if (!stat.isFile())
483
+ throw new Error(`Binary path is not a regular file: ${file}`);
484
+ return stat;
485
+ }
486
+ catch (err) {
487
+ if (err.code === "ENOENT")
488
+ return undefined;
489
+ throw err;
490
+ }
491
+ }
492
+ function fileDigestSync(file) {
493
+ const hash = crypto.createHash("sha256");
494
+ const buffer = Buffer.allocUnsafe(64 * 1024);
495
+ const fd = fs.openSync(file, "r");
496
+ try {
497
+ for (;;) {
498
+ const bytes = fs.readSync(fd, buffer, 0, buffer.length, null);
499
+ if (bytes === 0)
500
+ break;
501
+ hash.update(buffer.subarray(0, bytes));
502
+ }
503
+ }
504
+ finally {
505
+ fs.closeSync(fd);
506
+ }
507
+ return hash.digest("hex");
508
+ }
509
+ export function recoverBinaryUnlockProbe(target) {
510
+ const probe = binaryUnlockProbePath(target);
511
+ const probeStat = regularFileStat(probe);
512
+ if (!probeStat)
513
+ return;
514
+ const targetStat = regularFileStat(target);
515
+ if (!targetStat) {
516
+ durableRenameSync(probe, target);
517
+ return;
518
+ }
519
+ if (probeStat.size === targetStat.size && fileDigestSync(probe) === fileDigestSync(target)) {
520
+ durableRemoveFileSync(probe);
521
+ return;
522
+ }
523
+ throw new Error(`Core binary and unlock probe both exist with different content; preserved ${target} and ${probe}`);
524
+ }
525
+ async function recoverUnlockProbeWithRetry(target) {
526
+ let lastError;
527
+ for (let attempt = 0; attempt < 4; attempt++) {
528
+ try {
529
+ recoverBinaryUnlockProbe(target);
530
+ return;
531
+ }
532
+ catch (err) {
533
+ lastError = err;
534
+ if (attempt < 3)
535
+ await sleep(100);
536
+ }
537
+ }
538
+ throw lastError;
539
+ }
540
+ export async function waitForBinaryUnlocked(target, timeoutMs = 30_000) {
541
+ await recoverUnlockProbeWithRetry(target);
542
+ if (process.platform !== "win32" || !fs.existsSync(target))
543
+ return;
544
+ const probe = binaryUnlockProbePath(target);
545
+ const deadline = Date.now() + timeoutMs;
546
+ let delay = 150;
547
+ while (Date.now() < deadline) {
548
+ try {
549
+ durableRenameSync(target, probe);
550
+ }
551
+ catch {
552
+ await sleep(delay);
553
+ delay = Math.min(1000, Math.floor(delay * 1.5));
554
+ continue;
555
+ }
556
+ try {
557
+ durableRenameSync(probe, target);
558
+ return;
559
+ }
560
+ catch (secondError) {
561
+ try {
562
+ await recoverUnlockProbeWithRetry(target);
563
+ return;
564
+ }
565
+ catch (recoveryError) {
566
+ throw new Error(`Failed to restore the binary after the lock probe; preserved state near ${probe}: ${secondError.message}; recovery failed: ${recoveryError.message}`);
567
+ }
568
+ }
569
+ }
570
+ await recoverUnlockProbeWithRetry(target);
571
+ throw new Error(`Binary is still locked after ${timeoutMs}ms: ${target}. Close programs using it and retry.`);
572
+ }
@@ -0,0 +1,101 @@
1
+ import fs from "node:fs";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import YAML from "yaml";
4
+ import { commitManagedStateTransaction, defaultManagedStateFileOperations, } from "./managed-state-transaction.js";
5
+ import { buildDefaultConfig, isValidMihomoConfig, PROFILE_DOWNLOAD_SIZE_LIMIT, stripManagedKeys, } from "./mihomo-config.js";
6
+ import { allocateProfileId, DEFAULT_PROFILE_INTERVAL_HOURS, findProfileByUrl, loadProfiles, NEVER_UPDATED, profileNameFromUrl, } from "./profiles.js";
7
+ export const IMPORTED_CONFIG_PROFILE_NAME = "Imported config";
8
+ export async function migrateLegacyProfileSetting(settings, layout, files = defaultManagedStateFileOperations) {
9
+ if (!("subscriptionUrl" in settings))
10
+ return false;
11
+ const candidate = { ...settings };
12
+ const url = candidate.subscriptionUrl?.trim() ?? "";
13
+ delete candidate.subscriptionUrl;
14
+ let migratedIndex;
15
+ if (url) {
16
+ const index = loadProfiles(layout);
17
+ if (!findProfileByUrl(index, url)) {
18
+ const now = new Date().toISOString();
19
+ const profile = {
20
+ id: allocateProfileId(index, layout),
21
+ name: profileNameFromUrl(url),
22
+ url,
23
+ intervalHours: DEFAULT_PROFILE_INTERVAL_HOURS,
24
+ createdAt: now,
25
+ updatedAt: NEVER_UPDATED,
26
+ };
27
+ migratedIndex = {
28
+ activeId: index.activeId ?? profile.id,
29
+ profiles: [...index.profiles, profile],
30
+ };
31
+ }
32
+ }
33
+ await commitManagedStateTransaction(layout, { ...(migratedIndex ? { index: migratedIndex } : {}), settings: candidate }, undefined, files);
34
+ delete settings.subscriptionUrl;
35
+ return true;
36
+ }
37
+ function hasMeaningfulRoutingContent(doc) {
38
+ const unmanaged = stripManagedKeys(doc);
39
+ const defaultConfig = buildDefaultConfig();
40
+ if (isDeepStrictEqual(unmanaged, defaultConfig))
41
+ return false;
42
+ if (Array.isArray(unmanaged.proxies) && unmanaged.proxies.length > 0)
43
+ return true;
44
+ const providers = unmanaged["proxy-providers"];
45
+ if (typeof providers === "object" &&
46
+ providers !== null &&
47
+ !Array.isArray(providers) &&
48
+ Object.keys(providers).length > 0) {
49
+ return true;
50
+ }
51
+ const groups = unmanaged["proxy-groups"];
52
+ if (Array.isArray(groups) &&
53
+ groups.length > 0 &&
54
+ !isDeepStrictEqual(groups, defaultConfig["proxy-groups"])) {
55
+ return true;
56
+ }
57
+ return (Array.isArray(unmanaged.rules) &&
58
+ unmanaged.rules.length > 0 &&
59
+ !isDeepStrictEqual(unmanaged.rules, defaultConfig.rules));
60
+ }
61
+ export async function migrateUnmanagedConfig(layout, files = defaultManagedStateFileOperations) {
62
+ if (fs.existsSync(layout.profilesIndexFile) || !fs.existsSync(layout.configFile))
63
+ return false;
64
+ const stat = fs.lstatSync(layout.configFile);
65
+ if (!stat.isFile() || stat.size > PROFILE_DOWNLOAD_SIZE_LIMIT) {
66
+ throw new Error(`Unmanaged config must be a regular file no larger than ${PROFILE_DOWNLOAD_SIZE_LIMIT} bytes: ${layout.configFile}`);
67
+ }
68
+ const yamlText = fs.readFileSync(layout.configFile, "utf8");
69
+ let doc;
70
+ try {
71
+ doc = YAML.parse(yamlText);
72
+ }
73
+ catch (err) {
74
+ throw new Error(`Unmanaged config is invalid YAML and was left unchanged: ${layout.configFile}: ${err.message}`);
75
+ }
76
+ if (!isValidMihomoConfig(doc)) {
77
+ throw new Error(`Unmanaged config is not a valid core configuration and was left unchanged: ${layout.configFile}`);
78
+ }
79
+ if (!hasMeaningfulRoutingContent(doc))
80
+ return false;
81
+ const now = new Date().toISOString();
82
+ const id = allocateProfileId({ activeId: null, profiles: [] }, layout);
83
+ const profile = {
84
+ id,
85
+ name: IMPORTED_CONFIG_PROFILE_NAME,
86
+ url: "",
87
+ intervalHours: 0,
88
+ createdAt: now,
89
+ updatedAt: now,
90
+ };
91
+ await commitManagedStateTransaction(layout, {
92
+ index: { activeId: id, profiles: [profile] },
93
+ profile: { id, yamlText },
94
+ }, undefined, files);
95
+ return true;
96
+ }
97
+ export async function migrateProfileState(settings, layout, files = defaultManagedStateFileOperations) {
98
+ const legacySubscription = await migrateLegacyProfileSetting(settings, layout, files);
99
+ const unmanagedConfig = await migrateUnmanagedConfig(layout, files);
100
+ return { legacySubscription, unmanagedConfig };
101
+ }