shadow-claw 1.51.0 → 1.52.1

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 (378) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +10 -10
  4. package/dist/cli/build/build.js +1 -1
  5. package/dist/cli/{chunk-Dtse8rAn.js → chunk-5ryVpyup.js} +1 -1
  6. package/dist/cli/chunk-Aq3057Xo.js +1 -0
  7. package/dist/cli/{chunk-4CZJpLqA.js → chunk-BV9JBYpl.js} +2 -2
  8. package/dist/cli/{chunk-mbFAP9Ev.js → chunk-BZI3bWDA.js} +1 -1
  9. package/dist/cli/chunk-BaUCHRPD.js +1 -0
  10. package/dist/cli/{chunk-vzUrhBN5.js → chunk-BdbEt6gM.js} +1 -1
  11. package/dist/cli/{chunk-DYq-orHK.js → chunk-BgTv9w0K.js} +1 -1
  12. package/dist/cli/{chunk-BU8UG4L_.js → chunk-BiWLawPV.js} +1 -1
  13. package/dist/cli/chunk-BkLnHJeo.js +1 -0
  14. package/dist/cli/{chunk-BqfPWIBf.js → chunk-BzWY1sKE.js} +1 -1
  15. package/dist/cli/{chunk-DajM7Kro.js → chunk-C79eIU-4.js} +1 -1
  16. package/dist/cli/{chunk-CI9r1AM5.js → chunk-CDw214Zi.js} +1 -1
  17. package/dist/cli/chunk-CNqc7LLn.js +1 -0
  18. package/dist/cli/{chunk-CEhU-Ah-.js → chunk-CWW7dqIv.js} +1 -1
  19. package/dist/cli/{chunk-BT_C3OlA.js → chunk-CcSTYHtR.js} +1 -1
  20. package/dist/cli/chunk-ClOfk6Ga.js +1 -0
  21. package/dist/cli/chunk-ComXUwW0.js +1 -0
  22. package/dist/cli/{chunk-lL1BhHVe.js → chunk-CooeO4UQ.js} +1 -1
  23. package/dist/cli/{chunk-khr7--yI.js → chunk-CtfWY4G6.js} +1 -1
  24. package/dist/cli/{chunk-C4iW1XuQ.js → chunk-DFgSiYMW.js} +1 -1
  25. package/dist/cli/chunk-DGBnjngK.js +1 -0
  26. package/dist/cli/{chunk-D7bFIUK3.js → chunk-DGu1OhJs.js} +1 -1
  27. package/dist/cli/{chunk-CzfOerQR.js → chunk-DIjCY0Wf.js} +1 -1
  28. package/dist/cli/{chunk-D-iqEY5G.js → chunk-DJ_LeQtz.js} +1 -1
  29. package/dist/cli/{chunk-BmEsDkRK.js → chunk-DQRu-9wO.js} +1 -1
  30. package/dist/cli/{chunk-DojiYody.js → chunk-DToVgO9a.js} +1 -1
  31. package/dist/cli/{chunk-De5a_Rlp.js → chunk-DVbNkQRL.js} +1 -1
  32. package/dist/cli/{chunk-c8MIYB2y.js → chunk-Ddtzaj43.js} +1 -1
  33. package/dist/cli/{chunk-aJ6fufe-.js → chunk-DiqS56rD.js} +1 -1
  34. package/dist/cli/{chunk-CeUEmZNt.js → chunk-DnRcu9b4.js} +1 -1
  35. package/dist/cli/chunk-DtgZDsJA.js +1 -0
  36. package/dist/cli/{chunk-DVMcK7g6.js → chunk-Du4qvxo_.js} +2 -2
  37. package/dist/cli/{chunk-CegcvNc9.js → chunk-Dz5_hgvL.js} +8 -8
  38. package/dist/cli/{chunk-Cd4Dv5Eq.js → chunk-I3mxEelq.js} +1 -1
  39. package/dist/cli/{chunk-Do3VICh1.js → chunk-IU2SH_ti.js} +1 -1
  40. package/dist/cli/{chunk-BQveaAaD.js → chunk-NC8XsSSA.js} +1 -1
  41. package/dist/cli/{chunk-D702W_Ry.js → chunk-Q4BZ9QN9.js} +1 -1
  42. package/dist/cli/chunk-WTlDd4Ou.js +1 -0
  43. package/dist/cli/chunk-XGzq5joC.js +1 -0
  44. package/dist/cli/{chunk-ar7eAlxK.js → chunk-aJfaGe2s.js} +8 -8
  45. package/dist/cli/chunk-bPVrFlmk.js +1 -0
  46. package/dist/cli/{chunk-C6jii2Fl.js → chunk-d5hIhj8t.js} +1 -1
  47. package/dist/cli/chunk-jXJHKxdR.js +1 -0
  48. package/dist/cli/{chunk-C3dTAmV_.js → chunk-rstVU7U0.js} +1 -1
  49. package/dist/cli/{chunk-DGfTzQui.js → chunk-t72uObQ7.js} +1 -1
  50. package/dist/cli/{chunk-DDgfljzN.js → chunk-vq8xlroY.js} +1 -1
  51. package/dist/cli/cli.js +1 -1
  52. package/dist/headless-agent.js +1 -1
  53. package/dist/lib/components/common/shadow-claw-provider-model-picker.js +1 -1
  54. package/dist/lib/components/index.js +27 -27
  55. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  56. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  57. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  58. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  59. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  60. package/dist/lib/components/settings/shadow-claw-llm/model-ranking.d.ts +12 -0
  61. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  62. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  63. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  64. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  65. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  66. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  67. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  68. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  69. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  70. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  71. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  72. package/dist/lib/components/shadow-claw-channels.js +1 -1
  73. package/dist/lib/components/shadow-claw-chat/shadow-claw-chat.d.ts +1 -0
  74. package/dist/lib/components/shadow-claw-chat.js +1 -1
  75. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  76. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  77. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  78. package/dist/lib/components/shadow-claw-files.js +1 -1
  79. package/dist/lib/components/shadow-claw-git.js +1 -1
  80. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  81. package/dist/lib/components/shadow-claw-llm.js +1 -1
  82. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  83. package/dist/lib/components/shadow-claw-networking.js +1 -1
  84. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  85. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  86. package/dist/lib/components/shadow-claw-pages.js +1 -1
  87. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  88. package/dist/lib/components/shadow-claw-provider-model-picker.js +1 -1
  89. package/dist/lib/components/shadow-claw-settings.js +1 -1
  90. package/dist/lib/components/shadow-claw-storage.js +1 -1
  91. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  92. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  93. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  94. package/dist/lib/components/shadow-claw-tools.js +1 -1
  95. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  96. package/dist/lib/components/shadow-claw.js +1 -1
  97. package/dist/lib/config/config.d.ts +2 -1
  98. package/dist/lib/{config-DxDaVDTn.js → config-DFLs7eoC.js} +3 -2
  99. package/dist/lib/config-DFLs7eoC.js.map +1 -0
  100. package/dist/lib/{configurePeerJs-ScwZiGbF.js → configurePeerJs-DJ0H6e6s.js} +3 -3
  101. package/dist/lib/{configurePeerJs-ScwZiGbF.js.map → configurePeerJs-DJ0H6e6s.js.map} +1 -1
  102. package/dist/lib/{connections-D82zele0.js → connections-DT3nPylw.js} +2 -2
  103. package/dist/lib/{connections-D82zele0.js.map → connections-DT3nPylw.js.map} +1 -1
  104. package/dist/lib/{downloadGroupFile-D7-5y6NI.js → downloadGroupFile-DMnAiPj4.js} +2 -2
  105. package/dist/lib/{downloadGroupFile-D7-5y6NI.js.map → downloadGroupFile-DMnAiPj4.js.map} +1 -1
  106. package/dist/lib/{file-viewer-BMvvndPQ.js → file-viewer-OHmGC_l7.js} +5 -5
  107. package/dist/lib/{file-viewer-BMvvndPQ.js.map → file-viewer-OHmGC_l7.js.map} +1 -1
  108. package/dist/lib/{getGroupDir-BidwYPfP.js → getGroupDir-mTV2Iz2b.js} +3 -3
  109. package/dist/lib/{getGroupDir-BidwYPfP.js.map → getGroupDir-mTV2Iz2b.js.map} +1 -1
  110. package/dist/lib/{git-CXmWufWS.js → git-CGdKghMC.js} +3 -3
  111. package/dist/lib/{git-CXmWufWS.js.map → git-CGdKghMC.js.map} +1 -1
  112. package/dist/lib/index.js +28 -28
  113. package/dist/lib/{initChatSplitResize-tvUZ-wsp.js → initChatSplitResize-B8zHHQdm.js} +2 -2
  114. package/dist/lib/{initChatSplitResize-tvUZ-wsp.js.map → initChatSplitResize-B8zHHQdm.js.map} +1 -1
  115. package/dist/lib/{initControlPlane-Dnjkb-6J.js → initControlPlane-BczrlTf3.js} +14 -14
  116. package/dist/lib/{initControlPlane-Dnjkb-6J.js.map → initControlPlane-BczrlTf3.js.map} +1 -1
  117. package/dist/lib/{markdown-WCXar9C8.js → markdown-D-7Cjyye.js} +3 -3
  118. package/dist/lib/{markdown-WCXar9C8.js.map → markdown-D-7Cjyye.js.map} +1 -1
  119. package/dist/lib/{mcp-reconnect-WTOQONHZ.js → mcp-reconnect-Czf7QCCv.js} +2 -2
  120. package/dist/lib/{mcp-reconnect-WTOQONHZ.js.map → mcp-reconnect-Czf7QCCv.js.map} +1 -1
  121. package/dist/lib/{memoryStorage-Bo2Wpfmg.js → memoryStorage-CXyKsKMo.js} +2 -2
  122. package/dist/lib/{memoryStorage-Bo2Wpfmg.js.map → memoryStorage-CXyKsKMo.js.map} +1 -1
  123. package/dist/lib/model-ranking-Bo8CpGH1.js +116 -0
  124. package/dist/lib/model-ranking-Bo8CpGH1.js.map +1 -0
  125. package/dist/lib/{orchestrator-Crw4btGn.js → orchestrator-BK138MkV.js} +10 -10
  126. package/dist/lib/{orchestrator-Crw4btGn.js.map → orchestrator-BK138MkV.js.map} +1 -1
  127. package/dist/lib/{orchestrator-Dro541iH.js → orchestrator-BtbxiPQB.js} +17 -16
  128. package/dist/lib/{orchestrator-Dro541iH.js.map → orchestrator-BtbxiPQB.js.map} +1 -1
  129. package/dist/lib/{peerjs-D741bHrf.js → peerjs-QJr03K9F.js} +7 -7
  130. package/dist/lib/{peerjs-D741bHrf.js.map → peerjs-QJr03K9F.js.map} +1 -1
  131. package/dist/lib/{prompt-api-D9XdrCep.js → prompt-api-vf351sf2.js} +2 -19
  132. package/dist/lib/{prompt-api-D9XdrCep.js.map → prompt-api-vf351sf2.js.map} +1 -1
  133. package/dist/lib/{push-client-DKFUY_Ti.js → push-client-BOHgqHbk.js} +4 -4
  134. package/dist/lib/{push-client-DKFUY_Ti.js.map → push-client-BOHgqHbk.js.map} +1 -1
  135. package/dist/lib/{readGroupFileBytes-C8Xqlb8l.js → readGroupFileBytes-CRA6dmgT.js} +2 -2
  136. package/dist/lib/{readGroupFileBytes-C8Xqlb8l.js.map → readGroupFileBytes-CRA6dmgT.js.map} +1 -1
  137. package/dist/lib/{shadow-claw-a2ui-CPydWVdW.js → shadow-claw-a2ui-DbZa4Gxp.js} +2 -2
  138. package/dist/lib/{shadow-claw-a2ui-CPydWVdW.js.map → shadow-claw-a2ui-DbZa4Gxp.js.map} +1 -1
  139. package/dist/lib/{shadow-claw-accounts-CUypXc3I.js → shadow-claw-accounts-B6OW2NVD.js} +3 -3
  140. package/dist/lib/{shadow-claw-accounts-CUypXc3I.js.map → shadow-claw-accounts-B6OW2NVD.js.map} +1 -1
  141. package/dist/lib/{shadow-claw-channel-config-D_Vb2ous.js → shadow-claw-channel-config-F_EJ-uK2.js} +3 -3
  142. package/dist/lib/{shadow-claw-channel-config-D_Vb2ous.js.map → shadow-claw-channel-config-F_EJ-uK2.js.map} +1 -1
  143. package/dist/lib/{shadow-claw-channels-hgjjzvRr.js → shadow-claw-channels-XYylMpjg.js} +3 -3
  144. package/dist/lib/{shadow-claw-channels-hgjjzvRr.js.map → shadow-claw-channels-XYylMpjg.js.map} +1 -1
  145. package/dist/lib/{shadow-claw-chat-D69IiLXd.js → shadow-claw-chat-dbK_DGO_.js} +124 -38
  146. package/dist/lib/shadow-claw-chat-dbK_DGO_.js.map +1 -0
  147. package/dist/lib/{shadow-claw-control-plane-zBYlFvzQ.js → shadow-claw-control-plane-BJKkmahQ.js} +7 -7
  148. package/dist/lib/{shadow-claw-control-plane-zBYlFvzQ.js.map → shadow-claw-control-plane-BJKkmahQ.js.map} +1 -1
  149. package/dist/lib/{shadow-claw-conversations-1GW8_e8o.js → shadow-claw-conversations-BKSsMYTc.js} +6 -6
  150. package/dist/lib/{shadow-claw-conversations-1GW8_e8o.js.map → shadow-claw-conversations-BKSsMYTc.js.map} +1 -1
  151. package/dist/lib/{shadow-claw-D3z3tFyE.js → shadow-claw-eBB2dbpd.js} +11 -11
  152. package/dist/lib/{shadow-claw-D3z3tFyE.js.map → shadow-claw-eBB2dbpd.js.map} +1 -1
  153. package/dist/lib/{shadow-claw-file-viewer-DI4r7Th9.js → shadow-claw-file-viewer-BqChtTvM.js} +8 -8
  154. package/dist/lib/{shadow-claw-file-viewer-DI4r7Th9.js.map → shadow-claw-file-viewer-BqChtTvM.js.map} +1 -1
  155. package/dist/lib/{shadow-claw-files-BM7qW3TW.js → shadow-claw-files-Bu7VCQw6.js} +9 -9
  156. package/dist/lib/{shadow-claw-files-BM7qW3TW.js.map → shadow-claw-files-Bu7VCQw6.js.map} +1 -1
  157. package/dist/lib/{shadow-claw-git-BBTiTYIn.js → shadow-claw-git-CdPPN6Qb.js} +6 -6
  158. package/dist/lib/{shadow-claw-git-BBTiTYIn.js.map → shadow-claw-git-CdPPN6Qb.js.map} +1 -1
  159. package/dist/lib/{shadow-claw-integrations-DTY3E545.js → shadow-claw-integrations-DehqBSRe.js} +4 -4
  160. package/dist/lib/{shadow-claw-integrations-DTY3E545.js.map → shadow-claw-integrations-DehqBSRe.js.map} +1 -1
  161. package/dist/lib/{shadow-claw-llm-AJwzkOGo.js → shadow-claw-llm-COZkufA8.js} +13 -11
  162. package/dist/lib/shadow-claw-llm-COZkufA8.js.map +1 -0
  163. package/dist/lib/{shadow-claw-mcp-remote-n7YXKxmv.js → shadow-claw-mcp-remote-axF50-le.js} +4 -4
  164. package/dist/lib/{shadow-claw-mcp-remote-n7YXKxmv.js.map → shadow-claw-mcp-remote-axF50-le.js.map} +1 -1
  165. package/dist/lib/{shadow-claw-networking-B3leDgZ4.js → shadow-claw-networking-BT_Rxj36.js} +2 -2
  166. package/dist/lib/{shadow-claw-networking-B3leDgZ4.js.map → shadow-claw-networking-BT_Rxj36.js.map} +1 -1
  167. package/dist/lib/{shadow-claw-notifications-Bm2aHQ0U.js → shadow-claw-notifications-ClqAXDDN.js} +3 -3
  168. package/dist/lib/{shadow-claw-notifications-Bm2aHQ0U.js.map → shadow-claw-notifications-ClqAXDDN.js.map} +1 -1
  169. package/dist/lib/{shadow-claw-page-header-BBqSWlhP.js → shadow-claw-page-header-bigC_h6U.js} +2 -2
  170. package/dist/lib/{shadow-claw-page-header-BBqSWlhP.js.map → shadow-claw-page-header-bigC_h6U.js.map} +1 -1
  171. package/dist/lib/{shadow-claw-pages-BFOcluFY.js → shadow-claw-pages-CjHS50HN.js} +10 -10
  172. package/dist/lib/{shadow-claw-pages-BFOcluFY.js.map → shadow-claw-pages-CjHS50HN.js.map} +1 -1
  173. package/dist/lib/{shadow-claw-peerjs-BvD_MH_l.js → shadow-claw-peerjs-CvcH2DkA.js} +3 -3
  174. package/dist/lib/{shadow-claw-peerjs-BvD_MH_l.js.map → shadow-claw-peerjs-CvcH2DkA.js.map} +1 -1
  175. package/dist/lib/{shadow-claw-provider-model-picker-CsA-507a.js → shadow-claw-provider-model-picker-fcVQdDtQ.js} +2 -2
  176. package/dist/lib/{shadow-claw-provider-model-picker-CsA-507a.js.map → shadow-claw-provider-model-picker-fcVQdDtQ.js.map} +1 -1
  177. package/dist/lib/{shadow-claw-settings-CZgGMz0a.js → shadow-claw-settings-CkO6JjA4.js} +6 -6
  178. package/dist/lib/{shadow-claw-settings-CZgGMz0a.js.map → shadow-claw-settings-CkO6JjA4.js.map} +1 -1
  179. package/dist/lib/{shadow-claw-storage-BTUpEKO0.js → shadow-claw-storage-10w-y8vL.js} +5 -5
  180. package/dist/lib/{shadow-claw-storage-BTUpEKO0.js.map → shadow-claw-storage-10w-y8vL.js.map} +1 -1
  181. package/dist/lib/{shadow-claw-task-server-B1X2AYyZ.js → shadow-claw-task-server-U98sZxPu.js} +2 -2
  182. package/dist/lib/{shadow-claw-task-server-B1X2AYyZ.js.map → shadow-claw-task-server-U98sZxPu.js.map} +1 -1
  183. package/dist/lib/{shadow-claw-tasks-BYtaRUAd.js → shadow-claw-tasks-DrSVUOsP.js} +8 -8
  184. package/dist/lib/{shadow-claw-tasks-BYtaRUAd.js.map → shadow-claw-tasks-DrSVUOsP.js.map} +1 -1
  185. package/dist/lib/{shadow-claw-terminal-LGuXKu77.js → shadow-claw-terminal-B7HYLuce.js} +2 -2
  186. package/dist/lib/{shadow-claw-terminal-LGuXKu77.js.map → shadow-claw-terminal-B7HYLuce.js.map} +1 -1
  187. package/dist/lib/{shadow-claw-tools-C_S-xXVG.js → shadow-claw-tools-BKrXzZfF.js} +7 -7
  188. package/dist/lib/{shadow-claw-tools-C_S-xXVG.js.map → shadow-claw-tools-BKrXzZfF.js.map} +1 -1
  189. package/dist/lib/{shadow-claw-webvm-Cab1n1bu.js → shadow-claw-webvm-Duwa5oiW.js} +3 -3
  190. package/dist/lib/{shadow-claw-webvm-Cab1n1bu.js.map → shadow-claw-webvm-Duwa5oiW.js.map} +1 -1
  191. package/dist/lib/{syncWebMcpRegistration-Dy5NS_4G.js → syncWebMcpRegistration-BOx-0F6_.js} +5 -5
  192. package/dist/lib/{syncWebMcpRegistration-Dy5NS_4G.js.map → syncWebMcpRegistration-BOx-0F6_.js.map} +1 -1
  193. package/dist/lib/{tools-B7gC1eb_.js → tools-BEuBatUs.js} +4 -4
  194. package/dist/lib/{tools-B7gC1eb_.js.map → tools-BEuBatUs.js.map} +1 -1
  195. package/dist/lib/utils/index.js +1 -1
  196. package/dist/lib/utils/utils.js +1 -1
  197. package/dist/lib/{utils-D_ylqOY-.js → utils-D8AxCH6m.js} +2 -2
  198. package/dist/lib/{utils-D_ylqOY-.js.map → utils-D8AxCH6m.js.map} +1 -1
  199. package/dist/lib/{webmcp-CkYINKJj.js → webmcp-BjfVWX1K.js} +2 -2
  200. package/dist/lib/{webmcp-CkYINKJj.js.map → webmcp-BjfVWX1K.js.map} +1 -1
  201. package/dist/public/.well-known/mcp/server-card.json +1 -1
  202. package/dist/public/.well-known/mcp.json +1 -1
  203. package/dist/public/AGENTS.md +1 -1
  204. package/dist/public/README.md +10 -10
  205. package/dist/public/agent.worker.js +1 -1
  206. package/dist/public/components/shadow-claw-chat/shadow-claw-chat.html +1 -1
  207. package/dist/public/config-071HSho8.js +1 -0
  208. package/dist/public/{configurePeerJs-DQQ6YL-5.js → configurePeerJs-DL-LurJY.js} +1 -1
  209. package/dist/public/{connections-CRNk2FxF.js → connections-Bb0rBpV-.js} +1 -1
  210. package/dist/public/{custom-element-security-3_FKCXcg.js → custom-element-security-Td4vrRc_.js} +1 -1
  211. package/dist/public/docs/README.md +1 -1
  212. package/dist/public/docs/architecture/orchestrator.md +2 -2
  213. package/dist/public/docs/architecture/overview.md +3 -3
  214. package/dist/public/docs/architecture/worker-protocol.md +29 -29
  215. package/dist/public/docs/example/article/index.html +1 -1
  216. package/dist/public/docs/example/article.html +1 -1
  217. package/dist/public/docs/publishing/index.html +1 -1
  218. package/dist/public/docs/publishing.html +1 -1
  219. package/dist/public/docs/skill-creator/index.html +1 -1
  220. package/dist/public/docs/skill-creator.html +1 -1
  221. package/dist/public/docs/subsystems/local-models.md +1 -1
  222. package/dist/public/docs/subsystems/providers.md +12 -5
  223. package/dist/public/{downloadGroupFile-wkdhVgrZ.js → downloadGroupFile-Unq8_7D9.js} +1 -1
  224. package/dist/public/e2e/README.md +2 -2
  225. package/dist/public/{e2e-bridge-DnFHPuwg.js → e2e-bridge-Dbc9fCDq.js} +1 -1
  226. package/dist/public/{file-viewer-C92v4Ggd.js → file-viewer-QkCuknFn.js} +1 -1
  227. package/dist/public/{getGroupDir-BGGIJL4Y.js → getGroupDir-Bj5XCflQ.js} +1 -1
  228. package/dist/public/{git-BH6qyToB.js → git-Bhz4YHc8.js} +2 -2
  229. package/dist/public/index.html +1 -1
  230. package/dist/public/index.js +1 -1
  231. package/dist/public/{initChatSplitResize--RH5kzVF.js → initChatSplitResize-KNL9Oh7s.js} +1 -1
  232. package/dist/public/{initControlPlane-yPJ3ajkj.js → initControlPlane-D2fVhRFC.js} +1 -1
  233. package/dist/public/llms.txt +1 -1
  234. package/dist/public/main/index.html +1 -1
  235. package/dist/public/main/memory/index.html +1 -1
  236. package/dist/public/main/memory.html +1 -1
  237. package/dist/public/main.html +1 -1
  238. package/dist/public/{markdown-CA0v11cd.js → markdown-By6gdUvF.js} +1 -1
  239. package/dist/public/{mcp-reconnect-uyNSigzu.js → mcp-reconnect-CDRJPuoN.js} +1 -1
  240. package/dist/public/{memoryStorage-DjPNAT5t.js → memoryStorage-C-ao9y-j.js} +1 -1
  241. package/dist/public/model-ranking-DUdrFOcC.js +1 -0
  242. package/dist/public/{orchestrator-DTzgyL23.js → orchestrator-B7MpomoG.js} +2 -2
  243. package/dist/public/{orchestrator-Cmr69pMC.js → orchestrator-C8aIHbDy.js} +2 -2
  244. package/dist/public/{peerjs-Br38mUFC.js → peerjs-DwGjY5VX.js} +1 -1
  245. package/dist/public/prompt-api-BWe_j3Wg.js +1 -0
  246. package/dist/public/{push-client-CB8haOGC.js → push-client-o8g5_EOe.js} +1 -1
  247. package/dist/public/service-worker.js +1 -1
  248. package/dist/public/{shadow-claw-RuFQ2Ej5.js → shadow-claw-T9oPpqxT.js} +2 -2
  249. package/dist/public/{shadow-claw-accounts-BMsEItB6.js → shadow-claw-accounts-BbNPiiaV.js} +1 -1
  250. package/dist/public/{shadow-claw-channels-Q_AXuEaI.js → shadow-claw-channels-CHGtPkfY.js} +1 -1
  251. package/dist/public/{shadow-claw-chat-DZQ3OaH7.js → shadow-claw-chat-3TmjJA6-.js} +90 -23
  252. package/dist/public/{shadow-claw-control-plane-CyXA8tB8.js → shadow-claw-control-plane-BAFT06LL.js} +2 -2
  253. package/dist/public/{shadow-claw-conversations-BLKtlvHe.js → shadow-claw-conversations-DsQ_kxWm.js} +1 -1
  254. package/dist/public/{shadow-claw-file-viewer-CVLDfpZe.js → shadow-claw-file-viewer-hBxTknL2.js} +1 -1
  255. package/dist/public/{shadow-claw-files-DB6VVjir.js → shadow-claw-files-Daw6IIrJ.js} +1 -1
  256. package/dist/public/{shadow-claw-git-D57bTSMF.js → shadow-claw-git-CxGwyxim.js} +2 -2
  257. package/dist/public/{shadow-claw-integrations-BOwhUY-4.js → shadow-claw-integrations-DnIr6xp-.js} +1 -1
  258. package/dist/public/{shadow-claw-llm-CYenm-mm.js → shadow-claw-llm-Bc6tZ8kk.js} +3 -3
  259. package/dist/public/{shadow-claw-mcp-remote-CJ-Ckl0j.js → shadow-claw-mcp-remote-nGPpFpI-.js} +1 -1
  260. package/dist/public/{shadow-claw-networking-Cut76YQL.js → shadow-claw-networking-B6YgtiA7.js} +1 -1
  261. package/dist/public/{shadow-claw-notifications-C3kYCrIa.js → shadow-claw-notifications-BSxifptS.js} +1 -1
  262. package/dist/public/{shadow-claw-page-header-Dd7QafUk.js → shadow-claw-page-header-LzVgEn46.js} +1 -1
  263. package/dist/public/{shadow-claw-pages-BWtVtwTG.js → shadow-claw-pages-IW_LNEL_.js} +2 -2
  264. package/dist/public/{shadow-claw-settings-Bj7jnngO.js → shadow-claw-settings-DGkwt1ge.js} +2 -2
  265. package/dist/public/{shadow-claw-storage-CMSzQs1-.js → shadow-claw-storage-DX_4T7KR.js} +1 -1
  266. package/dist/public/{shadow-claw-task-server-CFclo7UX.js → shadow-claw-task-server-DTsbEs5z.js} +1 -1
  267. package/dist/public/{shadow-claw-tasks-ClDdeB6Z.js → shadow-claw-tasks-rr6fMxAM.js} +1 -1
  268. package/dist/public/{shadow-claw-terminal-3ysWZJix.js → shadow-claw-terminal-C7DhrrX6.js} +1 -1
  269. package/dist/public/{shadow-claw-tools-DQVwpTcZ.js → shadow-claw-tools-DcQroojH.js} +1 -1
  270. package/dist/public/{shadow-claw-webvm-Cnxz04Fk.js → shadow-claw-webvm-bZj44Did.js} +1 -1
  271. package/dist/public/{syncWebMcpRegistration-QwnLMAK3.js → syncWebMcpRegistration-XJjIlDNO.js} +1 -1
  272. package/dist/public/{tools-DvQ_ngXn.js → tools-BpZh7hgB.js} +1 -1
  273. package/dist/public/{webmcp-CJymew4t.js → webmcp-CkfDZfZ0.js} +1 -1
  274. package/dist/server.js +1 -1
  275. package/package.json +1 -1
  276. package/src/cli/build/build.test.ts +95 -0
  277. package/src/cli/build/build.ts +5 -1
  278. package/src/cli/commands/agent.test.ts +134 -0
  279. package/src/cli/commands/backup.test.ts +85 -0
  280. package/src/cli/commands/clients.test.ts +32 -0
  281. package/src/cli/commands/mcp.test.ts +260 -1
  282. package/src/cli/commands/send-file.test.ts +73 -0
  283. package/src/cli/commands/send.test.ts +58 -0
  284. package/src/cli/commands/tasks.test.ts +78 -0
  285. package/src/cli/commands/webrtc-listen.test.ts +74 -0
  286. package/src/cli/tools/assert-version-bump.test.ts +30 -1
  287. package/src/cli/tools/file-search-replace.test.ts +33 -1
  288. package/src/cli/tools/patch-service-worker-trusted-types.test.ts +24 -1
  289. package/src/cli/tools/touch-nojekyll.test.ts +12 -0
  290. package/src/cli/tools/version.test.ts +109 -5
  291. package/src/cli/utils/agent-core.test.ts +40 -0
  292. package/src/cli/utils/control-client.test.ts +239 -2
  293. package/src/cli/utils/load-workspace-config.test.ts +63 -0
  294. package/src/cli/utils/local-models.test.ts +98 -0
  295. package/src/cli/utils/progress-bar.test.ts +41 -2
  296. package/src/cli/utils/resolve-cache-dir.test.ts +61 -0
  297. package/src/cli/utils/resolve-project-root.test.ts +17 -0
  298. package/src/cli/utils/stdin.test.ts +38 -0
  299. package/src/cli/utils/stdio.test.ts +51 -1
  300. package/src/cli/utils/suppress-warnings.test.ts +9 -0
  301. package/src/cli/utils/webrtc-control-client.test.ts +569 -0
  302. package/src/common/utils/frontmatter.test.ts +117 -0
  303. package/src/components/common/help/providers.test.ts +67 -0
  304. package/src/components/common/shadow-claw-provider-model-picker/shadow-claw-provider-model-picker.test.ts +121 -0
  305. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +131 -0
  306. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +130 -0
  307. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +12 -0
  308. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +328 -0
  309. package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +121 -7
  310. package/src/components/settings/shadow-claw-llm/model-ranking.ts +97 -0
  311. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +565 -8
  312. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +15 -5
  313. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +459 -0
  314. package/src/components/settings/shadow-claw-networking/shadow-claw-networking.test.ts +88 -0
  315. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +92 -0
  316. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +127 -0
  317. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +173 -0
  318. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +62 -0
  319. package/src/components/settings/shadow-claw-webvm/shadow-claw-webvm.test.ts +104 -0
  320. package/src/components/shadow-claw/shadow-claw.test.ts +112 -0
  321. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +109 -0
  322. package/src/components/shadow-claw-chat/shadow-claw-chat.html +81 -14
  323. package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +73 -0
  324. package/src/components/shadow-claw-chat/shadow-claw-chat.trusted-types.test.ts +3 -0
  325. package/src/components/shadow-claw-chat/shadow-claw-chat.ts +79 -32
  326. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +361 -0
  327. package/src/components/shadow-claw-dialog/shadow-claw-dialog.test.ts +76 -0
  328. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +307 -0
  329. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +398 -1
  330. package/src/components/shadow-claw-pages/shadow-claw-pages.test.ts +85 -0
  331. package/src/components/shadow-claw-pages/shadow-claw-pages.ts +2 -1
  332. package/src/components/shadow-claw-pdf-viewer/shadow-claw-pdf-viewer.test.ts +229 -0
  333. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +233 -0
  334. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +223 -1
  335. package/src/components/shadow-claw-toast/shadow-claw-toast.test.ts +79 -0
  336. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +229 -1
  337. package/src/config/config.test.ts +4 -4
  338. package/src/config/config.ts +2 -1
  339. package/src/core/orchestrator/orchestrator.test.ts +2 -1
  340. package/src/core/orchestrator/utils/initTasks.ts +5 -4
  341. package/src/server/logger.test.ts +57 -0
  342. package/src/server/peer.test.ts +47 -0
  343. package/src/server/routes/gemini.test.ts +244 -0
  344. package/src/server/server-peer.test.ts +245 -11
  345. package/src/server/services/llamafile-manager.test.ts +668 -0
  346. package/src/share-target/pending-shares.test.ts +90 -0
  347. package/src/shell/fs.test.ts +125 -0
  348. package/src/shell/shell.test.ts +8 -0
  349. package/src/subsystems/git/credentials.test.ts +260 -0
  350. package/src/subsystems/git/git.test.ts +64 -0
  351. package/src/subsystems/providers/executeNativeAiTask.test.ts +78 -0
  352. package/src/subsystems/providers/utils/parseLocalModelToolCall.test.ts +57 -0
  353. package/src/worker/tools/builtin-ai/builtin-ai.test.ts +88 -0
  354. package/src/worker/tools/email/email.test.ts +358 -0
  355. package/src/worker/tools/email/utils/resolveIntegrationEmailAuth.test.ts +120 -0
  356. package/src/worker/tools/email/utils/resolveUniqueAttachmentPath.test.ts +80 -0
  357. package/src/worker/tools/fetch-file/fetch-file.test.ts +111 -0
  358. package/src/worker/tools/fetch-url/fetch-url.test.ts +295 -0
  359. package/dist/cli/chunk-B3S061IE.js +0 -1
  360. package/dist/cli/chunk-BUJdF9o3.js +0 -1
  361. package/dist/cli/chunk-BX0R2gIY.js +0 -1
  362. package/dist/cli/chunk-BoiPfhwJ.js +0 -1
  363. package/dist/cli/chunk-BtJYj7vB.js +0 -1
  364. package/dist/cli/chunk-BwXrZVeU.js +0 -1
  365. package/dist/cli/chunk-CaWh9Pru.js +0 -1
  366. package/dist/cli/chunk-Ck4ePI8z.js +0 -1
  367. package/dist/cli/chunk-Ctg-SXcP.js +0 -1
  368. package/dist/cli/chunk-DTKv1-tk.js +0 -1
  369. package/dist/cli/chunk-DdYpd9Nu.js +0 -1
  370. package/dist/cli/chunk-DflhmBY2.js +0 -1
  371. package/dist/lib/config-DxDaVDTn.js.map +0 -1
  372. package/dist/lib/model-ranking-CDXs7hkK.js +0 -57
  373. package/dist/lib/model-ranking-CDXs7hkK.js.map +0 -1
  374. package/dist/lib/shadow-claw-chat-D69IiLXd.js.map +0 -1
  375. package/dist/lib/shadow-claw-llm-AJwzkOGo.js.map +0 -1
  376. package/dist/public/config-Dqn5OMfC.js +0 -1
  377. package/dist/public/model-ranking-DLIC9IDU.js +0 -1
  378. package/dist/public/prompt-api-B-8N-MRY.js +0 -11
