@dylanpiercey/work 0.0.119 → 0.0.121

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 (262) hide show
  1. package/README.md +35 -8
  2. package/bin/work.mjs +380 -41
  3. package/dist/_B1lVb01g.js +2 -0
  4. package/dist/_Bjh5JEm7.js +2 -0
  5. package/dist/_CfwTkTbL.js +352 -0
  6. package/dist/{_BOIUqihs.js → _CivNK9fo.js} +20 -18
  7. package/dist/{_DE2RUdbF.js → _Cr71Xd0d.js} +8 -3
  8. package/dist/_DJyHc1w-.js +1739 -0
  9. package/dist/{_BIhoUpsW.js → _DnaZVHJ7.js} +95 -94
  10. package/dist/_Dpa8WFuv.js +2 -0
  11. package/dist/index.mjs +4301 -4236
  12. package/dist/public/assets/{_BSsp7Z4i2.js → _-zdUyUYM2.js} +1 -1
  13. package/dist/public/assets/_-zdUyUYM2.js.br +0 -0
  14. package/dist/public/assets/{_LUNsNRnJ2.js → _0L2K8sFq2.js} +1 -1
  15. package/dist/public/assets/_0L2K8sFq2.js.br +0 -0
  16. package/dist/public/assets/{_BE3wNPJo.js → _42jdam49.js} +1 -1
  17. package/dist/public/assets/_42jdam49.js.br +0 -0
  18. package/dist/public/assets/{_CIFN7c1t2.js → _9dJ8BnyZ2.js} +1 -1
  19. package/dist/public/assets/_9dJ8BnyZ2.js.br +0 -0
  20. package/dist/public/assets/{_CqDb111T.js → _B03G1Nvs.js} +1 -1
  21. package/dist/public/assets/_B03G1Nvs.js.br +0 -0
  22. package/dist/public/assets/{_Dzy9r1lm.js → _B0dZ7_qK.js} +1 -1
  23. package/dist/public/assets/_B0dZ7_qK.js.br +0 -0
  24. package/dist/public/assets/{_c1YZLlfE.js → _B1VuWiKK.js} +1 -1
  25. package/dist/public/assets/_B1VuWiKK.js.br +0 -0
  26. package/dist/public/assets/{_Bp1b0XWB2.js → _B5nrQZIq2.js} +1 -1
  27. package/dist/public/assets/_B5nrQZIq2.js.br +0 -0
  28. package/dist/public/assets/{_Bq2fn_8D.js → _B8ZQ8inF.js} +1 -1
  29. package/dist/public/assets/_B8ZQ8inF.js.br +0 -0
  30. package/dist/public/assets/{_CvnxxlPb.js → _B9e51G1K.js} +1 -1
  31. package/dist/public/assets/_B9e51G1K.js.br +0 -0
  32. package/dist/public/assets/{_CUjnrp_u2.js → _BH015W2i2.js} +1 -1
  33. package/dist/public/assets/_BH015W2i2.js.br +0 -0
  34. package/dist/public/assets/{_B62GzYdT.js → _BHjcfVXB.js} +6 -6
  35. package/dist/public/assets/_BHjcfVXB.js.br +0 -0
  36. package/dist/public/assets/{_DMHXo8uX.js → _BNIilCsC.js} +1 -1
  37. package/dist/public/assets/_BNIilCsC.js.br +0 -0
  38. package/dist/public/assets/{_szXVEFuj2.js → _BPgL37mi2.js} +1 -1
  39. package/dist/public/assets/_BPgL37mi2.js.br +0 -0
  40. package/dist/public/assets/{_Ctuh4IDL2.js → _BR3D4nzj2.js} +1 -1
  41. package/dist/public/assets/_BR3D4nzj2.js.br +0 -0
  42. package/dist/public/assets/{_BkTJUq-u.js → _BRSJwfZ3.js} +1 -1
  43. package/dist/public/assets/{_DS628Dnv.js → _BSFENb4l.js} +1 -1
  44. package/dist/public/assets/_BSFENb4l.js.br +0 -0
  45. package/dist/public/assets/{_Btt4k5w3.js → _BSWpz0dK.js} +1 -1
  46. package/dist/public/assets/_BSWpz0dK.js.br +0 -0
  47. package/dist/public/assets/{_N0vzdKCD.js → _BUOWar32.js} +1 -1
  48. package/dist/public/assets/_BUOWar32.js.br +0 -0
  49. package/dist/public/assets/{_1bmjwuWH.js → _BZWlvMSJ.js} +1 -1
  50. package/dist/public/assets/_BZWlvMSJ.js.br +0 -0
  51. package/dist/public/assets/{_BlVrxgss2.js → _B_dMglA92.js} +1 -1
  52. package/dist/public/assets/_B_dMglA92.js.br +0 -0
  53. package/dist/public/assets/{_XgOGaUKD.js → _Beb6Lb1w.js} +1 -1
  54. package/dist/public/assets/_Beb6Lb1w.js.br +0 -0
  55. package/dist/public/assets/{_CXQ_K2Vh2.js → _BjIMoOV-2.js} +1 -1
  56. package/dist/public/assets/_BjIMoOV-2.js.br +0 -0
  57. package/dist/public/assets/{_Dhu480me.js → _BlLuOKRT.js} +1 -1
  58. package/dist/public/assets/_BlLuOKRT.js.br +0 -0
  59. package/dist/public/assets/{_BijwdXvp.js → _BlXwius8.js} +1 -1
  60. package/dist/public/assets/_BlXwius8.js.br +0 -0
  61. package/dist/public/assets/{_DBJ43cPF.js → _Bmt76eWT.js} +1 -1
  62. package/dist/public/assets/_Bmt76eWT.js.br +0 -0
  63. package/dist/public/assets/{_CkuVE2zK.js → _BoS5LU5l.js} +1 -1
  64. package/dist/public/assets/_BoS5LU5l.js.br +0 -0
  65. package/dist/public/assets/{_DtLQVzKB2.js → _BsNKabYu2.js} +1 -1
  66. package/dist/public/assets/_BsNKabYu2.js.br +0 -0
  67. package/dist/public/assets/{_CEWjw4lq.js → _C0OXw3Vy.js} +1 -1
  68. package/dist/public/assets/_C0OXw3Vy.js.br +0 -0
  69. package/dist/public/assets/{_CpRKjPVO2.js → _CBUJNdeq2.js} +1 -1
  70. package/dist/public/assets/_CBUJNdeq2.js.br +0 -0
  71. package/dist/public/assets/{_CZzZOY4P2.js → _CCNhM32c2.js} +1 -1
  72. package/dist/public/assets/_CCNhM32c2.js.br +0 -0
  73. package/dist/public/assets/{_BgnvPND_.js → _CFBaZNUT.js} +1 -1
  74. package/dist/public/assets/_CFBaZNUT.js.br +0 -0
  75. package/dist/public/assets/{_CJT7ujcv2.js → _CJHqzl--2.js} +1 -1
  76. package/dist/public/assets/_CJHqzl--2.js.br +0 -0
  77. package/dist/public/assets/{_duTaTgEk2.js → _CMGgQ-4H2.js} +1 -1
  78. package/dist/public/assets/_CMGgQ-4H2.js.br +0 -0
  79. package/dist/public/assets/{_5YlmpA6X.js → _CPX0D-my.js} +1 -1
  80. package/dist/public/assets/_CPX0D-my.js.br +0 -0
  81. package/dist/public/assets/{_BYYDEuFR2.js → _CRuHDn_O2.js} +1 -1
  82. package/dist/public/assets/_CRuHDn_O2.js.br +0 -0
  83. package/dist/public/assets/{_CnKbN1QV.js → _CUOao_Hj.js} +1 -1
  84. package/dist/public/assets/_CUOao_Hj.js.br +0 -0
  85. package/dist/public/assets/{_CZejW_fq.js → _CUVw5V4z.js} +1 -1
  86. package/dist/public/assets/_CUVw5V4z.js.br +0 -0
  87. package/dist/public/assets/{_DO4n-NwM.js → _CYMGOhCb.js} +1 -1
  88. package/dist/public/assets/_CYMGOhCb.js.br +0 -0
  89. package/dist/public/assets/{_ER-_pvil.js → _CZfap_Dj.js} +1 -1
  90. package/dist/public/assets/_CZfap_Dj.js.br +0 -0
  91. package/dist/public/assets/{_DYngS949.js → _CbNmSe5k.js} +1 -1
  92. package/dist/public/assets/_CbNmSe5k.js.br +0 -0
  93. package/dist/public/assets/{_DKciAPQl.js → _CdtuhZSc.js} +1 -1
  94. package/dist/public/assets/_CdtuhZSc.js.br +0 -0
  95. package/dist/public/assets/{_B7w9Jp5h.js → _ClhApln3.js} +1 -1
  96. package/dist/public/assets/_ClhApln3.js.br +0 -0
  97. package/dist/public/assets/{_BD9TBsnM2.js → _CvA5IOAW2.js} +1 -1
  98. package/dist/public/assets/_CvA5IOAW2.js.br +0 -0
  99. package/dist/public/assets/{_Cp-yobQE2.js → _CwZYmUdt2.js} +1 -1
  100. package/dist/public/assets/{_MofKWwY9.js → _CxrcWZPE.js} +1 -1
  101. package/dist/public/assets/_CxrcWZPE.js.br +0 -0
  102. package/dist/public/assets/{_RhYe7Uqa.js → _D-KjGTSW.js} +24 -21
  103. package/dist/public/assets/_D-KjGTSW.js.br +0 -0
  104. package/dist/public/assets/{_BRiOyxeF.js → _D4u4SpiK.js} +1 -1
  105. package/dist/public/assets/_D4u4SpiK.js.br +0 -0
  106. package/dist/public/assets/{_D8raMSvy.js → _D6CjH3JN.js} +1 -1
  107. package/dist/public/assets/_D6CjH3JN.js.br +0 -0
  108. package/dist/public/assets/{_Cym2lUeD2.js → _D7cQLLGK2.js} +1 -1
  109. package/dist/public/assets/_D7cQLLGK2.js.br +0 -0
  110. package/dist/public/assets/{_C8DWM9rr.js → _DBoABI85.js} +1 -1
  111. package/dist/public/assets/_DBoABI85.js.br +0 -0
  112. package/dist/public/assets/{_Rf_7ZEPL.js → _DNcd1xtp.js} +1 -1
  113. package/dist/public/assets/_DNcd1xtp.js.br +0 -0
  114. package/dist/public/assets/{_Bhwci4Zy.js → _DQGTProt.js} +1 -1
  115. package/dist/public/assets/_DQGTProt.js.br +0 -0
  116. package/dist/public/assets/{_B2g0gJmZ.js → _DSIH6_Li.js} +1 -1
  117. package/dist/public/assets/_DSIH6_Li.js.br +0 -0
  118. package/dist/public/assets/{_T2999saD2.js → _DXHf4WHs2.js} +1 -1
  119. package/dist/public/assets/_DXHf4WHs2.js.br +0 -0
  120. package/dist/public/assets/{_CoStmjbL.js → _D_qDhyn2.js} +1 -1
  121. package/dist/public/assets/_D_qDhyn2.js.br +0 -0
  122. package/dist/public/assets/{_CGOYaUuV.js → _DcMfCkmd.js} +1 -1
  123. package/dist/public/assets/_DcMfCkmd.js.br +0 -0
  124. package/dist/public/assets/{_Cv9Hgy8P.js → _DgDEdxEu.js} +1 -1
  125. package/dist/public/assets/_DgDEdxEu.js.br +0 -0
  126. package/dist/public/assets/{_Bsk9ewWF.js → _DlhDim1D.js} +1 -1
  127. package/dist/public/assets/_DlhDim1D.js.br +0 -0
  128. package/dist/public/assets/{_BNQgBkwF.js → _DqLib98x.js} +1 -1
  129. package/dist/public/assets/_DqLib98x.js.br +0 -0
  130. package/dist/public/assets/{_l9vJoVeP.js → _Ds0Qa0YX.js} +1 -1
  131. package/dist/public/assets/_Ds0Qa0YX.js.br +0 -0
  132. package/dist/public/assets/{_iDn41XPe2.js → _DyUvL09k2.js} +1 -1
  133. package/dist/public/assets/_DyUvL09k2.js.br +0 -0
  134. package/dist/public/assets/{_AqmX0pBH2.js → _J6xlcydH2.js} +1 -1
  135. package/dist/public/assets/_J6xlcydH2.js.br +0 -0
  136. package/dist/public/assets/{_Dni1s_3Q2.js → _MInulccf2.js} +1 -1
  137. package/dist/public/assets/{_BsOtzbIC.js → _QMVFINxi.js} +1 -1
  138. package/dist/public/assets/_QMVFINxi.js.br +0 -0
  139. package/dist/public/assets/{_CWNv7bdi.js → _S9QQtt68.js} +1 -1
  140. package/dist/public/assets/_S9QQtt68.js.br +0 -0
  141. package/dist/public/assets/{_DlZyjzE_.js → _XCJLLGxV.js} +1 -1
  142. package/dist/public/assets/_XCJLLGxV.js.br +0 -0
  143. package/dist/public/assets/{_B2LXCPdE.js → _Y-bTh0dW.js} +1 -1
  144. package/dist/public/assets/_Y-bTh0dW.js.br +0 -0
  145. package/dist/public/assets/{_zPAwS_-H.js → _Y8TOk1pR.js} +1 -1
  146. package/dist/public/assets/_Y8TOk1pR.js.br +0 -0
  147. package/dist/public/assets/{_CzI2QhH4.js → _YetFhXkM.js} +1 -1
  148. package/dist/public/assets/_YetFhXkM.js.br +0 -0
  149. package/dist/public/assets/{_O8R4l7yw2.js → _YmJopjX92.js} +1 -1
  150. package/dist/public/assets/_YmJopjX92.js.br +0 -0
  151. package/dist/public/assets/{_Du5pjG7G.js → _mbzaOqik.js} +1 -1
  152. package/dist/public/assets/_mbzaOqik.js.br +0 -0
  153. package/dist/public/assets/{_qj_i_cqe.js → _rMcfapaa.js} +1 -1
  154. package/dist/public/assets/_rMcfapaa.js.br +0 -0
  155. package/dist/public/assets/{_yTOoKAfM2.js → _rRtbbfKJ2.js} +1 -1
  156. package/dist/public/assets/_rRtbbfKJ2.js.br +0 -0
  157. package/dist/public/assets/{_BD68UZIf.js → _sSWgnHeU.js} +1 -1
  158. package/dist/public/assets/_sSWgnHeU.js.br +0 -0
  159. package/dist/public/assets/{_CegJB2vl.js → _xOeYy_3v.js} +1 -1
  160. package/dist/public/assets/_xOeYy_3v.js.br +0 -0
  161. package/dist/public/assets/app-page-BWf0w6W9.css +1 -0
  162. package/dist/public/assets/app-page-BWf0w6W9.css.br +0 -0
  163. package/dist/public/assets/diff-view-BZaYYEDK.css +1 -0
  164. package/dist/public/assets/diff-view-BZaYYEDK.css.br +0 -0
  165. package/dist/public/assets/new-K-7z0qE8.js +1 -0
  166. package/dist/public/assets/settings-K-7z0qE8.js +1 -0
  167. package/dist/public/assets/w-K-7z0qE8.js +1 -0
  168. package/dist/public/assets/w._-D5d4klUv.js +1 -0
  169. package/dist/public/assets/w._-K-7z0qE8.js +1 -0
  170. package/lib/review/markdown.mjs +6 -2
  171. package/lib/setup/catalog.d.mts +3 -1
  172. package/lib/setup/catalog.mjs +11 -2
  173. package/lib/setup/doctor.mjs +96 -20
  174. package/lib/setup/install.d.mts +9 -3
  175. package/lib/setup/install.mjs +68 -39
  176. package/lib/share/manifest.d.mts +27 -0
  177. package/lib/share/manifest.mjs +144 -0
  178. package/package.json +6 -2
  179. package/scripts/migrate.mjs +101 -30
  180. package/dist/_BIDn_RW1.js +0 -2
  181. package/dist/public/assets/_1bmjwuWH.js.br +0 -0
  182. package/dist/public/assets/_5YlmpA6X.js.br +0 -0
  183. package/dist/public/assets/_AqmX0pBH2.js.br +0 -0
  184. package/dist/public/assets/_B2LXCPdE.js.br +0 -0
  185. package/dist/public/assets/_B2g0gJmZ.js.br +0 -0
  186. package/dist/public/assets/_B62GzYdT.js.br +0 -0
  187. package/dist/public/assets/_B7w9Jp5h.js.br +0 -0
  188. package/dist/public/assets/_BD68UZIf.js.br +0 -0
  189. package/dist/public/assets/_BD9TBsnM2.js.br +0 -0
  190. package/dist/public/assets/_BE3wNPJo.js.br +0 -0
  191. package/dist/public/assets/_BNQgBkwF.js.br +0 -0
  192. package/dist/public/assets/_BRiOyxeF.js.br +0 -0
  193. package/dist/public/assets/_BSsp7Z4i2.js.br +0 -0
  194. package/dist/public/assets/_BYYDEuFR2.js.br +0 -0
  195. package/dist/public/assets/_BgnvPND_.js.br +0 -0
  196. package/dist/public/assets/_Bhwci4Zy.js.br +0 -0
  197. package/dist/public/assets/_BijwdXvp.js.br +0 -0
  198. package/dist/public/assets/_BlVrxgss2.js.br +0 -0
  199. package/dist/public/assets/_Bp1b0XWB2.js.br +0 -0
  200. package/dist/public/assets/_Bq2fn_8D.js.br +0 -0
  201. package/dist/public/assets/_BsOtzbIC.js.br +0 -0
  202. package/dist/public/assets/_Bsk9ewWF.js.br +0 -0
  203. package/dist/public/assets/_Btt4k5w3.js.br +0 -0
  204. package/dist/public/assets/_C8DWM9rr.js.br +0 -0
  205. package/dist/public/assets/_CEWjw4lq.js.br +0 -0
  206. package/dist/public/assets/_CGOYaUuV.js.br +0 -0
  207. package/dist/public/assets/_CIFN7c1t2.js.br +0 -0
  208. package/dist/public/assets/_CJT7ujcv2.js.br +0 -0
  209. package/dist/public/assets/_CUjnrp_u2.js.br +0 -0
  210. package/dist/public/assets/_CWNv7bdi.js.br +0 -0
  211. package/dist/public/assets/_CXQ_K2Vh2.js.br +0 -0
  212. package/dist/public/assets/_CZejW_fq.js.br +0 -0
  213. package/dist/public/assets/_CZzZOY4P2.js.br +0 -0
  214. package/dist/public/assets/_CegJB2vl.js.br +0 -0
  215. package/dist/public/assets/_CkuVE2zK.js.br +0 -0
  216. package/dist/public/assets/_CnKbN1QV.js.br +0 -0
  217. package/dist/public/assets/_CoStmjbL.js.br +0 -0
  218. package/dist/public/assets/_CpRKjPVO2.js.br +0 -0
  219. package/dist/public/assets/_CqDb111T.js.br +0 -0
  220. package/dist/public/assets/_Ctuh4IDL2.js.br +0 -0
  221. package/dist/public/assets/_Cv9Hgy8P.js.br +0 -0
  222. package/dist/public/assets/_CvnxxlPb.js.br +0 -0
  223. package/dist/public/assets/_Cym2lUeD2.js.br +0 -0
  224. package/dist/public/assets/_CzI2QhH4.js.br +0 -0
  225. package/dist/public/assets/_D8raMSvy.js.br +0 -0
  226. package/dist/public/assets/_DBJ43cPF.js.br +0 -0
  227. package/dist/public/assets/_DKciAPQl.js.br +0 -0
  228. package/dist/public/assets/_DMHXo8uX.js.br +0 -0
  229. package/dist/public/assets/_DO4n-NwM.js.br +0 -0
  230. package/dist/public/assets/_DS628Dnv.js.br +0 -0
  231. package/dist/public/assets/_DYngS949.js.br +0 -0
  232. package/dist/public/assets/_Dhu480me.js.br +0 -0
  233. package/dist/public/assets/_DlZyjzE_.js.br +0 -0
  234. package/dist/public/assets/_DtLQVzKB2.js.br +0 -0
  235. package/dist/public/assets/_Du5pjG7G.js.br +0 -0
  236. package/dist/public/assets/_Dzy9r1lm.js.br +0 -0
  237. package/dist/public/assets/_ER-_pvil.js.br +0 -0
  238. package/dist/public/assets/_LUNsNRnJ2.js.br +0 -0
  239. package/dist/public/assets/_MofKWwY9.js.br +0 -0
  240. package/dist/public/assets/_N0vzdKCD.js.br +0 -0
  241. package/dist/public/assets/_O8R4l7yw2.js.br +0 -0
  242. package/dist/public/assets/_Rf_7ZEPL.js.br +0 -0
  243. package/dist/public/assets/_RhYe7Uqa.js.br +0 -0
  244. package/dist/public/assets/_T2999saD2.js.br +0 -0
  245. package/dist/public/assets/_XgOGaUKD.js.br +0 -0
  246. package/dist/public/assets/_c1YZLlfE.js.br +0 -0
  247. package/dist/public/assets/_duTaTgEk2.js.br +0 -0
  248. package/dist/public/assets/_iDn41XPe2.js.br +0 -0
  249. package/dist/public/assets/_l9vJoVeP.js.br +0 -0
  250. package/dist/public/assets/_qj_i_cqe.js.br +0 -0
  251. package/dist/public/assets/_szXVEFuj2.js.br +0 -0
  252. package/dist/public/assets/_yTOoKAfM2.js.br +0 -0
  253. package/dist/public/assets/_zPAwS_-H.js.br +0 -0
  254. package/dist/public/assets/app-page-BXbewHHv.css +0 -1
  255. package/dist/public/assets/app-page-BXbewHHv.css.br +0 -0
  256. package/dist/public/assets/diff-view-BJ1RAVYB.css +0 -1
  257. package/dist/public/assets/diff-view-BJ1RAVYB.css.br +0 -0
  258. package/dist/public/assets/new-DUXTU7jB.js +0 -1
  259. package/dist/public/assets/settings-DUXTU7jB.js +0 -1
  260. package/dist/public/assets/w-DUXTU7jB.js +0 -1
  261. package/dist/public/assets/w._-DUXTU7jB.js +0 -1
  262. package/dist/public/assets/w._-bwrmfhu0.js +0 -1
