@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,72 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { atomicWriteFileSync } from "./fs-atomic.js";
5
+ import { buildSanitizedEnv } from "./process.js";
6
+ function defaultRunner(executable, args) {
7
+ return new Promise((resolve, reject) => {
8
+ execFile(executable, args, {
9
+ encoding: "utf8",
10
+ env: buildSanitizedEnv(),
11
+ maxBuffer: 1024 * 1024,
12
+ timeout: 20_000,
13
+ windowsHide: true,
14
+ }, (err, stdout, stderr) => {
15
+ if (!err) {
16
+ resolve();
17
+ return;
18
+ }
19
+ reject(Object.assign(err, { stdout, stderr }));
20
+ });
21
+ });
22
+ }
23
+ function errorOutput(err) {
24
+ if (typeof err !== "object" || err === null)
25
+ return String(err);
26
+ const record = err;
27
+ for (const value of [record.stderr, record.stdout, record.message]) {
28
+ const text = Buffer.isBuffer(value)
29
+ ? value.toString("utf8").trim()
30
+ : String(value ?? "").trim();
31
+ if (text)
32
+ return text.slice(0, 1000);
33
+ }
34
+ return "unknown validation error";
35
+ }
36
+ export async function validateCoreConfigFile(executable, configFile, layout, runner = defaultRunner) {
37
+ if (!fs.existsSync(executable)) {
38
+ throw new Error(`Core executable is missing: ${executable}`);
39
+ }
40
+ if (!fs.existsSync(configFile)) {
41
+ throw new Error(`Core configuration is missing: ${configFile}`);
42
+ }
43
+ try {
44
+ await runner(executable, ["-t", "-d", layout.root, "-f", configFile]);
45
+ }
46
+ catch (err) {
47
+ throw new Error(`Core rejected generated configuration: ${errorOutput(err)}`);
48
+ }
49
+ }
50
+ export async function validateCoreConfigTextWithExecutable(executable, yaml, layout, runner = defaultRunner) {
51
+ if (!fs.existsSync(executable)) {
52
+ throw new Error(`Core executable is missing: ${executable}`);
53
+ }
54
+ fs.mkdirSync(layout.tempDir, { recursive: true });
55
+ const candidate = path.join(layout.tempDir, `config-validate-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.yaml`);
56
+ try {
57
+ atomicWriteFileSync(candidate, yaml);
58
+ await validateCoreConfigFile(executable, candidate, layout, runner);
59
+ }
60
+ catch (err) {
61
+ if (err instanceof Error && err.message.startsWith("Core rejected generated configuration:")) {
62
+ throw err;
63
+ }
64
+ throw new Error(`Core rejected generated configuration: ${errorOutput(err)}`);
65
+ }
66
+ finally {
67
+ fs.rmSync(candidate, { force: true });
68
+ }
69
+ }
70
+ export function validateCoreConfigText(yaml, layout, runner = defaultRunner) {
71
+ return validateCoreConfigTextWithExecutable(layout.coreExe, yaml, layout, runner);
72
+ }
@@ -0,0 +1,55 @@
1
+ import fs from "node:fs";
2
+ import { atomicWriteFileSync } from "./fs-atomic.js";
3
+ import { hasExactOwnKeys, isCanonicalIsoTimestamp, isPlainObject } from "./json-shape.js";
4
+ import { sashLayout } from "./paths.js";
5
+ const INSTALL_RECORD_SIZE_LIMIT = 16 * 1024;
6
+ export function validateCoreReleaseTag(tag) {
7
+ const normalized = tag.trim();
8
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(normalized)) {
9
+ throw new Error(`Invalid Core release tag: ${tag}`);
10
+ }
11
+ return normalized;
12
+ }
13
+ export function parseInstallRecord(value) {
14
+ if (!isPlainObject(value) || !hasExactOwnKeys(value, ["coreVersion", "installedAt"])) {
15
+ return undefined;
16
+ }
17
+ if (typeof value.coreVersion !== "string" || !isCanonicalIsoTimestamp(value.installedAt)) {
18
+ return undefined;
19
+ }
20
+ try {
21
+ return {
22
+ coreVersion: validateCoreReleaseTag(value.coreVersion),
23
+ installedAt: value.installedAt,
24
+ };
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ export function readInstallRecord(layout = sashLayout()) {
31
+ try {
32
+ const stat = fs.lstatSync(layout.installFile);
33
+ if (!stat.isFile() || stat.size > INSTALL_RECORD_SIZE_LIMIT)
34
+ return undefined;
35
+ return parseInstallRecord(JSON.parse(fs.readFileSync(layout.installFile, "utf8")));
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
41
+ export function writeInstallRecord(record, layout = sashLayout()) {
42
+ const coreVersion = validateCoreReleaseTag(record.coreVersion);
43
+ if (!isCanonicalIsoTimestamp(record.installedAt)) {
44
+ throw new Error(`Invalid Core install timestamp: ${record.installedAt}`);
45
+ }
46
+ atomicWriteFileSync(layout.installFile, `${JSON.stringify({ coreVersion, installedAt: record.installedAt }, null, 2)}\n`);
47
+ }
48
+ export function currentCoreVersion(layout = sashLayout()) {
49
+ return readInstallRecord(layout)?.coreVersion ?? "";
50
+ }
51
+ export function installRecordsEqual(left, right) {
52
+ if (!left || !right)
53
+ return left === undefined && right === null;
54
+ return left.coreVersion === right.coreVersion && left.installedAt === right.installedAt;
55
+ }
@@ -0,0 +1,114 @@
1
+ import fs from "node:fs";
2
+ import { atomicWriteFileSync, durableRemoveFileSync, pathEntryExists } from "./fs-atomic.js";
3
+ import { hasExactOwnKeys, isCanonicalIsoTimestamp, isPlainObject } from "./json-shape.js";
4
+ const TRANSACTION_VERSION = 1;
5
+ const TRANSACTION_SIZE_LIMIT = 16 * 1024;
6
+ function isCanonicalReleaseTag(value) {
7
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);
8
+ }
9
+ function parseTransaction(value) {
10
+ if (!isPlainObject(value))
11
+ return undefined;
12
+ if (!hasExactOwnKeys(value, [
13
+ "binaryExisted",
14
+ "createdAt",
15
+ "installRecordExisted",
16
+ "phase",
17
+ "targetVersion",
18
+ "version",
19
+ ]) ||
20
+ value.version !== TRANSACTION_VERSION ||
21
+ (value.phase !== "publishing" && value.phase !== "committed") ||
22
+ !isCanonicalIsoTimestamp(value.createdAt) ||
23
+ !isCanonicalReleaseTag(value.targetVersion) ||
24
+ value.binaryExisted !== false ||
25
+ value.installRecordExisted !== false) {
26
+ return undefined;
27
+ }
28
+ return {
29
+ version: 1,
30
+ phase: value.phase,
31
+ createdAt: value.createdAt,
32
+ targetVersion: value.targetVersion,
33
+ binaryExisted: false,
34
+ installRecordExisted: false,
35
+ };
36
+ }
37
+ function readTransaction(layout) {
38
+ let stat;
39
+ try {
40
+ stat = fs.lstatSync(layout.coreInstallTransactionFile);
41
+ }
42
+ catch (err) {
43
+ if (err.code === "ENOENT")
44
+ return undefined;
45
+ throw new Error(`Cannot inspect Core install transaction ${layout.coreInstallTransactionFile}: ${err.message}`);
46
+ }
47
+ if (!stat.isFile()) {
48
+ throw new Error(`Core install transaction is not a regular file: ${layout.coreInstallTransactionFile}`);
49
+ }
50
+ if (stat.size > TRANSACTION_SIZE_LIMIT) {
51
+ throw new Error(`Core install transaction exceeds ${TRANSACTION_SIZE_LIMIT} bytes: ${layout.coreInstallTransactionFile}`);
52
+ }
53
+ let parsed;
54
+ try {
55
+ parsed = JSON.parse(fs.readFileSync(layout.coreInstallTransactionFile, "utf8"));
56
+ }
57
+ catch (err) {
58
+ throw new Error(`Core install transaction is not valid JSON: ${layout.coreInstallTransactionFile} (${err.message})`);
59
+ }
60
+ const transaction = parseTransaction(parsed);
61
+ if (!transaction) {
62
+ throw new Error(`Core install transaction has an invalid version, phase, timestamp, tag, or shape: ${layout.coreInstallTransactionFile}`);
63
+ }
64
+ return transaction;
65
+ }
66
+ function writeTransaction(transaction, layout) {
67
+ atomicWriteFileSync(layout.coreInstallTransactionFile, `${JSON.stringify(transaction, null, 2)}\n`);
68
+ }
69
+ export function beginCoreInstallTransaction(targetVersion, layout, createdAt = new Date().toISOString()) {
70
+ if (!isCanonicalReleaseTag(targetVersion)) {
71
+ throw new Error(`Invalid Core release tag: ${targetVersion}`);
72
+ }
73
+ if (!isCanonicalIsoTimestamp(createdAt)) {
74
+ throw new Error(`Invalid Core install timestamp: ${createdAt}`);
75
+ }
76
+ if (readTransaction(layout)) {
77
+ throw new Error(`A Core install transaction is already pending: ${layout.coreInstallTransactionFile}`);
78
+ }
79
+ if (pathEntryExists(layout.coreExe) || pathEntryExists(layout.installFile)) {
80
+ throw new Error("Cannot begin a first-install transaction over existing Core state");
81
+ }
82
+ const transaction = {
83
+ version: 1,
84
+ phase: "publishing",
85
+ createdAt,
86
+ targetVersion,
87
+ binaryExisted: false,
88
+ installRecordExisted: false,
89
+ };
90
+ writeTransaction(transaction, layout);
91
+ return transaction;
92
+ }
93
+ export function markCoreInstallTransactionCommitted(transaction, layout) {
94
+ const current = readTransaction(layout);
95
+ if (current?.phase !== "publishing" ||
96
+ current.createdAt !== transaction.createdAt ||
97
+ current.targetVersion !== transaction.targetVersion) {
98
+ throw new Error("Core install transaction changed before commit");
99
+ }
100
+ writeTransaction({ ...current, phase: "committed" }, layout);
101
+ }
102
+ export function clearCoreInstallTransaction(layout) {
103
+ durableRemoveFileSync(layout.coreInstallTransactionFile);
104
+ }
105
+ export function recoverCoreInstallTransaction(layout) {
106
+ const transaction = readTransaction(layout);
107
+ if (!transaction)
108
+ return;
109
+ if (transaction.phase === "publishing") {
110
+ durableRemoveFileSync(layout.coreExe);
111
+ durableRemoveFileSync(layout.installFile);
112
+ }
113
+ clearCoreInstallTransaction(layout);
114
+ }
@@ -0,0 +1,105 @@
1
+ import { completePendingCoreUpdateAfterStart, finalizeCoreUpdateTransaction, readCoreUpdateTransaction, recoverCoreUpdateTransaction, rollbackCoreUpdateTransaction, } from "./core-update.js";
2
+ import { clearCommittedManagedStateTransaction, markRetainedManagedStateTransactionCommitted, readManagedStateTransactionStatus, recoverManagedStateTransaction, rollbackRetainedManagedStateTransaction, } from "./managed-state-transaction.js";
3
+ function rollbackRequired(transaction) {
4
+ return (transaction.phase === "repair-prepared" ||
5
+ transaction.phase === "repair-restoring" ||
6
+ transaction.phase === "prepared" ||
7
+ (!transaction.deferredHealth && transaction.phase === "swapped"));
8
+ }
9
+ export function rollbackCoordinatedCoreUpdate(layout, verifier) {
10
+ const errors = [];
11
+ let previous;
12
+ const managed = readManagedStateTransactionStatus(layout);
13
+ if (managed?.coordination === "core-update") {
14
+ if (managed.phase === "committed") {
15
+ errors.push("managed-state commit decision is already durable");
16
+ }
17
+ else {
18
+ try {
19
+ rollbackRetainedManagedStateTransaction(layout);
20
+ }
21
+ catch (err) {
22
+ errors.push(`managed state: ${err.message}`);
23
+ }
24
+ }
25
+ }
26
+ try {
27
+ previous = rollbackCoreUpdateTransaction(layout, verifier);
28
+ }
29
+ catch (err) {
30
+ errors.push(`Core binary: ${err.message}`);
31
+ }
32
+ if (errors.length) {
33
+ throw new Error(`Coordinated Core update rollback failed: ${errors.join("; ")}`);
34
+ }
35
+ return previous;
36
+ }
37
+ export function recoverCoordinatedCoreUpdate(layout, verifier) {
38
+ const managed = readManagedStateTransactionStatus(layout);
39
+ if (managed?.coordination !== "core-update") {
40
+ recoverManagedStateTransaction(layout);
41
+ return recoverCoreUpdateTransaction(layout, verifier);
42
+ }
43
+ const core = readCoreUpdateTransaction(layout);
44
+ if (managed.phase === "committed") {
45
+ if (!core) {
46
+ clearCommittedManagedStateTransaction(layout);
47
+ return undefined;
48
+ }
49
+ if (core.phase === "health-verified") {
50
+ finalizeCoreUpdateTransaction(layout, verifier);
51
+ }
52
+ else if (core.phase === "swapped" && core.deferredHealth) {
53
+ completePendingCoreUpdateAfterStart(layout, verifier);
54
+ }
55
+ else {
56
+ throw new Error("Committed managed-state Core update has no durable Core health decision");
57
+ }
58
+ clearCommittedManagedStateTransaction(layout);
59
+ return undefined;
60
+ }
61
+ if (managed.phase === "publishing") {
62
+ if (core) {
63
+ throw new Error("Publishing managed-state Core update unexpectedly overlaps a Core update journal");
64
+ }
65
+ rollbackRetainedManagedStateTransaction(layout);
66
+ return recoverCoreUpdateTransaction(layout, verifier);
67
+ }
68
+ if (!core) {
69
+ rollbackRetainedManagedStateTransaction(layout);
70
+ return recoverCoreUpdateTransaction(layout, verifier);
71
+ }
72
+ if (rollbackRequired(core)) {
73
+ rollbackCoordinatedCoreUpdate(layout, verifier);
74
+ return undefined;
75
+ }
76
+ return recoverCoreUpdateTransaction(layout, verifier);
77
+ }
78
+ function markManagedCommitDecision(layout) {
79
+ const managed = readManagedStateTransactionStatus(layout);
80
+ if (managed?.coordination !== "core-update")
81
+ return false;
82
+ if (managed.phase === "retained") {
83
+ markRetainedManagedStateTransactionCommitted(layout);
84
+ return true;
85
+ }
86
+ if (managed.phase !== "committed") {
87
+ throw new Error("Coordinated managed-state publication is not ready to commit");
88
+ }
89
+ return true;
90
+ }
91
+ function clearManagedCommitDecision(layout, coordinated) {
92
+ if (coordinated)
93
+ clearCommittedManagedStateTransaction(layout);
94
+ }
95
+ export function completeCoordinatedCoreUpdateAfterStart(layout, verifier) {
96
+ const coordinated = markManagedCommitDecision(layout);
97
+ const version = completePendingCoreUpdateAfterStart(layout, verifier);
98
+ clearManagedCommitDecision(layout, coordinated);
99
+ return version;
100
+ }
101
+ export function finalizeCoordinatedCoreUpdate(layout, verifier) {
102
+ const coordinated = markManagedCommitDecision(layout);
103
+ finalizeCoreUpdateTransaction(layout, verifier);
104
+ clearManagedCommitDecision(layout, coordinated);
105
+ }
@@ -0,0 +1,245 @@
1
+ import fs from "node:fs";
2
+ import { assertCoreInstallationConsistent, coreInstalled, currentCoreVersion, stageCore, verifyCoreExecutable, } from "./core.js";
3
+ import { validateCoreConfigFile, validateCoreConfigTextWithExecutable, } from "./core-config-validation.js";
4
+ import { commitCoreUpdate, readCoreUpdateTransaction, } from "./core-update.js";
5
+ import { finalizeCoordinatedCoreUpdate } from "./core-update-coordination.js";
6
+ import { SashDaemonClient } from "./daemon-client.js";
7
+ import { ensureDaemon, evaluateDaemon, prepareDaemonMaintenance, } from "./daemon-lifecycle.js";
8
+ import { MIHOMO_REPO, resolveLatestTag } from "./github.js";
9
+ import { log } from "./log.js";
10
+ import { readManagedStateTransactionStatus, retainManagedStateTransaction, rollbackRetainedManagedStateTransaction, } from "./managed-state-transaction.js";
11
+ import { runOfflineMutation } from "./offline-mutation.js";
12
+ import { ProfileConflictError, ProfileService } from "./profile-service.js";
13
+ import { assertCoreControllerVacant } from "./runtime-recovery.js";
14
+ import { loadSettings } from "./settings.js";
15
+ import { withStateLock } from "./state-lock.js";
16
+ import { CoreSupervisor } from "./supervisor.js";
17
+ export function prepareUpdateMaintenance(ctx, deps = {}) {
18
+ return prepareDaemonMaintenance(ctx.layout, ctx.settings, "Core update", deps);
19
+ }
20
+ function defaultCoreVerifier(exe, expectedVersion) {
21
+ verifyCoreExecutable(exe, 5000, expectedVersion);
22
+ }
23
+ function installedTargetIsVerified(ctx, target, verifier) {
24
+ if (currentCoreVersion(ctx.layout) !== target ||
25
+ !coreInstalled(ctx.layout) ||
26
+ fs.existsSync(ctx.layout.coreInstallTransactionFile) ||
27
+ fs.existsSync(ctx.layout.coreUpdateTransactionFile) ||
28
+ readManagedStateTransactionStatus(ctx.layout) ||
29
+ fs.existsSync(`${ctx.layout.coreExe}.bak`) ||
30
+ fs.existsSync(`${ctx.layout.coreExe}.repair.bak`) ||
31
+ fs.existsSync(`${ctx.layout.installFile}.repair.bak`)) {
32
+ return false;
33
+ }
34
+ try {
35
+ verifier(ctx.layout.coreExe, target);
36
+ return true;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ export async function runCoreUpdate(ctx, opts = {}, deps = {}) {
43
+ const target = opts.version ?? (await (deps.resolveLatestTag ?? resolveLatestTag)(MIHOMO_REPO));
44
+ const current = currentCoreVersion(ctx.layout);
45
+ const verifier = deps.verifyExecutable ?? defaultCoreVerifier;
46
+ if (!opts.force) {
47
+ const verified = await withStateLock(ctx.layout.runtimeOperationLockFile, { purpose: "verify installed Core", timeoutMs: 30_000 }, () => installedTargetIsVerified(ctx, target, verifier));
48
+ if (verified) {
49
+ log.ok(`core is already up to date (${target})`);
50
+ return;
51
+ }
52
+ }
53
+ log.info(`updating core: ${current || "(none)"} → ${target}`);
54
+ log.info("downloading and validating the replacement binary...");
55
+ const staged = await (deps.stageCore ?? stageCore)({ layout: ctx.layout, tag: target });
56
+ try {
57
+ await withStateLock(ctx.layout.runtimeOperationLockFile, { purpose: "update Core runtime", timeoutMs: 180_000 }, async () => {
58
+ if (!opts.force && installedTargetIsVerified(ctx, target, verifier)) {
59
+ log.ok(`core is already up to date (${target})`);
60
+ return;
61
+ }
62
+ ctx.settings = loadSettings(ctx.layout);
63
+ const maintenance = await prepareUpdateMaintenance(ctx, deps);
64
+ await commitStagedCoreUpdate(ctx, staged, maintenance, deps, opts.force === true, verifier);
65
+ });
66
+ }
67
+ finally {
68
+ fs.rmSync(staged.exe, { force: true });
69
+ }
70
+ }
71
+ async function commitStagedCoreUpdate(ctx, staged, maintenance, deps, forceRepair, verifier) {
72
+ let updateError;
73
+ let resultVersion;
74
+ let pendingStartupValidation = false;
75
+ try {
76
+ await runOfflineMutation(ctx, "prepare Core update profile", () => {
77
+ if (!forceRepair)
78
+ assertCoreInstallationConsistent(ctx.layout);
79
+ }, {
80
+ reconcileRuntime: {
81
+ ...(maintenance.legacyDaemon ? { legacyDaemon: true } : {}),
82
+ verifyControllerVacant: true,
83
+ ...(deps.runtimeRecovery ? { deps: deps.runtimeRecovery } : {}),
84
+ },
85
+ migrateProfiles: true,
86
+ });
87
+ const profileService = new ProfileService({
88
+ layout: ctx.layout,
89
+ settings: () => ctx.settings,
90
+ validateConfig: (generated) => (deps.validateConfigText ?? validateCoreConfigTextWithExecutable)(staged.exe, generated.yaml, ctx.layout),
91
+ });
92
+ const commitPreparedUpdate = async (attempt = 0) => {
93
+ const preparedProfile = await profileService.prepareActiveReload();
94
+ let callbackEntered = false;
95
+ try {
96
+ return await runOfflineMutation(ctx, "commit Core update", () => profileService.withPreparedActiveReloadPublication(preparedProfile, async (publication) => {
97
+ await assertCoreControllerVacant(ctx.settings, deps.runtimeRecovery?.controllerReachable);
98
+ callbackEntered = true;
99
+ await retainManagedStateTransaction(ctx.layout, {
100
+ index: publication.index,
101
+ ...(publication.profile
102
+ ? {
103
+ profile: {
104
+ id: publication.profile.id,
105
+ yamlText: publication.profile.yamlText,
106
+ },
107
+ }
108
+ : {}),
109
+ config: publication.config,
110
+ reloadRuntime: false,
111
+ });
112
+ let coreCommitStarted = false;
113
+ try {
114
+ await (deps.validateConfigFile ?? validateCoreConfigFile)(staged.exe, ctx.layout.configFile, ctx.layout);
115
+ let activeVerifier;
116
+ const runtime = maintenance.coreWasRunning
117
+ ? {
118
+ verifyRuntime: true,
119
+ stop: async () => {
120
+ await activeVerifier?.stop();
121
+ activeVerifier = undefined;
122
+ },
123
+ startAndVerify: async (expectedVersion) => {
124
+ activeVerifier = deps.createVerificationSupervisor
125
+ ? deps.createVerificationSupervisor({
126
+ layout: ctx.layout,
127
+ settings: () => ctx.settings,
128
+ expectedVersion,
129
+ })
130
+ : new CoreSupervisor({
131
+ layout: ctx.layout,
132
+ settings: () => ctx.settings,
133
+ expectedVersion,
134
+ });
135
+ const started = await activeVerifier.start();
136
+ log.info(`Core ${expectedVersion} health check passed (PID=${started.pid})`);
137
+ },
138
+ }
139
+ : undefined;
140
+ try {
141
+ coreCommitStarted = true;
142
+ return await commitCoreUpdate({
143
+ layout: ctx.layout,
144
+ staged,
145
+ runtime,
146
+ forceRepair,
147
+ verifyExecutable: verifier,
148
+ beforeRollback: () => {
149
+ rollbackRetainedManagedStateTransaction(ctx.layout);
150
+ },
151
+ });
152
+ }
153
+ finally {
154
+ await activeVerifier?.stop();
155
+ }
156
+ }
157
+ catch (err) {
158
+ if (!coreCommitStarted) {
159
+ try {
160
+ rollbackRetainedManagedStateTransaction(ctx.layout);
161
+ }
162
+ catch (rollbackErr) {
163
+ throw new Error(`${err.message}; managed-state rollback also failed: ${rollbackErr.message}`);
164
+ }
165
+ }
166
+ throw err;
167
+ }
168
+ }), {
169
+ ...(forceRepair ? { allowInconsistentCore: "force-update" } : {}),
170
+ migrateProfiles: true,
171
+ });
172
+ }
173
+ catch (err) {
174
+ if (callbackEntered || !(err instanceof ProfileConflictError) || attempt >= 1)
175
+ throw err;
176
+ return commitPreparedUpdate(attempt + 1);
177
+ }
178
+ };
179
+ const result = await commitPreparedUpdate();
180
+ resultVersion = result.version;
181
+ pendingStartupValidation = result.pendingStartupValidation;
182
+ }
183
+ catch (err) {
184
+ updateError = err;
185
+ }
186
+ let recoveryError;
187
+ if (updateError) {
188
+ try {
189
+ await runOfflineMutation(ctx, "recover failed Core update", () => undefined, {
190
+ reconcileRuntime: {
191
+ ...(deps.runtimeRecovery ? { deps: deps.runtimeRecovery } : {}),
192
+ },
193
+ });
194
+ }
195
+ catch (err) {
196
+ recoveryError = err;
197
+ }
198
+ }
199
+ ctx.settings = loadSettings(ctx.layout);
200
+ let restartError;
201
+ if (maintenance.daemonWasRunning && recoveryError) {
202
+ restartError = new Error(`runtime was not restarted because update rollback ownership remains unresolved: ${recoveryError.message}`);
203
+ }
204
+ else if (maintenance.daemonWasRunning) {
205
+ try {
206
+ await (deps.ensureDaemon ?? ensureDaemon)({ layout: ctx.layout, settings: ctx.settings });
207
+ ctx.settings = loadSettings(ctx.layout);
208
+ if (maintenance.coreWasRunning) {
209
+ const daemon = await (deps.evaluateDaemon ?? evaluateDaemon)(ctx.layout, ctx.settings);
210
+ if (daemon.kind !== "healthy") {
211
+ throw new Error("sashd did not become healthy during update restoration");
212
+ }
213
+ const client = (deps.clientFactory ?? ((port, secret) => new SashDaemonClient(port, secret)))(daemon.port, ctx.settings.daemonSecret);
214
+ const started = await client.startCore();
215
+ log.info(`runtime restored (PID=${started.pid})`);
216
+ }
217
+ }
218
+ catch (err) {
219
+ restartError = err;
220
+ }
221
+ }
222
+ if (updateError) {
223
+ const suffix = recoveryError
224
+ ? `; update recovery failed: ${recoveryError.message}${maintenance.daemonWasRunning ? "; runtime was not restarted" : ""}`
225
+ : restartError
226
+ ? `; runtime restoration failed: ${restartError.message}`
227
+ : "";
228
+ log.warn("update failed; the previous binary and install metadata were restored when possible");
229
+ throw new Error(`${updateError.message}${suffix}`);
230
+ }
231
+ if (restartError) {
232
+ const attemptedVersion = resultVersion ?? staged.version;
233
+ const restoredVersion = currentCoreVersion(ctx.layout);
234
+ if (!readCoreUpdateTransaction(ctx.layout) && restoredVersion !== attemptedVersion) {
235
+ throw new Error(`Core ${attemptedVersion} failed during runtime restoration and was rolled back${restoredVersion ? ` to ${restoredVersion}` : ""}: ${restartError.message}`);
236
+ }
237
+ throw new Error(`Core ${attemptedVersion} was installed and verified, but runtime restoration failed: ${restartError.message}. The previous binary remains in the rollback slot.`);
238
+ }
239
+ if (pendingStartupValidation) {
240
+ log.ok(`core ${resultVersion ?? staged.version} staged; rollback is retained until the next successful \`sash start\``);
241
+ return;
242
+ }
243
+ finalizeCoordinatedCoreUpdate(ctx.layout, verifier);
244
+ log.ok(`core updated to ${resultVersion ?? staged.version}`);
245
+ }