@@ -22,6 +22,7 @@ describe("runBackupCommand", () => {
22
22
  afterEach(() => {
23
23
  logSpy.mockRestore();
24
24
  errorSpy.mockRestore();
25
+ jest.restoreAllMocks();
25
26
  process.exitCode = 0;
26
27
  });
27
28
 
@@ -64,4 +65,88 @@ describe("runBackupCommand", () => {
64
65
  );
65
66
  expect(process.exitCode).toBe(1);
66
67
  });
68
+
69
+ it("deletes backup when backupId is provided", async () => {
70
+ const deleteSpy = jest
71
+ .spyOn(CliControlClient.prototype, "deleteBackup")
72
+ .mockResolvedValueOnce(undefined as any);
73
+
74
+ await runBackupCommand("delete", {
75
+ backupId: "bk-123",
76
+ client: "client-1",
77
+ });
78
+
79
+ expect(deleteSpy).toHaveBeenCalledWith("bk-123", "client-1");
80
+ expect(logSpy).toHaveBeenCalledWith("Backup bk-123 deleted.");
81
+ });
82
+
83
+ it("triggers backup with provided client successfully", async () => {
84
+ const sendSpy = jest
85
+ .spyOn(CliControlClient.prototype, "sendCommand")
86
+ .mockResolvedValueOnce({ success: true, data: { fileCount: 10 } });
87
+
88
+ await runBackupCommand("trigger", {
89
+ client: "client-target",
90
+ group: "grp-1",
91
+ });
92
+
93
+ expect(sendSpy).toHaveBeenCalledWith("client-target", "trigger-backup", {
94
+ groupId: "grp-1",
95
+ token: expect.anything(),
96
+ });
97
+ expect(logSpy).toHaveBeenCalledWith("Backup completed successfully.");
98
+ });
99
+
100
+ it("triggers backup falling back to first client from listClients", async () => {
101
+ jest
102
+ .spyOn(CliControlClient.prototype, "listClients")
103
+ .mockResolvedValueOnce([{ clientId: "auto-client" }]);
104
+ const sendSpy = jest
105
+ .spyOn(CliControlClient.prototype, "sendCommand")
106
+ .mockResolvedValueOnce({ success: true, data: {} });
107
+
108
+ await runBackupCommand("trigger", {});
109
+
110
+ expect(sendSpy).toHaveBeenCalledWith("auto-client", "trigger-backup", {
111
+ groupId: undefined,
112
+ token: expect.anything(),
113
+ });
114
+ });
115
+
116
+ it("errors when triggering backup and no clients are connected", async () => {
117
+ jest
118
+ .spyOn(CliControlClient.prototype, "listClients")
119
+ .mockResolvedValueOnce([]);
120
+
121
+ await runBackupCommand("trigger", {});
122
+
123
+ expect(errorSpy).toHaveBeenCalledWith(
124
+ "Error: No clients are connected to back up.",
125
+ );
126
+ expect(process.exitCode).toBe(1);
127
+ });
128
+
129
+ it("handles backup failure response", async () => {
130
+ jest
131
+ .spyOn(CliControlClient.prototype, "sendCommand")
132
+ .mockResolvedValueOnce({ success: false, error: "Disk full" });
133
+
134
+ await runBackupCommand("trigger", { client: "client-fail" });
135
+
136
+ expect(errorSpy).toHaveBeenCalledWith("Backup failed: Disk full");
137
+ expect(process.exitCode).toBe(1);
138
+ });
139
+
140
+ it("handles exception thrown during execution", async () => {
141
+ jest
142
+ .spyOn(CliControlClient.prototype, "listBackups")
143
+ .mockRejectedValueOnce(new Error("Network timeout"));
144
+
145
+ await runBackupCommand("list", {});
146
+
147
+ expect(errorSpy).toHaveBeenCalledWith(
148
+ "Error executing backup command: Network timeout",
149
+ );
150
+ expect(process.exitCode).toBe(1);
151
+ });
67
152
  });
