agent-messenger 2.3.0 → 2.5.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 (438) hide show
  1. package/.claude-plugin/README.md +16 -16
  2. package/.claude-plugin/marketplace.json +29 -29
  3. package/.claude-plugin/plugin.json +5 -5
  4. package/.github/workflows/release.yml +0 -12
  5. package/CONTRIBUTING.md +1 -1
  6. package/README.md +11 -8
  7. package/bun.lock +70 -110
  8. package/bunfig.toml +3 -0
  9. package/dist/package.json +11 -3
  10. package/dist/src/platforms/channeltalk/cli.d.ts.map +1 -1
  11. package/dist/src/platforms/channeltalk/cli.js +2 -1
  12. package/dist/src/platforms/channeltalk/cli.js.map +1 -1
  13. package/dist/src/platforms/channeltalk/commands/index.d.ts +1 -0
  14. package/dist/src/platforms/channeltalk/commands/index.d.ts.map +1 -1
  15. package/dist/src/platforms/channeltalk/commands/index.js +1 -0
  16. package/dist/src/platforms/channeltalk/commands/index.js.map +1 -1
  17. package/dist/src/platforms/channeltalk/commands/whoami.d.ts +22 -0
  18. package/dist/src/platforms/channeltalk/commands/whoami.d.ts.map +1 -0
  19. package/dist/src/platforms/channeltalk/commands/whoami.js +40 -0
  20. package/dist/src/platforms/channeltalk/commands/whoami.js.map +1 -0
  21. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  22. package/dist/src/platforms/channeltalkbot/cli.js +2 -1
  23. package/dist/src/platforms/channeltalkbot/cli.js.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/commands/index.d.ts +1 -0
  25. package/dist/src/platforms/channeltalkbot/commands/index.d.ts.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/commands/index.js +1 -0
  27. package/dist/src/platforms/channeltalkbot/commands/index.js.map +1 -1
  28. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts +13 -0
  29. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts.map +1 -0
  30. package/dist/src/platforms/channeltalkbot/commands/whoami.js +31 -0
  31. package/dist/src/platforms/channeltalkbot/commands/whoami.js.map +1 -0
  32. package/dist/src/platforms/discord/cli.d.ts.map +1 -1
  33. package/dist/src/platforms/discord/cli.js +2 -1
  34. package/dist/src/platforms/discord/cli.js.map +1 -1
  35. package/dist/src/platforms/discord/commands/index.d.ts +1 -0
  36. package/dist/src/platforms/discord/commands/index.d.ts.map +1 -1
  37. package/dist/src/platforms/discord/commands/index.js +1 -0
  38. package/dist/src/platforms/discord/commands/index.js.map +1 -1
  39. package/dist/src/platforms/discord/commands/whoami.d.ts +6 -0
  40. package/dist/src/platforms/discord/commands/whoami.d.ts.map +1 -0
  41. package/dist/src/platforms/discord/commands/whoami.js +33 -0
  42. package/dist/src/platforms/discord/commands/whoami.js.map +1 -0
  43. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  44. package/dist/src/platforms/discordbot/cli.js +2 -1
  45. package/dist/src/platforms/discordbot/cli.js.map +1 -1
  46. package/dist/src/platforms/discordbot/client.js +2 -2
  47. package/dist/src/platforms/discordbot/client.js.map +1 -1
  48. package/dist/src/platforms/discordbot/commands/index.d.ts +1 -0
  49. package/dist/src/platforms/discordbot/commands/index.d.ts.map +1 -1
  50. package/dist/src/platforms/discordbot/commands/index.js +1 -0
  51. package/dist/src/platforms/discordbot/commands/index.js.map +1 -1
  52. package/dist/src/platforms/discordbot/commands/whoami.d.ts +14 -0
  53. package/dist/src/platforms/discordbot/commands/whoami.d.ts.map +1 -0
  54. package/dist/src/platforms/discordbot/commands/whoami.js +32 -0
  55. package/dist/src/platforms/discordbot/commands/whoami.js.map +1 -0
  56. package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
  57. package/dist/src/platforms/instagram/cli.js +2 -1
  58. package/dist/src/platforms/instagram/cli.js.map +1 -1
  59. package/dist/src/platforms/instagram/client.d.ts +6 -0
  60. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  61. package/dist/src/platforms/instagram/client.js +12 -0
  62. package/dist/src/platforms/instagram/client.js.map +1 -1
  63. package/dist/src/platforms/instagram/commands/index.d.ts +1 -0
  64. package/dist/src/platforms/instagram/commands/index.d.ts.map +1 -1
  65. package/dist/src/platforms/instagram/commands/index.js +1 -0
  66. package/dist/src/platforms/instagram/commands/index.js.map +1 -1
  67. package/dist/src/platforms/instagram/commands/whoami.d.ts +7 -0
  68. package/dist/src/platforms/instagram/commands/whoami.d.ts.map +1 -0
  69. package/dist/src/platforms/instagram/commands/whoami.js +19 -0
  70. package/dist/src/platforms/instagram/commands/whoami.js.map +1 -0
  71. package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
  72. package/dist/src/platforms/kakaotalk/cli.js +2 -1
  73. package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
  74. package/dist/src/platforms/kakaotalk/client.d.ts +2 -1
  75. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  76. package/dist/src/platforms/kakaotalk/client.js +225 -23
  77. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  78. package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
  79. package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
  80. package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
  81. package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
  82. package/dist/src/platforms/kakaotalk/commands/whoami.d.ts +3 -0
  83. package/dist/src/platforms/kakaotalk/commands/whoami.d.ts.map +1 -0
  84. package/dist/src/platforms/kakaotalk/commands/whoami.js +19 -0
  85. package/dist/src/platforms/kakaotalk/commands/whoami.js.map +1 -0
  86. package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
  87. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  88. package/dist/src/platforms/kakaotalk/index.js +1 -1
  89. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  90. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +4 -2
  91. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  92. package/dist/src/platforms/kakaotalk/protocol/session.js +27 -7
  93. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  94. package/dist/src/platforms/kakaotalk/protocol/types.d.ts +17 -0
  95. package/dist/src/platforms/kakaotalk/protocol/types.d.ts.map +1 -1
  96. package/dist/src/platforms/kakaotalk/protocol/types.js.map +1 -1
  97. package/dist/src/platforms/kakaotalk/types.d.ts +28 -0
  98. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  99. package/dist/src/platforms/kakaotalk/types.js +14 -0
  100. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  101. package/dist/src/platforms/line/cli.js +2 -2
  102. package/dist/src/platforms/line/cli.js.map +1 -1
  103. package/dist/src/platforms/line/client.d.ts.map +1 -1
  104. package/dist/src/platforms/line/client.js +9 -36
  105. package/dist/src/platforms/line/client.js.map +1 -1
  106. package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
  107. package/dist/src/platforms/line/commands/auth.js +9 -47
  108. package/dist/src/platforms/line/commands/auth.js.map +1 -1
  109. package/dist/src/platforms/line/commands/index.d.ts +1 -1
  110. package/dist/src/platforms/line/commands/index.d.ts.map +1 -1
  111. package/dist/src/platforms/line/commands/index.js +1 -1
  112. package/dist/src/platforms/line/commands/index.js.map +1 -1
  113. package/dist/src/platforms/line/commands/whoami.d.ts +3 -0
  114. package/dist/src/platforms/line/commands/whoami.d.ts.map +1 -0
  115. package/dist/src/platforms/line/commands/{profile.js → whoami.js} +5 -5
  116. package/dist/src/platforms/line/commands/whoami.js.map +1 -0
  117. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  118. package/dist/src/platforms/slack/cli.js +2 -1
  119. package/dist/src/platforms/slack/cli.js.map +1 -1
  120. package/dist/src/platforms/slack/commands/index.d.ts +1 -0
  121. package/dist/src/platforms/slack/commands/index.d.ts.map +1 -1
  122. package/dist/src/platforms/slack/commands/index.js +1 -0
  123. package/dist/src/platforms/slack/commands/index.js.map +1 -1
  124. package/dist/src/platforms/slack/commands/whoami.d.ts +6 -0
  125. package/dist/src/platforms/slack/commands/whoami.d.ts.map +1 -0
  126. package/dist/src/platforms/slack/commands/whoami.js +39 -0
  127. package/dist/src/platforms/slack/commands/whoami.js.map +1 -0
  128. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  129. package/dist/src/platforms/slackbot/cli.js +2 -1
  130. package/dist/src/platforms/slackbot/cli.js.map +1 -1
  131. package/dist/src/platforms/slackbot/commands/index.d.ts +1 -0
  132. package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -1
  133. package/dist/src/platforms/slackbot/commands/index.js +1 -0
  134. package/dist/src/platforms/slackbot/commands/index.js.map +1 -1
  135. package/dist/src/platforms/slackbot/commands/whoami.d.ts +14 -0
  136. package/dist/src/platforms/slackbot/commands/whoami.d.ts.map +1 -0
  137. package/dist/src/platforms/slackbot/commands/whoami.js +32 -0
  138. package/dist/src/platforms/slackbot/commands/whoami.js.map +1 -0
  139. package/dist/src/platforms/teams/cli.d.ts.map +1 -1
  140. package/dist/src/platforms/teams/cli.js +2 -1
  141. package/dist/src/platforms/teams/cli.js.map +1 -1
  142. package/dist/src/platforms/teams/commands/index.d.ts +1 -0
  143. package/dist/src/platforms/teams/commands/index.d.ts.map +1 -1
  144. package/dist/src/platforms/teams/commands/index.js +1 -0
  145. package/dist/src/platforms/teams/commands/index.js.map +1 -1
  146. package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
  147. package/dist/src/platforms/teams/commands/reaction.js +2 -0
  148. package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
  149. package/dist/src/platforms/teams/commands/whoami.d.ts +6 -0
  150. package/dist/src/platforms/teams/commands/whoami.d.ts.map +1 -0
  151. package/dist/src/platforms/teams/commands/whoami.js +30 -0
  152. package/dist/src/platforms/teams/commands/whoami.js.map +1 -0
  153. package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
  154. package/dist/src/platforms/telegram/cli.js +2 -1
  155. package/dist/src/platforms/telegram/cli.js.map +1 -1
  156. package/dist/src/platforms/telegram/commands/index.d.ts +1 -0
  157. package/dist/src/platforms/telegram/commands/index.d.ts.map +1 -1
  158. package/dist/src/platforms/telegram/commands/index.js +1 -0
  159. package/dist/src/platforms/telegram/commands/index.js.map +1 -1
  160. package/dist/src/platforms/telegram/commands/whoami.d.ts +7 -0
  161. package/dist/src/platforms/telegram/commands/whoami.d.ts.map +1 -0
  162. package/dist/src/platforms/telegram/commands/whoami.js +27 -0
  163. package/dist/src/platforms/telegram/commands/whoami.js.map +1 -0
  164. package/dist/src/platforms/webex/cli.d.ts.map +1 -1
  165. package/dist/src/platforms/webex/cli.js +2 -1
  166. package/dist/src/platforms/webex/cli.js.map +1 -1
  167. package/dist/src/platforms/webex/commands/index.d.ts +1 -0
  168. package/dist/src/platforms/webex/commands/index.d.ts.map +1 -1
  169. package/dist/src/platforms/webex/commands/index.js +1 -0
  170. package/dist/src/platforms/webex/commands/index.js.map +1 -1
  171. package/dist/src/platforms/webex/commands/message.js +1 -1
  172. package/dist/src/platforms/webex/commands/message.js.map +1 -1
  173. package/dist/src/platforms/webex/commands/whoami.d.ts +6 -0
  174. package/dist/src/platforms/webex/commands/whoami.d.ts.map +1 -0
  175. package/dist/src/platforms/webex/commands/whoami.js +30 -0
  176. package/dist/src/platforms/webex/commands/whoami.js.map +1 -0
  177. package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
  178. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
  179. package/dist/src/platforms/wechatbot/cli.js +19 -0
  180. package/dist/src/platforms/wechatbot/cli.js.map +1 -0
  181. package/dist/src/platforms/wechatbot/client.d.ts +36 -0
  182. package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
  183. package/dist/src/platforms/wechatbot/client.js +208 -0
  184. package/dist/src/platforms/wechatbot/client.js.map +1 -0
  185. package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
  186. package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
  187. package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
  188. package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
  189. package/dist/src/platforms/wechatbot/commands/index.d.ts +6 -0
  190. package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
  191. package/dist/src/platforms/wechatbot/commands/index.js +6 -0
  192. package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
  193. package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
  194. package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
  195. package/dist/src/platforms/wechatbot/commands/message.js +80 -0
  196. package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
  197. package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
  198. package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
  199. package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
  200. package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
  201. package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
  202. package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
  203. package/dist/src/platforms/wechatbot/commands/template.js +76 -0
  204. package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
  205. package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
  206. package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
  207. package/dist/src/platforms/wechatbot/commands/user.js +53 -0
  208. package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
  209. package/dist/src/platforms/wechatbot/commands/whoami.d.ts +12 -0
  210. package/dist/src/platforms/wechatbot/commands/whoami.d.ts.map +1 -0
  211. package/dist/src/platforms/wechatbot/commands/whoami.js +33 -0
  212. package/dist/src/platforms/wechatbot/commands/whoami.js.map +1 -0
  213. package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
  214. package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
  215. package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
  216. package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
  217. package/dist/src/platforms/wechatbot/index.d.ts +5 -0
  218. package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
  219. package/dist/src/platforms/wechatbot/index.js +4 -0
  220. package/dist/src/platforms/wechatbot/index.js.map +1 -0
  221. package/dist/src/platforms/wechatbot/types.d.ts +94 -0
  222. package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
  223. package/dist/src/platforms/wechatbot/types.js +54 -0
  224. package/dist/src/platforms/wechatbot/types.js.map +1 -0
  225. package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
  226. package/dist/src/platforms/whatsapp/cli.js +2 -1
  227. package/dist/src/platforms/whatsapp/cli.js.map +1 -1
  228. package/dist/src/platforms/whatsapp/client.d.ts +9 -0
  229. package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
  230. package/dist/src/platforms/whatsapp/client.js +143 -21
  231. package/dist/src/platforms/whatsapp/client.js.map +1 -1
  232. package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
  233. package/dist/src/platforms/whatsapp/commands/auth.js +133 -60
  234. package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
  235. package/dist/src/platforms/whatsapp/commands/index.d.ts +1 -0
  236. package/dist/src/platforms/whatsapp/commands/index.d.ts.map +1 -1
  237. package/dist/src/platforms/whatsapp/commands/index.js +1 -0
  238. package/dist/src/platforms/whatsapp/commands/index.js.map +1 -1
  239. package/dist/src/platforms/whatsapp/commands/shared.js +2 -2
  240. package/dist/src/platforms/whatsapp/commands/shared.js.map +1 -1
  241. package/dist/src/platforms/whatsapp/commands/whoami.d.ts +7 -0
  242. package/dist/src/platforms/whatsapp/commands/whoami.d.ts.map +1 -0
  243. package/dist/src/platforms/whatsapp/commands/whoami.js +19 -0
  244. package/dist/src/platforms/whatsapp/commands/whoami.js.map +1 -0
  245. package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
  246. package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
  247. package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
  248. package/dist/src/platforms/whatsapp/ensure-auth.js +2 -2
  249. package/dist/src/platforms/whatsapp/ensure-auth.js.map +1 -1
  250. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  251. package/dist/src/platforms/whatsappbot/cli.js +2 -1
  252. package/dist/src/platforms/whatsappbot/cli.js.map +1 -1
  253. package/dist/src/platforms/whatsappbot/commands/index.d.ts +1 -0
  254. package/dist/src/platforms/whatsappbot/commands/index.d.ts.map +1 -1
  255. package/dist/src/platforms/whatsappbot/commands/index.js +1 -0
  256. package/dist/src/platforms/whatsappbot/commands/index.js.map +1 -1
  257. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts +17 -0
  258. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts.map +1 -0
  259. package/dist/src/platforms/whatsappbot/commands/whoami.js +39 -0
  260. package/dist/src/platforms/whatsappbot/commands/whoami.js.map +1 -0
  261. package/dist/src/shared/utils/qr.d.ts +15 -0
  262. package/dist/src/shared/utils/qr.d.ts.map +1 -0
  263. package/dist/src/shared/utils/qr.js +74 -0
  264. package/dist/src/shared/utils/qr.js.map +1 -0
  265. package/dist/src/tui/adapters/whatsapp-adapter.d.ts.map +1 -1
  266. package/dist/src/tui/adapters/whatsapp-adapter.js +20 -15
  267. package/dist/src/tui/adapters/whatsapp-adapter.js.map +1 -1
  268. package/docs/content/docs/agent-skills.mdx +4 -4
  269. package/docs/content/docs/cli/channeltalk.mdx +12 -1
  270. package/docs/content/docs/cli/channeltalkbot.mdx +10 -1
  271. package/docs/content/docs/cli/discord.mdx +11 -1
  272. package/docs/content/docs/cli/discordbot.mdx +10 -1
  273. package/docs/content/docs/cli/instagram.mdx +12 -1
  274. package/docs/content/docs/cli/kakaotalk.mdx +25 -1
  275. package/docs/content/docs/cli/line.mdx +5 -5
  276. package/docs/content/docs/cli/meta.json +1 -0
  277. package/docs/content/docs/cli/slack.mdx +11 -1
  278. package/docs/content/docs/cli/slackbot.mdx +10 -1
  279. package/docs/content/docs/cli/teams.mdx +11 -1
  280. package/docs/content/docs/cli/telegram.mdx +11 -0
  281. package/docs/content/docs/cli/webex.mdx +11 -1
  282. package/docs/content/docs/cli/wechatbot.mdx +188 -0
  283. package/docs/content/docs/cli/whatsapp.mdx +37 -8
  284. package/docs/content/docs/cli/whatsappbot.mdx +10 -1
  285. package/docs/content/docs/sdk/meta.json +1 -1
  286. package/docs/content/docs/sdk/wechatbot.mdx +282 -0
  287. package/docs/content/docs/tui.mdx +1 -1
  288. package/docs/src/app/page.tsx +5 -5
  289. package/package.json +11 -3
  290. package/skills/agent-channeltalk/SKILL.md +12 -1
  291. package/skills/agent-channeltalkbot/SKILL.md +10 -1
  292. package/skills/agent-discord/SKILL.md +11 -1
  293. package/skills/agent-discordbot/SKILL.md +10 -1
  294. package/skills/agent-instagram/SKILL.md +12 -1
  295. package/skills/agent-kakaotalk/SKILL.md +30 -1
  296. package/skills/agent-kakaotalk/references/common-patterns.md +1 -1
  297. package/skills/agent-line/SKILL.md +11 -15
  298. package/skills/agent-line/references/authentication.md +13 -4
  299. package/skills/agent-slack/SKILL.md +11 -1
  300. package/skills/agent-slackbot/SKILL.md +10 -1
  301. package/skills/agent-teams/SKILL.md +11 -1
  302. package/skills/agent-telegram/SKILL.md +6 -1
  303. package/skills/agent-webex/SKILL.md +11 -1
  304. package/skills/agent-wechatbot/SKILL.md +394 -0
  305. package/skills/agent-whatsapp/SKILL.md +63 -15
  306. package/skills/agent-whatsapp/references/authentication.md +36 -6
  307. package/skills/agent-whatsappbot/SKILL.md +10 -1
  308. package/src/platforms/channeltalk/cli.ts +2 -0
  309. package/src/platforms/channeltalk/commands/index.ts +1 -0
  310. package/src/platforms/channeltalk/commands/whoami.test.ts +64 -0
  311. package/src/platforms/channeltalk/commands/whoami.ts +62 -0
  312. package/src/platforms/channeltalkbot/cli.ts +2 -0
  313. package/src/platforms/channeltalkbot/commands/index.ts +1 -0
  314. package/src/platforms/channeltalkbot/commands/whoami.test.ts +104 -0
  315. package/src/platforms/channeltalkbot/commands/whoami.ts +42 -0
  316. package/src/platforms/discord/cli.ts +2 -0
  317. package/src/platforms/discord/commands/index.ts +1 -0
  318. package/src/platforms/discord/commands/whoami.test.ts +91 -0
  319. package/src/platforms/discord/commands/whoami.ts +36 -0
  320. package/src/platforms/discord/credential-manager.test.ts +18 -1
  321. package/src/platforms/discordbot/cli.ts +2 -0
  322. package/src/platforms/discordbot/client.ts +2 -2
  323. package/src/platforms/discordbot/commands/index.ts +1 -0
  324. package/src/platforms/discordbot/commands/whoami.test.ts +96 -0
  325. package/src/platforms/discordbot/commands/whoami.ts +44 -0
  326. package/src/platforms/instagram/cli.ts +2 -1
  327. package/src/platforms/instagram/client.ts +13 -0
  328. package/src/platforms/instagram/commands/auth.test.ts +216 -0
  329. package/src/platforms/instagram/commands/chat.test.ts +123 -0
  330. package/src/platforms/instagram/commands/index.ts +1 -0
  331. package/src/platforms/instagram/commands/message.test.ts +174 -0
  332. package/src/platforms/instagram/commands/whoami.test.ts +60 -0
  333. package/src/platforms/instagram/commands/whoami.ts +21 -0
  334. package/src/platforms/kakaotalk/cli.ts +2 -1
  335. package/src/platforms/kakaotalk/client.test.ts +182 -14
  336. package/src/platforms/kakaotalk/client.ts +261 -27
  337. package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
  338. package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
  339. package/src/platforms/kakaotalk/commands/index.ts +1 -0
  340. package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
  341. package/src/platforms/kakaotalk/commands/whoami.test.ts +116 -0
  342. package/src/platforms/kakaotalk/commands/whoami.ts +21 -0
  343. package/src/platforms/kakaotalk/index.test.ts +5 -0
  344. package/src/platforms/kakaotalk/index.ts +2 -0
  345. package/src/platforms/kakaotalk/protocol/session.ts +29 -7
  346. package/src/platforms/kakaotalk/protocol/types.ts +9 -0
  347. package/src/platforms/kakaotalk/types.ts +30 -0
  348. package/src/platforms/line/cli.ts +2 -2
  349. package/src/platforms/line/client.ts +14 -39
  350. package/src/platforms/line/commands/auth.test.ts +141 -0
  351. package/src/platforms/line/commands/auth.ts +37 -61
  352. package/src/platforms/line/commands/chat.test.ts +110 -0
  353. package/src/platforms/line/commands/friend.test.ts +98 -0
  354. package/src/platforms/line/commands/index.ts +1 -1
  355. package/src/platforms/line/commands/message.test.ts +119 -0
  356. package/src/platforms/line/commands/whoami.test.ts +85 -0
  357. package/src/platforms/line/commands/{profile.ts → whoami.ts} +4 -4
  358. package/src/platforms/slack/cli.ts +2 -0
  359. package/src/platforms/slack/commands/index.ts +1 -0
  360. package/src/platforms/slack/commands/whoami.test.ts +126 -0
  361. package/src/platforms/slack/commands/whoami.ts +40 -0
  362. package/src/platforms/slackbot/cli.ts +2 -1
  363. package/src/platforms/slackbot/commands/channel.test.ts +139 -0
  364. package/src/platforms/slackbot/commands/index.ts +1 -0
  365. package/src/platforms/slackbot/commands/message.test.ts +226 -0
  366. package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
  367. package/src/platforms/slackbot/commands/user.test.ts +143 -0
  368. package/src/platforms/slackbot/commands/whoami.test.ts +102 -0
  369. package/src/platforms/slackbot/commands/whoami.ts +44 -0
  370. package/src/platforms/teams/cli.ts +2 -0
  371. package/src/platforms/teams/commands/index.ts +1 -0
  372. package/src/platforms/teams/commands/reaction.test.ts +45 -61
  373. package/src/platforms/teams/commands/reaction.ts +2 -0
  374. package/src/platforms/teams/commands/whoami.test.ts +83 -0
  375. package/src/platforms/teams/commands/whoami.ts +33 -0
  376. package/src/platforms/telegram/cli.ts +2 -1
  377. package/src/platforms/telegram/commands/chat.test.ts +125 -0
  378. package/src/platforms/telegram/commands/index.ts +1 -0
  379. package/src/platforms/telegram/commands/message.test.ts +92 -0
  380. package/src/platforms/telegram/commands/whoami.test.ts +75 -0
  381. package/src/platforms/telegram/commands/whoami.ts +29 -0
  382. package/src/platforms/webex/cli.ts +2 -1
  383. package/src/platforms/webex/commands/auth.test.ts +58 -46
  384. package/src/platforms/webex/commands/index.ts +1 -0
  385. package/src/platforms/webex/commands/member.test.ts +60 -57
  386. package/src/platforms/webex/commands/message.test.ts +74 -121
  387. package/src/platforms/webex/commands/message.ts +1 -1
  388. package/src/platforms/webex/commands/snapshot.test.ts +54 -45
  389. package/src/platforms/webex/commands/space.test.ts +46 -49
  390. package/src/platforms/webex/commands/whoami.test.ts +113 -0
  391. package/src/platforms/webex/commands/whoami.ts +31 -0
  392. package/src/platforms/webex/credential-manager.test.ts +0 -1
  393. package/src/platforms/wechatbot/cli.ts +25 -0
  394. package/src/platforms/wechatbot/client.test.ts +497 -0
  395. package/src/platforms/wechatbot/client.ts +268 -0
  396. package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
  397. package/src/platforms/wechatbot/commands/auth.ts +203 -0
  398. package/src/platforms/wechatbot/commands/index.ts +5 -0
  399. package/src/platforms/wechatbot/commands/message.test.ts +155 -0
  400. package/src/platforms/wechatbot/commands/message.ts +104 -0
  401. package/src/platforms/wechatbot/commands/shared.ts +22 -0
  402. package/src/platforms/wechatbot/commands/template.test.ts +199 -0
  403. package/src/platforms/wechatbot/commands/template.ts +102 -0
  404. package/src/platforms/wechatbot/commands/user.test.ts +165 -0
  405. package/src/platforms/wechatbot/commands/user.ts +75 -0
  406. package/src/platforms/wechatbot/commands/whoami.test.ts +109 -0
  407. package/src/platforms/wechatbot/commands/whoami.ts +43 -0
  408. package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
  409. package/src/platforms/wechatbot/credential-manager.ts +148 -0
  410. package/src/platforms/wechatbot/index.test.ts +49 -0
  411. package/src/platforms/wechatbot/index.ts +19 -0
  412. package/src/platforms/wechatbot/types.test.ts +223 -0
  413. package/src/platforms/wechatbot/types.ts +107 -0
  414. package/src/platforms/whatsapp/cli.ts +2 -1
  415. package/src/platforms/whatsapp/client.ts +180 -37
  416. package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
  417. package/src/platforms/whatsapp/commands/auth.ts +194 -84
  418. package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
  419. package/src/platforms/whatsapp/commands/index.ts +1 -0
  420. package/src/platforms/whatsapp/commands/message.test.ts +231 -0
  421. package/src/platforms/whatsapp/commands/shared.ts +2 -2
  422. package/src/platforms/whatsapp/commands/whoami.test.ts +59 -0
  423. package/src/platforms/whatsapp/commands/whoami.ts +21 -0
  424. package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
  425. package/src/platforms/whatsapp/credential-manager.ts +17 -8
  426. package/src/platforms/whatsapp/ensure-auth.ts +2 -2
  427. package/src/platforms/whatsappbot/cli.ts +2 -1
  428. package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
  429. package/src/platforms/whatsappbot/commands/index.ts +1 -0
  430. package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
  431. package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
  432. package/src/platforms/whatsappbot/commands/whoami.test.ts +100 -0
  433. package/src/platforms/whatsappbot/commands/whoami.ts +57 -0
  434. package/src/shared/utils/qr.ts +92 -0
  435. package/src/tui/adapters/whatsapp-adapter.ts +19 -16
  436. package/dist/src/platforms/line/commands/profile.d.ts +0 -3
  437. package/dist/src/platforms/line/commands/profile.d.ts.map +0 -1
  438. package/dist/src/platforms/line/commands/profile.js.map +0 -1
