very-happy-cli 0.2.177 → 0.2.179

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/dist/{AcpBackend-B0K0WrtW.mjs → AcpBackend-CHp3y7cb.mjs} +135 -16
  2. package/dist/{AcpBackend-B-ny68XM.cjs → AcpBackend-DFipFF7-.cjs} +135 -16
  3. package/dist/{AcpSessionManager-CjdJLofq.cjs → AcpSessionManager-0zpvRJXc.cjs} +38 -4
  4. package/dist/{AcpSessionManager-DykjzBM4.mjs → AcpSessionManager-d2-rKWWa.mjs} +38 -4
  5. package/dist/{BasePermissionHandler-DR6nxuEn.cjs → BasePermissionHandler-CH3zuEkA.cjs} +5 -2
  6. package/dist/{BasePermissionHandler-hdY7kTqX.mjs → BasePermissionHandler-DdZBYIR4.mjs} +5 -2
  7. package/dist/acpSessionResume-DTGmAPLt.mjs +67 -0
  8. package/dist/acpSessionResume-oiCBnAVA.cjs +74 -0
  9. package/dist/{agentHome-BdTXEUPe.cjs → agentHome-BWcz8mz0.cjs} +5 -5
  10. package/dist/{agentHome-GRN2VfHv.mjs → agentHome-CghLvL_1.mjs} +5 -5
  11. package/dist/{api-BWX10o97.cjs → api-Cmjfr8Z7.cjs} +29 -261
  12. package/dist/{api-CKBViqI3.mjs → api-CzwFpJQc.mjs} +22 -254
  13. package/dist/{apiMachine-DttO4UFj.mjs → apiMachine-DJkTEiqy.mjs} +53 -16
  14. package/dist/{apiMachine-BYYFGgFs.cjs → apiMachine-G6OogT08.cjs} +53 -16
  15. package/dist/{attachmentContent-cPVF463_.mjs → attachmentContent-B2os4HbA.mjs} +2 -2
  16. package/dist/{attachmentContent-kJKXpkMX.cjs → attachmentContent-Bo0UAsSu.cjs} +4 -4
  17. package/dist/{auth-BmESeKuM.mjs → auth-BS6PaOOG.mjs} +6 -6
  18. package/dist/{auth-CTECt2Oz.cjs → auth-D6bBUwZa.cjs} +6 -6
  19. package/dist/{auth-CKdXb9zp.mjs → auth-DMX0xPNK.mjs} +9 -9
  20. package/dist/{auth-DNxVmVbn.cjs → auth-HEhRaNgL.cjs} +9 -9
  21. package/dist/{authRelay-DSAvg1uv.mjs → authRelay-DhQhkBvX.mjs} +1 -1
  22. package/dist/{authRelay-BdYkC1Po.cjs → authRelay-Dy1qU9td.cjs} +1 -0
  23. package/dist/{auto-D1ypjjfx.cjs → auto-C6I55iOh.cjs} +19 -18
  24. package/dist/{auto-Ch3203qd.mjs → auto-Cl1SEDcD.mjs} +19 -18
  25. package/dist/{bootstrap-6OI698q9.cjs → bootstrap-exVucYav.cjs} +2 -2
  26. package/dist/{bootstrap-C0s2bPFQ.mjs → bootstrap-iuyufnp3.mjs} +2 -2
  27. package/dist/{browser-DG6Uof5O.cjs → browser-BohJK12g.cjs} +1 -1
  28. package/dist/{browser-BlLWZUMx.mjs → browser-CSYum7xH.mjs} +1 -1
  29. package/dist/{claudeLocal-CcWcKnoo.mjs → claudeLocal-Dq_R3Ax_.mjs} +6 -5
  30. package/dist/{claudeLocal-CemsIArT.cjs → claudeLocal-re4yjSTx.cjs} +6 -5
  31. package/dist/{claudeLocalLauncher-A4VuevIi.cjs → claudeLocalLauncher-B4hJHpCA.cjs} +11 -10
  32. package/dist/{claudeLocalLauncher-7p6y88nj.mjs → claudeLocalLauncher-CN67lN7-.mjs} +11 -10
  33. package/dist/{claudeSessionFork-BtROOjqf.mjs → claudeSessionFork-BihfD0Ik.mjs} +1 -1
  34. package/dist/{claudeSessionFork-CbDfm8Fz.cjs → claudeSessionFork-DiSMYerV.cjs} +1 -1
  35. package/dist/{client-DyvBTYee.cjs → client-BKcK1Bi-.cjs} +3 -3
  36. package/dist/client-BhoEBnuN.mjs +71 -0
  37. package/dist/{client-COmgDDy0.cjs → client-CuVdN6PP.cjs} +2 -2
  38. package/dist/{client-BPMWutfs.mjs → client-DuTDwWSQ.mjs} +3 -3
  39. package/dist/client-ZjOsn6nZ.cjs +76 -0
  40. package/dist/{client-DG5ndWLw.mjs → client-wo9nbxuf.mjs} +2 -2
  41. package/dist/codex/happyMcpStdioBridge.cjs +25 -16
  42. package/dist/codex/happyMcpStdioBridge.mjs +25 -16
  43. package/dist/{codexAppServerClient-D-Z8qSfI.mjs → codexAppServerClient-BK3zra_1.mjs} +3 -3
  44. package/dist/{codexAppServerClient-R24WZZfg.cjs → codexAppServerClient-Dt5zxrun.cjs} +3 -3
  45. package/dist/{codexCommand-CVTkZJ1o.mjs → codexCommand-B6pY7CMh.mjs} +66 -61
  46. package/dist/{codexCommand-6hW19gsq.cjs → codexCommand-CAZ1SLjg.cjs} +89 -84
  47. package/dist/{codexTermination-CCIfwzcx.mjs → codexTermination-Cd1Eon_4.mjs} +1 -1
  48. package/dist/{codexTermination-BBU7e92y.cjs → codexTermination-DmGbHxcD.cjs} +1 -1
  49. package/dist/{config-CvsNChKX.mjs → config-2oMG_fEI.mjs} +3 -3
  50. package/dist/{config-DhdrvOLV.cjs → config-B0uK6K7n.cjs} +3 -3
  51. package/dist/{configuration-DZPy0T9Z.cjs → configuration-CMHZnp6G.cjs} +1 -1
  52. package/dist/{configuration-D55F0t16.mjs → configuration-WMi4idBB.mjs} +1 -1
  53. package/dist/{connect-CwCXMRmD.mjs → connect-BLVHhHzA.mjs} +15 -15
  54. package/dist/{connect-Wz-XZYXW.cjs → connect-uaglGrde.cjs} +15 -15
  55. package/dist/{context-BKbrZ55r.cjs → context-DXzU5bFM.cjs} +1 -1
  56. package/dist/{context-CCRpVXFo.mjs → context-DploEcoN.mjs} +1 -1
  57. package/dist/{controlClient-C4xB5J6O.mjs → controlClient-BWUJrj3t.mjs} +3 -3
  58. package/dist/{controlClient-SX8gSza2.cjs → controlClient-C-5qCt8W.cjs} +3 -3
  59. package/dist/{createSessionMetadata-Bi-m0Y_O.mjs → createSessionMetadata-3uXrJgOW.mjs} +2 -2
  60. package/dist/{createSessionMetadata-BCFmYlBo.cjs → createSessionMetadata-BkIzHALd.cjs} +2 -2
  61. package/dist/{defaultPermissionMode-wBcbBezm.mjs → defaultPermissionMode-BzxLh20a.mjs} +2 -2
  62. package/dist/{defaultPermissionMode-DMBEnXll.cjs → defaultPermissionMode-pS3vmiSg.cjs} +2 -2
  63. package/dist/{deterministicJson-ChGfQEF6.cjs → deterministicJson-B8z2GPi9.cjs} +220 -14
  64. package/dist/{deterministicJson-BkQETt8_.mjs → deterministicJson-D_whDMFd.mjs} +217 -12
  65. package/dist/{doctor-BsN3JZJn.cjs → doctor-Br1Kcy2J.cjs} +8 -8
  66. package/dist/{doctor-B9c2C9dF.mjs → doctor-CmtSsR2-.mjs} +8 -8
  67. package/dist/{downloadTarget-Dj3WoVJa.cjs → downloadTarget-BQJK_6oH.cjs} +6 -6
  68. package/dist/{downloadTarget-ByXzbDK4.mjs → downloadTarget-BoNwTIH_.mjs} +6 -6
  69. package/dist/{ensureDaemonRunning-7Wb8M7Lv.cjs → ensureDaemonRunning-BRUmsq6J.cjs} +5 -5
  70. package/dist/{ensureDaemonRunning--HYzs5Ce.mjs → ensureDaemonRunning-Czo9IHJQ.mjs} +5 -5
  71. package/dist/{handleResumeCommand-DHddBkNJ.mjs → handleResumeCommand-C8thKjqf.mjs} +22 -6
  72. package/dist/{handleResumeCommand-OmuUGYjQ.cjs → handleResumeCommand-tKD_Hpxc.cjs} +22 -6
  73. package/dist/{index-CrR4JISa.cjs → index-B0UjHlAe.cjs} +5 -5
  74. package/dist/{index-BzJNDsJq.mjs → index-BTxHphpV.mjs} +254 -49
  75. package/dist/index-BiyC7cZ3.mjs +18 -0
  76. package/dist/{index-De8L5lFK.cjs → index-C6O2vVjm.cjs} +156 -34
  77. package/dist/index-CUuc6iCn.cjs +24 -0
  78. package/dist/{index-Dhl6_bw7.mjs → index-CdQI0C9p.mjs} +5 -5
  79. package/dist/{index-D9NoYc5I.cjs → index-D09T1m8Y.cjs} +3 -3
  80. package/dist/{index-CEm0MYiS.mjs → index-D3bidoG8.mjs} +3 -3
  81. package/dist/{index-BRJzH4sc.cjs → index-DMqDMT1Q.cjs} +257 -52
  82. package/dist/{index-DMy3OCBe.mjs → index-DPOa4NhL.mjs} +154 -35
  83. package/dist/index.cjs +2 -2
  84. package/dist/index.mjs +2 -2
  85. package/dist/{install-BdvKiOQw.mjs → install-BX3lv3NO.mjs} +1 -1
  86. package/dist/{install-Cf6AFSWC.cjs → install-BXvf2E3o.cjs} +1 -1
  87. package/dist/{install-1qn-D9FS.cjs → install-CYRZxS87.cjs} +3 -3
  88. package/dist/{install-BDIUtPXi.mjs → install-hnve_9vq.mjs} +3 -3
  89. package/dist/lib.cjs +14 -14
  90. package/dist/lib.d.cts +65 -1
  91. package/dist/lib.d.mts +65 -1
  92. package/dist/lib.mjs +14 -14
  93. package/dist/{lidState-BqVsw7H1.mjs → lidState-Bb8fgXg3.mjs} +2 -2
  94. package/dist/{lidState-DxbK0qfu.cjs → lidState-Dv2wwFTj.cjs} +2 -2
  95. package/dist/{localHappyAgentAuth-D5tANdRD.cjs → localHappyAgentAuth-DjYvkXvV.cjs} +2 -2
  96. package/dist/{localHappyAgentAuth-Ce8kO2yc.mjs → localHappyAgentAuth-NEU7fax7.mjs} +2 -2
  97. package/dist/{logger-13cBr_ew.mjs → logger-BtVhHnD8.mjs} +2 -2
  98. package/dist/{logger-LvwVeATU.cjs → logger-C1GWx3gi.cjs} +2 -2
  99. package/dist/{machineMetadata-29h7vgf4.cjs → machineMetadata-CLHS9sv_.cjs} +3 -3
  100. package/dist/{machineMetadata-dMida2qq.mjs → machineMetadata-D-g--oU_.mjs} +3 -3
  101. package/dist/{main-x4nAhdH5.mjs → main-B2LWV8DI.mjs} +59 -46
  102. package/dist/{main-DpM5jaFH.cjs → main-CWhSsbtU.cjs} +59 -46
  103. package/dist/{mcp-DQv3caXs.cjs → mcp-CARUsXHV.cjs} +15 -15
  104. package/dist/{mcp-DMiFGYMT.mjs → mcp-ext92q3Y.mjs} +15 -15
  105. package/dist/{mirrorProtocol-Bz8tEabR.mjs → mirrorProtocol-BwYibqry.mjs} +3 -3
  106. package/dist/{mirrorProtocol-BfrERW7Z.cjs → mirrorProtocol-DEy2g1CB.cjs} +3 -3
  107. package/dist/{package-CjlsAaRe.mjs → package-BPPebGQF.mjs} +7 -7
  108. package/dist/{package-Ch9WGDV_.cjs → package-SgqTYsjn.cjs} +7 -7
  109. package/dist/panels-BZ0_Tt7B.mjs +137 -0
  110. package/dist/panels-D1c7q2dD.cjs +142 -0
  111. package/dist/{peerTools-CxmrKnfV.mjs → peerTools-CWKzcQ7u.mjs} +60 -10
  112. package/dist/{peerTools-DtGKWli5.cjs → peerTools-DRQETXaz.cjs} +60 -9
  113. package/dist/{persistence-DXbKOHxl.cjs → persistence-BegE7NPa.cjs} +14 -3
  114. package/dist/{persistence-C_tYtOqK.mjs → persistence-Cr-wutZ4.mjs} +14 -4
  115. package/dist/{piRunnerArgs-BVRZe2AR.cjs → piRunnerArgs-Cs-378Vz.cjs} +8 -2
  116. package/dist/{piRunnerArgs-BpHc-JZl.mjs → piRunnerArgs-DwvM_yWA.mjs} +7 -1
  117. package/dist/{piRuntime-D7eio8Ix.cjs → piRuntime-BG-1td_n.cjs} +2 -2
  118. package/dist/{piRuntime-DOdGF395.mjs → piRuntime-Bue5mnlc.mjs} +2 -2
  119. package/dist/{piTerminal-dpxz4Q8D.mjs → piTerminal-B5x9X5yV.mjs} +18 -18
  120. package/dist/{piTerminal-y_PRo2NY.cjs → piTerminal-G4z-xVyy.cjs} +18 -18
  121. package/dist/processTreeSnapshot-CuLJDNC-.mjs +101 -0
  122. package/dist/processTreeSnapshot-mDbnuojn.cjs +105 -0
  123. package/dist/prompt-BOouDaTM.mjs +99 -0
  124. package/dist/prompt-lgF9IE82.cjs +105 -0
  125. package/dist/{attachmentLimits-Di1Z93lN.mjs → pushNotifications-BdFjDjQe.mjs} +303 -3
  126. package/dist/{attachmentLimits-DtFqqiwR.cjs → pushNotifications-DaLQUyi0.cjs} +304 -2
  127. package/dist/{index-DFPT4HOO.cjs → query-Ctfg-5gy.cjs} +25 -5
  128. package/dist/{index-CEjJnq05.mjs → query-DYP_W1wp.mjs} +26 -3
  129. package/dist/{rateLimitWindows-DYHDGuzQ.mjs → rateLimitWindows-CuEqk_0g.mjs} +1 -1
  130. package/dist/{rateLimitWindows-DFnMU2Vt.cjs → rateLimitWindows-Dj_8voBK.cjs} +1 -1
  131. package/dist/{registerCommonHandlers-D8OMpBbQ.mjs → registerCommonHandlers-BAlP7SGc.mjs} +2 -2
  132. package/dist/{registerCommonHandlers-60VuTkdg.cjs → registerCommonHandlers-Cj084Sg7.cjs} +2 -2
  133. package/dist/{registerKillSessionHandler-Drhf7-Vz.mjs → registerKillSessionHandler-PD0jWtQo.mjs} +6 -6
  134. package/dist/{registerKillSessionHandler-pqChBKwO.cjs → registerKillSessionHandler-qMju0ZfF.cjs} +6 -6
  135. package/dist/{remoteSessionClient-B7NvjHD3.cjs → remoteSessionClient-B7i7MkYV.cjs} +5 -5
  136. package/dist/{remoteSessionClient-D01Hso2-.mjs → remoteSessionClient-Bn2qu5C5.mjs} +5 -5
  137. package/dist/{reportProgressTool-Du_zAK4h.cjs → reportProgressTool-CM1JFAHg.cjs} +84 -2
  138. package/dist/{reportProgressTool-B203EZCR.mjs → reportProgressTool-gHItlSzz.mjs} +82 -3
  139. package/dist/{resources-JDPugcDq.cjs → resources-CvIlkzpP.cjs} +30 -0
  140. package/dist/{resources-BdFBQi8r.mjs → resources-y9jfyO-_.mjs} +30 -0
  141. package/dist/{run-DX4Zs3Ug.mjs → run-BM4TxiyN.mjs} +249 -45
  142. package/dist/{run-DZ5jU53c.cjs → run-CNXwIe0A.cjs} +263 -59
  143. package/dist/{runClaude-ClRhHpwR.cjs → runClaude-CWhj2V4Q.cjs} +37 -41
  144. package/dist/{runClaude-CgPHXP0L.mjs → runClaude-C_3I8hvR.mjs} +37 -41
  145. package/dist/{runClaudeDeps-D2v_WDSL.cjs → runClaudeDeps-B81cOKnH.cjs} +295 -157
  146. package/dist/{runClaudeDeps-Dz2iydq4.mjs → runClaudeDeps-QGrOeDe2.mjs} +295 -160
  147. package/dist/{runGemini-DBXxpoY8.mjs → runGemini-BEdWEZLR.mjs} +61 -55
  148. package/dist/{runGemini-BtZqGFkp.cjs → runGemini-CgRLYbt5.cjs} +61 -55
  149. package/dist/{runOpenClaw-D8q8G57u.cjs → runOpenClaw-C1NVmzOL.cjs} +19 -19
  150. package/dist/{runOpenClaw-BOHpzCEU.mjs → runOpenClaw-DdQZAYj1.mjs} +19 -19
  151. package/dist/{sandbox-C452Vrsc.mjs → sandbox-DjQUdCMG.mjs} +5 -5
  152. package/dist/{sandbox-Cu9mMzjx.cjs → sandbox-nocIjliq.cjs} +5 -5
  153. package/dist/selfCheck-CjCF4C3v.mjs +85 -0
  154. package/dist/{selfCheck-gKa3uVr1.cjs → selfCheck-DCt4Uzbq.cjs} +36 -35
  155. package/dist/{send-B8HARi7x.mjs → send-Ct7X4pPU.mjs} +9 -9
  156. package/dist/{send-2Vb5eppI.cjs → send-Dydf_hwZ.cjs} +9 -9
  157. package/dist/{server-Db9Wuam3.cjs → server-CosABFbd.cjs} +7 -7
  158. package/dist/{server-BBWCIZyS.mjs → server-DTQGZMxh.mjs} +5 -5
  159. package/dist/{sessionLock-6qL_N6Rn.cjs → sessionLock-DokGab2R.cjs} +16 -3
  160. package/dist/{sessionLock-ZfvFVdUi.mjs → sessionLock-WYIyaVO4.mjs} +16 -4
  161. package/dist/{sessionOps-CdgbEJgf.mjs → sessionOps-BIGgRube.mjs} +11 -6
  162. package/dist/{sessionOps-Dmt7WUUs.cjs → sessionOps-S-qPBLFf.cjs} +11 -6
  163. package/dist/{sessionScanner-CQL_WSaC.cjs → sessionScanner-CIMM2hvZ.cjs} +3 -3
  164. package/dist/{sessionScanner-CNVaSNOn.mjs → sessionScanner-o5hnMi5e.mjs} +3 -3
  165. package/dist/{sessions-DTomHfFg.mjs → sessions-BJHEZpi2.mjs} +40 -18
  166. package/dist/{sessions-BJblG_Hx.cjs → sessions-JchI1w3z.cjs} +40 -17
  167. package/dist/{setupOfflineReconnection-DVA0VpQL.cjs → setupOfflineReconnection-CNiAt5ZP.cjs} +2 -2
  168. package/dist/{setupOfflineReconnection-C5trsV-j.mjs → setupOfflineReconnection-DMiBO2y9.mjs} +2 -2
  169. package/dist/{spawn-DRyNAqD8.cjs → spawn-6PD7nnoC.cjs} +49 -18
  170. package/dist/{spawn-CQJFgoOq.mjs → spawn-DgvUNYEo.mjs} +49 -18
  171. package/dist/{spawnAgents-CCjJifAW.cjs → spawnAgents-DsXgegHM.cjs} +37 -1
  172. package/dist/spawnAgents-P006Hqio.mjs +79 -0
  173. package/dist/{spawnHappyCLI-B_0RWxD4.mjs → spawnHappyCLI-CCOrEtf9.mjs} +1 -1
  174. package/dist/{spawnHappyCLI-ChniczJ5.cjs → spawnHappyCLI-Dywezwk6.cjs} +1 -1
  175. package/dist/{specialCommands-DkdFJi4N.mjs → specialCommands-DPcd9edP.mjs} +3 -3
  176. package/dist/{specialCommands-DbntVs0d.cjs → specialCommands-QxPpkGKf.cjs} +3 -3
  177. package/dist/{startFileWatcher-UQyTSRAP.mjs → startFileWatcher-DghDOCO4.mjs} +1 -1
  178. package/dist/{startFileWatcher-C292fhra.cjs → startFileWatcher-RECH5qY8.cjs} +1 -1
  179. package/dist/{streamRelay-_qCd873P.mjs → streamRelay-DMV_TXfS.mjs} +1 -1
  180. package/dist/{streamRelay-Cx01enfe.cjs → streamRelay-DlUWKUpo.cjs} +1 -1
  181. package/dist/{sync-C3p7-KLn.mjs → sync-CSe0Rf45.mjs} +1 -1
  182. package/dist/{sync-C7sxoUYG.cjs → sync-D23jbqVa.cjs} +1 -1
  183. package/dist/{systemPrompt-DtibzbRU.cjs → systemPrompt-BOy4kU4P.cjs} +4 -3
  184. package/dist/{systemPrompt-CdHOlyVW.mjs → systemPrompt-CFNKnh65.mjs} +4 -3
  185. package/dist/{teams-ujXr3AFn.mjs → teams-DyY669f0.mjs} +9 -9
  186. package/dist/{teams-D7O7TpVU.cjs → teams-Ov2BDT-A.cjs} +9 -9
  187. package/dist/{time-yEL6wHUF.mjs → time-65DcU6Jx.mjs} +1 -1
  188. package/dist/{time-Dg5tVB8S.cjs → time-CrwIMHta.cjs} +1 -1
  189. package/dist/{titleGenerator-BMEaDTwU.cjs → titleGenerator-CS_rh2_J.cjs} +9 -8
  190. package/dist/{titleGenerator-Dr-BtjzO.mjs → titleGenerator-CiAMxf7s.mjs} +9 -8
  191. package/dist/{todo-CvJe4M2C.mjs → todo-BDONw0sK.mjs} +7 -7
  192. package/dist/{todo-CUcFamGs.cjs → todo-KY-okGii.cjs} +7 -7
  193. package/dist/{tools-BrM3bZW5.mjs → tools-D8-PnZEG.mjs} +1 -1
  194. package/dist/{tools-DDKTB5aQ.cjs → tools-_w52DZG6.cjs} +1 -1
  195. package/dist/{uninstall-7oLuhYIu.cjs → uninstall-8vnLN-IE.cjs} +3 -3
  196. package/dist/{uninstall-CEcMMQGz.mjs → uninstall-_fjGRP9b.mjs} +3 -3
  197. package/dist/webPanels-B2_C2-3g.mjs +49 -0
  198. package/dist/webPanels-FCgFKvVe.cjs +53 -0
  199. package/package.json +7 -7
  200. package/dist/selfCheck-BAvqBwBR.mjs +0 -84
  201. package/dist/sessionModeFile-CWVq8AT9.mjs +0 -32
  202. package/dist/sessionModeFile-CsLtn_PL.cjs +0 -36
  203. package/dist/spawnAgents-BjtmjBvP.mjs +0 -45