@@ -57,4 +57,36 @@ describe("runClientsCommand", () => {
57
57
  "No clients currently registered or connected.",
58
58
  );
59
59
  });
60
+
61
+ it("formats client without peerId, empty capabilities, and older lastSeen", async () => {
62
+ jest
63
+ .spyOn(CliControlClient.prototype, "listClients")
64
+ .mockResolvedValueOnce([
65
+ {
66
+ clientId: "c-old",
67
+ deviceLabel: "Headless Agent",
68
+ capabilities: [],
69
+ version: "2.0.0",
70
+ lastSeen: Date.now() - 125000,
71
+ },
72
+ ]);
73
+
74
+ await runClientsCommand({});
75
+
76
+ expect(logSpy).toHaveBeenCalledWith(" Capabilities: none");
77
+ expect(logSpy).toHaveBeenCalledWith(" Last seen: 2m ago\n");
78
+ });
79
+
80
+ it("handles exception during fetch", async () => {
81
+ jest
82
+ .spyOn(CliControlClient.prototype, "listClients")
83
+ .mockRejectedValueOnce(new Error("Connection refused"));
84
+
85
+ await runClientsCommand({});
86
+
87
+ expect(errorSpy).toHaveBeenCalledWith(
88
+ "Error fetching clients: Connection refused",
89
+ );
90
+ expect(process.exitCode).toBe(1);
91
+ });
60
92
  });