@@ -0,0 +1,268 @@
1
+ import type { WeChatBotNewsArticle, WeChatBotTemplate, WeChatBotUserInfo } from './types'
2
+ import { WeChatBotError } from './types'
3
+
4
+ const BASE_URL = 'https://api.weixin.qq.com'
5
+ const MAX_RETRIES = 3
6
+ const BASE_BACKOFF_MS = 100
7
+
8
+ export class WeChatBotClient {
9
+ private appId: string | null = null
10
+ private appSecret: string | null = null
11
+ private accessToken: string | null = null
12
+ private tokenExpiresAt: number = 0
13
+
14
+ async login(credentials?: { appId: string; appSecret: string }): Promise<this> {
15
+ if (credentials) {
16
+ if (!credentials.appId) {
17
+ throw new WeChatBotError('App ID is required', 'missing_app_id')
18
+ }
19
+ if (!credentials.appSecret) {
20
+ throw new WeChatBotError('App Secret is required', 'missing_app_secret')
21
+ }
22
+ this.appId = credentials.appId
23
+ this.appSecret = credentials.appSecret
24
+ return this
25
+ }
26
+ const { WeChatBotCredentialManager } = await import('./credential-manager')
27
+ const credManager = new WeChatBotCredentialManager()
28
+ const creds = await credManager.getCredentials()
29
+ if (!creds) {
30
+ throw new WeChatBotError(
31
+ 'No WeChat Bot credentials found. Run "agent-wechatbot auth set" first.',
32
+ 'no_credentials',
33
+ )
34
+ }
35
+ return this.login({ appId: creds.app_id, appSecret: creds.app_secret })
36
+ }
37
+
38
+ private ensureAuth(): void {
39
+ if (this.appId === null) {
40
+ throw new WeChatBotError('Not authenticated. Call .login() first.', 'not_authenticated')
41
+ }
42
+ }
43
+
44
+ private async ensureAccessToken(): Promise<string> {
45
+ const now = Date.now()
46
+ if (this.accessToken && now < this.tokenExpiresAt) {
47
+ return this.accessToken
48
+ }
49
+ const result = await this.fetchAccessToken()
50
+ this.accessToken = result.access_token
51
+ this.tokenExpiresAt = now + (result.expires_in - 10) * 1000
52
+ return this.accessToken
53
+ }
54
+
55
+ private async fetchAccessToken(): Promise<{ access_token: string; expires_in: number }> {
56
+ const url = `${BASE_URL}/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`
57
+ let response: Response
58
+ try {
59
+ response = await fetch(url)
60
+ } catch (error) {
61
+ const msg = error instanceof Error ? error.message : String(error)
62
+ throw new WeChatBotError(`Network error fetching access token: ${msg}`, 'network_error')
63
+ }
64
+
65
+ const data = (await response.json()) as { access_token?: string; expires_in?: number; errcode?: number; errmsg?: string }
66
+
67
+ if (data.errcode) {
68
+ throw new WeChatBotError(data.errmsg ?? `Error ${data.errcode}`, String(data.errcode))
69
+ }
70
+
71
+ if (!data.access_token) {
72
+ throw new WeChatBotError('Failed to obtain access token', 'token_fetch_failed')
73
+ }
74
+
75
+ return { access_token: data.access_token, expires_in: data.expires_in ?? 7200 }
76
+ }
77
+
78
+ async verifyCredentials(): Promise<boolean> {
79
+ this.ensureAuth()
80
+ try {
81
+ await this.fetchAccessToken()
82
+ return true
83
+ } catch {
84
+ return false
85
+ }
86
+ }
87
+
88
+ async sendTextMessage(openId: string, text: string): Promise<void> {
89
+ await this.request<void>('POST', '/cgi-bin/message/custom/send', {
90
+ touser: openId,
91
+ msgtype: 'text',
92
+ text: { content: text },
93
+ })
94
+ }
95
+
96
+ async sendImageMessage(openId: string, mediaId: string): Promise<void> {
97
+ await this.request<void>('POST', '/cgi-bin/message/custom/send', {
98
+ touser: openId,
99
+ msgtype: 'image',
100
+ image: { media_id: mediaId },
101
+ })
102
+ }
103
+
104
+ async sendNewsMessage(openId: string, articles: WeChatBotNewsArticle[]): Promise<void> {
105
+ await this.request<void>('POST', '/cgi-bin/message/custom/send', {
106
+ touser: openId,
107
+ msgtype: 'news',
108
+ news: { articles },
109
+ })
110
+ }
111
+
112
+ async sendTemplateMessage(
113
+ openId: string,
114
+ templateId: string,
115
+ data: Record<string, { value: string }>,
116
+ url?: string,
117
+ ): Promise<{ msgid: number }> {
118
+ return this.request<{ msgid: number }>('POST', '/cgi-bin/message/template/send', {
119
+ touser: openId,
120
+ template_id: templateId,
121
+ url,
122
+ data,
123
+ })
124
+ }
125
+
126
+ async listTemplates(): Promise<WeChatBotTemplate[]> {
127
+ const result = await this.request<{ template_list: WeChatBotTemplate[] }>(
128
+ 'GET',
129
+ '/cgi-bin/template/get_all_private_template',
130
+ )
131
+ return result.template_list
132
+ }
133
+
134
+ async deleteTemplate(templateId: string): Promise<void> {
135
+ await this.request<void>('POST', '/cgi-bin/template/del_private_template', {
136
+ template_id: templateId,
137
+ })
138
+ }
139
+
140
+ async getFollowers(nextOpenId?: string): Promise<{
141
+ total: number
142
+ count: number
143
+ openids: string[]
144
+ next_openid: string
145
+ }> {
146
+ const path = this.buildPath('/cgi-bin/user/get', nextOpenId ? { next_openid: nextOpenId } : undefined)
147
+ const result = await this.request<{
148
+ total: number
149
+ count: number
150
+ data: { openid: string[] }
151
+ next_openid: string
152
+ }>('GET', path)
153
+ return {
154
+ total: result.total,
155
+ count: result.count,
156
+ openids: result.data?.openid ?? [],
157
+ next_openid: result.next_openid,
158
+ }
159
+ }
160
+
161
+ async getUserInfo(openId: string, lang = 'zh_CN'): Promise<WeChatBotUserInfo> {
162
+ const path = this.buildPath('/cgi-bin/user/info', { openid: openId, lang })
163
+ return this.request<WeChatBotUserInfo>('GET', path)
164
+ }
165
+
166
+ private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
167
+ this.ensureAuth()
168
+ let lastError: Error | undefined
169
+ let tokenInvalidated = false
170
+
171
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
172
+ const token = await this.ensureAccessToken()
173
+ const url = `${BASE_URL}${path}${path.includes('?') ? '&' : '?'}access_token=${token}`
174
+
175
+ const options: RequestInit = {
176
+ method,
177
+ headers: {
178
+ 'Content-Type': 'application/json',
179
+ },
180
+ }
181
+
182
+ if (body !== undefined) {
183
+ options.body = JSON.stringify(body)
184
+ }
185
+
186
+ let response: Response
187
+
188
+ try {
189
+ response = await fetch(url, options)
190
+ } catch (error) {
191
+ lastError = error instanceof Error ? error : new Error(String(error))
192
+ if (attempt < MAX_RETRIES && method === 'GET') {
193
+ await this.sleep(BASE_BACKOFF_MS * 2 ** attempt)
194
+ continue
195
+ }
196
+ throw new WeChatBotError(`Network error: ${lastError.message}`, 'network_error')
197
+ }
198
+
199
+ if (response.status >= 500 && response.status <= 599) {
200
+ if (attempt < MAX_RETRIES && method === 'GET') {
201
+ await this.sleep(BASE_BACKOFF_MS * 2 ** attempt)
202
+ continue
203
+ }
204
+ throw new WeChatBotError(`HTTP ${response.status}`, `http_${response.status}`)
205
+ }
206
+
207
+ const data = (await response.json()) as {
208
+ errcode?: number
209
+ errmsg?: string
210
+ [key: string]: unknown
211
+ }
212
+
213
+ if (data.errcode === 40001 || data.errcode === 42001) {
214
+ if (!tokenInvalidated) {
215
+ tokenInvalidated = true
216
+ this.accessToken = null
217
+ this.tokenExpiresAt = 0
218
+ continue
219
+ }
220
+ throw new WeChatBotError(data.errmsg ?? `Error ${data.errcode}`, String(data.errcode))
221
+ }
222
+
223
+ if (data.errcode === -1) {
224
+ if (attempt < MAX_RETRIES) {
225
+ await this.sleep(BASE_BACKOFF_MS * 2 ** attempt)
226
+ continue
227
+ }
228
+ throw new WeChatBotError('WeChat system busy', '-1')
229
+ }
230
+
231
+ if (data.errcode === 45009) {
232
+ throw new WeChatBotError('WeChat API frequency limit exceeded', '45009')
233
+ }
234
+
235
+ if (data.errcode && data.errcode !== 0) {
236
+ throw new WeChatBotError(data.errmsg ?? `Error ${data.errcode}`, String(data.errcode))
237
+ }
238
+
239
+ return data as T
240
+ }
241
+
242
+ throw lastError ?? new WeChatBotError('Request failed after retries', 'max_retries')
243
+ }
244
+
245
+ private buildPath(path: string, params?: Record<string, string | number | undefined>): string {
246
+ if (!params) {
247
+ return path
248
+ }
249
+
250
+ const searchParams = new URLSearchParams()
251
+ for (const [key, value] of Object.entries(params)) {
252
+ if (value !== undefined) {
253
+ searchParams.set(key, String(value))
254
+ }
255
+ }
256
+
257
+ const query = searchParams.toString()
258
+ if (!query) {
259
+ return path
260
+ }
261
+
262
+ return `${path}?${query}`
263
+ }
264
+
265
+ private sleep(ms: number): Promise<void> {
266
+ return new Promise((resolve) => setTimeout(resolve, ms))
267
+ }
268
+ }
@@ -0,0 +1,211 @@
1
+ import { afterAll, describe, expect, mock, test } from 'bun:test'
2
+ import { rmSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+
5
+ import { WeChatBotCredentialManager } from '@/platforms/wechatbot/credential-manager'
6
+
7
+ mock.module('../client', () => ({
8
+ WeChatBotClient: class MockWeChatBotClient {
9
+ async login() {
10
+ return this
11
+ }
12
+ verifyCredentials = mock(() => Promise.resolve(true))
13
+ },
14
+ }))
15
+
16
+ const { setAction, statusAction, clearAction, listAction, useAction, removeAction } = await import(
17
+ '@/platforms/wechatbot/commands/auth'
18
+ )
19
+
20
+ const testDirs: string[] = []
21
+
22
+ function makeCredManager(): WeChatBotCredentialManager {
23
+ const dir = join(
24
+ import.meta.dir,
25
+ `.test-auth-config-${Date.now()}-${Math.random().toString(36).slice(2)}`,
26
+ )
27
+ testDirs.push(dir)
28
+ return new WeChatBotCredentialManager(dir)
29
+ }
30
+
31
+ afterAll(() => {
32
+ for (const dir of testDirs) {
33
+ rmSync(dir, { recursive: true, force: true })
34
+ }
35
+ })
36
+
37
+ describe('setAction', () => {
38
+ test('returns success with app_id when credentials are valid', async () => {
39
+ const credManager = makeCredManager()
40
+ const result = await setAction('wx123', 'secret123', { _credManager: credManager })
41
+
42
+ expect(result.success).toBe(true)
43
+ expect(result.app_id).toBe('wx123')
44
+ expect(result.account_name).toBe('wx123')
45
+ })
46
+
47
+ test('saves credentials to credManager', async () => {
48
+ const credManager = makeCredManager()
49
+ await setAction('wx456', 'secret456', { _credManager: credManager })
50
+
51
+ const creds = await credManager.getCredentials()
52
+ expect(creds?.app_id).toBe('wx456')
53
+ })
54
+
55
+ test('returns error when verifyCredentials returns false', async () => {
56
+ mock.module('../client', () => ({
57
+ WeChatBotClient: class MockWeChatBotClient {
58
+ async login() {
59
+ return this
60
+ }
61
+ verifyCredentials = mock(() => Promise.resolve(false))
62
+ },
63
+ }))
64
+
65
+ const { setAction: setActionFresh } = await import('@/platforms/wechatbot/commands/auth')
66
+ const credManager = makeCredManager()
67
+ const result = await setActionFresh('wx-bad', 'bad-secret', { _credManager: credManager })
68
+
69
+ expect(result.error).toBeDefined()
70
+ expect(result.success).toBeUndefined()
71
+ })
72
+ })
73
+
74
+ describe('statusAction', () => {
75
+ test('returns valid: false when no credentials configured', async () => {
76
+ const credManager = makeCredManager()
77
+ const result = await statusAction({ _credManager: credManager })
78
+
79
+ expect(result.valid).toBe(false)
80
+ expect(result.error).toBeDefined()
81
+ })
82
+
83
+ test('returns valid: true when credentials exist and are valid', async () => {
84
+ mock.module('../client', () => ({
85
+ WeChatBotClient: class MockWeChatBotClient {
86
+ async login() {
87
+ return this
88
+ }
89
+ verifyCredentials = mock(() => Promise.resolve(true))
90
+ },
91
+ }))
92
+
93
+ const { statusAction: statusActionFresh } = await import('@/platforms/wechatbot/commands/auth')
94
+ const credManager = makeCredManager()
95
+ await credManager.setCredentials({ app_id: 'wx123', app_secret: 'secret123', account_name: 'My Account' })
96
+
97
+ const result = await statusActionFresh({ _credManager: credManager })
98
+
99
+ expect(result.valid).toBe(true)
100
+ expect(result.app_id).toBe('wx123')
101
+ })
102
+
103
+ test('returns valid: false when credentials exist but are invalid', async () => {
104
+ mock.module('../client', () => ({
105
+ WeChatBotClient: class MockWeChatBotClient {
106
+ async login() {
107
+ return this
108
+ }
109
+ verifyCredentials = mock(() => Promise.resolve(false))
110
+ },
111
+ }))
112
+
113
+ const { statusAction: statusActionFresh } = await import('@/platforms/wechatbot/commands/auth')
114
+ const credManager = makeCredManager()
115
+ await credManager.setCredentials({ app_id: 'wx123', app_secret: 'bad-secret', account_name: 'My Account' })
116
+
117
+ const result = await statusActionFresh({ _credManager: credManager })
118
+
119
+ expect(result.valid).toBe(false)
120
+ })
121
+
122
+ test('returns error message when account not found', async () => {
123
+ const credManager = makeCredManager()
124
+ const result = await statusAction({ account: 'nonexistent', _credManager: credManager })
125
+
126
+ expect(result.valid).toBe(false)
127
+ expect(result.error).toContain('nonexistent')
128
+ })
129
+ })
130
+
131
+ describe('clearAction', () => {
132
+ test('clears all credentials and returns success', async () => {
133
+ const credManager = makeCredManager()
134
+ await credManager.setCredentials({ app_id: 'wx123', app_secret: 'secret123', account_name: 'My Account' })
135
+
136
+ const result = await clearAction({ _credManager: credManager })
137
+
138
+ expect(result.success).toBe(true)
139
+
140
+ const creds = await credManager.getCredentials()
141
+ expect(creds).toBeNull()
142
+ })
143
+ })
144
+
145
+ describe('listAction', () => {
146
+ test('returns empty accounts array when none configured', async () => {
147
+ const credManager = makeCredManager()
148
+ const result = await listAction({ _credManager: credManager })
149
+
150
+ expect(result.accounts).toEqual([])
151
+ })
152
+
153
+ test('returns all accounts with is_current flag', async () => {
154
+ const credManager = makeCredManager()
155
+ await credManager.setCredentials({ app_id: 'wx-a', app_secret: 'secret-a', account_name: 'Account A' })
156
+ await credManager.setCredentials({ app_id: 'wx-b', app_secret: 'secret-b', account_name: 'Account B' })
157
+
158
+ const result = await listAction({ _credManager: credManager })
159
+
160
+ expect(result.accounts).toHaveLength(2)
161
+ const acctA = result.accounts?.find((a) => a.app_id === 'wx-a')
162
+ const acctB = result.accounts?.find((a) => a.app_id === 'wx-b')
163
+ expect(acctA?.is_current).toBe(false)
164
+ expect(acctB?.is_current).toBe(true)
165
+ })
166
+ })
167
+
168
+ describe('useAction', () => {
169
+ test('switches to specified account and returns success', async () => {
170
+ const credManager = makeCredManager()
171
+ await credManager.setCredentials({ app_id: 'wx-a', app_secret: 'secret-a', account_name: 'Account A' })
172
+ await credManager.setCredentials({ app_id: 'wx-b', app_secret: 'secret-b', account_name: 'Account B' })
173
+
174
+ const result = await useAction('wx-a', { _credManager: credManager })
175
+
176
+ expect(result.success).toBe(true)
177
+ expect(result.app_id).toBe('wx-a')
178
+ })
179
+
180
+ test('returns error when account not found', async () => {
181
+ const credManager = makeCredManager()
182
+ const result = await useAction('nonexistent', { _credManager: credManager })
183
+
184
+ expect(result.error).toBeDefined()
185
+ expect(result.error).toContain('nonexistent')
186
+ expect(result.success).toBeUndefined()
187
+ })
188
+ })
189
+
190
+ describe('removeAction', () => {
191
+ test('removes specified account and returns success', async () => {
192
+ const credManager = makeCredManager()
193
+ await credManager.setCredentials({ app_id: 'wx123', app_secret: 'secret123', account_name: 'My Account' })
194
+
195
+ const result = await removeAction('wx123', { _credManager: credManager })
196
+
197
+ expect(result.success).toBe(true)
198
+
199
+ const creds = await credManager.getCredentials()
200
+ expect(creds).toBeNull()
201
+ })
202
+
203
+ test('returns error when account not found', async () => {
204
+ const credManager = makeCredManager()
205
+ const result = await removeAction('nonexistent', { _credManager: credManager })
206
+
207
+ expect(result.error).toBeDefined()
208
+ expect(result.error).toContain('nonexistent')
209
+ expect(result.success).toBeUndefined()
210
+ })
211
+ })
@@ -0,0 +1,203 @@
1
+ import { Command } from 'commander'
2
+
3
+ import { formatOutput } from '@/shared/utils/output'
4
+
5
+ import { WeChatBotClient } from '../client'
6
+ import { WeChatBotCredentialManager } from '../credential-manager'
7
+
8
+ interface ActionOptions {
9
+ account?: string
10
+ pretty?: boolean
11
+ _credManager?: WeChatBotCredentialManager
12
+ }
13
+
14
+ interface ActionResult {
15
+ success?: boolean
16
+ error?: string
17
+ valid?: boolean
18
+ app_id?: string
19
+ account_name?: string
20
+ accounts?: Array<{ app_id: string; account_name: string; is_current: boolean }>
21
+ }
22
+
23
+ export async function setAction(appId: string, appSecret: string, options: ActionOptions): Promise<ActionResult> {
24
+ try {
25
+ const client = await new WeChatBotClient().login({ appId, appSecret })
26
+ const valid = await client.verifyCredentials()
27
+
28
+ if (!valid) {
29
+ return { error: 'Invalid credentials. Could not obtain access token.' }
30
+ }
31
+
32
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
33
+ await credManager.setCredentials({
34
+ app_id: appId,
35
+ app_secret: appSecret,
36
+ account_name: appId,
37
+ })
38
+
39
+ return { success: true, app_id: appId, account_name: appId }
40
+ } catch (error: unknown) {
41
+ return { error: (error as Error).message }
42
+ }
43
+ }
44
+
45
+ export async function statusAction(options: ActionOptions): Promise<ActionResult> {
46
+ try {
47
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
48
+ const creds = await credManager.getCredentials(options.account)
49
+
50
+ if (!creds) {
51
+ return {
52
+ valid: false,
53
+ error: options.account
54
+ ? `Account "${options.account}" not found. Run "auth list" to see available accounts.`
55
+ : 'No credentials configured. Run "auth set <app-id> <app-secret>" first.',
56
+ }
57
+ }
58
+
59
+ let valid = false
60
+ let appId: string | undefined
61
+ let accountName: string | undefined
62
+
63
+ try {
64
+ const client = await new WeChatBotClient().login({ appId: creds.app_id, appSecret: creds.app_secret })
65
+ valid = await client.verifyCredentials()
66
+ appId = creds.app_id
67
+ accountName = creds.account_name
68
+ } catch {
69
+ valid = false
70
+ appId = creds.app_id
71
+ accountName = creds.account_name
72
+ }
73
+
74
+ return { valid, app_id: appId, account_name: accountName }
75
+ } catch (error: unknown) {
76
+ return { error: (error as Error).message }
77
+ }
78
+ }
79
+
80
+ export async function clearAction(options: ActionOptions): Promise<ActionResult> {
81
+ try {
82
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
83
+ await credManager.clearCredentials()
84
+ return { success: true }
85
+ } catch (error: unknown) {
86
+ return { error: (error as Error).message }
87
+ }
88
+ }
89
+
90
+ export async function listAction(options: ActionOptions): Promise<ActionResult> {
91
+ try {
92
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
93
+ const all = await credManager.listAll()
94
+
95
+ return {
96
+ accounts: all.map((a) => ({
97
+ app_id: a.app_id,
98
+ account_name: a.account_name,
99
+ is_current: a.is_current,
100
+ })),
101
+ }
102
+ } catch (error: unknown) {
103
+ return { error: (error as Error).message }
104
+ }
105
+ }
106
+
107
+ export async function useAction(accountId: string, options: ActionOptions): Promise<ActionResult> {
108
+ try {
109
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
110
+ const found = await credManager.setCurrent(accountId)
111
+
112
+ if (!found) {
113
+ return { error: `Account "${accountId}" not found. Run "auth list" to see available accounts.` }
114
+ }
115
+
116
+ const creds = await credManager.getCredentials()
117
+ return {
118
+ success: true,
119
+ app_id: creds?.app_id,
120
+ account_name: creds?.account_name,
121
+ }
122
+ } catch (error: unknown) {
123
+ return { error: (error as Error).message }
124
+ }
125
+ }
126
+
127
+ export async function removeAction(accountId: string, options: ActionOptions): Promise<ActionResult> {
128
+ try {
129
+ const credManager = options._credManager ?? new WeChatBotCredentialManager()
130
+ const removed = await credManager.removeAccount(accountId)
131
+
132
+ if (!removed) {
133
+ return { error: `Account "${accountId}" not found. Run "auth list" to see available accounts.` }
134
+ }
135
+
136
+ return { success: true }
137
+ } catch (error: unknown) {
138
+ return { error: (error as Error).message }
139
+ }
140
+ }
141
+
142
+ function cliOutput(result: ActionResult, pretty?: boolean, exitOnError = true): void {
143
+ console.log(formatOutput(result, pretty))
144
+ if (result.error && exitOnError) process.exit(1)
145
+ }
146
+
147
+ export const authCommand = new Command('auth')
148
+ .description('Authentication commands')
149
+ .addCommand(
150
+ new Command('set')
151
+ .description('Set account credentials')
152
+ .argument('<app-id>', 'WeChat Official Account App ID')
153
+ .argument('<app-secret>', 'WeChat Official Account App Secret')
154
+ .option('--pretty', 'Pretty print JSON output')
155
+ .action(async (appId: string, appSecret: string, opts: { pretty?: boolean }) => {
156
+ cliOutput(await setAction(appId, appSecret, opts), opts.pretty)
157
+ }),
158
+ )
159
+ .addCommand(
160
+ new Command('status')
161
+ .description('Show authentication status')
162
+ .option('--account <id>', 'Check specific account (default: current)')
163
+ .option('--pretty', 'Pretty print JSON output')
164
+ .action(async (opts: { account?: string; pretty?: boolean }) => {
165
+ const result = await statusAction(opts)
166
+ console.log(formatOutput(result, opts.pretty))
167
+ if (!result.valid) process.exit(1)
168
+ }),
169
+ )
170
+ .addCommand(
171
+ new Command('clear')
172
+ .description('Clear all stored credentials')
173
+ .option('--pretty', 'Pretty print JSON output')
174
+ .action(async (opts: { pretty?: boolean }) => {
175
+ cliOutput(await clearAction(opts), opts.pretty)
176
+ }),
177
+ )
178
+ .addCommand(
179
+ new Command('list')
180
+ .description('List all stored accounts')
181
+ .option('--pretty', 'Pretty print JSON output')
182
+ .action(async (opts: { pretty?: boolean }) => {
183
+ cliOutput(await listAction(opts), opts.pretty)
184
+ }),
185
+ )
186
+ .addCommand(
187
+ new Command('use')
188
+ .description('Switch active account')
189
+ .argument('<account-id>', 'Account ID (App ID)')
190
+ .option('--pretty', 'Pretty print JSON output')
191
+ .action(async (accountId: string, opts: { pretty?: boolean }) => {
192
+ cliOutput(await useAction(accountId, opts), opts.pretty)
193
+ }),
194
+ )
195
+ .addCommand(
196
+ new Command('remove')
197
+ .description('Remove a stored account')
198
+ .argument('<account-id>', 'Account ID (App ID)')
199
+ .option('--pretty', 'Pretty print JSON output')
200
+ .action(async (accountId: string, opts: { pretty?: boolean }) => {
201
+ cliOutput(await removeAction(accountId, opts), opts.pretty)
202
+ }),
203
+ )
@@ -0,0 +1,5 @@
1
+ export { authCommand } from './auth'
2
+ export { messageCommand } from './message'
3
+ export { templateCommand } from './template'
4
+ export { userCommand } from './user'
5
+ export { whoamiCommand } from './whoami'