@dylanpiercey/work 0.0.120 → 0.0.122

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/{_DEBFGwto.js → _BPX2_wNT.js} +20 -18
  5. package/dist/{_BZhGz-eY.js → _BdBrOaZF.js} +95 -94
  6. package/dist/_Bjh5JEm7.js +2 -0
  7. package/dist/_CfwTkTbL.js +352 -0
  8. package/dist/{_DE2RUdbF.js → _Cr71Xd0d.js} +8 -3
  9. package/dist/_DJyHc1w-.js +1739 -0
  10. package/dist/_Dpa8WFuv.js +2 -0
  11. package/dist/index.mjs +4136 -4140
  12. package/dist/public/assets/{_z3QzE4ht2.js → _22Wep3Qj2.js} +1 -1
  13. package/dist/public/assets/{_DdYfAoJz.js → _4C4ZKftZ.js} +1 -1
  14. package/dist/public/assets/_4C4ZKftZ.js.br +0 -0
  15. package/dist/public/assets/{_h6HS-Zb_2.js → _5waGGcsH2.js} +1 -1
  16. package/dist/public/assets/_5waGGcsH2.js.br +0 -0
  17. package/dist/public/assets/{_BqNoqWIY.js → _6yLwnUth.js} +1 -1
  18. package/dist/public/assets/_6yLwnUth.js.br +0 -0
  19. package/dist/public/assets/{_BuF6uiIm.js → _8ixfpSLV.js} +1 -1
  20. package/dist/public/assets/_8ixfpSLV.js.br +0 -0
  21. package/dist/public/assets/{_BEPt7EWk.js → _9NNG2Bg-.js} +1 -1
  22. package/dist/public/assets/_9NNG2Bg-.js.br +0 -0
  23. package/dist/public/assets/{_D8lg2J6H.js → _9wp1Weya.js} +1 -1
  24. package/dist/public/assets/_9wp1Weya.js.br +0 -0
  25. package/dist/public/assets/{_BoXfCUcF.js → _B80Me7dX.js} +1 -1
  26. package/dist/public/assets/{_BbE6iGBd.js → _BJVUTWAN.js} +1 -1
  27. package/dist/public/assets/_BJVUTWAN.js.br +0 -0
  28. package/dist/public/assets/{_CihMY3_B2.js → _BO5eXuo12.js} +1 -1
  29. package/dist/public/assets/_BO5eXuo12.js.br +0 -0
  30. package/dist/public/assets/{_CwZCc-fE2.js → _BO6TG3u42.js} +1 -1
  31. package/dist/public/assets/_BO6TG3u42.js.br +0 -0
  32. package/dist/public/assets/{_D4V7vTBe.js → _BO_carFG.js} +1 -1
  33. package/dist/public/assets/_BO_carFG.js.br +0 -0
  34. package/dist/public/assets/{_D11ZFURg.js → _BPjnrd1A.js} +1 -1
  35. package/dist/public/assets/_BPjnrd1A.js.br +0 -0
  36. package/dist/public/assets/{_DRKLaQtq.js → _BQfrm6Hv.js} +1 -1
  37. package/dist/public/assets/_BQfrm6Hv.js.br +0 -0
  38. package/dist/public/assets/{_CwtAB8Ju2.js → _BYOrCsb12.js} +1 -1
  39. package/dist/public/assets/_BYOrCsb12.js.br +0 -0
  40. package/dist/public/assets/{_CIio55Q2.js → _BcA9iBrt.js} +1 -1
  41. package/dist/public/assets/_BcA9iBrt.js.br +0 -0
  42. package/dist/public/assets/{_Du-i6H8r.js → _BcnKn75V.js} +24 -21
  43. package/dist/public/assets/_BcnKn75V.js.br +0 -0
  44. package/dist/public/assets/{_CJ_9Hl5d.js → _BeYy7LJv.js} +1 -1
  45. package/dist/public/assets/_BeYy7LJv.js.br +0 -0
  46. package/dist/public/assets/{_DBXfsrRd.js → _BfzwZXGo.js} +1 -1
  47. package/dist/public/assets/_BfzwZXGo.js.br +0 -0
  48. package/dist/public/assets/{_BfZMmKaW.js → _Bgq0Djxf.js} +1 -1
  49. package/dist/public/assets/_Bgq0Djxf.js.br +0 -0
  50. package/dist/public/assets/{_zJkCtRhb.js → _BgxHsV2q.js} +1 -1
  51. package/dist/public/assets/_BgxHsV2q.js.br +0 -0
  52. package/dist/public/assets/{_p1VLQxPw.js → _Bhf_wksG.js} +1 -1
  53. package/dist/public/assets/_Bhf_wksG.js.br +0 -0
  54. package/dist/public/assets/{_CrPK-ZOH2.js → _Bkd9T8qO2.js} +1 -1
  55. package/dist/public/assets/_Bkd9T8qO2.js.br +0 -0
  56. package/dist/public/assets/{_CYLlpjQc.js → _BllRu2dt.js} +1 -1
  57. package/dist/public/assets/_BllRu2dt.js.br +0 -0
  58. package/dist/public/assets/{_DWctbEWg.js → _Buu8_cyc.js} +1 -1
  59. package/dist/public/assets/_Buu8_cyc.js.br +0 -0
  60. package/dist/public/assets/{_F8phZWNM2.js → _BvJHLrwP2.js} +1 -1
  61. package/dist/public/assets/_BvJHLrwP2.js.br +0 -0
  62. package/dist/public/assets/{_wc-pkVQS.js → _BvTWp2QQ.js} +1 -1
  63. package/dist/public/assets/_BvTWp2QQ.js.br +0 -0
  64. package/dist/public/assets/{_CkBv_F0f.js → _BxfzYLyp.js} +1 -1
  65. package/dist/public/assets/_BxfzYLyp.js.br +0 -0
  66. package/dist/public/assets/{_BzULaZGs.js → _Bz61WBUI.js} +1 -1
  67. package/dist/public/assets/_Bz61WBUI.js.br +0 -0
  68. package/dist/public/assets/{_Cv-4N4xQ.js → _C0Wcm1DA.js} +1 -1
  69. package/dist/public/assets/_C0Wcm1DA.js.br +0 -0
  70. package/dist/public/assets/{_BUmLRB7c2.js → _C5fkGs032.js} +1 -1
  71. package/dist/public/assets/_C5fkGs032.js.br +0 -0
  72. package/dist/public/assets/{_BEOZghNJ2.js → _CKAWtCKg2.js} +1 -1
  73. package/dist/public/assets/_CKAWtCKg2.js.br +0 -0
  74. package/dist/public/assets/{_BFgJoTOo.js → _CLM87vGj.js} +1 -1
  75. package/dist/public/assets/_CLM87vGj.js.br +0 -0
  76. package/dist/public/assets/{_B6bSumz1.js → _CM8zmLXB.js} +1 -1
  77. package/dist/public/assets/_CM8zmLXB.js.br +0 -0
  78. package/dist/public/assets/{_8hF31A2G.js → _CNBki2Aq.js} +1 -1
  79. package/dist/public/assets/_CNBki2Aq.js.br +0 -0
  80. package/dist/public/assets/{_CQ-tqbUq.js → _CNVk64eD.js} +1 -1
  81. package/dist/public/assets/_CNVk64eD.js.br +0 -0
  82. package/dist/public/assets/{_BHgE7f-_2.js → _CPCFHsPT2.js} +1 -1
  83. package/dist/public/assets/_CPCFHsPT2.js.br +0 -0
  84. package/dist/public/assets/{_BLgtdDQm.js → _CW70CETb.js} +1 -1
  85. package/dist/public/assets/_CW70CETb.js.br +0 -0
  86. package/dist/public/assets/{_BCKhXQct2.js → _CWU_IJjz2.js} +1 -1
  87. package/dist/public/assets/_CWU_IJjz2.js.br +0 -0
  88. package/dist/public/assets/{_BaiP7ogH.js → _ClA-9QLW.js} +1 -1
  89. package/dist/public/assets/_ClA-9QLW.js.br +0 -0
  90. package/dist/public/assets/{_dBVoJwVC.js → _CorgOnjQ.js} +1 -1
  91. package/dist/public/assets/_CorgOnjQ.js.br +0 -0
  92. package/dist/public/assets/{_DdnCyOPs.js → _CpByFq0N.js} +1 -1
  93. package/dist/public/assets/_CpByFq0N.js.br +0 -0
  94. package/dist/public/assets/{_fR2ilomn.js → _CqEc7KwE.js} +1 -1
  95. package/dist/public/assets/_CqEc7KwE.js.br +0 -0
  96. package/dist/public/assets/{_BtmFYBHk.js → _CqgcFjOQ.js} +1 -1
  97. package/dist/public/assets/_CqgcFjOQ.js.br +0 -0
  98. package/dist/public/assets/{_CeECfdVo2.js → _CrlXwiFN2.js} +1 -1
  99. package/dist/public/assets/_CrlXwiFN2.js.br +0 -0
  100. package/dist/public/assets/{_FEbRnVo7.js → _CtuCeJaB.js} +1 -1
  101. package/dist/public/assets/_CtuCeJaB.js.br +0 -0
  102. package/dist/public/assets/{_DfpMH3So2.js → _D1zxC16I2.js} +1 -1
  103. package/dist/public/assets/_D1zxC16I2.js.br +0 -0
  104. package/dist/public/assets/{_Cvly93xo2.js → _D2SIC9vH2.js} +1 -1
  105. package/dist/public/assets/_D2SIC9vH2.js.br +0 -0
  106. package/dist/public/assets/{_-3OmHyNU.js → _D9QYQO8L.js} +1 -1
  107. package/dist/public/assets/_D9QYQO8L.js.br +0 -0
  108. package/dist/public/assets/{_CzMjLSTK.js → _DDNq8klN.js} +1 -1
  109. package/dist/public/assets/_DDNq8klN.js.br +0 -0
  110. package/dist/public/assets/{_BIsQjQ872.js → _DL2HExUi2.js} +1 -1
  111. package/dist/public/assets/_DL2HExUi2.js.br +0 -0
  112. package/dist/public/assets/{_QuFTKxMO2.js → _DLbcflUX2.js} +1 -1
  113. package/dist/public/assets/_DLbcflUX2.js.br +0 -0
  114. package/dist/public/assets/{_N0Ea8Q_R.js → _DMzElmqz.js} +1 -1
  115. package/dist/public/assets/_DMzElmqz.js.br +0 -0
  116. package/dist/public/assets/{_cS3NYZ_K2.js → _DOa36sDf2.js} +1 -1
  117. package/dist/public/assets/_DOa36sDf2.js.br +0 -0
  118. package/dist/public/assets/{_DDag_nXJ.js → _DWZLw9u5.js} +1 -1
  119. package/dist/public/assets/_DWZLw9u5.js.br +0 -0
  120. package/dist/public/assets/{_DiaYv9e1.js → _DY5MuJqh.js} +1 -1
  121. package/dist/public/assets/_DY5MuJqh.js.br +0 -0
  122. package/dist/public/assets/{_D2xWjoK-2.js → _DZXcx3AV2.js} +1 -1
  123. package/dist/public/assets/_DZXcx3AV2.js.br +0 -0
  124. package/dist/public/assets/{_BPcKgx9L.js → _D_5rjG0o.js} +1 -1
  125. package/dist/public/assets/_D_5rjG0o.js.br +0 -0
  126. package/dist/public/assets/{_Ca811EeR.js → _DbEs3COY.js} +1 -1
  127. package/dist/public/assets/_DbEs3COY.js.br +0 -0
  128. package/dist/public/assets/{_BrTLGsp82.js → _DegNdi0y2.js} +1 -1
  129. package/dist/public/assets/_DegNdi0y2.js.br +0 -0
  130. package/dist/public/assets/{_B_MP9UES.js → _DfLxwQXd.js} +1 -1
  131. package/dist/public/assets/_DfLxwQXd.js.br +0 -0
  132. package/dist/public/assets/{_BilqQTfw.js → _DwTIenq9.js} +1 -1
  133. package/dist/public/assets/_DwTIenq9.js.br +0 -0
  134. package/dist/public/assets/{_0_ngOEbL.js → _Dwsfrcqr.js} +1 -1
  135. package/dist/public/assets/_Dwsfrcqr.js.br +0 -0
  136. package/dist/public/assets/{_uJTU0UZd2.js → _DyZPE8LD2.js} +1 -1
  137. package/dist/public/assets/_DyZPE8LD2.js.br +0 -0
  138. package/dist/public/assets/{_D2be5yUI.js → _MEclBKIH.js} +1 -1
  139. package/dist/public/assets/_MEclBKIH.js.br +0 -0
  140. package/dist/public/assets/{_DhLWIgCs.js → _Tpid4lIw.js} +1 -1
  141. package/dist/public/assets/_Tpid4lIw.js.br +0 -0
  142. package/dist/public/assets/{_cdbANa0O.js → _W9aOkr2v.js} +1 -1
  143. package/dist/public/assets/_W9aOkr2v.js.br +0 -0
  144. package/dist/public/assets/{_BDRc66NB2.js → _Y8lHXkX62.js} +1 -1
  145. package/dist/public/assets/_Y8lHXkX62.js.br +0 -0
  146. package/dist/public/assets/{_GdcmEEuJ.js → _Z4spekbX.js} +1 -1
  147. package/dist/public/assets/_Z4spekbX.js.br +0 -0
  148. package/dist/public/assets/{_CeVHIZWy2.js → _a18bdHwu2.js} +1 -1
  149. package/dist/public/assets/_a18bdHwu2.js.br +0 -0
  150. package/dist/public/assets/{_DkYHMy0q2.js → _hO1aV02w2.js} +1 -1
  151. package/dist/public/assets/{_DZgx_rAt.js → _ih_22Lwc.js} +1 -1
  152. package/dist/public/assets/_ih_22Lwc.js.br +0 -0
  153. package/dist/public/assets/{_CG2uZvAG.js → _kQ849tt6.js} +1 -1
  154. package/dist/public/assets/_kQ849tt6.js.br +0 -0
  155. package/dist/public/assets/{_Ces5jihM.js → _lo4uGYhv.js} +1 -1
  156. package/dist/public/assets/_lo4uGYhv.js.br +0 -0
  157. package/dist/public/assets/{_Brws7C1N.js → _sWtKlZZ5.js} +6 -6
  158. package/dist/public/assets/_sWtKlZZ5.js.br +0 -0
  159. package/dist/public/assets/{_BGHYY00K2.js → _xvmiPi6J2.js} +1 -1
  160. package/dist/public/assets/_xvmiPi6J2.js.br +0 -0
  161. package/dist/public/assets/app-page-9ukuDxSW.css +1 -0
  162. package/dist/public/assets/app-page-9ukuDxSW.css.br +0 -0
  163. package/dist/public/assets/diff-view-BmyKvz_f.css +1 -0
  164. package/dist/public/assets/diff-view-BmyKvz_f.css.br +0 -0
  165. package/dist/public/assets/new-PmY_tc2O.js +1 -0
  166. package/dist/public/assets/settings-PmY_tc2O.js +1 -0
  167. package/dist/public/assets/w-PmY_tc2O.js +1 -0
  168. package/dist/public/assets/w._-DL1tdfaH.js +1 -0
  169. package/dist/public/assets/w._-PmY_tc2O.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/_-3OmHyNU.js.br +0 -0
  182. package/dist/public/assets/_0_ngOEbL.js.br +0 -0
  183. package/dist/public/assets/_8hF31A2G.js.br +0 -0
  184. package/dist/public/assets/_B6bSumz1.js.br +0 -0
  185. package/dist/public/assets/_BCKhXQct2.js.br +0 -0
  186. package/dist/public/assets/_BDRc66NB2.js.br +0 -0
  187. package/dist/public/assets/_BEOZghNJ2.js.br +0 -0
  188. package/dist/public/assets/_BEPt7EWk.js.br +0 -0
  189. package/dist/public/assets/_BFgJoTOo.js.br +0 -0
  190. package/dist/public/assets/_BGHYY00K2.js.br +0 -0
  191. package/dist/public/assets/_BHgE7f-_2.js.br +0 -0
  192. package/dist/public/assets/_BIsQjQ872.js.br +0 -0
  193. package/dist/public/assets/_BLgtdDQm.js.br +0 -0
  194. package/dist/public/assets/_BPcKgx9L.js.br +0 -0
  195. package/dist/public/assets/_BUmLRB7c2.js.br +0 -0
  196. package/dist/public/assets/_B_MP9UES.js.br +0 -0
  197. package/dist/public/assets/_BaiP7ogH.js.br +0 -0
  198. package/dist/public/assets/_BbE6iGBd.js.br +0 -0
  199. package/dist/public/assets/_BfZMmKaW.js.br +0 -0
  200. package/dist/public/assets/_BilqQTfw.js.br +0 -0
  201. package/dist/public/assets/_BqNoqWIY.js.br +0 -0
  202. package/dist/public/assets/_BrTLGsp82.js.br +0 -0
  203. package/dist/public/assets/_Brws7C1N.js.br +0 -0
  204. package/dist/public/assets/_BtmFYBHk.js.br +0 -0
  205. package/dist/public/assets/_BuF6uiIm.js.br +0 -0
  206. package/dist/public/assets/_BzULaZGs.js.br +0 -0
  207. package/dist/public/assets/_CG2uZvAG.js.br +0 -0
  208. package/dist/public/assets/_CIio55Q2.js.br +0 -0
  209. package/dist/public/assets/_CJ_9Hl5d.js.br +0 -0
  210. package/dist/public/assets/_CQ-tqbUq.js.br +0 -0
  211. package/dist/public/assets/_CYLlpjQc.js.br +0 -0
  212. package/dist/public/assets/_Ca811EeR.js.br +0 -0
  213. package/dist/public/assets/_CeECfdVo2.js.br +0 -0
  214. package/dist/public/assets/_CeVHIZWy2.js.br +0 -0
  215. package/dist/public/assets/_Ces5jihM.js.br +0 -7
  216. package/dist/public/assets/_CihMY3_B2.js.br +0 -0
  217. package/dist/public/assets/_CkBv_F0f.js.br +0 -0
  218. package/dist/public/assets/_CrPK-ZOH2.js.br +0 -0
  219. package/dist/public/assets/_Cv-4N4xQ.js.br +0 -0
  220. package/dist/public/assets/_Cvly93xo2.js.br +0 -0
  221. package/dist/public/assets/_CwZCc-fE2.js.br +0 -0
  222. package/dist/public/assets/_CwtAB8Ju2.js.br +0 -0
  223. package/dist/public/assets/_CzMjLSTK.js.br +0 -0
  224. package/dist/public/assets/_D11ZFURg.js.br +0 -0
  225. package/dist/public/assets/_D2be5yUI.js.br +0 -0
  226. package/dist/public/assets/_D2xWjoK-2.js.br +0 -0
  227. package/dist/public/assets/_D4V7vTBe.js.br +0 -0
  228. package/dist/public/assets/_D8lg2J6H.js.br +0 -0
  229. package/dist/public/assets/_DBXfsrRd.js.br +0 -0
  230. package/dist/public/assets/_DDag_nXJ.js.br +0 -0
  231. package/dist/public/assets/_DRKLaQtq.js.br +0 -0
  232. package/dist/public/assets/_DWctbEWg.js.br +0 -3
  233. package/dist/public/assets/_DZgx_rAt.js.br +0 -0
  234. package/dist/public/assets/_DdYfAoJz.js.br +0 -0
  235. package/dist/public/assets/_DdnCyOPs.js.br +0 -0
  236. package/dist/public/assets/_DfpMH3So2.js.br +0 -0
  237. package/dist/public/assets/_DhLWIgCs.js.br +0 -0
  238. package/dist/public/assets/_DiaYv9e1.js.br +0 -0
  239. package/dist/public/assets/_Du-i6H8r.js.br +0 -0
  240. package/dist/public/assets/_F8phZWNM2.js.br +0 -0
  241. package/dist/public/assets/_FEbRnVo7.js.br +0 -0
  242. package/dist/public/assets/_GdcmEEuJ.js.br +0 -0
  243. package/dist/public/assets/_N0Ea8Q_R.js.br +0 -0
  244. package/dist/public/assets/_QuFTKxMO2.js.br +0 -0
  245. package/dist/public/assets/_cS3NYZ_K2.js.br +0 -0
  246. package/dist/public/assets/_cdbANa0O.js.br +0 -0
  247. package/dist/public/assets/_dBVoJwVC.js.br +0 -0
  248. package/dist/public/assets/_fR2ilomn.js.br +0 -0
  249. package/dist/public/assets/_h6HS-Zb_2.js.br +0 -0
  250. package/dist/public/assets/_p1VLQxPw.js.br +0 -0
  251. package/dist/public/assets/_uJTU0UZd2.js.br +0 -0
  252. package/dist/public/assets/_wc-pkVQS.js.br +0 -0
  253. package/dist/public/assets/_zJkCtRhb.js.br +0 -0
  254. package/dist/public/assets/app-page-DgsAA8Om.css +0 -1
  255. package/dist/public/assets/app-page-DgsAA8Om.css.br +0 -0
  256. package/dist/public/assets/diff-view-BqwTpf5k.css +0 -1
  257. package/dist/public/assets/diff-view-BqwTpf5k.css.br +0 -0
  258. package/dist/public/assets/new-cBa61wMi.js +0 -1
  259. package/dist/public/assets/settings-cBa61wMi.js +0 -1
  260. package/dist/public/assets/w-cBa61wMi.js +0 -1
  261. package/dist/public/assets/w._-B9llVTHr.js +0 -1
  262. package/dist/public/assets/w._-cBa61wMi.js +0 -1
@@ -0,0 +1,2 @@
1
+ import { n as pokeStatusWatch } from "./_CfwTkTbL.js";
2
+ export { pokeStatusWatch };
@@ -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 };
@@ -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 };