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
@@ -400,5 +400,79 @@ describe("webrtc-listen command", () => {
400
400
  expect(res1.path.includes("shadow-claw")).toBe(true);
401
401
  expect(res1.path.includes("workspace-")).toBe(true);
402
402
  });
403
+
404
+ it("handles error during runWebRtcListenCommand startup", async () => {
405
+ const { CliWebRtcListener } =
406
+ await import("../utils/webrtc-control-client.js");
407
+ jest
408
+ .spyOn(CliWebRtcListener.prototype, "start")
409
+ .mockRejectedValueOnce(new Error("Signaling fail"));
410
+ const errorSpy = jest
411
+ .spyOn(console, "error")
412
+ .mockImplementation(() => {});
413
+ await runWebRtcListenCommand({ cacheDir: tmpDir });
414
+ expect(errorSpy).toHaveBeenCalledWith(
415
+ "Error starting WebRTC listener: Signaling fail",
416
+ );
417
+ expect(process.exitCode).toBe(1);
418
+ errorSpy.mockRestore();
419
+ });
420
+
421
+ it("wires up IPC command result handler in _setupConnection during runWebRtcListenCommand", async () => {
422
+ const { CliWebRtcListener } =
423
+ await import("../utils/webrtc-control-client.js");
424
+ jest
425
+ .spyOn(CliWebRtcListener.prototype, "start")
426
+ .mockImplementation(async function (this: any) {
427
+ const listeners: Record<string, Function> = {};
428
+ const mockConn = {
429
+ on: (ev: string, cb: any) => {
430
+ listeners[ev] = cb;
431
+ },
432
+ };
433
+ let receivedPayload: any = null;
434
+ this._pendingIpc.set("cmd-1", (payload: any) => {
435
+ receivedPayload = payload;
436
+ });
437
+
438
+ this._setupConnection(mockConn, "remote-peer-1");
439
+
440
+ // Test non-object/null
441
+ listeners["data"]?.(null);
442
+ // Test invalid JSON string
443
+ listeners["data"]?.("invalid-json");
444
+ // Test valid command result
445
+ listeners["data"]?.(
446
+ JSON.stringify({
447
+ type: "command:result",
448
+ payload: { commandId: "cmd-1", success: true },
449
+ }),
450
+ );
451
+
452
+ expect(receivedPayload).toEqual({
453
+ commandId: "cmd-1",
454
+ success: true,
455
+ });
456
+ throw new Error("Simulated stop");
457
+ });
458
+
459
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
460
+ const errorSpy = jest
461
+ .spyOn(console, "error")
462
+ .mockImplementation(() => {});
463
+
464
+ await runWebRtcListenCommand({
465
+ cacheDir: tmpDir,
466
+ secure: true,
467
+ insecure: true,
468
+ trustedPeer: ["peer-a", "peer-b"],
469
+ });
470
+
471
+ expect(errorSpy).toHaveBeenCalledWith(
472
+ "Error starting WebRTC listener: Simulated stop",
473
+ );
474
+ logSpy.mockRestore();
475
+ errorSpy.mockRestore();
476
+ });
403
477
  });
404
478
  });
@@ -1,8 +1,9 @@
1
- import { describe, it, expect } from "@jest/globals";
1
+ import { describe, it, expect, jest } from "@jest/globals";
2
2
  import {
3
3
  parseSemver,
4
4
  compareSemver,
5
5
  assertVersionBump,
6
+ main,
6
7
  } from "./assert-version-bump.js";
7
8
 
8
9
  describe("assert-version-bump", () => {
@@ -122,6 +123,34 @@ describe("assert-version-bump", () => {
122
123
  }),
123
124
  ).resolves.toBeUndefined();
124
125
  });
126
+
127
+ it("logs message when silent is false and versions pass", async () => {
128
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
129
+ try {
130
+ await assertVersionBump({
131
+ localPkgVersion: "1.23.4",
132
+ localLockVersion: "1.23.4",
133
+ wellKnownMcpVersion: "1.23.4",
134
+ wellKnownServerCardVersion: "1.23.4",
135
+ pkgName: "shadow-claw",
136
+ publishedVersion: "1.23.3",
137
+ silent: false,
138
+ });
139
+ expect(logSpy).toHaveBeenCalledWith(
140
+ expect.stringContaining("Version assertion passed"),
141
+ );
142
+ } finally {
143
+ logSpy.mockRestore();
144
+ }
145
+ });
146
+
147
+ it("runs main() and validates repository version against npm", async () => {
148
+ try {
149
+ await main();
150
+ } catch (err: any) {
151
+ expect(err.message).toMatch(/Version assertion failed/);
152
+ }
153
+ });
125
154
  });