@@ -0,0 +1,352 @@
1
+ import { U as subscriberCount, V as publish, W as subscriberJoins, dt as all, ft as db, g as sessionStates, l as listSessions } from "./_Cr71Xd0d.js";
2
+ import { A as portsByWorkspace, J as hostToken, K as forgetToken, M as headOid, Q as ownsBranch, R as repoParent, V as DEFAULT_HOST, Y as joinRepo, Z as splitRepo, h as listWorkspaces, o as pushFailureFor, q as graphqlUrl, z as swr } from "./_DJyHc1w-.js";
3
+ import path from "node:path";
4
+ //#region src/server/github-api.ts
5
+ /** Run a GraphQL query against one host.
6
+ *
7
+ * Partial failures come back as a populated `errors` alongside usable `data`
8
+ * — one repo deleted, renamed, or outside this token's scope. Callers want
9
+ * the repos that did resolve, so `data` wins whenever it is present; this
10
+ * matches how the `gh` path treated a non-zero exit with usable stdout. */
11
+ async function graphql(query, host = DEFAULT_HOST) {
12
+ const res = await fetch(graphqlUrl(host), {
13
+ method: "POST",
14
+ headers: {
15
+ authorization: `Bearer ${await hostToken(host)}`,
16
+ "content-type": "application/json",
17
+ accept: "application/vnd.github+json",
18
+ "user-agent": "work"
19
+ },
20
+ body: JSON.stringify({ query })
21
+ });
22
+ if (!res.ok && res.status !== 200) {
23
+ if (res.status === 401 || res.status === 403) forgetToken(host);
24
+ throw new Error(`github graphql ${res.status}`);
25
+ }
26
+ const body = await res.json();
27
+ if (!body?.data) throw new Error(body?.errors?.[0]?.message || "graphql response without data");
28
+ return body.data;
29
+ }
30
+ //#endregion
31
+ //#region src/server/prs.ts
32
+ var TTL_MS$1 = 12e4;
33
+ /** PRs whose head is the workspace branch or one of its `-topic` lanes.
34
+ * `exclude` lists other workspaces' branches — slugs are user-chosen, so
35
+ * another workspace's branch can extend this prefix and its PRs must not
36
+ * be attributed here. */
37
+ function prsForBranchPrefix(prs, prefix, exclude = []) {
38
+ return prs.filter((pr) => ownsBranch(pr.headRefName, prefix) && !exclude.some((x) => ownsBranch(pr.headRefName, x)));
39
+ }
40
+ /** `[host/]owner/name#branch` — the host prefix travels with the name. */
41
+ var branchKey = (fullName, branch) => `${fullName}#${branch}`;
42
+ /** One workspace's PRs in one repo — its branch and every `-topic` lane, in
43
+ * every state. Asking for the workspace's own branches is what keeps this
44
+ * proportional to the workspace rather than to the repo: the alternative,
45
+ * a recency window over all of a repo's PRs, fetched roughly twice what it
46
+ * kept here and still aged a quiet workspace out on a busy repo.
47
+ * Key: `fullName#branch`. */
48
+ var branchPrs = swr(TTL_MS$1, async (key, refresh) => {
49
+ const sep = key.indexOf("#");
50
+ const fullName = key.slice(0, sep);
51
+ const { host, name } = splitRepo(fullName);
52
+ const upstream = await repoParent(fullName);
53
+ return (await searchPrs(host, upstream ? [name, splitRepo(upstream).name] : [name], [key.slice(sep + 1)], refresh)).filter((n) => ownsNode(n, fullName, upstream));
54
+ });
55
+ /** A workspace repo's PRs, minus any belonging to a workspace whose branch
56
+ * extends this one's. `since` is an imported workspace's arrival: a
57
+ * restored or joined workspace keeps its branch name, so the branch's whole
58
+ * history on GitHub matches — finished PRs from before it arrived here are
59
+ * its previous life's, not chips to dismiss again. Open PRs always show;
60
+ * hiding live work would be the worse mistake. */
61
+ async function workspacePrs(fullName, branch, foreign, refresh = false, since) {
62
+ return prsForBranchPrefix(await branchPrs(branchKey(fullName, branch), refresh).catch(() => []), branch, foreign).filter((pr) => !since || pr.state === "OPEN" || !pr.updatedAt || pr.updatedAt >= since);
63
+ }
64
+ /** Refresh many repos' PRs with one GraphQL request (vs one `gh pr list`
65
+ * process per repo) — called from the status watcher for active
66
+ * workspaces so open/merge/close flips surface in ~watch-tick time
67
+ * instead of waiting out the per-repo TTL. Throttled internally. */
68
+ var BATCH_MIN_MS = 15e3;
69
+ /** After a detected local push, poll fast for a bit — the PR appears
70
+ * and merges/closes in the minutes right after. */
71
+ var HOT_MIN_MS = 3e3;
72
+ var HOT_WINDOW_MS = 18e4;
73
+ var lastBatch = 0;
74
+ var hotUntil = 0;
75
+ function notePushActivity() {
76
+ hotUntil = Date.now() + HOT_WINDOW_MS;
77
+ }
78
+ /** Inside the post-push window where PR state is actively flipping. */
79
+ function inHotWindow() {
80
+ return Date.now() < hotUntil;
81
+ }
82
+ /** `headRefOid` is the PR's own head commit, which is what the UI compares
83
+ * against. Reaching through `headRef` gets the branch's *current* tip
84
+ * instead — a different commit entirely once a workspace branch is reused,
85
+ * and `headRef { oid }` is not even a field, so GitHub answers 200 with an
86
+ * `errors` array and no `data`. */
87
+ var GQL_PR_FIELDS = "number title url headRefName baseRefName isDraft state headRefOid updatedAt";
88
+ /** Does this search hit belong to `fullName`'s workspace repo? Directly when
89
+ * the PR lives in that repo *with its head branch there too*; via the fork
90
+ * case when it lives in `upstream` with its head on the fork. The head check
91
+ * on both arms keeps one PR from being claimed twice when a workspace holds
92
+ * a fork and its upstream, and keeps a same-named branch pushed by someone
93
+ * else's fork from being claimed at all. A null headRepository (head repo
94
+ * deleted) stays with the PR's own repo. */
95
+ function ownsNode(n, fullName, upstream) {
96
+ const head = n.headRepository?.nameWithOwner;
97
+ return n.repository.nameWithOwner === fullName && (!head || head === fullName) || !!upstream && n.repository.nameWithOwner === upstream && head === fullName;
98
+ }
99
+ /** Dedup key: PR numbers restart per repo, and a fork target's list spans
100
+ * the fork and its upstream. */
101
+ var nodeKey = (p) => `${p.repository.nameWithOwner}#${p.number}`;
102
+ /** Search's `head:` matches a branch *and* its `-topic` lanes, so one term
103
+ * covers a whole workspace, and repeated terms are OR'd within a qualifier
104
+ * while different qualifiers are AND'd — `repo:a repo:b head:x head:y`
105
+ * reads as (a|b) and (x|y). That is what lets every workspace share one
106
+ * request instead of needing an alias per repo and per branch. */
107
+ function searchTerms(repos, branches) {
108
+ return [...repos.map((r) => `repo:${r}`), ...branches.map((b) => `head:${b}`)].join(" ");
109
+ }
110
+ /** Finished PRs are asked for by recency, open ones are not: a merged PR
111
+ * only has to outlive its dismissal, while an open PR must stay visible
112
+ * however long it has sat idle — which is exactly what a recency window
113
+ * over the whole repo got wrong. */
114
+ var CLOSED_FIRST = 60;
115
+ var OPEN_FIRST = 100;
116
+ /** Search pages at 100. Open PRs are the page that can genuinely overflow
117
+ * (every workspace's, in one request), so it is followed — bounded, because
118
+ * an unbounded follow is a way to hang the status tick on a bad query. */
119
+ var MAX_OPEN_PAGES = 4;
120
+ var searchField = (alias, query, first, after) => `${alias}: search(query: ${JSON.stringify(query)}, type: ISSUE, first: ${first}${after ? `, after: ${JSON.stringify(after)}` : ""}) { pageInfo { hasNextPage endCursor } nodes { ... on PullRequest { ${GQL_PR_FIELDS} repository { nameWithOwner } headRepository { nameWithOwner } } } }`;
121
+ /** Every PR on the given branches (and their lanes) across the given repos
122
+ * of one host (`repos` are bare `owner/name`). `refresh` is accepted for the
123
+ * swr signature; freshness here is GitHub's, not ours. Nodes come back with
124
+ * the host folded into `repository.nameWithOwner` so callers compare full
125
+ * names. */
126
+ async function searchPrs(host, repos, branches, _refresh) {
127
+ const terms = searchTerms(repos, branches);
128
+ const out = [];
129
+ let cursor;
130
+ for (let page = 0; page < MAX_OPEN_PAGES; page++) {
131
+ const data = await graphql("query {" + searchField("open", `is:pr is:open ${terms}`, OPEN_FIRST, cursor) + (page === 0 ? " " + searchField("done", `is:pr is:closed sort:updated-desc ${terms}`, CLOSED_FIRST) : "") + "}", host);
132
+ const open = data.open;
133
+ const done = data.done;
134
+ for (const node of [...open?.nodes ?? [], ...done?.nodes ?? []]) if (node?.number) out.push({
135
+ ...node,
136
+ repository: { nameWithOwner: joinRepo(host, node.repository.nameWithOwner) },
137
+ headRepository: node.headRepository ? { nameWithOwner: joinRepo(host, node.headRepository.nameWithOwner) } : null
138
+ });
139
+ if (!open?.pageInfo.hasNextPage || !open.pageInfo.endCursor) break;
140
+ cursor = open.pageInfo.endCursor;
141
+ }
142
+ return out;
143
+ }
144
+ async function refreshPrsBatch(targets, force = false) {
145
+ const wanted = [...new Map(targets.filter((t) => t.fullName.includes("/") && t.branch).map((t) => [branchKey(t.fullName, t.branch), t])).values()];
146
+ const minMs = force ? 2e3 : Date.now() < hotUntil ? HOT_MIN_MS : BATCH_MIN_MS;
147
+ if (!wanted.length || Date.now() - lastBatch < minMs) return false;
148
+ const prevBatch = lastBatch;
149
+ lastBatch = Date.now();
150
+ try {
151
+ const upstreams = new Map(await Promise.all(wanted.map(async (t) => [t.fullName, await repoParent(t.fullName)])));
152
+ const byHost = /* @__PURE__ */ new Map();
153
+ for (const t of wanted) {
154
+ const { host, name } = splitRepo(t.fullName);
155
+ const names = (byHost.get(host) || /* @__PURE__ */ new Set()).add(name);
156
+ const upstream = upstreams.get(t.fullName);
157
+ if (upstream) names.add(splitRepo(upstream).name);
158
+ byHost.set(host, names);
159
+ }
160
+ const branches = [...new Set(wanted.map((t) => t.branch))];
161
+ const results = await Promise.all([...byHost].map(([host, names]) => searchPrs(host, [...names], branches).catch((err) => {
162
+ if (byHost.size === 1) throw err;
163
+ return null;
164
+ })));
165
+ const nodes = results.flatMap((r) => r ?? []);
166
+ const failedHosts = new Set([...byHost.keys()].filter((_, i) => results[i] === null));
167
+ for (const target of wanted) {
168
+ if (failedHosts.has(splitRepo(target.fullName).host)) continue;
169
+ const foreign = branches.filter((b) => b !== target.branch);
170
+ const key = branchKey(target.fullName, target.branch);
171
+ const incoming = prsForBranchPrefix(nodes.filter((n) => ownsNode(n, target.fullName, upstreams.get(target.fullName) ?? null)), target.branch, foreign);
172
+ const open = incoming.filter((p) => p.state === "OPEN");
173
+ const openKeys = new Set(open.map(nodeKey));
174
+ const done = /* @__PURE__ */ new Map();
175
+ for (const p of branchPrs.get(key) ?? []) if (p.state !== "OPEN" && !openKeys.has(nodeKey(p))) done.set(nodeKey(p), p);
176
+ for (const p of incoming) if (p.state !== "OPEN" && !openKeys.has(nodeKey(p))) done.set(nodeKey(p), p);
177
+ branchPrs.set(key, [...open, ...done.values()]);
178
+ }
179
+ return true;
180
+ } catch (err) {
181
+ lastBatch = prevBatch;
182
+ throw err;
183
+ }
184
+ }
185
+ /** Confirm a finished (merged/closed) PR so it leaves the manage list. */
186
+ function dismissPr(workspaceId, fullName, number) {
187
+ db.prepare("INSERT OR IGNORE INTO dismissed_prs (workspace_id, full_name, number) VALUES (?, ?, ?)").run(workspaceId, fullName, number);
188
+ }
189
+ /** Drop dismissed PRs from a repo's list — unless one reopened. Keyed by
190
+ * each PR's own repo + number: a fork target's list spans the fork and its
191
+ * upstream (same numbers restart per repo), so a workspace-repo-wide key
192
+ * would let dismissing upstream #N hide the fork's own #N too. Rows written
193
+ * before PRs carried their repo are keyed by the workspace repo alone and
194
+ * still match via that fallback. */
195
+ function withoutDismissed(workspaceId, fullName, list) {
196
+ const dismissed = new Set(all("SELECT full_name, number FROM dismissed_prs WHERE workspace_id = ?", workspaceId).map((row) => `${row.full_name}#${row.number}`));
197
+ return list.filter((pr) => pr.state === "OPEN" || !dismissed.has(`${pr.repository?.nameWithOwner ?? fullName}#${pr.number}`));
198
+ }
199
+ //#endregion
200
+ //#region src/server/status.ts
201
+ var TTL_MS = 2e3;
202
+ var snapshot = null;
203
+ var inflight = null;
204
+ var snapshotGen = 0;
205
+ /** Drop the cached snapshot AND detach any in-flight compute — callers
206
+ * after an invalidation get a computation that starts from fresh state. */
207
+ function invalidateSnapshot() {
208
+ snapshot = null;
209
+ snapshotGen++;
210
+ inflight = null;
211
+ }
212
+ function statusForAll() {
213
+ if (snapshot && Date.now() - snapshot.at < TTL_MS) return Promise.resolve(snapshot.value);
214
+ if (!inflight) {
215
+ const gen = snapshotGen;
216
+ const run = computeStatus().then((value) => {
217
+ if (gen === snapshotGen) snapshot = {
218
+ at: Date.now(),
219
+ value
220
+ };
221
+ return value;
222
+ }).finally(() => {
223
+ if (inflight === run) inflight = null;
224
+ });
225
+ inflight = run;
226
+ }
227
+ return inflight;
228
+ }
229
+ /** Push-based freshness: while any events-bus client is connected,
230
+ * recompute the (shared, deduped) snapshot on a slow tick and publish it
231
+ * only when it actually changed — ports appearing, PR open/merge/close flips reach
232
+ * every client in ~15s instead of waiting out a slow poll, and idle
233
+ * systems push nothing at all. */
234
+ var WATCH_MS = 1e4;
235
+ /** Right after a local push, PR+check state flips within seconds — tick
236
+ * fast so the badge lands while it still feels immediate. */
237
+ var WATCH_HOT_MS = 4e3;
238
+ var watching = false;
239
+ /** Last published state per workspace, so a tick can publish the ones that
240
+ * actually moved. Comparing the whole snapshot instead meant one agent's
241
+ * commit — or any session, port or PR flip in a workspace nobody is
242
+ * looking at — pushed every workspace to every client, which each then
243
+ * re-rendered and re-fingerprinted to conclude nothing of theirs had
244
+ * changed. That amplification grows with the number of workspaces. */
245
+ var lastPushed = /* @__PURE__ */ new Map();
246
+ var lastJoins = -1;
247
+ async function watchTick() {
248
+ if (!subscriberCount()) return;
249
+ try {
250
+ if (await refreshPrsBatch(activeRepoTargets()).catch(() => false)) invalidateSnapshot();
251
+ const value = await statusForAll();
252
+ const joins = subscriberJoins();
253
+ const everything = joins !== lastJoins;
254
+ lastJoins = joins;
255
+ const changed = {};
256
+ for (const [id, ws] of Object.entries(value)) {
257
+ const json = JSON.stringify(ws);
258
+ if (everything || lastPushed.get(id) !== json) changed[id] = ws;
259
+ lastPushed.set(id, json);
260
+ }
261
+ for (const id of lastPushed.keys()) if (!(id in value)) lastPushed.delete(id);
262
+ if (Object.keys(changed).length) publish("status.changed", changed);
263
+ } catch {}
264
+ }
265
+ var watchTimer;
266
+ function scheduleTick(ms) {
267
+ clearTimeout(watchTimer);
268
+ watchTimer = setTimeout(tick, ms);
269
+ watchTimer.unref?.();
270
+ }
271
+ async function tick() {
272
+ await watchTick();
273
+ scheduleTick(inHotWindow() ? WATCH_HOT_MS : WATCH_MS);
274
+ }
275
+ /** Recompute and push right now (e.g. a local push was detected) instead
276
+ * of waiting out the watch tick — and drop the loop into its fast cadence
277
+ * (the push opened the hot window, but the pending timeout was scheduled
278
+ * for the slow one). */
279
+ function pokeStatusWatch() {
280
+ invalidateSnapshot();
281
+ watchTick().then(() => {
282
+ if (watching) scheduleTick(inHotWindow() ? WATCH_HOT_MS : WATCH_MS);
283
+ });
284
+ }
285
+ function startStatusWatch() {
286
+ if (watching) return;
287
+ watching = true;
288
+ scheduleTick(WATCH_MS);
289
+ }
290
+ /** Every active workspace's repos, paired with the branch that workspace
291
+ * works on — the batch refreshes both the recency window and the exact-head
292
+ * lookup for each, in one request. */
293
+ function activeRepoTargets() {
294
+ return listWorkspaces().filter((ws) => ws.status === "active").flatMap((ws) => ws.repos.map((r) => ({
295
+ fullName: r.full_name,
296
+ branch: ws.branch
297
+ })));
298
+ }
299
+ /** Status with PRs refetched first (throttled): a client returning to the
300
+ * foreground wants current PR state — merges/closes happen on GitHub and
301
+ * send no local signal, so without this they wait out the slow poll. */
302
+ async function statusForAllFresh() {
303
+ if (await refreshPrsBatch(activeRepoTargets(), true).catch(() => false)) invalidateSnapshot();
304
+ return statusForAll();
305
+ }
306
+ /** RSS here is the Node + Marko baseline (~80 MB empty, ~115 MB loaded),
307
+ * not a leak and not worth chasing — young-gen flags do not move it. */
308
+ async function computeStatus() {
309
+ const everyWorkspace = listWorkspaces();
310
+ const workspaces = everyWorkspace.filter((ws) => ws.status === "active");
311
+ const paths = new Map(workspaces.map((ws) => [ws.id, ws.path]));
312
+ const [ports, states] = await Promise.all([portsByWorkspace(paths), sessionStates()]);
313
+ const entries = await Promise.all(workspaces.map(async (ws) => {
314
+ const foreign = everyWorkspace.filter((w) => w.id !== ws.id).map((w) => w.branch);
315
+ const statuses = await Promise.all(ws.repos.map((repo) => repoStatus(ws, repo, foreign)));
316
+ return [ws.id, {
317
+ ports: ports.get(ws.id) || [],
318
+ repos: statuses,
319
+ ...sessionCounts(ws.id, states)
320
+ }];
321
+ }));
322
+ return Object.fromEntries(entries);
323
+ }
324
+ async function repoStatus(ws, repo, foreign) {
325
+ const dir = path.join(ws.path, repo.worktree);
326
+ const [repoPrs, localHead] = await Promise.all([workspacePrs(repo.full_name, ws.branch, foreign, false, ws.imported_at).then((list) => withoutDismissed(ws.id, repo.full_name, list)), headOid(dir).then((h) => h.oid)]);
327
+ const unpushed = pushFailureFor(repo.full_name, ws.branch, foreign);
328
+ return {
329
+ full_name: repo.full_name,
330
+ worktree: repo.worktree,
331
+ ...localHead && { localHead },
332
+ ...unpushed && { unpushed },
333
+ prs: repoPrs
334
+ };
335
+ }
336
+ function sessionCounts(workspaceId, states) {
337
+ const sessions = listSessions(workspaceId).map((s) => ({
338
+ id: s.id,
339
+ agent: s.agent,
340
+ title: s.title,
341
+ state: states[s.id] || { alive: false }
342
+ }));
343
+ const live = sessions.map((s) => s.state).filter((state) => !!state.alive);
344
+ return {
345
+ sessions,
346
+ agents: live.length,
347
+ working: live.filter((state) => state.phase === "working").length,
348
+ waiting: live.filter((state) => state.phase === "waiting").length
349
+ };
350
+ }
351
+ //#endregion
352
+ export { statusForAllFresh as a, refreshPrsBatch as c, statusForAll as i, workspacePrs as l, pokeStatusWatch as n, dismissPr as o, startStatusWatch as r, notePushActivity as s, activeRepoTargets as t };
@@ -1594,6 +1594,9 @@ function _attr_details_or_dialog_open(scopeId, nodeAccessor, open, openChange, s
1594
1594
  let normalizedOpen = isNotVoid(open);
1595
1595
  return openChange && writeControlledScope(4, scopeId, nodeAccessor, normalizedOpen || void 0, openChange, serializeType), normalizedOpen ? " open" : "";
1596
1596
  }
