hyperclaw 4.0.1 → 5.0.0

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 (277) hide show
  1. package/README.md +58 -4
  2. package/dist/a2ui-protocol-CT_jDEU9.js +75 -0
  3. package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
  4. package/dist/agents-routing-683Q2JGp.js +129 -0
  5. package/dist/agents-routing-BpZBswBH.js +4 -0
  6. package/dist/agents-routing-ChHiZp36.js +327 -0
  7. package/dist/agents-routing-ChqZ6l2S.js +4 -0
  8. package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
  9. package/dist/api-keys-guide-Dq5Obbp4.js +149 -0
  10. package/dist/audit-BYxPlnTQ.js +248 -0
  11. package/dist/audit-BaIiyWFu.js +441 -0
  12. package/dist/bounty-tools-C6LyzxM-.js +211 -0
  13. package/dist/bounty-tools-DWudyZie.js +211 -0
  14. package/dist/browser-tools-BsTeGMnX.js +5 -0
  15. package/dist/browser-tools-CQBSbIuO.js +5 -0
  16. package/dist/browser-tools-D8_rLe2p.js +179 -0
  17. package/dist/browser-tools-YQmwRLLM.js +179 -0
  18. package/dist/claw-tasks-BRLUvFRD.js +80 -0
  19. package/dist/claw-tasks-CgTsiNE8.js +80 -0
  20. package/dist/connector-3HnyH8fn.js +167 -0
  21. package/dist/connector-5N0-X_xs.js +194 -0
  22. package/dist/connector-6PMZo5Ky.js +189 -0
  23. package/dist/connector-B3v0qcXg.js +425 -0
  24. package/dist/connector-B6eoF3DD.js +181 -0
  25. package/dist/connector-B8R3iBY1.js +280 -0
  26. package/dist/connector-B9tLG8UZ.js +196 -0
  27. package/dist/connector-BAM-08NN.js +189 -0
  28. package/dist/connector-BC8FIVu4.js +181 -0
  29. package/dist/connector-BDmwwaVc.js +213 -0
  30. package/dist/connector-BGjbBy69.js +225 -0
  31. package/dist/connector-BO2SRzfG.js +218 -0
  32. package/dist/connector-BOlqjXWP.js +182 -0
  33. package/dist/connector-BP8zsbP8.js +189 -0
  34. package/dist/connector-BPoSevxp.js +286 -0
  35. package/dist/connector-BRHj773i.js +163 -0
  36. package/dist/connector-BToxU-jV.js +267 -0
  37. package/dist/connector-BfXky0L3.js +167 -0
  38. package/dist/connector-BiiSJpx3.js +192 -0
  39. package/dist/connector-BliDVsJQ.js +239 -0
  40. package/dist/connector-BnDmIhIu.js +85 -0
  41. package/dist/connector-Bv6s9oP7.js +88 -0
  42. package/dist/connector-By5wWGTR.js +343 -0
  43. package/dist/connector-C1BaFFgN.js +213 -0
  44. package/dist/connector-C1HSoUyk.js +189 -0
  45. package/dist/connector-CKQHZOXg.js +568 -0
  46. package/dist/connector-CRRWY5Wv.js +167 -0
  47. package/dist/connector-CRl-iidy.js +239 -0
  48. package/dist/connector-CXPQVGyI.js +85 -0
  49. package/dist/connector-Cdk1CXKi.js +194 -0
  50. package/dist/connector-Ci9glMD-.js +340 -0
  51. package/dist/connector-CjtZIEDj.js +181 -0
  52. package/dist/connector-Ck6JtOsX.js +531 -0
  53. package/dist/connector-CwlgFgjx.js +181 -0
  54. package/dist/connector-D8Kelee0.js +286 -0
  55. package/dist/connector-DAnRJ0oP.js +162 -0
  56. package/dist/connector-DFchk6l7.js +178 -0
  57. package/dist/connector-DKw7tRAy.js +192 -0
  58. package/dist/connector-DRv1ahC_.js +2 -2
  59. package/dist/connector-DU63KW94.js +165 -0
  60. package/dist/connector-DXTp5PE8.js +508 -0
  61. package/dist/connector-Dbvb1Cj9.js +280 -0
  62. package/dist/connector-DcZdQcgR.js +173 -0
  63. package/dist/connector-Dih6dUPP.js +173 -0
  64. package/dist/connector-DqTH_tPX.js +182 -0
  65. package/dist/connector-DrnEiiyP.js +419 -0
  66. package/dist/connector-DtR5GGTX.js +167 -0
  67. package/dist/connector-DxKL8VvZ.js +182 -0
  68. package/dist/connector-T_YdZtzv.js +162 -0
  69. package/dist/connector-Tky_qS_K.js +350 -0
  70. package/dist/connector-ZSc3oTTy.js +305 -0
  71. package/dist/connector-i4gOS9xL.js +154 -0
  72. package/dist/connector-rHXE1ZD2.js +167 -0
  73. package/dist/connector-sW5yhU1m.js +498 -0
  74. package/dist/connector-u3ICd3Ic.js +552 -0
  75. package/dist/connector-wdUXChwa.js +172 -0
  76. package/dist/cost-tracker-DD9wtWsr.js +103 -0
  77. package/dist/cost-tracker-pVE15Yq4.js +103 -0
  78. package/dist/credentials-store-BvnMPJwi.js +4 -0
  79. package/dist/credentials-store-C6ir0Dae.js +4 -0
  80. package/dist/credentials-store-H13LqOwJ.js +77 -0
  81. package/dist/credentials-store-sb-TRLwR.js +77 -0
  82. package/dist/cron-tasks-Bli7Kzd2.js +82 -0
  83. package/dist/cron-tasks-BvDFNyiE.js +82 -0
  84. package/dist/daemon-Bg4GtCmc.js +318 -0
  85. package/dist/daemon-DhmwY8k4.js +5 -0
  86. package/dist/delivery-BmIYy9VQ.js +4 -0
  87. package/dist/delivery-D5Z98EVq.js +95 -0
  88. package/dist/delivery-DCOXhXEO.js +5 -0
  89. package/dist/delivery-pWUPBp1F.js +95 -0
  90. package/dist/destructive-gate-D6vWOdEl.js +101 -0
  91. package/dist/destructive-gate-m-dWqUFg.js +101 -0
  92. package/dist/developer-keys-CPWT7Q6S.js +8 -0
  93. package/dist/developer-keys-DrrcUqFa.js +127 -0
  94. package/dist/developer-keys-JaJK3T27.js +127 -0
  95. package/dist/developer-keys-kyqmtWK3.js +8 -0
  96. package/dist/doctor-3oi89QIc.js +175 -0
  97. package/dist/doctor-BvCe8BBk.js +230 -0
  98. package/dist/doctor-Cf1XSfp9.js +4 -0
  99. package/dist/doctor-CxyPLYsJ.js +6 -0
  100. package/dist/engine-B4eMiTgl.js +7 -0
  101. package/dist/engine-B8M7dYul.js +7 -0
  102. package/dist/engine-BhT-1M9W.js +256 -0
  103. package/dist/engine-CEDSqXfw.js +256 -0
  104. package/dist/engine-D49jnSd_.js +256 -0
  105. package/dist/engine-Da4JMNpI.js +7 -0
  106. package/dist/env-resolve-CiXbWYwe.js +10 -0
  107. package/dist/env-resolve-CmGWhWXJ.js +115 -0
  108. package/dist/env-resolve-DWOQ45jG.js +9 -0
  109. package/dist/env-resolve-szSWl0UF.js +94 -0
  110. package/dist/extraction-tools-D3qDFBJ1.js +91 -0
  111. package/dist/extraction-tools-DLr_AEwq.js +5 -0
  112. package/dist/extraction-tools-HOZstZ0y.js +91 -0
  113. package/dist/extraction-tools-m4lmAv7l.js +5 -0
  114. package/dist/form_data-B_hIUrxU.js +8657 -0
  115. package/dist/form_data-Cz040rio.js +8657 -0
  116. package/dist/gmail-watch-setup-Czt8rXaX.js +40 -0
  117. package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
  118. package/dist/health-B-asI__D.js +6 -0
  119. package/dist/health-Ds2YlpTB.js +152 -0
  120. package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
  121. package/dist/heartbeat-engine-CRqfPcFM.js +83 -0
  122. package/dist/hub-D0XwdjM-.js +515 -0
  123. package/dist/hub-DTsqe5Bt.js +6 -0
  124. package/dist/hub-FrPTA33j.js +515 -0
  125. package/dist/hub-LiD5Iztb.js +6 -0
  126. package/dist/hyperclawbot-D9KCtc4P.js +480 -0
  127. package/dist/hyperclawbot-Dw27pJo4.js +480 -0
  128. package/dist/hyperclawbot-zvczQgKx.js +505 -0
  129. package/dist/inference-BKVkBREb.js +6 -0
  130. package/dist/inference-CTWJeX9Q.js +922 -0
  131. package/dist/inference-DCXH4Q3x.js +922 -0
  132. package/dist/inference-ix607p7k.js +6 -0
  133. package/dist/knowledge-graph-DqA-Fztl.js +131 -0
  134. package/dist/knowledge-graph-iBG76fvm.js +131 -0
  135. package/dist/loader-CC45xGpC.js +4 -0
  136. package/dist/loader-CISCqBto.js +400 -0
  137. package/dist/loader-CYMQ8VOS.js +4 -0
  138. package/dist/loader-CnEdOyjT.js +400 -0
  139. package/dist/logger-8tEtAd3y.js +83 -0
  140. package/dist/logger-ybOp7VOC.js +83 -0
  141. package/dist/manager-03ipO9R0.js +105 -0
  142. package/dist/manager-BpDfbDjg.js +117 -0
  143. package/dist/manager-Bxl0sqlh.js +4 -0
  144. package/dist/manager-CPjeRe-6.js +4 -0
  145. package/dist/manager-CrVDn6eN.js +6 -0
  146. package/dist/manager-Cwzj7w5R.js +105 -0
  147. package/dist/manager-DLmZI-9R.js +6 -0
  148. package/dist/manager-DSGhn5i3.js +117 -0
  149. package/dist/manager-DgyF52mg.js +218 -0
  150. package/dist/manager-Dm8nrMFx.js +40 -0
  151. package/dist/manager-FCgF1plu.js +218 -0
  152. package/dist/manager-rgCsaWT1.js +40 -0
  153. package/dist/mcp-B_9Ber63.js +139 -0
  154. package/dist/mcp-CfoSU4Uz.js +139 -0
  155. package/dist/mcp-loader-DSM5UiFG.js +94 -0
  156. package/dist/mcp-loader-DkRBsLpk.js +94 -0
  157. package/dist/mcp-loader-j5ZLLw5O.js +94 -0
  158. package/dist/memory-BI1kPkAN.js +4 -0
  159. package/dist/memory-BVFGkxxK.js +270 -0
  160. package/dist/memory-BlHL7JCO.js +4 -0
  161. package/dist/memory-DsS_eFvJ.js +270 -0
  162. package/dist/memory-auto-Bc7euou4.js +306 -0
  163. package/dist/memory-auto-BkvtSFUw.js +5 -0
  164. package/dist/memory-auto-Bnz_-1wP.js +306 -0
  165. package/dist/memory-auto-DPfbkMVt.js +5 -0
  166. package/dist/memory-integration-DZExqWr4.js +91 -0
  167. package/dist/memory-integration-cSYkZyEo.js +91 -0
  168. package/dist/moltbook-B6ZeGN5_.js +81 -0
  169. package/dist/moltbook-BtLDZTfM.js +81 -0
  170. package/dist/node-Dw2Gi-cP.js +222 -0
  171. package/dist/node-pwL6O_KX.js +222 -0
  172. package/dist/nodes-registry-B8dmrlLv.js +52 -0
  173. package/dist/nodes-registry-CsPm_-CJ.js +52 -0
  174. package/dist/oauth-flow-CpWlgvNB.js +150 -0
  175. package/dist/oauth-flow-DQPvMHRH.js +150 -0
  176. package/dist/oauth-provider-BZb6qOw5.js +110 -0
  177. package/dist/oauth-provider-Uo4Nib_c.js +110 -0
  178. package/dist/observability-B43YvNQV.js +89 -0
  179. package/dist/observability-BV-Yx0V9.js +89 -0
  180. package/dist/onboard-0WoDxbv_.js +10 -0
  181. package/dist/onboard-BXNXCQp4.js +4070 -0
  182. package/dist/onboard-Bd_wsYdi.js +4086 -0
  183. package/dist/onboard-CAN7x3me.js +3026 -0
  184. package/dist/onboard-DnegOHMh.js +4 -4
  185. package/dist/onboard-RYtDlYBw.js +9 -0
  186. package/dist/onboard-aTwlQs-4.js +9 -0
  187. package/dist/orchestrator-BSp2M5EU.js +189 -0
  188. package/dist/orchestrator-C7ko5tWa.js +6 -0
  189. package/dist/orchestrator-DfPkIx2Z.js +6 -0
  190. package/dist/orchestrator-DmnEvMaL.js +189 -0
  191. package/dist/orchestrator-NJQsmiBE.js +189 -0
  192. package/dist/orchestrator-RI3bpqqc.js +6 -0
  193. package/dist/pairing-6iM27aD8.js +196 -0
  194. package/dist/pairing-DU0_J28n.js +87 -0
  195. package/dist/pairing-DWllbSbO.js +4 -0
  196. package/dist/pairing-dGoiGepK.js +4 -0
  197. package/dist/pc-access-CgCsYrpt.js +8 -0
  198. package/dist/pc-access-Ly-uA8mn.js +8 -0
  199. package/dist/pc-access-NxBvTrRj.js +819 -0
  200. package/dist/pc-access-_iH2aorG.js +819 -0
  201. package/dist/pending-approval-CUXjysAo.js +22 -0
  202. package/dist/pending-approval-DIHvwwWS.js +22 -0
  203. package/dist/puppeteer-2o3QOwAy.js +2 -2
  204. package/dist/puppeteer-BYTMp3BI.js +2 -2
  205. package/dist/puppeteer-DQ45qwWk.js +2 -2
  206. package/dist/reminders-store-D79qdfN0.js +58 -0
  207. package/dist/reminders-store-Drjed_-h.js +58 -0
  208. package/dist/renderer-BVQrd0_g.js +225 -0
  209. package/dist/renderer-pqlDRKbH.js +225 -0
  210. package/dist/rules-BE4GV6cV.js +103 -0
  211. package/dist/rules-BooT_qFP.js +103 -0
  212. package/dist/run-main.js +1649 -1227
  213. package/dist/runner-D1rjuMTJ.js +810 -0
  214. package/dist/runner-DatMMYYE.js +1271 -0
  215. package/dist/sdk/index.js +2 -2
  216. package/dist/sdk/index.mjs +2 -2
  217. package/dist/security-BqNyT4ID.js +4 -0
  218. package/dist/security-C-5URby1.js +73 -0
  219. package/dist/security-_xve79aq.js +4 -0
  220. package/dist/security-tpgqPWWH.js +73 -0
  221. package/dist/server-0kgyELx4.js +1047 -0
  222. package/dist/server-BIuTobTC.js +4 -0
  223. package/dist/server-BRlCEjyT.js +1047 -0
  224. package/dist/server-CCI1hv45.js +2 -2
  225. package/dist/server-D4wVHiX9.js +4 -0
  226. package/dist/server-DU9POoWc.js +4 -0
  227. package/dist/server-Dh3JlBFB.js +1255 -0
  228. package/dist/session-store-BUiPz0Vv.js +5 -0
  229. package/dist/session-store-CujxByI6.js +113 -0
  230. package/dist/session-store-is4B6qmD.js +113 -0
  231. package/dist/session-store-qpJUg2M1.js +5 -0
  232. package/dist/sessions-tools-CB2qbwIk.js +5 -0
  233. package/dist/sessions-tools-CbUTFe4i.js +5 -0
  234. package/dist/sessions-tools-CeqD7iil.js +95 -0
  235. package/dist/sessions-tools-DHMaTZIs.js +95 -0
  236. package/dist/skill-loader-BaNLVmJy.js +7 -0
  237. package/dist/skill-loader-BkceKkIg.js +7 -0
  238. package/dist/skill-loader-DhgIwK4J.js +159 -0
  239. package/dist/skill-loader-HgpF6Vqs.js +159 -0
  240. package/dist/skill-runtime--LqxWrp5.js +102 -0
  241. package/dist/skill-runtime-C5l0Tgt-.js +5 -0
  242. package/dist/skill-runtime-CJN24QPW.js +102 -0
  243. package/dist/skill-runtime-DsXK_HYG.js +102 -0
  244. package/dist/skill-runtime-IVTiqrMR.js +5 -0
  245. package/dist/skill-runtime-w1ig_lcw.js +5 -0
  246. package/dist/src-BEVLgaF1.js +63 -0
  247. package/dist/src-Bgu_OxTQ.js +458 -0
  248. package/dist/src-Bq-oKt7Z.js +458 -0
  249. package/dist/src-BxPHKO5x.js +63 -0
  250. package/dist/src-DIc-L2IG.js +20 -0
  251. package/dist/src-DWCUhnD4.js +20 -0
  252. package/dist/src-cfRTjFef.js +63 -0
  253. package/dist/src-g_rNx5rh.js +458 -0
  254. package/dist/sub-agent-tools-BD9DF8_g.js +39 -0
  255. package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
  256. package/dist/sub-agent-tools-V7b3T9_s.js +39 -0
  257. package/dist/theme-DcxwcUgZ.js +180 -0
  258. package/dist/theme-cx0fkgWC.js +8 -0
  259. package/dist/tool-policy-CNT-mF2Z.js +189 -0
  260. package/dist/tool-policy-DNvNRnve.js +189 -0
  261. package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
  262. package/dist/tts-elevenlabs-BUOGKL-k.js +61 -0
  263. package/dist/update-check-BD4qH7Am.js +81 -0
  264. package/dist/update-check-C2Dz85wJ.js +81 -0
  265. package/dist/vision-BMmiIKy7.js +121 -0
  266. package/dist/vision-DRq-f-Dj.js +121 -0
  267. package/dist/vision-tools-CFZEpQKm.js +5 -0
  268. package/dist/vision-tools-CQnBI9aa.js +51 -0
  269. package/dist/vision-tools-DVuYc17I.js +51 -0
  270. package/dist/vision-tools-U3YC4L-g.js +5 -0
  271. package/dist/voice-transcription-B555DbWR.js +138 -0
  272. package/dist/voice-transcription-CgWq54hn.js +138 -0
  273. package/dist/website-watch-tools-Bk_TnwuE.js +5 -0
  274. package/dist/website-watch-tools-DFMrJU-R.js +139 -0
  275. package/dist/website-watch-tools-DraMPxdl.js +139 -0
  276. package/dist/website-watch-tools-Du3W5sN7.js +5 -0
  277. package/package.json +1 -1
package/dist/run-main.js CHANGED
@@ -1,17 +1,22 @@
1
1
  const require_chunk = require('./chunk-jS-bbMI5.js');
2
- const require_paths = require('./paths-AIyBxIzm.js');
3
- const require_paths$1 = require('./paths-DPovhojT.js');
4
- require('./env-resolve-BzDlV2CS.js');
5
- const require_onboard = require('./onboard-DnegOHMh.js');
6
- require('./server-CCI1hv45.js');
7
- require('./theme-LUTKWUWd.js');
8
- const require_manager = require('./manager-DWQSp1oL.js');
9
- const require_manager$1 = require('./manager-C4ZdCiPx.js');
10
- const require_memory = require('./memory-DvsoY6nv.js');
11
- const require_loader = require('./loader-BR-QFJOm.js');
12
- const require_pairing = require('./pairing-tNnYsLWG.js');
13
- const require_security = require('./security-CVzgwvDN.js');
14
- const require_developer_keys = require('./developer-keys-BNo6wlFV.js');
2
+ require('./paths-AIyBxIzm.js');
3
+ require('./paths-DPovhojT.js');
4
+ require('./env-resolve-CmGWhWXJ.js');
5
+ const require_onboard = require('./onboard-BXNXCQp4.js');
6
+ require('./server-Dh3JlBFB.js');
7
+ const require_daemon = require('./daemon-Bg4GtCmc.js');
8
+ require('./theme-DcxwcUgZ.js');
9
+ const require_hub = require('./hub-D0XwdjM-.js');
10
+ const require_manager = require('./manager-rgCsaWT1.js');
11
+ const require_manager$1 = require('./manager-03ipO9R0.js');
12
+ const require_memory = require('./memory-DsS_eFvJ.js');
13
+ const require_loader = require('./loader-CnEdOyjT.js');
14
+ const require_agents_routing = require('./agents-routing-ChHiZp36.js');
15
+ const require_pairing = require('./pairing-6iM27aD8.js');
16
+ const require_doctor = require('./doctor-BvCe8BBk.js');
17
+ const require_health = require('./health-Ds2YlpTB.js');
18
+ const require_security = require('./security-tpgqPWWH.js');
19
+ const require_developer_keys = require('./developer-keys-DrrcUqFa.js');
15
20
  const commander = require_chunk.__toESM(require("commander"));
16
21
  const chalk = require_chunk.__toESM(require("chalk"));
17
22
  const inquirer = require_chunk.__toESM(require("inquirer"));
@@ -19,507 +24,14 @@ const ora = require_chunk.__toESM(require("ora"));
19
24
  const fs_extra = require_chunk.__toESM(require("fs-extra"));
20
25
  const path = require_chunk.__toESM(require("path"));
21
26
  const os = require_chunk.__toESM(require("os"));
27
+ const crypto = require_chunk.__toESM(require("crypto"));
22
28
  const child_process = require_chunk.__toESM(require("child_process"));
23
29
  const util = require_chunk.__toESM(require("util"));
24
30
  const http = require_chunk.__toESM(require("http"));
25
- const https = require_chunk.__toESM(require("https"));
26
- const readline = require_chunk.__toESM(require("readline"));
27
- const tar = require_chunk.__toESM(require("tar"));
28
31
  const fs = require_chunk.__toESM(require("fs"));
32
+ const readline = require_chunk.__toESM(require("readline"));
29
33
  const net = require_chunk.__toESM(require("net"));
30
34
 