126
155
 
127
156
  describe("repository live version parity", () => {
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, jest } from "@jest/globals";
2
2
 
3
- import { fileSearchReplace, readStdin } from "./file-search-replace.js";
3
+ import { fileSearchReplace, readStdin, main } from "./file-search-replace.js";
4
4
 
5
5
  describe("readStdin", () => {
6
6
  it("joins stdin chunks and trims the result", async () => {
@@ -69,4 +69,36 @@ describe("fileSearchReplace", () => {
69
69
  ).rejects.toThrow(SyntaxError);
70
70
  expect(readFileImpl).not.toHaveBeenCalled();
71
71
  });
72
+
73
+ it("joins string chunks in readStdin", async () => {
74
+ const stdin = (async function* () {
75
+ yield " non-buffer ";
76
+ yield "chunk \n";
77
+ })();
78
+
79
+ await expect(readStdin(stdin)).resolves.toBe("non-buffer chunk");
80
+ });
81
+
82
+ it("main() replaces content when valid arguments are passed", async () => {
83
+ const fs = await import("node:fs/promises");
84
+ const os = await import("node:os");
85
+ const path = await import("node:path");
86
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fsr-test-"));
87
+ const tempFile = path.join(tempDir, "replace-target.txt");
88
+ await fs.writeFile(tempFile, "apple and orange and apple", "utf8");
89
+
90
+ const origLength = process.argv.length;
91
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
92
+
93
+ try {
94
+ process.argv.push("apple", tempFile, "", "banana");
95
+ await main();
96
+ const updated = await fs.readFile(tempFile, "utf8");
97
+ expect(updated).toBe("banana and orange and banana");
98
+ } finally {
99
+ process.argv.length = origLength;
100
+ logSpy.mockRestore();
101
+ await fs.rm(tempDir, { recursive: true, force: true });
102
+ }
103
+ });
72
104
  });
@@ -1,6 +1,9 @@
1
1
  import { describe, expect, it, jest } from "@jest/globals";
2
2
 
3
- import { patchServiceWorkerTrustedTypesFile } from "./patch-service-worker-trusted-types.js";
3
+ import {
4
+ patchServiceWorkerTrustedTypesFile,
5
+ main,
6
+ } from "./patch-service-worker-trusted-types.js";
4
7
 
5
8
  describe("patchServiceWorkerTrustedTypesFile", () => {
6
9
  it("writes the patched service worker when content changes", async () => {
@@ -69,4 +72,24 @@ describe("patchServiceWorkerTrustedTypesFile", () => {
69
72
  }),
70
73
  ).rejects.toBe(error);
71
74
  });
75
+
76
+ it("main() patches target path specified in process.argv[2]", async () => {
77
+ const fs = await import("node:fs/promises");
78
+ const os = await import("node:os");
79
+ const path = await import("node:path");
80
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "sw-patch-"));
81
+ const tempFile = path.join(tempDir, "service-worker.js");
82
+ await fs.writeFile(tempFile, "/* sw content */", "utf8");
83
+
84
+ const prevArgv = process.argv;
85
+ try {
86
+ process.argv = ["node", "patch.js", tempFile];
87
+ await main();
88
+ const content = await fs.readFile(tempFile, "utf8");
89
+ expect(content).toBeDefined();
90
+ } finally {
91
+ process.argv = prevArgv;
92
+ await fs.rm(tempDir, { recursive: true, force: true });
93
+ }
94
+ });
72
95
  });
@@ -15,4 +15,16 @@ describe("touchNoJekyll", () => {
15
15
  "utf-8",
16
16
  );
17
17
  });
18
+
19
+ it("uses default project root dist/public when targetDir is omitted", async () => {
20
+ const writeFileImpl = jest.fn<any>().mockResolvedValue(undefined);
21
+
22
+ await touchNoJekyll(undefined, { writeFileImpl });
23
+
24
+ expect(writeFileImpl).toHaveBeenCalledWith(
25
+ expect.stringMatching(/dist\/public\/\.nojekyll$/),
26
+ "",
27
+ "utf-8",
28
+ );
29
+ });
18
30
  });