@@ -11,7 +11,9 @@ import {
11
11
  beforeEach,
12
12
  afterEach,
13
13
  } from "@jest/globals";
14
- import { createCliMcpEngine } from "./mcp.js";
14
+ import http from "node:http";
15
+ import readline from "node:readline";
16
+ import { createCliMcpEngine, runMcpCommand } from "./mcp.js";
15
17
 
16
18
  const pkg = JSON.parse(
17
19
  fs.readFileSync(
@@ -1086,5 +1088,262 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
1086
1088
  expect(toolNames).not.toContain("shadowclaw_local_write_file");
1087
1089
  expect(toolNames).not.toContain("shadowclaw_local_bash");
1088
1090
  });
1091
+
1092
+ it("supports setting and getting activeClientId", () => {
1093
+ const engine = createCliMcpEngine({});
1094
+ expect(engine.getActiveClientId()).toBe("");
1095
+ engine.setActiveClientId?.("client-custom-1");
1096
+ expect(engine.getActiveClientId()).toBe("client-custom-1");
1097
+ });
1098
+
1099
+ it("handles tasks/get method", async () => {
1100
+ const engine = createCliMcpEngine({});
1101
+ const res = await engine.handleMessage({
1102
+ jsonrpc: "2.0",
1103
+ id: 50,
1104
+ method: "tasks/get",
1105
+ params: { taskId: "task-test-99" },
1106
+ });
1107
+ expect(res.result.task.taskId).toBe("task-test-99");
1108
+ expect(res.result.task.status).toBe("completed");
1109
+ });
1110
+
1111
+ it("handles unknown method returning error or null for notification", async () => {
1112
+ const engine = createCliMcpEngine({});
1113
+ const errRes = await engine.handleMessage({
1114
+ jsonrpc: "2.0",
1115
+ id: 51,
1116
+ method: "unknown/method",
1117
+ });
1118
+ expect(errRes.error.code).toBe(-32601);
1119
+
1120
+ const notifRes = await engine.handleMessage({
1121
+ jsonrpc: "2.0",
1122
+ method: "unknown/notification",
1123
+ });
1124
+ expect(notifRes).toBeNull();
1125
+ });
1126
+
1127
+ it("handles tools/call when no client is available", async () => {
1128
+ const engine = createCliMcpEngine({
1129
+ client: { listClients: async () => [] },
1130
+ });
1131
+ const res = await engine.handleMessage({
1132
+ jsonrpc: "2.0",
1133
+ id: 52,
1134
+ method: "tools/call",
1135
+ params: {
1136
+ name: "remote_tool_1",
1137
+ arguments: {},
1138
+ },
1139
+ });
1140
+ expect(res.result.isError).toBe(true);
1141
+ expect(res.result.content[0].text).toContain(
1142
+ "No connected client available",
1143
+ );
1144
+ });
1145
+
1146
+ it("closes sqlite database when engine.close is invoked after headless context creation", async () => {
1147
+ const tmpDir = await mkdtemp(path.join(tmpdir(), "sc-mcp-close-test-"));
1148
+ const engine = createCliMcpEngine({
1149
+ workspace: tmpDir,
1150
+ localTools: true,
1151
+ });
1152
+
1153
+ // Invoke local tool to initialize headlessContextPromise
1154
+ await engine.handleMessage({
1155
+ jsonrpc: "2.0",
1156
+ id: 53,
1157
+ method: "tools/call",
1158
+ params: {
1159
+ name: "shadowclaw_local_list_files",
1160
+ arguments: {},
1161
+ },
1162
+ });
1163
+
1164
+ // Call close
1165
+ await engine.close?.();
1166
+ await rm(tmpDir, { recursive: true, force: true });
1167
+ });
1168
+ });
1169
+
1170
+ describe("runMcpCommand", () => {
1171
+ let tmpWorkspace: string;
1172
+
1173
+ beforeEach(async () => {
1174
+ tmpWorkspace = await mkdtemp(path.join(tmpdir(), "sc-mcp-cmd-test-"));
1175
+ });
1176
+
1177
+ afterEach(async () => {
1178
+ try {
1179
+ await rm(tmpWorkspace, { recursive: true, force: true });
1180
+ } catch (_) {}
1181
+ });
1182
+
1183
+ it("handles HTTP transport requests including 404, parse error, success, notification, and 500", async () => {
1184
+ let requestHandler: any;
1185
+ const mockServer = {
1186
+ listen: jest.fn((_port: any, _host: any, cb: any) => cb?.()),
1187
+ close: jest.fn(),
1188
+ };
1189
+ jest.spyOn(http, "createServer").mockImplementation((handler: any) => {
1190
+ requestHandler = handler;
1191
+ return mockServer as any;
1192
+ });
1193
+
1194
+ await runMcpCommand({
1195
+ mcpTransport: "http",
1196
+ port: 9999,
1197
+ host: "127.0.0.1",
1198
+ workspace: tmpWorkspace,
1199
+ });
1200
+
1201
+ expect(http.createServer).toHaveBeenCalled();
1202
+ expect(mockServer.listen).toHaveBeenCalledWith(
1203
+ 9999,
1204
+ "127.0.0.1",
1205
+ expect.any(Function),
1206
+ );
1207
+
1208
+ // 1. Non-POST or wrong URL -> 404
1209
+ const res404 = {
1210
+ writeHead: jest.fn(),
1211
+ end: jest.fn(),
1212
+ };
1213
+ await requestHandler({ method: "GET", url: "/mcp" }, res404);
1214
+ expect(res404.writeHead).toHaveBeenCalledWith(404, expect.any(Object));
1215
+
1216
+ // Helper to simulate request events
1217
+ const invokeHandler = (reqMeta: any, rawData: string) => {
1218
+ return new Promise<any>((resolve) => {
1219
+ const res = {
1220
+ writeHead: jest.fn(),
1221
+ end: jest.fn(() => resolve(res)),
1222
+ };
1223
+ const handlers: Record<string, Function> = {};
1224
+ const req: any = {
1225
+ method: "POST",
1226
+ url: "/mcp",
1227
+ headers: {},
1228
+ ...reqMeta,
1229
+ on: (event: string, cb: any) => {
1230
+ handlers[event] = cb;
1231
+ },
1232
+ };
1233
+ Promise.resolve(requestHandler(req, res)).then(() => {
1234
+ handlers["data"]?.(rawData);
1235
+ handlers["end"]?.();
1236
+ });
1237
+ });
1238
+ };
1239
+
1240
+ // 2. Parse error -> 400
1241
+ const res400 = await invokeHandler({}, "not-valid-json");
1242
+ expect(res400.writeHead).toHaveBeenCalledWith(400, expect.any(Object));
1243
+
1244
+ // 3. Valid JSON-RPC method -> 200
1245
+ const validReq = JSON.stringify({
1246
+ jsonrpc: "2.0",
1247
+ id: 1,
1248
+ method: "tools/list",
1249
+ });
1250
+ const res200 = await invokeHandler(
1251
+ {
1252
+ headers: {
1253
+ "mcp-protocol-version": "2026-07-28",
1254
+ "mcp-method": "tools/list",
1255
+ "mcp-name": "test-client",
1256
+ },
1257
+ },
1258
+ validReq,
1259
+ );
1260
+ expect(res200.writeHead).toHaveBeenCalledWith(200, expect.any(Object));
1261
+
1262
+ // 4. Notification (reply === null) -> 202
1263
+ const notifReq = JSON.stringify({
1264
+ jsonrpc: "2.0",
1265
+ method: "notifications/initialized",
1266
+ });
1267
+ const res202 = await invokeHandler({}, notifReq);
1268
+ expect(res202.writeHead).toHaveBeenCalledWith(202);
1269
+
1270
+ // 5. Error during handling -> 500
1271
+ const throwingHeaders = {
1272
+ get ["mcp-protocol-version"]() {
1273
+ throw new Error("Boom headers");
1274
+ },
1275
+ };
1276
+ const res500 = await invokeHandler(
1277
+ { headers: throwingHeaders },
1278
+ validReq,
1279
+ );
1280
+ expect(res500.writeHead).toHaveBeenCalledWith(500, expect.any(Object));
1281
+
1282
+ // Restore
1283
+ jest.spyOn(http, "createServer").mockRestore();
1284
+ });
1285
+
1286
+ it("handles STDIO transport lines including parse error, valid request, notification, and close", async () => {
1287
+ let lineHandler: any;
1288
+ let closeHandler: any;
1289
+ const mockRl = {
1290
+ on: jest.fn((event: string, cb: any) => {
1291
+ if (event === "line") lineHandler = cb;
1292
+ if (event === "close") closeHandler = cb;
1293
+ return mockRl;
1294
+ }),
1295
+ };
1296
+ jest.spyOn(readline, "createInterface").mockReturnValue(mockRl as any);
1297
+ const writeSpy = jest
1298
+ .spyOn(process.stdout, "write")
1299
+ .mockImplementation(() => true);
1300
+
1301
+ await runMcpCommand({
1302
+ mcpTransport: "stdio",
1303
+ workspace: tmpWorkspace,
1304
+ });
1305
+
1306
+ expect(readline.createInterface).toHaveBeenCalled();
1307
+
1308
+ // Empty line ignored
1309
+ await lineHandler(" ");
1310
+ expect(writeSpy).not.toHaveBeenCalled();
1311
+
1312
+ // Parse error
1313
+ await lineHandler("bad-json");
1314
+ expect(writeSpy).toHaveBeenCalledWith(expect.stringContaining("-32700"));
1315
+ writeSpy.mockClear();
1316
+
1317
+ // Valid tools/list
1318
+ await lineHandler(
1319
+ JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list" }),
1320
+ );
1321
+ expect(writeSpy).toHaveBeenCalledWith(
1322
+ expect.stringContaining('"result"'),
1323
+ );
1324
+ writeSpy.mockClear();
1325
+
1326
+ // Exception during write triggering catch block (lines 1613-1620)
1327
+ writeSpy.mockImplementationOnce(() => {
1328
+ throw new Error("Stdout write failure");
1329
+ });
1330
+ await lineHandler(
1331
+ JSON.stringify({ jsonrpc: "2.0", id: 3, method: "tools/list" }),
1332
+ );
1333
+ expect(writeSpy).toHaveBeenCalledWith(expect.stringContaining("-32603"));
1334
+ writeSpy.mockClear();
1335
+
1336
+ // Notification
1337
+ await lineHandler(
1338
+ JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }),
1339
+ );
1340
+ expect(writeSpy).not.toHaveBeenCalled();
1341
+
1342
+ // Close handler
1343
+ await closeHandler();
1344
+
1345
+ writeSpy.mockRestore();
1346
+ jest.spyOn(readline, "createInterface").mockRestore();
1347
+ });
1089
1348
  });
