@dylanpiercey/work 0.0.201 → 0.0.203
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.
- package/README.md +30 -6
- package/bin/work.mjs +33 -1
- package/dist/_7W8iLTql.js +2 -0
- package/dist/{_BntJJ2eD.js → _B9FeLAO5.js} +863 -218
- package/dist/index.mjs +1210 -1030
- package/dist/public/assets/{_D9nJ4nG1.js → _-K2pkpBq.js} +1 -1
- package/dist/public/assets/_-K2pkpBq.js.br +0 -0
- package/dist/public/assets/_-O6dm4G6.js +1 -0
- package/dist/public/assets/_-tNsWCRW.js +1 -0
- package/dist/public/assets/{_BfP8hsVl.js → _3aic_W0y.js} +1 -1
- package/dist/public/assets/{_DFMu7qvY.js → _3ewKhZLc.js} +1 -1
- package/dist/public/assets/_3ewKhZLc.js.br +0 -0
- package/dist/public/assets/{_Dj8W5hAy.js → _3xHfzmVK.js} +1 -1
- package/dist/public/assets/_3xHfzmVK.js.br +0 -0
- package/dist/public/assets/{_B7pXsDiF.js → _8Q9P2uHp.js} +1 -1
- package/dist/public/assets/_8Q9P2uHp.js.br +0 -0
- package/dist/public/assets/{_DAVXBkYN.js → _AoYqPW6v.js} +1 -1
- package/dist/public/assets/_AoYqPW6v.js.br +0 -0
- package/dist/public/assets/{_C8hQ66UC.js → _B1q5pQla.js} +1 -1
- package/dist/public/assets/_B1q5pQla.js.br +0 -0
- package/dist/public/assets/{_CgUj7qP_.js → _B66w2fqU.js} +1 -1
- package/dist/public/assets/_B66w2fqU.js.br +0 -0
- package/dist/public/assets/{_DVy22yDU.js → _B80CP8fn.js} +1 -1
- package/dist/public/assets/_B80CP8fn.js.br +0 -0
- package/dist/public/assets/_B9FnTf4h.js +1 -0
- package/dist/public/assets/{_tPOATR_S.js → _B9kU6sMh.js} +1 -1
- package/dist/public/assets/_B9kU6sMh.js.br +0 -0
- package/dist/public/assets/{_CD9reWft.js → _BADK8qnN.js} +1 -1
- package/dist/public/assets/_BADK8qnN.js.br +0 -0
- package/dist/public/assets/_BD2qsrlz.js +1 -0
- package/dist/public/assets/{_4w6Dwjlh.js → _BDdtj9Cq.js} +1 -1
- package/dist/public/assets/_BDdtj9Cq.js.br +0 -0
- package/dist/public/assets/{_DPtSJ35G.js → _BERyw-_I.js} +1 -1
- package/dist/public/assets/_BERyw-_I.js.br +0 -0
- package/dist/public/assets/_BH5mQvfn.js +1 -0
- package/dist/public/assets/{_BubcBfAr.js → _BK0E9Er2.js} +1 -1
- package/dist/public/assets/_BK0E9Er2.js.br +0 -0
- package/dist/public/assets/{_oA-Vr8Cf.js → _BKkFTJtI.js} +1 -1
- package/dist/public/assets/_BKkFTJtI.js.br +0 -0
- package/dist/public/assets/_BOBHk1-a.js +1 -0
- package/dist/public/assets/_BOk1AEgN.js +1 -0
- package/dist/public/assets/{_Ba1mrOxI.js → _BZ3C3l3R.js} +1 -1
- package/dist/public/assets/_BZ3C3l3R.js.br +0 -0
- package/dist/public/assets/{_Bc_Ezk5L.js → _BaiQtGR9.js} +1 -1
- package/dist/public/assets/_BaiQtGR9.js.br +0 -0
- package/dist/public/assets/_BdUIic1U.js +1 -0
- package/dist/public/assets/_BdUIic1U.js.br +0 -0
- package/dist/public/assets/{_DPT6ZO90.js → _BeVk3dbG.js} +1 -1
- package/dist/public/assets/_BeVk3dbG.js.br +0 -0
- package/dist/public/assets/_Bedi1JZC.js +1 -0
- package/dist/public/assets/_BgCpq5St.js +1 -0
- package/dist/public/assets/{_CwXTg1fU.js → _BjXiH2oW.js} +1 -1
- package/dist/public/assets/_BjXiH2oW.js.br +0 -0
- package/dist/public/assets/{_Ckc7dIlo.js → _Bk31AYSk.js} +1 -1
- package/dist/public/assets/_Bk31AYSk.js.br +0 -0
- package/dist/public/assets/{_P1g2Hmou.js → _BmZVM8Ww.js} +1 -1
- package/dist/public/assets/_BmZVM8Ww.js.br +0 -0
- package/dist/public/assets/_BqgMkZ3f.js +1 -0
- package/dist/public/assets/{_oBcpaEKd.js → _Brbaz_V-.js} +1 -1
- package/dist/public/assets/_Brbaz_V-.js.br +0 -0
- package/dist/public/assets/_Bs9HVA9T.js +1 -0
- package/dist/public/assets/{_B4t3OJDi.js → _Bwo88m9W.js} +1 -1
- package/dist/public/assets/_Bwo88m9W.js.br +0 -0
- package/dist/public/assets/{_Bg1dyEQN.js → _Bx5kmADX.js} +1 -1
- package/dist/public/assets/_Bx5kmADX.js.br +0 -0
- package/dist/public/assets/{_hI9Uq04u.js → _BzU-RPev.js} +1 -1
- package/dist/public/assets/_BzVRtEsz.js +1 -0
- package/dist/public/assets/{_BACT_fWM.js → _C0K-WRJD.js} +1 -1
- package/dist/public/assets/_C0K-WRJD.js.br +0 -0
- package/dist/public/assets/{_DMb-aGhT.js → _C3M50_zF.js} +1 -1
- package/dist/public/assets/_C3M50_zF.js.br +0 -0
- package/dist/public/assets/_C5l9z0N5.js +1 -0
- package/dist/public/assets/{_BLMA7nPf.js → _CArazMIS.js} +1 -1
- package/dist/public/assets/_CArazMIS.js.br +0 -0
- package/dist/public/assets/{_BWXbXLjv.js → _CBZB5Yxv.js} +1 -1
- package/dist/public/assets/_CBZB5Yxv.js.br +0 -0
- package/dist/public/assets/{_og2j-mr5.js → _CCDOu5-k.js} +1 -1
- package/dist/public/assets/_CCDOu5-k.js.br +0 -0
- package/dist/public/assets/_CCnHpcK_.js +1 -0
- package/dist/public/assets/_CDMh_Czk.js +1 -0
- package/dist/public/assets/_CE_CwnMm.js +1 -0
- package/dist/public/assets/_CG4M2EXP.js +1 -0
- package/dist/public/assets/{_DxS6OhEn.js → _CHEpaVmI.js} +1 -1
- package/dist/public/assets/_CHEpaVmI.js.br +0 -0
- package/dist/public/assets/_CHxSYsqp.js +1 -0
- package/dist/public/assets/_CJMlU8h-.js +1 -0
- package/dist/public/assets/{_DKj8yOqM.js → _CJoG6S3-.js} +1 -1
- package/dist/public/assets/_CJoG6S3-.js.br +0 -0
- package/dist/public/assets/{_TX0w8pNw.js → _CKO6VeWe.js} +1 -1
- package/dist/public/assets/_CKO6VeWe.js.br +0 -0
- package/dist/public/assets/_COzp254y.js +1 -0
- package/dist/public/assets/{_Cgv_vb21.js → _CPH47jXm.js} +1 -1
- package/dist/public/assets/_CPH47jXm.js.br +0 -0
- package/dist/public/assets/{_vSKAFeTh.js → _CRZGR7so.js} +1 -1
- package/dist/public/assets/_CRZGR7so.js.br +0 -0
- package/dist/public/assets/{_DQGEdzOn.js → _CSmKiIv8.js} +1 -1
- package/dist/public/assets/{_DTJl30hJ.js → _CTPBS0LC.js} +1 -1
- package/dist/public/assets/_CTPBS0LC.js.br +0 -0
- package/dist/public/assets/{_BsM6FL0G.js → _CThA4lbu.js} +1 -1
- package/dist/public/assets/_CThA4lbu.js.br +0 -0
- package/dist/public/assets/{_DqvnbtAR.js → _CXBw8HMs.js} +1 -1
- package/dist/public/assets/_CXBw8HMs.js.br +0 -0
- package/dist/public/assets/_CXzJ6bil.js +1 -0
- package/dist/public/assets/{_BVgYEt31.js → _CYUNskmo.js} +1 -1
- package/dist/public/assets/_CYUNskmo.js.br +0 -0
- package/dist/public/assets/{_C4rxuku1.js → _C_Lr0eFb.js} +1 -1
- package/dist/public/assets/_C_Lr0eFb.js.br +0 -0
- package/dist/public/assets/_CbnW4odW.js +1 -0
- package/dist/public/assets/_Cfuem2T_.js +1 -0
- package/dist/public/assets/{_CEKYlz0a.js → _Cg-uvNvy.js} +1 -1
- package/dist/public/assets/_Cg-uvNvy.js.br +0 -0
- package/dist/public/assets/{_C6u-Srqk.js → _CgLkQJNG.js} +1 -1
- package/dist/public/assets/_CgLkQJNG.js.br +0 -0
- package/dist/public/assets/{_DZIuSdjQ.js → _CkwMzG0u.js} +1 -1
- package/dist/public/assets/_CkwMzG0u.js.br +0 -0
- package/dist/public/assets/{_B2k34w2U.js → _Cl7KlO12.js} +1 -1
- package/dist/public/assets/_Cl7KlO12.js.br +0 -0
- package/dist/public/assets/_ClSzF1-k.js +1 -0
- package/dist/public/assets/{_CgP5xz-S.js → _CljaR6t8.js} +1 -1
- package/dist/public/assets/_CljaR6t8.js.br +0 -0
- package/dist/public/assets/{_DtY8_Qtl.js → _CoZYY0F5.js} +1 -1
- package/dist/public/assets/_CoZYY0F5.js.br +0 -0
- package/dist/public/assets/{_BaxiLRDq.js → _CpposQLo.js} +1 -1
- package/dist/public/assets/_CpposQLo.js.br +0 -0
- package/dist/public/assets/{_B9vNWNy5.js → _CrONT8um.js} +1 -1
- package/dist/public/assets/_CrONT8um.js.br +0 -0
- package/dist/public/assets/_Cu1guAvn.js +1 -0
- package/dist/public/assets/{_Dem8VmJt.js → _CygSeSG1.js} +1 -1
- package/dist/public/assets/_CygSeSG1.js.br +0 -0
- package/dist/public/assets/_Czf21qbL.js +1 -0
- package/dist/public/assets/_D-2kyUyG.js +9 -0
- package/dist/public/assets/_D-2kyUyG.js.br +0 -0
- package/dist/public/assets/{_CNG8WwTi.js → _D07_FXWo.js} +1 -1
- package/dist/public/assets/_D3Je7YVw.js +1 -0
- package/dist/public/assets/_D4xPo0HZ.js +1 -0
- package/dist/public/assets/_D605QHvK.js +1 -0
- package/dist/public/assets/{_BSyVWzez.js → _D8WMh6gu.js} +1 -1
- package/dist/public/assets/_D8WMh6gu.js.br +0 -0
- package/dist/public/assets/{_2jMrHLFD.js → _D8ve50C6.js} +1 -1
- package/dist/public/assets/_D8ve50C6.js.br +0 -0
- package/dist/public/assets/{_BIY6vLu9.js → _D9b8PlSh.js} +17 -17
- package/dist/public/assets/_D9b8PlSh.js.br +0 -0
- package/dist/public/assets/{_BX4xzjKF.js → _D9cUlE3a.js} +1 -1
- package/dist/public/assets/_D9cUlE3a.js.br +0 -0
- package/dist/public/assets/{_Clv6aVv7.js → _DCg8UEc3.js} +1 -1
- package/dist/public/assets/_DCg8UEc3.js.br +0 -0
- package/dist/public/assets/{_BGPJXvZs.js → _DEf8mUHj.js} +1 -1
- package/dist/public/assets/_DEf8mUHj.js.br +0 -0
- package/dist/public/assets/{_B-Y1YuyQ.js → _DFjp3R_E.js} +1 -1
- package/dist/public/assets/_DFjp3R_E.js.br +0 -0
- package/dist/public/assets/_DGLS_XIM.js +1 -0
- package/dist/public/assets/{_DvW_OT8c.js → _DH7j4yfd.js} +1 -1
- package/dist/public/assets/_DH7j4yfd.js.br +0 -0
- package/dist/public/assets/{_CSsQyHnz.js → _DITz3wmu.js} +1 -1
- package/dist/public/assets/_DITz3wmu.js.br +0 -0
- package/dist/public/assets/{_B23autIT.js → _DIbtkkt7.js} +1 -1
- package/dist/public/assets/_DIhhsT9E.js +1 -0
- package/dist/public/assets/_DJ16po4M.js +1 -0
- package/dist/public/assets/_DKPOXVYN.js +1 -0
- package/dist/public/assets/_DKPOXVYN.js.br +0 -0
- package/dist/public/assets/{_BqoWO0Lt.js → _DMVwpwke.js} +1 -1
- package/dist/public/assets/_DMVwpwke.js.br +0 -0
- package/dist/public/assets/_DMWaKaIG.js +1 -0
- package/dist/public/assets/_DOXCLF1P.js +1 -0
- package/dist/public/assets/_DQYtsGep.js +1 -0
- package/dist/public/assets/_DRNYHvLK.js +86 -0
- package/dist/public/assets/_DRNYHvLK.js.br +0 -0
- package/dist/public/assets/{_BFUZuxfB.js → _DSn_einI.js} +1 -1
- package/dist/public/assets/_DSn_einI.js.br +0 -0
- package/dist/public/assets/{_fkTQCq_W.js → _DVs9_2EC.js} +1 -1
- package/dist/public/assets/_DVs9_2EC.js.br +0 -0
- package/dist/public/assets/{_CLSP-PhQ.js → _DY6PUWO1.js} +1 -1
- package/dist/public/assets/_DY6PUWO1.js.br +0 -0
- package/dist/public/assets/_DYq5I1a_.js +1 -0
- package/dist/public/assets/{_DiBlTRgB.js → _DZaWjeov.js} +1 -1
- package/dist/public/assets/_DZaWjeov.js.br +0 -0
- package/dist/public/assets/{_DyJiy6q7.js → _D_zYO-l8.js} +1 -1
- package/dist/public/assets/_D_zYO-l8.js.br +0 -0
- package/dist/public/assets/_Db15f2uj.js +1 -0
- package/dist/public/assets/_DcyKrmeq.js +1 -0
- package/dist/public/assets/{_1ROJt5O3.js → _DdHifLBM.js} +1 -1
- package/dist/public/assets/_DdHifLBM.js.br +0 -0
- package/dist/public/assets/{_Ci0fM4lS.js → _DdfXpdn_.js} +1 -1
- package/dist/public/assets/_DdfXpdn_.js.br +0 -0
- package/dist/public/assets/_DevPwgsp.js +1 -0
- package/dist/public/assets/_DgwVeEsD.js +6 -0
- package/dist/public/assets/_DgwVeEsD.js.br +0 -0
- package/dist/public/assets/{_-dqzO7S_.js → _DhHO6Tuz.js} +1 -1
- package/dist/public/assets/_DhHO6Tuz.js.br +0 -0
- package/dist/public/assets/_DkSuDAhs.js +1 -0
- package/dist/public/assets/{_8LHYZ0G8.js → _DmGawoV0.js} +1 -1
- package/dist/public/assets/_DmGawoV0.js.br +0 -0
- package/dist/public/assets/{_DaSvMIzg.js → _DnlCrUDV.js} +2 -2
- package/dist/public/assets/_DnlCrUDV.js.br +0 -0
- package/dist/public/assets/{_CT9R7V5L.js → _Do95KzEK.js} +1 -1
- package/dist/public/assets/_Do95KzEK.js.br +0 -0
- package/dist/public/assets/_Dob_Fmbm.js +1 -0
- package/dist/public/assets/{_B0m_B4cy.js → _Dq-Jad5X.js} +1 -1
- package/dist/public/assets/_Dq-Jad5X.js.br +0 -0
- package/dist/public/assets/{_rd-8foIp.js → _DqAamLSj.js} +1 -1
- package/dist/public/assets/_DqAamLSj.js.br +0 -0
- package/dist/public/assets/_DsT0GWgJ.js +1 -0
- package/dist/public/assets/_Ds_ya4mr.js +1 -0
- package/dist/public/assets/{_CcKsIH5h.js → _Dsv1qAjm.js} +1 -1
- package/dist/public/assets/_Dsv1qAjm.js.br +0 -0
- package/dist/public/assets/_DuR6ueoD.js +1 -0
- package/dist/public/assets/_Dy4QBSz2.js +1 -0
- package/dist/public/assets/_GHV_3jJp.js +1 -0
- package/dist/public/assets/_GN2ORfhz.js +1 -0
- package/dist/public/assets/{_MatnNE0E.js → _GnLkrWZM.js} +1 -1
- package/dist/public/assets/_GnLkrWZM.js.br +0 -0
- package/dist/public/assets/_IsgXwMDs.js +1 -0
- package/dist/public/assets/{_PwtpBaMf.js → _ItxI9nk3.js} +1 -1
- package/dist/public/assets/_ItxI9nk3.js.br +0 -0
- package/dist/public/assets/_IzVuLVN1.js +1 -0
- package/dist/public/assets/{_B_DIXi0k.js → _JekmkJTS.js} +1 -1
- package/dist/public/assets/_JekmkJTS.js.br +0 -0
- package/dist/public/assets/_KiUN85cI.js +1 -0
- package/dist/public/assets/_NeT1bfJI.js +1 -0
- package/dist/public/assets/_QWPZ0V06.js +1 -0
- package/dist/public/assets/_Qs4Lwkwv.js +1 -0
- package/dist/public/assets/_Qs4Lwkwv.js.br +0 -0
- package/dist/public/assets/_RNDgNS1c.js +1 -0
- package/dist/public/assets/_RVKQdbGj.js +1 -0
- package/dist/public/assets/{_DeqN8IUm.js → _RwgpF7LC.js} +1 -1
- package/dist/public/assets/_RwgpF7LC.js.br +0 -0
- package/dist/public/assets/{_Dr6eQaDf.js → _S99TppFt.js} +1 -1
- package/dist/public/assets/_S99TppFt.js.br +0 -0
- package/dist/public/assets/_S_seG_by.js +1 -0
- package/dist/public/assets/{_Btm6Fw4r.js → _TcqYUNNo.js} +1 -1
- package/dist/public/assets/_TcqYUNNo.js.br +0 -0
- package/dist/public/assets/_WkU7AijT.js +1 -0
- package/dist/public/assets/__bV-79mJ.js +1 -0
- package/dist/public/assets/{_C2vaJoKP.js → _e-EoIF14.js} +1 -1
- package/dist/public/assets/_e-EoIF14.js.br +0 -0
- package/dist/public/assets/{_C7NJXs7I.js → _eutX35zw.js} +1 -1
- package/dist/public/assets/_eutX35zw.js.br +0 -0
- package/dist/public/assets/{_DLQCOYuD.js → _gkUCl--6.js} +1 -1
- package/dist/public/assets/_gkUCl--6.js.br +0 -0
- package/dist/public/assets/{_DqzA5hgG.js → _kmXANkGr.js} +1 -1
- package/dist/public/assets/_kmXANkGr.js.br +0 -0
- package/dist/public/assets/{_0C7z67Qm.js → _kqic6B_q.js} +1 -1
- package/dist/public/assets/_kqic6B_q.js.br +0 -0
- package/dist/public/assets/_oXuFhEmo.js +1 -0
- package/dist/public/assets/{_Opj7Hn0u.js → _q4Q8wf1-.js} +1 -1
- package/dist/public/assets/_q4Q8wf1-.js.br +0 -0
- package/dist/public/assets/_rHKeW7zh.js +1 -0
- package/dist/public/assets/_ryYtmLmM.js +1 -0
- package/dist/public/assets/_tHa-ZHkQ.js +1 -0
- package/dist/public/assets/{_RA0df7Za.js → _up7lLRgA.js} +1 -1
- package/dist/public/assets/{_Be9c3c5k.js → _vunrMsGK.js} +1 -1
- package/dist/public/assets/_vunrMsGK.js.br +0 -0
- package/dist/public/assets/{_Crcj75Rn.js → _wjT9tLjb.js} +1 -1
- package/dist/public/assets/_wjT9tLjb.js.br +0 -0
- package/dist/public/assets/_xw7rRdJa.js +1 -0
- package/dist/public/assets/_y1uwlPvR.js +1 -0
- package/dist/public/assets/{_BwugFc4p.js → _z3bkSqoV.js} +1 -1
- package/dist/public/assets/_z3bkSqoV.js.br +0 -0
- package/dist/public/assets/{app-page-CyU0ft_x.css → app-page-a-M15PMY.css} +1 -1
- package/dist/public/assets/app-page-a-M15PMY.css.br +0 -0
- package/dist/public/assets/{command-palette-BgUVS5fJ.css → command-palette-DnTWF_r2.css} +1 -1
- package/dist/public/assets/command-palette-DnTWF_r2.css.br +0 -0
- package/dist/public/assets/files-view-C_Ak1o_u.css +1 -0
- package/dist/public/assets/files-view-C_Ak1o_u.css.br +0 -0
- package/dist/public/assets/files-view-D29gwNTU.js +2 -0
- package/dist/public/assets/{icon-search-Cslr912D.css → icon-search-CiIAr7NA.css} +1 -1
- package/dist/public/assets/icon-search-CiIAr7NA.css.br +0 -0
- package/dist/public/assets/manage-workspace-FNt9673q.css +1 -0
- package/dist/public/assets/manage-workspace-LPUu3-Rp.js +2 -0
- package/dist/public/assets/{modal-view-C0q2p23m.css → modal-view-B53laKnF.css} +1 -1
- package/dist/public/assets/modal-view-B53laKnF.css.br +0 -0
- package/dist/public/assets/new-pJJ6ZABY.js +1 -0
- package/dist/public/assets/new-workspace-Bj3jwBnp.css +1 -0
- package/dist/public/assets/new-workspace-Bj3jwBnp.css.br +0 -0
- package/dist/public/assets/new-workspace-Cukm9rzJ.js +2 -0
- package/dist/public/assets/port-chip-B2VAUY_i.css +1 -0
- package/dist/public/assets/port-chip-B2VAUY_i.css.br +0 -0
- package/dist/public/assets/prompt-dialog-CtmcwmFw.css +1 -0
- package/dist/public/assets/prompt-dialog-CtmcwmFw.css.br +0 -0
- package/dist/public/assets/quick-open-CuR4WzpL.css +1 -0
- package/dist/public/assets/quick-open-CuR4WzpL.css.br +0 -0
- package/dist/public/assets/repo-picker-DFTPYkNQ.css +1 -0
- package/dist/public/assets/repo-picker-DFTPYkNQ.css.br +2 -0
- package/dist/public/assets/review-view-B6bsrcls.css +1 -0
- package/dist/public/assets/review-view-B6bsrcls.css.br +0 -0
- package/dist/public/assets/review-view-BNyQ646Y.js +2 -0
- package/dist/public/assets/section-aiv_79hg.css +1 -0
- package/dist/public/assets/section-aiv_79hg.css.br +0 -0
- package/dist/public/assets/settings-pJJ6ZABY.js +1 -0
- package/dist/public/assets/settings-page-DAwtsEQ9.css +1 -0
- package/dist/public/assets/settings-page-DAwtsEQ9.css.br +0 -0
- package/dist/public/assets/settings-page-DsbRNeSq.js +2 -0
- package/dist/public/assets/sso-auth-JILClN0h.css +1 -0
- package/dist/public/assets/sso-auth-JILClN0h.css.br +0 -0
- package/dist/public/assets/w-pJJ6ZABY.js +1 -0
- package/dist/public/assets/w._-C4FeaeOO.js +1 -0
- package/dist/public/assets/w._-DChqn_ll.js +1 -0
- package/dist/public/assets/w._-pJJ6ZABY.js +1 -0
- package/dist/public/badge-96.png +0 -0
- package/dist/public/sw.js +51 -7
- package/dist/public/sw.js.br +0 -0
- package/lib/review/markdown.mjs +51 -34
- package/lib/setup/agents.json +4 -1
- package/package.json +1 -1
- package/scripts/migrate.mjs +27 -0
- package/dist/_V3gojze9.js +0 -2
- package/dist/public/assets/_-dqzO7S_.js.br +0 -0
- package/dist/public/assets/_0C7z67Qm.js.br +0 -9
- package/dist/public/assets/_1ROJt5O3.js.br +0 -0
- package/dist/public/assets/_1XZR3z1L.js +0 -1
- package/dist/public/assets/_2jMrHLFD.js.br +0 -0
- package/dist/public/assets/_4w6Dwjlh.js.br +0 -0
- package/dist/public/assets/_7qQCv119.js +0 -1
- package/dist/public/assets/_8LHYZ0G8.js.br +0 -0
- package/dist/public/assets/_9lyEjkvB.js +0 -1
- package/dist/public/assets/_ATtSGU4R.js +0 -1
- package/dist/public/assets/_B-Y1YuyQ.js.br +0 -0
- package/dist/public/assets/_B0m_B4cy.js.br +0 -0
- package/dist/public/assets/_B2k34w2U.js.br +0 -0
- package/dist/public/assets/_B2r0KxkM.js +0 -1
- package/dist/public/assets/_B4t3OJDi.js.br +0 -0
- package/dist/public/assets/_B6XD8a0B.js +0 -1
- package/dist/public/assets/_B6XD8a0B.js.br +0 -0
- package/dist/public/assets/_B7pXsDiF.js.br +0 -0
- package/dist/public/assets/_B9vNWNy5.js.br +0 -0
- package/dist/public/assets/_BACT_fWM.js.br +0 -0
- package/dist/public/assets/_BD9DXti2.js +0 -1
- package/dist/public/assets/_BFUZuxfB.js.br +0 -0
- package/dist/public/assets/_BGPJXvZs.js.br +0 -0
- package/dist/public/assets/_BHPRD7wn.js +0 -1
- package/dist/public/assets/_BIY6vLu9.js.br +0 -0
- package/dist/public/assets/_BIqT1F8o.js +0 -1
- package/dist/public/assets/_BKPR7Exm.js +0 -1
- package/dist/public/assets/_BLMA7nPf.js.br +0 -0
- package/dist/public/assets/_BMG0X0XF.js +0 -1
- package/dist/public/assets/_BPX5CxjX.js +0 -1
- package/dist/public/assets/_BS9oCUE0.js +0 -1
- package/dist/public/assets/_BSSpkgu8.js +0 -1
- package/dist/public/assets/_BSyVWzez.js.br +0 -0
- package/dist/public/assets/_BVelHN7q.js +0 -1
- package/dist/public/assets/_BVgYEt31.js.br +0 -0
- package/dist/public/assets/_BWXbXLjv.js.br +0 -0
- package/dist/public/assets/_BX4xzjKF.js.br +0 -0
- package/dist/public/assets/_BXdWjkI5.js +0 -1
- package/dist/public/assets/_B_DIXi0k.js.br +0 -0
- package/dist/public/assets/_Ba1mrOxI.js.br +0 -0
- package/dist/public/assets/_BaxiLRDq.js.br +0 -0
- package/dist/public/assets/_Bc_Ezk5L.js.br +0 -0
- package/dist/public/assets/_Be9c3c5k.js.br +0 -0
- package/dist/public/assets/_BeNkN5H0.js +0 -1
- package/dist/public/assets/_Bg1dyEQN.js.br +0 -0
- package/dist/public/assets/_Bi9nBUKQ.js +0 -1
- package/dist/public/assets/_BmB3qu5w.js +0 -1
- package/dist/public/assets/_BpyT0ApF.js +0 -1
- package/dist/public/assets/_BqA4Q1j3.js +0 -1
- package/dist/public/assets/_BqoWO0Lt.js.br +0 -0
- package/dist/public/assets/_BsM6FL0G.js.br +0 -0
- package/dist/public/assets/_Btm6Fw4r.js.br +0 -0
- package/dist/public/assets/_BubcBfAr.js.br +0 -0
- package/dist/public/assets/_BvKUsWT1.js +0 -1
- package/dist/public/assets/_BwugFc4p.js.br +0 -0
- package/dist/public/assets/_BxlXGcbO.js +0 -1
- package/dist/public/assets/_Byer70eB.js +0 -1
- package/dist/public/assets/_C0udqXoV.js +0 -1
- package/dist/public/assets/_C1a168fm.js +0 -1
- package/dist/public/assets/_C1x7pcqW.js +0 -1
- package/dist/public/assets/_C2vaJoKP.js.br +0 -0
- package/dist/public/assets/_C3uAjjk3.js +0 -1
- package/dist/public/assets/_C4rxuku1.js.br +0 -0
- package/dist/public/assets/_C6KQjdMM.js +0 -1
- package/dist/public/assets/_C6u-Srqk.js.br +0 -0
- package/dist/public/assets/_C7NJXs7I.js.br +0 -0
- package/dist/public/assets/_C8hQ66UC.js.br +0 -0
- package/dist/public/assets/_CAB7vlnJ.js +0 -1
- package/dist/public/assets/_CASEbE0K.js +0 -86
- package/dist/public/assets/_CASEbE0K.js.br +0 -0
- package/dist/public/assets/_CD9reWft.js.br +0 -0
- package/dist/public/assets/_CEKYlz0a.js.br +0 -0
- package/dist/public/assets/_CEj4Mwc1.js +0 -1
- package/dist/public/assets/_CI6Hg_A9.js +0 -1
- package/dist/public/assets/_CIFyxofO.js +0 -1
- package/dist/public/assets/_CKxJlwEy.js +0 -1
- package/dist/public/assets/_CLSP-PhQ.js.br +0 -0
- package/dist/public/assets/_CPzscDEb.js +0 -1
- package/dist/public/assets/_CQLI-En1.js +0 -1
- package/dist/public/assets/_CSsQyHnz.js.br +0 -0
- package/dist/public/assets/_CT9R7V5L.js.br +0 -0
- package/dist/public/assets/_CYk469iO.js +0 -1
- package/dist/public/assets/_CZ5Fi642.js +0 -1
- package/dist/public/assets/_CZ5Fi642.js.br +0 -0
- package/dist/public/assets/_C_5HKb42.js +0 -9
- package/dist/public/assets/_C_5HKb42.js.br +0 -0
- package/dist/public/assets/_CcKsIH5h.js.br +0 -0
- package/dist/public/assets/_Ce4nsqo2.js +0 -6
- package/dist/public/assets/_Ce4nsqo2.js.br +0 -0
- package/dist/public/assets/_Cf4gqSQz.js +0 -1
- package/dist/public/assets/_CgP5xz-S.js.br +0 -0
- package/dist/public/assets/_CgUj7qP_.js.br +0 -0
- package/dist/public/assets/_Cgv_vb21.js.br +0 -0
- package/dist/public/assets/_Ci0fM4lS.js.br +0 -0
- package/dist/public/assets/_Ckc7dIlo.js.br +0 -0
- package/dist/public/assets/_Clv6aVv7.js.br +0 -0
- package/dist/public/assets/_CqkTkgJB.js +0 -1
- package/dist/public/assets/_Crcj75Rn.js.br +0 -0
- package/dist/public/assets/_CuGgpK2u.js +0 -1
- package/dist/public/assets/_CuVmBZbd.js +0 -1
- package/dist/public/assets/_Cw8c7j2h.js +0 -1
- package/dist/public/assets/_CwXTg1fU.js.br +0 -0
- package/dist/public/assets/_D6fhoYuY.js +0 -1
- package/dist/public/assets/_D9nJ4nG1.js.br +0 -0
- package/dist/public/assets/_DAVXBkYN.js.br +0 -0
- package/dist/public/assets/_DDQhJR3T.js +0 -1
- package/dist/public/assets/_DFMu7qvY.js.br +0 -0
- package/dist/public/assets/_DKj8yOqM.js.br +0 -0
- package/dist/public/assets/_DLQCOYuD.js.br +0 -0
- package/dist/public/assets/_DMb-aGhT.js.br +0 -0
- package/dist/public/assets/_DOghH8Tl.js +0 -1
- package/dist/public/assets/_DPT6ZO90.js.br +0 -0
- package/dist/public/assets/_DPtSJ35G.js.br +0 -0
- package/dist/public/assets/_DTJl30hJ.js.br +0 -0
- package/dist/public/assets/_DTcQCcgk.js +0 -1
- package/dist/public/assets/_DVy22yDU.js.br +0 -0
- package/dist/public/assets/_DWyu89Hi.js +0 -1
- package/dist/public/assets/_DZIuSdjQ.js.br +0 -0
- package/dist/public/assets/_DaSvMIzg.js.br +0 -0
- package/dist/public/assets/_DbZpXQPs.js +0 -1
- package/dist/public/assets/_Dd3TMJAY.js +0 -1
- package/dist/public/assets/_Dem8VmJt.js.br +0 -0
- package/dist/public/assets/_DeqN8IUm.js.br +0 -0
- package/dist/public/assets/_DfrEESyG.js +0 -1
- package/dist/public/assets/_DiBlTRgB.js.br +0 -0
- package/dist/public/assets/_Dj8W5hAy.js.br +0 -0
- package/dist/public/assets/_DmehuAAq.js +0 -1
- package/dist/public/assets/_DmehuAAq.js.br +0 -0
- package/dist/public/assets/_DoTqr-WV.js +0 -1
- package/dist/public/assets/_DqvnbtAR.js.br +0 -0
- package/dist/public/assets/_DqzA5hgG.js.br +0 -0
- package/dist/public/assets/_Dr6eQaDf.js.br +0 -0
- package/dist/public/assets/_DtY8_Qtl.js.br +0 -0
- package/dist/public/assets/_Dv8p2zgH.js +0 -1
- package/dist/public/assets/_DvW_OT8c.js.br +0 -0
- package/dist/public/assets/_DxS6OhEn.js.br +0 -0
- package/dist/public/assets/_DyJiy6q7.js.br +0 -0
- package/dist/public/assets/_FC28AlBj.js +0 -1
- package/dist/public/assets/_MatnNE0E.js.br +0 -0
- package/dist/public/assets/_N0oP21xr.js +0 -1
- package/dist/public/assets/_Opj7Hn0u.js.br +0 -0
- package/dist/public/assets/_P1g2Hmou.js.br +0 -0
- package/dist/public/assets/_PwtpBaMf.js.br +0 -0
- package/dist/public/assets/_TX0w8pNw.js.br +0 -0
- package/dist/public/assets/_ZHFqgS6H.js +0 -1
- package/dist/public/assets/_awhwVzn2.js +0 -1
- package/dist/public/assets/_bcmbsCHv.js +0 -1
- package/dist/public/assets/_cX3i7NsL.js +0 -1
- package/dist/public/assets/_fkTQCq_W.js.br +0 -0
- package/dist/public/assets/_h4esEJW5.js +0 -1
- package/dist/public/assets/_kxgPx4kL.js +0 -1
- package/dist/public/assets/_naTBruJS.js +0 -1
- package/dist/public/assets/_oA-Vr8Cf.js.br +0 -0
- package/dist/public/assets/_oBcpaEKd.js.br +0 -0
- package/dist/public/assets/_og2j-mr5.js.br +0 -0
- package/dist/public/assets/_qI-Bm0LQ.js +0 -1
- package/dist/public/assets/_rd-8foIp.js.br +0 -0
- package/dist/public/assets/_sQjKpNXF.js +0 -1
- package/dist/public/assets/_tPOATR_S.js.br +0 -0
- package/dist/public/assets/_vSKAFeTh.js.br +0 -0
- package/dist/public/assets/_yUTKWdFy.js +0 -1
- package/dist/public/assets/app-page-CyU0ft_x.css.br +0 -0
- package/dist/public/assets/command-palette-BgUVS5fJ.css.br +0 -0
- package/dist/public/assets/files-view-68OApH0T.css +0 -1
- package/dist/public/assets/files-view-68OApH0T.css.br +0 -0
- package/dist/public/assets/files-view-CK0sC8sL.js +0 -2
- package/dist/public/assets/icon-search-Cslr912D.css.br +0 -0
- package/dist/public/assets/manage-workspace-Cp4MnvNw.js +0 -2
- package/dist/public/assets/manage-workspace-EFWH7-Cv.css +0 -1
- package/dist/public/assets/modal-view-C0q2p23m.css.br +0 -0
- package/dist/public/assets/new-BHVoRMDD.js +0 -1
- package/dist/public/assets/new-workspace-ChGmqibN.css +0 -1
- package/dist/public/assets/new-workspace-ChGmqibN.css.br +0 -0
- package/dist/public/assets/new-workspace-TC9V3qGi.js +0 -2
- package/dist/public/assets/port-chip-DefDqlaR.css +0 -1
- package/dist/public/assets/port-chip-DefDqlaR.css.br +0 -0
- package/dist/public/assets/prompt-dialog-CJDGPwkv.css +0 -1
- package/dist/public/assets/prompt-dialog-CJDGPwkv.css.br +0 -0
- package/dist/public/assets/quick-open-Diva1_qC.css +0 -1
- package/dist/public/assets/quick-open-Diva1_qC.css.br +0 -0
- package/dist/public/assets/repo-picker-CpKpWL1n.css +0 -1
- package/dist/public/assets/repo-picker-CpKpWL1n.css.br +0 -0
- package/dist/public/assets/review-view-CFzk48_R.js +0 -2
- package/dist/public/assets/review-view-CgJv3K8t.css +0 -1
- package/dist/public/assets/review-view-CgJv3K8t.css.br +0 -0
- package/dist/public/assets/section-iG3mkmoF.css +0 -1
- package/dist/public/assets/section-iG3mkmoF.css.br +0 -0
- package/dist/public/assets/settings-BHVoRMDD.js +0 -1
- package/dist/public/assets/settings-page-SZbATvFs.js +0 -2
- package/dist/public/assets/settings-page-lzx2uvfg.css +0 -1
- package/dist/public/assets/settings-page-lzx2uvfg.css.br +0 -0
- package/dist/public/assets/sso-auth-hsN6VJBq.css +0 -1
- package/dist/public/assets/sso-auth-hsN6VJBq.css.br +0 -4
- package/dist/public/assets/w-BHVoRMDD.js +0 -1
- package/dist/public/assets/w._-BHVoRMDD.js +0 -1
- package/dist/public/assets/w._-Cwa3FQTr.js +0 -1
- package/dist/public/assets/w._-XzA5CKe1.js +0 -1
|
@@ -36,14 +36,31 @@ var config = {
|
|
|
36
36
|
* `3000-work.example.com`. The server accepts both; this only picks
|
|
37
37
|
* which one the port chips link to. `dash` stays one label under the
|
|
38
38
|
* zone, which is what a free Cloudflare certificate covers. */
|
|
39
|
-
portHosts: process.env.WORK_PORT_HOSTS === "dash" ? "dash" : "sub"
|
|
39
|
+
portHosts: process.env.WORK_PORT_HOSTS === "dash" ? "dash" : "sub",
|
|
40
|
+
/** The address the server listens on: this machine only, unless set
|
|
41
|
+
* (`0.0.0.0` / `::` for every interface, or one address) — reaching it
|
|
42
|
+
* from elsewhere is an auth wrapper's job (server/access.ts). Under
|
|
43
|
+
* systemd the socket unit binds, and `work setup` writes this into it. */
|
|
44
|
+
listen: process.env.WORK_LISTEN || "127.0.0.1",
|
|
45
|
+
/** The auth wrapper's addresses when it runs on another machine (IPs or
|
|
46
|
+
* CIDRs, or `*`): the only peers besides this machine work answers. */
|
|
47
|
+
trustedProxies: list(process.env.WORK_TRUSTED_PROXIES),
|
|
48
|
+
/** Names beyond this machine's own that work answers to — the ones a
|
|
49
|
+
* wrapper in front of it serves (their port subdomains included). */
|
|
50
|
+
allowedHosts: list(process.env.WORK_ALLOWED_HOSTS)
|
|
40
51
|
};
|
|
52
|
+
function list(value) {
|
|
53
|
+
return (value || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
54
|
+
}
|
|
41
55
|
fs.mkdirSync(config.dataDir, { recursive: true });
|
|
42
56
|
//#endregion
|
|
43
57
|
//#region src/server/exec.ts
|
|
44
58
|
/** Dedicated tmux server so we never touch another multiplexer's or the
|
|
45
|
-
* user's default tmux.
|
|
46
|
-
|
|
59
|
+
* user's default tmux. `WORK_TMUX_SOCKET` names another for a server that
|
|
60
|
+
* must not share the machine's (the smoke test, a throwaway): each start
|
|
61
|
+
* detaches every client on its socket, which on the live one cut every
|
|
62
|
+
* open terminal. */
|
|
63
|
+
var TMUX_SOCKET = process.env.WORK_TMUX_SOCKET || "work";
|
|
47
64
|
var execFileAsync = promisify(execFile);
|
|
48
65
|
async function run$1(cmd, args, cwd, env, opts = {}) {
|
|
49
66
|
const child = execFileAsync(cmd, args, {
|
|
@@ -305,7 +322,8 @@ var agents_default = [
|
|
|
305
322
|
"shell": "sh"
|
|
306
323
|
},
|
|
307
324
|
"icon": "M4 5.25h16v2.9h-2.35v10.6h-2.9V8.15h-3.5v10.6h-2.9V8.15H7.1c-.62 0-1.13.32-1.46.86l-.36.6L4 8.6V5.25z",
|
|
308
|
-
"reauth": "pi"
|
|
325
|
+
"reauth": "pi",
|
|
326
|
+
"pathDirs": ["~/.pi/agent/bin"]
|
|
309
327
|
}
|
|
310
328
|
];
|
|
311
329
|
//#endregion
|
|
@@ -890,7 +908,7 @@ function shared(fn, ttlMs) {
|
|
|
890
908
|
}
|
|
891
909
|
//#endregion
|
|
892
910
|
//#region src/server/events.ts
|
|
893
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
911
|
+
var listeners$1 = /* @__PURE__ */ new Set();
|
|
894
912
|
function publish(type, data) {
|
|
895
913
|
publishJson(type, data === void 0 ? void 0 : JSON.stringify(data), data);
|
|
896
914
|
}
|
|
@@ -902,20 +920,20 @@ function publishJson(type, dataJson, data) {
|
|
|
902
920
|
data,
|
|
903
921
|
frame: dataJson === void 0 ? JSON.stringify({ type }) : `{"type":${JSON.stringify(type)},"data":${dataJson}}`
|
|
904
922
|
};
|
|
905
|
-
for (const listener of [...listeners]) try {
|
|
923
|
+
for (const listener of [...listeners$1]) try {
|
|
906
924
|
listener(event);
|
|
907
925
|
} catch (err) {
|
|
908
926
|
console.error("[work] event listener error", err);
|
|
909
927
|
}
|
|
910
928
|
}
|
|
911
929
|
function subscribe(listener) {
|
|
912
|
-
listeners.add(listener);
|
|
913
|
-
return () => listeners.delete(listener);
|
|
930
|
+
listeners$1.add(listener);
|
|
931
|
+
return () => listeners$1.delete(listener);
|
|
914
932
|
}
|
|
915
933
|
/** Connected SSE clients — lets background watchers idle when no one
|
|
916
934
|
* is listening. */
|
|
917
935
|
function subscriberCount() {
|
|
918
|
-
return listeners.size;
|
|
936
|
+
return listeners$1.size;
|
|
919
937
|
}
|
|
920
938
|
//#endregion
|
|
921
939
|
//#region node_modules/.pnpm/bn.js@4.12.5/node_modules/bn.js/lib/bn.js
|
|
@@ -6563,12 +6581,20 @@ var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
|
|
|
6563
6581
|
sendNotification: webPush.sendNotification.bind(webPush)
|
|
6564
6582
|
};
|
|
6565
6583
|
})))(), 1);
|
|
6566
|
-
/** Web Push for
|
|
6567
|
-
*
|
|
6568
|
-
* push endpoint reports
|
|
6584
|
+
/** Web Push for agent alerts (a turn finished, an agent stopped on the
|
|
6585
|
+
* user). VAPID keys are generated once and kept in the data dir;
|
|
6586
|
+
* subscriptions live in sqlite and are pruned when a push endpoint reports
|
|
6587
|
+
* them gone. */
|
|
6569
6588
|
function pushPublicKey() {
|
|
6570
6589
|
return keys().publicKey;
|
|
6571
6590
|
}
|
|
6591
|
+
/** Whether a subscription's keys are the sizes Web Push needs (a P-256
|
|
6592
|
+
* point and a 16-byte secret): one that is not fails every send, forever,
|
|
6593
|
+
* with nothing to say it is gone. */
|
|
6594
|
+
function validSubscription(subscription) {
|
|
6595
|
+
const bytes = (b64) => Buffer.from(b64, "base64url").length;
|
|
6596
|
+
return bytes(subscription.keys.p256dh) === 65 && bytes(subscription.keys.auth) === 16;
|
|
6597
|
+
}
|
|
6572
6598
|
function saveSubscription(subscription) {
|
|
6573
6599
|
db.prepare("INSERT OR REPLACE INTO push_subscriptions (endpoint, data) VALUES (?, ?)").run(subscription.endpoint, JSON.stringify(subscription));
|
|
6574
6600
|
}
|
|
@@ -6576,13 +6602,15 @@ function deleteSubscription(endpoint) {
|
|
|
6576
6602
|
db.prepare("DELETE FROM push_subscriptions WHERE endpoint = ?").run(endpoint);
|
|
6577
6603
|
}
|
|
6578
6604
|
/** Push to every subscribed device, or to the one `endpoint` names (a
|
|
6579
|
-
* test from Settings → Notifications).
|
|
6580
|
-
|
|
6605
|
+
* test from Settings → Notifications), and say what became of each.
|
|
6606
|
+
* `urgency`: how hard a phone should wake for it (a finished turn can wait
|
|
6607
|
+
* for the next wake; a question cannot). */
|
|
6608
|
+
async function sendPush(payload, { endpoint, urgency = "high" } = {}) {
|
|
6581
6609
|
const rows = all("SELECT endpoint, data FROM push_subscriptions").filter((row) => !endpoint || row.endpoint === endpoint);
|
|
6582
|
-
if (!rows.length) return;
|
|
6610
|
+
if (!rows.length) return [];
|
|
6583
6611
|
const { publicKey, privateKey } = keys();
|
|
6584
6612
|
const body = JSON.stringify(payload);
|
|
6585
|
-
|
|
6613
|
+
return Promise.all(rows.map(async (row) => {
|
|
6586
6614
|
try {
|
|
6587
6615
|
await import_src.default.sendNotification(JSON.parse(row.data), body, {
|
|
6588
6616
|
vapidDetails: {
|
|
@@ -6591,26 +6619,45 @@ async function sendPush(payload, endpoint) {
|
|
|
6591
6619
|
privateKey
|
|
6592
6620
|
},
|
|
6593
6621
|
TTL: 600,
|
|
6594
|
-
urgency
|
|
6622
|
+
urgency,
|
|
6623
|
+
timeout: 1e4
|
|
6595
6624
|
});
|
|
6625
|
+
return { ok: true };
|
|
6596
6626
|
} catch (err) {
|
|
6597
6627
|
const status = err.statusCode;
|
|
6598
6628
|
if (status === 404 || status === 410) deleteSubscription(row.endpoint);
|
|
6599
6629
|
else console.warn("[work] push failed", status ?? err);
|
|
6630
|
+
return {
|
|
6631
|
+
ok: false,
|
|
6632
|
+
status,
|
|
6633
|
+
error: status === 404 || status === 410 ? "the push service no longer knows this device" : status === 401 || status === 403 ? "the push service refused this server's key" : err.message || String(err)
|
|
6634
|
+
};
|
|
6600
6635
|
}
|
|
6601
6636
|
}));
|
|
6602
6637
|
}
|
|
6603
6638
|
var VAPID_SUBJECT = "https://github.com/DylanPiercey/work";
|
|
6604
6639
|
var cachedKeys = null;
|
|
6640
|
+
/** The server's key pair, made once. Only a missing file makes a new
|
|
6641
|
+
* one: every subscription was made for the old key, and a new key fails
|
|
6642
|
+
* every send to them, so a file that will not read is an error to see,
|
|
6643
|
+
* not a reason to silence every device. Written whole or not at all. */
|
|
6605
6644
|
function keys() {
|
|
6606
6645
|
if (cachedKeys) return cachedKeys;
|
|
6607
6646
|
const file = path.join(config.dataDir, "vapid.json");
|
|
6647
|
+
let text = null;
|
|
6608
6648
|
try {
|
|
6609
|
-
|
|
6610
|
-
} catch {
|
|
6611
|
-
|
|
6612
|
-
|
|
6649
|
+
text = fs.readFileSync(file, "utf8");
|
|
6650
|
+
} catch (err) {
|
|
6651
|
+
if (err.code !== "ENOENT") throw err;
|
|
6652
|
+
}
|
|
6653
|
+
if (text != null) {
|
|
6654
|
+
cachedKeys = JSON.parse(text);
|
|
6655
|
+
return cachedKeys;
|
|
6613
6656
|
}
|
|
6657
|
+
cachedKeys = import_src.default.generateVAPIDKeys();
|
|
6658
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
6659
|
+
fs.writeFileSync(tmp, JSON.stringify(cachedKeys), { mode: 384 });
|
|
6660
|
+
fs.renameSync(tmp, file);
|
|
6614
6661
|
return cachedKeys;
|
|
6615
6662
|
}
|
|
6616
6663
|
var ACCENT_RE = /^#[0-9a-f]{6}$/;
|
|
@@ -6764,7 +6811,8 @@ var meta_default = {
|
|
|
6764
6811
|
"shell": "sh"
|
|
6765
6812
|
},
|
|
6766
6813
|
icon: "M4 5.25h16v2.9h-2.35v10.6h-2.9V8.15h-3.5v10.6h-2.9V8.15H7.1c-.62 0-1.13.32-1.46.86l-.36.6L4 8.6V5.25z",
|
|
6767
|
-
reauth: "pi"
|
|
6814
|
+
reauth: "pi",
|
|
6815
|
+
pathDirs: ["~/.pi/agent/bin"]
|
|
6768
6816
|
};
|
|
6769
6817
|
//#endregion
|
|
6770
6818
|
//#region src/agents/meta.ts
|
|
@@ -6824,14 +6872,37 @@ var scraped = /* @__PURE__ */ new Map();
|
|
|
6824
6872
|
var reasons = /* @__PURE__ */ new Map();
|
|
6825
6873
|
var summaries = /* @__PURE__ */ new Map();
|
|
6826
6874
|
var viewers = /* @__PURE__ */ new Map();
|
|
6875
|
+
/** "Finished" pushes waiting out `DONE_SETTLE_MS`. */
|
|
6876
|
+
var pendingDone = /* @__PURE__ */ new Map();
|
|
6877
|
+
/** When each session's hooks last reported, since its process started:
|
|
6878
|
+
* a session whose hooks never report (not loaded, not trusted, turned
|
|
6879
|
+
* off) is left to the pane scraper rather than to silence. */
|
|
6880
|
+
var reportedAt = /* @__PURE__ */ new Map();
|
|
6881
|
+
/** A turn's end is pushed once it has stayed ended this long. Stop fires
|
|
6882
|
+
* again each time something sends the agent back to work (a stop hook
|
|
6883
|
+
* that continues it, a goal loop, background subagents reporting in), and
|
|
6884
|
+
* each of those ends was a "Finished" on the phone while it carried on. */
|
|
6885
|
+
var DONE_SETTLE_MS = 3e3;
|
|
6886
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
6887
|
+
/** Called on every phase transition (the status snapshot drops itself). */
|
|
6888
|
+
function onPhaseChange(fn) {
|
|
6889
|
+
listeners.add(fn);
|
|
6890
|
+
}
|
|
6827
6891
|
function setAgentPhase(sessionId, phase, reason, summary) {
|
|
6892
|
+
reportedAt.set(sessionId, Date.now());
|
|
6893
|
+
cancelDone(sessionId);
|
|
6894
|
+
if (phase === "idle") {
|
|
6895
|
+
if (phases.has(sessionId)) applyChange(sessionId, () => dropHook(sessionId));
|
|
6896
|
+
return;
|
|
6897
|
+
}
|
|
6828
6898
|
if (phase === "waiting" && reason === "done") {
|
|
6899
|
+
const failed = phases.get(sessionId) === "waiting" && reasons.get(sessionId) === "attention";
|
|
6829
6900
|
if (summary) summaries.set(sessionId, summary);
|
|
6830
|
-
|
|
6831
|
-
if (
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
},
|
|
6901
|
+
if (phases.has(sessionId)) applyChange(sessionId, () => dropHook(sessionId));
|
|
6902
|
+
if (!failed) pendingDone.set(sessionId, setTimeout(() => {
|
|
6903
|
+
pendingDone.delete(sessionId);
|
|
6904
|
+
notify(sessionId, "done");
|
|
6905
|
+
}, DONE_SETTLE_MS));
|
|
6835
6906
|
return;
|
|
6836
6907
|
}
|
|
6837
6908
|
if (phase === "waiting" && summary) summaries.set(sessionId, summary);
|
|
@@ -6844,7 +6915,43 @@ function setAgentPhase(sessionId, phase, reason, summary) {
|
|
|
6844
6915
|
applyChange(sessionId, () => {
|
|
6845
6916
|
phases.set(sessionId, phase);
|
|
6846
6917
|
if (phase === "waiting" && reason) reasons.set(sessionId, reason);
|
|
6847
|
-
},
|
|
6918
|
+
}, reasons.get(sessionId) !== next);
|
|
6919
|
+
}
|
|
6920
|
+
function dropHook(sessionId) {
|
|
6921
|
+
phases.delete(sessionId);
|
|
6922
|
+
reasons.delete(sessionId);
|
|
6923
|
+
}
|
|
6924
|
+
function cancelDone(sessionId) {
|
|
6925
|
+
const timer = pendingDone.get(sessionId);
|
|
6926
|
+
if (!timer) return;
|
|
6927
|
+
clearTimeout(timer);
|
|
6928
|
+
pendingDone.delete(sessionId);
|
|
6929
|
+
}
|
|
6930
|
+
/** What the session's hooks last said, apart from the scraper. */
|
|
6931
|
+
function hookPhase(sessionId) {
|
|
6932
|
+
return phases.get(sessionId);
|
|
6933
|
+
}
|
|
6934
|
+
/** When the session's hooks last reported (undefined: never, this run). */
|
|
6935
|
+
function hookReportedAt(sessionId) {
|
|
6936
|
+
return reportedAt.get(sessionId);
|
|
6937
|
+
}
|
|
6938
|
+
/** A hook's "working" whose pane has gone still: the turn ended without
|
|
6939
|
+
* a hook saying so (an interrupt, a failure the CLI reports no hook for),
|
|
6940
|
+
* since every agent's screen animates while it works. Idle, no push. */
|
|
6941
|
+
function settleQuietWorking(sessionId) {
|
|
6942
|
+
if (phases.get(sessionId) !== "working") return;
|
|
6943
|
+
applyChange(sessionId, () => dropHook(sessionId));
|
|
6944
|
+
}
|
|
6945
|
+
/** The user answered at the terminal while the session was stopped on
|
|
6946
|
+
* them (approved, denied, picked, interrupted): no longer waiting on
|
|
6947
|
+
* them, whether or not the CLI has a hook for the answer. Working until
|
|
6948
|
+
* a hook says otherwise, or the pane goes still. */
|
|
6949
|
+
function noteUserAnswered(sessionId) {
|
|
6950
|
+
if (phases.get(sessionId) !== "waiting") return;
|
|
6951
|
+
applyChange(sessionId, () => {
|
|
6952
|
+
phases.set(sessionId, "working");
|
|
6953
|
+
reasons.delete(sessionId);
|
|
6954
|
+
});
|
|
6848
6955
|
}
|
|
6849
6956
|
/** Live visible-terminal count per session, reported by the term server;
|
|
6850
6957
|
* a session someone is watching does not push notifications. */
|
|
@@ -6888,15 +6995,19 @@ async function notify(sessionId, reason) {
|
|
|
6888
6995
|
workspaceId: row.workspace_id,
|
|
6889
6996
|
sessionId
|
|
6890
6997
|
}
|
|
6891
|
-
}).catch(() => {});
|
|
6998
|
+
}, { urgency: reason === "done" ? "normal" : "high" }).catch(() => {});
|
|
6892
6999
|
}
|
|
7000
|
+
/** Forget everything about a session: its pane went away, or it is being
|
|
7001
|
+
* started afresh (a respawn must not inherit the last process's wait). */
|
|
6893
7002
|
function clearAgentPhase(sessionId) {
|
|
7003
|
+
cancelDone(sessionId);
|
|
7004
|
+
summaries.delete(sessionId);
|
|
7005
|
+
reportedAt.delete(sessionId);
|
|
6894
7006
|
if (!phases.has(sessionId) && !scraped.has(sessionId)) return;
|
|
6895
7007
|
applyChange(sessionId, () => {
|
|
6896
7008
|
phases.delete(sessionId);
|
|
6897
7009
|
scraped.delete(sessionId);
|
|
6898
7010
|
reasons.delete(sessionId);
|
|
6899
|
-
summaries.delete(sessionId);
|
|
6900
7011
|
});
|
|
6901
7012
|
}
|
|
6902
7013
|
/** What a session is doing. The hook wins where there is one: it reports
|
|
@@ -6904,26 +7015,23 @@ function clearAgentPhase(sessionId) {
|
|
|
6904
7015
|
function agentPhase(sessionId) {
|
|
6905
7016
|
return phases.get(sessionId) ?? scraped.get(sessionId);
|
|
6906
7017
|
}
|
|
6907
|
-
/**
|
|
6908
|
-
*
|
|
6909
|
-
*
|
|
6910
|
-
|
|
6911
|
-
* that was running through it) is exactly the one worth hearing about. */
|
|
6912
|
-
var BOOT_GRACE_MS = 15e3;
|
|
6913
|
-
var bootedAt = Date.now();
|
|
6914
|
-
function applyChange(sessionId, mutate, fromHook = false) {
|
|
7018
|
+
/** Apply a mutation and broadcast when it moved the session's phase (or,
|
|
7019
|
+
* `force`, its reason: finished and blocked draw differently). Only a
|
|
7020
|
+
* hook ever reports "waiting", so a transition into it is a push. */
|
|
7021
|
+
function applyChange(sessionId, mutate, force = false) {
|
|
6915
7022
|
const before = agentPhase(sessionId);
|
|
6916
7023
|
mutate();
|
|
6917
7024
|
const after = agentPhase(sessionId);
|
|
6918
|
-
if (before === after) return;
|
|
7025
|
+
if (before === after && !force) return;
|
|
6919
7026
|
publishChange(sessionId);
|
|
6920
|
-
if (after === "waiting" && before !== "waiting"
|
|
7027
|
+
if (after === "waiting" && before !== "waiting") notify(sessionId, reasons.get(sessionId) ?? "attention");
|
|
6921
7028
|
}
|
|
6922
7029
|
function publishChange(sessionId) {
|
|
7030
|
+
for (const fn of listeners) fn();
|
|
6923
7031
|
const row = get("SELECT workspace_id FROM sessions WHERE id = ?", sessionId);
|
|
6924
7032
|
if (row?.workspace_id) {
|
|
6925
7033
|
const workspaceId = row.workspace_id;
|
|
6926
|
-
import("./
|
|
7034
|
+
import("./_7W8iLTql.js").then((m) => m.notifySessionsChanged(workspaceId));
|
|
6927
7035
|
return;
|
|
6928
7036
|
}
|
|
6929
7037
|
publish("sessions.changed");
|
|
@@ -6933,37 +7041,42 @@ function publishChange(sessionId) {
|
|
|
6933
7041
|
/** Shell command a CLI hook runs to report a state: POSTs the hook's own
|
|
6934
7042
|
* stdin JSON to the app, which mines it for a notification summary. `exit 0`
|
|
6935
7043
|
* always — a state report must never block the agent. Session and port
|
|
6936
|
-
* come from the env tmux gave the pane (see sessions.ts)
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
7044
|
+
* come from the env tmux gave the pane (see sessions.ts); `family` names
|
|
7045
|
+
* the CLI, so one run inside another session's pane (a nested `grok -p`)
|
|
7046
|
+
* does not report as that session's agent. */
|
|
7047
|
+
function stateCurl(family, state, reason) {
|
|
7048
|
+
const qs = `session=$WORK_SESSION_ID&agent=${family}&state=${state}` + (reason ? `&reason=${reason}` : "");
|
|
7049
|
+
return `[ -n "$WORK_SESSION_ID" ] && curl -sf --connect-timeout 1 -m 2 -X POST "http://127.0.0.1:\${WORK_APP_PORT:-${config.appPort}}/api/agent-state?${qs}" -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1; exit 0`;
|
|
6940
7050
|
}
|
|
6941
7051
|
/** Claude-format hook groups (`{matcher?, hooks:[{type:"command",command}]}`)
|
|
6942
7052
|
* for a hook map — the JSON shape Claude, Copilot plugins and Grok all read. */
|
|
6943
|
-
function claudeStyleHooks(map, extra = {}) {
|
|
7053
|
+
function claudeStyleHooks(family, map, extra = {}) {
|
|
6944
7054
|
const hooks = {};
|
|
6945
7055
|
for (const [event, state, reason, matcher] of map) (hooks[event] ??= []).push({
|
|
6946
7056
|
...matcher ? { matcher } : {},
|
|
6947
7057
|
hooks: [{
|
|
6948
7058
|
type: "command",
|
|
6949
|
-
command: stateCurl(state, reason),
|
|
7059
|
+
command: stateCurl(family, state, reason),
|
|
6950
7060
|
...extra
|
|
6951
7061
|
}]
|
|
6952
7062
|
});
|
|
6953
7063
|
return hooks;
|
|
6954
7064
|
}
|
|
7065
|
+
/** The notifications that mean the agent is stopped on the user, and how
|
|
7066
|
+
* each reads. Only these: the CLIs also notify about an idle session (a
|
|
7067
|
+
* nudge a minute after Stop already pushed), a finished sign-in, a
|
|
7068
|
+
* question already answered, a subagent done, a quota wait resuming —
|
|
7069
|
+
* none of them a session needing anything, and each one read as
|
|
7070
|
+
* "attention" buzzed a phone and painted the nav red for nothing. */
|
|
6955
7071
|
var NOTIFICATION_LABELS = {
|
|
6956
7072
|
permission_prompt: "Waiting for permission",
|
|
6957
7073
|
agent_needs_input: "Waiting for input",
|
|
6958
7074
|
elicitation_dialog: "Asking a question",
|
|
6959
7075
|
elicitation_url_dialog: "Needs a browser sign-in"
|
|
6960
7076
|
};
|
|
6961
|
-
/**
|
|
6962
|
-
*
|
|
6963
|
-
|
|
6964
|
-
* session that merely sat there, and — read as "done" — buzzed a second
|
|
6965
|
-
* time a minute after every finish. Dropped entirely. */
|
|
6966
|
-
var NUDGE_NOTIFICATIONS = /* @__PURE__ */ new Set(["idle_prompt"]);
|
|
7077
|
+
/** A matcher for the notifications worth a report, for CLIs whose hook
|
|
7078
|
+
* config takes one (so the rest never run a hook at all). */
|
|
7079
|
+
var NOTIFICATION_MATCHER = Object.keys(NOTIFICATION_LABELS).join("|");
|
|
6967
7080
|
function str$1(v) {
|
|
6968
7081
|
return typeof v === "string" ? v.trim() : "";
|
|
6969
7082
|
}
|
|
@@ -6972,12 +7085,12 @@ function notificationType(p) {
|
|
|
6972
7085
|
return str$1(p.notification_type) || str$1(p.notificationType);
|
|
6973
7086
|
}
|
|
6974
7087
|
/** Read a Claude-format payload: last message on Stop, the CLI's own
|
|
6975
|
-
* notification line (or a label for its type) on Notification, and
|
|
6976
|
-
*
|
|
6977
|
-
* add their own quirks. */
|
|
7088
|
+
* notification line (or a label for its type) on Notification, and any
|
|
7089
|
+
* other notification dropped. Families that speak this dialect start
|
|
7090
|
+
* here and add their own quirks. */
|
|
6978
7091
|
function readClaudeStyle(p, reported) {
|
|
6979
7092
|
const kind = notificationType(p);
|
|
6980
|
-
if (
|
|
7093
|
+
if (kind && !NOTIFICATION_LABELS[kind]) return { ignore: true };
|
|
6981
7094
|
const reason = reported;
|
|
6982
7095
|
const text = str$1(p.last_assistant_message) || str$1(p["last-assistant-message"]) || str$1(p.lastAssistantMessage);
|
|
6983
7096
|
if (text) return {
|
|
@@ -6997,6 +7110,16 @@ function cleanSummary(text) {
|
|
|
6997
7110
|
if (!flat) return void 0;
|
|
6998
7111
|
return flat.length > 180 ? `${flat.slice(0, 177)}…` : flat;
|
|
6999
7112
|
}
|
|
7113
|
+
/** Write a file work keeps in its data dir for a CLI to load (a plugin,
|
|
7114
|
+
* an extension, a hook list), only when its content moved: each launch
|
|
7115
|
+
* asks, and a rewrite would bump the mtime a running CLI may watch. */
|
|
7116
|
+
function writeIfChanged(file, content) {
|
|
7117
|
+
try {
|
|
7118
|
+
if (fs.readFileSync(file, "utf8") === content) return;
|
|
7119
|
+
} catch {}
|
|
7120
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
7121
|
+
fs.writeFileSync(file, content);
|
|
7122
|
+
}
|
|
7000
7123
|
//#endregion
|
|
7001
7124
|
//#region src/server/usage/shared.ts
|
|
7002
7125
|
var HOME = os.homedir();
|
|
@@ -7231,11 +7354,16 @@ function numberedDirs() {
|
|
|
7231
7354
|
* macOS Claude Code keeps them in the login keychain instead (service
|
|
7232
7355
|
* "Claude Code-credentials"), which `security` reads back as the same JSON.
|
|
7233
7356
|
* The keychain read is cached per process tick of the usage refresh — it
|
|
7234
|
-
* forks a process — and only tried for the primary account.
|
|
7357
|
+
* forks a process — and only tried for the primary account. Read once per
|
|
7358
|
+
* account per refresh: a row asks for them half a dozen times, and every
|
|
7359
|
+
* refresh lists the accounts afresh. */
|
|
7360
|
+
var credentialsRead = /* @__PURE__ */ new WeakMap();
|
|
7235
7361
|
var credentials = (account) => {
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7362
|
+
if (credentialsRead.has(account)) return credentialsRead.get(account);
|
|
7363
|
+
let cred = readJson(path.join(account.dir, ".credentials.json"));
|
|
7364
|
+
if (!cred && process.platform === "darwin" && !account.suffix) cred = keychainCredentials();
|
|
7365
|
+
credentialsRead.set(account, cred);
|
|
7366
|
+
return cred;
|
|
7239
7367
|
};
|
|
7240
7368
|
var keychain = null;
|
|
7241
7369
|
function keychainCredentials() {
|
|
@@ -7273,7 +7401,7 @@ function credentialNote(account) {
|
|
|
7273
7401
|
if (expired(obj(cred.claudeAiOauth).expiresAt)) return "token expired — use this Claude account to refresh";
|
|
7274
7402
|
return "usage unavailable";
|
|
7275
7403
|
}
|
|
7276
|
-
function tokenExpired(account) {
|
|
7404
|
+
function tokenExpired$1(account) {
|
|
7277
7405
|
const cred = credentials(account);
|
|
7278
7406
|
return cred ? expired(obj(cred.claudeAiOauth).expiresAt) : false;
|
|
7279
7407
|
}
|
|
@@ -7297,6 +7425,38 @@ async function fetchUsage$3(account) {
|
|
|
7297
7425
|
Accept: "application/json"
|
|
7298
7426
|
} }));
|
|
7299
7427
|
}
|
|
7428
|
+
/** How often an account's plan is asked for: it changes on an upgrade,
|
|
7429
|
+
* not by the minute, so once an hour stands behind the usage refresh. */
|
|
7430
|
+
var PROFILE_TTL_MS = 36e5;
|
|
7431
|
+
var profiles = /* @__PURE__ */ new Map();
|
|
7432
|
+
/** The account's plan as Anthropic has it now. `.claude.json` holds what
|
|
7433
|
+
* Claude Code saw at sign-in, and it only asks again on a new login, so an
|
|
7434
|
+
* upgrade (Max 5x → 20x) kept reading as the old plan until then. Asked
|
|
7435
|
+
* with the same token as the usage; the cached plan stands in when it
|
|
7436
|
+
* cannot be. */
|
|
7437
|
+
async function livePlan(account) {
|
|
7438
|
+
const hit = profiles.get(account.dir);
|
|
7439
|
+
if (hit && Date.now() - hit.at < PROFILE_TTL_MS) return hit.plan;
|
|
7440
|
+
const token = str(obj(credentials(account)?.claudeAiOauth).accessToken);
|
|
7441
|
+
if (!token) return null;
|
|
7442
|
+
try {
|
|
7443
|
+
const org = obj(obj(await fetchJson("https://api.anthropic.com/api/oauth/profile", { headers: {
|
|
7444
|
+
Authorization: `Bearer ${token}`,
|
|
7445
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
7446
|
+
"User-Agent": "claude-cli",
|
|
7447
|
+
Accept: "application/json"
|
|
7448
|
+
} })).organization);
|
|
7449
|
+
const plan = str(org.rate_limit_tier || org.organization_type);
|
|
7450
|
+
if (!plan) return null;
|
|
7451
|
+
profiles.set(account.dir, {
|
|
7452
|
+
at: Date.now(),
|
|
7453
|
+
plan
|
|
7454
|
+
});
|
|
7455
|
+
return plan;
|
|
7456
|
+
} catch {
|
|
7457
|
+
return hit?.plan ?? null;
|
|
7458
|
+
}
|
|
7459
|
+
}
|
|
7300
7460
|
/** The CLI's own last-seen utilization, written into .claude.json. Older and
|
|
7301
7461
|
* coarser than the API, but it survives an expired token. */
|
|
7302
7462
|
function claudeJsonFallback(account) {
|
|
@@ -7368,8 +7528,9 @@ async function claudeRow(account) {
|
|
|
7368
7528
|
note: row.note ?? credentialNote(account)
|
|
7369
7529
|
};
|
|
7370
7530
|
}
|
|
7371
|
-
const lapsed = tokenExpired(account);
|
|
7372
|
-
const loaded = await loadPayload(`claude${account.suffix}`, () => lapsed ? Promise.reject(new FetchFailed("auth expired")) : fetchUsage$3(account));
|
|
7531
|
+
const lapsed = tokenExpired$1(account);
|
|
7532
|
+
const [loaded, plan] = await Promise.all([loadPayload(`claude${account.suffix}`, () => lapsed ? Promise.reject(new FetchFailed("auth expired")) : fetchUsage$3(account)), lapsed ? null : livePlan(account)]);
|
|
7533
|
+
if (plan) account.plan = plan;
|
|
7373
7534
|
const fallback = loaded.payload ? null : claudeJsonFallback(account);
|
|
7374
7535
|
const row = parseRow$4(account, loaded.payload ?? fallback, fallback ? "claude.json" : loaded.source);
|
|
7375
7536
|
if (lapsed) return {
|
|
@@ -7385,65 +7546,159 @@ var meta$6 = meta_default$6;
|
|
|
7385
7546
|
* like any other, but the turn has not ended, so no Stop or Notification
|
|
7386
7547
|
* says so — PreToolUse on this tool is the only signal, and PostToolUse
|
|
7387
7548
|
* (the answer arrived) clears it. */
|
|
7388
|
-
var QUESTION_TOOL = "AskUserQuestion";
|
|
7389
|
-
/**
|
|
7390
|
-
|
|
7549
|
+
var QUESTION_TOOL$1 = "AskUserQuestion";
|
|
7550
|
+
/** A teammate in an agent team stopped on a permission only the lead
|
|
7551
|
+
* session can grant: the user has to answer it there, so it is as much a
|
|
7552
|
+
* wait as the lead's own prompt. Claude's own notification type, beside
|
|
7553
|
+
* the shared ones. */
|
|
7554
|
+
var WORKER_PERMISSION = "worker_permission_prompt";
|
|
7555
|
+
/** How StopFailure's `error` reads on a phone. */
|
|
7556
|
+
var FAILURES = {
|
|
7557
|
+
authentication_failed: "Signed out",
|
|
7558
|
+
oauth_org_not_allowed: "Organization not allowed",
|
|
7559
|
+
account_on_hold: "Account on hold",
|
|
7560
|
+
verification_required: "Verification required",
|
|
7561
|
+
billing_error: "Billing problem",
|
|
7562
|
+
rate_limit: "Rate limited",
|
|
7563
|
+
overloaded: "Overloaded",
|
|
7564
|
+
invalid_request: "Request rejected",
|
|
7565
|
+
model_not_found: "Model not found",
|
|
7566
|
+
server_error: "Server error",
|
|
7567
|
+
max_output_tokens: "Hit the output limit",
|
|
7568
|
+
cloud_credential_error: "Cloud credentials failed"
|
|
7569
|
+
};
|
|
7570
|
+
/** Claude's dialect: subagents' tool calls left out, the question tool's
|
|
7571
|
+
* prompt, the command awaiting approval and a failed turn's error as the
|
|
7572
|
+
* summary, and a Stop that hands off to background subagents not counted
|
|
7573
|
+
* as the turn's end. */
|
|
7574
|
+
function readHook$6(p, reported, _state) {
|
|
7575
|
+
const event = p.hook_event_name;
|
|
7576
|
+
if ((event === "PreToolUse" || event === "PostToolUse" || event === "PostToolUseFailure") && str$1(p.agent_id)) return { ignore: true };
|
|
7577
|
+
if (event === "Stop" && runningSubagents(p)) return { ignore: true };
|
|
7578
|
+
if (event === "PostToolUseFailure" && p.is_interrupt === true) return { state: "idle" };
|
|
7579
|
+
if (notificationType(p) === WORKER_PERMISSION) return {
|
|
7580
|
+
reason: "attention",
|
|
7581
|
+
summary: cleanSummary(str$1(p.message)) ?? "A teammate needs permission"
|
|
7582
|
+
};
|
|
7583
|
+
if (event === "StopFailure") {
|
|
7584
|
+
const label = FAILURES[str$1(p.error)] ?? "The turn failed";
|
|
7585
|
+
const details = str$1(p.error_details);
|
|
7586
|
+
return {
|
|
7587
|
+
reason: "attention",
|
|
7588
|
+
summary: cleanSummary(details ? `${label}: ${details}` : label)
|
|
7589
|
+
};
|
|
7590
|
+
}
|
|
7391
7591
|
const read = readClaudeStyle(p, reported);
|
|
7392
7592
|
if (read.ignore) return read;
|
|
7393
|
-
if (
|
|
7593
|
+
if (event === "PreToolUse" && p.tool_name === QUESTION_TOOL$1) {
|
|
7394
7594
|
const input = p.tool_input;
|
|
7395
7595
|
const q = str$1(input?.questions?.[0]?.question);
|
|
7396
7596
|
read.summary = q ? cleanSummary(q) : "Asking a question";
|
|
7597
|
+
} else if (event === "PermissionRequest" && !read.summary) {
|
|
7598
|
+
const input = p.tool_input;
|
|
7599
|
+
const what = str$1(input?.description) || str$1(input?.command) || str$1(p.tool_name);
|
|
7600
|
+
read.summary = what ? cleanSummary(`Approve: ${what}`) : "Waiting for permission";
|
|
7397
7601
|
}
|
|
7398
7602
|
return read;
|
|
7399
7603
|
}
|
|
7604
|
+
/** Stop's `background_tasks` (in-flight work that will wake the session)
|
|
7605
|
+
* holding a subagent. A background shell (a dev server) is left out: it
|
|
7606
|
+
* may never finish, and the turn really has ended. */
|
|
7607
|
+
function runningSubagents(p) {
|
|
7608
|
+
const tasks = p.background_tasks;
|
|
7609
|
+
return Array.isArray(tasks) && tasks.some((t) => !!t && typeof t === "object" && t.type === "subagent");
|
|
7610
|
+
}
|
|
7400
7611
|
/** Claude Code takes its settings inline (`--settings` accepts a JSON
|
|
7401
7612
|
* string), so the hooks travel in argv and nothing is written anywhere.
|
|
7402
|
-
*
|
|
7403
|
-
*
|
|
7613
|
+
* PermissionRequest is the prompt itself, the moment it shows; the
|
|
7614
|
+
* permission Notification only comes after the user has left it for a
|
|
7615
|
+
* while (each keystroke defers it), but it is the one that covers a
|
|
7616
|
+
* sandbox's network prompt. A turn that fails (rate limit, signed out)
|
|
7617
|
+
* ends with StopFailure rather than Stop; an interrupt ends with no hook
|
|
7618
|
+
* at all, which the pane going still catches (scraper.ts). */
|
|
7404
7619
|
var claude = {
|
|
7405
7620
|
meta: meta$6,
|
|
7406
|
-
launchFlags: () => ["--settings", JSON.stringify({ hooks: claudeStyleHooks([
|
|
7621
|
+
launchFlags: () => ["--settings", JSON.stringify({ hooks: claudeStyleHooks("claude", [
|
|
7407
7622
|
["UserPromptSubmit", "working"],
|
|
7408
7623
|
[
|
|
7409
7624
|
"PreToolUse",
|
|
7410
7625
|
"working",
|
|
7411
7626
|
void 0,
|
|
7412
|
-
`^(?!${QUESTION_TOOL}$)`
|
|
7627
|
+
`^(?!${QUESTION_TOOL$1}$)`
|
|
7413
7628
|
],
|
|
7414
7629
|
[
|
|
7415
7630
|
"PreToolUse",
|
|
7416
7631
|
"waiting",
|
|
7417
7632
|
"attention",
|
|
7418
|
-
`^${QUESTION_TOOL}$`
|
|
7633
|
+
`^${QUESTION_TOOL$1}$`
|
|
7419
7634
|
],
|
|
7420
7635
|
["PostToolUse", "working"],
|
|
7636
|
+
["PostToolUseFailure", "working"],
|
|
7637
|
+
[
|
|
7638
|
+
"PermissionRequest",
|
|
7639
|
+
"waiting",
|
|
7640
|
+
"attention"
|
|
7641
|
+
],
|
|
7421
7642
|
[
|
|
7422
7643
|
"Stop",
|
|
7423
7644
|
"waiting",
|
|
7424
7645
|
"done"
|
|
7425
7646
|
],
|
|
7426
7647
|
[
|
|
7427
|
-
"
|
|
7648
|
+
"StopFailure",
|
|
7428
7649
|
"waiting",
|
|
7429
7650
|
"attention"
|
|
7651
|
+
],
|
|
7652
|
+
[
|
|
7653
|
+
"Notification",
|
|
7654
|
+
"waiting",
|
|
7655
|
+
"attention",
|
|
7656
|
+
`${NOTIFICATION_MATCHER}|${WORKER_PERMISSION}`
|
|
7430
7657
|
]
|
|
7431
7658
|
]) })],
|
|
7432
7659
|
reportsWorking: () => true,
|
|
7433
|
-
envPrefixes: [
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
"AI_AGENT"
|
|
7437
|
-
],
|
|
7438
|
-
readHook: readHook$4,
|
|
7660
|
+
envPrefixes: ["CLAUDE", "AI_AGENT"],
|
|
7661
|
+
startupPrompt: (screen) => /Is this a project you created or one you trust/.test(screen) ? ["Enter"] : void 0,
|
|
7662
|
+
readHook: readHook$6,
|
|
7439
7663
|
usage: () => Promise.all(claudeAccounts().map((account) => claudeRow(account)))
|
|
7440
7664
|
};
|
|
7441
7665
|
//#endregion
|
|
7442
7666
|
//#region src/agents/codex/usage.ts
|
|
7443
|
-
|
|
7667
|
+
/** Codex's home, where it keeps auth.json and config.toml: `CODEX_HOME`
|
|
7668
|
+
* when set, as the CLI reads it. */
|
|
7669
|
+
function codexHome() {
|
|
7670
|
+
return process.env.CODEX_HOME || path.join(HOME, ".codex");
|
|
7671
|
+
}
|
|
7672
|
+
var authFile$1 = () => path.join(codexHome(), "auth.json");
|
|
7673
|
+
/** Credentials Codex was told to keep in the system keyring
|
|
7674
|
+
* (`cli_auth_credentials_store = "keyring"`, or `"auto"` where one is
|
|
7675
|
+
* available) leave no auth.json to read: signed in, but nothing here can
|
|
7676
|
+
* ask on its behalf. */
|
|
7677
|
+
function keyringStore() {
|
|
7678
|
+
try {
|
|
7679
|
+
const top = fs.readFileSync(path.join(codexHome(), "config.toml"), "utf8").split(/^\s*\[/m)[0];
|
|
7680
|
+
const mode = /^\s*cli_auth_credentials_store\s*=\s*"(\w+)"/m.exec(top)?.[1];
|
|
7681
|
+
return mode === "keyring" || mode === "auto";
|
|
7682
|
+
} catch {
|
|
7683
|
+
return false;
|
|
7684
|
+
}
|
|
7685
|
+
}
|
|
7686
|
+
/** The access token's own `exp` (it is a JWT): whether a 401 or 429 is a
|
|
7687
|
+
* lapsed token rather than the provider throttling. Undefined when there
|
|
7688
|
+
* is no token or it does not decode. */
|
|
7689
|
+
function tokenExpired() {
|
|
7690
|
+
const payload = str(obj(readJson(authFile$1())?.tokens).access_token).split(".")[1];
|
|
7691
|
+
if (!payload) return void 0;
|
|
7692
|
+
try {
|
|
7693
|
+
const claims = obj(JSON.parse(Buffer.from(payload, "base64url").toString("utf8")));
|
|
7694
|
+
return claims.exp == null ? void 0 : expired(claims.exp);
|
|
7695
|
+
} catch {
|
|
7696
|
+
return;
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7444
7699
|
/** Live rate-limit windows from the endpoint the Codex CLI itself uses. */
|
|
7445
7700
|
async function fetchUsage$2() {
|
|
7446
|
-
const tokens = obj(readJson(
|
|
7701
|
+
const tokens = obj(readJson(authFile$1())?.tokens);
|
|
7447
7702
|
const token = str(tokens.access_token);
|
|
7448
7703
|
if (!token) return null;
|
|
7449
7704
|
return obj(await fetchJson("https://chatgpt.com/backend-api/wham/usage", { headers: {
|
|
@@ -7484,7 +7739,7 @@ function parseRow$3(payload, source) {
|
|
|
7484
7739
|
/** Signed in with an API key rather than a ChatGPT plan: pay-as-you-go,
|
|
7485
7740
|
* no rate-limit windows to draw. */
|
|
7486
7741
|
function apiKeyMode() {
|
|
7487
|
-
const auth = readJson(
|
|
7742
|
+
const auth = readJson(authFile$1());
|
|
7488
7743
|
return !!auth && (str(auth.auth_mode) === "apikey" || !obj(auth.tokens).access_token && !!auth.OPENAI_API_KEY);
|
|
7489
7744
|
}
|
|
7490
7745
|
async function codexRow() {
|
|
@@ -7494,19 +7749,21 @@ async function codexRow() {
|
|
|
7494
7749
|
note: API_KEY_NOTE
|
|
7495
7750
|
};
|
|
7496
7751
|
const { payload, source, reason } = await loadPayload("codex", fetchUsage$2);
|
|
7497
|
-
const missingAuth = fs.existsSync(
|
|
7752
|
+
const missingAuth = fs.existsSync(authFile$1()) ? void 0 : keyringStore() ? "credentials are in the system keyring — usage can't be read" : noAuthReason("codex");
|
|
7498
7753
|
return markStale(payload ? parseRow$3(payload, source) : {
|
|
7499
7754
|
...blankRow("codex", source),
|
|
7500
7755
|
note: missingAuth ?? "usage unavailable"
|
|
7501
|
-
}, "codex",
|
|
7756
|
+
}, "codex", tokenExpired(), missingAuth ?? reason);
|
|
7502
7757
|
}
|
|
7503
7758
|
//#endregion
|
|
7504
7759
|
//#region src/agents/codex/index.ts
|
|
7505
7760
|
var meta$5 = meta_default$5;
|
|
7506
7761
|
/** Codex hook events → state. PermissionRequest fires just before the
|
|
7507
7762
|
* approval prompt (PostToolUse clears it once the approved call has run,
|
|
7508
|
-
* or the next PreToolUse does if a reviewer auto-decided).
|
|
7509
|
-
|
|
7763
|
+
* or the next PreToolUse does if a reviewer auto-decided). A turn the
|
|
7764
|
+
* user interrupts, or a session that ends, has no Stop: Interrupt and
|
|
7765
|
+
* SessionEnd say the pane is back at its prompt (or gone). */
|
|
7766
|
+
var HOOKS$2 = claudeStyleHooks("codex", [
|
|
7510
7767
|
["UserPromptSubmit", "working"],
|
|
7511
7768
|
["PreToolUse", "working"],
|
|
7512
7769
|
["PostToolUse", "working"],
|
|
@@ -7519,23 +7776,37 @@ var HOOKS$2 = claudeStyleHooks([
|
|
|
7519
7776
|
"Stop",
|
|
7520
7777
|
"waiting",
|
|
7521
7778
|
"done"
|
|
7522
|
-
]
|
|
7779
|
+
],
|
|
7780
|
+
["Interrupt", "idle"],
|
|
7781
|
+
["SessionEnd", "idle"]
|
|
7523
7782
|
]);
|
|
7783
|
+
/** Codex runs these two with a short deadline (one second by default,
|
|
7784
|
+
* three at most) where every other event gets ten minutes; the report
|
|
7785
|
+
* needs a little over two at worst (stateCurl's timeouts). */
|
|
7786
|
+
var SHORT_HOOKS = {
|
|
7787
|
+
Interrupt: 3,
|
|
7788
|
+
SessionEnd: 3
|
|
7789
|
+
};
|
|
7790
|
+
/** The tool Codex asks the user a question with (Plan mode and wherever
|
|
7791
|
+
* it is enabled). It goes through PreToolUse like any tool, but the turn
|
|
7792
|
+
* is waiting on the user until its PostToolUse. */
|
|
7793
|
+
var QUESTION_TOOL = "request_user_input";
|
|
7524
7794
|
/** Codex only runs hooks it trusts. Trust is a hash of the normalized hook
|
|
7525
7795
|
* kept in config (`hooks.state.<key>.trusted_hash`), and both the hooks and
|
|
7526
7796
|
* that state are read from the `-c` override layer too — so the launch flags
|
|
7527
7797
|
* can carry their own trust without touching ~/.codex/config.toml.
|
|
7528
7798
|
* Mirrors codex-rs/hooks/src/engine/discovery.rs (hook_hash, hook_key,
|
|
7529
|
-
*
|
|
7530
|
-
* fields omitted) and
|
|
7531
|
-
*
|
|
7532
|
-
|
|
7799
|
+
* normalize_command_hook: timeout defaults to 600, or 1 clamped to 1..3
|
|
7800
|
+
* for SessionEnd and Interrupt; async false; None fields omitted) and
|
|
7801
|
+
* codex-rs/config/src/fingerprint.rs (sha256 of key-sorted JSON).
|
|
7802
|
+
* scripts/smoke.mjs pins it against a hash Codex wrote. */
|
|
7803
|
+
function codexHookTrustHash(event, command, timeout = 600) {
|
|
7533
7804
|
const identity = {
|
|
7534
7805
|
event_name: hookKeyLabel(event),
|
|
7535
7806
|
hooks: [{
|
|
7536
7807
|
async: false,
|
|
7537
7808
|
command,
|
|
7538
|
-
timeout
|
|
7809
|
+
timeout,
|
|
7539
7810
|
type: "command"
|
|
7540
7811
|
}]
|
|
7541
7812
|
};
|
|
@@ -7547,24 +7818,46 @@ function hookKeyLabel(event) {
|
|
|
7547
7818
|
}
|
|
7548
7819
|
/** Synthetic path Codex gives the `-c` override layer when keying hook state. */
|
|
7549
7820
|
var SESSION_FLAGS_PATH = "/<session-flags>/config.toml";
|
|
7550
|
-
/** `-c` overrides that install work's state hooks
|
|
7551
|
-
|
|
7821
|
+
/** `-c` overrides that install work's state hooks, trust them, and trust
|
|
7822
|
+
* the workspace folder for this session. Codex asks whether to trust a
|
|
7823
|
+
* folder it has not seen before it takes input, and every workspace is
|
|
7824
|
+
* one: the first prompt would land in that menu, and the Enter after it
|
|
7825
|
+
* would answer "trust" into the user's config.toml on their behalf. The
|
|
7826
|
+
* `-c` layer is this process's alone. Its keys split on every "." with no
|
|
7827
|
+
* quoting (codex-rs/config/src/overrides.rs), so a path goes in as the
|
|
7828
|
+
* key of an inline table, which is merged over the user's projects. */
|
|
7829
|
+
function launchFlags(ctx) {
|
|
7552
7830
|
const flags = [];
|
|
7553
7831
|
const state = [];
|
|
7554
7832
|
for (const [event, groups] of Object.entries(HOOKS$2)) {
|
|
7555
7833
|
const command = groups[0].hooks[0].command;
|
|
7556
|
-
|
|
7834
|
+
const timeout = SHORT_HOOKS[event];
|
|
7835
|
+
flags.push("-c", `hooks.${event}=[{hooks=[{type="command",command=${JSON.stringify(command)}${timeout ? `,timeout=${timeout}` : ""}}]}]`);
|
|
7557
7836
|
const key = `${SESSION_FLAGS_PATH}:${hookKeyLabel(event)}:0:0`;
|
|
7558
|
-
state.push(`${JSON.stringify(key)}={trusted_hash=${JSON.stringify(codexHookTrustHash(event, command))}}`);
|
|
7837
|
+
state.push(`${JSON.stringify(key)}={trusted_hash=${JSON.stringify(codexHookTrustHash(event, command, timeout))}}`);
|
|
7559
7838
|
}
|
|
7560
7839
|
flags.push("-c", `hooks.state={${state.join(",")}}`);
|
|
7840
|
+
const folders = /* @__PURE__ */ new Set([ctx.cwd]);
|
|
7841
|
+
try {
|
|
7842
|
+
folders.add(fs.realpathSync(ctx.cwd));
|
|
7843
|
+
} catch {}
|
|
7844
|
+
flags.push("-c", `projects={${[...folders].map((dir) => `${JSON.stringify(dir)}={trust_level="trusted"}`).join(",")}}`);
|
|
7561
7845
|
return flags;
|
|
7562
7846
|
}
|
|
7563
|
-
function readHook$
|
|
7847
|
+
function readHook$5(p, reported, _state) {
|
|
7848
|
+
const input = p.tool_input;
|
|
7849
|
+
if (p.hook_event_name === "PreToolUse" && p.tool_name === QUESTION_TOOL) {
|
|
7850
|
+
const questions = input?.questions;
|
|
7851
|
+
const q = str$1(questions?.[0]?.question);
|
|
7852
|
+
return {
|
|
7853
|
+
state: "waiting",
|
|
7854
|
+
reason: "attention",
|
|
7855
|
+
summary: q ? cleanSummary(q) : "Asking a question"
|
|
7856
|
+
};
|
|
7857
|
+
}
|
|
7564
7858
|
const read = readClaudeStyle(p, reported);
|
|
7565
7859
|
if (!read.summary && p.hook_event_name === "PermissionRequest") {
|
|
7566
|
-
const
|
|
7567
|
-
const what = str$1(input?.command) || str$1(p.tool_name);
|
|
7860
|
+
const what = str$1(input?.description) || str$1(input?.command) || str$1(p.tool_name);
|
|
7568
7861
|
read.summary = what ? cleanSummary(`Approve: ${what}`) : "Waiting for permission";
|
|
7569
7862
|
}
|
|
7570
7863
|
return read;
|
|
@@ -7574,18 +7867,21 @@ var codex = {
|
|
|
7574
7867
|
launchFlags,
|
|
7575
7868
|
reportsWorking: () => true,
|
|
7576
7869
|
envPrefixes: ["CODEX_"],
|
|
7577
|
-
readHook: readHook$
|
|
7870
|
+
readHook: readHook$5,
|
|
7578
7871
|
usage: () => codexRow().then((row) => [row])
|
|
7579
7872
|
};
|
|
7580
7873
|
//#endregion
|
|
7581
7874
|
//#region src/agents/copilot/usage.ts
|
|
7582
7875
|
var USER_API = "https://api.github.com/copilot_internal/user";
|
|
7583
7876
|
var HOSTS = path.join(process.env.XDG_CONFIG_HOME || path.join(HOME, ".config"), "gh", "hosts.yml");
|
|
7584
|
-
|
|
7585
|
-
|
|
7877
|
+
/** Copilot's own home (`COPILOT_HOME`, as the CLI reads it). */
|
|
7878
|
+
var sessionDb = () => path.join(process.env.COPILOT_HOME || path.join(HOME, ".copilot"), "session-store.db");
|
|
7879
|
+
/** github.com's active `oauth_token` straight out of gh's own hosts.yml.
|
|
7586
7880
|
*
|
|
7587
7881
|
* Deliberately not a YAML parse (no dependency): track the current top-level
|
|
7588
|
-
* host key by indentation and take the
|
|
7882
|
+
* host key by indentation and take the token that is a direct child of
|
|
7883
|
+
* github.com. With several accounts signed in, each also has one under
|
|
7884
|
+
* `users:`, listed first; the host's own is the active account's. */
|
|
7589
7885
|
function hostsToken() {
|
|
7590
7886
|
let text;
|
|
7591
7887
|
try {
|
|
@@ -7594,12 +7890,17 @@ function hostsToken() {
|
|
|
7594
7890
|
return null;
|
|
7595
7891
|
}
|
|
7596
7892
|
let host = "";
|
|
7893
|
+
let childIndent = -1;
|
|
7597
7894
|
for (const line of text.split("\n")) {
|
|
7598
7895
|
if (line[0]?.trim() && line.trimEnd().endsWith(":")) {
|
|
7599
7896
|
host = line.trim().replace(/:$/, "");
|
|
7897
|
+
childIndent = -1;
|
|
7600
7898
|
continue;
|
|
7601
7899
|
}
|
|
7602
|
-
if (host !== "github.com") continue;
|
|
7900
|
+
if (host !== "github.com" || !line.trim()) continue;
|
|
7901
|
+
const indent = line.length - line.trimStart().length;
|
|
7902
|
+
if (childIndent < 0) childIndent = indent;
|
|
7903
|
+
if (indent !== childIndent) continue;
|
|
7603
7904
|
const match = /^\s+oauth_token:\s*(\S+)/.exec(line);
|
|
7604
7905
|
if (match) return match[1] ?? null;
|
|
7605
7906
|
}
|
|
@@ -7609,7 +7910,11 @@ function hostsToken() {
|
|
|
7609
7910
|
* gh manages. Copilot entitlement is account-scoped, so any valid token for
|
|
7610
7911
|
* the entitled user reads the quota. */
|
|
7611
7912
|
async function token() {
|
|
7612
|
-
for (const key of [
|
|
7913
|
+
for (const key of [
|
|
7914
|
+
"COPILOT_GITHUB_TOKEN",
|
|
7915
|
+
"GH_TOKEN",
|
|
7916
|
+
"GITHUB_TOKEN"
|
|
7917
|
+
]) {
|
|
7613
7918
|
const value = process.env[key]?.trim();
|
|
7614
7919
|
if (value) return value;
|
|
7615
7920
|
}
|
|
@@ -7647,9 +7952,10 @@ function periodStart(resetIso) {
|
|
|
7647
7952
|
* only, which is why the caller floors it with upstream rather than trusting
|
|
7648
7953
|
* it alone. */
|
|
7649
7954
|
function localCredits(start) {
|
|
7650
|
-
|
|
7955
|
+
const file = sessionDb();
|
|
7956
|
+
if (!start || !fs.existsSync(file)) return null;
|
|
7651
7957
|
try {
|
|
7652
|
-
const db = new DatabaseSync(
|
|
7958
|
+
const db = new DatabaseSync(file, { readOnly: true });
|
|
7653
7959
|
try {
|
|
7654
7960
|
const nano = num(db.prepare("select sum(total_nano_aiu) as nano from assistant_usage_events where created_at >= ?").get(start)?.nano);
|
|
7655
7961
|
return nano == null ? 0 : nano / 1e9;
|
|
@@ -7712,14 +8018,25 @@ var meta$4 = meta_default$4;
|
|
|
7712
8018
|
/** Copilot hook events (Claude-format names, which make it emit Claude's
|
|
7713
8019
|
* snake_case payloads) → state. Notification rather than PermissionRequest
|
|
7714
8020
|
* for "needs input": the latter fires before Copilot's own allow rules, the
|
|
7715
|
-
* former only once a prompt is actually on screen.
|
|
7716
|
-
|
|
8021
|
+
* former only once a prompt is actually on screen. Its matcher keeps out
|
|
8022
|
+
* the rest of what Copilot notifies about (a background shell or subagent
|
|
8023
|
+
* finishing, an inbox message), none of which is the user being needed.
|
|
8024
|
+
* A tool that fails has PostToolUseFailure instead of PostToolUse; an
|
|
8025
|
+
* error the turn cannot recover from ends it with ErrorOccurred. */
|
|
8026
|
+
var HOOKS$1 = claudeStyleHooks("copilot", [
|
|
7717
8027
|
["UserPromptSubmit", "working"],
|
|
7718
8028
|
["PreToolUse", "working"],
|
|
7719
8029
|
["PostToolUse", "working"],
|
|
8030
|
+
["PostToolUseFailure", "working"],
|
|
7720
8031
|
[
|
|
7721
8032
|
"Notification",
|
|
7722
8033
|
"waiting",
|
|
8034
|
+
"attention",
|
|
8035
|
+
NOTIFICATION_MATCHER
|
|
8036
|
+
],
|
|
8037
|
+
[
|
|
8038
|
+
"ErrorOccurred",
|
|
8039
|
+
"waiting",
|
|
7723
8040
|
"attention"
|
|
7724
8041
|
],
|
|
7725
8042
|
[
|
|
@@ -7733,41 +8050,36 @@ var HOOKS$1 = claudeStyleHooks([
|
|
|
7733
8050
|
* prompt. The plugin is two files kept under work's data dir and rewritten
|
|
7734
8051
|
* only when their content changes (a stale hook would report to the wrong
|
|
7735
8052
|
* port after a config change). */
|
|
7736
|
-
function pluginDir() {
|
|
8053
|
+
function pluginDir$1() {
|
|
7737
8054
|
const dir = path.join(config.dataDir, "copilot-plugin");
|
|
7738
8055
|
writeIfChanged(path.join(dir, ".claude-plugin", "plugin.json"), JSON.stringify({
|
|
7739
8056
|
name: "work-session-state",
|
|
7740
8057
|
version: "1",
|
|
7741
8058
|
description: "Reports this session's working / needs-input / done state to work."
|
|
7742
8059
|
}, null, 2));
|
|
7743
|
-
writeIfChanged(path.join(dir, "hooks", "hooks.json"), JSON.stringify({
|
|
8060
|
+
writeIfChanged(path.join(dir, "hooks", "hooks.json"), JSON.stringify({
|
|
8061
|
+
version: 1,
|
|
8062
|
+
hooks: HOOKS$1
|
|
8063
|
+
}, null, 2));
|
|
7744
8064
|
return dir;
|
|
7745
8065
|
}
|
|
7746
|
-
function
|
|
7747
|
-
|
|
7748
|
-
if (
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
var CHATTER = /* @__PURE__ */ new Set([
|
|
7756
|
-
"shell_completed",
|
|
7757
|
-
"shell_detached_completed",
|
|
7758
|
-
"new_inbox_message",
|
|
7759
|
-
"instruction_discovered",
|
|
7760
|
-
"factory_completed",
|
|
7761
|
-
"unclassified"
|
|
7762
|
-
]);
|
|
7763
|
-
function readHook$2(p, reported) {
|
|
7764
|
-
if (CHATTER.has(notificationType(p))) return { ignore: true };
|
|
8066
|
+
function readHook$4(p, reported, _state) {
|
|
8067
|
+
if (p.hook_event_name === "ErrorOccurred") {
|
|
8068
|
+
if (p.recoverable !== false) return { ignore: true };
|
|
8069
|
+
const error = p.error;
|
|
8070
|
+
return {
|
|
8071
|
+
reason: "attention",
|
|
8072
|
+
summary: cleanSummary(str$1(error?.message)) ?? "Stopped on an error"
|
|
8073
|
+
};
|
|
8074
|
+
}
|
|
7765
8075
|
const read = readClaudeStyle(p, reported);
|
|
7766
8076
|
if (!read.summary && reported === "done") read.summary = lastMessageFromTranscript(p);
|
|
7767
8077
|
return read;
|
|
7768
8078
|
}
|
|
7769
|
-
/** The last non-empty `assistant.message`
|
|
7770
|
-
*
|
|
8079
|
+
/** The last non-empty `assistant.message` of the turn that just ended in
|
|
8080
|
+
* Copilot's events.jsonl — none rather than an earlier turn's, so the scan
|
|
8081
|
+
* stops at the prompt that started it. Reads the tail only — transcripts
|
|
8082
|
+
* grow for the life of a session. */
|
|
7771
8083
|
function lastMessageFromTranscript(p) {
|
|
7772
8084
|
const file = p.transcript_path;
|
|
7773
8085
|
if (typeof file !== "string" || !file.endsWith("events.jsonl")) return void 0;
|
|
@@ -7780,6 +8092,7 @@ function lastMessageFromTranscript(p) {
|
|
|
7780
8092
|
fs.readSync(fd, buf, 0, len, size - len);
|
|
7781
8093
|
const lines = buf.toString("utf8").split("\n");
|
|
7782
8094
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
8095
|
+
if (lines[i].includes("\"user.message\"")) break;
|
|
7783
8096
|
if (!lines[i].includes("\"assistant.message\"")) continue;
|
|
7784
8097
|
try {
|
|
7785
8098
|
const ev = JSON.parse(lines[i]);
|
|
@@ -7803,31 +8116,41 @@ function startupPrompt(screen) {
|
|
|
7803
8116
|
}
|
|
7804
8117
|
var copilot = {
|
|
7805
8118
|
meta: meta$4,
|
|
7806
|
-
launchFlags: () => ["--plugin-dir", pluginDir()],
|
|
8119
|
+
launchFlags: () => ["--plugin-dir", pluginDir$1()],
|
|
7807
8120
|
startupPrompt,
|
|
7808
8121
|
reportsWorking: () => true,
|
|
7809
8122
|
envPrefixes: ["COPILOT_"],
|
|
7810
|
-
readHook: readHook$
|
|
8123
|
+
readHook: readHook$4,
|
|
7811
8124
|
usage: () => copilotRow().then((row) => [row])
|
|
7812
8125
|
};
|
|
7813
8126
|
//#endregion
|
|
7814
8127
|
//#region src/agents/cursor/usage.ts
|
|
7815
|
-
var AUTH$1 = path.join(HOME, ".config", "cursor", "auth.json");
|
|
7816
8128
|
var CLI_CONFIG = path.join(HOME, ".cursor", "cli-config.json");
|
|
8129
|
+
/** Where the CLI keeps its auth, as it looks it up itself: `~/.cursor` on a
|
|
8130
|
+
* Mac, the XDG config dir elsewhere. */
|
|
8131
|
+
function authFile() {
|
|
8132
|
+
if (process.platform === "darwin") return path.join(HOME, ".cursor", "auth.json");
|
|
8133
|
+
const config = process.env.XDG_CONFIG_HOME || path.join(HOME, ".config");
|
|
8134
|
+
return path.join(config, "cursor", "auth.json");
|
|
8135
|
+
}
|
|
7817
8136
|
var API = "https://api2.cursor.sh";
|
|
7818
8137
|
var WWW = "https://cursor.com";
|
|
7819
8138
|
function cursorAuth() {
|
|
7820
|
-
const auth = readJson(
|
|
8139
|
+
const auth = readJson(authFile());
|
|
7821
8140
|
if (!auth) return null;
|
|
7822
8141
|
const token = str(auth.accessToken || auth.access_token);
|
|
7823
8142
|
if (!token) return null;
|
|
7824
8143
|
let sub = "";
|
|
8144
|
+
let exp;
|
|
7825
8145
|
try {
|
|
7826
|
-
|
|
8146
|
+
const claims = obj(JSON.parse(Buffer.from(str(token.split(".")[1]), "base64url").toString()));
|
|
8147
|
+
sub = str(claims.sub);
|
|
8148
|
+
exp = claims.exp;
|
|
7827
8149
|
} catch {}
|
|
7828
8150
|
return {
|
|
7829
8151
|
token,
|
|
7830
8152
|
sub,
|
|
8153
|
+
exp,
|
|
7831
8154
|
email: str(obj(readJson(CLI_CONFIG)?.authInfo).email).trim()
|
|
7832
8155
|
};
|
|
7833
8156
|
}
|
|
@@ -7846,7 +8169,8 @@ var dashboardPost = async (token, method) => obj(await fetchJson(`${API}/aiserve
|
|
|
7846
8169
|
* it is only readable with the web session cookie. */
|
|
7847
8170
|
async function usageSummary(auth) {
|
|
7848
8171
|
if (!auth.sub) return null;
|
|
7849
|
-
const
|
|
8172
|
+
const user = auth.sub.split("|").pop();
|
|
8173
|
+
const cookie = `WorkosCursorSessionToken=${encodeURIComponent(`${user}::${auth.token}`)}`;
|
|
7850
8174
|
return obj(await fetchJson(`${WWW}/api/usage-summary`, { headers: {
|
|
7851
8175
|
Cookie: cookie,
|
|
7852
8176
|
Accept: "application/json",
|
|
@@ -7915,14 +8239,58 @@ async function cursorRow() {
|
|
|
7915
8239
|
return markStale(payload ? parseRow$1(payload, auth, source) : {
|
|
7916
8240
|
...blankRow("cursor", source),
|
|
7917
8241
|
note: missingAuth ?? "usage unavailable"
|
|
7918
|
-
}, "cursor", void 0, missingAuth ?? reason);
|
|
8242
|
+
}, "cursor", auth?.exp != null ? expired(auth.exp) : void 0, missingAuth ?? reason);
|
|
8243
|
+
}
|
|
8244
|
+
//#endregion
|
|
8245
|
+
//#region src/agents/cursor/index.ts
|
|
8246
|
+
var meta$3 = meta_default$3;
|
|
8247
|
+
/** Cursor has no hook for a permission prompt or a question, so the pane
|
|
8248
|
+
* scraper (scraper.ts) still says when a session is working and when it
|
|
8249
|
+
* has gone quiet — a stopped turn and a question look the same there. What
|
|
8250
|
+
* a hook adds is the end of a turn and how it ended, which is what a push
|
|
8251
|
+
* needs: one `stop` hook and nothing else. A hook that reported "working"
|
|
8252
|
+
* would outrank the scraper and hold a session stopped on a prompt at
|
|
8253
|
+
* "working" for good, since nothing reports the prompt.
|
|
8254
|
+
*
|
|
8255
|
+
* The hook comes from a plugin in work's data dir named with `--plugin-dir`
|
|
8256
|
+
* (the CLI runs plugin hooks for every step, those of a `--plugin-dir`
|
|
8257
|
+
* plugin included), never ~/.cursor/hooks.json — the user's, which Grok
|
|
8258
|
+
* also loads. Cursor hands the payload to a command as a heredoc after it,
|
|
8259
|
+
* so the report is a brace group: the heredoc is its stdin, not `exit`'s. */
|
|
8260
|
+
function pluginDir() {
|
|
8261
|
+
const dir = path.join(config.dataDir, "cursor-plugin");
|
|
8262
|
+
writeIfChanged(path.join(dir, ".cursor-plugin", "plugin.json"), JSON.stringify({
|
|
8263
|
+
name: "work-session-state",
|
|
8264
|
+
version: "1",
|
|
8265
|
+
description: "Reports when this session's turn ends to work."
|
|
8266
|
+
}, null, 2));
|
|
8267
|
+
writeIfChanged(path.join(dir, "hooks", "hooks.json"), JSON.stringify({
|
|
8268
|
+
version: 1,
|
|
8269
|
+
hooks: { stop: [{
|
|
8270
|
+
command: `{ ${stateCurl("cursor", "waiting", "done")}; }`,
|
|
8271
|
+
timeout: 5
|
|
8272
|
+
}] }
|
|
8273
|
+
}, null, 2));
|
|
8274
|
+
return dir;
|
|
8275
|
+
}
|
|
8276
|
+
/** `stop` carries how the turn ended: finished, interrupted (back at the
|
|
8277
|
+
* prompt, nothing to push) or failed (worth reading). */
|
|
8278
|
+
function readHook$3(p, reported) {
|
|
8279
|
+
if (p.status === "aborted") return { state: "idle" };
|
|
8280
|
+
if (p.status === "error") return {
|
|
8281
|
+
reason: "attention",
|
|
8282
|
+
summary: "The turn failed"
|
|
8283
|
+
};
|
|
8284
|
+
return { reason: reported };
|
|
7919
8285
|
}
|
|
7920
|
-
/** Cursor's plugin-dir hooks can't carry stop/prompt events (those load only
|
|
7921
|
-
* from ~/.cursor/hooks.json) and it has no permission-prompt hook at all,
|
|
7922
|
-
* so its sessions get working/idle from pane activity (see scraper.ts) and
|
|
7923
|
-
* nothing else — a stopped turn and a question look the same. */
|
|
7924
8286
|
var cursor = {
|
|
7925
|
-
meta:
|
|
8287
|
+
meta: meta$3,
|
|
8288
|
+
launchFlags: () => [
|
|
8289
|
+
"--trust",
|
|
8290
|
+
"--plugin-dir",
|
|
8291
|
+
pluginDir()
|
|
8292
|
+
],
|
|
8293
|
+
readHook: readHook$3,
|
|
7926
8294
|
readyScreen: /Plan, search, build anything|Add a follow-up/,
|
|
7927
8295
|
usage: () => cursorRow().then((row) => [row])
|
|
7928
8296
|
};
|
|
@@ -7985,7 +8353,8 @@ function billingFromLogs() {
|
|
|
7985
8353
|
if (!Object.keys(cfg).length) return null;
|
|
7986
8354
|
return {
|
|
7987
8355
|
config: cfg,
|
|
7988
|
-
subscriptionTier: ctx.subscriptionTier
|
|
8356
|
+
subscriptionTier: ctx.subscriptionTier,
|
|
8357
|
+
loggedAt: Date.parse(str(latest.ts)) || null
|
|
7989
8358
|
};
|
|
7990
8359
|
}
|
|
7991
8360
|
function parseRow(payload, auth, source) {
|
|
@@ -8019,7 +8388,8 @@ async function grokRow() {
|
|
|
8019
8388
|
const loaded = await loadPayload("grok", () => auth?.token ? fetchBilling(auth.token) : Promise.resolve(null));
|
|
8020
8389
|
const fromLog = loaded.payload ? null : billingFromLogs();
|
|
8021
8390
|
const payload = loaded.payload ?? fromLog;
|
|
8022
|
-
const
|
|
8391
|
+
const loggedAt = num(fromLog?.loggedAt);
|
|
8392
|
+
const source = fromLog ? loggedAt ? `stale-cache ${Math.max(0, Math.round((Date.now() - loggedAt) / 1e3))}s` : "stale-cache" : loaded.source;
|
|
8023
8393
|
const missingAuth = auth?.token ? void 0 : noAuthReason("grok");
|
|
8024
8394
|
return markStale(payload ? parseRow(payload, auth, source) : {
|
|
8025
8395
|
...blankRow("grok", source),
|
|
@@ -8029,14 +8399,25 @@ async function grokRow() {
|
|
|
8029
8399
|
//#endregion
|
|
8030
8400
|
//#region src/agents/grok/index.ts
|
|
8031
8401
|
var meta$2 = meta_default$2;
|
|
8032
|
-
/** Grok hook events → state
|
|
8033
|
-
*
|
|
8034
|
-
*
|
|
8035
|
-
*
|
|
8036
|
-
*
|
|
8037
|
-
|
|
8402
|
+
/** Grok hook events → state: the busy/idle indicator its hook guide spells
|
|
8403
|
+
* out, five registrations and two more. UserPromptSubmit starts a turn;
|
|
8404
|
+
* Stop, StopFailure and StopCancelled end it however it ended (readHook
|
|
8405
|
+
* tells an interrupt from a cut-off); the `idle_prompt` Notification is
|
|
8406
|
+
* the backstop for the turns that report none of the three, and
|
|
8407
|
+
* SessionEnd for a session that exits first. A tool call is not "working"
|
|
8408
|
+
* of itself — Grok's matchers are Rust regexes with no way to except one
|
|
8409
|
+
* tool — so PreToolUse only catches the question card, which stops the
|
|
8410
|
+
* turn on the user, and PostToolUse (the question answered, or any tool
|
|
8411
|
+
* done) puts it back to work. Payloads are camelCase (hookEventName,
|
|
8412
|
+
* notificationType, lastAssistantMessage). */
|
|
8413
|
+
var HOOKS = claudeStyleHooks("grok", [
|
|
8038
8414
|
["UserPromptSubmit", "working"],
|
|
8039
|
-
[
|
|
8415
|
+
[
|
|
8416
|
+
"PreToolUse",
|
|
8417
|
+
"waiting",
|
|
8418
|
+
"attention",
|
|
8419
|
+
"^ask_user_question$"
|
|
8420
|
+
],
|
|
8040
8421
|
["PostToolUse", "working"],
|
|
8041
8422
|
[
|
|
8042
8423
|
"Notification",
|
|
@@ -8045,23 +8426,49 @@ var HOOKS = claudeStyleHooks([
|
|
|
8045
8426
|
"permission_prompt|elicitation_dialog"
|
|
8046
8427
|
],
|
|
8047
8428
|
[
|
|
8048
|
-
"
|
|
8049
|
-
"
|
|
8050
|
-
|
|
8429
|
+
"Notification",
|
|
8430
|
+
"idle",
|
|
8431
|
+
void 0,
|
|
8432
|
+
"^idle_prompt$"
|
|
8051
8433
|
],
|
|
8052
8434
|
[
|
|
8053
|
-
"
|
|
8435
|
+
"Stop",
|
|
8054
8436
|
"waiting",
|
|
8055
8437
|
"done"
|
|
8056
8438
|
],
|
|
8439
|
+
["StopCancelled", "idle"],
|
|
8057
8440
|
[
|
|
8058
8441
|
"StopFailure",
|
|
8059
8442
|
"waiting",
|
|
8060
8443
|
"attention"
|
|
8061
|
-
]
|
|
8444
|
+
],
|
|
8445
|
+
["SessionEnd", "idle"]
|
|
8062
8446
|
], { timeout: 5 });
|
|
8063
|
-
|
|
8064
|
-
var
|
|
8447
|
+
/** Where Grok keeps its config, the registry among it. */
|
|
8448
|
+
var GROK_HOME = () => process.env.GROK_HOME || path.join(os.homedir(), ".grok");
|
|
8449
|
+
var REGISTRY = () => path.join(GROK_HOME(), "hooks-paths");
|
|
8450
|
+
/** work's hook file, by its real path: Grok's sandbox refuses to start
|
|
8451
|
+
* while a registry line runs through a symlink. */
|
|
8452
|
+
function hookFile() {
|
|
8453
|
+
let dir = config.dataDir;
|
|
8454
|
+
try {
|
|
8455
|
+
dir = fs.realpathSync(dir);
|
|
8456
|
+
} catch {}
|
|
8457
|
+
return path.join(dir, "grok-hooks.json");
|
|
8458
|
+
}
|
|
8459
|
+
/** A data dir under the system temp dir is a scratch server's: naming its
|
|
8460
|
+
* hook file in the user's registry would outlive it, and a registry line
|
|
8461
|
+
* whose file is gone keeps Grok's sandbox from starting at all. */
|
|
8462
|
+
function scratch() {
|
|
8463
|
+
const tmp = (() => {
|
|
8464
|
+
try {
|
|
8465
|
+
return fs.realpathSync(os.tmpdir());
|
|
8466
|
+
} catch {
|
|
8467
|
+
return os.tmpdir();
|
|
8468
|
+
}
|
|
8469
|
+
})();
|
|
8470
|
+
return hookFile().startsWith(tmp + path.sep);
|
|
8471
|
+
}
|
|
8065
8472
|
/** Whether Grok's hook registry names work's hook file — without it Grok
|
|
8066
8473
|
* reports nothing and the screen has to speak for it. Read from disk, not
|
|
8067
8474
|
* remembered from a launch: a session that outlives the app process still
|
|
@@ -8069,20 +8476,24 @@ var REGISTRY = () => path.join(process.env.HOME || "", ".grok", "hooks-paths");
|
|
|
8069
8476
|
* back on a session that reports for itself. */
|
|
8070
8477
|
function registered() {
|
|
8071
8478
|
try {
|
|
8072
|
-
const file =
|
|
8479
|
+
const file = hookFile();
|
|
8073
8480
|
return fs.readFileSync(REGISTRY(), "utf8").split("\n").some((line) => line.trim() === file);
|
|
8074
8481
|
} catch {
|
|
8075
8482
|
return false;
|
|
8076
8483
|
}
|
|
8077
8484
|
}
|
|
8078
|
-
/** Grok has no flag for hooks. It discovers them from
|
|
8485
|
+
/** Grok has no flag for hooks. It discovers them from `$GROK_HOME/hooks-paths`:
|
|
8079
8486
|
* one absolute path per line, the registry its own `/hooks-add` command
|
|
8080
8487
|
* appends to. work's hook file is kept in the data dir — the commands read
|
|
8081
8488
|
* the port from env, so the file never needs rewriting — and named there
|
|
8082
8489
|
* once. The one line in that registry is the only thing work writes
|
|
8083
|
-
* outside its own dir for any agent
|
|
8490
|
+
* outside its own dir for any agent; lines naming a work hook file that is
|
|
8491
|
+
* gone (a data dir moved or removed) are dropped as it is written, since
|
|
8492
|
+
* Grok's sandbox will not start while one is there, and a stale one on
|
|
8493
|
+
* another port reported every event twice. */
|
|
8084
8494
|
function registerHooks() {
|
|
8085
|
-
|
|
8495
|
+
if (scratch()) return;
|
|
8496
|
+
const file = hookFile();
|
|
8086
8497
|
writeIfChanged(file, JSON.stringify({ hooks: HOOKS }, null, 2));
|
|
8087
8498
|
const registry = REGISTRY();
|
|
8088
8499
|
try {
|
|
@@ -8090,17 +8501,74 @@ function registerHooks() {
|
|
|
8090
8501
|
try {
|
|
8091
8502
|
current = fs.readFileSync(registry, "utf8");
|
|
8092
8503
|
} catch {}
|
|
8093
|
-
|
|
8504
|
+
const lines = current.split("\n").filter((line) => {
|
|
8505
|
+
const target = line.trim();
|
|
8506
|
+
return target === file || !target.endsWith(`${path.sep}grok-hooks.json`) || fs.existsSync(target);
|
|
8507
|
+
});
|
|
8508
|
+
if (!lines.some((line) => line.trim() === file)) {
|
|
8509
|
+
while (lines.length && !lines.at(-1).trim()) lines.pop();
|
|
8510
|
+
lines.push(file, "");
|
|
8511
|
+
}
|
|
8512
|
+
const next = lines.join("\n");
|
|
8513
|
+
if (next !== current) {
|
|
8094
8514
|
fs.mkdirSync(path.dirname(registry), { recursive: true });
|
|
8095
|
-
fs.
|
|
8515
|
+
fs.writeFileSync(registry, next);
|
|
8096
8516
|
}
|
|
8097
8517
|
} catch {}
|
|
8098
8518
|
}
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8519
|
+
/** The turns each Grok session has started, oldest first, so a report
|
|
8520
|
+
* for a turn that has since been replaced is known for what it is: a
|
|
8521
|
+
* cancelled turn's report is sent off Grok's command loop, and can land
|
|
8522
|
+
* after the next turn's UserPromptSubmit. */
|
|
8523
|
+
var TURNS_KEPT = 32;
|
|
8524
|
+
var turns = /* @__PURE__ */ new Map();
|
|
8525
|
+
function noteTurn(session, prompt) {
|
|
8526
|
+
let list = turns.get(session);
|
|
8527
|
+
if (!list) {
|
|
8528
|
+
turns.set(session, list = []);
|
|
8529
|
+
if (turns.size > 200) turns.delete(turns.keys().next().value);
|
|
8530
|
+
}
|
|
8531
|
+
list.push(prompt);
|
|
8532
|
+
if (list.length > TURNS_KEPT) list.shift();
|
|
8533
|
+
}
|
|
8534
|
+
/** A turn-end report for a turn the session has moved on from. One for a
|
|
8535
|
+
* turn it never saw start (an interrupted bash-mode command) is not: it
|
|
8536
|
+
* settles like any other. */
|
|
8537
|
+
function superseded(session, prompt) {
|
|
8538
|
+
const list = turns.get(session);
|
|
8539
|
+
if (!list) return false;
|
|
8540
|
+
const at = list.indexOf(prompt);
|
|
8541
|
+
return at >= 0 && at < list.length - 1;
|
|
8542
|
+
}
|
|
8543
|
+
/** StopCancelled reasons the agent chose rather than the user: cut off,
|
|
8544
|
+
* the session is waiting on them to say what next. Anything else — an
|
|
8545
|
+
* interrupt, a declined or dismissed prompt, a reason Grok adds later —
|
|
8546
|
+
* is the user having stopped it. */
|
|
8547
|
+
var RUNTIME_STOPS = /* @__PURE__ */ new Set(["max_turns", "no_progress"]);
|
|
8548
|
+
function readHook$2(p, reported, state) {
|
|
8549
|
+
const event = str$1(p.hook_event_name);
|
|
8550
|
+
const session = str$1(p.sessionId);
|
|
8551
|
+
const prompt = str$1(p.promptId);
|
|
8552
|
+
if (str$1(p.subagentType) && notificationType(p) !== "permission_prompt") return { ignore: true };
|
|
8553
|
+
if (event === "UserPromptSubmit" && session && prompt) noteTurn(session, prompt);
|
|
8554
|
+
if ((event === "Stop" || event === "StopCancelled" || event === "StopFailure") && session && prompt && superseded(session, prompt)) return { ignore: true };
|
|
8555
|
+
if (event === "Stop" && str$1(p.reason) && p.reason !== "end_turn") return { state: "idle" };
|
|
8556
|
+
if (event === "Stop" && Array.isArray(p.backgroundTasks) && p.backgroundTasks.some((task) => task?.type === "subagent")) return { ignore: true };
|
|
8557
|
+
if (event === "StopCancelled") {
|
|
8558
|
+
if (!RUNTIME_STOPS.has(str$1(p.reason))) return { state: "idle" };
|
|
8559
|
+
return {
|
|
8560
|
+
state: "waiting",
|
|
8561
|
+
reason: "attention",
|
|
8562
|
+
summary: cleanSummary(str$1(p.reasonDetails) || str$1(p.lastAssistantMessage) || (p.reason === "max_turns" ? "Stopped at the turn limit" : "Stopped making progress"))
|
|
8563
|
+
};
|
|
8564
|
+
}
|
|
8565
|
+
if (state === "idle") return { state: "idle" };
|
|
8566
|
+
if (event === "PreToolUse") return {
|
|
8567
|
+
reason: "attention",
|
|
8568
|
+
summary: cleanSummary(str$1((p.toolInput ?? {}).questions?.[0]?.question)) ?? "Asking a question"
|
|
8569
|
+
};
|
|
8102
8570
|
const read = readClaudeStyle(p, reported);
|
|
8103
|
-
if (!read.summary) {
|
|
8571
|
+
if (!read.ignore && !read.summary) {
|
|
8104
8572
|
const err = str$1(p.errorDetails) || str$1(p.error);
|
|
8105
8573
|
if (err) read.summary = cleanSummary(err);
|
|
8106
8574
|
}
|
|
@@ -8115,7 +8583,7 @@ var grok = {
|
|
|
8115
8583
|
reportsWorking: registered,
|
|
8116
8584
|
promptAsArgv: true,
|
|
8117
8585
|
envPrefixes: ["GROK_"],
|
|
8118
|
-
readHook: readHook$
|
|
8586
|
+
readHook: readHook$2,
|
|
8119
8587
|
usage: () => grokRow().then((row) => [row])
|
|
8120
8588
|
};
|
|
8121
8589
|
//#endregion
|
|
@@ -8133,7 +8601,7 @@ const post = async (state, reason, summary) => {
|
|
|
8133
8601
|
if (!session) return;
|
|
8134
8602
|
const port = process.env.WORK_APP_PORT || "${appPort}";
|
|
8135
8603
|
const qs =
|
|
8136
|
-
\`session=\${encodeURIComponent(session)}&state=\${state}\` +
|
|
8604
|
+
\`session=\${encodeURIComponent(session)}&agent=opencode&state=\${state}\` +
|
|
8137
8605
|
(reason ? \`&reason=\${reason}\` : "");
|
|
8138
8606
|
try {
|
|
8139
8607
|
await fetch(\`http://127.0.0.1:\${port}/api/agent-state?\${qs}\`, {
|
|
@@ -8145,19 +8613,51 @@ const post = async (state, reason, summary) => {
|
|
|
8145
8613
|
} catch {}
|
|
8146
8614
|
};
|
|
8147
8615
|
|
|
8148
|
-
export const WorkSessionState = async () => {
|
|
8616
|
+
export const WorkSessionState = async ({ client } = {}) => {
|
|
8149
8617
|
// A subagent's turn is not the pane's: task sessions carry a parentID,
|
|
8150
8618
|
// and their busy/idle would otherwise end the turn the user is watching.
|
|
8619
|
+
// One this process never saw created (a resumed session's subagent,
|
|
8620
|
+
// restarted by task id) is looked up once.
|
|
8151
8621
|
const children = new Set();
|
|
8622
|
+
const roots = new Set();
|
|
8152
8623
|
const roles = new Map();
|
|
8153
8624
|
const lastText = new Map();
|
|
8154
|
-
|
|
8625
|
+
// Sessions whose turn has already been reported ended (so an idle that
|
|
8626
|
+
// follows another, old and new events both, reports once), and those
|
|
8627
|
+
// whose turn ended on an error (whose report was the error).
|
|
8628
|
+
const settled = new Set();
|
|
8629
|
+
const errored = new Set();
|
|
8630
|
+
const child = async (id) => {
|
|
8631
|
+
if (!id || children.has(id)) return true;
|
|
8632
|
+
if (roots.has(id)) return false;
|
|
8633
|
+
try {
|
|
8634
|
+
const res = await client?.session?.get?.({ sessionID: id, path: { id } });
|
|
8635
|
+
if ((res?.data ?? res)?.parentID) {
|
|
8636
|
+
children.add(id);
|
|
8637
|
+
return true;
|
|
8638
|
+
}
|
|
8639
|
+
} catch {}
|
|
8640
|
+
roots.add(id);
|
|
8641
|
+
return false;
|
|
8642
|
+
};
|
|
8643
|
+
const turnEnd = (id) => {
|
|
8644
|
+
if (settled.has(id)) return;
|
|
8645
|
+
settled.add(id);
|
|
8646
|
+
const summary = lastText.get(id);
|
|
8647
|
+
lastText.delete(id);
|
|
8648
|
+
roles.clear();
|
|
8649
|
+
// The error was the report; "Finished" after it would replace it, and
|
|
8650
|
+
// clear the session's call for attention with it.
|
|
8651
|
+
if (errored.delete(id)) return;
|
|
8652
|
+
return post("waiting", "done", summary);
|
|
8653
|
+
};
|
|
8155
8654
|
return {
|
|
8156
8655
|
event: async ({ event }) => {
|
|
8157
8656
|
const type = event?.type;
|
|
8158
8657
|
const p = event?.properties || {};
|
|
8159
8658
|
if (type === "session.created" || type === "session.updated") {
|
|
8160
8659
|
if (p.info?.parentID) children.add(p.info.id);
|
|
8660
|
+
else if (p.info?.id) roots.add(p.info.id);
|
|
8161
8661
|
return;
|
|
8162
8662
|
}
|
|
8163
8663
|
// Text parts carry no role; the message they belong to does, and a
|
|
@@ -8171,22 +8671,9 @@ export const WorkSessionState = async () => {
|
|
|
8171
8671
|
lastText.set(p.part.sessionID, p.part.text);
|
|
8172
8672
|
return;
|
|
8173
8673
|
}
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
if (type === "session.idle") {
|
|
8178
|
-
const summary = lastText.get(p.sessionID);
|
|
8179
|
-
lastText.delete(p.sessionID);
|
|
8180
|
-
roles.clear();
|
|
8181
|
-
return post("waiting", "done", summary);
|
|
8182
|
-
}
|
|
8183
|
-
if (type === "session.error")
|
|
8184
|
-
return post(
|
|
8185
|
-
"waiting",
|
|
8186
|
-
"attention",
|
|
8187
|
-
p.error?.data?.message || p.error?.name,
|
|
8188
|
-
);
|
|
8189
|
-
// A prompt is on screen and nothing moves until it is answered.
|
|
8674
|
+
// A prompt is on screen and nothing moves until it is answered —
|
|
8675
|
+
// whichever session raised it: the TUI shows a subagent's with the
|
|
8676
|
+
// session's own.
|
|
8190
8677
|
if (type === "permission.asked")
|
|
8191
8678
|
return post(
|
|
8192
8679
|
"waiting",
|
|
@@ -8195,7 +8682,44 @@ export const WorkSessionState = async () => {
|
|
|
8195
8682
|
.filter(Boolean)
|
|
8196
8683
|
.join(": "),
|
|
8197
8684
|
);
|
|
8198
|
-
if (type === "
|
|
8685
|
+
if (type === "question.asked")
|
|
8686
|
+
return post(
|
|
8687
|
+
"waiting",
|
|
8688
|
+
"attention",
|
|
8689
|
+
p.questions?.[0]?.question || "Asking a question",
|
|
8690
|
+
);
|
|
8691
|
+
if (
|
|
8692
|
+
type === "permission.replied" ||
|
|
8693
|
+
type === "question.replied" ||
|
|
8694
|
+
type === "question.rejected"
|
|
8695
|
+
)
|
|
8696
|
+
return post("working");
|
|
8697
|
+
if (!p.sessionID || (await child(p.sessionID))) return;
|
|
8698
|
+
// session.status is the current event (idle | retry | busy);
|
|
8699
|
+
// session.idle the older one, which builds still send beside it.
|
|
8700
|
+
if (type === "session.status") {
|
|
8701
|
+
if (p.status?.type === "busy") {
|
|
8702
|
+
settled.delete(p.sessionID);
|
|
8703
|
+
errored.delete(p.sessionID);
|
|
8704
|
+
return post("working");
|
|
8705
|
+
}
|
|
8706
|
+
if (p.status?.type === "idle") return turnEnd(p.sessionID);
|
|
8707
|
+
return;
|
|
8708
|
+
}
|
|
8709
|
+
if (type === "session.idle") return turnEnd(p.sessionID);
|
|
8710
|
+
if (type === "session.error") {
|
|
8711
|
+
// Stopped by the user: back at the prompt, nothing to push.
|
|
8712
|
+
if (p.error?.name === "MessageAbortedError") {
|
|
8713
|
+
settled.add(p.sessionID);
|
|
8714
|
+
return post("idle");
|
|
8715
|
+
}
|
|
8716
|
+
errored.add(p.sessionID);
|
|
8717
|
+
return post(
|
|
8718
|
+
"waiting",
|
|
8719
|
+
"attention",
|
|
8720
|
+
p.error?.data?.message || p.error?.name,
|
|
8721
|
+
);
|
|
8722
|
+
}
|
|
8199
8723
|
},
|
|
8200
8724
|
};
|
|
8201
8725
|
};
|
|
@@ -8205,7 +8729,7 @@ var PLUGIN_FILE = () => path.join(config.dataDir, "opencode-plugin.js");
|
|
|
8205
8729
|
* screen has to speak for the session. Checked on disk rather than
|
|
8206
8730
|
* remembered from a launch, so a session that outlives the app process
|
|
8207
8731
|
* is still known to report for itself. */
|
|
8208
|
-
function installed() {
|
|
8732
|
+
function installed$1() {
|
|
8209
8733
|
return fs.existsSync(PLUGIN_FILE());
|
|
8210
8734
|
}
|
|
8211
8735
|
/** opencode resolves a `plugin` entry that looks like a path as a local
|
|
@@ -8219,6 +8743,114 @@ function pluginConfig() {
|
|
|
8219
8743
|
} catch {}
|
|
8220
8744
|
return JSON.stringify({ plugin: [file] });
|
|
8221
8745
|
}
|
|
8746
|
+
function readHook$1(p, reported) {
|
|
8747
|
+
const summary = str$1(p.summary);
|
|
8748
|
+
return {
|
|
8749
|
+
reason: reported,
|
|
8750
|
+
summary: summary ? cleanSummary(summary) : void 0
|
|
8751
|
+
};
|
|
8752
|
+
}
|
|
8753
|
+
var opencode = {
|
|
8754
|
+
meta: meta$1,
|
|
8755
|
+
readyScreen: /Ask anything/,
|
|
8756
|
+
launchEnv: () => ({ OPENCODE_CONFIG_CONTENT: pluginConfig() }),
|
|
8757
|
+
reportsWorking: installed$1,
|
|
8758
|
+
envPrefixes: ["OPENCODE_"],
|
|
8759
|
+
readHook: readHook$1
|
|
8760
|
+
};
|
|
8761
|
+
//#endregion
|
|
8762
|
+
//#region src/agents/pi/index.ts
|
|
8763
|
+
var meta = meta_default;
|
|
8764
|
+
/** Pi's hooks are extensions: a module loaded with `-e <path>` (repeatable,
|
|
8765
|
+
* alongside the user's own, no config written) that subscribes to the
|
|
8766
|
+
* agent's events. `agent_settled` is the end of a turn — `agent_end` fires
|
|
8767
|
+
* after every run, and Pi may still retry, compact or carry on with a
|
|
8768
|
+
* queued follow-up — and `ui_prompt_start`/`_end` bracket an extension's
|
|
8769
|
+
* prompt, the one way Pi stops on the user. A Pi older than
|
|
8770
|
+
* `agent_settled` (0.80.4) never sends it, so a run's end waits a moment
|
|
8771
|
+
* for one and reports itself when none comes. The extension posts its own
|
|
8772
|
+
* reports, as opencode's plugin does; `readHook` only unwraps them. */
|
|
8773
|
+
var EXTENSION = (appPort) => `// Generated by work. Reports this
|
|
8774
|
+
// session's working / needs-input / done state to the app; edits are
|
|
8775
|
+
// overwritten. Port and session id come from the env tmux gave the pane.
|
|
8776
|
+
const post = async (state, reason, summary) => {
|
|
8777
|
+
const session = process.env.WORK_SESSION_ID;
|
|
8778
|
+
if (!session) return;
|
|
8779
|
+
const port = process.env.WORK_APP_PORT || "${appPort}";
|
|
8780
|
+
const qs =
|
|
8781
|
+
\`session=\${encodeURIComponent(session)}&agent=pi&state=\${state}\` +
|
|
8782
|
+
(reason ? \`&reason=\${reason}\` : "");
|
|
8783
|
+
try {
|
|
8784
|
+
await fetch(\`http://127.0.0.1:\${port}/api/agent-state?\${qs}\`, {
|
|
8785
|
+
method: "POST",
|
|
8786
|
+
headers: { "content-type": "application/json" },
|
|
8787
|
+
body: JSON.stringify({ summary }),
|
|
8788
|
+
signal: AbortSignal.timeout(3000),
|
|
8789
|
+
});
|
|
8790
|
+
} catch {}
|
|
8791
|
+
};
|
|
8792
|
+
|
|
8793
|
+
const textOf = (message) =>
|
|
8794
|
+
(Array.isArray(message?.content) ? message.content : [])
|
|
8795
|
+
.filter((part) => part?.type === "text" && part.text)
|
|
8796
|
+
.map((part) => part.text)
|
|
8797
|
+
.join("\\n");
|
|
8798
|
+
|
|
8799
|
+
export default function (pi) {
|
|
8800
|
+
// The run's last assistant message, for the summary and how it ended.
|
|
8801
|
+
let last = null;
|
|
8802
|
+
// This Pi sends agent_settled; until one arrives, a run's end waits.
|
|
8803
|
+
let settles = false;
|
|
8804
|
+
let fallback = null;
|
|
8805
|
+
const settle = () => {
|
|
8806
|
+
clearTimeout(fallback);
|
|
8807
|
+
fallback = null;
|
|
8808
|
+
const message = last;
|
|
8809
|
+
last = null;
|
|
8810
|
+
if (message?.stopReason === "aborted") return post("idle");
|
|
8811
|
+
if (message?.stopReason === "error")
|
|
8812
|
+
return post("waiting", "attention", message.errorMessage || textOf(message));
|
|
8813
|
+
return post("waiting", "done", textOf(message));
|
|
8814
|
+
};
|
|
8815
|
+
pi.on("agent_start", () => {
|
|
8816
|
+
clearTimeout(fallback);
|
|
8817
|
+
fallback = null;
|
|
8818
|
+
return post("working");
|
|
8819
|
+
});
|
|
8820
|
+
pi.on("agent_end", (event) => {
|
|
8821
|
+
const messages = Array.isArray(event?.messages) ? event.messages : [];
|
|
8822
|
+
last = [...messages].reverse().find((m) => m?.role === "assistant") ?? last;
|
|
8823
|
+
if (!settles) fallback = setTimeout(settle, 1500);
|
|
8824
|
+
});
|
|
8825
|
+
pi.on("agent_settled", (_event, ctx) => {
|
|
8826
|
+
settles = true;
|
|
8827
|
+
// Another extension may have started a run already.
|
|
8828
|
+
if (ctx?.isIdle && !ctx.isIdle()) return;
|
|
8829
|
+
return settle();
|
|
8830
|
+
});
|
|
8831
|
+
pi.on("ui_prompt_start", (event) =>
|
|
8832
|
+
post("waiting", "attention", event?.title || "Waiting for input"),
|
|
8833
|
+
);
|
|
8834
|
+
pi.on("ui_prompt_end", (_event, ctx) =>
|
|
8835
|
+
post(ctx?.isIdle && ctx.isIdle() ? "idle" : "working"),
|
|
8836
|
+
);
|
|
8837
|
+
}
|
|
8838
|
+
`;
|
|
8839
|
+
var EXTENSION_FILE = () => path.join(config.dataDir, "pi-extension.ts");
|
|
8840
|
+
/** Whether the extension is on disk for Pi to load — without it the
|
|
8841
|
+
* screen has to speak for the session. Checked on disk rather than
|
|
8842
|
+
* remembered from a launch, so a session that outlives the app process
|
|
8843
|
+
* is still known to report for itself. */
|
|
8844
|
+
function installed() {
|
|
8845
|
+
return fs.existsSync(EXTENSION_FILE());
|
|
8846
|
+
}
|
|
8847
|
+
function extensionFile() {
|
|
8848
|
+
const file = EXTENSION_FILE();
|
|
8849
|
+
try {
|
|
8850
|
+
writeIfChanged(file, EXTENSION(config.appPort));
|
|
8851
|
+
} catch {}
|
|
8852
|
+
return file;
|
|
8853
|
+
}
|
|
8222
8854
|
function readHook(p, reported) {
|
|
8223
8855
|
const summary = str$1(p.summary);
|
|
8224
8856
|
return {
|
|
@@ -8237,17 +8869,13 @@ var AGENTS = [
|
|
|
8237
8869
|
copilot,
|
|
8238
8870
|
cursor,
|
|
8239
8871
|
grok,
|
|
8872
|
+
opencode,
|
|
8240
8873
|
{
|
|
8241
|
-
meta
|
|
8242
|
-
|
|
8243
|
-
launchEnv: () => ({ OPENCODE_CONFIG_CONTENT: pluginConfig() }),
|
|
8874
|
+
meta,
|
|
8875
|
+
launchFlags: () => ["-e", extensionFile()],
|
|
8244
8876
|
reportsWorking: installed,
|
|
8245
|
-
envPrefixes: ["
|
|
8877
|
+
envPrefixes: ["PI_"],
|
|
8246
8878
|
readHook
|
|
8247
|
-
},
|
|
8248
|
-
{
|
|
8249
|
-
meta: meta_default,
|
|
8250
|
-
envPrefixes: ["PI_"]
|
|
8251
8879
|
}
|
|
8252
8880
|
];
|
|
8253
8881
|
var byFamily = new Map(AGENTS.map((a) => [a.meta.id, a]));
|
|
@@ -8256,13 +8884,13 @@ function agentFor(id) {
|
|
|
8256
8884
|
return id ? byFamily.get(agentFamily(id)) : void 0;
|
|
8257
8885
|
}
|
|
8258
8886
|
/** Extra argv for an agent CLI so it reports working/waiting state. */
|
|
8259
|
-
function agentLaunchFlags(id) {
|
|
8260
|
-
return agentFor(id)?.launchFlags?.() ?? [];
|
|
8887
|
+
function agentLaunchFlags(id, ctx) {
|
|
8888
|
+
return agentFor(id)?.launchFlags?.(ctx) ?? [];
|
|
8261
8889
|
}
|
|
8262
8890
|
/** Env for a spawned pane so the agent CLI reports state — the families
|
|
8263
8891
|
* configured by environment rather than argv. */
|
|
8264
|
-
function agentLaunchEnv(id) {
|
|
8265
|
-
return agentFor(id)?.launchEnv?.() ?? {};
|
|
8892
|
+
function agentLaunchEnv(id, ctx) {
|
|
8893
|
+
return agentFor(id)?.launchEnv?.(ctx) ?? {};
|
|
8266
8894
|
}
|
|
8267
8895
|
/** Whether the agent says when it *starts* working, not just when it stops
|
|
8268
8896
|
* — believing the screen over that costs it the state its own hook just
|
|
@@ -8270,6 +8898,14 @@ function agentLaunchEnv(id) {
|
|
|
8270
8898
|
function reportsWorking(id) {
|
|
8271
8899
|
return agentFor(id)?.reportsWorking?.() ?? false;
|
|
8272
8900
|
}
|
|
8901
|
+
/** The pane title a CLI shows at its prompt, if it sets one. */
|
|
8902
|
+
function idleTitle(id) {
|
|
8903
|
+
return agentFor(id)?.idleTitle;
|
|
8904
|
+
}
|
|
8905
|
+
/** The CLI draws menus before its input box that work answers. */
|
|
8906
|
+
function hasStartupPrompt(id) {
|
|
8907
|
+
return !!agentFor(id)?.startupPrompt;
|
|
8908
|
+
}
|
|
8273
8909
|
/** Keys that answer whatever startup prompt the CLI is showing, if any. */
|
|
8274
8910
|
function startupPromptKeys(id, screen) {
|
|
8275
8911
|
return agentFor(id)?.startupPrompt?.(screen);
|
|
@@ -8294,10 +8930,10 @@ function agentPathDirs(home) {
|
|
|
8294
8930
|
}
|
|
8295
8931
|
/** What a hook payload the session's CLI POSTed means. Unknown families
|
|
8296
8932
|
* (or an unreadable body) keep the reported reason and carry no summary. */
|
|
8297
|
-
function readHookPayload(id, payload, reported) {
|
|
8933
|
+
function readHookPayload(id, payload, reported, state) {
|
|
8298
8934
|
if (!payload || typeof payload !== "object") return { reason: reported };
|
|
8299
8935
|
const read = agentFor(id)?.readHook;
|
|
8300
|
-
return read ? read(payload, reported) : { reason: reported };
|
|
8936
|
+
return read ? read(payload, reported, state) : { reason: reported };
|
|
8301
8937
|
}
|
|
8302
8938
|
//#endregion
|
|
8303
8939
|
//#region src/server/sessions.ts
|
|
@@ -8481,18 +9117,19 @@ async function listPanesNow() {
|
|
|
8481
9117
|
"list-panes",
|
|
8482
9118
|
"-a",
|
|
8483
9119
|
"-F",
|
|
8484
|
-
"#{session_name} #{pane_current_command} #{pane_pid} #{window_activity}"
|
|
9120
|
+
"#{session_name} #{pane_current_command} #{pane_pid} #{window_activity} #{pane_title}"
|
|
8485
9121
|
]).catch(() => null);
|
|
8486
9122
|
if (out === null) return null;
|
|
8487
9123
|
const panes = [];
|
|
8488
9124
|
for (const line of out.split("\n")) {
|
|
8489
9125
|
if (!line) continue;
|
|
8490
|
-
const [name, command, pid, stamp] = line.split(" ");
|
|
9126
|
+
const [name, command, pid, stamp, ...title] = line.split(" ");
|
|
8491
9127
|
panes.push({
|
|
8492
9128
|
id: name?.startsWith("wk-") ? name.slice(3) : void 0,
|
|
8493
9129
|
command: command || "",
|
|
8494
9130
|
pid: Number(pid),
|
|
8495
|
-
activityAt: Number(stamp) * 1e3
|
|
9131
|
+
activityAt: Number(stamp) * 1e3,
|
|
9132
|
+
title: title.join(" ")
|
|
8496
9133
|
});
|
|
8497
9134
|
}
|
|
8498
9135
|
lastPanes = {
|
|
@@ -8649,20 +9286,20 @@ function newSessionArgs(session, wsPath, mode, initialPrompt, size) {
|
|
|
8649
9286
|
tmuxName(session.id),
|
|
8650
9287
|
"-c",
|
|
8651
9288
|
wsPath,
|
|
8652
|
-
...sessionEnvArgs(session.id, session.agent),
|
|
8653
|
-
...loginShellCommand(session.agent, initialPrompt)
|
|
9289
|
+
...sessionEnvArgs(session.id, session.agent, wsPath),
|
|
9290
|
+
...loginShellCommand(session.agent, wsPath, initialPrompt)
|
|
8654
9291
|
];
|
|
8655
9292
|
}
|
|
8656
9293
|
/** Run the agent through a login shell so the pane's PATH matches the
|
|
8657
9294
|
* user's interactive terminal (mise activation, cargo, custom bins) —
|
|
8658
9295
|
* a service manager's PATH is far slimmer than a login shell's. */
|
|
8659
|
-
function loginShellCommand(agent, initialPrompt) {
|
|
9296
|
+
function loginShellCommand(agent, cwd, initialPrompt) {
|
|
8660
9297
|
const words = sessionCommand(agent);
|
|
8661
9298
|
if (!words.length) return [];
|
|
8662
9299
|
const line = [
|
|
8663
9300
|
...words,
|
|
8664
|
-
...agentLaunchFlags(agent),
|
|
8665
|
-
...initialPrompt ? [initialPrompt] : []
|
|
9301
|
+
...agentLaunchFlags(agent, { cwd }),
|
|
9302
|
+
...initialPrompt ? initialPrompt.startsWith("-") ? ["--", initialPrompt] : [initialPrompt] : []
|
|
8666
9303
|
].map(shellQuote).join(" ");
|
|
8667
9304
|
return [
|
|
8668
9305
|
process.env.SHELL || "bash",
|
|
@@ -8691,6 +9328,14 @@ function cleanEnv() {
|
|
|
8691
9328
|
env.PATH = [...parts, ...bins.filter((bin) => !parts.includes(bin))].join(":");
|
|
8692
9329
|
return forceUtf8Locale(env);
|
|
8693
9330
|
}
|
|
9331
|
+
/** Answer the menus a CLI draws before its input box (a folder-trust
|
|
9332
|
+
* question) on a pane an attach just created, as sending a first prompt
|
|
9333
|
+
* does — so a session opened with nothing to send is not left on one. */
|
|
9334
|
+
async function settleStartup(id) {
|
|
9335
|
+
const session = getSession(id);
|
|
9336
|
+
if (!session || !hasStartupPrompt(session.agent)) return;
|
|
9337
|
+
await waitForAgent(tmuxName(id), cleanEnv(), session.agent).catch(() => {});
|
|
9338
|
+
}
|
|
8694
9339
|
/** Upper bound on waiting for an agent to be ready for input; past it
|
|
8695
9340
|
* the paste goes in regardless (a TUI with a boot animation never settles). */
|
|
8696
9341
|
var AGENT_BOOT_MS = 8e3;
|
|
@@ -8780,13 +9425,13 @@ function sessionCommand(agent) {
|
|
|
8780
9425
|
/** tmux new-session env flags so the agent's hooks can report back which
|
|
8781
9426
|
* session they belong to, plus whatever the family needs to load its own
|
|
8782
9427
|
* hooks (opencode names its plugin here — tmux quotes it, not the shell). */
|
|
8783
|
-
function sessionEnvArgs(sessionId, agent) {
|
|
9428
|
+
function sessionEnvArgs(sessionId, agent, cwd) {
|
|
8784
9429
|
return [
|
|
8785
9430
|
"-e",
|
|
8786
9431
|
`WORK_SESSION_ID=${sessionId}`,
|
|
8787
9432
|
"-e",
|
|
8788
9433
|
`WORK_APP_PORT=${config.appPort}`,
|
|
8789
|
-
...Object.entries(agentLaunchEnv(agent)).flatMap(([key, value]) => ["-e", `${key}=${value}`])
|
|
9434
|
+
...Object.entries(agentLaunchEnv(agent, { cwd })).flatMap(([key, value]) => ["-e", `${key}=${value}`])
|
|
8790
9435
|
];
|
|
8791
9436
|
}
|
|
8792
9437
|
function tmuxName(sessionId) {
|
|
@@ -8796,4 +9441,4 @@ function sleep(ms) {
|
|
|
8796
9441
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8797
9442
|
}
|
|
8798
9443
|
//#endregion
|
|
8799
|
-
export {
|
|
9444
|
+
export { validSubscription as $, hookPhase as A, get as At, agentFromCommand as B, AGENTS as C, DEFAULT_GITHUB_HOST as Ct, blankRow as D, toolById as Dt, reportsWorking as E, agentLabel$1 as Et, setScrapedPhase as F, run$1 as Ft, iconSvg as G, agentMeta as H, setSessionViewers as I, tmux as It, setInstance as J, manifest as K, settleQuietWorking as L, config as Lt, noteUserAnswered as M, TMUX_SOCKET as Mt, onPhaseChange as N, gh as Nt, agentPhase as O, all as Ot, setAgentPhase as P, git as Pt, sendPush as Q, agentBadge as R, tmuxWasStarted as S, which as St, readHookPayload as T, agentById as Tt, ACCENT_RE as U, agentLabel as V, getInstance as W, pushPublicKey as X, deleteSubscription as Y, saveSubscription as Z, renameSession as _, doctor as _t, ensureTmuxServer as a, swr as at, settleStartup as b, miseVersions as bt, killSession as c, allPrefs as ct, listPanesWithin as d, getSetting as dt, publish as et, listSessions as f, preferredAgent as ft, panePids as g, discoverAgents as gt, notifySessionsChanged as h, setSetting as ht, disposeSessions as i, shared as it, hookReportedAt as j, workspacePath as jt, clearAgentPhase as k, db as kt, killWorkspaceSessions as l, disabledAgents as lt, newSessionArgs as m, setPref as mt, clearTmuxStarted as n, subscribe as nt, forgetPanes as o, withLock as ot, markTmuxStarted as p, setDisabledAgents as pt, normalizeAccent as q, createSession as r, subscriberCount as rt, getSession as s, agentChoices as st, cleanEnv as t, publishJson as tt, listPanes as u, enabledAgents as ut, sendToSession as v, ghKnownHosts as vt, idleTitle as w, TOOLS as wt, tmuxName as x, setupPath as xt, sessionStates as y, miseInstalls as yt, agentFamily as z };
|