@@ -1,4 +1,11 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
1
+ import {
2
+ describe,
3
+ it,
4
+ expect,
5
+ beforeEach,
6
+ afterEach,
7
+ jest,
8
+ } from "@jest/globals";
2
9
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
10
  import os from "node:os";
4
11
  import path from "node:path";
@@ -125,10 +132,27 @@ describe("src/cli/tools/version.ts", () => {
125
132
  expect(result.versions["shadow-claw"]).toBeDefined();
126
133
  });
127
134
 
128
- it("returns help action when --help flag is provided", async () => {
129
- const result = await runVersion(["--help"], { stdio: "pipe" });
130
- expect(result.action).toBe("help");
131
- expect(result.message).toContain(USAGE_NOTICE);
135
+ it("logs version map when stdio is inherit and no arguments provided", async () => {
136
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
137
+ try {
138
+ const result = await runVersion([], { stdio: "inherit" });
139
+ expect(result.action).toBe("list");
140
+ expect(logSpy).toHaveBeenCalled();
141
+ } finally {
142
+ logSpy.mockRestore();
143
+ }
144
+ });
145
+
146
+ it("returns help action and logs USAGE_NOTICE when stdio is inherit", async () => {
147
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
148
+ try {
149
+ const result = await runVersion(["--help"], { stdio: "inherit" });
150
+ expect(result.action).toBe("help");
151
+ expect(result.message).toContain(USAGE_NOTICE);
152
+ expect(logSpy).toHaveBeenCalledWith(USAGE_NOTICE);
153
+ } finally {
154
+ logSpy.mockRestore();
155
+ }
132
156
  });
133
157
 
134
158
  it("throws error when disallowed keyword is provided", async () => {
@@ -136,5 +160,85 @@ describe("src/cli/tools/version.ts", () => {
136
160
  'Keyword command "minor" is not supported',
137
161
  );
138
162
  });
163
+
164
+ it("handles isLifecycle mode with git directory", async () => {
165
+ const tempDir = await mkdtemp(path.join(os.tmpdir(), "lifecycle-test-"));
166
+ try {
167
+ await writeFile(
168
+ path.join(tempDir, "package.json"),
169
+ JSON.stringify({ name: "test-pkg", version: "2.0.0" }),
170
+ "utf8",
171
+ );
172
+ await mkdir(path.join(tempDir, ".well-known/mcp"), { recursive: true });
173
+ await writeFile(
174
+ path.join(tempDir, ".well-known/mcp.json"),
175
+ JSON.stringify({ version: "1.0.0" }),
176
+ "utf8",
177
+ );
178
+ await mkdir(path.join(tempDir, ".git"), { recursive: true });
179
+
180
+ const result = await runVersion([], {
181
+ isLifecycle: true,
182
+ rootDir: tempDir,
183
+ });
184
+
185
+ expect(result.version).toBe("2.0.0");
186
+ expect(result.updated).toContain(".well-known/mcp.json");
187
+ } finally {
188
+ await rm(tempDir, { recursive: true, force: true });
189
+ }
190
+ });
191
+
192
+ it("handles syncWellKnownVersions JSON parse error gracefully", async () => {
193
+ const tempDir = await mkdtemp(path.join(os.tmpdir(), "corrupt-test-"));
194
+ const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
195
+ try {
196
+ await mkdir(path.join(tempDir, ".well-known"), { recursive: true });
197
+ await writeFile(
198
+ path.join(tempDir, ".well-known/mcp.json"),
199
+ "invalid-json",
200
+ "utf8",
201
+ );
202
+
203
+ const updated = await syncWellKnownVersions("1.2.3", tempDir);
204
+ expect(updated).toEqual([]);
205
+ expect(warnSpy).toHaveBeenCalled();
206
+ } finally {
207
+ warnSpy.mockRestore();
208
+ await rm(tempDir, { recursive: true, force: true });
209
+ }
210
+ });
211
+
212
+ it("executes npm version and syncs well-known when valid semver given", async () => {
213
+ const tempDir = await mkdtemp(
214
+ path.join(os.tmpdir(), "npm-version-test-"),
215
+ );
216
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
217
+ try {
218
+ await writeFile(
219
+ path.join(tempDir, "package.json"),
220
+ JSON.stringify({ name: "version-bump-test", version: "1.0.0" }),
221
+ "utf8",
222
+ );
223
+ await mkdir(path.join(tempDir, ".well-known/mcp"), { recursive: true });
224
+ await writeFile(
225
+ path.join(tempDir, ".well-known/mcp.json"),
226
+ JSON.stringify({ version: "1.0.0" }),
227
+ "utf8",
228
+ );
229
+
230
+ const result = await runVersion(["1.0.1"], {
231
+ rootDir: tempDir,
232
+ stdio: "inherit",
233
+ execOptions: { cwd: tempDir, stdio: "pipe" },
234
+ });
235
+
236
+ expect(result.version).toBe("1.0.1");
237
+ expect(result.updated).toContain(".well-known/mcp.json");
238
+ } finally {
239
+ logSpy.mockRestore();
240
+ await rm(tempDir, { recursive: true, force: true });
241
+ }
242
+ });
139
243
  });
140
244
  });