1090
1349
  });
@@ -138,4 +138,77 @@ describe("runSendFileCommand", () => {
138
138
  expect.stringContaining("The CSV has 2 columns and 1 row."),
139
139
  );
140
140
  });
141
+
142
+ it("fails when path is a directory", async () => {
143
+ await runSendFileCommand(tempDir, {});
144
+ expect(errorSpy).toHaveBeenCalledWith(
145
+ expect.stringContaining("Cannot send directory"),
146
+ );
147
+ expect(process.exitCode).toBe(1);
148
+ });
149
+
150
+ it("fails when no client specified and no clients connected", async () => {
151
+ const filePath = path.join(tempDir, "file.txt");
152
+ fs.writeFileSync(filePath, "data", "utf8");
153
+
154
+ jest
155
+ .spyOn(CliControlClient.prototype, "listClients")
156
+ .mockResolvedValueOnce([]);
157
+
158
+ await runSendFileCommand(filePath, {});
159
+
160
+ expect(errorSpy).toHaveBeenCalledWith("Error: No clients are connected.");
161
+ expect(process.exitCode).toBe(1);
162
+ });
163
+
164
+ it("handles client transfer failure", async () => {
165
+ const filePath = path.join(tempDir, "fail.txt");
166
+ fs.writeFileSync(filePath, "data", "utf8");
167
+
168
+ jest
169
+ .spyOn(CliControlClient.prototype, "sendFile")
170
+ .mockResolvedValueOnce({ success: false, error: "Disk quota exceeded" });
171
+
172
+ await runSendFileCommand(filePath, { client: "client-1" });
173
+
174
+ expect(errorSpy).toHaveBeenCalledWith(
175
+ "Error from client: Disk quota exceeded",
176
+ );
177
+ expect(process.exitCode).toBe(1);
178
+ });
179
+
180
+ it("handles exception thrown during file send", async () => {
181
+ const filePath = path.join(tempDir, "err.txt");
182
+ fs.writeFileSync(filePath, "data", "utf8");
183
+
184
+ jest
185
+ .spyOn(CliControlClient.prototype, "sendFile")
186
+ .mockRejectedValueOnce(new Error("Pipe broken"));
187
+
188
+ await runSendFileCommand(filePath, { client: "client-1" });
189
+
190
+ expect(errorSpy).toHaveBeenCalledWith("Error sending file: Pipe broken");
191
+ expect(process.exitCode).toBe(1);
192
+ });
193
+
194
+ it("outputs json when --json is passed and no reply text", async () => {
195
+ const filePath = path.join(tempDir, "json.txt");
196
+ fs.writeFileSync(filePath, "data", "utf8");
197
+
198
+ jest.spyOn(CliControlClient.prototype, "sendFile").mockResolvedValueOnce({
199
+ success: true,
200
+ data: { transferredBytes: 4 },
201
+ });
202
+
203
+ await runSendFileCommand(filePath, {
204
+ client: "client-1",
205
+ json: true,
206
+ name: "custom.txt",
207
+ timeout: 15,
208
+ });
209
+
210
+ expect(logSpy).toHaveBeenCalledWith(
211
+ expect.stringContaining('"transferredBytes": 4'),
212
+ );
213
+ });
141
214
  });