31
- //#region src/skills/clawhub.ts
32
- require_paths$1.init_paths();
33
- const CLAWHUB_API = process.env.CLAWHUB_API_URL || "https://clawhub.com";
34
- const WORKSPACE_SKILLS$1 = path.default.join(require_paths.getHyperClawDir(), "workspace", "skills");
35
- async function searchSkills(query, category) {
36
- const q = new URLSearchParams({ q: query });
37
- if (category) q.set("category", category);
38
- const url = `${CLAWHUB_API}/api/skills/search?${q}`;
39
- try {
40
- const body = await fetchJson(url);
41
- return Array.isArray(body.skills) ? body.skills : Array.isArray(body) ? body : [];
42
- } catch (e) {
43
- return [];
44
- }
45
- }
46
- async function installSkill(skillId, version) {
47
- const ver = version ? `@${version}` : "";
48
- const url = `${CLAWHUB_API}/api/skills/${encodeURIComponent(skillId)}/download${ver}`;
49
- try {
50
- const body = await fetchJson(url);
51
- const tarballUrl = body.url || body.tarball;
52
- if (!tarballUrl) throw new Error("No download URL in registry response");
53
- await fs_extra.default.ensureDir(WORKSPACE_SKILLS$1);
54
- const destDir = path.default.join(WORKSPACE_SKILLS$1, skillId);
55
- await fs_extra.default.ensureDir(destDir);
56
- if (body.content || body.skillMarkdown) {
57
- const content = body.content || body.skillMarkdown;
58
- await fs_extra.default.writeFile(path.default.join(destDir, "SKILL.md"), content, "utf8");
59
- return destDir;
60
- }
61
- const tarballBuffer = await fetchBuffer(tarballUrl);
62
- const extractDir = path.default.join(path.default.dirname(destDir), `.skill-extract-${skillId}-${Date.now()}`);
63
- await fs_extra.default.ensureDir(extractDir);
64
- try {
65
- const tarPath = path.default.join(extractDir, "skill.tar.gz");
66
- await fs_extra.default.writeFile(tarPath, tarballBuffer);
67
- await tar.default.x({
68
- file: tarPath,
69
- cwd: extractDir
70
- });
71
- await fs_extra.default.remove(tarPath);
72
- const entries = await fs_extra.default.readdir(extractDir);
73
- let skillDir = extractDir;
74
- const topSkill = path.default.join(extractDir, "SKILL.md");
75
- if (!await fs_extra.default.pathExists(topSkill)) {
76
- const sub = entries.find((e) => e !== "package.json" && !e.startsWith("."));
77
- if (sub) {
78
- const subPath = path.default.join(extractDir, sub);
79
- if ((await fs_extra.default.stat(subPath)).isDirectory() && await fs_extra.default.pathExists(path.default.join(subPath, "SKILL.md"))) skillDir = subPath;
80
- }
81
- }
82
- await fs_extra.default.copy(skillDir, destDir, { filter: (src) => !src.includes("node_modules") });
83
- if (!await fs_extra.default.pathExists(path.default.join(destDir, "SKILL.md"))) throw new Error("Tarball did not contain SKILL.md");
84
- return destDir;
85
- } finally {
86
- await fs_extra.default.remove(extractDir).catch(() => {});
87
- }
88
- } catch (e) {
89
- if (e.message?.includes("ENOTFOUND") || e.code === "ENOTFOUND") throw new Error(`ClawHub registry unavailable. Install manually: mkdir -p ~/.hyperclaw/workspace/skills/${skillId} && add SKILL.md`);
90
- throw e;
91
- }
92
- }
93
- async function listInstalledFromClawHub() {
94
- if (!await fs_extra.default.pathExists(WORKSPACE_SKILLS$1)) return [];
95
- const dirs = await fs_extra.default.readdir(WORKSPACE_SKILLS$1);
96
- const out = [];
97
- for (const id of dirs) {
98
- const p = path.default.join(WORKSPACE_SKILLS$1, id, "SKILL.md");
99
- if (await fs_extra.default.pathExists(p)) out.push(id);
100
- }
101
- return out;
102
- }
103
- function fetchBuffer(url) {
104
- return new Promise((resolve, reject) => {
105
- const parsed = new URL(url);
106
- const mod = parsed.protocol === "https:" ? https.default : http.default;
107
- const req = mod.request({
108
- hostname: parsed.hostname,
109
- port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
110
- path: parsed.pathname + parsed.search,
111
- method: "GET",
112
- headers: { "User-Agent": "HyperClaw/4.0.1" }
113
- }, (res) => {
114
- const chunks = [];
115
- res.on("data", (c) => chunks.push(c));
116
- res.on("end", () => resolve(Buffer.concat(chunks)));
117
- });
118
- req.on("error", reject);
119
- req.setTimeout(3e4, () => {
120
- req.destroy();
121
- reject(new Error("Tarball download timeout"));
122
- });
123
- req.end();
124
- });
125
- }
126
- function fetchJson(url) {
127
- return new Promise((resolve, reject) => {
128
- const parsed = new URL(url);
129
- const req = https.default.request({
130
- hostname: parsed.hostname,
131
- port: 443,
132
- path: parsed.pathname + parsed.search,
133
- method: "GET",
134
- headers: { "User-Agent": "HyperClaw/4.0.1" }
135
- }, (res) => {
136
- let data = "";
137
- res.on("data", (c) => data += c);
138
- res.on("end", () => {
139
- try {
140
- resolve(JSON.parse(data));
141
- } catch {
142
- reject(new Error("Invalid JSON from registry"));
143
- }
144
- });
145
- });
146
- req.on("error", reject);
147
- req.setTimeout(15e3, () => {
148
- req.destroy();
149
- reject(new Error("Timeout"));
150
- });
151
- req.end();
152
- });
153
- }
154
-
155
- //#endregion
156
- //#region src/plugins/hub.ts
157
- require_paths$1.init_paths();
158
- const SKILL_REGISTRY = [
159
- {
160
- id: "web-search",
161
- name: "Web Search (Tavily)",
162
- version: "2.1.0",
163
- description: "Real-time web search via Tavily API. Powers research and news queries.",
164
- author: "hyperclaw-team",
165
- category: "utility",
166
- downloads: 48200,
167
- rating: 4.8,
168
- risk: "clean",
169
- requiresKeys: ["TAVILY_API_KEY"],
170
- tags: [
171
- "search",
172
- "internet",
173
- "tavily"
174
- ]
175
- },
176
- {
177
- id: "calendar",
178
- name: "Google Calendar",
179
- version: "1.4.0",
180
- description: "Read and create Google Calendar events via OAuth.",
181
- author: "hyperclaw-team",
182
- category: "productivity",
183
- downloads: 32100,
184
- rating: 4.6,
185
- risk: "clean",
186
- requiresKeys: ["GOOGLE_CALENDAR_CREDS"],
187
- tags: [
188
- "calendar",
189
- "schedule",
190
- "google"
191
- ],
192
- npmPackage: "googleapis"
193
- },
194
- {
195
- id: "github",
196
- name: "GitHub Integration",
197
- version: "1.2.0",
198
- description: "Create issues, PRs, read repos. Requires GitHub PAT.",
199
- author: "hyperclaw-team",
200
- category: "integration",
201
- downloads: 27800,
202
- rating: 4.7,
203
- risk: "clean",
204
- requiresKeys: ["GITHUB_TOKEN"],
205
- tags: [
206
- "github",
207
- "git",
208
- "code"
209
- ],
210
- npmPackage: "@octokit/rest"
211
- },
212
- {
213
- id: "home-assistant",
214
- name: "Home Assistant",
215
- version: "1.5.0",
216
- description: "Control smart home devices via Home Assistant REST API.",
217
- author: "hyperclaw-team",
218
- category: "automation",
219
- downloads: 19400,
220
- rating: 4.5,
221
- risk: "clean",
222
- requiresKeys: ["HA_URL", "HA_TOKEN"],
223
- tags: [
224
- "smart-home",
225
- "iot",
226
- "automation"
227
- ]
228
- },
229
- {
230
- id: "code-executor",
231
- name: "Code Executor (Sandbox)",
232
- version: "3.0.1",
233
- description: "Execute Python/JS/Bash code in a sandboxed Docker container.",
234
- author: "hyperclaw-team",
235
- category: "utility",
236
- downloads: 41e3,
237
- rating: 4.9,
238
- risk: "clean",
239
- tags: [
240
- "code",
241
- "sandbox",
242
- "python",
243
- "bash"
244
- ],
245
- npmPackage: "dockerode"
246
- },
247
- {
248
- id: "translator",
249
- name: "Real-time Translator",
250
- version: "2.0.0",
251
- description: "DeepL + Google Translate integration for 90+ languages.",
252
- author: "hyperclaw-team",
253
- category: "utility",
254
- downloads: 38500,
255
- rating: 4.7,
256
- risk: "clean",
257
- requiresKeys: ["DEEPL_API_KEY"],
258
- tags: [
259
- "translate",
260
- "language",
261
- "deepl"
262
- ],
263
- installed: true
264
- },
265
- {
266
- id: "reminders",
267
- name: "Smart Reminders",
268
- version: "2.1.0",
269
- description: "Natural language reminders with cron scheduling.",
270
- author: "hyperclaw-team",
271
- category: "productivity",
272
- downloads: 29e3,
273
- rating: 4.6,
274
- risk: "clean",
275
- tags: [
276
- "reminders",
277
- "cron",
278
- "schedule"
279
- ],
280
- installed: true
281
- },
282
- {
283
- id: "weather",
284
- name: "Weather Forecast",
285
- version: "1.3.0",
286
- description: "OpenWeatherMap integration. Current + 7-day forecast.",
287
- author: "hyperclaw-team",
288
- category: "utility",
289
- downloads: 22100,
290
- rating: 4.4,
291
- risk: "clean",
292
- requiresKeys: ["OPENWEATHER_API_KEY"],
293
- tags: ["weather", "forecast"]
294
- },
295
- {
296
- id: "stealth-browser",
297
- name: "Stealth Browser",
298
- version: "1.0.3",
299
- description: "Headless browser with fingerprint evasion. Can bypass bot detection.",
300
- author: "unknown-dev",
301
- category: "utility",
302
- downloads: 3200,
303
- rating: 3.1,
304
- risk: "suspicious",
305
- riskReason: "Fingerprint evasion may violate ToS on some sites. VirusTotal: 2/72 engines flagged.",
306
- tags: [
307
- "browser",
308
- "puppeteer",
309
- "stealth"
310
- ],
311
- npmPackage: "puppeteer-extra-plugin-stealth"
312
- },
313
- {
314
- id: "db-reader",
315
- name: "Database Reader",
316
- version: "1.1.0",
317
- description: "Read from PostgreSQL/MySQL/SQLite databases.",
318
- author: "hyperclaw-team",
319
- category: "integration",
320
- downloads: 15600,
321
- rating: 4.5,
322
- risk: "clean",
323
- requiresKeys: ["DATABASE_URL"],
324
- tags: [
325
- "database",
326
- "sql",
327
- "postgres"
328
- ],
329
- npmPackage: "pg"
330
- },
331
- {
332
- id: "keylogger-util",
333
- name: "Input Monitor Pro",
334
- version: "0.9.1",
335
- description: "Monitors keyboard events for automation triggers.",
336
- author: "shadowy-scripts",
337
- category: "automation",
338
- downloads: 890,
339
- rating: 1.8,
340
- risk: "dangerous",
341
- riskReason: "Detected keylogging behavior. VirusTotal: 31/72 engines flagged as malware.",
342
- tags: ["keyboard", "monitor"]
343
- }
344
- ];
345
- const WORKSPACE_SKILLS = () => path.default.join(require_paths.getHyperClawDir(), "workspace", "skills");
346
- var SkillHub = class {
347
- installed = /* @__PURE__ */ new Set();
348
- /** Sync installed set from workspace disk (persisted installs). */
349
- async refreshInstalledFromDisk() {
350
- const ids = await listInstalledFromClawHub();
351
- this.installed = new Set(ids);
352
- }
353
- /** Persist bundled skill to workspace so it survives restarts and is loaded by skill-loader. */
354
- async persistBundledSkill(skill) {
355
- const destDir = path.default.join(WORKSPACE_SKILLS(), skill.id);
356
- await fs_extra.default.ensureDir(destDir);
357
- const skillPath = path.default.join(destDir, "SKILL.md");
358
- const repoSkillPath = path.default.join(process.cwd(), "skills", skill.id, "SKILL.md");
359
- const altRepoPath = path.default.join(__dirname, "..", "..", "skills", skill.id, "SKILL.md");
360
- if (await fs_extra.default.pathExists(repoSkillPath)) await fs_extra.default.copy(repoSkillPath, skillPath);
361
- else if (await fs_extra.default.pathExists(altRepoPath)) await fs_extra.default.copy(altRepoPath, skillPath);
362
- else {
363
- const content = `# ${skill.name}\n\n${skill.description}\n\n## Usage\n\nWhen the user needs ${skill.description.toLowerCase()}, use this skill.${skill.requiresKeys?.length ? `\n\nRequires: ${skill.requiresKeys.join(", ")}` : ""}\n`;
364
- await fs_extra.default.writeFile(skillPath, content, "utf8");
365
- }
366
- this.installed.add(skill.id);
367
- }
368
- async showHub(hideSuspicious = false) {
369
- await this.refreshInstalledFromDisk();
370
- console.log(chalk.default.bold.cyan("\n╔═══════════════════════════════════════════╗"));
371
- console.log(chalk.default.bold.cyan("║ 🧩 HYPERCLAW SKILL HUB ║"));
372
- console.log(chalk.default.bold.cyan("╚═══════════════════════════════════════════╝\n"));
373
- const skills = hideSuspicious ? SKILL_REGISTRY.filter((s) => s.risk === "clean") : SKILL_REGISTRY;
374
- for (const skill of skills) this.printSkillCard(skill);
375
- }
376
- printSkillCard(skill) {
377
- const riskBadge = {
378
- "clean": chalk.default.green("✔ CLEAN"),
379
- "suspicious": chalk.default.yellow("⚠ SUSPICIOUS"),
380
- "dangerous": chalk.default.red("✖ DANGEROUS")
381
- }[skill.risk];
382
- const instBadge = this.installed.has(skill.id) ? chalk.default.green("[installed]") : chalk.default.gray("[available]");
383
- const stars = "★".repeat(Math.round(skill.rating)) + "☆".repeat(5 - Math.round(skill.rating));
384
- console.log(` ${chalk.default.bold(skill.name)} ${chalk.default.gray(`v${skill.version}`)} ${instBadge}`);
385
- console.log(` ${chalk.default.gray(skill.description)}`);
386
- console.log(` ${riskBadge} ${chalk.default.yellow(stars)} ${chalk.default.gray(`${(skill.downloads / 1e3).toFixed(1)}k downloads`)}`);
387
- if (skill.riskReason) console.log(` ${chalk.default.yellow("⚠")} ${chalk.default.yellow(skill.riskReason)}`);
388
- if (skill.requiresKeys?.length) console.log(` 🔑 Requires: ${chalk.default.cyan(skill.requiresKeys.join(", "))}`);
389
- console.log();
390
- }
391
- async install(skillId, force = false) {
392
- const skill = SKILL_REGISTRY.find((s) => s.id === skillId);
393
- if (!skill) {
394
- console.log(chalk.default.red(`❌ Skill not found: ${skillId}`));
395
- return;
396
- }
397
- if (skill.risk === "dangerous" && !force) {
398
- console.log(chalk.default.red(`\n🚨 DANGEROUS SKILL BLOCKED: ${skill.name}`));
399
- console.log(chalk.default.red(` ${skill.riskReason}`));
400
- console.log(chalk.default.gray(" Use --force to override (NOT RECOMMENDED)\n"));
401
- return;
402
- }
403
- if (skill.risk === "suspicious" && !force) {
404
- console.log(chalk.default.yellow(`\n⚠️ SUSPICIOUS SKILL: ${skill.name}`));
405
- console.log(chalk.default.yellow(` ${skill.riskReason}`));
406
- console.log(chalk.default.gray(" Use --force to install anyway\n"));
407
- return;
408
- }
409
- const spinner = (0, ora.default)(`Installing ${skill.name}...`).start();
410
- if (skill.npmPackage) {
411
- spinner.text = `Installing npm package: ${skill.npmPackage}`;
412
- await new Promise((r) => setTimeout(r, 800));
413
- }
414
- await this.persistBundledSkill(skill);
415
- spinner.succeed(`${skill.name} installed ✓`);
416
- if (skill.requiresKeys?.length) {
417
- console.log(chalk.default.yellow(`\n📋 Required API keys to activate:`));
418
- skill.requiresKeys.forEach((k) => {
419
- console.log(chalk.default.cyan(` hyperclaw config set-key ${k}`));
420
- });
421
- }
422
- console.log();
423
- }
424
- async scan(skillId) {
425
- const skill = SKILL_REGISTRY.find((s) => s.id === skillId);
426
- if (!skill) return;
427
- const spinner = (0, ora.default)(`Scanning ${skill.name}...`).start();
428
- const stages = [
429
- "Checking manifest...",
430
- "Scanning for malicious patterns...",
431
- "Checking VirusTotal...",
432
- "Verifying author..."
433
- ];
434
- for (const stage of stages) {
435
- spinner.text = stage;
436
- await new Promise((r) => setTimeout(r, 600));
437
- }
438
- const result = {
439
- "clean": chalk.default.green("✅ All green — safe to install"),
440
- "suspicious": chalk.default.yellow("⚠️ Suspicious patterns detected — proceed with caution"),
441
- "dangerous": chalk.default.red("🚨 Malicious patterns detected — do NOT install")
442
- }[skill.risk];
443
- spinner.stop();
444
- console.log(`\n🔬 Scan results for ${chalk.default.bold(skill.name)}:`);
445
- console.log(` ${result}`);
446
- if (skill.riskReason) console.log(chalk.default.gray(` Detail: ${skill.riskReason}`));
447
- console.log();
448
- }
449
- async checkEligibility() {
450
- const spinner = (0, ora.default)("Checking system eligibility...").start();
451
- await new Promise((r) => setTimeout(r, 1e3));
452
- spinner.succeed("Eligibility check complete");
453
- console.log(chalk.default.green("\n✅ All installed skills are eligible on this system\n"));
454
- }
455
- async getInstalled() {
456
- await this.refreshInstalledFromDisk();
457
- const ids = this.installed;
458
- const fromRegistry = SKILL_REGISTRY.filter((s) => ids.has(s.id));
459
- const fromWorkspace = (await listInstalledFromClawHub()).filter((id) => !SKILL_REGISTRY.some((s) => s.id === id));
460
- return [...fromRegistry, ...fromWorkspace.map((id) => ({
461
- id,
462
- name: id,
463
- version: "0",
464
- description: "",
465
- author: "",
466
- category: "utility",
467
- downloads: 0,
468
- rating: 0,
469
- risk: "clean",
470
- tags: []
471
- }))];
472
- }
473
- /** ClawHub integration: search remote registry, fallback to bundled when remote unavailable */
474
- async searchClawHub(query, category) {
475
- let remote = await searchSkills(query, category);
476
- if (remote.length === 0) {
477
- const q = (query || "").toLowerCase();
478
- const filtered = SKILL_REGISTRY.filter((s) => !q || s.id.includes(q) || s.name.toLowerCase().includes(q) || s.tags.some((t) => t.includes(q))).filter((s) => !category || s.category === category);
479
- remote = filtered.map((s) => ({
480
- id: s.id,
481
- name: s.name,
482
- author: s.author,
483
- description: s.description,
484
- rating: s.rating,
485
- downloads: s.downloads,
486
- version: s.version,
487
- categories: [s.category]
488
- }));
489
- }
490
- return remote;
491
- }
492
- /** ClawHub integration: install from remote registry */
493
- async installFromClawHub(skillId, version) {
494
- return installSkill(skillId, version);
495
- }
496
- /** ClawHub marketplace UX: unified browse (bundled + remote) */
497
- async showMarketplace(opts) {
498
- await this.refreshInstalledFromDisk();
499
- const installedClawHub = await listInstalledFromClawHub();
500
- const bundled = (opts?.hideSuspicious ? SKILL_REGISTRY.filter((s) => s.risk === "clean") : SKILL_REGISTRY).filter((s) => !opts?.category || s.category === opts.category);
501
- console.log(chalk.default.bold.cyan("\n╔══════════════════════════════════════════════════════════╗"));
502
- console.log(chalk.default.bold.cyan("║ 🧩 CLAWHUB MARKETPLACE ║"));
503
- console.log(chalk.default.bold.cyan("╚══════════════════════════════════════════════════════════╝\n"));
504
- if (installedClawHub.length > 0) {
505
- console.log(chalk.default.bold.green(" Installed (ClawHub):"));
506
- installedClawHub.forEach((id) => console.log(chalk.default.gray(` • ${id}`)));
507
- console.log();
508
- }
509
- console.log(chalk.default.bold(" Bundled skills:"));
510
- for (const skill of bundled) {
511
- const inst = this.installed.has(skill.id) || installedClawHub.includes(skill.id);
512
- const badge = inst ? chalk.default.green("✓ installed") : chalk.default.cyan("hyperclaw skill install " + skill.id);
513
- const risk = skill.risk === "clean" ? "" : chalk.default.yellow(` [${skill.risk}]`);
514
- console.log(` ${chalk.default.bold(skill.name)} ${chalk.default.gray(`v${skill.version}`)} ${badge}${risk}`);
515
- console.log(chalk.default.gray(` ${skill.description}`));
516
- }
517
- console.log(chalk.default.gray("\n Search remote: hyperclaw skill search <query>"));
518
- console.log(chalk.default.gray(" Install: hyperclaw skill install <id>\n"));
519
- }
520
- };
521
-
522
- //#endregion
523
35
  //#region src/cli/dashboard.ts