@@ -0,0 +1,40 @@
1
+ import { jest } from "@jest/globals";
2
+ import { getAgentCore } from "./agent-core.js";
3
+
4
+ describe("agent-core", () => {
5
+ it("loads and caches the headless-agent core module", async () => {
6
+ const core1 = await getAgentCore();
7
+ expect(core1).toBeDefined();
8
+
9
+ // second call should return cached instance
10
+ const core2 = await getAgentCore();
11
+ expect(core2).toBe(core1);
12
+ });
13
+
14
+ it("loads from dist when NODE_ENV is not test", async () => {
15
+ const prevEnv = process.env.NODE_ENV;
16
+ try {
17
+ process.env.NODE_ENV = "production";
18
+ await jest.isolateModulesAsync(async () => {
19
+ const { getAgentCore: isolatedGetAgentCore } =
20
+ await import("./agent-core.js");
21
+ const core = await isolatedGetAgentCore();
22
+ expect(core).toBeDefined();
23
+ });
24
+ } finally {
25
+ process.env.NODE_ENV = prevEnv;
26
+ }
27
+ });
28
+
29
+ it("falls back to dist/headless-agent.js when worker/headless-agent import fails", async () => {
30
+ await jest.isolateModulesAsync(async () => {
31
+ jest.unstable_mockModule("../../worker/headless-agent.js", () => {
32
+ throw new Error("Module failed to load");
33
+ });
34
+ const { getAgentCore: isolatedGetAgentCore } =
35
+ await import("./agent-core.js");
36
+ const core = await isolatedGetAgentCore();
37
+ expect(core).toBeDefined();
38
+ });
39
+ });
40
+ });
@@ -1,13 +1,23 @@
1
1
  import http from "node:http";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
2
5
  import express from "express";
3
- import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
6
+ import {
7
+ describe,
8
+ it,
9
+ expect,
10
+ beforeEach,
11
+ afterEach,
12
+ jest,
13
+ } from "@jest/globals";
4
14
  import {
5
15
  openClientStore,
6
16
  closeClientStore,
7
17
  } from "../../server/client-registry.js";
8
18
  import { createControlPlane } from "../../server/control-plane.js";
9
19
  import { registerBackupRoutes } from "../../server/routes/backup.js";
10
- import { CliControlClient } from "./control-client.js";
20
+ import { CliControlClient, resolveControlToken } from "./control-client.js";
11
21
 