@@ -89,4 +89,62 @@ describe("runSendCommand", () => {
89
89
  }),
90
90
  );
91
91
  });
92
+
93
+ it("auto-resolves client when client option omitted", async () => {
94
+ jest
95
+ .spyOn(CliControlClient.prototype, "listClients")
96
+ .mockResolvedValueOnce([
97
+ { clientId: "c-first", deviceLabel: "My Laptop" },
98
+ ]);
99
+ const sendSpy = jest
100
+ .spyOn(CliControlClient.prototype, "sendCommand")
101
+ .mockResolvedValueOnce({ success: true, data: { status: "ok" } });
102
+
103
+ await runSendCommand("Hello first", { timeout: 10, json: true });
104
+
105
+ expect(sendSpy).toHaveBeenCalledWith(
106
+ "c-first",
107
+ "send-message",
108
+ { text: "Hello first", groupId: undefined },
109
+ 10000,
110
+ );
111
+ expect(logSpy).toHaveBeenCalledWith(
112
+ expect.stringContaining("Targeting client: My Laptop (c-first)"),
113
+ );
114
+ });
115
+
116
+ it("errors when client option omitted and no clients connected", async () => {
117
+ jest
118
+ .spyOn(CliControlClient.prototype, "listClients")
119
+ .mockResolvedValueOnce([]);
120
+
121
+ await runSendCommand("Hello nobody", {});
122
+
123
+ expect(errorSpy).toHaveBeenCalledWith("Error: No clients are connected.");
124
+ expect(process.exitCode).toBe(1);
125
+ });
126
+
127
+ it("handles client error response", async () => {
128
+ jest
129
+ .spyOn(CliControlClient.prototype, "sendCommand")
130
+ .mockResolvedValueOnce({ success: false, error: "Model timeout" });
131
+
132
+ await runSendCommand("Hello failure", { client: "client-target" });
133
+
134
+ expect(errorSpy).toHaveBeenCalledWith("Error from client: Model timeout");
135
+ expect(process.exitCode).toBe(1);
136
+ });
137
+
138
+ it("handles exception during sending", async () => {
139
+ jest
140
+ .spyOn(CliControlClient.prototype, "sendCommand")
141
+ .mockRejectedValueOnce(new Error("Socket closed"));
142
+
143
+ await runSendCommand("Hello exception", { client: "client-target" });
144
+
145
+ expect(errorSpy).toHaveBeenCalledWith(
146
+ "Error sending message: Socket closed",
147
+ );
148
+ expect(process.exitCode).toBe(1);
149
+ });
92
150
  });