524
36
  var Dashboard = class {
525
37
  async launch(live) {
@@ -533,7 +45,7 @@ var Dashboard = class {
533
45
  async drawDashboard() {
534
46
  const cfg = await new require_onboard.ConfigStore().load();
535
47
  const gm = new require_onboard.GatewayManager();
536
- const hub = new SkillHub();
48
+ const hub = new require_hub.SkillHub();
537
49
  const installed = await hub.getInstalled();
538
50
  const port = cfg?.gateway?.port || 1515;
539
51
  const agent = cfg?.identity?.agentName || "Hyper";
@@ -552,7 +64,7 @@ var Dashboard = class {
552
64
  return c(`║ `) + content + " ".repeat(pad) + c(`║`);
553
65
  };
554
66
  console.log(c(`╔${line}╗`));
555
- console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW v4.0.1 — GATEWAY DASHBOARD".padStart(45).padEnd(w)}`) + c(`║`));
67
+ console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW v5.0.0 — GATEWAY DASHBOARD".padStart(45).padEnd(w)}`) + c(`║`));
556
68
  console.log(c(`╠${line}╣`));
557
69
  console.log(row(`${statusDot} Gateway ${statusText} ${chalk.default.gray("│")} ws://localhost:${port} ${chalk.default.gray("│")} Agent: ${c(agent)}`));
558
70
  console.log(row(`${c("◆")} Model ${chalk.default.gray(model.slice(0, 30))} ${chalk.default.gray("│")} User: ${c(user)}`));
@@ -633,7 +145,7 @@ async function recordAudio(outFile, seconds) {
633
145
  async function transcribeWhisper(filePath, lang) {
634
146
  const apiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
635
147
  if (!apiKey) throw new Error("No OPENAI_API_KEY set");
636
- const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-fpopMWNo.js"))).catch(() => null))?.default;
148
+ const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-Cz040rio.js"))).catch(() => null))?.default;
637
149
  if (!FormData) throw new Error("form-data not installed");
638
150
  const form = new FormData();
639
151
  form.append("file", fs.createReadStream(filePath), {
@@ -778,281 +290,183 @@ var VoiceEngine = class {
778
290
  };
779
291
 
780
292
  //#endregion
781
- //#region src/routing/agents-routing.ts
782
- var AgentRouter = class {
783
- stateFile;
784
- agents = [];
785
- constructor() {
786
- this.stateFile = path.default.join(os.default.homedir(), ".hyperclaw", "agents.json");
787
- this.load();
788
- }
789
- load() {
790
- try {
791
- this.agents = fs_extra.default.readJsonSync(this.stateFile);
792
- } catch {
793
- this.agents = [{
794
- workspace: path.default.join(os.default.homedir(), ".hyperclaw", "workspace"),
795
- name: "default",
796
- model: void 0,
797
- bindings: []
798
- }];
799
- }
293
+ //#region src/infra/device-pairing.ts
294
+ const DEVICES_DIR = path.default.join(os.default.homedir(), ".hyperclaw", "devices");
295
+ const PENDING_FILE = path.default.join(DEVICES_DIR, "pending.json");
296
+ const PAIRED_FILE = path.default.join(DEVICES_DIR, "paired.json");
297
+ const PENDING_EXPIRY_MS = 10 * 60 * 1e3;
298
+ const TOKEN_BYTES = 16;
299
+ async function readPending() {
300
+ try {
301
+ const data = await fs_extra.default.readJson(PENDING_FILE);
302
+ return Array.isArray(data) ? data : [];
303
+ } catch {
304
+ return [];
800
305
  }
801
- save() {
802
- fs_extra.default.ensureDirSync(path.default.dirname(this.stateFile));
803
- fs_extra.default.writeJsonSync(this.stateFile, this.agents, { spaces: 2 });
306
+ }
307
+ async function writePending(entries) {
308
+ await fs_extra.default.ensureDir(DEVICES_DIR);
309
+ await fs_extra.default.writeJson(PENDING_FILE, entries, {
310
+ spaces: 2,
311
+ mode: 384
312
+ });
313
+ }
314
+ async function readPaired() {
315
+ try {
316
+ const data = await fs_extra.default.readJson(PAIRED_FILE);
317
+ return Array.isArray(data) ? data : [];
318
+ } catch {
319
+ return [];
804
320
  }
805
- listBindings() {
806
- console.log(chalk.default.bold.cyan("\n 🦅 AGENT BINDINGS\n"));
807
- if (this.agents.length === 0) {
808
- console.log(chalk.default.gray(" No agents configured."));
809
- return;
810
- }
811
- for (const agent of this.agents) {
812
- console.log(` ${chalk.default.bold(agent.name)} ${chalk.default.gray(agent.workspace)}`);
813
- if (agent.bindings.length === 0) console.log(` ${chalk.default.gray("No channel bindings — receives from all channels")}`);
814
- else for (const b of agent.bindings) {
815
- const acct = b.accountId ? chalk.default.gray(`@${b.accountId}`) : chalk.default.gray("(all accounts)");
816
- const role = b.role === "primary" ? chalk.default.green("[primary]") : chalk.default.gray("[secondary]");
817
- console.log(` ${chalk.default.cyan(b.channelId)} ${acct} ${role}`);
818
- }
819
- console.log();
820
- }
321
+ }
322
+ async function writePaired(devices) {
323
+ await fs_extra.default.ensureDir(DEVICES_DIR);
324
+ await fs_extra.default.writeJson(PAIRED_FILE, devices, {
325
+ spaces: 2,
326
+ mode: 384
327
+ });
328
+ }
329
+ var DevicePairingStore = class {
330
+ /**
331
+
332
+ * Creates a pending pairing request and returns:
333
+
334
+ * - requestId: show to user for approve/reject
335
+
336
+ * - setupCode: base64-encoded JSON {url, token} — send to device
337
+
338
+ */
339
+ async createRequest(gatewayUrl, opts) {
340
+ const now = Date.now();
341
+ const all = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
342
+ const requestId = crypto.default.randomBytes(6).toString("hex");
343
+ const token = crypto.default.randomBytes(TOKEN_BYTES).toString("hex");
344
+ const expiresAt = new Date(now + PENDING_EXPIRY_MS).toISOString();
345
+ const entry = {
346
+ requestId,
347
+ token,
348
+ expiresAt,
349
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
350
+ ...opts?.deviceName ? { deviceName: opts.deviceName } : {},
351
+ ...opts?.platform ? { platform: opts.platform } : {}
352
+ };
353
+ all.push(entry);
354
+ await writePending(all);
355
+ const payload = {
356
+ url: gatewayUrl,
357
+ token
358
+ };
359
+ const setupCode = Buffer.from(JSON.stringify(payload)).toString("base64");
360
+ return {
361
+ requestId,
362
+ setupCode,
363
+ expiresAt
364
+ };
821
365
  }
822
- async bind() {
823
- console.log(chalk.default.cyan("\n Bind a channel to an agent workspace\n"));
824
- const { channel, workspace, role } = await inquirer.default.prompt([
825
- {
826
- type: "input",
827
- name: "channel",
828
- message: "Channel ID (e.g. telegram, discord, slack):",
829
- validate: (v) => v.trim().length > 0 || "Required"
830
- },
831
- {
832
- type: "input",
833
- name: "workspace",
834
- message: "Agent workspace (directory or name):",
835
- default: "default"
836
- },
837
- {
838
- type: "list",
839
- name: "role",
840
- message: "Binding role:",
841
- choices: [{
842
- name: "primary — routes all traffic from this channel",
843
- value: "primary"
844
- }, {
845
- name: "secondary fallback if primary is busy",
846
- value: "secondary"
847
- }]
848
- }
849
- ]);
850
- let agent = this.agents.find((a) => a.name === workspace || a.workspace === workspace);
851
- if (!agent) {
852
- agent = {
853
- workspace,
854
- name: workspace,
855
- bindings: []
856
- };
857
- this.agents.push(agent);
858
- }
859
- agent.bindings.push({
860
- channelId: channel,
861
- agentWorkspace: agent.workspace,
862
- role,
863
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
864
- });
865
- this.save();
866
- console.log(chalk.default.green(`\n ✔ Bound ${channel} → ${workspace} (${role})\n`));
867
- }
868
- async unbind() {
869
- const allBindings = [];
870
- for (const agent of this.agents) for (const b of agent.bindings) allBindings.push({
871
- agent: agent.name,
872
- channel: b.channelId
873
- });
874
- if (allBindings.length === 0) {
875
- console.log(chalk.default.gray("\n No bindings to remove.\n"));
876
- return;
877
- }
878
- const { toRemove } = await inquirer.default.prompt([{
879
- type: "checkbox",
880
- name: "toRemove",
881
- message: "Select bindings to remove:",
882
- choices: allBindings.map((b) => ({
883
- name: `${b.channel} → ${b.agent}`,
884
- value: b
885
- }))
886
- }]);
887
- for (const { agent: agentName, channel } of toRemove) {
888
- const agent = this.agents.find((a) => a.name === agentName);
889
- if (agent) agent.bindings = agent.bindings.filter((b) => b.channelId !== channel);
366
+ async listPending() {
367
+ const now = Date.now();
368
+ const all = await readPending();
369
+ return all.filter((e) => new Date(e.expiresAt).getTime() > now);
370
+ }
371
+ async listPaired() {
372
+ return readPaired();
373
+ }
374
+ async approve(requestId) {
375
+ const now = Date.now();
376
+ const pending = await readPending();
377
+ const idx = pending.findIndex((e) => e.requestId === requestId && new Date(e.expiresAt).getTime() > now);
378
+ if (idx === -1) return null;
379
+ const entry = pending[idx];
380
+ pending.splice(idx, 1);
381
+ await writePending(pending);
382
+ const deviceId = `device-${crypto.default.randomBytes(4).toString("hex")}`;
383
+ const longToken = crypto.default.randomBytes(32).toString("hex");
384
+ const device = {
385
+ deviceId,
386
+ requestId: entry.requestId,
387
+ token: longToken,
388
+ pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
389
+ ...entry.deviceName ? { deviceName: entry.deviceName } : {},
390
+ ...entry.platform ? { platform: entry.platform } : {}
391
+ };
392
+ const paired = await readPaired();
393
+ paired.push(device);
394
+ await writePaired(paired);
395
+ return device;
396
+ }
397
+ async reject(requestId) {
398
+ const pending = await readPending();
399
+ const idx = pending.findIndex((e) => e.requestId === requestId);
400
+ if (idx === -1) return false;
401
+ pending.splice(idx, 1);
402
+ await writePending(pending);
403
+ return true;
404
+ }
405
+ async unpair(deviceId) {
406
+ const paired = await readPaired();
407
+ const idx = paired.findIndex((d) => d.deviceId === deviceId);
408
+ if (idx === -1) return false;
409
+ paired.splice(idx, 1);
410
+ await writePaired(paired);
411
+ return true;
412
+ }
413
+ async verifyToken(token) {
414
+ const now = Date.now();
415
+ const pending = await readPending();
416
+ const pendingMatch = pending.find((e) => e.token === token && new Date(e.expiresAt).getTime() > now);
417
+ if (pendingMatch) return this.approve(pendingMatch.requestId);
418
+ const paired = await readPaired();
419
+ return paired.find((d) => d.token === token) ?? null;
420
+ }
421
+ async touchDevice(deviceId) {
422
+ const paired = await readPaired();
423
+ const device = paired.find((d) => d.deviceId === deviceId);
424
+ if (device) {
425
+ device.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
426
+ await writePaired(paired);
890
427
  }
891
- this.save();
892
- console.log(chalk.default.green(`\n ✔ Removed ${toRemove.length} binding(s)\n`));
893
428
  }
894
- };
895
-
896
- //#endregion
897
- //#region src/commands/doctor.ts
898
- async function isPortOpen(port) {
899
- return new Promise((resolve) => {
900
- const s = new net.default.Socket();
901
- s.setTimeout(500);
902
- s.on("connect", () => {
903
- s.destroy();
904
- resolve(true);
905
- });
906
- s.on("error", () => resolve(false));
907
- s.on("timeout", () => resolve(false));
429
+ static parseSetupCode(setupCode) {
908
430
  try {
909
- s.connect(port, "127.0.0.1");
431
+ const json = Buffer.from(setupCode, "base64").toString("utf8");
432
+ const parsed = JSON.parse(json);
433
+ if (typeof parsed.url === "string" && typeof parsed.token === "string") return parsed;
434
+ return null;
910
435
  } catch {
911
- resolve(false);
436
+ return null;
912
437
  }
913
- });
914
- }
915
- async function runDoctor(fix = false) {
916
- const spinner = (0, ora.default)("Running health checks...").start();
917
- await new Promise((r) => setTimeout(r, 800));
918
- spinner.stop();
919
- const configDir = path.default.join(os.default.homedir(), ".hyperclaw");
920
- const configFile = path.default.join(configDir, "config.json");
921
- const agentsFile = path.default.join(configDir, "AGENTS.md");
922
- const authFile = path.default.join(configDir, "auth.json");
923
- const pairingFile = path.default.join(configDir, "pairing-store.json");
924
- let cfg = null;
925
- try {
926
- cfg = fs_extra.default.readJsonSync(configFile);
927
- } catch {}
928
- const issues = [];
929
- if (!cfg) issues.push({
930
- id: "no-config",
931
- severity: "error",
932
- title: "No configuration found",
933
- detail: "Run: hyperclaw init",
934
- fixable: false
935
- });
936
- else {
937
- const hasToken = !!cfg.gateway?.authToken;
938
- issues.push({
939
- id: "gateway-token",
940
- severity: hasToken ? "ok" : "warn",
941
- title: hasToken ? "Gateway auth token set" : "Gateway auth token missing",
942
- detail: hasToken ? "Token is configured" : "Set a strong token in gateway config",
943
- fixable: !hasToken,
944
- fix: async () => {
945
- const crypto = await import("crypto");
946
- cfg.gateway = cfg.gateway || {};
947
- cfg.gateway.authToken = crypto.randomBytes(32).toString("hex");
948
- fs_extra.default.writeJsonSync(configFile, cfg, { spaces: 2 });
949
- console.log(chalk.default.green(" ✔ Generated and saved gateway auth token"));
438
+ }
439
+ async showCLI() {
440
+ const now = Date.now();
441
+ const pending = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
442
+ const paired = await readPaired();
443
+ console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIRING\n"));
444
+ if (pending.length > 0) {
445
+ console.log(chalk.default.yellow(" Pending requests:\n"));
446
+ for (const e of pending) {
447
+ const expiresIn = Math.round((new Date(e.expiresAt).getTime() - now) / 6e4);
448
+ const name = e.deviceName ? chalk.default.white(` "${e.deviceName}"`) : "";
449
+ const plat = e.platform ? chalk.default.gray(` (${e.platform})`) : "";
450
+ console.log(` ${chalk.default.yellow("○")} ${chalk.default.bold(e.requestId)}${name}${plat} ${chalk.default.gray(`expires in ${expiresIn}m`)}`);
950
451
  }
951
- });
952
- const channels = cfg.channels || [];
953
- const channelConfigs = cfg.channelConfigs || {};
954
- for (const ch of channels) {
955
- const dmPolicy = channelConfigs[ch]?.dmPolicy?.policy;
956
- if (dmPolicy === "open") issues.push({
957
- id: `dm-open-${ch}`,
958
- severity: "warn",
959
- title: `DM policy is "open" on ${ch}`,
960
- detail: `Anyone can DM your agent on ${ch}. Consider using "pairing" or "allowlist".`,
961
- fixable: false
962
- });
963
- if (dmPolicy === "allowlist") {
964
- const allowFrom = channelConfigs[ch]?.dmPolicy?.allowFrom || [];
965
- if (allowFrom.length === 0) issues.push({
966
- id: `dm-empty-allowlist-${ch}`,
967
- severity: "error",
968
- title: `Empty allowlist on ${ch} — DMs will be silently dropped`,
969
- detail: `channel.${ch}.dmPolicy.allowFrom is empty. Add users or change policy.`,
970
- fixable: true,
971
- fix: async () => {
972
- try {
973
- const pairingEntries = fs_extra.default.readJsonSync(pairingFile);
974
- const approved = pairingEntries.filter((e) => e.channelId === ch && e.status === "approved" && e.userId);
975
- if (approved.length > 0) {
976
- channelConfigs[ch].dmPolicy.allowFrom = approved.map((e) => e.userId);
977
- cfg.channelConfigs = channelConfigs;
978
- fs_extra.default.writeJsonSync(configFile, cfg, { spaces: 2 });
979
- console.log(chalk.default.green(` ✔ Restored ${approved.length} user(s) from pairing store to ${ch} allowlist`));
980
- }
981
- } catch {}
982
- }
983
- });
452
+ console.log();
453
+ }
454
+ if (paired.length > 0) {
455
+ console.log(chalk.default.green(" Paired devices:\n"));
456
+ for (const d of paired) {
457
+ const name = d.deviceName ? chalk.default.white(` "${d.deviceName}"`) : "";
458
+ const plat = d.platform ? chalk.default.gray(` (${d.platform})`) : "";
459
+ const seen = d.lastSeenAt ? chalk.default.gray(` last seen: ${new Date(d.lastSeenAt).toLocaleDateString()}`) : "";
460
+ console.log(` ${chalk.default.green("")} ${chalk.default.bold(d.deviceId)}${name}${plat} ${chalk.default.gray(`paired: ${new Date(d.pairedAt).toLocaleDateString()}`)}${seen}`);
984
461
  }
462
+ console.log();
985
463
  }
986
- const hasApiKey = !!cfg.provider?.apiKey;
987
- const isLocal = cfg.provider?.providerId === "local";
988
- if (!hasApiKey && !isLocal) issues.push({
989
- id: "no-api-key",
990
- severity: "error",
991
- title: "No AI provider API key configured",
992
- detail: "Run: hyperclaw config set-key",
993
- fixable: false
994
- });
995
- else issues.push({
996
- id: "api-key",
997
- severity: "ok",
998
- title: "AI provider key configured",
999
- detail: `Provider: ${cfg.provider?.providerId}`,
1000
- fixable: false
1001
- });
1002
- issues.push({
1003
- id: "agents-md",
1004
- severity: await fs_extra.default.pathExists(agentsFile) ? "ok" : "warn",
1005
- title: await fs_extra.default.pathExists(agentsFile) ? "AGENTS.md exists" : "AGENTS.md missing",
1006
- detail: await fs_extra.default.pathExists(agentsFile) ? agentsFile : "Run: hyperclaw memory init to generate",
1007
- fixable: false
1008
- });
1009
- const port = cfg.gateway?.port || 1515;
1010
- const running = await isPortOpen(port);
1011
- issues.push({
1012
- id: "gateway-running",
1013
- severity: running ? "ok" : "warn",
1014
- title: running ? `Gateway running on port ${port}` : `Gateway not running on port ${port}`,
1015
- detail: running ? `ws://127.0.0.1:${port}` : "Run: hyperclaw daemon start",
1016
- fixable: false
1017
- });
1018
- if (await fs_extra.default.pathExists(authFile)) {
1019
- const stat = await fs_extra.default.stat(authFile);
1020
- const unsafe = (stat.mode & 63) !== 0;
1021
- issues.push({
1022
- id: "auth-permissions",
1023
- severity: unsafe ? "warn" : "ok",
1024
- title: unsafe ? "Auth store has unsafe permissions" : "Auth store permissions OK",
1025
- detail: unsafe ? `chmod 600 ${authFile}` : `Mode: 600`,
1026
- fixable: unsafe,
1027
- fix: async () => {
1028
- await fs_extra.default.chmod(authFile, 384);
1029
- console.log(chalk.default.green(` ✔ Fixed permissions on ${authFile}`));
1030
- }
1031
- });
464
+ if (pending.length === 0 && paired.length === 0) {
465
+ console.log(chalk.default.gray(" No pending or paired devices."));
466
+ console.log(chalk.default.gray(" In Telegram, message your bot: /pair\n"));
1032
467
  }
1033
468
  }
1034
- console.log(chalk.default.bold.cyan("\n 🩺 HYPERCLAW DOCTOR\n"));
1035
- let errorCount = 0, warnCount = 0;
1036
- for (const issue of issues) {
1037
- const icon = {
1038
- error: chalk.default.red("✖"),
1039
- warn: chalk.default.yellow("⚠"),
1040
- ok: chalk.default.green("✔")
1041
- }[issue.severity];
1042
- console.log(` ${icon} ${chalk.default.white(issue.title)}`);
1043
- console.log(` ${chalk.default.gray(issue.detail)}`);
1044
- if (issue.fixable && fix && issue.fix) await issue.fix();
1045
- else if (issue.fixable && !fix) console.log(chalk.default.gray(" Run with --fix to auto-repair"));
1046
- if (issue.severity === "error") errorCount++;
1047
- if (issue.severity === "warn") warnCount++;
1048
- console.log();
1049
- }
1050
- const total = issues.length;
1051
- const okCount = total - errorCount - warnCount;
1052
- console.log(` ${chalk.default.bold("Summary:")} ${chalk.default.green(`${okCount} ok`)} ${chalk.default.yellow(`${warnCount} warnings`)} ${chalk.default.red(`${errorCount} errors`)}`);
1053
- if (errorCount > 0 || warnCount > 0) console.log(chalk.default.gray("\n Run: hyperclaw doctor --fix to auto-repair fixable issues\n"));
1054
- else console.log(chalk.default.green("\n ✔ All checks passed!\n"));
1055
- }
469
+ };
1056
470
 
1057
471
  //#endregion
1058
472
  //#region src/commands/message-send.ts
@@ -1115,17 +529,28 @@ const CHANNELS = [
1115
529
  platforms: ["all"],
1116
530
  tokenLabel: "Telegram Bot Token",
1117
531
  tokenHint: "Get from @BotFather → /newbot",
532
+ extraFields: [{
533
+ name: "dmPolicy",
534
+ label: "DM policy",
535
+ hint: "pairing (default) | allowlist | open | disabled",
536
+ required: false
537
+ }, {
538
+ name: "groupActivation",
539
+ label: "Group activation",
540
+ hint: "mention (default) | always",
541
+ required: false
542
+ }],
1118
543
  setupSteps: [
1119
- "1. Open Telegram and search for @BotFather (the official bot for creating bots).",
1120
- "2. Start a conversation with /start and type /newbot to create a new bot.",
1121
- "3. Give the bot a name (e.g. \"My HyperClaw Bot\") and a username ending in \"bot\" (e.g. my_hyperclaw_bot).",
1122
- "4. @BotFather will send you the Bot Token a string starting with 7xxxxxx:AAH... Keep it secret!",
1123
- "5. Copy the token and paste it below.",
544
+ "1. Open Telegram @BotFather /newbot. Save the token.",
545
+ "2. Config: channels.telegram.botToken, dmPolicy (default: pairing), groups.",
546
+ "3. Start gateway, approve first DM: hyperclaw pairing approve telegram <CODE>",
547
+ "4. Add bot to groups; set groups[\"*\"].requireMention for mention gating.",
1124
548
  "",
1125
- " 🔗 t.me/BotFather"
549
+ " 🔗 docs/telegram.md — full setup"
1126
550
  ],
1127
551
  status: "recommended",
1128
- npmPackage: "node-telegram-bot-api"
552
+ npmPackage: "node-telegram-bot-api",
553
+ notes: "DMs + groups. Pairing, allowlist, voice notes. Long polling default."
1129
554
  },
1130
555
  {
1131
556
  id: "discord",
@@ -1137,23 +562,37 @@ const CHANNELS = [
1137
562
  tokenLabel: "Discord Bot Token",
1138
563
  tokenHint: "discord.com/developers/applications",
1139
564
  setupSteps: [
1140
- "1. Go to Discord Developer Portal: https://discord.com/developers/applications",
1141
- "2. Click \"New Application\", give it a name and create it.",
1142
- "3. Left menu: Bot Add Bot.",
1143
- "4. Click \"Reset Token\" and copy the token (keep it secret!).",
1144
- "5. Settings OAuth2 General, copy the Application ID (Client ID).",
1145
- "6. Optional: To add the bot to a server, Bot OAuth2 URL Generator, scope: bot.",
565
+ "1. Discord Developer Portal → New Application → Bot → Reset Token.",
566
+ "2. Enable Message Content Intent (and Server Members if needed).",
567
+ "3. OAuth2 URL Generator: scope bot + applications.commands, permissions: View Channels, Send Messages, Read Message History.",
568
+ "4. Add bot to server, enable Developer Mode, copy Server ID and User ID.",
569
+ "5. Enable DMs from server members (right‑click server Privacy Settings).",
570
+ "6. hyperclaw gateway DM the bot → hyperclaw pairing approve discord <CODE>",
1146
571
  "",
1147
- " 🔗 discord.com/developers/applications"
572
+ " 🔗 docs/discord-setup.md — full setup guide"
573
+ ],
574
+ extraFields: [
575
+ {
576
+ name: "listenGuildIds",
577
+ label: "Guild IDs to listen in",
578
+ hint: "[] = all. Add Server IDs to restrict.",
579
+ required: false
580
+ },
581
+ {
582
+ name: "requireMentionInGuild",
583
+ label: "Require @mention in guild",
584
+ hint: "true (default) | false",
585
+ required: false
586
+ },
587
+ {
588
+ name: "dmPolicy",
589
+ label: "DM policy",
590
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"none\"",
591
+ required: false
592
+ }
1148
593
  ],
1149
- extraFields: [{
1150
- name: "clientId",
1151
- label: "Client ID (Application ID)",
1152
- hint: "From OAuth2 → General",
1153
- required: true
1154
- }],
1155
594
  status: "recommended",
1156
- npmPackage: "discord.js"
595
+ npmPackage: "ws"
1157
596
  },
1158
597
  {
1159
598
  id: "whatsapp",
@@ -1162,18 +601,38 @@ const CHANNELS = [
1162
601
  requiresGateway: true,
1163
602
  supportsDM: true,
1164
603
  platforms: ["all"],
1165
- tokenLabel: "WhatsApp Business API key",
604
+ tokenLabel: "Access Token",
1166
605
  tokenHint: "business.whatsapp.com",
606
+ extraFields: [
607
+ {
608
+ name: "phoneNumberId",
609
+ label: "Phone Number ID",
610
+ hint: "From Meta API Setup",
611
+ required: true
612
+ },
613
+ {
614
+ name: "verifyToken",
615
+ label: "Webhook verify token",
616
+ hint: "Any string for webhook verification",
617
+ required: false
618
+ },
619
+ {
620
+ name: "dmPolicy",
621
+ label: "DM policy",
622
+ hint: "pairing (default) | allowlist | open | disabled",
623
+ required: false
624
+ }
625
+ ],
1167
626
  setupSteps: [
1168
- "1. Go to Meta for Developers: https://developers.facebook.com/",
1169
- "2. My Apps Create App Business type.",
1170
- "3. Add product: WhatsApp Get started.",
1171
- "4. WhatsApp API Setup: copy the Temporary access token or create a permanent one.",
1172
- "5. You also need a Phone Number ID and WhatsApp Business Account ID.",
627
+ "1. Meta for Developers → Create App → Business → Add WhatsApp.",
628
+ "2. API Setup: copy Phone Number ID and Access Token.",
629
+ "3. Webhook: https://<host>/webhook/whatsapp, subscribe to messages.",
630
+ "4. Start gateway. Approve DMs: hyperclaw pairing approve whatsapp <CODE>",
1173
631
  "",
1174
- " 🔗 developers.facebook.combusiness.whatsapp.com"
632
+ " 🔗 docs/whatsapp.mdfull setup"
1175
633
  ],
1176
634
  status: "available",
635
+ notes: "Meta Business API. Webhook required.",
1177
636
  npmPackage: void 0
1178
637
  },
1179
638
  {
@@ -1183,42 +642,87 @@ const CHANNELS = [
1183
642
  requiresGateway: true,
1184
643
  supportsDM: true,
1185
644
  platforms: ["all"],
645
+ extraFields: [{
646
+ name: "dmPolicy",
647
+ label: "DM policy",
648
+ hint: "pairing (default) | allowlist | open | disabled",
649
+ required: false
650
+ }],
1186
651
  setupSteps: [
1187
- "1. No Meta Business API needed — uses WhatsApp Web.",
1188
- "2. Make sure you have installed: npm install @whiskeysockets/baileys",
1189
- "3. Start the gateway. On first connection a QR code will appear.",
1190
- "4. Scan the QR with your phone (WhatsApp → Linked Devices → Link a device).",
1191
- "5. After connecting, the session is saved — no QR needed again.",
652
+ "1. No Meta API — uses WhatsApp Web. Install: npm install @whiskeysockets/baileys",
653
+ "2. hyperclaw channels add whatsapp-baileys, then hyperclaw gateway",
654
+ "3. Scan QR (WhatsApp Linked Devices Link a device)",
655
+ "4. Approve first DM: hyperclaw pairing approve whatsapp-baileys <CODE>",
1192
656
  "",
1193
- " 📖 docs: github.com/WhiskeySockets/Baileys"
657
+ " 🔗 docs/whatsapp.md — full setup"
1194
658
  ],
1195
659
  status: "available",
1196
- notes: "WhatsApp Web via Baileys no Meta Business. Scan QR on first run.",
660
+ notes: "WhatsApp Web via Baileys. No Meta Business. Pairing, voice notes.",
1197
661
  npmPackage: "@whiskeysockets/baileys"
1198
662
  },
1199
663
  {
1200
664
  id: "slack",
1201
665
  name: "Slack",
1202
666
  emoji: "💼",
1203
- requiresGateway: false,
667
+ requiresGateway: true,
1204
668
  supportsDM: true,
1205
669
  platforms: ["all"],
1206
670
  tokenLabel: "Slack Bot Token (xoxb-...)",
1207
- extraFields: [{
1208
- name: "signingSecret",
1209
- label: "Signing Secret",
1210
- required: true
1211
- }],
671
+ extraFields: [
672
+ {
673
+ name: "appToken",
674
+ label: "App Token (xapp-...)",
675
+ hint: "Required for Socket Mode (default) — connections:write scope",
676
+ required: false
677
+ },
678
+ {
679
+ name: "signingSecret",
680
+ label: "Signing Secret",
681
+ hint: "Required for HTTP Events API mode only",
682
+ required: false
683
+ },
684
+ {
685
+ name: "mode",
686
+ label: "Connection mode",
687
+ hint: "socket (default) | http",
688
+ required: false
689
+ },
690
+ {
691
+ name: "userToken",
692
+ label: "User Token (xoxp-...)",
693
+ hint: "Optional — for read operations",
694
+ required: false
695
+ },
696
+ {
697
+ name: "ackReaction",
698
+ label: "Ack reaction emoji",
699
+ hint: "Shortcode without colons, e.g. eyes",
700
+ required: false
701
+ },
702
+ {
703
+ name: "typingReaction",
704
+ label: "Typing reaction emoji",
705
+ hint: "Shortcode, e.g. hourglass_flowing_sand",
706
+ required: false
707
+ }
708
+ ],
1212
709
  setupSteps: [
1213
710
  "1. Go to api.slack.com/apps → Create New App → From scratch.",
1214
- "2. Give it a name and choose a workspace.",
1215
- "3. OAuth & Permissions: Add Bot Token Scopes (chat:write, users:read, im:read, im:history, etc.).",
1216
- "4. Install App to workspace copy the \"Bot User OAuth Token\" (starts with xoxb-).",
1217
- "5. Basic Information App Credentials Signing Secret copy it.",
711
+ "2. Socket Mode (default no public URL needed):",
712
+ " a. Settings Socket Mode Enable Socket Mode.",
713
+ " b. Settings Basic Information App-Level Tokens Generate Token (connections:write) copy xapp-...",
714
+ " c. OAuth & Permissions: add bot scopes (chat:write, im:read, im:history, channels:history, etc.).",
715
+ " d. Install App → copy Bot Token (xoxb-...).",
716
+ "3. HTTP mode (alternative):",
717
+ " a. Event Subscriptions → Request URL: https://<host>/webhook/slack.",
718
+ " b. Basic Information → Signing Secret — copy it.",
719
+ "4. Subscribe to bot events: app_mention, message.im, message.channels, message.groups, message.mpim, reaction_added.",
720
+ "5. App Home → Messages Tab → Enable.",
1218
721
  "",
1219
722
  " 🔗 api.slack.com/apps"
1220
723
  ],
1221
- status: "available",
724
+ status: "recommended",
725
+ notes: "Socket Mode (default) requires appToken. HTTP mode requires signingSecret. Supports DMs, channels, threads, reactions, streaming.",
1222
726
  npmPackage: "@slack/bolt"
1223
727
  },
1224
728
  {
@@ -1228,38 +732,160 @@ const CHANNELS = [
1228
732
  requiresGateway: true,
1229
733
  supportsDM: true,
1230
734
  platforms: ["linux", "darwin"],
1231
- tokenLabel: "Signal phone number",
1232
- tokenHint: "Requires signal-cli installed",
735
+ tokenLabel: "Bot phone number (E.164)",
736
+ tokenHint: "e.g. +15551234567 — use a dedicated bot number",
1233
737
  setupSteps: [
1234
738
  "1. Install signal-cli: https://github.com/AsamK/signal-cli",
1235
- "2. Register number: signal-cli -a +1XXXXXXXXX register",
1236
- "3. Verify electronically (if available) or via SMS code.",
1237
- "4. Enter your phone number here (e.g. +1XXXXXXXXX).",
1238
739
  "",
1239
- " 🔗 github.com/AsamK/signal-cli"
740
+ " Path A — Link existing Signal account (QR):",
741
+ " signal-cli link -n \"HyperClaw\" then scan in Signal.",
742
+ "",
743
+ " Path B — Register dedicated bot number (SMS):",
744
+ " signal-cli -a +<BOT_NUMBER> register",
745
+ " signal-cli -a +<BOT_NUMBER> register --captcha '<URL>' (if captcha required)",
746
+ " signal-cli -a +<BOT_NUMBER> verify <CODE>",
747
+ "",
748
+ "2. Set account: \"+<BOT_NUMBER>\" in config.",
749
+ "3. autoStart=true (default) spawns daemon automatically.",
750
+ " Or run daemon yourself and set httpUrl: \"http://127.0.0.1:8080\".",
751
+ "",
752
+ " Access control:",
753
+ " dmPolicy=pairing (default) — senders get pairing code (expires 1h)",
754
+ " groupPolicy=allowlist (default) — only groupAllowFrom senders trigger bot",
755
+ "",
756
+ " Chunking: textChunkLimit=4000, chunkMode=length|newline",
757
+ " Reactions: actions.reactions=true, reactionLevel=off|ack|minimal|extensive",
758
+ "",
759
+ " 🔗 github.com/AsamK/signal-cli",
760
+ " 🔗 github.com/AsamK/signal-cli/wiki/Registration-with-captcha"
761
+ ],
762
+ extraFields: [
763
+ {
764
+ name: "account",
765
+ label: "Bot number (E.164)",
766
+ hint: "+15551234567",
767
+ required: true
768
+ },
769
+ {
770
+ name: "cliPath",
771
+ label: "signal-cli path",
772
+ hint: "signal-cli (if on PATH)",
773
+ required: false
774
+ },
775
+ {
776
+ name: "httpUrl",
777
+ label: "Daemon URL (external)",
778
+ hint: "http://127.0.0.1:8080 — skips autoStart",
779
+ required: false
780
+ },
781
+ {
782
+ name: "httpPort",
783
+ label: "Daemon port",
784
+ hint: "8080 (default)",
785
+ required: false
786
+ },
787
+ {
788
+ name: "autoStart",
789
+ label: "Auto-spawn daemon",
790
+ hint: "true (default) / false",
791
+ required: false
792
+ },
793
+ {
794
+ name: "startupTimeoutMs",
795
+ label: "Startup timeout (ms)",
796
+ hint: "15000 default, max 120000",
797
+ required: false
798
+ },
799
+ {
800
+ name: "dmPolicy",
801
+ label: "DM policy",
802
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"disabled\"",
803
+ required: false
804
+ },
805
+ {
806
+ name: "groupPolicy",
807
+ label: "Group policy",
808
+ hint: "\"allowlist\" (default) | \"open\" | \"disabled\"",
809
+ required: false
810
+ },
811
+ {
812
+ name: "textChunkLimit",
813
+ label: "Text chunk limit (chars)",
814
+ hint: "4000 default",
815
+ required: false
816
+ },
817
+ {
818
+ name: "chunkMode",
819
+ label: "Chunk mode",
820
+ hint: "\"length\" (default) | \"newline\"",
821
+ required: false
822
+ }
1240
823
  ],
1241
824
  status: "available",
1242
- notes: "Requires signal-cli to be installed and registered"
825
+ notes: "signal-cli HTTP daemon + SSE events. DMs, groups, typing, reactions, chunking, multi-account."
1243
826
  },
1244
827
  {
1245
828
  id: "imessage",
1246
- name: "iMessage",
829
+ name: "iMessage (BlueBubbles)",
1247
830
  emoji: "💬",
1248
831
  requiresGateway: true,
1249
832
  supportsDM: true,
1250
833
  platforms: ["darwin"],
834
+ tokenLabel: "BlueBubbles server URL",
835
+ extraFields: [{
836
+ name: "password",
837
+ label: "BlueBubbles password",
838
+ hint: "Set in BlueBubbles server settings",
839
+ required: true
840
+ }, {
841
+ name: "dmPolicy",
842
+ label: "DM policy",
843
+ hint: "pairing (default) | allowlist | open | disabled",
844
+ required: false
845
+ }],
1251
846
  setupSteps: [
1252
- "1. macOS only. You need BlueBubbles (bluebubbles.app) or Beeper bridge.",
1253
- "2. BlueBubbles: install on Mac, check server URL and API key.",
1254
- "3. Or Beeper: connect to iMessage via Beeper desktop app.",
1255
- "4. Set server URL and token in the channel configuration.",
847
+ "1. macOS only. Install BlueBubbles server: bluebubbles.app",
848
+ "2. BlueBubbles → Settings: enable web API, set password, note Server URL.",
849
+ "3. Add channel (imessage or bluebubbles), enter serverUrl + password.",
850
+ "4. hyperclaw gateway hyperclaw pairing approve bluebubbles <CODE>",
1256
851
  "",
1257
- " 🔗 bluebubbles.appbeeper.com"
852
+ " 🔗 docs/bluebubbles.mdbluebubbles.app"
1258
853
  ],
1259
- status: os.default.platform() === "darwin" ? "available" : "unavailable",
1260
- notes: "macOS only uses BlueBubbles or Beeper bridge",
854
+ status: os.default.platform() === "darwin" ? "recommended" : "unavailable",
855
+ notes: "BlueBubbles server on Mac. DMs, pairing. Groups planned.",
1261
856
  npmPackage: "bluebubbles-api"
1262
857
  },
858
+ {
859
+ id: "imessage-native",
860
+ name: "iMessage (imsg CLI — legacy)",
861
+ emoji: "💬",
862
+ requiresGateway: true,
863
+ supportsDM: true,
864
+ platforms: ["darwin"],
865
+ extraFields: [{
866
+ name: "cliPath",
867
+ label: "imsg binary path",
868
+ hint: "Default: imsg (must be in PATH)",
869
+ required: false
870
+ }, {
871
+ name: "dbPath",
872
+ label: "Messages DB path",
873
+ hint: "Default: ~/Library/Messages/chat.db",
874
+ required: false
875
+ }],
876
+ setupSteps: [
877
+ "1. macOS only. Install imsg: brew install steipete/tap/imsg",
878
+ "2. Verify: imsg rpc --help",
879
+ "3. Grant Full Disk Access + Automation to Terminal/Node (one-time: imsg chats --limit 1).",
880
+ "4. No token needed — imsg runs locally via JSON-RPC on stdio.",
881
+ "5. (Optional) Set cliPath if imsg is not in PATH.",
882
+ "",
883
+ " ⚠️ Legacy integration — for new setups use iMessage (BlueBubbles)",
884
+ " 🔗 github.com/steipete/imsg"
885
+ ],
886
+ status: os.default.platform() === "darwin" ? "available" : "unavailable",
887
+ notes: "Legacy — gateway spawns imsg rpc over JSON-RPC stdio. For new setups prefer BlueBubbles."
888
+ },
1263
889
  {
1264
890
  id: "matrix",
1265
891
  name: "Matrix",
@@ -1267,26 +893,84 @@ const CHANNELS = [
1267
893
  requiresGateway: true,
1268
894
  supportsDM: true,
1269
895
  platforms: ["all"],
1270
- tokenLabel: void 0,
1271
- extraFields: [{
1272
- name: "homeserver",
1273
- label: "Homeserver URL",
1274
- hint: "e.g. https://matrix.org",
1275
- required: true
1276
- }, {
1277
- name: "accessToken",
1278
- label: "Access Token",
1279
- required: true
1280
- }],
896
+ tokenLabel: "Access Token (syt_...)",
897
+ tokenHint: "Or set userId + password instead",
898
+ extraFields: [
899
+ {
900
+ name: "homeserver",
901
+ label: "Homeserver URL",
902
+ hint: "e.g. https://matrix.example.org",
903
+ required: true
904
+ },
905
+ {
906
+ name: "accessToken",
907
+ label: "Access Token (syt_...)",
908
+ hint: "Preferred; userId auto-fetched via /whoami",
909
+ required: false
910
+ },
911
+ {
912
+ name: "userId",
913
+ label: "Matrix User ID",
914
+ hint: "@bot:example.org — required only for password login",
915
+ required: false
916
+ },
917
+ {
918
+ name: "password",
919
+ label: "Password (alternative to token)",
920
+ hint: "Token cached to credentials file on first login",
921
+ required: false
922
+ },
923
+ {
924
+ name: "deviceName",
925
+ label: "Device display name",
926
+ hint: "Shown in Matrix clients",
927
+ required: false
928
+ },
929
+ {
930
+ name: "encryption",
931
+ label: "Enable E2EE",
932
+ hint: "true | false — requires crypto native module",
933
+ required: false
934
+ },
935
+ {
936
+ name: "threadReplies",
937
+ label: "Thread replies",
938
+ hint: "off | inbound (default) | always",
939
+ required: false
940
+ },
941
+ {
942
+ name: "textChunkLimit",
943
+ label: "Text chunk limit (chars)",
944
+ hint: "Default: 16000",
945
+ required: false
946
+ },
947
+ {
948
+ name: "mediaMaxMb",
949
+ label: "Media size limit (MB)",
950
+ hint: "Default: 10",
951
+ required: false
952
+ },
953
+ {
954
+ name: "autoJoin",
955
+ label: "Auto-join invites",
956
+ hint: "always (default) | allowlist | off",
957
+ required: false
958
+ }
959
+ ],
1281
960
  setupSteps: [
1282
- "1. Create a bot account on matrix.org or another homeserver.",
1283
- "2. Access token: Element/SchildiChat Settings Help & About → Access Token.",
1284
- "3. Or via API: POST /_matrix/client/r0/login with type=m.login.password.",
1285
- "4. Homeserver URL: https://matrix.org or your server URL.",
961
+ "1. Create a Matrix bot account on any homeserver (matrix.org has free accounts).",
962
+ "2. Get an access token via the login API:",
963
+ " curl -X POST https://<homeserver>/_matrix/client/v3/login \\",
964
+ " -H \"Content-Type: application/json\" \\",
965
+ " -d '{\"type\":\"m.login.password\",\"identifier\":{\"type\":\"m.id.user\",\"user\":\"<username>\"},\"password\":\"<password>\"}'",
966
+ " Or set userId + password — HyperClaw will call the login API and cache the token.",
967
+ "3. Invite the bot account to a room or DM it from any Matrix client (Element, Beeper, etc.).",
968
+ "4. For Beeper, enable E2EE: set encryption: true and verify the device in Element.",
1286
969
  "",
1287
- " 🔗 matrix.org — element.io"
970
+ " 🔗 matrix.org/ecosystem/hosting/ — element.io"
1288
971
  ],
1289
972
  status: "available",
973
+ notes: "Supports DMs, rooms, threads, media, reactions, polls, location, E2EE, multi-account.",
1290
974
  npmPackage: "matrix-js-sdk"
1291
975
  },
1292
976
  {
@@ -1300,25 +984,81 @@ const CHANNELS = [
1300
984
  extraFields: [
1301
985
  {
1302
986
  name: "server",
1303
- label: "Server",
987
+ label: "Server (IRC_HOST)",
1304
988
  hint: "e.g. irc.libera.chat",
1305
989
  required: true
1306
990
  },
991
+ {
992
+ name: "port",
993
+ label: "Port (IRC_PORT)",
994
+ hint: "Default: 6697 (TLS) or 6667",
995
+ required: false
996
+ },
997
+ {
998
+ name: "tls",
999
+ label: "TLS (IRC_TLS)",
1000
+ hint: "true / false — default: false",
1001
+ required: false
1002
+ },
1307
1003
  {
1308
1004
  name: "nick",
1309
- label: "Nickname",
1005
+ label: "Nickname (IRC_NICK)",
1310
1006
  required: true
1311
1007
  },
1008
+ {
1009
+ name: "username",
1010
+ label: "Username / ident (IRC_USERNAME)",
1011
+ required: false
1012
+ },
1013
+ {
1014
+ name: "realname",
1015
+ label: "Real name (IRC_REALNAME)",
1016
+ required: false
1017
+ },
1018
+ {
1019
+ name: "password",
1020
+ label: "Server password (IRC_PASSWORD)",
1021
+ hint: "Not NickServ — leave blank if none",
1022
+ required: false
1023
+ },
1312
1024
  {
1313
1025
  name: "channels",
1314
- label: "Default channels (#room)",
1026
+ label: "Channels to join (IRC_CHANNELS)",
1027
+ hint: "#room1,#room2",
1028
+ required: false
1029
+ },
1030
+ {
1031
+ name: "nickservPassword",
1032
+ label: "NickServ password (IRC_NICKSERV_PASSWORD)",
1033
+ hint: "Identify after connect",
1034
+ required: false
1035
+ },
1036
+ {
1037
+ name: "groupPolicy",
1038
+ label: "Group policy",
1039
+ hint: "\"allowlist\" (default) or \"open\"",
1040
+ required: false
1041
+ },
1042
+ {
1043
+ name: "dmPolicy",
1044
+ label: "DM policy",
1045
+ hint: "\"pairing\" (default) | \"allowlist\" | \"open\"",
1315
1046
  required: false
1316
1047
  }
1317
1048
  ],
1318
1049
  setupSteps: [
1319
1050
  "1. Choose an IRC server (e.g. irc.libera.chat, irc.oftc.net).",
1320
- "2. You need a nickname for the bot and optionally a channel to join.",
1321
- "3. Some servers require authentication before /join.",
1051
+ "2. Set a nickname for the bot and the channels it should join.",
1052
+ "3. Enable TLS (recommended): set port 6697 and tls: true.",
1053
+ "4. If your nick is registered, set nickservPassword to auto-identify.",
1054
+ "5. Access control defaults: groupPolicy=allowlist (bot only replies in",
1055
+ " configured groups), dmPolicy=pairing (new DMs need pairing approval).",
1056
+ "6. To allow everyone in a channel without mention, set per-channel:",
1057
+ " groups[\"#mychan\"].requireMention = false, allowFrom = [\"*\"].",
1058
+ "",
1059
+ " Env vars: IRC_HOST IRC_PORT IRC_TLS IRC_NICK IRC_USERNAME",
1060
+ " IRC_REALNAME IRC_PASSWORD IRC_CHANNELS",
1061
+ " IRC_NICKSERV_PASSWORD IRC_NICKSERV_REGISTER_EMAIL",
1322
1062
  "",
1323
1063
  " 🔗 libera.chat — oftc.net"
1324
1064
  ],
@@ -1332,31 +1072,71 @@ const CHANNELS = [
1332
1072
  requiresGateway: true,
1333
1073
  supportsDM: true,
1334
1074
  platforms: ["all"],
1335
- tokenLabel: "Personal Access Token (or Bot token)",
1336
- tokenHint: "Account Settings > Security > Personal Access Tokens",
1075
+ tokenLabel: "Bot Token (MATTERMOST_BOT_TOKEN)",
1076
+ tokenHint: "System Console Integrations Bot Accounts → Add Bot Account",
1337
1077
  setupSteps: [
1338
- "1. In Mattermost: ProfileAccount Settings SecurityPersonal Access Tokens.",
1339
- "2. Create token copy it (not shown again).",
1340
- "3. Integrations Outgoing Webhooks Add outgoing webhook.",
1341
- "4. Note the webhook URL and Trigger Word. The webhook token is needed for verification.",
1342
- "5. Webhook URL for gateway: https://<gateway>/webhook/mattermost",
1078
+ "1. Create a Bot Account: System Console IntegrationsBot Accounts Add Bot Account.",
1079
+ "2. Copy the bot token shown after creation (not shown again).",
1080
+ "3. Note your Mattermost base URL (e.g. https://chat.example.com).",
1081
+ "4. The connector uses WebSocket events no outgoing webhook needed.",
1082
+ "5. For slash commands: set commands.native=true and expose callbackUrl.",
1083
+ "6. For buttons: add capabilities: [\"inlineButtons\"] and set interactions.callbackBaseUrl.",
1084
+ "",
1085
+ " Env vars: MATTERMOST_BOT_TOKEN MATTERMOST_URL",
1343
1086
  "",
1344
- " 🔗 docs.mattermost.com"
1087
+ " Chat modes:",
1088
+ " oncall (default) — reply only when @mentioned",
1089
+ " onmessage — reply to every channel message",
1090
+ " onchar — reply when message starts with a prefix (e.g. \">\", \"!\")",
1091
+ "",
1092
+ " Access control:",
1093
+ " dmPolicy=pairing (default) | allowlist | open | none",
1094
+ " groupPolicy=allowlist (default) | open",
1095
+ " groupAllowFrom=[userId1, ...] — sender gate for channels",
1096
+ "",
1097
+ " 🔗 docs.mattermost.com — mattermost.com"
1098
+ ],
1099
+ extraFields: [
1100
+ {
1101
+ name: "baseUrl",
1102
+ label: "Base URL (MATTERMOST_URL)",
1103
+ hint: "https://chat.example.com",
1104
+ required: true
1105
+ },
1106
+ {
1107
+ name: "chatmode",
1108
+ label: "Chat mode",
1109
+ hint: "\"oncall\" (default) | \"onmessage\" | \"onchar\"",
1110
+ required: false
1111
+ },
1112
+ {
1113
+ name: "oncharPrefixes",
1114
+ label: "onchar prefixes",
1115
+ hint: ">, ! (comma-separated, for chatmode=onchar)",
1116
+ required: false
1117
+ },
1118
+ {
1119
+ name: "dmPolicy",
1120
+ label: "DM policy",
1121
+ hint: "\"pairing\" (default) | \"open\" | \"allowlist\" | \"none\"",
1122
+ required: false
1123
+ },
1124
+ {
1125
+ name: "groupPolicy",
1126
+ label: "Group policy",
1127
+ hint: "\"allowlist\" (default) | \"open\"",
1128
+ required: false
1129
+ },
1130
+ {
1131
+ name: "capabilities",
1132
+ label: "Capabilities",
1133
+ hint: "\"inlineButtons\" — comma-separated",
1134
+ required: false
1135
+ }
1345
1136
  ],
1346
- extraFields: [{
1347
- name: "serverUrl",
1348
- label: "Server URL",
1349
- hint: "https://mattermost.example.com",
1350
- required: true
1351
- }, {
1352
- name: "webhookToken",
1353
- label: "Outgoing Webhook Token",
1354
- hint: "From Integrations > Outgoing Webhook",
1355
- required: true
1356
- }],
1357
1137
  status: "available",
1358
- notes: "Requires Outgoing Webhook + PAT. Webhook URL: /webhook/mattermost",
1359
- npmPackage: "@mattermost/client"
1138
+ notes: "WebSocket + REST. Supports DMs, channels, buttons, reactions, slash commands, multi-account.",
1139
+ npmPackage: "ws"
1360
1140
  },
1361
1141
  {
1362
1142
  id: "googlechat",
@@ -1379,18 +1159,27 @@ const CHANNELS = [
1379
1159
  id: "msteams",
1380
1160
  name: "Microsoft Teams",
1381
1161
  emoji: "🟣",
1382
- requiresGateway: false,
1383
- supportsDM: false,
1162
+ requiresGateway: true,
1163
+ supportsDM: true,
1384
1164
  platforms: ["all"],
1385
- tokenLabel: "Teams incoming webhook URL",
1165
+ tokenLabel: "App ID (Azure Bot)",
1166
+ extraFields: [{
1167
+ name: "appPassword",
1168
+ label: "App Password (client secret)",
1169
+ hint: "From Azure Bot → Manage → Certificates & secrets",
1170
+ required: true
1171
+ }],
1386
1172
  setupSteps: [
1387
- "1. In Teams: ChannelConnectors Incoming Webhook Configure.",
1388
- "2. Give it a name and copy the Webhook URL.",
1389
- "3. Paste the URL below.",
1173
+ "1. Create an Azure Bot: portal.azure.comCreate a resourceAzure Bot",
1174
+ "2. Type of App: Single Tenant. Create new Microsoft App ID.",
1175
+ "3. Configuration copy App ID. Manage → Certificates & secrets → New client secret → copy Value (appPassword).",
1176
+ "4. Channels → Microsoft Teams → Configure. Set Messaging endpoint: https://<your-host>/webhook/msteams",
1177
+ "5. Build a Teams app manifest with botId = App ID. Upload to Teams.",
1390
1178
  "",
1391
- " 🔗 docs.microsoft.com/microsoftteams/platform/webhooks-and-connectors"
1179
+ " 🔗 docs/msteams.md — full setup guide"
1392
1180
  ],
1393
- status: "available"
1181
+ status: "available",
1182
+ notes: "Bot Framework. Text + DM. Channel/group files require Graph + SharePoint."
1394
1183
  },
1395
1184
  {
1396
1185
  id: "nostr",
@@ -1420,24 +1209,61 @@ const CHANNELS = [
1420
1209
  id: "line",
1421
1210
  name: "LINE",
1422
1211
  emoji: "🟩",
1423
- requiresGateway: false,
1212
+ requiresGateway: true,
1424
1213
  supportsDM: true,
1425
1214
  platforms: ["all"],
1426
1215
  tokenLabel: "LINE Channel access token",
1427
- extraFields: [{
1428
- name: "secret",
1429
- label: "Channel Secret",
1430
- required: true
1431
- }],
1216
+ extraFields: [
1217
+ {
1218
+ name: "channelSecret",
1219
+ label: "Channel Secret",
1220
+ hint: "From LINE Developers Console → Basic settings",
1221
+ required: true
1222
+ },
1223
+ {
1224
+ name: "tokenFile",
1225
+ label: "Token file path (optional)",
1226
+ hint: "Alternative to pasting token directly",
1227
+ required: false
1228
+ },
1229
+ {
1230
+ name: "secretFile",
1231
+ label: "Secret file path (optional)",
1232
+ hint: "Alternative to pasting secret directly",
1233
+ required: false
1234
+ },
1235
+ {
1236
+ name: "webhookPath",
1237
+ label: "Webhook path",
1238
+ hint: "Default: /line/webhook",
1239
+ required: false
1240
+ },
1241
+ {
1242
+ name: "mediaMaxMb",
1243
+ label: "Media download limit (MB)",
1244
+ hint: "Default: 10",
1245
+ required: false
1246
+ },
1247
+ {
1248
+ name: "groupPolicy",
1249
+ label: "Group policy",
1250
+ hint: "open | allowlist | disabled (default: allowlist)",
1251
+ required: false
1252
+ }
1253
+ ],
1432
1254
  setupSteps: [
1433
- "1. Go to developers.line.biz → Console → Create provider & channel.",
1434
- "2. Messaging API channel Configure Basic settings.",
1435
- "3. Channel access token: Issue or Regenerate — copy it.",
1436
- "4. Channel secret: from Basic settings copy it.",
1255
+ "1. Go to developers.line.biz → Console → Create provider & Messaging API channel.",
1256
+ "2. Channel access token: Issue or Regenerate — copy it.",
1257
+ "3. Channel secret: from Basic settings — copy it.",
1258
+ "4. Enable \"Use webhook\" in Messaging API settings.",
1259
+ "5. Set webhook URL (HTTPS required):",
1260
+ " https://<gateway-host>/line/webhook",
1261
+ "6. Paste token + secret below.",
1437
1262
  "",
1438
1263
  " 🔗 developers.line.biz"
1439
1264
  ],
1440
1265
  status: "available",
1266
+ notes: "Webhook receiver. Supports DMs, groups, media, Flex messages, quick replies, locations.",
1441
1267
  npmPackage: "@line/bot-sdk"
1442
1268
  },
1443
1269
  {
@@ -1465,27 +1291,68 @@ const CHANNELS = [
1465
1291
  {
1466
1292
  id: "synology-chat",
1467
1293
  name: "Synology Chat",
1468
- emoji: "💬",
1294
+ emoji: "💬",
1469
1295
  requiresGateway: true,
1470
1296
  supportsDM: true,
1471
1297
  platforms: ["all"],
1472
- tokenLabel: "Incoming Webhook URL",
1473
- tokenHint: "Synology Chat incoming webhook URL",
1474
- extraFields: [{
1475
- name: "outgoingToken",
1476
- label: "Outgoing Webhook Token",
1477
- hint: "Optional verification token",
1478
- required: false
1479
- }],
1298
+ tokenLabel: "Outgoing Webhook Token (SYNOLOGY_CHAT_TOKEN)",
1299
+ tokenHint: "From Synology Chat Integrations Outgoing Webhook",
1300
+ extraFields: [
1301
+ {
1302
+ name: "incomingUrl",
1303
+ label: "Incoming Webhook URL (SYNOLOGY_CHAT_INCOMING_URL)",
1304
+ hint: "From Synology Chat Integrations Incoming Webhook",
1305
+ required: true
1306
+ },
1307
+ {
1308
+ name: "webhookPath",
1309
+ label: "Gateway inbound path",
1310
+ hint: "/webhook/synology (default)",
1311
+ required: false
1312
+ },
1313
+ {
1314
+ name: "dmPolicy",
1315
+ label: "DM policy",
1316
+ hint: "\"allowlist\" (default) | \"open\" | \"pairing\" | \"disabled\"",
1317
+ required: false
1318
+ },
1319
+ {
1320
+ name: "allowedUserIds",
1321
+ label: "Allowed user IDs (SYNOLOGY_ALLOWED_USER_IDS)",
1322
+ hint: "Numeric Synology Chat user IDs, comma-separated",
1323
+ required: false
1324
+ },
1325
+ {
1326
+ name: "rateLimitPerMinute",
1327
+ label: "Rate limit per sender/min (SYNOLOGY_RATE_LIMIT)",
1328
+ hint: "30 (default)",
1329
+ required: false
1330
+ },
1331
+ {
1332
+ name: "allowInsecureSsl",
1333
+ label: "Allow insecure SSL",
1334
+ hint: "false (default) — true only for self-signed NAS certs",
1335
+ required: false
1336
+ }
1337
+ ],
1480
1338
  setupSteps: [
1481
- "1. Synology Chat Integration → Incoming Webhook: create a webhook and copy the URL.",
1482
- "2. If you want inbound bot events, create an Outgoing Webhook pointing to /webhook/synology-chat.",
1483
- "3. Paste the incoming webhook URL below.",
1339
+ "1. Synology Chat Integrations Incoming Webhook Create copy URL.",
1340
+ "2. Synology Chat Integrations Outgoing Webhook Create:",
1341
+ " Outgoing URL: https://<gateway>/webhook/synology",
1342
+ " Copy the generated token.",
1343
+ "3. Set token + incomingUrl in config (or env vars).",
1344
+ "4. dmPolicy=allowlist requires at least one allowedUserId.",
1345
+ "",
1346
+ " Env vars: SYNOLOGY_CHAT_TOKEN SYNOLOGY_CHAT_INCOMING_URL",
1347
+ " SYNOLOGY_ALLOWED_USER_IDS SYNOLOGY_RATE_LIMIT OPENCLAW_BOT_NAME",
1348
+ "",
1349
+ " Multi-account: channels.synology-chat.accounts.{ default, alerts }",
1350
+ " Targets: <numericId>, synology-chat:<id>, user:<id>",
1484
1351
  "",
1485
1352
  " 🔗 kb.synology.com"
1486
1353
  ],
1487
1354
  status: "available",
1488
- notes: "Webhook bridge for Synology Chat rooms / bot automation."
1355
+ notes: "Gateway webhooks. Token verify, rate limiting, multi-account, allowInsecureSsl."
1489
1356
  },
1490
1357
  {
1491
1358
  id: "twitch",
@@ -1496,61 +1363,84 @@ const CHANNELS = [
1496
1363
  platforms: ["all"],
1497
1364
  tokenLabel: "OAuth Token (oauth:...)",
1498
1365
  tokenHint: "Generate a Twitch chat token for your bot account",
1499
- extraFields: [{
1500
- name: "username",
1501
- label: "Bot Username",
1502
- required: true
1503
- }, {
1504
- name: "channels",
1505
- label: "Channels (#name or comma-separated)",
1506
- required: true
1507
- }],
1366
+ extraFields: [
1367
+ {
1368
+ name: "username",
1369
+ label: "Bot username",
1370
+ required: true
1371
+ },
1372
+ {
1373
+ name: "channels",
1374
+ label: "Channels (comma-separated)",
1375
+ hint: "vevisk,secondchannel",
1376
+ required: true
1377
+ },
1378
+ {
1379
+ name: "commandPrefix",
1380
+ label: "Command prefix",
1381
+ hint: "! (default)",
1382
+ required: false
1383
+ }
1384
+ ],
1508
1385
  setupSteps: [
1509
- "1. Create or use a Twitch bot account.",
1510
- "2. Generate an IRC OAuth token for Twitch chat (format: oauth:...).",
1511
- "3. Add one or more channels the bot should join.",
1386
+ "1. Create Twitch bot account. Generate OAuth: twitchapps.com/tmi",
1387
+ "2. Config: username, oauthToken, channels (required).",
1388
+ "3. Add allowFrom (recommended) to restrict who can trigger.",
1389
+ "4. Public chat: prefix required (default !). Whispers: no prefix.",
1512
1390
  "",
1513
- " 🔗 dev.twitch.tv"
1391
+ " 🔗 docs/twitch.md — dev.twitch.tv"
1514
1392
  ],
1515
1393
  status: "available",
1516
- notes: "Twitch IRC chat integration for streams / channel chat."
1394
+ notes: "IRC over WebSocket. Channel chat + whispers. Pairing, allowlist, modsBypass."
1517
1395
  },
1518
1396
  {
1519
1397
  id: "tlon",
1520
- name: "Tlon",
1521
- emoji: "🔵",
1398
+ name: "Tlon (Urbit Groups)",
1399
+ emoji: "🌊",
1522
1400
  requiresGateway: true,
1523
1401
  supportsDM: true,
1524
1402
  platforms: ["all"],
1525
- tokenLabel: "API Key",
1526
- tokenHint: "Token for your Tlon API / bot gateway",
1403
+ tokenLabel: "Login Code",
1404
+ tokenHint: "Ship login code from Landscape Settings → Access key (e.g. lidlut-tabwed-pillex-ridrup)",
1527
1405
  extraFields: [
1528
1406
  {
1529
- name: "apiBaseUrl",
1530
- label: "API Base URL",
1531
- hint: "e.g. https://tlon.example.com",
1407
+ name: "ship",
1408
+ label: "Ship Name",
1409
+ hint: "e.g. ~sampel-palnet",
1410
+ required: true
1411
+ },
1412
+ {
1413
+ name: "url",
1414
+ label: "Ship URL",
1415
+ hint: "e.g. https://sampel-palnet.tlon.network or http://localhost:8080",
1532
1416
  required: true
1533
1417
  },
1534
1418
  {
1535
- name: "channelId",
1536
- label: "Default Channel ID",
1419
+ name: "ownerShip",
1420
+ label: "Owner Ship",
1421
+ hint: "Your personal ship — always authorized, receives approval notifications",
1537
1422
  required: false
1538
1423
  },
1539
1424
  {
1540
- name: "webhookSecret",
1541
- label: "Webhook Secret",
1425
+ name: "allowPrivateNetwork",
1426
+ label: "Allow Private Network",
1427
+ hint: "Enable for localhost/LAN ships (SSRF opt-in)",
1542
1428
  required: false
1543
1429
  }
1544
1430
  ],
1545
1431
  setupSteps: [
1546
- "1. Point Tlon events to /webhook/tlon on your HyperClaw gateway.",
1547
- "2. Use the API base URL and API key for outbound sends.",
1548
- "3. Optionally set a default channel ID and webhook secret.",
1432
+ "1. Install plugin: hyperclaw plugins install @hyperclaw/extension-tlon",
1433
+ "2. Get your ship login code from Landscape (Settings System → Access key).",
1434
+ "3. Configure channels.tlon with ship name, URL, and login code.",
1435
+ "4. Optionally set ownerShip to receive approval notifications.",
1436
+ "5. Restart gateway: hyperclaw gateway restart",
1437
+ "6. DM the bot ship in Tlon or mention it in a group channel.",
1549
1438
  "",
1550
- " 🔗 Tlon bot gateway / integration endpoint"
1439
+ " Plugin required connects via Urbit Eyre HTTP API + SSE stream.",
1440
+ " See: docs/tlon.md"
1551
1441
  ],
1552
1442
  status: "available",
1553
- notes: "Generic Tlon bridge via webhook ingress + outbound API send."
1443
+ notes: "Urbit Eyre HTTP API + SSE. DMs, groups, reactions, owner approval, auto-discovery. Plugin: @hyperclaw/extension-tlon"
1554
1444
  },
1555
1445
  {
1556
1446
  id: "instagram",
@@ -1684,46 +1574,136 @@ const CHANNELS = [
1684
1574
  status: "available"
1685
1575
  },
1686
1576
  {
1687
- id: "nextcloud",
1577
+ id: "nextcloud-talk",
1688
1578
  name: "Nextcloud Talk",
1689
1579
  emoji: "☁️",
1690
1580
  requiresGateway: true,
1691
1581
  supportsDM: true,
1692
1582
  platforms: ["all"],
1693
- tokenLabel: "Nextcloud URL",
1694
- extraFields: [{
1695
- name: "username",
1696
- label: "Username",
1697
- required: true
1698
- }, {
1699
- name: "password",
1700
- label: "App Password",
1701
- required: true
1702
- }],
1583
+ tokenLabel: "Nextcloud instance URL",
1584
+ tokenHint: "e.g. https://cloud.example.com",
1585
+ extraFields: [
1586
+ {
1587
+ name: "botSecret",
1588
+ label: "Bot shared secret",
1589
+ hint: "From: occ talk:bot:install",
1590
+ required: true
1591
+ },
1592
+ {
1593
+ name: "botSecretFile",
1594
+ label: "Bot secret file path (optional)",
1595
+ hint: "Alternative to inline secret",
1596
+ required: false
1597
+ },
1598
+ {
1599
+ name: "apiUser",
1600
+ label: "API username (optional)",
1601
+ hint: "For DM detection + fallback send via OCS API",
1602
+ required: false
1603
+ },
1604
+ {
1605
+ name: "apiPassword",
1606
+ label: "API/app password (optional)",
1607
+ hint: "Nextcloud app password for apiUser",
1608
+ required: false
1609
+ },
1610
+ {
1611
+ name: "webhookPort",
1612
+ label: "Webhook listener port",
1613
+ hint: "Default: 8788",
1614
+ required: false
1615
+ },
1616
+ {
1617
+ name: "webhookPath",
1618
+ label: "Webhook path",
1619
+ hint: "Default: /nextcloud-talk-webhook",
1620
+ required: false
1621
+ },
1622
+ {
1623
+ name: "webhookPublicUrl",
1624
+ label: "Webhook public URL",
1625
+ hint: "If behind a proxy — set this in occ talk:bot:install",
1626
+ required: false
1627
+ },
1628
+ {
1629
+ name: "groupPolicy",
1630
+ label: "Room policy",
1631
+ hint: "allowlist (default) | open | disabled",
1632
+ required: false
1633
+ },
1634
+ {
1635
+ name: "textChunkLimit",
1636
+ label: "Text chunk limit (chars)",
1637
+ hint: "Default: 32000",
1638
+ required: false
1639
+ }
1640
+ ],
1703
1641
  setupSteps: [
1704
- "1. Create App Password: Nextcloud Profile Security → App passwords.",
1705
- "2. You need the Nextcloud URL, username and App Password.",
1642
+ "1. On your Nextcloud server, create the bot:",
1643
+ " ./occ talk:bot:install \"HyperClaw\" \"<shared-secret>\" \"<webhook-url>\" --feature reaction",
1644
+ " Replace <webhook-url> with your gateway URL, e.g. https://yourhost/nextcloud-talk-webhook",
1645
+ "2. Enable the bot in the target room settings (room → ⋯ → Bots).",
1646
+ "3. Enter the Nextcloud URL (token field) and the shared secret below.",
1647
+ "4. (Optional) Add apiUser + app password to enable DM detection via OCS API.",
1706
1648
  "",
1707
- " 🔗 nextcloud.com"
1649
+ " 🔗 nextcloud.com — docs.nextcloud.com/server/latest/admin_manual/talk_bots.html"
1708
1650
  ],
1709
- status: "available"
1651
+ status: "available",
1652
+ notes: "Webhook bot. Supports DMs (with apiUser), rooms, reactions. No media uploads."
1710
1653
  },
1711
1654
  {
1712
1655
  id: "zalo",
1713
1656
  name: "Zalo",
1714
1657
  emoji: "🔵",
1715
- requiresGateway: false,
1658
+ requiresGateway: true,
1716
1659
  supportsDM: true,
1717
1660
  platforms: ["all"],
1718
- tokenLabel: "Zalo OA Access Token",
1661
+ tokenLabel: "Zalo Bot Token (12345689:abc-xyz)",
1662
+ tokenHint: "From bot.zaloplatforms.com",
1663
+ extraFields: [
1664
+ {
1665
+ name: "tokenFile",
1666
+ label: "Token file path (optional)",
1667
+ hint: "Alternative to inline token",
1668
+ required: false
1669
+ },
1670
+ {
1671
+ name: "groupPolicy",
1672
+ label: "Group policy",
1673
+ hint: "allowlist (default) | open | disabled",
1674
+ required: false
1675
+ },
1676
+ {
1677
+ name: "webhookUrl",
1678
+ label: "Webhook URL (optional)",
1679
+ hint: "HTTPS required — leave blank for long-polling",
1680
+ required: false
1681
+ },
1682
+ {
1683
+ name: "webhookSecret",
1684
+ label: "Webhook secret (optional)",
1685
+ hint: "8-256 chars — required if webhookUrl is set",
1686
+ required: false
1687
+ },
1688
+ {
1689
+ name: "mediaMaxMb",
1690
+ label: "Media size limit (MB)",
1691
+ hint: "Default: 5",
1692
+ required: false
1693
+ }
1694
+ ],
1719
1695
  setupSteps: [
1720
- "1. Zalo Official Account (OA): developers.zalo.me My Apps.",
1721
- "2. Create an app and connect it to your OA.",
1722
- "3. Access Token from Zalo API (OAuth flow or test token for development).",
1696
+ "1. Go to https://bot.zaloplatforms.com and sign in.",
1697
+ "2. Create a new bot and configure its settings.",
1698
+ "3. Copy the bot token (format: 12345689:abc-xyz).",
1699
+ "4. Paste the token below.",
1700
+ "5. (Optional) Set webhookUrl for webhook mode (HTTPS required).",
1701
+ " Leave blank to use long-polling (no public URL needed).",
1723
1702
  "",
1724
- " 🔗 developers.zalo.me"
1703
+ " 🔗 bot.zaloplatforms.com"
1725
1704
  ],
1726
- status: "available"
1705
+ status: "available",
1706
+ notes: "Experimental. DMs supported; groups with allowlist policy. Long-polling by default."
1727
1707
  },
1728
1708
  {
1729
1709
  id: "web",
@@ -1812,18 +1792,24 @@ const ZALO_PERSONAL = {
1812
1792
  requiresGateway: true,
1813
1793
  supportsDM: true,
1814
1794
  platforms: ["all"],
1815
- tokenLabel: "Zalo Personal cookie token",
1816
- tokenHint: "Extract from browser",
1795
+ tokenLabel: "Cookie (from chat.zalo.me)",
1796
+ tokenHint: "DevTools Application → Cookies",
1797
+ extraFields: [{
1798
+ name: "dmPolicy",
1799
+ label: "DM policy",
1800
+ hint: "pairing (default) | allowlist | open | disabled",
1801
+ required: false
1802
+ }],
1817
1803
  setupSteps: [
1818
- "1. Unofficial APIuses browser cookies. May break with Zalo updates.",
1819
- "2. Open Zalo Web in browser, Developer ToolsApplicationCookies.",
1820
- "3. Look for the token/cookie Zalo uses for auth.",
1821
- "4. See docs/channels/zalo-personal.md for details.",
1804
+ "1. ⚠️ Experimentalunofficial. Account ban risk. Use at your own risk.",
1805
+ "2. Open chat.zalo.me, log in. DevTools ApplicationCookiescopy.",
1806
+ "3. Add channel, set cookie (or ZALO_PERSONAL_COOKIE env).",
1807
+ "4. hyperclaw gateway → hyperclaw pairing approve zalo-personal <CODE>",
1822
1808
  "",
1823
- " ⚠️ Unofficialuse at your own risk"
1809
+ " 🔗 docs/zalo-personal.md full setup"
1824
1810
  ],
1825
1811
  status: "available",
1826
- notes: "Uses unofficial Zalo personal API may break on Zalo app updates"
1812
+ notes: "Zalo Web cookie auth. DMs only. No groups. Text chunked ~2000 chars."
1827
1813
  };
1828
1814
  CHANNELS.push(ZALO_PERSONAL);
1829
1815
 
@@ -1955,6 +1941,180 @@ async function getConfiguredChannels() {
1955
1941
  return [];
1956
1942
  }
1957
1943
  }
1944
+ /**
1945
+ * hyperclaw channels login [channel]
1946
+ * Shortcut for first-time login flows (QR pairing, bot tokens, OAuth).
1947
+ * Delegates to channelsAdd with a login-oriented preamble.
1948
+ */
1949
+ async function channelsLogin(channelId) {
1950
+ console.log(chalk.default.bold.cyan("\n 🔑 Channel Login\n"));
1951
+ console.log(chalk.default.gray(" This wizard will guide you through first-time login for a channel.\n"));
1952
+ console.log(chalk.default.gray(" WhatsApp → QR code scan"));
1953
+ console.log(chalk.default.gray(" Telegram → bot token (from @BotFather)"));
1954
+ console.log(chalk.default.gray(" Discord → bot token (from Discord Developer Portal)"));
1955
+ console.log(chalk.default.gray(" Slack → bot + app tokens"));
1956
+ console.log(chalk.default.gray(" Signal → phone number + signal-cli daemon\n"));
1957
+ await channelsAdd(channelId);
1958
+ }
1959
+ function httpProbe(url, ms = 1200) {
1960
+ return new Promise((resolve) => {
1961
+ const req = http.default.get(url, { timeout: ms }, (res) => {
1962
+ res.resume();
1963
+ resolve((res.statusCode ?? 0) < 500);
1964
+ });
1965
+ req.on("error", () => resolve(false));
1966
+ req.on("timeout", () => {
1967
+ req.destroy();
1968
+ resolve(false);
1969
+ });
1970
+ });
1971
+ }
1972
+ /** Attempt a lightweight connectivity probe for a channel. */
1973
+ async function probeChannel(channelId, channelCfg) {
1974
+ switch (channelId) {
1975
+ case "telegram": {
1976
+ const token = channelCfg?.token || process.env.TELEGRAM_BOT_TOKEN;
1977
+ if (!token) return {
1978
+ status: "skipped",
1979
+ detail: "no token"
1980
+ };
1981
+ const ok = await httpProbe(`https://api.telegram.org/bot${token}/getMe`);
1982
+ return ok ? {
1983
+ status: "connected",
1984
+ detail: "api.telegram.org ok"
1985
+ } : {
1986
+ status: "unreachable",
1987
+ detail: "api.telegram.org unreachable"
1988
+ };
1989
+ }
1990
+ case "discord": {
1991
+ const ok = await httpProbe("https://discord.com/api/v10/gateway");
1992
+ return ok ? {
1993
+ status: "connected",
1994
+ detail: "discord.com/api ok"
1995
+ } : {
1996
+ status: "unreachable",
1997
+ detail: "discord.com unreachable"
1998
+ };
1999
+ }
2000
+ case "signal": {
2001
+ const daemonUrl = channelCfg?.daemonUrl || "http://127.0.0.1:8080";
2002
+ const ok = await httpProbe(`${daemonUrl}/v1/about`);
2003
+ return ok ? {
2004
+ status: "connected",
2005
+ detail: `signal-cli at ${daemonUrl}`
2006
+ } : {
2007
+ status: "unreachable",
2008
+ detail: `signal-cli not reachable at ${daemonUrl}`
2009
+ };
2010
+ }
2011
+ case "mattermost": {
2012
+ const baseUrl = channelCfg?.baseUrl;
2013
+ if (!baseUrl) return {
2014
+ status: "skipped",
2015
+ detail: "no baseUrl"
2016
+ };
2017
+ const ok = await httpProbe(`${baseUrl}/api/v4/system/ping`);
2018
+ return ok ? {
2019
+ status: "connected",
2020
+ detail: "system/ping ok"
2021
+ } : {
2022
+ status: "unreachable",
2023
+ detail: "server unreachable"
2024
+ };
2025
+ }
2026
+ case "matrix": {
2027
+ const homeserver = channelCfg?.homeserver;
2028
+ if (!homeserver) return {
2029
+ status: "skipped",
2030
+ detail: "no homeserver"
2031
+ };
2032
+ const ok = await httpProbe(`${homeserver}/_matrix/client/versions`);
2033
+ return ok ? {
2034
+ status: "connected",
2035
+ detail: "matrix client ok"
2036
+ } : {
2037
+ status: "unreachable",
2038
+ detail: "homeserver unreachable"
2039
+ };
2040
+ }
2041
+ case "slack": {
2042
+ const ok = await httpProbe("https://slack.com/api/api.test");
2043
+ return ok ? {
2044
+ status: "connected",
2045
+ detail: "slack.com/api ok"
2046
+ } : {
2047
+ status: "unreachable",
2048
+ detail: "slack.com unreachable"
2049
+ };
2050
+ }
2051
+ default: return {
2052
+ status: "skipped",
2053
+ detail: "probe not implemented"
2054
+ };
2055
+ }
2056
+ }
2057
+ /**
2058
+ * hyperclaw channels status [--probe]
2059
+ * Show all configured channels with their status.
2060
+ * --probe attempts a real connectivity check for each.
2061
+ */
2062
+ async function channelsStatus(opts = {}) {
2063
+ const configFile = path.default.join(os.default.homedir(), ".hyperclaw", "config.json");
2064
+ let cfg = {};
2065
+ try {
2066
+ cfg = fs_extra.default.readJsonSync(configFile);
2067
+ } catch {}
2068
+ const configured = cfg.channels || [];
2069
+ console.log(chalk.default.bold.cyan("\n 📡 CHANNEL STATUS\n"));
2070
+ if (configured.length === 0) {
2071
+ console.log(chalk.default.gray(" No channels configured.\n"));
2072
+ console.log(chalk.default.gray(" Add a channel: hyperclaw channels add\n"));
2073
+ return;
2074
+ }
2075
+ const spinner = opts.probe ? (0, ora.default)("Probing channels...").start() : null;
2076
+ const results = [];
2077
+ for (const id of configured) {
2078
+ const ch = getChannel(id);
2079
+ if (!ch) continue;
2080
+ const channelCfg = cfg.channelConfigs?.[id];
2081
+ const dmPolicy = channelCfg?.dmPolicy?.policy ?? channelCfg?.dmPolicy;
2082
+ const r = {
2083
+ id,
2084
+ name: ch.name,
2085
+ emoji: ch.emoji,
2086
+ configured: true,
2087
+ dmPolicy
2088
+ };
2089
+ if (opts.probe) {
2090
+ const { status, detail } = await probeChannel(id, channelCfg);
2091
+ r.probe = status;
2092
+ r.probeDetail = detail;
2093
+ }
2094
+ results.push(r);
2095
+ }
2096
+ if (spinner) spinner.stop();
2097
+ const allIds = CHANNELS.map((c) => c.id);
2098
+ const unconfigured = allIds.filter((id) => !configured.includes(id));
2099
+ for (const r of results) {
2100
+ const probeStr = opts.probe ? r.probe === "connected" ? chalk.default.green(" ✔ connected") : r.probe === "unreachable" ? chalk.default.red(" ✖ unreachable") : chalk.default.gray(" — skipped") : "";
2101
+ const dm = r.dmPolicy ? chalk.default.gray(` dm:${r.dmPolicy}`) : "";
2102
+ console.log(` ${chalk.default.green("●")} ${r.emoji} ${r.name.padEnd(18)}${dm}${probeStr}`);
2103
+ if (opts.probe && r.probeDetail) console.log(` ${chalk.default.gray(r.probeDetail)}`);
2104
+ }
2105
+ if (unconfigured.length > 0) console.log(chalk.default.gray(`\n ○ ${unconfigured.length} unconfigured channel(s) — add with: hyperclaw channels add`));
2106
+ console.log();
2107
+ if (opts.probe) {
2108
+ const unreachable = results.filter((r) => r.probe === "unreachable");
2109
+ if (unreachable.length > 0) {
2110
+ console.log(chalk.default.yellow(` ⚠ ${unreachable.length} channel(s) unreachable:`));
2111
+ for (const r of unreachable) console.log(chalk.default.gray(` ${r.id}: ${r.probeDetail}`));
2112
+ console.log();
2113
+ console.log(chalk.default.gray(" Troubleshoot: hyperclaw doctor"));
2114
+ console.log(chalk.default.gray(" Logs: hyperclaw logs --follow\n"));
2115
+ } else console.log(chalk.default.green(" ✔ All probed channels reachable\n"));
2116
+ }
2117
+ }
1958
2118
 
1959
2119
  //#endregion
1960
2120
  //#region src/infra/update-channels.ts
@@ -2266,38 +2426,93 @@ var init_queue = require_chunk.__esm({ "src/delivery/queue.ts"() {
2266
2426
  //#endregion
2267
2427
  //#region src/cli/run-main.ts
2268
2428
  const program = new commander.Command();
2269
- program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("4.0.1");
2429
+ program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("5.0.0").option("--profile <name>", "Use an isolated gateway profile. Auto-scopes HYPERCLAW_STATE_DIR and HYPERCLAW_CONFIG_PATH. Required for multi-gateway setups (rescue bot, staging, etc.). Example: hyperclaw --profile rescue gateway --port 19001").hook("preAction", (thisCommand) => {
2430
+ const profile = thisCommand.opts().profile;
2431
+ if (profile) {
2432
+ const os$8 = require("os");
2433
+ const path$7 = require("path");
2434
+ const home = os$8.homedir();
2435
+ if (!process.env.HYPERCLAW_STATE_DIR) process.env.HYPERCLAW_STATE_DIR = path$7.join(home, `.hyperclaw-${profile}`);
2436
+ if (!process.env.HYPERCLAW_CONFIG_PATH) process.env.HYPERCLAW_CONFIG_PATH = path$7.join(process.env.HYPERCLAW_STATE_DIR, "hyperclaw.json");
2437
+ }
2438
+ });
2270
2439
  program.command("init").description("Initialize HyperClaw with interactive wizard").option("-a, --auto-config", "Auto-configure with defaults").option("-d, --daemon", "Install as system daemon").option("-s, --start-now", "Start gateway after setup").action(async (opts) => {
2271
2440
  await new require_onboard.Banner().showNeonBanner(false);
2272
2441
  await new require_onboard.HyperClawWizard().run(opts);
2273
2442
  process.exit(0);
2274
2443
  });
2275
- program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").action(async (opts) => {
2444
+ program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").option("--reset", "Reset config before running wizard (sends to trash, not deleted)").option("--reset-scope <scope>", "What to reset: config | config+creds | full", "config").option("--non-interactive", "Run in non-interactive mode (use flags for all options)").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all (non-interactive)", "loopback").option("--daemon-runtime <runtime>", "Daemon runtime: node | bun (non-interactive)", "node").option("--skip-skills", "Skip skills setup (non-interactive)").option("--skip-search", "Skip web search setup (non-interactive)").action(async (opts) => {
2276
2445
  await new require_onboard.Banner().showNeonBanner(false);
2446
+ if (opts.reset) {
2447
+ const fs$7 = require("fs-extra");
2448
+ const path$7 = require("path");
2449
+ const os$8 = require("os");
2450
+ const hcDir = path$7.join(os$8.homedir(), ".hyperclaw");
2451
+ const scope = opts.resetScope ?? "config";
2452
+ const filesToRemove = [path$7.join(hcDir, "hyperclaw.json")];
2453
+ if (scope === "config+creds" || scope === "full") {
2454
+ filesToRemove.push(path$7.join(hcDir, "credentials"));
2455
+ filesToRemove.push(path$7.join(hcDir, "sessions"));
2456
+ }
2457
+ if (scope === "full") filesToRemove.push(path$7.join(hcDir, "workspace"));
2458
+ const chalk$11 = require("chalk");
2459
+ console.log(chalk$11.yellow(`\n ⚠ Reset scope: ${chalk$11.bold(scope)}\n`));
2460
+ console.log(chalk$11.gray(" Files to remove:"));
2461
+ filesToRemove.forEach((f) => console.log(chalk$11.gray(` • ${f}`)));
2462
+ const inquirer$2 = require("inquirer");
2463
+ const { confirmReset } = await inquirer$2.prompt([{
2464
+ type: "confirm",
2465
+ name: "confirmReset",
2466
+ message: "Confirm reset? (files will be moved to trash/backup, not permanently deleted)",
2467
+ default: false
2468
+ }]);
2469
+ if (confirmReset) {
2470
+ const backupDir = path$7.join(hcDir, `backup-${Date.now()}`);
2471
+ await fs$7.ensureDir(backupDir);
2472
+ for (const f of filesToRemove) if (await fs$7.pathExists(f)) {
2473
+ const dest = path$7.join(backupDir, path$7.basename(f));
2474
+ await fs$7.move(f, dest);
2475
+ console.log(chalk$11.gray(` ✓ Moved ${path$7.basename(f)} → backup/`));
2476
+ }
2477
+ console.log(chalk$11.green("\n ✔ Reset complete. Starting fresh...\n"));
2478
+ } else {
2479
+ console.log(chalk$11.gray("\n Reset cancelled.\n"));
2480
+ process.exit(0);
2481
+ }
2482
+ }
2277
2483
  if (opts.installDaemon) {
2278
- const chalk$13 = require("chalk");
2279
- console.log(chalk$13.yellow("\n ⚠ --install-daemon mode\n"));
2280
- console.log(chalk$13.gray(" The daemon will run as a background system service and will have:\n"));
2281
- console.log(chalk$13.white(" • Full shell / command execution on this machine"));
2282
- console.log(chalk$13.white(" • File system read & write access"));
2283
- console.log(chalk$13.white(" • Network access (gateway WebSocket)"));
2284
- console.log(chalk$13.white(" • Auto-start on every system boot\n"));
2285
- const inquirer$3 = require("inquirer");
2286
- const { confirmed } = await inquirer$3.prompt([{
2484
+ const chalk$11 = require("chalk");
2485
+ console.log(chalk$11.yellow("\n ⚠ --install-daemon mode\n"));
2486
+ console.log(chalk$11.gray(" The daemon will run as a background system service and will have:\n"));
2487
+ console.log(chalk$11.white(" • Full shell / command execution on this machine"));
2488
+ console.log(chalk$11.white(" • File system read & write access"));
2489
+ console.log(chalk$11.white(" • Network access (gateway WebSocket)"));
2490
+ console.log(chalk$11.white(" • Auto-start on every system boot\n"));
2491
+ const inquirer$2 = require("inquirer");
2492
+ const { confirmed } = await inquirer$2.prompt([{
2287
2493
  type: "confirm",
2288
2494
  name: "confirmed",
2289
2495
  message: "I understand and want to continue with full PC access:",
2290
2496
  default: false
2291
2497
  }]);
2292
2498
  if (!confirmed) {
2293
- console.log(chalk$13.gray("\n Cancelled. Run without --install-daemon to choose during setup.\n"));
2499
+ console.log(chalk$11.gray("\n Cancelled. Run without --install-daemon to choose during setup.\n"));
2294
2500
  process.exit(0);
2295
2501
  }
2296
2502
  }
2297
2503
  await new require_onboard.HyperClawWizard().run({
2298
2504
  ...opts,
2299
2505
  wizard: true,
2300
- installDaemon: opts.installDaemon ?? false
2506
+ installDaemon: opts.installDaemon ?? false,
2507
+ nonInteractive: opts.nonInteractive ?? false,
2508
+ jsonOutput: opts.json ?? false,
2509
+ skipSkills: opts.skipSkills ?? false,
2510
+ skipSearch: opts.skipSearch ?? false,
2511
+ daemonRuntime: opts.daemonRuntime ?? "node",
2512
+ gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
2513
+ gatewayBind: opts.gatewayBind ?? "loopback",
2514
+ anthropicApiKey: opts.anthropicApiKey,
2515
+ openaiApiKey: opts.openaiApiKey
2301
2516
  });
2302
2517
  process.exit(0);
2303
2518
  });
@@ -2322,18 +2537,18 @@ gatewayCmd.command("status").description("Show gateway status").action(async ()
2322
2537
  process.exit(0);
2323
2538
  });
2324
2539
  gatewayCmd.command("start").description("Start the gateway service").option("-p, --port <port>", "Override port").action(async (opts) => {
2325
- await new require_onboard.DaemonManager().start();
2540
+ await new require_daemon.DaemonManager().start();
2326
2541
  });
2327
2542
  gatewayCmd.command("stop").description("Stop the gateway service").action(async () => {
2328
- await new require_onboard.DaemonManager().stop();
2543
+ await new require_daemon.DaemonManager().stop();
2329
2544
  process.exit(0);
2330
2545
  });
2331
2546
  gatewayCmd.command("restart").description("Restart the gateway service").action(async () => {
2332
- const dm = new require_onboard.DaemonManager();
2547
+ const dm = new require_daemon.DaemonManager();
2333
2548
  await dm.restart();
2334
2549
  });
2335
- program.command("daemon").description("Manage HyperClaw system service (alias: gateway)").argument("<action>", "start|stop|restart|status|logs").action(async (action) => {
2336
- const dm = new require_onboard.DaemonManager();
2550
+ program.command("daemon").description("Manage HyperClaw system service (alias: gateway)").argument("<action>", "start|stop|restart|status|logs|install|uninstall").action(async (action) => {
2551
+ const dm = new require_daemon.DaemonManager();
2337
2552
  if (action === "start") await new require_onboard.Banner().showNeonBanner(true);
2338
2553
  await dm.handle(action);
2339
2554
  if (action === "start" || action === "restart") return;
@@ -2341,20 +2556,20 @@ program.command("daemon").description("Manage HyperClaw system service (alias: g
2341
2556
  });
2342
2557
  const sandboxCmd = program.command("sandbox").description("Debug sandbox and tool policy");
2343
2558
  sandboxCmd.command("explain").description("Show effective sandbox mode, tool policy, and allowed tools").option("--json", "Output as JSON").action(async (opts) => {
2344
- const chalk$13 = require("chalk");
2345
- const fs$10 = require("fs-extra");
2346
- const path$10 = require("path");
2347
- const os$9 = require("os");
2559
+ const chalk$11 = require("chalk");
2560
+ const fs$7 = require("fs-extra");
2561
+ const path$7 = require("path");
2562
+ const os$8 = require("os");
2348
2563
  const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
2349
2564
  const cfgPath = getConfigPath();
2350
2565
  let cfg = {};
2351
2566
  try {
2352
- cfg = await fs$10.readJson(cfgPath);
2567
+ cfg = await fs$7.readJson(cfgPath);
2353
2568
  } catch {}
2354
2569
  const sandboxMode = cfg?.agents?.defaults?.sandbox?.mode ?? "non-main";
2355
2570
  const toolsCfg = cfg?.tools ?? {};
2356
- const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-CFF-d5fs.js"));
2357
- const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
2571
+ const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-CNT-mF2Z.js"));
2572
+ const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2358
2573
  const allTools = [
2359
2574
  ...getBuiltinTools(),
2360
2575
  ...getSessionsTools(() => null),
@@ -2382,15 +2597,15 @@ sandboxCmd.command("explain").description("Show effective sandbox mode, tool pol
2382
2597
  }, null, 2));
2383
2598
  process.exit(0);
2384
2599
  }
2385
- console.log(chalk$13.bold.hex("#06b6d4")("\n 🔒 SANDBOX EXPLAIN\n"));
2600
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 🔒 SANDBOX EXPLAIN\n"));
2386
2601
  console.log(` Sandbox mode: ${sandboxMode} (non-main sessions get restricted pcTools)`);
2387
2602
  console.log(` Tool profile: ${policy.profile}`);
2388
2603
  console.log(` Policy source: ${policy.source}`);
2389
2604
  if (policy.allow?.length) console.log(` Allow: ${policy.allow.join(", ")}`);
2390
2605
  if (policy.deny?.length) console.log(` Deny: ${policy.deny.join(", ")}`);
2391
2606
  console.log(` Tools: ${filtered.length} / ${allTools.length} allowed`);
2392
- console.log(chalk$13.gray("\n Allowed: " + filtered.map((t) => t.name).join(", ")));
2393
- console.log(chalk$13.gray("\n Elevated: " + ((cfg?.tools?.elevated)?.enabled ? "enabled" : "disabled")));
2607
+ console.log(chalk$11.gray("\n Allowed: " + filtered.map((t) => t.name).join(", ")));
2608
+ console.log(chalk$11.gray("\n Elevated: " + ((cfg?.tools?.elevated)?.enabled ? "enabled" : "disabled")));
2394
2609
  console.log();
2395
2610
  process.exit(0);
2396
2611
  });
@@ -2407,6 +2622,14 @@ channelsCmd.command("remove <channel>").description("Remove a channel").action(a
2407
2622
  await channelsRemove(channel);
2408
2623
  process.exit(0);
2409
2624
  });
2625
+ channelsCmd.command("login [channel]").description("First-time login / QR pairing for a channel").action(async (channel) => {
2626
+ await channelsLogin(channel);
2627
+ process.exit(0);
2628
+ });
2629
+ channelsCmd.command("status").description("Show channel status (use --probe to test connectivity)").option("--probe", "Probe each channel for real connectivity").action(async (opts) => {
2630
+ await channelsStatus({ probe: !!opts.probe });
2631
+ process.exit(0);
2632
+ });
2410
2633
  const hooksCmd = program.command("hooks").description("Hook management");
2411
2634
  hooksCmd.command("list").description("List all hooks").option("--eligible", "Show only eligible hooks").action((opts) => {
2412
2635
  new require_loader.HookLoader().list(opts.eligible);
@@ -2430,24 +2653,79 @@ hooksCmd.command("install <pack>").description("Install a hook pack").action(asy
2430
2653
  });
2431
2654
  const agentsCmd = program.command("agents").description("Multi-agent routing");
2432
2655
  agentsCmd.command("bindings").description("List agent ↔ channel bindings").action(() => {
2433
- new AgentRouter().listBindings();
2656
+ new require_agents_routing.AgentRouter().listBindings();
2434
2657
  process.exit(0);
2435
2658
  });
2436
2659
  agentsCmd.command("bind").description("Bind a channel to an agent workspace").action(async () => {
2437
- await new AgentRouter().bind();
2660
+ await new require_agents_routing.AgentRouter().bind();
2438
2661
  process.exit(0);
2439
2662
  });
2440
2663
  agentsCmd.command("unbind").description("Remove channel ↔ agent bindings").action(async () => {
2441
- await new AgentRouter().unbind();
2664
+ await new require_agents_routing.AgentRouter().unbind();
2442
2665
  process.exit(0);
2443
2666
  });
2444
2667
  const pairingCmd = program.command("pairing").description("DM pairing codes");
2445
- pairingCmd.command("list").description("List pending and approved pairing codes").action(() => {
2446
- new require_pairing.PairingStore().showList();
2668
+ pairingCmd.command("list [channel]").description("List pending DM pairing requests (optionally filter by channel)").action(async (channel) => {
2669
+ await new require_pairing.GlobalPairingManager().showList(channel);
2670
+ process.exit(0);
2671
+ });
2672
+ pairingCmd.command("approve <channel> <code>").description("Approve a pairing code and add sender to channel allowlist").option("--account <id>", "Account ID for multi-account channels", "default").action(async (channel, code, opts) => {
2673
+ await new require_pairing.GlobalPairingManager().cliApprove(channel, code, opts.account);
2674
+ process.exit(0);
2675
+ });
2676
+ const devicesCmd = program.command("devices").description("Node/device pairing (iOS, Android, macOS, headless)");
2677
+ devicesCmd.command("list").description("List pending and paired devices").action(async () => {
2678
+ await new DevicePairingStore().showCLI();
2679
+ process.exit(0);
2680
+ });
2681
+ devicesCmd.command("pair").description("Create a new device pairing request and print setup code").option("-u, --gateway-url <url>", "Gateway WebSocket URL", "ws://localhost:18789").option("-n, --name <name>", "Device name (optional)").option("-p, --platform <platform>", "Platform hint: ios|android|macos|headless (optional)").action(async (opts) => {
2682
+ const store = new DevicePairingStore();
2683
+ const result = await store.createRequest(opts.gatewayUrl, {
2684
+ deviceName: opts.name,
2685
+ platform: opts.platform
2686
+ });
2687
+ console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIR REQUEST\n"));
2688
+ console.log(` Request ID: ${chalk.default.bold(result.requestId)}`);
2689
+ console.log(` Expires: ${chalk.default.gray(new Date(result.expiresAt).toLocaleTimeString())}`);
2690
+ console.log();
2691
+ console.log(chalk.default.yellow(" Setup code (send to device or paste in app):"));
2692
+ console.log(chalk.default.bold(`\n ${result.setupCode}\n`));
2693
+ console.log(chalk.default.gray(" Approve: hyperclaw devices approve " + result.requestId));
2694
+ console.log(chalk.default.gray(" Reject: hyperclaw devices reject " + result.requestId));
2695
+ console.log(chalk.default.gray("\n Telegram: message your bot with /pair for guided flow.\n"));
2696
+ process.exit(0);
2697
+ });
2698
+ devicesCmd.command("approve <requestId>").description("Approve a pending device pairing request").action(async (requestId) => {
2699
+ const store = new DevicePairingStore();
2700
+ const device = await store.approve(requestId);
2701
+ if (device) {
2702
+ console.log(chalk.default.green(`\n ✔ Device approved: ${chalk.default.bold(device.deviceId)}`));
2703
+ if (device.deviceName) console.log(chalk.default.gray(` Name: ${device.deviceName}`));
2704
+ console.log(chalk.default.gray(` Paired at: ${device.pairedAt}\n`));
2705
+ } else {
2706
+ console.log(chalk.default.red(`\n ✖ Request not found or expired: ${requestId}\n`));
2707
+ process.exit(1);
2708
+ }
2709
+ process.exit(0);
2710
+ });
2711
+ devicesCmd.command("reject <requestId>").description("Reject a pending device pairing request").action(async (requestId) => {
2712
+ const store = new DevicePairingStore();
2713
+ const ok = await store.reject(requestId);
2714
+ if (ok) console.log(chalk.default.green(`\n ✔ Request rejected: ${requestId}\n`));
2715
+ else {
2716
+ console.log(chalk.default.red(`\n ✖ Request not found: ${requestId}\n`));
2717
+ process.exit(1);
2718
+ }
2447
2719
  process.exit(0);
2448
2720
  });
2449
- pairingCmd.command("approve <channel> <code>").description("Approve a pairing code and add user to allowlist").action((channel, code) => {
2450
- new require_pairing.PairingStore().cliApprove(channel, code);
2721
+ devicesCmd.command("unpair <deviceId>").description("Remove a paired device").action(async (deviceId) => {
2722
+ const store = new DevicePairingStore();
2723
+ const ok = await store.unpair(deviceId);
2724
+ if (ok) console.log(chalk.default.green(`\n ✔ Device unpaired: ${deviceId}\n`));
2725
+ else {
2726
+ console.log(chalk.default.red(`\n ✖ Device not found: ${deviceId}\n`));
2727
+ process.exit(1);
2728
+ }
2451
2729
  process.exit(0);
2452
2730
  });
2453
2731
  const msgCmd = program.command("message").description("Send messages");
@@ -2456,7 +2734,7 @@ msgCmd.command("send").description("Send a message via a configured channel").op
2456
2734
  process.exit(0);
2457
2735
  });
2458
2736
  program.command("hub").description("Skill hub — browse marketplace, install, scan skills").option("-i, --install <id>", "Install skill").option("-s, --scan <id>", "Security scan a skill").option("-m, --marketplace", "ClawHub-style marketplace view (installed + bundled)").option("--force", "Force install (bypass risk block)").option("--hide-suspicious", "Hide suspicious/dangerous skills").action(async (opts) => {
2459
- const hub = new SkillHub();
2737
+ const hub = new require_hub.SkillHub();
2460
2738
  if (opts.scan) await hub.scan(opts.scan);
2461
2739
  else if (opts.install) await hub.install(opts.install, opts.force);
2462
2740
  else if (opts.marketplace) await hub.showMarketplace({ hideSuspicious: opts.hideSuspicious });
@@ -2465,7 +2743,7 @@ program.command("hub").description("Skill hub — browse marketplace, install, s
2465
2743
  });
2466
2744
  const skillCmd = program.command("skill").description("ClawHub — search and install skills from registry");
2467
2745
  skillCmd.command("search [query]").description("Search ClawHub for skills").option("-c, --category <cat>", "Filter by category").action(async (query, opts) => {
2468
- const hub = new SkillHub();
2746
+ const hub = new require_hub.SkillHub();
2469
2747
  const q = query || "";
2470
2748
  const skills = await hub.searchClawHub(q, opts.category);
2471
2749
  if (skills.length === 0) {
@@ -2483,11 +2761,11 @@ skillCmd.command("search [query]").description("Search ClawHub for skills").opti
2483
2761
  process.exit(0);
2484
2762
  });
2485
2763
  skillCmd.command("list").description("List installed skills (bundled + ClawHub)").action(async () => {
2486
- const hub = new SkillHub();
2764
+ const hub = new require_hub.SkillHub();
2487
2765
  const installed = await hub.getInstalled();
2488
2766
  console.log(chalk.default.bold.hex("#06b6d4")("\n Installed skills:\n"));
2489
2767
  for (const s of installed) {
2490
- const src = SKILL_REGISTRY.some((r) => r.id === s.id) ? "" : " (ClawHub)";
2768
+ const src = require_hub.SKILL_REGISTRY.some((r) => r.id === s.id) ? "" : " (ClawHub)";
2491
2769
  console.log(` ${chalk.default.hex("#06b6d4")("✓")} ${s.name} ${chalk.default.gray(`(${s.id})${src}`)}`);
2492
2770
  }
2493
2771
  if (installed.length === 0) console.log(chalk.default.gray(" No skills installed. Run: hyperclaw hub or hyperclaw skill search <query>\n"));
@@ -2495,14 +2773,14 @@ skillCmd.command("list").description("List installed skills (bundled + ClawHub)"
2495
2773
  process.exit(0);
2496
2774
  });
2497
2775
  skillCmd.command("install <id>").description("Install skill from ClawHub (or bundled when registry unavailable)").option("-v, --version <ver>", "Pin version (e.g. 2.0.0)").option("--force", "Force install (bypass risk block for bundled)").action(async (id, opts) => {
2498
- const hub = new SkillHub();
2499
- const ora$6 = (await import("ora")).default;
2500
- const s = ora$6(`Installing ${id}...`).start();
2776
+ const hub = new require_hub.SkillHub();
2777
+ const ora$4 = (await import("ora")).default;
2778
+ const s = ora$4(`Installing ${id}...`).start();
2501
2779
  try {
2502
2780
  const dest = await hub.installFromClawHub(id, opts.version);
2503
2781
  s.succeed(`Installed to ${dest}`);
2504
2782
  } catch (e) {
2505
- const match = SKILL_REGISTRY.find((x) => x.id === id);
2783
+ const match = require_hub.SKILL_REGISTRY.find((x) => x.id === id);
2506
2784
  if (match) {
2507
2785
  await hub.install(id, !!opts?.force);
2508
2786
  s.succeed(`Installed bundled skill: ${match.name}`);
@@ -2511,13 +2789,13 @@ skillCmd.command("install <id>").description("Install skill from ClawHub (or bun
2511
2789
  process.exit(0);
2512
2790
  });
2513
2791
  program.command("menu-bar").description("Launch macOS menu bar companion (Electron tray app)").action(async () => {
2514
- const path$10 = await import("path");
2792
+ const path$7 = await import("path");
2515
2793
  const { spawn } = await import("child_process");
2516
- const fs$10 = await import("fs-extra");
2517
- const root = path$10.join(process.cwd(), "apps", "macos");
2518
- const altRoot = path$10.join(__dirname, "..", "..", "apps", "macos");
2519
- const macosDir = await fs$10.pathExists(root) ? root : await fs$10.pathExists(altRoot) ? altRoot : null;
2520
- if (!macosDir || !await fs$10.pathExists(path$10.join(macosDir, "package.json"))) {
2794
+ const fs$7 = await import("fs-extra");
2795
+ const root = path$7.join(process.cwd(), "apps", "macos");
2796
+ const altRoot = path$7.join(__dirname, "..", "..", "apps", "macos");
2797
+ const macosDir = await fs$7.pathExists(root) ? root : await fs$7.pathExists(altRoot) ? altRoot : null;
2798
+ if (!macosDir || !await fs$7.pathExists(path$7.join(macosDir, "package.json"))) {
2521
2799
  console.log(chalk.default.gray("\n macOS menu bar app not found."));
2522
2800
  console.log(chalk.default.gray(" Run from HyperClaw repo root, or: cd apps/macos && npm start\n"));
2523
2801
  process.exit(1);
@@ -2544,8 +2822,15 @@ program.command("update").description("Update HyperClaw").option("-c, --channel
2544
2822
  await performUpdate(effective.channel, installKind);
2545
2823
  process.exit(0);
2546
2824
  });
2547
- program.command("doctor").description("Health check — surfaces misconfigs and risky DM policies").option("--fix", "Auto-repair fixable issues").action(async (opts) => {
2548
- await runDoctor(opts.fix);
2825
+ program.command("doctor").description("Health check — surfaces misconfigs, risky DM policies, and repairs").option("--fix", "Auto-repair fixable issues").option("--repair", "Apply recommended repairs (same as --fix)").option("--force", "Apply aggressive repairs (use with --repair)").option("-y, --yes", "Accept defaults without prompting").option("--non-interactive", "Skip prompts; only run safe migrations").option("--deep", "Scan system services for extra gateway installs").action(async (opts) => {
2826
+ await require_doctor.runDoctor(opts.fix || opts.repair, {
2827
+ fix: opts.fix,
2828
+ repair: opts.repair,
2829
+ force: opts.force,
2830
+ yes: opts.yes,
2831
+ nonInteractive: opts.nonInteractive,
2832
+ deep: opts.deep
2833
+ });
2549
2834
  process.exit(0);
2550
2835
  });
2551
2836
  const memCmd = program.command("memory").description("Agent memory management");
@@ -2591,12 +2876,12 @@ cfgCmd.command("set-key <KEY=value>").description("Set an API key or config valu
2591
2876
  process.exit(0);
2592
2877
  });
2593
2878
  cfgCmd.command("set-service-key <serviceId> [apiKey]").description("Set API key for a service (hackerone, bugcrowd, synack, or custom). Prompts if apiKey omitted.").action(async (serviceId, apiKey) => {
2594
- const inquirer$3 = (await import("inquirer")).default;
2879
+ const inquirer$2 = (await import("inquirer")).default;
2595
2880
  const config = new require_manager.ConfigManager();
2596
2881
  const cfg = await config.load();
2597
2882
  let key = apiKey;
2598
2883
  if (!key || key.trim().length === 0) {
2599
- const r = await inquirer$3.prompt([{
2884
+ const r = await inquirer$2.prompt([{
2600
2885
  type: "password",
2601
2886
  name: "k",
2602
2887
  message: `API key for ${serviceId}:`,
@@ -2627,7 +2912,7 @@ cfgCmd.command("set-service-key <serviceId> [apiKey]").description("Set API key
2627
2912
  cfgCmd.command("schema").description("Show configuration schema").action(() => {
2628
2913
  console.log(chalk.default.bold.hex("#06b6d4")("\n Config schema: ~/.hyperclaw/config.json\n"));
2629
2914
  const schema = {
2630
- version: "string (e.g. \"4.0.1\")",
2915
+ version: "string (e.g. \"5.0.0\")",
2631
2916
  workspaceName: "string",
2632
2917
  provider: {
2633
2918
  providerId: "string",
@@ -2720,17 +3005,17 @@ program.command("deploy").description("Deploy gateway to cloud (Fly.io or Render
2720
3005
  program.command("voice-call").description("Start voice call session — terminal mode, talks to gateway").option("-u, --gateway-url <url>", "Gateway URL", "http://localhost:18789").action(async (opts) => {
2721
3006
  const axios = (await import("axios")).default;
2722
3007
  const readline$2 = await import("readline");
2723
- const chalk$13 = require("chalk");
3008
+ const chalk$11 = require("chalk");
2724
3009
  const url = opts.gatewayUrl || "http://localhost:18789";
2725
- console.log(chalk$13.bold.cyan("\n 🎙️ HYPERCLAW VOICE CALL\n"));
2726
- console.log(chalk$13.gray(` Gateway: ${url}`));
2727
- console.log(chalk$13.gray(" Type a message and press Enter. Ctrl+C to exit.\n"));
3010
+ console.log(chalk$11.bold.cyan("\n 🎙️ HYPERCLAW VOICE CALL\n"));
3011
+ console.log(chalk$11.gray(` Gateway: ${url}`));
3012
+ console.log(chalk$11.gray(" Type a message and press Enter. Ctrl+C to exit.\n"));
2728
3013
  const rl = readline$2.createInterface({
2729
3014
  input: process.stdin,
2730
3015
  output: process.stdout
2731
3016
  });
2732
3017
  const ask = () => {
2733
- rl.question(chalk$13.cyan(" You: "), async (input) => {
3018
+ rl.question(chalk$11.cyan(" You: "), async (input) => {
2734
3019
  if (!input?.trim()) {
2735
3020
  ask();
2736
3021
  return;
@@ -2740,9 +3025,9 @@ program.command("voice-call").description("Start voice call session — terminal
2740
3025
  message: input.trim(),
2741
3026
  thinking: "none"
2742
3027
  }, { timeout: 6e4 });
2743
- console.log(chalk$13.green(` 🦅 Agent: ${(res.data?.response || "").slice(0, 500)}\n`));
3028
+ console.log(chalk$11.green(` 🦅 Agent: ${(res.data?.response || "").slice(0, 500)}\n`));
2744
3029
  } catch (e) {
2745
- console.log(chalk$13.red(` Error: ${e.response?.data?.error || e.message}\n`));
3030
+ console.log(chalk$11.red(` Error: ${e.response?.data?.error || e.message}\n`));
2746
3031
  }
2747
3032
  ask();
2748
3033
  });
@@ -2762,13 +3047,102 @@ program.command("dashboard").description("Launch live terminal dashboard").optio
2762
3047
  await new Dashboard().launch(opts.live);
2763
3048
  if (!opts.live) process.exit(0);
2764
3049
  });
2765
- program.command("status").description("System overview").action(async () => {
3050
+ program.command("status").description("System overview").option("--all", "Full local diagnosis (read-only)").option("--deep", "Also probe the running gateway").action(async (opts) => {
2766
3051
  await new require_onboard.Banner().showStatus();
3052
+ if (opts.all || opts.deep) {
3053
+ const fs$7 = await import("fs-extra");
3054
+ const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3055
+ const t = (await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"))).getTheme(false);
3056
+ const configPath = getConfigPath();
3057
+ console.log(t.bold("\n ─── Deep status ───\n"));
3058
+ try {
3059
+ const cfg = await fs$7.readJson(configPath);
3060
+ console.log(t.muted(" Config: ") + (cfg ? t.success("loaded") : t.error("missing")));
3061
+ console.log(t.muted(" Channels: ") + JSON.stringify(cfg?.gateway?.enabledChannels || cfg?.channels || []));
3062
+ } catch {
3063
+ console.log(t.muted(" Config: ") + t.error("unreadable"));
3064
+ }
3065
+ if (opts.deep) {
3066
+ const http$2 = await import("http");
3067
+ const { resolveGatewayUrl } = await Promise.resolve().then(() => require("./health-B-asI__D.js"));
3068
+ const cfg = await new require_manager.ConfigManager().load();
3069
+ const { gatewayUrl } = resolveGatewayUrl(cfg);
3070
+ const u = new URL(gatewayUrl);
3071
+ const optsReq = {
3072
+ hostname: u.hostname,
3073
+ port: u.port || (u.protocol === "https:" ? 443 : 80),
3074
+ path: "/api/status",
3075
+ method: "GET",
3076
+ timeout: 3e3
3077
+ };
3078
+ if (u.protocol === "https:") {
3079
+ const https = await import("https");
3080
+ await new Promise((resolve) => {
3081
+ const req = https.request(`${gatewayUrl}/api/status`, { timeout: 3e3 }, (res) => {
3082
+ let d = "";
3083
+ res.on("data", (c) => d += c);
3084
+ res.on("end", () => {
3085
+ try {
3086
+ const j = JSON.parse(d);
3087
+ console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
3088
+ } catch {
3089
+ console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
3090
+ }
3091
+ resolve();
3092
+ });
3093
+ });
3094
+ req.on("error", () => {
3095
+ console.log(t.muted(" Gateway: ") + t.error("unreachable"));
3096
+ resolve();
3097
+ });
3098
+ req.on("timeout", () => {
3099
+ req.destroy();
3100
+ console.log(t.muted(" Gateway: ") + t.error("timeout"));
3101
+ resolve();
3102
+ });
3103
+ req.end();
3104
+ });
3105
+ } else await new Promise((resolve) => {
3106
+ const req = http$2.request(optsReq, (res) => {
3107
+ let d = "";
3108
+ res.on("data", (c) => d += c);
3109
+ res.on("end", () => {
3110
+ try {
3111
+ const j = JSON.parse(d);
3112
+ console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
3113
+ } catch {
3114
+ console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
3115
+ }
3116
+ resolve();
3117
+ });
3118
+ });
3119
+ req.on("error", () => {
3120
+ console.log(t.muted(" Gateway: ") + t.error("unreachable"));
3121
+ resolve();
3122
+ });
3123
+ req.on("timeout", () => {
3124
+ req.destroy();
3125
+ console.log(t.muted(" Gateway: ") + t.error("timeout"));
3126
+ resolve();
3127
+ });
3128
+ req.end();
3129
+ });
3130
+ }
3131
+ console.log();
3132
+ }
2767
3133
  process.exit(0);
2768
3134
  });
3135
+ program.command("health").description("Quick gateway health probe (Runtime, RPC probe, channel count)").option("--json", "Output raw JSON").option("-v, --verbose", "Show state dir, config path, and env overrides").action(async (opts) => {
3136
+ const result = await require_health.runHealth({
3137
+ json: opts.json,
3138
+ verbose: opts.verbose
3139
+ });
3140
+ if (!result.allOk) process.exitCode = 1;
3141
+ process.exit(process.exitCode ?? 0);
3142
+ });
2769
3143
  const themeCmd = program.command("theme").description("Switch CLI color theme");
2770
3144
  themeCmd.command("list").description("List available themes").action(async () => {
2771
- const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3145
+ const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2772
3146
  const current = getThemeName();
2773
3147
  console.log(chalk.default.bold.hex("#06b6d4")("\n 🎨 AVAILABLE THEMES\n"));
2774
3148
  for (const { name, label } of allThemes()) {
@@ -2780,7 +3154,7 @@ themeCmd.command("list").description("List available themes").action(async () =>
2780
3154
  process.exit(0);
2781
3155
  });
2782
3156
  themeCmd.command("set <theme>").description("Set theme: dark | grey | white").action(async (name) => {
2783
- const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3157
+ const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2784
3158
  const valid = allThemes().map((t) => t.name);
2785
3159
  if (!valid.includes(name)) {
2786
3160
  console.log(chalk.default.red(`\n ✖ Unknown theme: "${name}". Use: ${valid.join(" | ")}\n`));
@@ -2792,7 +3166,7 @@ themeCmd.command("set <theme>").description("Set theme: dark | grey | white").ac
2792
3166
  process.exit(0);
2793
3167
  });
2794
3168
  themeCmd.command("preview").description("Preview all themes side-by-side").action(async () => {
2795
- const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3169
+ const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
2796
3170
  const current = getThemeName();
2797
3171
  console.log(chalk.default.bold("\n 🎨 THEME PREVIEW\n"));
2798
3172
  for (const { name, label } of allThemes()) {
@@ -2809,45 +3183,49 @@ themeCmd.command("preview").description("Preview all themes side-by-side").actio
2809
3183
  });
2810
3184
  const secretsCmd = program.command("secrets").description("External secrets management");
2811
3185
  secretsCmd.command("audit").description("Audit all required secrets").option("--required-by <ids>", "Filter by skill/provider IDs (comma-separated)").action(async (opts) => {
2812
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DOWs9lLX.js"));
3186
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2813
3187
  const filter = opts.requiredBy?.split(",");
2814
3188
  await new SecretsManager().audit(filter);
2815
3189
  process.exit(0);
2816
3190
  });
2817
3191
  secretsCmd.command("set <KEY=value>").description("Set a secret in .env file").action(async (kv) => {
2818
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DOWs9lLX.js"));
3192
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2819
3193
  await new SecretsManager().set(kv);
2820
3194
  process.exit(0);
2821
3195
  });
2822
3196
  secretsCmd.command("apply").description("Write secrets from .env to shell config (~/.bashrc, ~/.zshrc)").action(async () => {
2823
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DOWs9lLX.js"));
3197
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2824
3198
  await new SecretsManager().apply();
2825
3199
  process.exit(0);
2826
3200
  });
2827
3201
  secretsCmd.command("reload").description("Reload secrets into running gateway").action(async () => {
2828
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DOWs9lLX.js"));
3202
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2829
3203
  await new SecretsManager().reload();
2830
3204
  process.exit(0);
2831
3205
  });
2832
3206
  secretsCmd.command("remove <key>").description("Remove a secret from .env").action(async (key) => {
2833
- const { SecretsManager } = await Promise.resolve().then(() => require("./manager-DOWs9lLX.js"));
3207
+ const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
2834
3208
  await new SecretsManager().remove(key);
2835
3209
  process.exit(0);
2836
3210
  });
2837
3211
  secretsCmd.command("credentials").description("List provider credential files (credentials/*.json)").action(async () => {
2838
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-B3anVuZD.js"));
3212
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
2839
3213
  await new CredentialsStore().showList();
2840
3214
  process.exit(0);
2841
3215
  });
2842
3216
  const securityCmd = program.command("security").description("Security tools");
2843
- securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").action(async (opts) => {
2844
- const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-BTLzQBk-.js"));
2845
- await runSecurityAudit(opts.deep);
3217
+ securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").option("--fix", "Auto-fix safe findings (file permissions etc.)").option("--json", "Machine-readable JSON output").action(async (opts) => {
3218
+ const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-BaIiyWFu.js"));
3219
+ await runSecurityAudit({
3220
+ deep: opts.deep,
3221
+ fix: opts.fix,
3222
+ json: opts.json
3223
+ });
2846
3224
  process.exit(0);
2847
3225
  });
2848
3226
  const agentRunCmd = program.command("agent").description("Run agent with thinking control");
2849
3227
  agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent").option("--thinking <level>", "Thinking level: high|medium|low|none", "none").option("--model <model>", "Override model").option("--session <id>", "Session/thread ID").option("--multi-step", "Decompose into steps and run each (sequential)").option("--parallel", "Run sub-agents in parallel for independent subtasks").option("--verbose", "Show thinking blocks and request details").option("--workspace <dir>", "Override workspace directory").action(async (opts) => {
2850
- const { runAgent } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3228
+ const { runAgent } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2851
3229
  await runAgent({
2852
3230
  message: opts.message,
2853
3231
  thinking: opts.thinking,
@@ -2863,7 +3241,7 @@ agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent
2863
3241
  });
2864
3242
  const threadsCmd = program.command("threads").description("ACP thread-bound agent sessions");
2865
3243
  threadsCmd.command("list").description("List agent threads").option("--channel <id>", "Filter by channel").option("--active", "Show only active threads").action(async (opts) => {
2866
- const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3244
+ const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2867
3245
  const mgr = new ACPThreadManager();
2868
3246
  const threads = await mgr.list({
2869
3247
  channelId: opts.channel,
@@ -2873,33 +3251,33 @@ threadsCmd.command("list").description("List agent threads").option("--channel <
2873
3251
  process.exit(0);
2874
3252
  });
2875
3253
  threadsCmd.command("terminate <id>").description("Terminate a thread").action(async (id) => {
2876
- const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3254
+ const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
2877
3255
  await new ACPThreadManager().terminate(id);
2878
3256
  console.log(require("chalk").green(`\n ✔ Thread terminated: ${id}\n`));
2879
3257
  process.exit(0);
2880
3258
  });
2881
3259
  const canvasCmd = program.command("canvas").description("Live AI-driven UI canvas");
2882
3260
  canvasCmd.command("show").description("Show current canvas components").action(async () => {
2883
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-CN8ePGog.js"));
3261
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2884
3262
  await new CanvasRenderer().show();
2885
3263
  process.exit(0);
2886
3264
  });
2887
3265
  canvasCmd.command("add <type> <title>").description("Add a canvas component (type: chart|table|form|markdown|image|custom)").action(async (type, title) => {
2888
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-CN8ePGog.js"));
3266
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2889
3267
  await new CanvasRenderer().addComponent(type, title);
2890
3268
  process.exit(0);
2891
3269
  });
2892
3270
  canvasCmd.command("clear").description("Clear all canvas components").action(async () => {
2893
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-CN8ePGog.js"));
3271
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
2894
3272
  await new CanvasRenderer().clear();
2895
3273
  process.exit(0);
2896
3274
  });
2897
3275
  canvasCmd.command("export").description("Export canvas as HTML file").action(async () => {
2898
- const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-CN8ePGog.js"));
2899
- const fs$10 = require("fs-extra");
3276
+ const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
3277
+ const fs$7 = require("fs-extra");
2900
3278
  const html = await new CanvasRenderer().exportHtml();
2901
3279
  const outFile = require("path").join(require("os").homedir(), ".hyperclaw", "canvas", "export.html");
2902
- await fs$10.writeFile(outFile, html);
3280
+ await fs$7.writeFile(outFile, html);
2903
3281
  console.log(require("chalk").green(`\n ✔ Canvas exported to ${outFile}\n`));
2904
3282
  process.exit(0);
2905
3283
  });
@@ -2916,137 +3294,137 @@ deliveryCmd.command("retry <id>").description("Retry a dead-lettered delivery it
2916
3294
  });
2917
3295
  const mcpCmd = program.command("mcp").description("MCP (Model Context Protocol) server management");
2918
3296
  mcpCmd.command("list").action(async () => {
2919
- const { mcpList } = await Promise.resolve().then(() => require("./mcp-D0qVYL4A.js"));
3297
+ const { mcpList } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2920
3298
  await mcpList();
2921
3299
  process.exit(0);
2922
3300
  });
2923
3301
  mcpCmd.command("add").action(async () => {
2924
- const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-D0qVYL4A.js"));
3302
+ const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2925
3303
  await mcpAdd();
2926
3304
  process.exit(0);
2927
3305
  });
2928
3306
  mcpCmd.command("remove <id>").action(async (id) => {
2929
- const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-D0qVYL4A.js"));
3307
+ const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2930
3308
  await mcpRemove(id);
2931
3309
  process.exit(0);
2932
3310
  });
2933
3311
  mcpCmd.command("probe [id]").action(async (id) => {
2934
- const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-D0qVYL4A.js"));
3312
+ const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
2935
3313
  await mcpProbe(id);
2936
3314
  process.exit(0);
2937
3315
  });
2938
3316
  const nodeCmd = program.command("node").description("HyperClaw node management (local, remote, android)");
2939
3317
  nodeCmd.command("list").action(async () => {
2940
- const { nodeList } = await Promise.resolve().then(() => require("./node-CHBOeMvu.js"));
3318
+ const { nodeList } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2941
3319
  await nodeList();
2942
3320
  process.exit(0);
2943
3321
  });
2944
3322
  nodeCmd.command("add").action(async () => {
2945
- const { nodeAdd } = await Promise.resolve().then(() => require("./node-CHBOeMvu.js"));
3323
+ const { nodeAdd } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2946
3324
  await nodeAdd();
2947
3325
  process.exit(0);
2948
3326
  });
2949
3327
  nodeCmd.command("probe [id]").action(async (id) => {
2950
- const { nodeProbe } = await Promise.resolve().then(() => require("./node-CHBOeMvu.js"));
3328
+ const { nodeProbe } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2951
3329
  await nodeProbe(id);
2952
3330
  process.exit(0);
2953
3331
  });
2954
3332
  nodeCmd.command("remove <id>").action(async (id) => {
2955
- const { nodeRemove } = await Promise.resolve().then(() => require("./node-CHBOeMvu.js"));
3333
+ const { nodeRemove } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
2956
3334
  await nodeRemove(id);
2957
3335
  process.exit(0);
2958
3336
  });
2959
3337
  const arCmd = program.command("auto-reply").description("Auto-reply rule engine");
2960
3338
  arCmd.command("list").action(async () => {
2961
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-Dp4iYVQ4.js"));
3339
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2962
3340
  const e = new AutoReplyEngine();
2963
3341
  await e.load();
2964
3342
  e.showList();
2965
3343
  process.exit(0);
2966
3344
  });
2967
3345
  arCmd.command("toggle <id>").action(async (id) => {
2968
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-Dp4iYVQ4.js"));
3346
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2969
3347
  const e = new AutoReplyEngine();
2970
3348
  await e.toggle(id);
2971
3349
  process.exit(0);
2972
3350
  });
2973
3351
  arCmd.command("remove <id>").action(async (id) => {
2974
- const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-Dp4iYVQ4.js"));
3352
+ const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
2975
3353
  const e = new AutoReplyEngine();
2976
3354
  await e.remove(id);
2977
3355
  process.exit(0);
2978
3356
  });
2979
3357
  const gmailCmd = program.command("gmail").description("Gmail Pub/Sub real-time notifications");
2980
3358
  gmailCmd.command("watch-setup").description("Register Gmail watch for push notifications. Requires: hyperclaw auth oauth google-gmail").requiredOption("-t, --topic <name>", "Pub/Sub topic (e.g. projects/myproject/topics/gmail-push)").option("-l, --labels <ids>", "Label IDs to watch (comma-separated)", "INBOX").action(async (opts) => {
2981
- const chalk$13 = require("chalk");
3359
+ const chalk$11 = require("chalk");
2982
3360
  try {
2983
- const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-C6_zNpp1.js"));
3361
+ const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-Du7DVV7S.js"));
2984
3362
  const labelIds = opts.labels.split(",").map((s) => s.trim()).filter(Boolean);
2985
3363
  const result = await setupGmailWatch({
2986
3364
  topicName: opts.topic,
2987
3365
  labelIds
2988
3366
  });
2989
- console.log(chalk$13.hex("#06b6d4")("\n ✔ Gmail watch registered"));
2990
- console.log(chalk$13.gray(` historyId: ${result.historyId}`));
2991
- console.log(chalk$13.gray(` expiration: ${new Date(parseInt(result.expiration, 10)).toISOString()}`));
2992
- console.log(chalk$13.gray("\n Push endpoint: https://<your-server>/webhook/gmail-pubsub"));
2993
- console.log(chalk$13.gray(" Ensure email channel is enabled and gateway is publicly accessible.\n"));
3367
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ Gmail watch registered"));
3368
+ console.log(chalk$11.gray(` historyId: ${result.historyId}`));
3369
+ console.log(chalk$11.gray(` expiration: ${new Date(parseInt(result.expiration, 10)).toISOString()}`));
3370
+ console.log(chalk$11.gray("\n Push endpoint: https://<your-server>/webhook/gmail-pubsub"));
3371
+ console.log(chalk$11.gray(" Ensure email channel is enabled and gateway is publicly accessible.\n"));
2994
3372
  } catch (e) {
2995
- console.error(chalk$13.red("\n ✖ " + e.message + "\n"));
3373
+ console.error(chalk$11.red("\n ✖ " + e.message + "\n"));
2996
3374
  process.exit(1);
2997
3375
  }
2998
3376
  process.exit(0);
2999
3377
  });
3000
3378
  const cronCmd = program.command("cron").description("Scheduled tasks (cron → agent prompt)");
3001
3379
  cronCmd.command("list").action(async () => {
3002
- const chalk$13 = require("chalk");
3003
- const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bl9yuyCa.js"));
3380
+ const chalk$11 = require("chalk");
3381
+ const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
3004
3382
  const tasks = await loadCronTasks();
3005
- console.log(chalk$13.bold.cyan("\n ⏰ CRON TASKS\n"));
3383
+ console.log(chalk$11.bold.cyan("\n ⏰ CRON TASKS\n"));
3006
3384
  if (tasks.length === 0) {
3007
- console.log(chalk$13.gray(" No tasks. Add: hyperclaw cron add \"0 9 * * 1-5\" \"Check calendar\"\n"));
3385
+ console.log(chalk$11.gray(" No tasks. Add: hyperclaw cron add \"0 9 * * 1-5\" \"Check calendar\"\n"));
3008
3386
  process.exit(0);
3009
3387
  return;
3010
3388
  }
3011
3389
  for (const t of tasks) {
3012
- const dot = t.enabled ? chalk$13.green("●") : chalk$13.gray("○");
3013
- console.log(` ${dot} ${chalk$13.white(t.name || t.id)}`);
3014
- console.log(` ${chalk$13.gray("Schedule:")} ${t.schedule}`);
3015
- console.log(` ${chalk$13.gray("Prompt:")} ${t.prompt.slice(0, 60)}${t.prompt.length > 60 ? "..." : ""}`);
3016
- if (t.lastRunAt) console.log(` ${chalk$13.gray("Last run:")} ${t.lastRunAt}`);
3390
+ const dot = t.enabled ? chalk$11.green("●") : chalk$11.gray("○");
3391
+ console.log(` ${dot} ${chalk$11.white(t.name || t.id)}`);
3392
+ console.log(` ${chalk$11.gray("Schedule:")} ${t.schedule}`);
3393
+ console.log(` ${chalk$11.gray("Prompt:")} ${t.prompt.slice(0, 60)}${t.prompt.length > 60 ? "..." : ""}`);
3394
+ if (t.lastRunAt) console.log(` ${chalk$11.gray("Last run:")} ${t.lastRunAt}`);
3017
3395
  console.log();
3018
3396
  }
3019
3397
  process.exit(0);
3020
3398
  });
3021
3399
  cronCmd.command("add").arguments("<schedule> <prompt>").option("-n, --name <name>", "Task name").action(async (schedule, prompt, opts) => {
3022
- const chalk$13 = require("chalk");
3023
- const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bl9yuyCa.js"));
3400
+ const chalk$11 = require("chalk");
3401
+ const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
3024
3402
  await loadCronTasks();
3025
3403
  addCronTask(schedule, prompt, opts.name);
3026
3404
  await saveCronTasks();
3027
- console.log(chalk$13.green(`\n ✔ Cron task added: ${schedule} → "${prompt.slice(0, 40)}..."\n`));
3028
- console.log(chalk$13.gray(" Restart gateway to apply.\n"));
3405
+ console.log(chalk$11.green(`\n ✔ Cron task added: ${schedule} → "${prompt.slice(0, 40)}..."\n`));
3406
+ console.log(chalk$11.gray(" Restart gateway to apply.\n"));
3029
3407
  process.exit(0);
3030
3408
  });
3031
3409
  cronCmd.command("remove <id>").action(async (id) => {
3032
- const chalk$13 = require("chalk");
3033
- const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bl9yuyCa.js"));
3410
+ const chalk$11 = require("chalk");
3411
+ const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
3034
3412
  await loadCronTasks();
3035
3413
  if (removeCronTask(id)) {
3036
3414
  await saveCronTasks();
3037
- console.log(chalk$13.green(`\n ✔ Task removed\n`));
3038
- } else console.log(chalk$13.red(`\n ✖ Task not found: ${id}\n`));
3415
+ console.log(chalk$11.green(`\n ✔ Task removed\n`));
3416
+ } else console.log(chalk$11.red(`\n ✖ Task not found: ${id}\n`));
3039
3417
  process.exit(0);
3040
3418
  });
3041
3419
  program.command("nodes").description("List connected mobile nodes (iOS/Android Connect tab)").action(async () => {
3042
- const chalk$13 = require("chalk");
3420
+ const chalk$11 = require("chalk");
3043
3421
  const http$2 = await import("http");
3044
- const fs$10 = await import("fs-extra");
3045
- const path$10 = await import("path");
3046
- const os$9 = await import("os");
3422
+ const fs$7 = await import("fs-extra");
3423
+ const path$7 = await import("path");
3424
+ const os$8 = await import("os");
3047
3425
  let port = 18789;
3048
3426
  try {
3049
- const cfg = await fs$10.readJson(path$10.join(os$9.homedir(), ".hyperclaw", "hyperclaw.json"));
3427
+ const cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
3050
3428
  port = cfg?.gateway?.port ?? 18789;
3051
3429
  } catch {}
3052
3430
  return new Promise((resolve, reject) => {
@@ -3057,26 +3435,26 @@ program.command("nodes").description("List connected mobile nodes (iOS/Android C
3057
3435
  try {
3058
3436
  const j = JSON.parse(data);
3059
3437
  const nodes = j.nodes || [];
3060
- console.log(chalk$13.bold.cyan("\n 📱 CONNECTED NODES\n"));
3438
+ console.log(chalk$11.bold.cyan("\n 📱 CONNECTED NODES\n"));
3061
3439
  if (nodes.length === 0) {
3062
- console.log(chalk$13.gray(" No mobile nodes. Open iOS/Android app → Connect tab → pair with gateway."));
3063
- console.log(chalk$13.gray(` Gateway: ws://localhost:${port}\n`));
3440
+ console.log(chalk$11.gray(" No mobile nodes. Open iOS/Android app → Connect tab → pair with gateway."));
3441
+ console.log(chalk$11.gray(` Gateway: ws://localhost:${port}\n`));
3064
3442
  } else {
3065
3443
  for (const n of nodes) {
3066
- console.log(` ${chalk$13.green("●")} ${n.nodeId} ${chalk$13.gray(`(${n.platform || "?"})`)}`);
3067
- console.log(` ${chalk$13.gray("Device:")} ${n.deviceName || "—"}`);
3068
- console.log(` ${chalk$13.gray("Capabilities:")} ${Object.entries(n.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(", ") || "—"}`);
3444
+ console.log(` ${chalk$11.green("●")} ${n.nodeId} ${chalk$11.gray(`(${n.platform || "?"})`)}`);
3445
+ console.log(` ${chalk$11.gray("Device:")} ${n.deviceName || "—"}`);
3446
+ console.log(` ${chalk$11.gray("Capabilities:")} ${Object.entries(n.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(", ") || "—"}`);
3069
3447
  }
3070
3448
  console.log();
3071
3449
  }
3072
3450
  } catch {
3073
- console.log(chalk$13.red(" Could not reach gateway. Start with: hyperclaw daemon start\n"));
3451
+ console.log(chalk$11.red(" Could not reach gateway. Start with: hyperclaw daemon start\n"));
3074
3452
  }
3075
3453
  resolve();
3076
3454
  });
3077
3455
  });
3078
3456
  req.on("error", () => {
3079
- console.log(chalk$13.red(" Gateway offline. Start with: hyperclaw daemon start\n"));
3457
+ console.log(chalk$11.red(" Gateway offline. Start with: hyperclaw daemon start\n"));
3080
3458
  resolve();
3081
3459
  });
3082
3460
  req.setTimeout(3e3, () => {
@@ -3087,20 +3465,20 @@ program.command("nodes").description("List connected mobile nodes (iOS/Android C
3087
3465
  });
3088
3466
  const whCmd = program.command("webhooks").description("Webhook endpoint management");
3089
3467
  whCmd.command("list").action(async () => {
3090
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DyEeuP9g.js"));
3468
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
3091
3469
  const m = new WebhookManager();
3092
3470
  await m.load();
3093
3471
  m.showList();
3094
3472
  process.exit(0);
3095
3473
  });
3096
3474
  whCmd.command("remove <id>").action(async (id) => {
3097
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DyEeuP9g.js"));
3475
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
3098
3476
  const m = new WebhookManager();
3099
3477
  await m.remove(id);
3100
3478
  process.exit(0);
3101
3479
  });
3102
3480
  whCmd.command("toggle <id>").action(async (id) => {
3103
- const { WebhookManager } = await Promise.resolve().then(() => require("./manager-DyEeuP9g.js"));
3481
+ const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
3104
3482
  const m = new WebhookManager();
3105
3483
  await m.toggle(id);
3106
3484
  process.exit(0);
@@ -3109,7 +3487,7 @@ const logsCmd = program.command("logs").description("View gateway logs");
3109
3487
  logsCmd.option("-n, --lines <n>", "Number of lines to show", "50");
3110
3488
  logsCmd.option("-f, --follow", "Stream logs in real time");
3111
3489
  logsCmd.action(async (opts) => {
3112
- const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-CWyS7qAp.js"));
3490
+ const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-ybOp7VOC.js"));
3113
3491
  if (opts.follow) await streamLog();
3114
3492
  else {
3115
3493
  await tailLog(parseInt(opts.lines));
@@ -3117,7 +3495,7 @@ logsCmd.action(async (opts) => {
3117
3495
  }
3118
3496
  });
3119
3497
  program.command("gateway:serve").description("Start the gateway server in the foreground (used by daemon)").action(async () => {
3120
- const { startGateway } = await Promise.resolve().then(() => require("./server-RBqwE_GN.js"));
3498
+ const { startGateway } = await Promise.resolve().then(() => require("./server-D4wVHiX9.js"));
3121
3499
  await startGateway();
3122
3500
  process.on("SIGINT", () => process.exit(0));
3123
3501
  process.on("SIGTERM", () => process.exit(0));
@@ -3125,15 +3503,15 @@ program.command("gateway:serve").description("Start the gateway server in the fo
3125
3503
  });
3126
3504
  const gatewayCfgCmd = gatewayCmd.command("config").description("Configure gateway settings");
3127
3505
  gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--regenerate-token", "Generate a new random token").option("--set-port <port>", "Set gateway port").option("--set-bind <addr>", "Set gateway bind address").action(async (opts) => {
3128
- const chalk$13 = require("chalk");
3129
- const fs$10 = require("fs-extra");
3130
- const path$10 = require("path");
3131
- const os$9 = require("os");
3132
- const crypto = require("crypto");
3133
- const cfgFile = path$10.join(os$9.homedir(), ".hyperclaw", "hyperclaw.json");
3506
+ const chalk$11 = require("chalk");
3507
+ const fs$7 = require("fs-extra");
3508
+ const path$7 = require("path");
3509
+ const os$8 = require("os");
3510
+ const crypto$2 = require("crypto");
3511
+ const cfgFile = path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json");
3134
3512
  let cfg = {};
3135
3513
  try {
3136
- cfg = await fs$10.readJson(cfgFile);
3514
+ cfg = await fs$7.readJson(cfgFile);
3137
3515
  } catch {}
3138
3516
  if (!cfg.gateway) cfg.gateway = {
3139
3517
  port: 18789,
@@ -3144,53 +3522,53 @@ gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--
3144
3522
  hooks: true
3145
3523
  };
3146
3524
  if (opts.regenerateToken) {
3147
- cfg.gateway.authToken = crypto.randomBytes(32).toString("hex");
3148
- console.log(chalk$13.hex("#06b6d4")("\n ✔ New gateway token generated"));
3149
- console.log(chalk$13.gray(` Token: ${cfg.gateway.authToken}`));
3525
+ cfg.gateway.authToken = crypto$2.randomBytes(32).toString("hex");
3526
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ New gateway token generated"));
3527
+ console.log(chalk$11.gray(` Token: ${cfg.gateway.authToken}`));
3150
3528
  }
3151
3529
  if (opts.setToken) {
3152
3530
  cfg.gateway.authToken = opts.setToken;
3153
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Gateway token set`));
3531
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Gateway token set`));
3154
3532
  }
3155
3533
  if (opts.setPort) {
3156
3534
  cfg.gateway.port = parseInt(opts.setPort);
3157
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Port set to ${cfg.gateway.port}`));
3535
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Port set to ${cfg.gateway.port}`));
3158
3536
  }
3159
3537
  if (opts.setBind) {
3160
3538
  cfg.gateway.bind = opts.setBind;
3161
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Bind set to ${cfg.gateway.bind}`));
3539
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Bind set to ${cfg.gateway.bind}`));
3162
3540
  }
3163
- await fs$10.ensureDir(path$10.dirname(cfgFile));
3164
- await fs$10.writeJson(cfgFile, cfg, { spaces: 2 });
3165
- await fs$10.chmod(cfgFile, 384);
3166
- console.log(chalk$13.gray(` Saved to ${cfgFile}\n`));
3541
+ await fs$7.ensureDir(path$7.dirname(cfgFile));
3542
+ await fs$7.writeJson(cfgFile, cfg, { spaces: 2 });
3543
+ await fs$7.chmod(cfgFile, 384);
3544
+ console.log(chalk$11.gray(` Saved to ${cfgFile}\n`));
3167
3545
  process.exit(0);
3168
3546
  });
3169
3547
  const authCmd = program.command("auth").description("OAuth and provider credentials");
3170
3548
  authCmd.command("add <service_id>").description("Add API key for a service (any provider we do not ship). Stored in credentials/ and .env.").option("--key <api_key>", "API key (prompts if omitted)").option("--base-url <url>", "Base URL (optional, e.g. https://api.example.com)").option("--env-var <name>", "Env var name (default: <SERVICE_ID>_API_KEY)").action(async (serviceId, opts) => {
3171
- const chalk$13 = require("chalk");
3172
- const inquirer$3 = require("inquirer");
3173
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-B3anVuZD.js"));
3174
- const { getHyperClawDir: getHyperClawDir$1, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3175
- const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-Bzig1R5W.js"));
3176
- const fs$10 = await import("fs-extra");
3177
- const path$10 = await import("path");
3549
+ const chalk$11 = require("chalk");
3550
+ const inquirer$2 = require("inquirer");
3551
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
3552
+ const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3553
+ const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-BCcOl0Q7.js"));
3554
+ const fs$7 = await import("fs-extra");
3555
+ const path$7 = await import("path");
3178
3556
  const guide = getApiKeyGuide(serviceId);
3179
3557
  const steps = guide?.setupSteps ?? GENERIC_API_KEY_STEPS;
3180
- console.log(chalk$13.bold.hex("#06b6d4")(`\n 🔑 Add API key: ${guide?.name ?? serviceId}\n`));
3181
- console.log(chalk$13.bold(" Steps:\n"));
3182
- for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$13.hex("#06b6d4")(step));
3183
- else if (step.startsWith(" 💡")) console.log(chalk$13.gray(step));
3184
- else console.log(chalk$13.gray(` ${step}`));
3558
+ console.log(chalk$11.bold.hex("#06b6d4")(`\n 🔑 Add API key: ${guide?.name ?? serviceId}\n`));
3559
+ console.log(chalk$11.bold(" Steps:\n"));
3560
+ for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$11.hex("#06b6d4")(step));
3561
+ else if (step.startsWith(" 💡")) console.log(chalk$11.gray(step));
3562
+ else console.log(chalk$11.gray(` ${step}`));
3185
3563
  console.log();
3186
3564
  const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
3187
3565
  if (!safeId) {
3188
- console.log(chalk$13.red("\n ✖ Invalid service ID\n"));
3566
+ console.log(chalk$11.red("\n ✖ Invalid service ID\n"));
3189
3567
  process.exit(1);
3190
3568
  }
3191
3569
  let apiKey = opts.key || process.env[`${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`];
3192
3570
  if (!apiKey) {
3193
- const { key } = await inquirer$3.prompt([{
3571
+ const { key } = await inquirer$2.prompt([{
3194
3572
  type: "password",
3195
3573
  name: "key",
3196
3574
  message: `API key for ${serviceId}:`,
@@ -3199,7 +3577,7 @@ authCmd.command("add <service_id>").description("Add API key for a service (any
3199
3577
  }]);
3200
3578
  apiKey = key.trim();
3201
3579
  }
3202
- const creds = new CredentialsStore(getHyperClawDir$1());
3580
+ const creds = new CredentialsStore(getHyperClawDir());
3203
3581
  await creds.set(safeId, {
3204
3582
  apiKey,
3205
3583
  ...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
@@ -3207,52 +3585,52 @@ authCmd.command("add <service_id>").description("Add API key for a service (any
3207
3585
  });
3208
3586
  const envVar = opts.envVar || `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
3209
3587
  const envPath = getEnvFilePath();
3210
- await fs$10.ensureDir(path$10.dirname(envPath));
3588
+ await fs$7.ensureDir(path$7.dirname(envPath));
3211
3589
  let envContent = "";
3212
- if (await fs$10.pathExists(envPath)) envContent = await fs$10.readFile(envPath, "utf8");
3590
+ if (await fs$7.pathExists(envPath)) envContent = await fs$7.readFile(envPath, "utf8");
3213
3591
  const envLine = `${envVar}=${apiKey}`;
3214
3592
  const re = new RegExp(`^${envVar}=.*$`, "m");
3215
3593
  if (re.test(envContent)) envContent = envContent.replace(re, envLine);
3216
3594
  else envContent = envContent.trimEnd() + (envContent ? "\n" : "") + envLine + "\n";
3217
- await fs$10.writeFile(envPath, envContent, { mode: 384 });
3218
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Added: ${safeId}`));
3219
- console.log(chalk$13.gray(` Credentials: ~/.hyperclaw/credentials/${safeId}.json`));
3220
- console.log(chalk$13.gray(` Env: ${envVar} (in .env — use in skills via process.env.${envVar.replace(/-/g, "_")})`));
3221
- console.log(chalk$13.gray("\n Run: hyperclaw secrets apply to add to shell"));
3222
- console.log(chalk$13.gray(" Run: hyperclaw secrets reload to inject into running gateway\n"));
3595
+ await fs$7.writeFile(envPath, envContent, { mode: 384 });
3596
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Added: ${safeId}`));
3597
+ console.log(chalk$11.gray(` Credentials: ~/.hyperclaw/credentials/${safeId}.json`));
3598
+ console.log(chalk$11.gray(` Env: ${envVar} (in .env — use in skills via process.env.${envVar.replace(/-/g, "_")})`));
3599
+ console.log(chalk$11.gray("\n Run: hyperclaw secrets apply to add to shell"));
3600
+ console.log(chalk$11.gray(" Run: hyperclaw secrets reload to inject into running gateway\n"));
3223
3601
  process.exit(0);
3224
3602
  });
3225
3603
  authCmd.command("remove <service_id>").description("Remove API key for a service from credentials and .env").action(async (serviceId) => {
3226
- const chalk$13 = require("chalk");
3227
- const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-B3anVuZD.js"));
3228
- const { getHyperClawDir: getHyperClawDir$1, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3229
- const fs$10 = await import("fs-extra");
3604
+ const chalk$11 = require("chalk");
3605
+ const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
3606
+ const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
3607
+ const fs$7 = await import("fs-extra");
3230
3608
  const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
3231
- const creds = new CredentialsStore(getHyperClawDir$1());
3609
+ const creds = new CredentialsStore(getHyperClawDir());
3232
3610
  await creds.remove(safeId);
3233
3611
  const envVar = `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
3234
3612
  const envPath = getEnvFilePath();
3235
- if (await fs$10.pathExists(envPath)) {
3236
- let c = await fs$10.readFile(envPath, "utf8");
3613
+ if (await fs$7.pathExists(envPath)) {
3614
+ let c = await fs$7.readFile(envPath, "utf8");
3237
3615
  c = c.replace(new RegExp(`^${envVar}=.*\n?`, "gm"), "");
3238
- await fs$10.writeFile(envPath, c);
3616
+ await fs$7.writeFile(envPath, c);
3239
3617
  }
3240
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Removed: ${safeId}\n`));
3618
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Removed: ${safeId}\n`));
3241
3619
  process.exit(0);
3242
3620
  });
3243
3621
  authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers: google, google-gmail (Gmail Pub/Sub), microsoft").option("--client-id <id>", "OAuth client ID (or GOOGLE_OAUTH_CLIENT_ID)").option("--client-secret <secret>", "OAuth client secret (optional for PKCE)").action(async (provider, opts) => {
3244
- const chalk$13 = require("chalk");
3245
- const ora$6 = (await import("ora")).default;
3622
+ const chalk$11 = require("chalk");
3623
+ const ora$4 = (await import("ora")).default;
3246
3624
  try {
3247
- const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-HdvMxKmZ.js"));
3248
- const spinner = ora$6("Starting OAuth flow...").start();
3625
+ const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-DQPvMHRH.js"));
3626
+ const spinner = ora$4("Starting OAuth flow...").start();
3249
3627
  spinner.text = "Opening browser — complete the consent and return here.";
3250
3628
  const tokens = await runOAuthFlow(provider, {
3251
3629
  clientId: opts.clientId,
3252
3630
  clientSecret: opts.clientSecret
3253
3631
  });
3254
3632
  spinner.stop();
3255
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-DfEgSucI.js"));
3633
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
3256
3634
  const now = Math.floor(Date.now() / 1e3);
3257
3635
  const expires_at = tokens.expires_in ? now + tokens.expires_in : void 0;
3258
3636
  const tokenUrl = provider === "google" || provider === "google-gmail" ? "https://oauth2.googleapis.com/token" : provider === "microsoft" ? "https://login.microsoftonline.com/common/oauth2/v2.0/token" : void 0;
@@ -3262,46 +3640,46 @@ authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers:
3262
3640
  expires_at,
3263
3641
  token_url: tokenUrl
3264
3642
  });
3265
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ OAuth tokens saved for: ${provider}`));
3266
- console.log(chalk$13.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3643
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for: ${provider}`));
3644
+ console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3267
3645
  } catch (e) {
3268
- console.error(chalk$13.red("\n ✖ OAuth failed: " + e.message + "\n"));
3646
+ console.error(chalk$11.red("\n ✖ OAuth failed: " + e.message + "\n"));
3269
3647
  process.exit(1);
3270
3648
  }
3271
3649
  process.exit(0);
3272
3650
  });
3273
3651
  authCmd.command("setup-token <provider>").description("Save setup token (Anthropic Claude Pro/Max). Run: claude setup-token, paste result here.").action(async (provider) => {
3274
- const chalk$13 = require("chalk");
3275
- const inquirer$3 = await import("inquirer");
3652
+ const chalk$11 = require("chalk");
3653
+ const inquirer$2 = await import("inquirer");
3276
3654
  if (provider !== "anthropic") {
3277
- console.log(chalk$13.yellow(`\n Provider "${provider}" may not support setup-token. Use "hyperclaw auth add" for API keys.\n`));
3655
+ console.log(chalk$11.yellow(`\n Provider "${provider}" may not support setup-token. Use "hyperclaw auth add" for API keys.\n`));
3278
3656
  process.exit(1);
3279
3657
  }
3280
- const { token } = await inquirer$3.default.prompt([{
3658
+ const { token } = await inquirer$2.default.prompt([{
3281
3659
  type: "password",
3282
3660
  name: "token",
3283
3661
  message: "Paste setup token from `claude setup-token`:",
3284
3662
  mask: "●"
3285
3663
  }]);
3286
3664
  if (!token?.trim()) {
3287
- console.log(chalk$13.red("\n ✖ No token provided.\n"));
3665
+ console.log(chalk$11.red("\n ✖ No token provided.\n"));
3288
3666
  process.exit(1);
3289
3667
  }
3290
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-DfEgSucI.js"));
3668
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
3291
3669
  await writeOAuthToken("anthropic-setup", {
3292
3670
  access_token: token.trim(),
3293
3671
  token_url: "https://api.anthropic.com"
3294
3672
  });
3295
- console.log(chalk$13.hex("#06b6d4")("\n ✔ Anthropic setup token saved to ~/.hyperclaw/oauth-anthropic-setup.json"));
3296
- console.log(chalk$13.gray(" Use providerId: anthropic with authType: oauth and oauthTokenPath for Claude Pro/Max.\n"));
3673
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ Anthropic setup token saved to ~/.hyperclaw/oauth-anthropic-setup.json"));
3674
+ console.log(chalk$11.gray(" Use providerId: anthropic with authType: oauth and oauthTokenPath for Claude Pro/Max.\n"));
3297
3675
  process.exit(0);
3298
3676
  });
3299
3677
  authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually (access_token, refresh_token, etc.) to ~/.hyperclaw/oauth-<provider>.json").option("--token <access_token>", "Access token").option("--refresh <refresh_token>", "Refresh token (optional)").option("--expires-in <seconds>", "Token lifetime in seconds (optional)").option("--token-url <url>", "Refresh endpoint URL (optional)").action(async (provider, opts) => {
3300
- const chalk$13 = require("chalk");
3301
- const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-DfEgSucI.js"));
3678
+ const chalk$11 = require("chalk");
3679
+ const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
3302
3680
  const access_token = opts.token || process.env.OAUTH_ACCESS_TOKEN;
3303
3681
  if (!access_token) {
3304
- console.log(chalk$13.red("\n ✖ Provide --token <access_token> or set OAUTH_ACCESS_TOKEN\n"));
3682
+ console.log(chalk$11.red("\n ✖ Provide --token <access_token> or set OAUTH_ACCESS_TOKEN\n"));
3305
3683
  process.exit(1);
3306
3684
  }
3307
3685
  const expires_at = opts.expiresIn ? Math.floor(Date.now() / 1e3) + parseInt(opts.expiresIn, 10) : void 0;
@@ -3311,22 +3689,22 @@ authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually
3311
3689
  expires_at,
3312
3690
  token_url: opts.tokenUrl || void 0
3313
3691
  });
3314
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ OAuth tokens saved for provider: ${provider}`));
3315
- console.log(chalk$13.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3692
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for provider: ${provider}`));
3693
+ console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
3316
3694
  process.exit(0);
3317
3695
  });
3318
3696
  const workspaceCmd = program.command("workspace").description("Manage agent workspace files");
3319
3697
  workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL.md, USER.md, TOOLS.md, HEARTBEAT.md, BOOTSTRAP.md) in a directory").action(async (dir) => {
3320
- const chalk$13 = require("chalk");
3321
- const fs$10 = require("fs-extra");
3322
- const path$10 = require("path");
3323
- const os$9 = require("os");
3324
- const targetDir = dir || path$10.join(os$9.homedir(), ".hyperclaw");
3698
+ const chalk$11 = require("chalk");
3699
+ const fs$7 = require("fs-extra");
3700
+ const path$7 = require("path");
3701
+ const os$8 = require("os");
3702
+ const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
3325
3703
  let cfg = {};
3326
3704
  try {
3327
- cfg = await fs$10.readJson(path$10.join(os$9.homedir(), ".hyperclaw", "hyperclaw.json"));
3705
+ cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
3328
3706
  } catch {}
3329
- const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-DIZLpg-p.js"));
3707
+ const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-BlHL7JCO.js"));
3330
3708
  await initWorkspaceFiles({
3331
3709
  agentName: cfg.identity?.agentName || "Hyper",
3332
3710
  personality: cfg.identity?.personality || "helpful and concise",
@@ -3334,17 +3712,17 @@ workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL
3334
3712
  userName: cfg.identity?.userName || "User",
3335
3713
  rules: cfg.identity?.rules ?? []
3336
3714
  }, targetDir);
3337
- console.log(chalk$13.hex("#06b6d4")(`\n ✔ Workspace files initialized in ${targetDir}`));
3338
- console.log(chalk$13.gray(" Files: SOUL.md USER.md TOOLS.md HEARTBEAT.md BOOTSTRAP.md\n"));
3715
+ console.log(chalk$11.hex("#06b6d4")(`\n ✔ Workspace files initialized in ${targetDir}`));
3716
+ console.log(chalk$11.gray(" Files: SOUL.md USER.md TOOLS.md HEARTBEAT.md BOOTSTRAP.md\n"));
3339
3717
  process.exit(0);
3340
3718
  });
3341
3719
  workspaceCmd.command("show [dir]").description("Show workspace files summary").action(async (dir) => {
3342
- const chalk$13 = require("chalk");
3343
- const fs$10 = require("fs-extra");
3344
- const path$10 = require("path");
3345
- const os$9 = require("os");
3346
- const targetDir = dir || path$10.join(os$9.homedir(), ".hyperclaw");
3347
- console.log(chalk$13.bold.hex("#06b6d4")("\n 📁 WORKSPACE\n"));
3720
+ const chalk$11 = require("chalk");
3721
+ const fs$7 = require("fs-extra");
3722
+ const path$7 = require("path");
3723
+ const os$8 = require("os");
3724
+ const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
3725
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 📁 WORKSPACE\n"));
3348
3726
  for (const fname of [
3349
3727
  "SOUL.md",
3350
3728
  "USER.md",
@@ -3354,45 +3732,45 @@ workspaceCmd.command("show [dir]").description("Show workspace files summary").a
3354
3732
  "AGENTS.md",
3355
3733
  "MEMORY.md"
3356
3734
  ]) {
3357
- const fpath = path$10.join(targetDir, fname);
3358
- const exists = await fs$10.pathExists(fpath);
3359
- const size = exists ? (await fs$10.stat(fpath)).size : 0;
3360
- const dot = exists ? chalk$13.hex("#06b6d4")("✔") : chalk$13.gray("○");
3361
- console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$13.gray(`${size} bytes`) : chalk$13.gray("(missing)")}`);
3735
+ const fpath = path$7.join(targetDir, fname);
3736
+ const exists = await fs$7.pathExists(fpath);
3737
+ const size = exists ? (await fs$7.stat(fpath)).size : 0;
3738
+ const dot = exists ? chalk$11.hex("#06b6d4")("✔") : chalk$11.gray("○");
3739
+ console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$11.gray(`${size} bytes`) : chalk$11.gray("(missing)")}`);
3362
3740
  }
3363
3741
  console.log();
3364
3742
  process.exit(0);
3365
3743
  });
3366
3744
  const botCmd = program.command("bot").description("HyperClaw Bot — companion bot for remote gateway control");
3367
3745
  botCmd.command("status").action(async () => {
3368
- const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
3746
+ const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3369
3747
  await showBotStatus();
3370
3748
  process.exit(0);
3371
3749
  });
3372
3750
  botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, allowed users)").action(async () => {
3373
- const inquirer$3 = require("inquirer");
3374
- const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
3375
- const chalk$13 = require("chalk");
3376
- console.log(chalk$13.bold.hex("#06b6d4")("\n 🦅 HYPERCLAW BOT SETUP\n"));
3377
- console.log(chalk$13.gray(" Create a bot at t.me/BotFather, then paste the token below.\n"));
3378
- const { platform } = await inquirer$3.prompt([{
3751
+ const inquirer$2 = require("inquirer");
3752
+ const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3753
+ const chalk$11 = require("chalk");
3754
+ console.log(chalk$11.bold.hex("#06b6d4")("\n 🦅 HYPERCLAW BOT SETUP\n"));
3755
+ console.log(chalk$11.gray(" Create a bot at t.me/BotFather, then paste the token below.\n"));
3756
+ const { platform } = await inquirer$2.prompt([{
3379
3757
  type: "list",
3380
3758
  name: "platform",
3381
3759
  message: "Platform:",
3382
3760
  choices: ["telegram", "discord"]
3383
3761
  }]);
3384
- const { token } = await inquirer$3.prompt([{
3762
+ const { token } = await inquirer$2.prompt([{
3385
3763
  type: "input",
3386
3764
  name: "token",
3387
3765
  message: platform === "telegram" ? "Bot token (from @BotFather):" : "Discord bot token:",
3388
3766
  validate: (v) => v.trim().length > 10 || "Required"
3389
3767
  }]);
3390
- const { userIds } = await inquirer$3.prompt([{
3768
+ const { userIds } = await inquirer$2.prompt([{
3391
3769
  type: "input",
3392
3770
  name: "userIds",
3393
3771
  message: "Allowed user IDs (comma-separated, leave empty for unrestricted):"
3394
3772
  }]);
3395
- const { gatewayUrl } = await inquirer$3.prompt([{
3773
+ const { gatewayUrl } = await inquirer$2.prompt([{
3396
3774
  type: "input",
3397
3775
  name: "gatewayUrl",
3398
3776
  message: "Gateway URL:",
@@ -3408,20 +3786,20 @@ botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, al
3408
3786
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3409
3787
  };
3410
3788
  await saveBotConfig(cfg);
3411
- console.log(chalk$13.hex("#06b6d4")("\n ✔ HyperClaw Bot configured"));
3789
+ console.log(chalk$11.hex("#06b6d4")("\n ✔ HyperClaw Bot configured"));
3412
3790
  if (platform === "discord") try {
3413
3791
  require.resolve("discord.js");
3414
3792
  } catch {
3415
- console.log(chalk$13.yellow(" ⚠ For Discord: run: npm install discord.js"));
3793
+ console.log(chalk$11.yellow(" ⚠ For Discord: run: npm install discord.js"));
3416
3794
  }
3417
- console.log(chalk$13.gray(" Start with: hyperclaw bot start\n"));
3795
+ console.log(chalk$11.gray(" Start with: hyperclaw bot start\n"));
3418
3796
  process.exit(0);
3419
3797
  });
3420
3798
  botCmd.command("start").description("Start HyperClaw Bot (foreground or background)").option("--background", "Run bot in background (use hyperclaw bot stop to stop)").action(async (opts) => {
3421
3799
  const { spawn } = await import("child_process");
3422
- const path$10 = await import("path");
3800
+ const path$7 = await import("path");
3423
3801
  if (opts?.background) {
3424
- const entry = process.argv[1] || path$10.join(__dirname, "run-main.js");
3802
+ const entry = process.argv[1] || path$7.join(__dirname, "run-main.js");
3425
3803
  const child = spawn(process.execPath, [
3426
3804
  entry,
3427
3805
  "bot",
@@ -3433,14 +3811,14 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
3433
3811
  cwd: process.cwd()
3434
3812
  });
3435
3813
  child.unref();
3436
- const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
3814
+ const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3437
3815
  await writeBotPid(child.pid);
3438
3816
  console.log(require("chalk").green(`\n ✔ HyperClaw Bot started in background (PID ${child.pid})`));
3439
3817
  console.log(require("chalk").gray(" Stop with: hyperclaw bot stop\n"));
3440
3818
  process.exit(0);
3441
3819
  return;
3442
3820
  }
3443
- const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
3821
+ const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3444
3822
  const cfg = await loadBotConfig();
3445
3823
  if (!cfg) {
3446
3824
  console.log(require("chalk").red("\n ✖ HyperClaw Bot not configured. Run: hyperclaw bot setup\n"));
@@ -3466,42 +3844,42 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
3466
3844
  }
3467
3845
  });
3468
3846
  botCmd.command("stop").description("Stop HyperClaw Bot (when running in background)").action(async () => {
3469
- const chalk$13 = require("chalk");
3470
- const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-DfMGowZC.js"));
3847
+ const chalk$11 = require("chalk");
3848
+ const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
3471
3849
  const stopped = await stopBotProcess();
3472
- if (stopped) console.log(chalk$13.green("\n ✔ HyperClaw Bot stopped\n"));
3473
- else console.log(chalk$13.gray("\n Bot not running in background (no PID file). Use Ctrl+C to stop foreground bot.\n"));
3850
+ if (stopped) console.log(chalk$11.green("\n ✔ HyperClaw Bot stopped\n"));
3851
+ else console.log(chalk$11.gray("\n Bot not running in background (no PID file). Use Ctrl+C to stop foreground bot.\n"));
3474
3852
  process.exit(stopped ? 0 : 0);
3475
3853
  });
3476
3854
  memCmd.command("search <query>").description("Search MEMORY.md").action(async (query) => {
3477
- const { searchMemory } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3855
+ const { searchMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3478
3856
  await searchMemory(query);
3479
3857
  process.exit(0);
3480
3858
  });
3481
3859
  memCmd.command("auto-show").description("Show auto-extracted memories from MEMORY.md").action(async () => {
3482
- const { showMemory } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3860
+ const { showMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3483
3861
  await showMemory();
3484
3862
  process.exit(0);
3485
3863
  });
3486
3864
  memCmd.command("clear").description("Clear all auto-extracted memories").action(async () => {
3487
- const { clearMemory } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3865
+ const { clearMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3488
3866
  await clearMemory();
3489
3867
  process.exit(0);
3490
3868
  });
3491
3869
  memCmd.command("save <text>").description("Manually save a fact to MEMORY.md").action(async (text) => {
3492
- const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3870
+ const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3493
3871
  await saveMemoryDirect(text);
3494
3872
  console.log(chalk.default.hex("#06b6d4")(` ✅ Saved: ${text}\n`));
3495
3873
  process.exit(0);
3496
3874
  });
3497
3875
  const pcCmd = program.command("pc").description("PC access — give the AI access to your computer");
3498
3876
  pcCmd.command("status").description("Show PC access status and config").action(async () => {
3499
- const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3877
+ const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3500
3878
  await showPCAccessStatus();
3501
3879
  process.exit(0);
3502
3880
  });
3503
3881
  pcCmd.command("enable").description("Enable PC access for the AI").option("--level <level>", "Access level: read-only | sandboxed | full", "full").option("--paths <paths>", "Comma-separated allowed paths (sandboxed mode)").action(async (opts) => {
3504
- const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3882
+ const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3505
3883
  const level = opts.level;
3506
3884
  const allowed = [
3507
3885
  "read-only",
@@ -3528,7 +3906,7 @@ pcCmd.command("enable").description("Enable PC access for the AI").option("--lev
3528
3906
  process.exit(0);
3529
3907
  });
3530
3908
  pcCmd.command("disable").description("Disable PC access").action(async () => {
3531
- const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3909
+ const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3532
3910
  await savePCAccessConfig({ enabled: false });
3533
3911
  console.log(chalk.default.hex("#06b6d4")("\n ✅ PC access disabled\n"));
3534
3912
  process.exit(0);
@@ -3552,7 +3930,7 @@ pcCmd.command("log").description("Show PC access audit log").option("-n, --lines
3552
3930
  process.exit(0);
3553
3931
  });
3554
3932
  pcCmd.command("run <command>").description("Run a shell command via PC access (must be enabled)").action(async (command) => {
3555
- const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-BptR-54a.js"));
3933
+ const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
3556
3934
  const cfg = await loadPCAccessConfig();
3557
3935
  if (!cfg.enabled) {
3558
3936
  console.log(chalk.default.red("\n ✖ PC access disabled. Run: hyperclaw pc enable\n"));
@@ -3565,14 +3943,58 @@ pcCmd.command("run <command>").description("Run a shell command via PC access (m
3565
3943
  console.log(result);
3566
3944
  process.exit(0);
3567
3945
  });
3946
+ function checkForUpdate() {
3947
+ const { execFile: execFile$1 } = require("child_process");
3948
+ const { readFileSync } = require("fs");
3949
+ const path$7 = require("path");
3950
+ try {
3951
+ const pkgPath = path$7.resolve(__dirname, "../../package.json");
3952
+ const current = JSON.parse(readFileSync(pkgPath, "utf8")).version;
3953
+ execFile$1("npm", [
3954
+ "view",
3955
+ "hyperclaw",
3956
+ "version",
3957
+ "--json"
3958
+ ], { timeout: 5e3 }, (_err, stdout) => {
3959
+ if (_err || !stdout) return;
3960
+ try {
3961
+ const latest = JSON.parse(stdout.trim());
3962
+ if (latest && latest !== current) {
3963
+ const semver = (v) => v.split(".").map(Number);
3964
+ const [lMaj, lMin, lPat] = semver(latest);
3965
+ const [cMaj, cMin, cPat] = semver(current);
3966
+ const isNewer = lMaj > cMaj || lMaj === cMaj && lMin > cMin || lMaj === cMaj && lMin === cMin && lPat > cPat;
3967
+ if (isNewer) process.stdout.write(chalk.default.yellow(`\n ⬆ Update available: ${chalk.default.dim(current)} → ${chalk.default.green(latest)}\n`) + chalk.default.gray(` npm install -g hyperclaw@latest\n\n`));
3968
+ }
3969
+ } catch {}
3970
+ });
3971
+ } catch {}
3972
+ }
3973
+ program.command("setup").description("Setup wizard — alias for `hyperclaw onboard`").option("--install-daemon", "Auto-install system daemon").option("--reset", "Reset config before running wizard").option("--non-interactive", "Non-interactive mode").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all", "loopback").action(async (opts) => {
3974
+ await new require_onboard.Banner().showNeonBanner(false);
3975
+ const wizardOpts = {
3976
+ wizard: true,
3977
+ installDaemon: opts.installDaemon ?? false,
3978
+ reset: opts.reset ?? false,
3979
+ nonInteractive: opts.nonInteractive ?? false,
3980
+ jsonOutput: opts.json ?? false,
3981
+ gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
3982
+ gatewayBind: opts.gatewayBind ?? "loopback",
3983
+ anthropicApiKey: opts.anthropicApiKey,
3984
+ openaiApiKey: opts.openaiApiKey
3985
+ };
3986
+ await new require_onboard.HyperClawWizard().run(wizardOpts);
3987
+ process.exit(0);
3988
+ });
3989
+ checkForUpdate();
3568
3990
  if (process.argv.length === 2) (async () => {
3569
- const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-BX2lIsmk.js"));
3991
+ const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-CrVDn6eN.js"));
3570
3992
  const cfg = await new ConfigManager$1().load().catch(() => null);
3571
3993
  if (cfg?.provider?.apiKey || cfg?.provider?.providerId) {
3572
3994
  await new require_onboard.Banner().showNeonBanner(false);
3573
- const { getTheme } = await Promise.resolve().then(() => require("./theme-Iefa3L63.js"));
3995
+ const { getTheme } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
3574
3996
  const t = getTheme(false);
3575
- const chalk$13 = require("chalk");
3997
+ const chalk$11 = require("chalk");
3576
3998
  console.log(t.bold(" Quick actions:\n"));
3577
3999
  console.log(` ${t.c("hyperclaw onboard")} — re-run setup wizard`);
3578
4000
  console.log(` ${t.c("hyperclaw onboard --install-daemon")} — wizard + daemon (full PC access)`);
@@ -3584,7 +4006,7 @@ if (process.argv.length === 2) (async () => {
3584
4006
  console.log(` ${t.c("hyperclaw --help")} — all commands\n`);
3585
4007
  } else {
3586
4008
  await new require_onboard.Banner().showNeonBanner(false);
3587
- const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-3q20ZyHj.js"));
4009
+ const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-0WoDxbv_.js"));
3588
4010
  await new HyperClawWizard$1().run({ wizard: true });
3589
4011
  }
3590
4012
  process.exit(0);