@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,331 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { errnoCode, errorMessage } from "./error-utils.js";
5
+ import { isPlainObject } from "./json-shape.js";
6
+ import { isProcessAlive } from "./process.js";
7
+ const DEFAULT_TIMEOUT_MS = 10_000;
8
+ const DEFAULT_POLL_MS = 50;
9
+ const syncWaitCell = new Int32Array(new SharedArrayBuffer(4));
10
+ function ownerDescription(owner) {
11
+ const pid = owner?.pid ?? "unknown";
12
+ const purpose = owner?.purpose === undefined ? "unknown" : JSON.stringify(owner.purpose);
13
+ return `owner PID ${pid}, purpose ${purpose}`;
14
+ }
15
+ function lockError(file, message, owner) {
16
+ return new Error(`State lock ${message}: ${file} (${ownerDescription(owner)})`);
17
+ }
18
+ function normalizeOptions(file, options) {
19
+ if (typeof options !== "object" ||
20
+ options === null ||
21
+ typeof options.purpose !== "string" ||
22
+ !options.purpose.trim()) {
23
+ throw lockError(file, "requires a non-empty purpose");
24
+ }
25
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
26
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 0) {
27
+ throw lockError(file, "requires timeoutMs to be a non-negative integer");
28
+ }
29
+ const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
30
+ if (!Number.isInteger(pollMs) || pollMs <= 0) {
31
+ throw lockError(file, "requires pollMs to be a positive integer");
32
+ }
33
+ return {
34
+ purpose: options.purpose.trim(),
35
+ timeoutMs,
36
+ pollMs,
37
+ };
38
+ }
39
+ function partialOwner(value) {
40
+ if (!isPlainObject(value))
41
+ return undefined;
42
+ const pid = typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0
43
+ ? value.pid
44
+ : undefined;
45
+ const purpose = typeof value.purpose === "string" && value.purpose ? value.purpose : undefined;
46
+ return pid === undefined && purpose === undefined ? undefined : { pid, purpose };
47
+ }
48
+ function parseLockRecord(value) {
49
+ if (!isPlainObject(value))
50
+ return undefined;
51
+ if (value.version !== 1)
52
+ return undefined;
53
+ if (typeof value.pid !== "number" || !Number.isInteger(value.pid) || value.pid <= 0) {
54
+ return undefined;
55
+ }
56
+ if (typeof value.token !== "string" || !value.token)
57
+ return undefined;
58
+ if (typeof value.purpose !== "string" || !value.purpose)
59
+ return undefined;
60
+ if (typeof value.acquiredAt !== "string" || !value.acquiredAt)
61
+ return undefined;
62
+ return {
63
+ version: 1,
64
+ pid: value.pid,
65
+ token: value.token,
66
+ purpose: value.purpose,
67
+ acquiredAt: value.acquiredAt,
68
+ };
69
+ }
70
+ function readExistingStateLock(file) {
71
+ try {
72
+ if (!fs.lstatSync(file).isFile()) {
73
+ return { kind: "corrupt", reason: "lock path is not a regular file" };
74
+ }
75
+ let text;
76
+ try {
77
+ text = fs.readFileSync(file, "utf8");
78
+ }
79
+ catch (err) {
80
+ if (errnoCode(err) === "ENOENT")
81
+ return { kind: "missing" };
82
+ return { kind: "corrupt", reason: `lock record is not valid JSON (${errorMessage(err)})` };
83
+ }
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(text);
87
+ }
88
+ catch (err) {
89
+ return { kind: "corrupt", reason: `lock record is not valid JSON (${errorMessage(err)})` };
90
+ }
91
+ const record = parseLockRecord(parsed);
92
+ return record
93
+ ? { kind: "valid", record }
94
+ : {
95
+ kind: "corrupt",
96
+ reason: "lock record has an invalid shape",
97
+ owner: partialOwner(parsed),
98
+ };
99
+ }
100
+ catch (err) {
101
+ if (errnoCode(err) === "ENOENT")
102
+ return { kind: "missing" };
103
+ return { kind: "corrupt", reason: `lock record cannot be read (${errorMessage(err)})` };
104
+ }
105
+ }
106
+ function removeMatchingStateLock(file, token) {
107
+ const existing = readExistingStateLock(file);
108
+ if (existing.kind !== "valid" || existing.record.token !== token)
109
+ return;
110
+ try {
111
+ fs.unlinkSync(file);
112
+ }
113
+ catch (err) {
114
+ if (errnoCode(err) !== "ENOENT") {
115
+ throw lockError(file, `could not be released (${errorMessage(err)})`, existing.record);
116
+ }
117
+ }
118
+ }
119
+ function createLease(file, record) {
120
+ return {
121
+ file,
122
+ record,
123
+ release: () => {
124
+ removeMatchingStateLock(file, record.token);
125
+ },
126
+ };
127
+ }
128
+ function tryCreateStateLock(file, options) {
129
+ const record = {
130
+ version: 1,
131
+ pid: process.pid,
132
+ token: crypto.randomBytes(24).toString("hex"),
133
+ purpose: options.purpose,
134
+ acquiredAt: new Date().toISOString(),
135
+ };
136
+ const temp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${record.token}.tmp`);
137
+ let fd;
138
+ try {
139
+ fd = fs.openSync(temp, "wx", 0o600);
140
+ fs.writeFileSync(fd, `${JSON.stringify(record)}\n`, "utf8");
141
+ fs.fsyncSync(fd);
142
+ fs.closeSync(fd);
143
+ fd = undefined;
144
+ fs.linkSync(temp, file);
145
+ return createLease(file, record);
146
+ }
147
+ catch (err) {
148
+ if (errnoCode(err) === "EEXIST" && fs.existsSync(file))
149
+ return undefined;
150
+ throw lockError(file, `could not create (${errorMessage(err)})`, record);
151
+ }
152
+ finally {
153
+ if (fd !== undefined) {
154
+ try {
155
+ fs.closeSync(fd);
156
+ }
157
+ catch {
158
+ }
159
+ }
160
+ try {
161
+ fs.unlinkSync(temp);
162
+ }
163
+ catch {
164
+ }
165
+ }
166
+ }
167
+ function staleLockFile(file) {
168
+ return path.join(path.dirname(file), `.${path.basename(file)}.stale.${process.pid}.${crypto.randomBytes(8).toString("hex")}`);
169
+ }
170
+ function reclaimDeadStateLock(file, owner) {
171
+ const current = readExistingStateLock(file);
172
+ if (current.kind === "missing")
173
+ return;
174
+ if (current.kind !== "valid" ||
175
+ current.record.token !== owner.token ||
176
+ current.record.pid !== owner.pid) {
177
+ return;
178
+ }
179
+ if (isProcessAlive(current.record.pid))
180
+ return;
181
+ const stale = staleLockFile(file);
182
+ try {
183
+ fs.renameSync(file, stale);
184
+ }
185
+ catch (err) {
186
+ if (errnoCode(err) === "ENOENT")
187
+ return;
188
+ throw lockError(file, `could not move dead owner aside (${errorMessage(err)})`, owner);
189
+ }
190
+ const moved = readExistingStateLock(stale);
191
+ if (moved.kind !== "valid" ||
192
+ moved.record.token !== owner.token ||
193
+ moved.record.pid !== owner.pid) {
194
+ try {
195
+ fs.renameSync(stale, file);
196
+ }
197
+ catch {
198
+ }
199
+ throw lockError(file, "changed while reclaiming a dead owner; refusing to delete it", owner);
200
+ }
201
+ try {
202
+ fs.unlinkSync(stale);
203
+ }
204
+ catch (err) {
205
+ if (errnoCode(err) !== "ENOENT") {
206
+ throw lockError(file, `could not remove stale record (${errorMessage(err)})`, owner);
207
+ }
208
+ }
209
+ }
210
+ function inspectExistingLock(file) {
211
+ const existing = readExistingStateLock(file);
212
+ if (existing.kind !== "valid" || isProcessAlive(existing.record.pid))
213
+ return existing;
214
+ reclaimDeadStateLock(file, existing.record);
215
+ return { kind: "missing" };
216
+ }
217
+ function ensureParentDirectory(file) {
218
+ try {
219
+ fs.mkdirSync(path.dirname(file), { recursive: true });
220
+ }
221
+ catch (err) {
222
+ throw lockError(file, `could not create parent directory (${errorMessage(err)})`);
223
+ }
224
+ }
225
+ function sleep(ms) {
226
+ return new Promise((resolve) => setTimeout(resolve, ms));
227
+ }
228
+ function sleepSync(ms) {
229
+ Atomics.wait(syncWaitCell, 0, 0, ms);
230
+ }
231
+ function busyError(file, owner) {
232
+ return lockError(file, "is busy", owner);
233
+ }
234
+ export function readStateLockRecord(file) {
235
+ const existing = readExistingStateLock(file);
236
+ if (existing.kind === "missing")
237
+ return undefined;
238
+ if (existing.kind === "corrupt") {
239
+ throw lockError(file, `is corrupt and cannot be trusted (${existing.reason})`, existing.owner);
240
+ }
241
+ return existing.record;
242
+ }
243
+ function createStateLockAcquisition(file, options) {
244
+ const normalized = normalizeOptions(file, options);
245
+ ensureParentDirectory(file);
246
+ const deadline = Date.now() + normalized.timeoutMs;
247
+ let sawCorruptRecord = false;
248
+ return () => {
249
+ const lease = tryCreateStateLock(file, normalized);
250
+ if (lease)
251
+ return { kind: "acquired", lease };
252
+ const existing = inspectExistingLock(file);
253
+ if (existing.kind === "missing") {
254
+ sawCorruptRecord = false;
255
+ return { kind: "retry" };
256
+ }
257
+ const remaining = deadline - Date.now();
258
+ if (existing.kind === "corrupt") {
259
+ if (sawCorruptRecord || remaining <= 0) {
260
+ throw lockError(file, `is corrupt and cannot be reclaimed (${existing.reason})`, existing.owner);
261
+ }
262
+ sawCorruptRecord = true;
263
+ return { kind: "wait", delayMs: Math.min(normalized.pollMs, remaining) };
264
+ }
265
+ sawCorruptRecord = false;
266
+ if (remaining <= 0)
267
+ throw busyError(file, existing.record);
268
+ return { kind: "wait", delayMs: Math.min(normalized.pollMs, remaining) };
269
+ };
270
+ }
271
+ export async function acquireStateLock(file, options) {
272
+ const next = createStateLockAcquisition(file, options);
273
+ for (;;) {
274
+ const step = next();
275
+ if (step.kind === "acquired")
276
+ return step.lease;
277
+ if (step.kind === "wait")
278
+ await sleep(step.delayMs);
279
+ }
280
+ }
281
+ export function acquireStateLockSync(file, options) {
282
+ const next = createStateLockAcquisition(file, options);
283
+ for (;;) {
284
+ const step = next();
285
+ if (step.kind === "acquired")
286
+ return step.lease;
287
+ if (step.kind === "wait")
288
+ sleepSync(step.delayMs);
289
+ }
290
+ }
291
+ export async function withStateLock(file, options, action) {
292
+ const lease = await acquireStateLock(file, options);
293
+ let outcome;
294
+ try {
295
+ outcome = { ok: true, value: await action(lease) };
296
+ }
297
+ catch (err) {
298
+ outcome = { ok: false, error: err };
299
+ }
300
+ let releaseError;
301
+ try {
302
+ lease.release();
303
+ }
304
+ catch (err) {
305
+ releaseError = err;
306
+ }
307
+ if (!outcome.ok) {
308
+ if (releaseError) {
309
+ throw new Error(`${errorMessage(outcome.error)}; state lock release failed: ${errorMessage(releaseError)}`);
310
+ }
311
+ throw outcome.error;
312
+ }
313
+ if (releaseError)
314
+ throw releaseError;
315
+ return outcome.value;
316
+ }
317
+ export class StateMutationQueue {
318
+ file;
319
+ timeoutMs;
320
+ operationQueue = Promise.resolve();
321
+ constructor(file, timeoutMs = 30_000) {
322
+ this.file = file;
323
+ this.timeoutMs = timeoutMs;
324
+ }
325
+ run(purpose, action) {
326
+ const operation = () => withStateLock(this.file, { purpose, timeoutMs: this.timeoutMs }, () => action());
327
+ const next = this.operationQueue.then(operation, operation);
328
+ this.operationQueue = next.then(() => undefined, () => undefined);
329
+ return next;
330
+ }
331
+ }
package/dist/status.js ADDED
@@ -0,0 +1,279 @@
1
+ import { currentCoreVersion } from "./core.js";
2
+ import { SashDaemonClient } from "./daemon-client.js";
3
+ import { evaluateDaemon, } from "./daemon-lifecycle.js";
4
+ import { ProfileService } from "./profile-service.js";
5
+ import { SystemProxyManager } from "./system-proxy-manager.js";
6
+ import { uiInstalled } from "./webui.js";
7
+ export const CLI_STATUS_SCHEMA_VERSION = 1;
8
+ function errorText(err) {
9
+ const message = err instanceof Error ? err.message : String(err);
10
+ return (message
11
+ .replace(/[\r\n\t]+/g, " ")
12
+ .trim()
13
+ .slice(0, 300) || "unknown error");
14
+ }
15
+ function addError(errors, message) {
16
+ if (!errors.includes(message))
17
+ errors.push(message);
18
+ }
19
+ function observedSystemProxy(state) {
20
+ if (!state) {
21
+ return { supported: null, enabled: null, server: null, details: null };
22
+ }
23
+ return {
24
+ supported: typeof state.supported === "boolean" ? state.supported : null,
25
+ enabled: typeof state.enabled === "boolean" ? state.enabled : null,
26
+ server: typeof state.server === "string" && state.server ? state.server : null,
27
+ details: typeof state.details === "string" && state.details ? state.details : null,
28
+ };
29
+ }
30
+ function daemonObservation(state, daemonState) {
31
+ const resolvedState = daemonState ?? state.kind;
32
+ return {
33
+ state: resolvedState,
34
+ running: state.running,
35
+ healthy: resolvedState === "healthy",
36
+ pid: typeof state.pid === "number" ? state.pid : null,
37
+ port: state.kind === "stopped" ? 0 : (state.port ?? 0),
38
+ };
39
+ }
40
+ export function resolveObservedSystemProxy(source, fallbackDaemonApplied, inspection, inspectionError) {
41
+ const errors = [];
42
+ let daemonApplied = fallbackDaemonApplied;
43
+ let osObserved;
44
+ if (source) {
45
+ daemonApplied = source.appliedKnown ? source.applied : null;
46
+ if (daemonApplied === null)
47
+ addError(errors, "Daemon-applied proxy state is unavailable");
48
+ if (source.queryError)
49
+ addError(errors, `System proxy query failed: ${source.queryError}`);
50
+ if (source.stateKnown && source.state) {
51
+ osObserved = observedSystemProxy(source.state);
52
+ }
53
+ }
54
+ if (!osObserved && inspection) {
55
+ if (inspection.queryError) {
56
+ addError(errors, `System proxy query failed: ${inspection.queryError}`);
57
+ }
58
+ if (inspection.stateKnown) {
59
+ osObserved = observedSystemProxy(inspection.state);
60
+ }
61
+ }
62
+ if (inspectionError)
63
+ addError(errors, inspectionError);
64
+ osObserved ??= observedSystemProxy(undefined);
65
+ if (osObserved.supported === null || osObserved.enabled === null) {
66
+ addError(errors, "OS proxy state is unavailable");
67
+ }
68
+ return { daemonApplied, osObserved, errors };
69
+ }
70
+ export async function observeSystemProxy(context, dependencies, source, fallbackDaemonApplied) {
71
+ let inspection;
72
+ let inspectionError;
73
+ if (!source?.stateKnown || !source.state) {
74
+ try {
75
+ inspection = dependencies.inspectSystemProxy
76
+ ? await dependencies.inspectSystemProxy(context)
77
+ : await new SystemProxyManager({ layout: context.layout }).inspect();
78
+ }
79
+ catch (err) {
80
+ inspectionError = `OS proxy query failed: ${errorText(err)}`;
81
+ }
82
+ }
83
+ return resolveObservedSystemProxy(source, fallbackDaemonApplied, inspection, inspectionError);
84
+ }
85
+ async function evaluate(context, dependencies) {
86
+ return dependencies.evaluateDaemon
87
+ ? dependencies.evaluateDaemon(context)
88
+ : evaluateDaemon(context.layout, context.settings);
89
+ }
90
+ async function queryStatus(context, dependencies, daemon) {
91
+ if (dependencies.queryDaemonStatus) {
92
+ return dependencies.queryDaemonStatus(context, daemon);
93
+ }
94
+ return new SashDaemonClient(daemon.port, context.settings.daemonSecret).status();
95
+ }
96
+ function addObservationErrors(errors, observation) {
97
+ for (const error of observation.errors)
98
+ addError(errors, error);
99
+ }
100
+ export async function collectRuntimeStatus(context, dependencies = {}) {
101
+ const errors = [];
102
+ const daemonState = await evaluate(context, dependencies);
103
+ let daemon = daemonObservation(daemonState);
104
+ const installedVersion = dependencies.installedCoreVersion
105
+ ? dependencies.installedCoreVersion(context)
106
+ : currentCoreVersion(context.layout);
107
+ const profile = dependencies.activeProfile
108
+ ? dependencies.activeProfile(context)
109
+ : new ProfileService({
110
+ layout: context.layout,
111
+ settings: () => context.settings,
112
+ }).active();
113
+ let coreRunning = daemonState.running ? null : false;
114
+ let coreHealthy = daemonState.running ? null : false;
115
+ let corePid = null;
116
+ let coreVersion = null;
117
+ let tunActive = daemonState.running ? null : false;
118
+ let desiredProxy = context.settings.systemProxy;
119
+ let proxySource;
120
+ let queriedDaemon = false;
121
+ if (daemonState.kind === "healthy") {
122
+ try {
123
+ const status = await queryStatus(context, dependencies, daemonState);
124
+ queriedDaemon = true;
125
+ daemon = daemonObservation(daemonState, "healthy");
126
+ desiredProxy = status.systemProxy.desired;
127
+ proxySource = {
128
+ applied: status.systemProxy.applied,
129
+ appliedKnown: status.systemProxy.appliedKnown,
130
+ stateKnown: status.systemProxy.stateKnown,
131
+ ...(status.systemProxy.actual ? { state: status.systemProxy.actual } : {}),
132
+ ...(status.systemProxy.queryError ? { queryError: status.systemProxy.queryError } : {}),
133
+ };
134
+ if (typeof status.core.running !== "boolean") {
135
+ addError(errors, "Core running state is unavailable");
136
+ }
137
+ else if (!status.core.running) {
138
+ coreRunning = false;
139
+ coreHealthy = false;
140
+ tunActive = false;
141
+ }
142
+ else {
143
+ coreRunning = true;
144
+ coreHealthy = typeof status.core.healthy === "boolean" ? status.core.healthy : null;
145
+ corePid = typeof status.core.pid === "number" ? status.core.pid : null;
146
+ coreVersion =
147
+ typeof status.core.version === "string" && status.core.version
148
+ ? status.core.version
149
+ : null;
150
+ tunActive = typeof status.core.tunActive === "boolean" ? status.core.tunActive : null;
151
+ if (coreHealthy === null)
152
+ addError(errors, "Core health is unavailable");
153
+ else if (!coreHealthy)
154
+ addError(errors, "Core controller health probe failed");
155
+ if (corePid === null)
156
+ addError(errors, "Core PID is unavailable");
157
+ if (coreVersion === null)
158
+ addError(errors, "Core runtime version is unavailable");
159
+ if (tunActive === null)
160
+ addError(errors, "Core TUN state is unavailable");
161
+ }
162
+ }
163
+ catch (err) {
164
+ daemon = daemonObservation(daemonState, "unhealthy");
165
+ addError(errors, `Daemon status query failed: ${errorText(err)}`);
166
+ }
167
+ }
168
+ else if (daemonState.running) {
169
+ addError(errors, "sashd control API is unavailable");
170
+ }
171
+ const proxyObservation = await observeSystemProxy(context, dependencies, proxySource, daemonState.running ? null : false);
172
+ addObservationErrors(errors, proxyObservation);
173
+ const healthy = !daemonState.running
174
+ ? false
175
+ : queriedDaemon
176
+ ? coreRunning === false
177
+ ? false
178
+ : coreHealthy
179
+ : null;
180
+ const activeProfile = profile ? { id: profile.id, name: profile.name, url: profile.url } : null;
181
+ const daemonPort = daemon.port || context.settings.daemonPort;
182
+ return {
183
+ schemaVersion: CLI_STATUS_SCHEMA_VERSION,
184
+ complete: errors.length === 0,
185
+ healthy,
186
+ queryError: errors.length > 0 ? errors.join("; ") : null,
187
+ daemon: { ...daemon, port: daemonPort },
188
+ core: {
189
+ running: coreRunning,
190
+ healthy: coreHealthy,
191
+ pid: corePid,
192
+ version: coreVersion,
193
+ installedVersion: installedVersion || null,
194
+ },
195
+ systemProxy: {
196
+ desired: desiredProxy,
197
+ daemonApplied: proxyObservation.daemonApplied,
198
+ osObserved: proxyObservation.osObserved,
199
+ },
200
+ uiInstalled: dependencies.hasUi ? dependencies.hasUi(context) : uiInstalled(context.layout),
201
+ endpoints: {
202
+ mixedProxy: `127.0.0.1:${context.settings.mixedPort}`,
203
+ controller: context.settings.controller,
204
+ daemonApi: `http://127.0.0.1:${daemonPort}`,
205
+ dashboard: `http://127.0.0.1:${daemonPort}/ui/`,
206
+ },
207
+ activeProfile,
208
+ tun: {
209
+ desired: context.settings.tun,
210
+ active: tunActive,
211
+ },
212
+ paths: {
213
+ root: context.layout.root,
214
+ config: context.layout.configFile,
215
+ },
216
+ };
217
+ }
218
+ export function runtimeStatusHeadline(status) {
219
+ if (status.daemon.state === "stopped")
220
+ return { level: "info", text: "sash is not running" };
221
+ if (status.daemon.state === "unhealthy" || status.core.running === null) {
222
+ const owner = status.daemon.pid === null ? "" : ` (PID=${status.daemon.pid})`;
223
+ return {
224
+ level: "warn",
225
+ text: `sashd is running${owner}, but runtime status is unavailable`,
226
+ };
227
+ }
228
+ if (status.core.running) {
229
+ const pid = status.core.pid === null ? "unknown" : String(status.core.pid);
230
+ const version = status.core.version ? `, ${status.core.version}` : "";
231
+ return status.core.healthy
232
+ ? {
233
+ level: "ok",
234
+ text: `sashd running (PID=${status.daemon.pid}), core running (PID=${pid}${version})`,
235
+ }
236
+ : {
237
+ level: "warn",
238
+ text: `sashd running (PID=${status.daemon.pid}), core unhealthy (PID=${pid}${version})`,
239
+ };
240
+ }
241
+ return {
242
+ level: "ok",
243
+ text: `sashd running (PID=${status.daemon.pid}), core stopped`,
244
+ };
245
+ }
246
+ export function formatTunObservation(status) {
247
+ if (!status.tun.desired) {
248
+ if (status.tun.active === true)
249
+ return "off (runtime active)";
250
+ return status.tun.active === null && status.core.running !== false
251
+ ? "off (runtime unknown)"
252
+ : "off";
253
+ }
254
+ if (status.core.running === false)
255
+ return "on (core stopped)";
256
+ if (status.core.running === null)
257
+ return "on (runtime unknown)";
258
+ if (status.tun.active === true)
259
+ return "on (active)";
260
+ if (status.tun.active === false)
261
+ return "on (inactive)";
262
+ return "on (unverified)";
263
+ }
264
+ export function shouldShowTunGuidance(status) {
265
+ return (status.tun.desired &&
266
+ status.core.running === true &&
267
+ status.core.healthy === true &&
268
+ status.tun.active !== true);
269
+ }
270
+ export function formatObservedProxy(state) {
271
+ if (state.enabled === null)
272
+ return "unknown";
273
+ return state.enabled ? `on (${state.server || "unknown server"})` : "off";
274
+ }
275
+ export function markIncompleteObservation(complete) {
276
+ if (!complete && (process.exitCode === undefined || process.exitCode === 0)) {
277
+ process.exitCode = 2;
278
+ }
279
+ }