@@ -61,4 +61,82 @@ describe("runTasksCommand", () => {
61
61
 
62
62
  expect(logSpy).toHaveBeenCalledWith("No tasks configured on client.");
63
63
  });
64
+
65
+ it("prints empty message with group when no tasks exist for group", async () => {
66
+ jest
67
+ .spyOn(CliControlClient.prototype, "sendCommand")
68
+ .mockResolvedValueOnce({
69
+ success: true,
70
+ data: { tasks: [] },
71
+ });
72
+
73
+ await runTasksCommand({ client: "test-client", group: "my-group" });
74
+
75
+ expect(logSpy).toHaveBeenCalledWith(
76
+ 'No tasks configured on client for group "my-group".',
77
+ );
78
+ });
79
+
80
+ it("resolves default client from listClients when client option omitted", async () => {
81
+ jest
82
+ .spyOn(CliControlClient.prototype, "listClients")
83
+ .mockResolvedValueOnce([{ clientId: "first-client" }]);
84
+ const sendSpy = jest
85
+ .spyOn(CliControlClient.prototype, "sendCommand")
86
+ .mockResolvedValueOnce({
87
+ success: true,
88
+ data: {
89
+ tasks: [{ prompt: "Say hello and do things", enabled: true }],
90
+ },
91
+ });
92
+
93
+ await runTasksCommand({});
94
+
95
+ expect(sendSpy).toHaveBeenCalledWith("first-client", "list-tasks", {
96
+ groupId: undefined,
97
+ });
98
+ expect(logSpy).toHaveBeenCalledWith(
99
+ expect.stringContaining("1. [enabled] Say hello and do things (manual)"),
100
+ );
101
+ });
102
+
103
+ it("errors when client option omitted and no clients connected", async () => {
104
+ jest
105
+ .spyOn(CliControlClient.prototype, "listClients")
106
+ .mockResolvedValueOnce([]);
107
+
108
+ await runTasksCommand({});
109
+
110
+ expect(errorSpy).toHaveBeenCalledWith("Error: No clients connected.");
111
+ expect(process.exitCode).toBe(1);
112
+ });
113
+
114
+ it("handles sendCommand failure", async () => {
115
+ jest
116
+ .spyOn(CliControlClient.prototype, "sendCommand")
117
+ .mockResolvedValueOnce({
118
+ success: false,
119
+ error: "Client unreachable",
120
+ });
121
+
122
+ await runTasksCommand({ client: "offline-client" });
123
+
124
+ expect(errorSpy).toHaveBeenCalledWith(
125
+ "Failed to list tasks: Client unreachable",
126
+ );
127
+ expect(process.exitCode).toBe(1);
128
+ });
129
+
130
+ it("handles exception thrown during execution", async () => {
131
+ jest
132
+ .spyOn(CliControlClient.prototype, "sendCommand")
133
+ .mockRejectedValueOnce(new Error("Connection reset"));
134
+
135
+ await runTasksCommand({ client: "error-client" });
136
+
137
+ expect(errorSpy).toHaveBeenCalledWith(
138
+ "Error executing tasks command: Connection reset",
139
+ );
140
+ expect(process.exitCode).toBe(1);
141
+ });
64
142
  });