12
22
  describe("src/cli/utils/control-client.ts", () => {
13
23
  let app: any;
@@ -39,6 +49,27 @@ describe("src/cli/utils/control-client.ts", () => {
39
49
  res.json({ sent: 1, failed: 0, received: req.body });
40
50
  });
41
51
 
52
+ app.post("/api/control/command", (req: any, res: any) => {
53
+ res.json({ ok: true, result: "command-executed", body: req.body });
54
+ });
55
+
56
+ app.delete("/api/backup/:id", (req: any, res: any) => {
57
+ res.json({ ok: true, deleted: req.params.id });
58
+ });
59
+
60
+ app.get("/push/subscriptions", (_req: any, res: any) => {
61
+ res.json([
62
+ { client_id: "client-sub-1", device_label: "Device 1" },
63
+ { client_id: "client-sub-2" },
64
+ ]);
65
+ });
66
+
67
+ app.get("/push/clients", (_req: any, res: any) => {
68
+ res.json({
69
+ clients: [{ clientId: "client-push-1", deviceLabel: "Push 1" }],
70
+ });
71
+ });
72
+
42
73
  await new Promise<void>((resolve) => {
43
74
  server.listen(0, "127.0.0.1", () => {
44
75
  port = server.address().port;
@@ -149,4 +180,210 @@ describe("src/cli/utils/control-client.ts", () => {
149
180
  expect(Array.isArray(clients)).toBe(true);
150
181
  expect(client.token).toBe(token);
151
182
  });
183
+
184
+ it("deletes a backup via deleteBackup", async () => {
185
+ const client = new CliControlClient({
186
+ host: "127.0.0.1",
187
+ port,
188
+ token,
189
+ });
190
+ const res = await client.deleteBackup("backup-123", "client-1");
191
+ expect(res).toEqual({ success: true, deleted: "backup-123" });
192
+ });
193
+
194
+ it("lists push subscriptions and clients", async () => {
195
+ const client = new CliControlClient({
196
+ host: "127.0.0.1",
197
+ port,
198
+ token,
199
+ });
200
+ const subs = await client.listPushSubscriptions();
201
+ expect(subs).toHaveLength(2);
202
+
203
+ const pushClients = await client.listPushClients();
204
+ expect(pushClients).toHaveLength(1);
205
+ expect(pushClients[0].clientId).toBe("client-push-1");
206
+ });
207
+
208
+ it("sends commands and files via sendCommand and sendFile", async () => {
209
+ const client = new CliControlClient({
210
+ host: "127.0.0.1",
211
+ port,
212
+ token,
213
+ });
214
+ await expect(
215
+ client.sendCommand("client-1", "ping", { foo: "bar" }),
216
+ ).rejects.toThrow("Client not connected: client-1");
217
+
218
+ // sendFile error: file not found
219
+ await expect(
220
+ client.sendFile("client-1", "/non/existent/file.txt"),
221
+ ).rejects.toThrow("File not found");
222
+
223
+ // sendFile error: directory
224
+ await expect(client.sendFile("client-1", os.tmpdir())).rejects.toThrow(
225
+ "Cannot send directory",
226
+ );
227
+
228
+ // sendFile success attempt
229
+ const tmpFile = path.join(os.tmpdir(), `test-file-${Date.now()}.txt`);
230
+ fs.writeFileSync(tmpFile, "hello shadow-claw", "utf8");
231
+ try {
232
+ await expect(
233
+ client.sendFile("client-1", tmpFile, { prompt: "Read this" }),
234
+ ).rejects.toThrow("Client not connected: client-1");
235
+ } finally {
236
+ fs.unlinkSync(tmpFile);
237
+ }
238
+ });
239
+
240
+ it("supports WebRTC transport delegation and fallbacks", async () => {
241
+ const client = new CliControlClient({
242
+ host: "127.0.0.1",
243
+ port,
244
+ token,
245
+ transport: "webrtc",
246
+ });
247
+ expect(client._webrtcClient).not.toBeNull();
248
+
249
+ // 1. listClients with webrtc clients
250
+ client._webrtcClient!.listClients = jest
251
+ .fn()
252
+ .mockResolvedValue([
253
+ { clientId: "client-peer-ok", peerId: "peer-ok-id" },
254
+ { clientId: "client-no-peer" },
255
+ ]);
256
+ client._webrtcClient!.sendCommand = jest
257
+ .fn()
258
+ .mockResolvedValue({ ok: true, webrtc: true });
259
+
260
+ const clients = await client.listClients();
261
+ expect(clients[0].clientId).toBe("client-peer-ok");
262
+
263
+ // 2. sendCommand with matching peerId
264
+ const cmdRes = await client.sendCommand(
265
+ "client-peer-ok",
266
+ "test-action",
267
+ {},
268
+ );
269
+ expect(cmdRes).toEqual({ ok: true, webrtc: true });
270
+ expect(client._webrtcClient!.sendCommand).toHaveBeenCalledWith(
271
+ "peer-ok-id",
272
+ "test-action",
273
+ {},
274
+ 30000,
275
+ );
276
+
277
+ // 3. sendCommand with matching client that has NO peerId (triggers warn)
278
+ const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
279
+ try {
280
+ await client.sendCommand("client-no-peer", "test-action", {});
281
+ expect(warnSpy).toHaveBeenCalledWith(
282
+ expect.stringContaining("has no PeerJS peer ID registered"),
283
+ );
284
+ } finally {
285
+ warnSpy.mockRestore();
286
+ }
287
+
288
+ // 4. listClients fallback to control plane when webrtc listClients returns empty
289
+ client._webrtcClient!.listClients = jest.fn().mockResolvedValue([]);
290
+ const cpClients = await client.listClients();
291
+ expect(Array.isArray(cpClients)).toBe(true);
292
+ });
293
+
294
+ it("resolves control token from cacheDir and custom options", () => {
295
+ expect(resolveControlToken("custom-tok-123")).toBe("custom-tok-123");
296
+
297
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ctrl-tok-test-"));
298
+ try {
299
+ const tokFile = path.join(tempDir, "control-token.json");
300
+ fs.writeFileSync(
301
+ tokFile,
302
+ JSON.stringify({ token: "cached-file-tok" }),
303
+ "utf8",
304
+ );
305
+ const resolved = resolveControlToken(undefined, tempDir);
306
+ expect(resolved).toBe("cached-file-tok");
307
+ } finally {
308
+ fs.rmSync(tempDir, { recursive: true, force: true });
309
+ }
310
+ });
311
+
312
+ it("falls back to listPushSubscriptions when /push/clients fails", async () => {
313
+ const client = new CliControlClient({
314
+ host: "127.0.0.1",
315
+ port,
316
+ token,
317
+ });
318
+ jest
319
+ .spyOn(client as any, "_request")
320
+ .mockImplementation(async (opts: any) => {
321
+ if (opts.path === "/push/clients") {
322
+ throw new Error("404 Not Found");
323
+ }
324
+ if (opts.path === "/push/subscriptions") {
325
+ return [
326
+ { client_id: "client-sub-1", device_label: "Device 1" },
327
+ { client_id: "client-sub-1", device_label: "Device 1 duplicate" },
328
+ { id: 42, device_label: "Device ID Only" },
329
+ ];
330
+ }
331
+ return {};
332
+ });
333
+
334
+ const pushClients = await client.listPushClients();
335
+ expect(pushClients).toHaveLength(2);
336
+ expect(pushClients[0]).toEqual({
337
+ clientId: "client-sub-1",
338
+ deviceLabel: "Device 1",
339
+ });
340
+ expect(pushClients[1]).toEqual({
341
+ clientId: "42",
342
+ deviceLabel: "Device ID Only",
343
+ });
344
+ });
345
+
346
+ it("falls back to sqlite push-subscriptions.db when both endpoints fail", async () => {
347
+ const tempDir = fs.mkdtempSync(
348
+ path.join(os.tmpdir(), "control-client-sqlite-"),
349
+ );
350
+ try {
351
+ const sqlite = (process as any).getBuiltinModule?.("node:sqlite");
352
+ if (sqlite && sqlite.DatabaseSync) {
353
+ const dbFile = path.join(tempDir, "push-subscriptions.db");
354
+ const db = new sqlite.DatabaseSync(dbFile);
355
+ db.exec(
356
+ "CREATE TABLE subscriptions (id INTEGER PRIMARY KEY, client_id TEXT, device_label TEXT, created_at INTEGER);",
357
+ );
358
+ db.prepare(
359
+ "INSERT INTO subscriptions (id, client_id, device_label, created_at) VALUES (?, ?, ?, ?)",
360
+ ).run(1, "sql-client-1", "Desk 1", Date.now());
361
+ db.prepare(
362
+ "INSERT INTO subscriptions (id, client_id, device_label, created_at) VALUES (?, ?, ?, ?)",
363
+ ).run(2, "", "No CID", Date.now());
364
+ db.close();
365
+
366
+ const client = new CliControlClient({
367
+ host: "127.0.0.1",
368
+ port,
369
+ token,
370
+ cacheDir: tempDir,
371
+ });
372
+ jest
373
+ .spyOn(client as any, "_request")
374
+ .mockRejectedValue(new Error("Network failure"));
375
+
376
+ const clients = await client.listPushClients();
377
+ expect(clients.length).toBe(2);
378
+ const c1 = clients.find((c) => c.clientId === "sql-client-1");
379
+ expect(c1).toBeDefined();
380
+ expect(c1?.deviceLabel).toBe("Desk 1");
381
+ const c2 = clients.find((c) => c.clientId === "2");
382
+ expect(c2).toBeDefined();
383
+ expect(c2?.deviceLabel).toBe("No CID");
384
+ }
385
+ } finally {
386
+ fs.rmSync(tempDir, { recursive: true, force: true });
387
+ }
388
+ });
152
389
  });