1597
+ function _attr_nonce() {
1598
+ return getChunk().boundary.state.nonceAttr;
1599
+ }
1597
1600
  function _attr(name, value) {
1598
1601
  return isVoid(value) ? "" : nonVoidAttr(name, value);
1599
1602
  }
@@ -1975,7 +1978,7 @@ var toastTitle = {
1975
1978
  };
1976
1979
  //#endregion
1977
1980
  //#region src/tags/icons/icon-base.marko
1978
- var icon_base_default = _template("M", (input) => {
1981
+ var icon_base_default = _template("N", (input) => {
1979
1982
  const $scope0_reason = _scope_reason();
1980
1983
  const $scope0_id = _scope_id();
1981
1984
  const size = input.size ?? 15;
@@ -1984,7 +1987,7 @@ var icon_base_default = _template("M", (input) => {
1984
1987
  });
1985
1988
  //#endregion
1986
1989
  //#region src/tags/icons/icon-xmark.marko
1987
- var icon_xmark_default = _template("lb", (input) => {
1990
+ var icon_xmark_default = _template("nb", (input) => {
1988
1991
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
1989
1992
  const $scope0_id = _scope_id();
1990
1993
  _set_serialize_reason({
@@ -2001,7 +2004,7 @@ var icon_xmark_default = _template("lb", (input) => {
2001
2004
  });
2002
2005
  //#endregion
2003
2006
  //#region src/tags/toast.marko
2004
- var toast_default = _template("C", (input) => {
2007
+ var toast_default = _template("D", (input) => {
2005
2008
  const $scope0_reason = _scope_reason(), $sg__input_title = _serialize_guard($scope0_reason, 7), $sg__input_title__OR__input_content__OR__input_action__OR__input_onDismiss = _serialize_guard($scope0_reason, 5), $sg__input_content = _serialize_guard($scope0_reason, 8), $sg__input_action = _serialize_guard($scope0_reason, 9), $sg__input_action__OR__input_onDismiss = _serialize_guard($scope0_reason, 3), $sg__input_onDismiss = _serialize_guard($scope0_reason, 11), $si__input_content = _serialize_if($scope0_reason, 8), $si__input_action__OR__input_onDismiss = _serialize_if($scope0_reason, 3), $si__input_action_content = _serialize_if($scope0_reason, 10);
2006
2009
  const $scope0_id = _scope_id();
2007
2010
  const $input_action_content__closures = /* @__PURE__ */ new Set();
@@ -2058,7 +2061,7 @@ var toast_default = _template("C", (input) => {
2058
2061
  _html(`<button${_attr_class([iconBtnRow, "_bm"])} type=button title=Dismiss aria-label=Dismiss>`);
2059
2062
  icon_xmark_default({ size: 14 });
2060
2063
  _html(`</button>${_el_resume($scope5_id, "a")}`);
2061
- _script($scope5_id, "C0");
2064
+ _script($scope5_id, "D0");
2062
2065
  writeScope($scope5_id, { _: _scope_with_id($scope3_id) });
2063
2066
  return 0;
2064
2067
  }
@@ -2069,8 +2072,8 @@ var toast_default = _template("C", (input) => {
2069
2072
  }
2070
2073
  }, $scope0_id, "e", $sg__input_action__OR__input_onDismiss, $sg__input_action__OR__input_onDismiss, $sg__input_title__OR__input_content__OR__input_action__OR__input_onDismiss, 0, 1);
2071
2074
  _html(`</div>${_el_resume($scope0_id, "a")}`);
2072
- _script($scope0_id, "C1");
2073
- _script($scope0_id, "C2");
2075
+ _script($scope0_id, "D1");
2076
+ _script($scope0_id, "D2");
2074
2077
  writeScope($scope0_id, {
2075
2078
  j: _serialize_if($scope0_reason, 7) && title,
2076
2079
  k: _serialize_if($scope0_reason, 0) && inputClass,
@@ -2128,8 +2131,7 @@ function diffTreeOpen() {
2128
2131
  }
2129
2132
  function diffScope() {
2130
2133
  const v = get("diff-scope");
2131
- if (v === "working" || v === "dirty") return v;
2132
- return get("diff-uncommitted") === "1" ? "working" : "committed";
2134
+ return v === "working" || v === "dirty" ? v : "committed";
2133
2135
  }
2134
2136
  function setDiffScope(scope) {
2135
2137
  set("diff-scope", scope);
@@ -2176,7 +2178,7 @@ var row = "_bB _bt";
2176
2178
  var iconBtn = "_Z _j";
2177
2179
  //#endregion
2178
2180
  //#region src/tags/icons/icon-refresh.marko
2179
- var icon_refresh_default = _template("db", (input) => {
2181
+ var icon_refresh_default = _template("fb", (input) => {
2180
2182
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2181
2183
  const $scope0_id = _scope_id();
2182
2184
  _set_serialize_reason({
@@ -2193,7 +2195,7 @@ var icon_refresh_default = _template("db", (input) => {
2193
2195
  });
2194
2196
  //#endregion
2195
2197
  //#region src/tags/icons/icon-back.marko
2196
- var icon_back_default = _template("L", (input) => {
2198
+ var icon_back_default = _template("M", (input) => {
2197
2199
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2198
2200
  const $scope0_id = _scope_id();
2199
2201
  _set_serialize_reason({
@@ -2210,7 +2212,7 @@ var icon_back_default = _template("L", (input) => {
2210
2212
  });
2211
2213
  //#endregion
2212
2214
  //#region src/tags/icons/icon-chevron-right.marko
2213
- var icon_chevron_right_default = _template("R", (input) => {
2215
+ var icon_chevron_right_default = _template("S", (input) => {
2214
2216
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2215
2217
  const $scope0_id = _scope_id();
2216
2218
  _set_serialize_reason({
@@ -2227,7 +2229,7 @@ var icon_chevron_right_default = _template("R", (input) => {
2227
2229
  });
2228
2230
  //#endregion
2229
2231
  //#region src/tags/icons/icon-arrow-up.marko
2230
- var icon_arrow_up_default = _template("K", (input) => {
2232
+ var icon_arrow_up_default = _template("L", (input) => {
2231
2233
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2232
2234
  const $scope0_id = _scope_id();
2233
2235
  _set_serialize_reason({
@@ -2244,7 +2246,7 @@ var icon_arrow_up_default = _template("K", (input) => {
2244
2246
  });
2245
2247
  //#endregion
2246
2248
  //#region src/tags/icons/icon-search.marko
2247
- var icon_search_default = _template("eb", (input) => {
2249
+ var icon_search_default = _template("gb", (input) => {
2248
2250
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2249
2251
  const $scope0_id = _scope_id();
2250
2252
  _set_serialize_reason({
@@ -2303,7 +2305,7 @@ function fuzzy(query, text) {
2303
2305
  }
2304
2306
  //#endregion
2305
2307
  //#region src/tags/icons/icon-sidebar.marko
2306
- var icon_sidebar_default = _template("hb", (input) => {
2308
+ var icon_sidebar_default = _template("jb", (input) => {
2307
2309
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2308
2310
  const $scope0_id = _scope_id();
2309
2311
  _set_serialize_reason({
@@ -2318,11 +2320,11 @@ var icon_sidebar_default = _template("hb", (input) => {
2318
2320
  });
2319
2321
  _serialize_if($scope0_reason, 0) && writeScope($scope0_id, { a: _existing_scope($childScope) });
2320
2322
  });
2321
- var findInput = "_gc _l";
2322
- var findIconBtn = "_gf _j";
2323
+ var findInput = "_f9 _l";
2324
+ var findIconBtn = "_gb _j";
2323
2325
  //#endregion
2324
2326
  //#region src/tags/icons/icon-arrow-down.marko
2325
- var icon_arrow_down_default = _template("H", (input) => {
2327
+ var icon_arrow_down_default = _template("I", (input) => {
2326
2328
  const $scope0_reason = _scope_reason(), $sg__input_size__OR__input_class = _serialize_guard($scope0_reason, 0);
2327
2329
  const $scope0_id = _scope_id();
2328
2330
  _set_serialize_reason({
@@ -2338,4 +2340,4 @@ var icon_arrow_down_default = _template("H", (input) => {
2338
2340
  _serialize_if($scope0_reason, 0) && writeScope($scope0_id, { a: _existing_scope($childScope) });
2339
2341
  });
2340
2342
  //#endregion
2341
- export { _attrs_partial as $, parseRepoUrl as A, _var as At, metaTrunc as B, setWorkspaceTab as C, _serialize_guard as Ct, toast_default as D, _template as Dt, workspaceTab as E, _subscribe as Et, btnSm as F, _attr_class as G, tone as H, iconBtnLg as I, _attr_option_value as J, _attr_details_or_dialog_open as K, iconBtnRow as L, repoWebUrl as M, forOf as Mt, setPrimaryHost as N, withPageAssets as Nt, icon_xmark_default as O, _to_text as Ot, btnLg as P, writeScope as Pt, _attrs as Q, iconBtnXs as R, setTheme as S, _sep as St, theme as T, _set_serialize_reason as Tt, $global as U, phase as V, _attr as W, _attr_select_value as X, _attr_or as Y, _attr_style as Z, diffScope as _, _resume_branch as _t, fuzzy as a, _el_resume as at, setDiffPos as b, _scope_with_id as bt, icon_arrow_up_default as c, _flush_head as ct, icon_refresh_default as d, _hoist_read_error as dt, _content as et, iconBtn as f, _html as ft, diffPos as g, _resume as gt, diffFilter as h, _peek_scope_id as ht, icon_sidebar_default as i, _el_read_error as it, repoLabel as j, attrTag as jt, icon_base_default as k, _trailers as kt, icon_chevron_right_default as l, _for_of as lt, sectionCount as m, _if as mt, findIconBtn as n, _dynamic_tag as nt, rankRepos as o, _escape as ot, row as p, _id as pt, _attr_input_value as q, findInput as r, _el as rt, icon_search_default as s, _existing_scope as st, icon_arrow_down_default as t, _content_resume as tt, icon_back_default as u, _hoist as ut, diffTreeOpen as v, _scope_id as vt, sideNavOpen as w, _serialize_if as wt, setDiffScope as x, _script as xt, ignoredSsoOrgs as y, _scope_reason as yt, inputLg as z };
2343
+ export { _attr_style as $, parseRepoUrl as A, _subscribe as At, metaTrunc as B, setWorkspaceTab as C, _scope_reason as Ct, toast_default as D, _serialize_guard as Dt, workspaceTab as E, _sep as Et, btnSm as F, attrTag as Ft, _attr_class as G, tone as H, iconBtnLg as I, forOf as It, _attr_input_value as J, _attr_details_or_dialog_open as K, iconBtnRow as L, withPageAssets as Lt, repoWebUrl as M, _to_text as Mt, setPrimaryHost as N, _trailers as Nt, icon_xmark_default as O, _serialize_if as Ot, btnLg as P, _var as Pt, _attr_select_value as Q, iconBtnXs as R, writeScope as Rt, setTheme as S, _scope_id as St, theme as T, _script as Tt, $global as U, phase as V, _attr as W, _attr_option_value as X, _attr_nonce as Y, _attr_or as Z, diffScope as _, _id as _t, fuzzy as a, _dynamic_tag as at, setDiffPos as b, _resume as bt, icon_arrow_up_default as c, _el_resume as ct, icon_refresh_default as d, _existing_scope as dt, _attr_textarea_value as et, iconBtn as f, _flush_head as ft, diffPos as g, _html as gt, diffFilter as h, _hoist_read_error as ht, icon_sidebar_default as i, _content_resume as it, repoLabel as j, _template as jt, icon_base_default as k, _set_serialize_reason as kt, icon_chevron_right_default as l, _escape as lt, sectionCount as m, _hoist as mt, findIconBtn as n, _attrs_partial as nt, rankRepos as o, _el as ot, row as p, _for_of as pt, _attr_input_checked as q, findInput as r, _content as rt, icon_search_default as s, _el_read_error as st, icon_arrow_down_default as t, _attrs as tt, icon_back_default as u, _escape_script as ut, diffTreeOpen as v, _if as vt, sideNavOpen as w, _scope_with_id as wt, setDiffScope as x, _resume_branch as xt, ignoredSsoOrgs as y, _peek_scope_id as yt, inputLg as z };
@@ -9,7 +9,8 @@ import { promisify } from "node:util";
9
9
  import { DatabaseSync } from "node:sqlite";
10
10
  import { createHash } from "node:crypto";
11
11
  //#region src/server/config.ts
12
- var home$1 = os.homedir();
12
+ var rawHome = os.homedir();
13
+ var home$1 = rawHome && rawHome !== "undefined" && rawHome !== "null" ? rawHome : "";
13
14
  var config = {
14
15
  /** Root directory containing all workspaces. */
15
16
  workRoot: process.env.WORK_ROOT || path.join(home$1, "work"),
@@ -81,6 +82,7 @@ function utf8Locale(env) {
81
82
  }
82
83
  //#endregion
83
84
  //#region src/server/db.ts
85
+ if (!path.isAbsolute(config.dataDir)) throw new Error(`data dir must be absolute, got "${config.dataDir}" — check WORK_DATA_DIR and $HOME`);
84
86
  var db = new DatabaseSync(path.join(config.dataDir, "work.db"));
85
87
  db.exec("PRAGMA busy_timeout = 5000");
86
88
  function all(sql, ...params) {
@@ -165,6 +167,9 @@ function ensureColumn(table, column, decl) {
165
167
  }
166
168
  ensureColumn("review_comments", "head", "TEXT");
167
169
  ensureColumn("review_comments", "snippet", "TEXT");
170
+ ensureColumn("workspaces", "materialized", "INTEGER NOT NULL DEFAULT 1");
171
+ ensureColumn("workspaces", "origin_branch", "TEXT");
172
+ ensureColumn("workspaces", "imported_at", "TEXT");
168
173
  //#endregion
169
174
  //#region lib/setup/agents.json
170
175
  var agents_default = [
@@ -6551,7 +6556,7 @@ function publishChange(sessionId) {
6551
6556
  const row = get("SELECT workspace_id FROM sessions WHERE id = ?", sessionId);
6552
6557
  if (row?.workspace_id) {
6553
6558
  const workspaceId = row.workspace_id;
6554
- import("./_BIDn_RW1.js").then((m) => m.notifySessionsChanged(workspaceId));
6559
+ import("./_B1lVb01g.js").then((m) => m.notifySessionsChanged(workspaceId));
6555
6560
  return;
6556
6561
  }
6557
6562
  publish("sessions.changed");
@@ -8213,4 +8218,4 @@ function sleep(ms) {
8213
8218
  return new Promise((resolve) => setTimeout(resolve, ms));
8214
8219
  }
8215
8220
  //#endregion
8216
- export { setSetting as $, setScrapedPhase as A, saveSubscription as B, agentBadge as C, agentMeta as D, agentLabel as E, manifest as F, agentChoices as G, subscribe as H, normalizeAccent as I, enabledAgents as J, allPrefs as K, setInstance as L, ACCENT_RE as M, getInstance as N, clearAgentPhase as O, iconSvg as P, setPref as Q, deleteSubscription as R, blankRow as S, agentFromCommand as T, subscriberCount as U, publish as V, subscriberJoins as W, preferredAgent as X, getSetting as Y, setDisabledAgents as Z, tmuxName as _, run$1 as _t, ensureTmuxServer as a, which as at, readHookPayload as b, killWorkspaceSessions as c, agentById as ct, newSessionArgs as d, db as dt, discoverAgents as et, notifySessionsChanged as f, get as ft, sessionStates as g, git as gt, sendToSession as h, gh as ht, disposeSessions as i, setupPath as it, setSessionViewers as j, setAgentPhase as k, listSessions as l, toolById as lt, renameSession as m, TMUX_SOCKET as mt, clearTmuxStarted as n, ghKnownHosts as nt, getSession as o, DEFAULT_GITHUB_HOST as ot, panePids as p, workspacePath as pt, disabledAgents as q, createSession as r, miseVersions as rt, killSession as s, TOOLS as st, cleanEnv as t, doctor as tt, markTmuxStarted as u, all as ut, tmuxWasStarted as v, tmux as vt, agentFamily as w, reportsWorking as x, AGENTS as y, config as yt, pushPublicKey as z };
8221
+ export { setSetting as $, setScrapedPhase as A, saveSubscription as B, agentBadge as C, agentMeta as D, agentLabel as E, manifest as F, agentChoices as G, subscribe as H, normalizeAccent as I, enabledAgents as J, allPrefs as K, setInstance as L, ACCENT_RE as M, getInstance as N, clearAgentPhase as O, iconSvg as P, setPref as Q, deleteSubscription as R, blankRow as S, agentFromCommand as T, subscriberCount as U, publish as V, subscriberJoins as W, preferredAgent as X, getSetting as Y, setDisabledAgents as Z, tmuxName as _, git as _t, ensureTmuxServer as a, which as at, readHookPayload as b, config as bt, killWorkspaceSessions as c, agentById as ct, newSessionArgs as d, all as dt, discoverAgents as et, notifySessionsChanged as f, db as ft, sessionStates as g, gh as gt, sendToSession as h, TMUX_SOCKET as ht, disposeSessions as i, setupPath as it, setSessionViewers as j, setAgentPhase as k, listSessions as l, agentLabel$1 as lt, renameSession as m, workspacePath as mt, clearTmuxStarted as n, ghKnownHosts as nt, getSession as o, DEFAULT_GITHUB_HOST as ot, panePids as p, get as pt, disabledAgents as q, createSession as r, miseVersions as rt, killSession as s, TOOLS as st, cleanEnv as t, doctor as tt, markTmuxStarted as u, toolById as ut, tmuxWasStarted as v, run$1 as vt, agentFamily as w, reportsWorking as x, AGENTS as y, tmux as yt, pushPublicKey as z };