@@ -0,0 +1,137 @@
1
+ import axios from 'axios';
2
+ import chalk from 'chalk';
3
+ import { c as configuration } from './configuration-WMi4idBB.mjs';
4
+ import { b as readCredentialsForConfiguredRelay, r as readSettings } from './persistence-Cr-wutZ4.mjs';
5
+ import { n as normalizeWebPanels, w as webPanelPath } from './webPanels-B2_C2-3g.mjs';
6
+ import 'node:fs';
7
+ import 'node:os';
8
+ import 'node:path';
9
+ import './package-BPPebGQF.mjs';
10
+ import 'node:fs/promises';
11
+ import 'zod';
12
+ import './authRelay-DhQhkBvX.mjs';
13
+ import 'node:crypto';
14
+ import 'tweetnacl';
15
+ import './logger-BtVhHnD8.mjs';
16
+ import 'node:util';
17
+
18
+ function frameVerdict(headers) {
19
+ const xfo = headers["x-frame-options"]?.trim().toUpperCase();
20
+ if (xfo === "DENY" || xfo === "SAMEORIGIN") return { embeddable: false, reason: `X-Frame-Options: ${xfo}` };
21
+ const csp = headers["content-security-policy"] ?? "";
22
+ const fa = csp.split(";").map((d) => d.trim()).find((d) => d.toLowerCase().startsWith("frame-ancestors"));
23
+ if (fa) {
24
+ const sources = fa.split(/\s+/).slice(1);
25
+ if (!sources.includes("*") && !sources.some((s) => /veryhappy\.dev/.test(s))) {
26
+ return { embeddable: false, reason: `CSP ${fa}` };
27
+ }
28
+ }
29
+ return { embeddable: true };
30
+ }
31
+ function printPanels(panels, problems) {
32
+ if (panels.length === 0 && problems.length === 0) {
33
+ console.log("No webPanels declared in", configuration.settingsFile);
34
+ console.log(chalk.gray('Add e.g. "webPanels": [{ "id": "desktop", "title": "Desktop", "url": "https://host.example.ts.net:6443/vnc.html" }]'));
35
+ return;
36
+ }
37
+ for (const p of panels) console.log(`${chalk.bold(p.id.padEnd(16))} ${p.title.padEnd(20)} ${p.url}${p.embed ? "" : chalk.gray(" (open in new window)")}`);
38
+ for (const p of problems) console.log(chalk.yellow(`skipped #${p.index}${p.id ? ` (${p.id})` : ""}: ${p.reason}`));
39
+ }
40
+ async function handlePanelsCommand(args) {
41
+ const action = args[0] ?? "list";
42
+ const { panels, problems } = normalizeWebPanels((await readSettings()).webPanels);
43
+ if (action === "list") {
44
+ printPanels(panels, problems);
45
+ return;
46
+ }
47
+ if (action !== "check") {
48
+ console.log(`${chalk.bold("very-happy panels")} \u2014 web panels declared in ${configuration.settingsFile}
49
+
50
+ very-happy panels list Show the declared panels (and skipped entries)
51
+ very-happy panels check Fetch each URL from THIS machine: reachable? embeddable?`);
52
+ return;
53
+ }
54
+ printPanels(panels, problems);
55
+ for (const p of panels) {
56
+ try {
57
+ const res = await axios.get(p.url, { timeout: 8e3, maxRedirects: 3, validateStatus: () => true, responseType: "stream" });
58
+ res.data.destroy?.();
59
+ const headers = Object.fromEntries(Object.entries(res.headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v.join(", ") : v == null ? void 0 : String(v)]));
60
+ const frame = frameVerdict(headers);
61
+ const notes = [
62
+ `HTTP ${res.status}`,
63
+ p.url.startsWith("https:") ? "https" : chalk.yellow("http (only usable from this machine)"),
64
+ frame.embeddable ? "embeddable" : chalk.yellow(`not embeddable (${frame.reason}) \u2192 set "embed": false`)
65
+ ];
66
+ console.log(`${p.id}: ${notes.join(" \xB7 ")}`);
67
+ } catch (error) {
68
+ console.log(chalk.red(`${p.id}: unreachable from this machine (${error instanceof Error ? error.message : String(error)})`));
69
+ }
70
+ }
71
+ console.log(chalk.gray("Reachable here \u2260 reachable from your phone: the viewer's browser connects to the URL directly (tailnet / tunnel + HTTPS)."));
72
+ }
73
+ function parseAttentionArgs(args) {
74
+ let panel;
75
+ let session;
76
+ const words = [];
77
+ for (let i = 0; i < args.length; i++) {
78
+ if (args[i] === "--panel") panel = args[++i];
79
+ else if (args[i] === "--session") session = args[++i];
80
+ else if (args[i].startsWith("--")) return { error: `unknown option ${args[i]}` };
81
+ else words.push(args[i]);
82
+ }
83
+ const reason = words.join(" ").replace(/\s+/g, " ").trim().slice(0, 200);
84
+ if (!reason) return { error: 'usage: very-happy attention "<reason>" [--panel <id>] [--session <sessionId>]' };
85
+ if (args.includes("--panel") && !panel || args.includes("--session") && !session) return { error: "missing value after --panel / --session" };
86
+ return { reason, ...panel ? { panel } : {}, ...session ? { session } : {} };
87
+ }
88
+ async function handleAttentionCommand(args) {
89
+ const parsed = parseAttentionArgs(args);
90
+ if ("error" in parsed) {
91
+ console.error(parsed.error);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ const credentials = await readCredentialsForConfiguredRelay();
96
+ const settings = await readSettings();
97
+ if (!credentials || !settings.machineId) {
98
+ console.error('Not authenticated on this machine. Run "very-happy auth login" first.');
99
+ process.exitCode = 1;
100
+ return;
101
+ }
102
+ let link;
103
+ if (parsed.panel) {
104
+ const { panels } = normalizeWebPanels(settings.webPanels);
105
+ if (panels.some((p) => p.id === parsed.panel)) link = webPanelPath(settings.machineId, parsed.panel);
106
+ else console.error(chalk.yellow(`panel "${parsed.panel}" is not declared in ${configuration.settingsFile}; the notification will not deep-link`));
107
+ }
108
+ if (parsed.session) {
109
+ const { ApiClient } = await import('./api-CzwFpJQc.mjs').then(function (n) { return n.b; });
110
+ const api = await ApiClient.create(credentials);
111
+ const r = await api.push().sendAttention({ sessionId: parsed.session, reason: parsed.reason, url: link ?? `/session/${encodeURIComponent(parsed.session)}` });
112
+ if (!r.sent) {
113
+ console.error(`Failed: ${r.error}`);
114
+ process.exitCode = 1;
115
+ return;
116
+ }
117
+ console.log(r.kind === "attention" ? "Sent." : 'Sent (this server predates "attention"; delivered as a question).');
118
+ return;
119
+ }
120
+ try {
121
+ const res = await axios.post(`${configuration.serverUrl}/v1/webhook/notify`, {
122
+ title: "Needs you",
123
+ message: parsed.reason,
124
+ event: "permission",
125
+ ...link ? { link } : {}
126
+ }, {
127
+ headers: { Authorization: `Bearer ${credentials.token}`, "Content-Type": "application/json", "X-Happy-Client": `cli/${configuration.currentCliVersion}` },
128
+ timeout: 15e3
129
+ });
130
+ console.log(res.data?.delivered ? "Sent (devices + webhook)." : "Sent to devices (no webhook delivery).");
131
+ } catch (error) {
132
+ console.error(`Failed: ${error?.response ? `HTTP ${error.response.status}` : error?.message ?? error}`);
133
+ process.exitCode = 1;
134
+ }
135
+ }
136
+
137
+ export { frameVerdict, handleAttentionCommand, handlePanelsCommand, parseAttentionArgs };
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+ var chalk = require('chalk');
5
+ var configuration = require('./configuration-CMHZnp6G.cjs');
6
+ var persistence = require('./persistence-BegE7NPa.cjs');
7
+ var webPanels = require('./webPanels-FCgFKvVe.cjs');
8
+ require('node:fs');
9
+ require('node:os');
10
+ require('node:path');
11
+ require('./package-SgqTYsjn.cjs');
12
+ require('node:fs/promises');
13
+ require('zod');
14
+ require('./authRelay-Dy1qU9td.cjs');
15
+ require('node:crypto');
16
+ require('tweetnacl');
17
+ require('./logger-C1GWx3gi.cjs');
18
+ require('node:util');
19
+
20
+ function frameVerdict(headers) {
21
+ const xfo = headers["x-frame-options"]?.trim().toUpperCase();
22
+ if (xfo === "DENY" || xfo === "SAMEORIGIN") return { embeddable: false, reason: `X-Frame-Options: ${xfo}` };
23
+ const csp = headers["content-security-policy"] ?? "";
24
+ const fa = csp.split(";").map((d) => d.trim()).find((d) => d.toLowerCase().startsWith("frame-ancestors"));
25
+ if (fa) {
26
+ const sources = fa.split(/\s+/).slice(1);
27
+ if (!sources.includes("*") && !sources.some((s) => /veryhappy\.dev/.test(s))) {
28
+ return { embeddable: false, reason: `CSP ${fa}` };
29
+ }
30
+ }
31
+ return { embeddable: true };
32
+ }
33
+ function printPanels(panels, problems) {
34
+ if (panels.length === 0 && problems.length === 0) {
35
+ console.log("No webPanels declared in", configuration.configuration.settingsFile);
36
+ console.log(chalk.gray('Add e.g. "webPanels": [{ "id": "desktop", "title": "Desktop", "url": "https://host.example.ts.net:6443/vnc.html" }]'));
37
+ return;
38
+ }
39
+ for (const p of panels) console.log(`${chalk.bold(p.id.padEnd(16))} ${p.title.padEnd(20)} ${p.url}${p.embed ? "" : chalk.gray(" (open in new window)")}`);
40
+ for (const p of problems) console.log(chalk.yellow(`skipped #${p.index}${p.id ? ` (${p.id})` : ""}: ${p.reason}`));
41
+ }
42
+ async function handlePanelsCommand(args) {
43
+ const action = args[0] ?? "list";
44
+ const { panels, problems } = webPanels.normalizeWebPanels((await persistence.readSettings()).webPanels);
45
+ if (action === "list") {
46
+ printPanels(panels, problems);
47
+ return;
48
+ }
49
+ if (action !== "check") {
50
+ console.log(`${chalk.bold("very-happy panels")} \u2014 web panels declared in ${configuration.configuration.settingsFile}
51
+
52
+ very-happy panels list Show the declared panels (and skipped entries)
53
+ very-happy panels check Fetch each URL from THIS machine: reachable? embeddable?`);
54
+ return;
55
+ }
56
+ printPanels(panels, problems);
57
+ for (const p of panels) {
58
+ try {
59
+ const res = await axios.get(p.url, { timeout: 8e3, maxRedirects: 3, validateStatus: () => true, responseType: "stream" });
60
+ res.data.destroy?.();
61
+ const headers = Object.fromEntries(Object.entries(res.headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v.join(", ") : v == null ? void 0 : String(v)]));
62
+ const frame = frameVerdict(headers);
63
+ const notes = [
64
+ `HTTP ${res.status}`,
65
+ p.url.startsWith("https:") ? "https" : chalk.yellow("http (only usable from this machine)"),
66
+ frame.embeddable ? "embeddable" : chalk.yellow(`not embeddable (${frame.reason}) \u2192 set "embed": false`)
67
+ ];
68
+ console.log(`${p.id}: ${notes.join(" \xB7 ")}`);
69
+ } catch (error) {
70
+ console.log(chalk.red(`${p.id}: unreachable from this machine (${error instanceof Error ? error.message : String(error)})`));
71
+ }
72
+ }
73
+ console.log(chalk.gray("Reachable here \u2260 reachable from your phone: the viewer's browser connects to the URL directly (tailnet / tunnel + HTTPS)."));
74
+ }
75
+ function parseAttentionArgs(args) {
76
+ let panel;
77
+ let session;
78
+ const words = [];
79
+ for (let i = 0; i < args.length; i++) {
80
+ if (args[i] === "--panel") panel = args[++i];
81
+ else if (args[i] === "--session") session = args[++i];
82
+ else if (args[i].startsWith("--")) return { error: `unknown option ${args[i]}` };
83
+ else words.push(args[i]);
84
+ }
85
+ const reason = words.join(" ").replace(/\s+/g, " ").trim().slice(0, 200);
86
+ if (!reason) return { error: 'usage: very-happy attention "<reason>" [--panel <id>] [--session <sessionId>]' };
87
+ if (args.includes("--panel") && !panel || args.includes("--session") && !session) return { error: "missing value after --panel / --session" };
88
+ return { reason, ...panel ? { panel } : {}, ...session ? { session } : {} };
89
+ }
90
+ async function handleAttentionCommand(args) {
91
+ const parsed = parseAttentionArgs(args);
92
+ if ("error" in parsed) {
93
+ console.error(parsed.error);
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+ const credentials = await persistence.readCredentialsForConfiguredRelay();
98
+ const settings = await persistence.readSettings();
99
+ if (!credentials || !settings.machineId) {
100
+ console.error('Not authenticated on this machine. Run "very-happy auth login" first.');
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ let link;
105
+ if (parsed.panel) {
106
+ const { panels } = webPanels.normalizeWebPanels(settings.webPanels);
107
+ if (panels.some((p) => p.id === parsed.panel)) link = webPanels.webPanelPath(settings.machineId, parsed.panel);
108
+ else console.error(chalk.yellow(`panel "${parsed.panel}" is not declared in ${configuration.configuration.settingsFile}; the notification will not deep-link`));
109
+ }
110
+ if (parsed.session) {
111
+ const { ApiClient } = await Promise.resolve().then(function () { return require('./api-Cmjfr8Z7.cjs'); }).then(function (n) { return n.api; });
112
+ const api = await ApiClient.create(credentials);
113
+ const r = await api.push().sendAttention({ sessionId: parsed.session, reason: parsed.reason, url: link ?? `/session/${encodeURIComponent(parsed.session)}` });
114
+ if (!r.sent) {
115
+ console.error(`Failed: ${r.error}`);
116
+ process.exitCode = 1;
117
+ return;
118
+ }
119
+ console.log(r.kind === "attention" ? "Sent." : 'Sent (this server predates "attention"; delivered as a question).');
120
+ return;
121
+ }
122
+ try {
123
+ const res = await axios.post(`${configuration.configuration.serverUrl}/v1/webhook/notify`, {
124
+ title: "Needs you",
125
+ message: parsed.reason,
126
+ event: "permission",
127
+ ...link ? { link } : {}
128
+ }, {
129
+ headers: { Authorization: `Bearer ${credentials.token}`, "Content-Type": "application/json", "X-Happy-Client": `cli/${configuration.configuration.currentCliVersion}` },
130
+ timeout: 15e3
131
+ });
132
+ console.log(res.data?.delivered ? "Sent (devices + webhook)." : "Sent to devices (no webhook delivery).");
133
+ } catch (error) {
134
+ console.error(`Failed: ${error?.response ? `HTTP ${error.response.status}` : error?.message ?? error}`);
135
+ process.exitCode = 1;
136
+ }
137
+ }
138
+
139
+ exports.frameVerdict = frameVerdict;
140
+ exports.handleAttentionCommand = handleAttentionCommand;
141
+ exports.handlePanelsCommand = handlePanelsCommand;
142
+ exports.parseAttentionArgs = parseAttentionArgs;
@@ -2,12 +2,13 @@ import { randomUUID } from 'node:crypto';
2
2
  import { userInfo, hostname } from 'node:os';
3
3
  import { z } from 'zod';
4
4
  import { i as isValidSessionId } from './ids-DU6sLx4C.mjs';
5
- import { g as listDaemonPeers } from './controlClient-C4xB5J6O.mjs';
6
- import { b as readPersistedSessions } from './persistence-C_tYtOqK.mjs';
7
- import { s as sessionWebUrl, e as sendUserMessage } from './sessionOps-CdgbEJgf.mjs';
8
- import { b as sendRemotePeerMessage, d as deliverToSession, a as listRemotePeers } from './remoteSessionClient-D01Hso2-.mjs';
5
+ import { g as listDaemonPeers } from './controlClient-BWUJrj3t.mjs';
6
+ import { c as readPersistedSessions } from './persistence-Cr-wutZ4.mjs';
7
+ import { s as sessionWebUrl, e as sendUserMessage } from './sessionOps-BIGgRube.mjs';
8
+ import { a as listRemotePeers, b as sendRemotePeerMessage, d as deliverToSession } from './remoteSessionClient-Bn2qu5C5.mjs';
9
9
  import { existsSync, readFileSync, statSync } from 'node:fs';
10
10
  import { resolve, isAbsolute, dirname } from 'node:path';
11
+ import { P as ProjectsUnsupportedError, s as sharedProjectsClient } from './client-BhoEBnuN.mjs';
11
12
 
12
13
  const SESSION_PEER_SENT_FROM = "session-peer";
13
14
  const CLI_PEER_SENDER_ID = "cli";
@@ -110,7 +111,7 @@ const SESSION_PEER_TOOL_SCHEMAS = {
110
111
  session_message: {
111
112
  title: "Message Another Session",
112
113
  readOnly: false,
113
- description: "Send a message to another agent session (find ids with session_peers). It lands in that session's chat with your title, session id and cwd, and tells the peer how to reply to you. Use it to coordinate when you both edit the same files: say what you are changing and ask what they are changing. Returns as soon as the message is queued; the reply arrives later as a message from that session. Sessions on this machine are reached directly; a session spawned by another machine of this account is routed to that machine's daemon (it must be online). Terminal-mirror sessions cannot receive messages.",
114
+ description: "Send a message to another agent session (find ids with session_peers). It lands in that session's chat with your title, session id and cwd, and tells the peer how to reply to you. Use it to coordinate when you both edit the same files: say what you are changing and ask what they are changing. Returns as soon as the message is queued; the reply arrives later as a message from that session. Sessions on this machine are reached directly; a session spawned by another machine of this account is routed to that machine's daemon (it must be online). If the target is not running but is in your Very Happy project, the message is stored and read at its next turn (stored: true). Terminal-mirror sessions cannot receive messages.",
114
115
  inputSchema: {
115
116
  to: z.string().min(1).max(128).describe("Target session id (from session_peers, or a peer's message header)"),
116
117
  body: z.string().min(1).max(16e3).describe("The message text"),
@@ -121,9 +122,9 @@ const SESSION_PEER_TOOL_SCHEMAS = {
121
122
  session_peers: {
122
123
  title: "List Peer Sessions",
123
124
  readOnly: true,
124
- description: 'List the other agent sessions running on this machine and the files each edited in the last 30 minutes. Default scope "repo": sessions in the same git repository, including its other worktrees (sameWorktree says whether they share your checkout). "cwd": the same directory only. "machine": every live session here. With machineId, list the live sessions on that other machine of this account instead (scope "machine" unless you pass a cwd that exists there).',
125
+ description: 'List the other agent sessions running on this machine and the files each edited in the last 30 minutes. Default scope "repo": sessions in the same git repository, including its other worktrees (sameWorktree says whether they share your checkout). "cwd": the same directory only. "machine": every live session here. "project": the other sessions of your Very Happy project on any machine (title, agent, what it is doing). With machineId, list the live sessions on that other machine of this account instead (scope "machine" unless you pass a cwd that exists there).',
125
126
  inputSchema: {
126
- scope: z.enum(["repo", "cwd", "machine"]).optional().describe("repo (default) | cwd | machine"),
127
+ scope: z.enum(["repo", "cwd", "machine", "project"]).optional().describe("repo (default) | cwd | machine | project"),
127
128
  machineId: z.string().min(1).max(128).optional().describe("Another machine of this account (from the web's machine list) to list instead of this one"),
128
129
  cwd: z.string().min(1).max(4096).optional().describe("With machineId: a directory on that machine to compute repo/cwd scope from")
129
130
  }
@@ -131,6 +132,31 @@ const SESSION_PEER_TOOL_SCHEMAS = {
131
132
  };
132
133
  const PEER_MESSAGE_RATE_LIMIT = 8;
133
134
  const PEER_MESSAGE_RATE_WINDOW_MS = 10 * 6e4;
135
+ async function defaultStoreOffline(args) {
136
+ const client = sharedProjectsClient();
137
+ try {
138
+ const [mine, theirs] = await Promise.all([client.membership(args.from), client.membership(args.to)]);
139
+ if (!mine.projectId || mine.projectId !== theirs.projectId) return null;
140
+ const { message } = await client.message(mine.projectId, { fromSessionId: args.from, toSessionId: args.to, body: args.body });
141
+ return { projectId: mine.projectId, messageId: message.id };
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+ const OFFLINE_STORED_NOTE = "stored, will be read at recipient's next turn";
147
+ async function storeOfflineOrThrow(context, self, to, body, original) {
148
+ const stored = await (context.storeOffline ?? defaultStoreOffline)({ from: self.sessionId, to, body });
149
+ if (!stored) throw original;
150
+ return { delivered: false, stored: true, status: "offline-stored", messageId: stored.messageId.slice(0, 8), to, url: sessionWebUrl(to), note: OFFLINE_STORED_NOTE };
151
+ }
152
+ async function defaultProjectPeers(sessionId) {
153
+ const ctx = await sharedProjectsClient().context(sessionId, { full: true, peek: true });
154
+ return {
155
+ scope: "project",
156
+ project: ctx.project ? { id: ctx.project.id, name: ctx.project.name } : null,
157
+ peers: ctx.sessions.map((s) => ({ sessionId: s.sessionId, title: s.title, agent: s.agent, machine: s.machine, branch: s.branch, state: s.state, doing: s.doing, updatedAt: s.updatedAt, url: sessionWebUrl(s.sessionId) }))
158
+ };
159
+ }
134
160
  async function listPeerSessions(context, scope = "repo") {
135
161
  const self = context.self();
136
162
  const repoOf = context.repoOf ?? resolveRepoIdentity;
@@ -177,12 +203,28 @@ async function sendPeerMessage(context, args) {
177
203
  const budget = checkPeerSendBudget(context.sentTo, args.to, now);
178
204
  if (!budget.ok) throw new Error(replyLoopRefusal(budget.count, args.to));
179
205
  }
180
- const result2 = await remote({ to: args.to, body, replyTo: args.replyTo, from: self, machineId: args.machineId });
206
+ let result2;
207
+ try {
208
+ result2 = await remote({ to: args.to, body, replyTo: args.replyTo, from: self, machineId: args.machineId });
209
+ } catch (error) {
210
+ const stored = await storeOfflineOrThrow(context, self, args.to, body, error instanceof Error ? error : new Error(String(error)));
211
+ context.sentTo?.get(args.to)?.push(now);
212
+ return stored;
213
+ }
181
214
  context.sentTo?.get(args.to)?.push(now);
182
215
  return result2;
183
216
  }
184
217
  const live = (await (context.listPeers ?? listDaemonPeers)()).find((session) => session.sessionId === args.to);
185
- if (!live) throw new Error(`Session ${args.to} is not running on this machine; nothing would read the message`);
218
+ if (!live) {
219
+ const notRunning = new Error(`Session ${args.to} is not running on this machine; nothing would read the message`);
220
+ if (context.sentTo) {
221
+ const budget = checkPeerSendBudget(context.sentTo, args.to, now);
222
+ if (!budget.ok) throw new Error(replyLoopRefusal(budget.count, args.to));
223
+ }
224
+ const stored = await storeOfflineOrThrow(context, self, args.to, body, notRunning);
225
+ context.sentTo?.get(args.to)?.push(now);
226
+ return stored;
227
+ }
186
228
  if (live.kind === "mirror") throw new Error(`Session ${args.to} is a terminal mirror (a person typing claude in a terminal); it cannot receive messages`);
187
229
  if (context.sentTo) {
188
230
  const budget = checkPeerSendBudget(context.sentTo, args.to, now);
@@ -216,6 +258,14 @@ async function executeSessionPeerTool(name, args, context) {
216
258
  });
217
259
  case "session_peers": {
218
260
  const scope = args?.scope;
261
+ if (scope === "project") {
262
+ try {
263
+ return await (context.projectPeers ?? defaultProjectPeers)(context.self().sessionId);
264
+ } catch (error) {
265
+ if (error instanceof ProjectsUnsupportedError) throw new Error('This Very Happy server does not support projects yet; use scope "repo"');
266
+ throw error;
267
+ }
268
+ }
219
269
  if (scope !== void 0 && !PEER_SCOPES.includes(scope)) throw new Error(`scope must be one of ${PEER_SCOPES.join(", ")}`);
220
270
  if (args?.machineId) {
221
271
  return (context.remotePeers ?? listRemotePeers)(String(args.machineId), {
@@ -269,4 +319,4 @@ function registerSessionPeerTools(server, execute) {
269
319
  }
270
320
  }
271
321
 
272
- export { CLI_PEER_SENDER_ID as C, PEER_SCOPES as P, SESSION_PEER_TOOL_NAMES as S, createSessionPeerToolExecutor as a, SESSION_PEER_SENT_FROM as b, cliPeerSelf as c, formatEditConflictNotice as f, listPeerSessions as l, registerSessionPeerTools as r, sendPeerMessage as s };
322
+ export { CLI_PEER_SENDER_ID as C, PEER_SCOPES as P, SESSION_PEER_TOOL_NAMES as S, createSessionPeerToolExecutor as a, SESSION_PEER_SENT_FROM as b, cliPeerSelf as c, executeSessionPeerTool as e, formatEditConflictNotice as f, listPeerSessions as l, registerSessionPeerTools as r, sendPeerMessage as s };
@@ -4,12 +4,13 @@ var node_crypto = require('node:crypto');
4
4
  var os = require('node:os');
5
5
  var z = require('zod');
6
6
  var ids = require('./ids-B6ZC0HGt.cjs');
7
- var controlClient = require('./controlClient-SX8gSza2.cjs');
8
- var persistence = require('./persistence-DXbKOHxl.cjs');
9
- var sessionOps = require('./sessionOps-Dmt7WUUs.cjs');
10
- var remoteSessionClient = require('./remoteSessionClient-B7NvjHD3.cjs');
7
+ var controlClient = require('./controlClient-C-5qCt8W.cjs');
8
+ var persistence = require('./persistence-BegE7NPa.cjs');
9
+ var sessionOps = require('./sessionOps-S-qPBLFf.cjs');
10
+ var remoteSessionClient = require('./remoteSessionClient-B7i7MkYV.cjs');
11
11
  var node_fs = require('node:fs');
12
12
  var path = require('node:path');
13
+ var client = require('./client-ZjOsn6nZ.cjs');
13
14
 
14
15
  const SESSION_PEER_SENT_FROM = "session-peer";
15
16
  const CLI_PEER_SENDER_ID = "cli";
@@ -112,7 +113,7 @@ const SESSION_PEER_TOOL_SCHEMAS = {
112
113
  session_message: {
113
114
  title: "Message Another Session",
114
115
  readOnly: false,
115
- description: "Send a message to another agent session (find ids with session_peers). It lands in that session's chat with your title, session id and cwd, and tells the peer how to reply to you. Use it to coordinate when you both edit the same files: say what you are changing and ask what they are changing. Returns as soon as the message is queued; the reply arrives later as a message from that session. Sessions on this machine are reached directly; a session spawned by another machine of this account is routed to that machine's daemon (it must be online). Terminal-mirror sessions cannot receive messages.",
116
+ description: "Send a message to another agent session (find ids with session_peers). It lands in that session's chat with your title, session id and cwd, and tells the peer how to reply to you. Use it to coordinate when you both edit the same files: say what you are changing and ask what they are changing. Returns as soon as the message is queued; the reply arrives later as a message from that session. Sessions on this machine are reached directly; a session spawned by another machine of this account is routed to that machine's daemon (it must be online). If the target is not running but is in your Very Happy project, the message is stored and read at its next turn (stored: true). Terminal-mirror sessions cannot receive messages.",
116
117
  inputSchema: {
117
118
  to: z.z.string().min(1).max(128).describe("Target session id (from session_peers, or a peer's message header)"),
118
119
  body: z.z.string().min(1).max(16e3).describe("The message text"),
@@ -123,9 +124,9 @@ const SESSION_PEER_TOOL_SCHEMAS = {
123
124
  session_peers: {
124
125
  title: "List Peer Sessions",
125
126
  readOnly: true,
126
- description: 'List the other agent sessions running on this machine and the files each edited in the last 30 minutes. Default scope "repo": sessions in the same git repository, including its other worktrees (sameWorktree says whether they share your checkout). "cwd": the same directory only. "machine": every live session here. With machineId, list the live sessions on that other machine of this account instead (scope "machine" unless you pass a cwd that exists there).',
127
+ description: 'List the other agent sessions running on this machine and the files each edited in the last 30 minutes. Default scope "repo": sessions in the same git repository, including its other worktrees (sameWorktree says whether they share your checkout). "cwd": the same directory only. "machine": every live session here. "project": the other sessions of your Very Happy project on any machine (title, agent, what it is doing). With machineId, list the live sessions on that other machine of this account instead (scope "machine" unless you pass a cwd that exists there).',
127
128
  inputSchema: {
128
- scope: z.z.enum(["repo", "cwd", "machine"]).optional().describe("repo (default) | cwd | machine"),
129
+ scope: z.z.enum(["repo", "cwd", "machine", "project"]).optional().describe("repo (default) | cwd | machine | project"),
129
130
  machineId: z.z.string().min(1).max(128).optional().describe("Another machine of this account (from the web's machine list) to list instead of this one"),
130
131
  cwd: z.z.string().min(1).max(4096).optional().describe("With machineId: a directory on that machine to compute repo/cwd scope from")
131
132
  }
@@ -133,6 +134,31 @@ const SESSION_PEER_TOOL_SCHEMAS = {
133
134
  };
134
135
  const PEER_MESSAGE_RATE_LIMIT = 8;
135
136
  const PEER_MESSAGE_RATE_WINDOW_MS = 10 * 6e4;
137
+ async function defaultStoreOffline(args) {
138
+ const client$1 = client.sharedProjectsClient();
139
+ try {
140
+ const [mine, theirs] = await Promise.all([client$1.membership(args.from), client$1.membership(args.to)]);
141
+ if (!mine.projectId || mine.projectId !== theirs.projectId) return null;
142
+ const { message } = await client$1.message(mine.projectId, { fromSessionId: args.from, toSessionId: args.to, body: args.body });
143
+ return { projectId: mine.projectId, messageId: message.id };
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+ const OFFLINE_STORED_NOTE = "stored, will be read at recipient's next turn";
149
+ async function storeOfflineOrThrow(context, self, to, body, original) {
150
+ const stored = await (context.storeOffline ?? defaultStoreOffline)({ from: self.sessionId, to, body });
151
+ if (!stored) throw original;
152
+ return { delivered: false, stored: true, status: "offline-stored", messageId: stored.messageId.slice(0, 8), to, url: sessionOps.sessionWebUrl(to), note: OFFLINE_STORED_NOTE };
153
+ }
154
+ async function defaultProjectPeers(sessionId) {
155
+ const ctx = await client.sharedProjectsClient().context(sessionId, { full: true, peek: true });
156
+ return {
157
+ scope: "project",
158
+ project: ctx.project ? { id: ctx.project.id, name: ctx.project.name } : null,
159
+ peers: ctx.sessions.map((s) => ({ sessionId: s.sessionId, title: s.title, agent: s.agent, machine: s.machine, branch: s.branch, state: s.state, doing: s.doing, updatedAt: s.updatedAt, url: sessionOps.sessionWebUrl(s.sessionId) }))
160
+ };
161
+ }
136
162
  async function listPeerSessions(context, scope = "repo") {
137
163
  const self = context.self();
138
164
  const repoOf = context.repoOf ?? resolveRepoIdentity;
@@ -179,12 +205,28 @@ async function sendPeerMessage(context, args) {
179
205
  const budget = checkPeerSendBudget(context.sentTo, args.to, now);
180
206
  if (!budget.ok) throw new Error(replyLoopRefusal(budget.count, args.to));
181
207
  }
182
- const result2 = await remote({ to: args.to, body, replyTo: args.replyTo, from: self, machineId: args.machineId });
208
+ let result2;
209
+ try {
210
+ result2 = await remote({ to: args.to, body, replyTo: args.replyTo, from: self, machineId: args.machineId });
211
+ } catch (error) {
212
+ const stored = await storeOfflineOrThrow(context, self, args.to, body, error instanceof Error ? error : new Error(String(error)));
213
+ context.sentTo?.get(args.to)?.push(now);
214
+ return stored;
215
+ }
183
216
  context.sentTo?.get(args.to)?.push(now);
184
217
  return result2;
185
218
  }
186
219
  const live = (await (context.listPeers ?? controlClient.listDaemonPeers)()).find((session) => session.sessionId === args.to);
187
- if (!live) throw new Error(`Session ${args.to} is not running on this machine; nothing would read the message`);
220
+ if (!live) {
221
+ const notRunning = new Error(`Session ${args.to} is not running on this machine; nothing would read the message`);
222
+ if (context.sentTo) {
223
+ const budget = checkPeerSendBudget(context.sentTo, args.to, now);
224
+ if (!budget.ok) throw new Error(replyLoopRefusal(budget.count, args.to));
225
+ }
226
+ const stored = await storeOfflineOrThrow(context, self, args.to, body, notRunning);
227
+ context.sentTo?.get(args.to)?.push(now);
228
+ return stored;
229
+ }
188
230
  if (live.kind === "mirror") throw new Error(`Session ${args.to} is a terminal mirror (a person typing claude in a terminal); it cannot receive messages`);
189
231
  if (context.sentTo) {
190
232
  const budget = checkPeerSendBudget(context.sentTo, args.to, now);
@@ -218,6 +260,14 @@ async function executeSessionPeerTool(name, args, context) {
218
260
  });
219
261
  case "session_peers": {
220
262
  const scope = args?.scope;
263
+ if (scope === "project") {
264
+ try {
265
+ return await (context.projectPeers ?? defaultProjectPeers)(context.self().sessionId);
266
+ } catch (error) {
267
+ if (error instanceof client.ProjectsUnsupportedError) throw new Error('This Very Happy server does not support projects yet; use scope "repo"');
268
+ throw error;
269
+ }
270
+ }
221
271
  if (scope !== void 0 && !PEER_SCOPES.includes(scope)) throw new Error(`scope must be one of ${PEER_SCOPES.join(", ")}`);
222
272
  if (args?.machineId) {
223
273
  return (context.remotePeers ?? remoteSessionClient.listRemotePeers)(String(args.machineId), {
@@ -277,6 +327,7 @@ exports.SESSION_PEER_SENT_FROM = SESSION_PEER_SENT_FROM;
277
327
  exports.SESSION_PEER_TOOL_NAMES = SESSION_PEER_TOOL_NAMES;
278
328
  exports.cliPeerSelf = cliPeerSelf;
279
329
  exports.createSessionPeerToolExecutor = createSessionPeerToolExecutor;
330
+ exports.executeSessionPeerTool = executeSessionPeerTool;
280
331
  exports.formatEditConflictNotice = formatEditConflictNotice;
281
332
  exports.listPeerSessions = listPeerSessions;
282
333
  exports.registerSessionPeerTools = registerSessionPeerTools;
@@ -2,10 +2,10 @@
2
2
 
3
3
  var promises = require('node:fs/promises');
4
4
  var node_fs = require('node:fs');
5
- var configuration = require('./configuration-DZPy0T9Z.cjs');
5
+ var configuration = require('./configuration-CMHZnp6G.cjs');
6
6
  var z = require('zod');
7
- var authRelay = require('./authRelay-BdYkC1Po.cjs');
8
- var logger = require('./logger-LvwVeATU.cjs');
7
+ var authRelay = require('./authRelay-Dy1qU9td.cjs');
8
+ var logger = require('./logger-C1GWx3gi.cjs');
9
9
 
10
10
  function _interopNamespaceDefault(e) {
11
11
  var n = Object.create(null);
@@ -306,6 +306,15 @@ async function writeCredentialsDataKey(credentials) {
306
306
  authServerUrl: configuration.configuration.serverUrl
307
307
  }, null, 2));
308
308
  }
309
+ async function rewriteCredentialsForRelay(credentials, next) {
310
+ await configuration.ensurePrivateDirectory(configuration.configuration.happyHomeDir);
311
+ const body = credentials.encryption.type === "legacy" ? { secret: authRelay.encodeBase64(credentials.encryption.secret), token: next.token, authServerUrl: next.authServerUrl } : {
312
+ encryption: { publicKey: authRelay.encodeBase64(credentials.encryption.publicKey), machineKey: authRelay.encodeBase64(credentials.encryption.machineKey) },
313
+ token: next.token,
314
+ authServerUrl: next.authServerUrl
315
+ };
316
+ await configuration.writePrivateFile(configuration.configuration.privateKeyFile, JSON.stringify(body, null, 2));
317
+ }
309
318
  async function clearCredentials() {
310
319
  if (node_fs.existsSync(configuration.configuration.privateKeyFile)) {
311
320
  await promises.unlink(configuration.configuration.privateKeyFile);
@@ -481,6 +490,7 @@ var persistence = /*#__PURE__*/Object.freeze({
481
490
  readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
482
491
  readTrackedCodexThreadIds: readTrackedCodexThreadIds,
483
492
  releaseDaemonLock: releaseDaemonLock,
493
+ rewriteCredentialsForRelay: rewriteCredentialsForRelay,
484
494
  updateSettings: updateSettings,
485
495
  writeCredentialsDataKey: writeCredentialsDataKey,
486
496
  writeCredentialsLegacy: writeCredentialsLegacy,
@@ -515,6 +525,7 @@ exports.releaseDaemonLock = releaseDaemonLock;
515
525
  exports.reportSignature = reportSignature;
516
526
  exports.resolveCheckMs = resolveCheckMs;
517
527
  exports.resolveIdleMs = resolveIdleMs;
528
+ exports.rewriteCredentialsForRelay = rewriteCredentialsForRelay;
518
529
  exports.settingIdleMs = settingIdleMs;
519
530
  exports.updateSettings = updateSettings;
520
531
  exports.writeCredentialsDataKey = writeCredentialsDataKey;
@@ -1,9 +1,9 @@
1
1
  import { readFile, open, unlink, stat, rename } from 'node:fs/promises';
2
2
  import { existsSync, constants, readFileSync, unlinkSync, renameSync } from 'node:fs';
3
- import { c as configuration, P as PRIVATE_FILE_MODE, w as writePrivateFileSync, a as hardenPrivateFileSync, e as ensurePrivateDirectory, b as writePrivateFile, h as hardenPrivateFile } from './configuration-D55F0t16.mjs';
3
+ import { c as configuration, P as PRIVATE_FILE_MODE, w as writePrivateFileSync, a as hardenPrivateFileSync, e as ensurePrivateDirectory, b as writePrivateFile, h as hardenPrivateFile } from './configuration-WMi4idBB.mjs';
4
4
  import * as z from 'zod';
5
- import { c as credentialRelayProblem, e as encodeBase64 } from './authRelay-DSAvg1uv.mjs';
6
- import { l as logger } from './logger-13cBr_ew.mjs';
5
+ import { c as credentialRelayProblem, e as encodeBase64 } from './authRelay-DhQhkBvX.mjs';
6
+ import { l as logger } from './logger-BtVhHnD8.mjs';
7
7
 
8
8
  const HIBERNATION_CAPABILITY = "agent-hibernation-v1";
9
9
  const HOUR_MS = 60 * 60 * 1e3;
@@ -285,6 +285,15 @@ async function writeCredentialsDataKey(credentials) {
285
285
  authServerUrl: configuration.serverUrl
286
286
  }, null, 2));
287
287
  }
288
+ async function rewriteCredentialsForRelay(credentials, next) {
289
+ await ensurePrivateDirectory(configuration.happyHomeDir);
290
+ const body = credentials.encryption.type === "legacy" ? { secret: encodeBase64(credentials.encryption.secret), token: next.token, authServerUrl: next.authServerUrl } : {
291
+ encryption: { publicKey: encodeBase64(credentials.encryption.publicKey), machineKey: encodeBase64(credentials.encryption.machineKey) },
292
+ token: next.token,
293
+ authServerUrl: next.authServerUrl
294
+ };
295
+ await writePrivateFile(configuration.privateKeyFile, JSON.stringify(body, null, 2));
296
+ }
288
297
  async function clearCredentials() {
289
298
  if (existsSync(configuration.privateKeyFile)) {
290
299
  await unlink(configuration.privateKeyFile);
@@ -460,6 +469,7 @@ var persistence = /*#__PURE__*/Object.freeze({
460
469
  readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
461
470
  readTrackedCodexThreadIds: readTrackedCodexThreadIds,
462
471
  releaseDaemonLock: releaseDaemonLock,
472
+ rewriteCredentialsForRelay: rewriteCredentialsForRelay,
463
473
  updateSettings: updateSettings,
464
474
  writeCredentialsDataKey: writeCredentialsDataKey,
465
475
  writeCredentialsLegacy: writeCredentialsLegacy,
@@ -467,4 +477,4 @@ var persistence = /*#__PURE__*/Object.freeze({
467
477
  writeSettings: writeSettings
468
478
  });
469
479
 
470
- export { writeDaemonState as A, writeSettings as B, settingIdleMs as C, releaseDaemonLock as D, persistence as E, HIBERNATION_CAPABILITY as H, SandboxConfigSchema as S, resolveCheckMs as a, readPersistedSessions as b, readCredentialsForConfiguredRelay as c, readDaemonState as d, evaluateWrapperIdle as e, clearDaemonState as f, readCredentials as g, clearCredentials as h, clearMachineId as i, HIBERNATION_UNSUPPORTED_BACKOFF_MS as j, hibernationReplaySeq as k, latchKeepAwake as l, readSettingsOutcome as m, writeCredentialsDataKey as n, readTrackedClaudeSessionIds as o, readTrackedCodexThreadIds as p, reportSignature as q, readSettings as r, decideDaemonHibernation as s, nextCheckDelay as t, updateSettings as u, persistSession as v, writeCredentialsLegacy as w, acquireDaemonLock as x, resolveIdleMs as y, deletePersistedSessions as z };
480
+ export { writeDaemonState as A, writeSettings as B, settingIdleMs as C, rewriteCredentialsForRelay as D, releaseDaemonLock as E, persistence as F, HIBERNATION_CAPABILITY as H, SandboxConfigSchema as S, resolveCheckMs as a, readCredentialsForConfiguredRelay as b, readPersistedSessions as c, readDaemonState as d, evaluateWrapperIdle as e, clearDaemonState as f, readCredentials as g, clearCredentials as h, clearMachineId as i, HIBERNATION_UNSUPPORTED_BACKOFF_MS as j, readSettingsOutcome as k, latchKeepAwake as l, writeCredentialsDataKey as m, hibernationReplaySeq as n, readTrackedClaudeSessionIds as o, readTrackedCodexThreadIds as p, reportSignature as q, readSettings as r, decideDaemonHibernation as s, nextCheckDelay as t, updateSettings as u, persistSession as v, writeCredentialsLegacy as w, acquireDaemonLock as x, resolveIdleMs as y, deletePersistedSessions as z };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var sessionModeFile = require('./sessionModeFile-CsLtn_PL.cjs');
3
+ var acpSessionResume = require('./acpSessionResume-oiCBnAVA.cjs');
4
4
 
5
5
  function sanitizeSpawnModel(value) {
6
6
  if (typeof value !== "string") return null;
@@ -62,8 +62,14 @@ function parsePiRunnerArgs(args) {
62
62
  parsed.model = model;
63
63
  continue;
64
64
  }
65
+ if (arg === "--resume") {
66
+ const id = acpSessionResume.sanitizeAgentSessionId(args[++i]);
67
+ if (!id) throw new Error("Invalid --resume session id");
68
+ parsed.resume = id;
69
+ continue;
70
+ }
65
71
  if (arg === "--permission-mode") {
66
- const mode = sessionModeFile.normalizeAcpPermissionMode(args[++i]);
72
+ const mode = acpSessionResume.normalizeAcpPermissionMode(args[++i]);
67
73
  if (mode) parsed.permissionMode = mode;
68
74
  continue;
69
75
  }