agent-messenger 2.4.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 (329) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.github/workflows/release.yml +0 -12
  3. package/README.md +3 -3
  4. package/dist/package.json +1 -1
  5. package/dist/src/platforms/channeltalk/cli.d.ts.map +1 -1
  6. package/dist/src/platforms/channeltalk/cli.js +2 -1
  7. package/dist/src/platforms/channeltalk/cli.js.map +1 -1
  8. package/dist/src/platforms/channeltalk/commands/index.d.ts +1 -0
  9. package/dist/src/platforms/channeltalk/commands/index.d.ts.map +1 -1
  10. package/dist/src/platforms/channeltalk/commands/index.js +1 -0
  11. package/dist/src/platforms/channeltalk/commands/index.js.map +1 -1
  12. package/dist/src/platforms/channeltalk/commands/whoami.d.ts +22 -0
  13. package/dist/src/platforms/channeltalk/commands/whoami.d.ts.map +1 -0
  14. package/dist/src/platforms/channeltalk/commands/whoami.js +40 -0
  15. package/dist/src/platforms/channeltalk/commands/whoami.js.map +1 -0
  16. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  17. package/dist/src/platforms/channeltalkbot/cli.js +2 -1
  18. package/dist/src/platforms/channeltalkbot/cli.js.map +1 -1
  19. package/dist/src/platforms/channeltalkbot/commands/index.d.ts +1 -0
  20. package/dist/src/platforms/channeltalkbot/commands/index.d.ts.map +1 -1
  21. package/dist/src/platforms/channeltalkbot/commands/index.js +1 -0
  22. package/dist/src/platforms/channeltalkbot/commands/index.js.map +1 -1
  23. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts +13 -0
  24. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts.map +1 -0
  25. package/dist/src/platforms/channeltalkbot/commands/whoami.js +31 -0
  26. package/dist/src/platforms/channeltalkbot/commands/whoami.js.map +1 -0
  27. package/dist/src/platforms/discord/cli.d.ts.map +1 -1
  28. package/dist/src/platforms/discord/cli.js +2 -1
  29. package/dist/src/platforms/discord/cli.js.map +1 -1
  30. package/dist/src/platforms/discord/commands/index.d.ts +1 -0
  31. package/dist/src/platforms/discord/commands/index.d.ts.map +1 -1
  32. package/dist/src/platforms/discord/commands/index.js +1 -0
  33. package/dist/src/platforms/discord/commands/index.js.map +1 -1
  34. package/dist/src/platforms/discord/commands/whoami.d.ts +6 -0
  35. package/dist/src/platforms/discord/commands/whoami.d.ts.map +1 -0
  36. package/dist/src/platforms/discord/commands/whoami.js +33 -0
  37. package/dist/src/platforms/discord/commands/whoami.js.map +1 -0
  38. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  39. package/dist/src/platforms/discordbot/cli.js +2 -1
  40. package/dist/src/platforms/discordbot/cli.js.map +1 -1
  41. package/dist/src/platforms/discordbot/commands/index.d.ts +1 -0
  42. package/dist/src/platforms/discordbot/commands/index.d.ts.map +1 -1
  43. package/dist/src/platforms/discordbot/commands/index.js +1 -0
  44. package/dist/src/platforms/discordbot/commands/index.js.map +1 -1
  45. package/dist/src/platforms/discordbot/commands/whoami.d.ts +14 -0
  46. package/dist/src/platforms/discordbot/commands/whoami.d.ts.map +1 -0
  47. package/dist/src/platforms/discordbot/commands/whoami.js +32 -0
  48. package/dist/src/platforms/discordbot/commands/whoami.js.map +1 -0
  49. package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
  50. package/dist/src/platforms/instagram/cli.js +2 -1
  51. package/dist/src/platforms/instagram/cli.js.map +1 -1
  52. package/dist/src/platforms/instagram/client.d.ts +6 -0
  53. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  54. package/dist/src/platforms/instagram/client.js +12 -0
  55. package/dist/src/platforms/instagram/client.js.map +1 -1
  56. package/dist/src/platforms/instagram/commands/index.d.ts +1 -0
  57. package/dist/src/platforms/instagram/commands/index.d.ts.map +1 -1
  58. package/dist/src/platforms/instagram/commands/index.js +1 -0
  59. package/dist/src/platforms/instagram/commands/index.js.map +1 -1
  60. package/dist/src/platforms/instagram/commands/whoami.d.ts +7 -0
  61. package/dist/src/platforms/instagram/commands/whoami.d.ts.map +1 -0
  62. package/dist/src/platforms/instagram/commands/whoami.js +19 -0
  63. package/dist/src/platforms/instagram/commands/whoami.js.map +1 -0
  64. package/dist/src/platforms/kakaotalk/cli.js +2 -2
  65. package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
  66. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  67. package/dist/src/platforms/kakaotalk/client.js +173 -21
  68. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  69. package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -1
  70. package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
  71. package/dist/src/platforms/kakaotalk/commands/index.js +1 -1
  72. package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
  73. package/dist/src/platforms/kakaotalk/commands/whoami.d.ts +3 -0
  74. package/dist/src/platforms/kakaotalk/commands/whoami.d.ts.map +1 -0
  75. package/dist/src/platforms/kakaotalk/commands/{profile.js → whoami.js} +5 -5
  76. package/dist/src/platforms/kakaotalk/commands/whoami.js.map +1 -0
  77. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +4 -2
  78. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  79. package/dist/src/platforms/kakaotalk/protocol/session.js +25 -6
  80. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  81. package/dist/src/platforms/kakaotalk/protocol/types.d.ts +17 -0
  82. package/dist/src/platforms/kakaotalk/protocol/types.d.ts.map +1 -1
  83. package/dist/src/platforms/kakaotalk/protocol/types.js.map +1 -1
  84. package/dist/src/platforms/kakaotalk/types.d.ts +12 -0
  85. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  86. package/dist/src/platforms/kakaotalk/types.js +6 -0
  87. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  88. package/dist/src/platforms/line/cli.js +2 -2
  89. package/dist/src/platforms/line/cli.js.map +1 -1
  90. package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
  91. package/dist/src/platforms/line/commands/auth.js +9 -59
  92. package/dist/src/platforms/line/commands/auth.js.map +1 -1
  93. package/dist/src/platforms/line/commands/index.d.ts +1 -1
  94. package/dist/src/platforms/line/commands/index.d.ts.map +1 -1
  95. package/dist/src/platforms/line/commands/index.js +1 -1
  96. package/dist/src/platforms/line/commands/index.js.map +1 -1
  97. package/dist/src/platforms/line/commands/whoami.d.ts +3 -0
  98. package/dist/src/platforms/line/commands/whoami.d.ts.map +1 -0
  99. package/dist/src/platforms/line/commands/{profile.js → whoami.js} +5 -5
  100. package/dist/src/platforms/line/commands/whoami.js.map +1 -0
  101. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  102. package/dist/src/platforms/slack/cli.js +2 -1
  103. package/dist/src/platforms/slack/cli.js.map +1 -1
  104. package/dist/src/platforms/slack/commands/index.d.ts +1 -0
  105. package/dist/src/platforms/slack/commands/index.d.ts.map +1 -1
  106. package/dist/src/platforms/slack/commands/index.js +1 -0
  107. package/dist/src/platforms/slack/commands/index.js.map +1 -1
  108. package/dist/src/platforms/slack/commands/whoami.d.ts +6 -0
  109. package/dist/src/platforms/slack/commands/whoami.d.ts.map +1 -0
  110. package/dist/src/platforms/slack/commands/whoami.js +39 -0
  111. package/dist/src/platforms/slack/commands/whoami.js.map +1 -0
  112. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  113. package/dist/src/platforms/slackbot/cli.js +2 -1
  114. package/dist/src/platforms/slackbot/cli.js.map +1 -1
  115. package/dist/src/platforms/slackbot/commands/index.d.ts +1 -0
  116. package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -1
  117. package/dist/src/platforms/slackbot/commands/index.js +1 -0
  118. package/dist/src/platforms/slackbot/commands/index.js.map +1 -1
  119. package/dist/src/platforms/slackbot/commands/whoami.d.ts +14 -0
  120. package/dist/src/platforms/slackbot/commands/whoami.d.ts.map +1 -0
  121. package/dist/src/platforms/slackbot/commands/whoami.js +32 -0
  122. package/dist/src/platforms/slackbot/commands/whoami.js.map +1 -0
  123. package/dist/src/platforms/teams/cli.d.ts.map +1 -1
  124. package/dist/src/platforms/teams/cli.js +2 -1
  125. package/dist/src/platforms/teams/cli.js.map +1 -1
  126. package/dist/src/platforms/teams/commands/index.d.ts +1 -0
  127. package/dist/src/platforms/teams/commands/index.d.ts.map +1 -1
  128. package/dist/src/platforms/teams/commands/index.js +1 -0
  129. package/dist/src/platforms/teams/commands/index.js.map +1 -1
  130. package/dist/src/platforms/teams/commands/whoami.d.ts +6 -0
  131. package/dist/src/platforms/teams/commands/whoami.d.ts.map +1 -0
  132. package/dist/src/platforms/teams/commands/whoami.js +30 -0
  133. package/dist/src/platforms/teams/commands/whoami.js.map +1 -0
  134. package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
  135. package/dist/src/platforms/telegram/cli.js +2 -1
  136. package/dist/src/platforms/telegram/cli.js.map +1 -1
  137. package/dist/src/platforms/telegram/commands/index.d.ts +1 -0
  138. package/dist/src/platforms/telegram/commands/index.d.ts.map +1 -1
  139. package/dist/src/platforms/telegram/commands/index.js +1 -0
  140. package/dist/src/platforms/telegram/commands/index.js.map +1 -1
  141. package/dist/src/platforms/telegram/commands/whoami.d.ts +7 -0
  142. package/dist/src/platforms/telegram/commands/whoami.d.ts.map +1 -0
  143. package/dist/src/platforms/telegram/commands/whoami.js +27 -0
  144. package/dist/src/platforms/telegram/commands/whoami.js.map +1 -0
  145. package/dist/src/platforms/webex/cli.d.ts.map +1 -1
  146. package/dist/src/platforms/webex/cli.js +2 -1
  147. package/dist/src/platforms/webex/cli.js.map +1 -1
  148. package/dist/src/platforms/webex/commands/index.d.ts +1 -0
  149. package/dist/src/platforms/webex/commands/index.d.ts.map +1 -1
  150. package/dist/src/platforms/webex/commands/index.js +1 -0
  151. package/dist/src/platforms/webex/commands/index.js.map +1 -1
  152. package/dist/src/platforms/webex/commands/message.js +1 -1
  153. package/dist/src/platforms/webex/commands/message.js.map +1 -1
  154. package/dist/src/platforms/webex/commands/whoami.d.ts +6 -0
  155. package/dist/src/platforms/webex/commands/whoami.d.ts.map +1 -0
  156. package/dist/src/platforms/webex/commands/whoami.js +30 -0
  157. package/dist/src/platforms/webex/commands/whoami.js.map +1 -0
  158. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
  159. package/dist/src/platforms/wechatbot/cli.js +2 -1
  160. package/dist/src/platforms/wechatbot/cli.js.map +1 -1
  161. package/dist/src/platforms/wechatbot/commands/index.d.ts +1 -0
  162. package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -1
  163. package/dist/src/platforms/wechatbot/commands/index.js +1 -0
  164. package/dist/src/platforms/wechatbot/commands/index.js.map +1 -1
  165. package/dist/src/platforms/wechatbot/commands/whoami.d.ts +12 -0
  166. package/dist/src/platforms/wechatbot/commands/whoami.d.ts.map +1 -0
  167. package/dist/src/platforms/wechatbot/commands/whoami.js +33 -0
  168. package/dist/src/platforms/wechatbot/commands/whoami.js.map +1 -0
  169. package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
  170. package/dist/src/platforms/whatsapp/cli.js +2 -1
  171. package/dist/src/platforms/whatsapp/cli.js.map +1 -1
  172. package/dist/src/platforms/whatsapp/client.d.ts +8 -0
  173. package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
  174. package/dist/src/platforms/whatsapp/client.js +116 -8
  175. package/dist/src/platforms/whatsapp/client.js.map +1 -1
  176. package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
  177. package/dist/src/platforms/whatsapp/commands/auth.js +115 -45
  178. package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
  179. package/dist/src/platforms/whatsapp/commands/index.d.ts +1 -0
  180. package/dist/src/platforms/whatsapp/commands/index.d.ts.map +1 -1
  181. package/dist/src/platforms/whatsapp/commands/index.js +1 -0
  182. package/dist/src/platforms/whatsapp/commands/index.js.map +1 -1
  183. package/dist/src/platforms/whatsapp/commands/shared.js +2 -2
  184. package/dist/src/platforms/whatsapp/commands/shared.js.map +1 -1
  185. package/dist/src/platforms/whatsapp/commands/whoami.d.ts +7 -0
  186. package/dist/src/platforms/whatsapp/commands/whoami.d.ts.map +1 -0
  187. package/dist/src/platforms/whatsapp/commands/whoami.js +19 -0
  188. package/dist/src/platforms/whatsapp/commands/whoami.js.map +1 -0
  189. package/dist/src/platforms/whatsapp/ensure-auth.js +2 -2
  190. package/dist/src/platforms/whatsapp/ensure-auth.js.map +1 -1
  191. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  192. package/dist/src/platforms/whatsappbot/cli.js +2 -1
  193. package/dist/src/platforms/whatsappbot/cli.js.map +1 -1
  194. package/dist/src/platforms/whatsappbot/commands/index.d.ts +1 -0
  195. package/dist/src/platforms/whatsappbot/commands/index.d.ts.map +1 -1
  196. package/dist/src/platforms/whatsappbot/commands/index.js +1 -0
  197. package/dist/src/platforms/whatsappbot/commands/index.js.map +1 -1
  198. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts +17 -0
  199. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts.map +1 -0
  200. package/dist/src/platforms/whatsappbot/commands/whoami.js +39 -0
  201. package/dist/src/platforms/whatsappbot/commands/whoami.js.map +1 -0
  202. package/dist/src/shared/utils/qr.d.ts +15 -0
  203. package/dist/src/shared/utils/qr.d.ts.map +1 -0
  204. package/dist/src/shared/utils/qr.js +74 -0
  205. package/dist/src/shared/utils/qr.js.map +1 -0
  206. package/dist/src/tui/adapters/whatsapp-adapter.d.ts.map +1 -1
  207. package/dist/src/tui/adapters/whatsapp-adapter.js +20 -15
  208. package/dist/src/tui/adapters/whatsapp-adapter.js.map +1 -1
  209. package/docs/content/docs/cli/channeltalk.mdx +11 -0
  210. package/docs/content/docs/cli/channeltalkbot.mdx +9 -0
  211. package/docs/content/docs/cli/discord.mdx +10 -0
  212. package/docs/content/docs/cli/discordbot.mdx +9 -0
  213. package/docs/content/docs/cli/instagram.mdx +11 -0
  214. package/docs/content/docs/cli/kakaotalk.mdx +24 -0
  215. package/docs/content/docs/cli/line.mdx +4 -4
  216. package/docs/content/docs/cli/slack.mdx +10 -0
  217. package/docs/content/docs/cli/slackbot.mdx +9 -0
  218. package/docs/content/docs/cli/teams.mdx +10 -0
  219. package/docs/content/docs/cli/telegram.mdx +11 -0
  220. package/docs/content/docs/cli/webex.mdx +10 -0
  221. package/docs/content/docs/cli/wechatbot.mdx +9 -0
  222. package/docs/content/docs/cli/whatsapp.mdx +36 -7
  223. package/docs/content/docs/cli/whatsappbot.mdx +9 -0
  224. package/package.json +1 -1
  225. package/skills/agent-channeltalk/SKILL.md +12 -1
  226. package/skills/agent-channeltalkbot/SKILL.md +10 -1
  227. package/skills/agent-discord/SKILL.md +11 -1
  228. package/skills/agent-discordbot/SKILL.md +10 -1
  229. package/skills/agent-instagram/SKILL.md +12 -1
  230. package/skills/agent-kakaotalk/SKILL.md +14 -8
  231. package/skills/agent-kakaotalk/references/common-patterns.md +1 -1
  232. package/skills/agent-line/SKILL.md +5 -5
  233. package/skills/agent-slack/SKILL.md +11 -1
  234. package/skills/agent-slackbot/SKILL.md +10 -1
  235. package/skills/agent-teams/SKILL.md +11 -1
  236. package/skills/agent-telegram/SKILL.md +6 -1
  237. package/skills/agent-webex/SKILL.md +11 -1
  238. package/skills/agent-wechatbot/SKILL.md +10 -1
  239. package/skills/agent-whatsapp/SKILL.md +52 -15
  240. package/skills/agent-whatsapp/references/authentication.md +36 -6
  241. package/skills/agent-whatsappbot/SKILL.md +10 -1
  242. package/src/platforms/channeltalk/cli.ts +2 -0
  243. package/src/platforms/channeltalk/commands/index.ts +1 -0
  244. package/src/platforms/channeltalk/commands/whoami.test.ts +64 -0
  245. package/src/platforms/channeltalk/commands/whoami.ts +62 -0
  246. package/src/platforms/channeltalkbot/cli.ts +2 -0
  247. package/src/platforms/channeltalkbot/commands/index.ts +1 -0
  248. package/src/platforms/channeltalkbot/commands/whoami.test.ts +104 -0
  249. package/src/platforms/channeltalkbot/commands/whoami.ts +42 -0
  250. package/src/platforms/discord/cli.ts +2 -0
  251. package/src/platforms/discord/commands/index.ts +1 -0
  252. package/src/platforms/discord/commands/whoami.test.ts +91 -0
  253. package/src/platforms/discord/commands/whoami.ts +36 -0
  254. package/src/platforms/discordbot/cli.ts +2 -0
  255. package/src/platforms/discordbot/commands/index.ts +1 -0
  256. package/src/platforms/discordbot/commands/whoami.test.ts +96 -0
  257. package/src/platforms/discordbot/commands/whoami.ts +44 -0
  258. package/src/platforms/instagram/cli.ts +2 -1
  259. package/src/platforms/instagram/client.ts +13 -0
  260. package/src/platforms/instagram/commands/chat.test.ts +1 -5
  261. package/src/platforms/instagram/commands/index.ts +1 -0
  262. package/src/platforms/instagram/commands/message.test.ts +1 -5
  263. package/src/platforms/instagram/commands/whoami.test.ts +60 -0
  264. package/src/platforms/instagram/commands/whoami.ts +21 -0
  265. package/src/platforms/kakaotalk/cli.ts +2 -2
  266. package/src/platforms/kakaotalk/client.test.ts +25 -14
  267. package/src/platforms/kakaotalk/client.ts +204 -24
  268. package/src/platforms/kakaotalk/commands/index.ts +1 -1
  269. package/src/platforms/kakaotalk/commands/{profile.test.ts → whoami.test.ts} +37 -5
  270. package/src/platforms/kakaotalk/commands/{profile.ts → whoami.ts} +4 -4
  271. package/src/platforms/kakaotalk/protocol/session.ts +27 -7
  272. package/src/platforms/kakaotalk/protocol/types.ts +9 -0
  273. package/src/platforms/kakaotalk/types.ts +12 -0
  274. package/src/platforms/line/cli.ts +2 -2
  275. package/src/platforms/line/commands/auth.ts +37 -70
  276. package/src/platforms/line/commands/index.ts +1 -1
  277. package/src/platforms/line/commands/{profile.test.ts → whoami.test.ts} +11 -11
  278. package/src/platforms/line/commands/{profile.ts → whoami.ts} +4 -4
  279. package/src/platforms/slack/cli.ts +2 -0
  280. package/src/platforms/slack/commands/index.ts +1 -0
  281. package/src/platforms/slack/commands/whoami.test.ts +126 -0
  282. package/src/platforms/slack/commands/whoami.ts +40 -0
  283. package/src/platforms/slackbot/cli.ts +2 -1
  284. package/src/platforms/slackbot/commands/index.ts +1 -0
  285. package/src/platforms/slackbot/commands/whoami.test.ts +102 -0
  286. package/src/platforms/slackbot/commands/whoami.ts +44 -0
  287. package/src/platforms/teams/cli.ts +2 -0
  288. package/src/platforms/teams/commands/index.ts +1 -0
  289. package/src/platforms/teams/commands/whoami.test.ts +83 -0
  290. package/src/platforms/teams/commands/whoami.ts +33 -0
  291. package/src/platforms/telegram/cli.ts +2 -1
  292. package/src/platforms/telegram/commands/index.ts +1 -0
  293. package/src/platforms/telegram/commands/whoami.test.ts +75 -0
  294. package/src/platforms/telegram/commands/whoami.ts +29 -0
  295. package/src/platforms/webex/cli.ts +2 -1
  296. package/src/platforms/webex/commands/auth.test.ts +58 -46
  297. package/src/platforms/webex/commands/index.ts +1 -0
  298. package/src/platforms/webex/commands/member.test.ts +1 -5
  299. package/src/platforms/webex/commands/message.test.ts +1 -5
  300. package/src/platforms/webex/commands/message.ts +1 -1
  301. package/src/platforms/webex/commands/snapshot.test.ts +1 -5
  302. package/src/platforms/webex/commands/space.test.ts +1 -5
  303. package/src/platforms/webex/commands/whoami.test.ts +113 -0
  304. package/src/platforms/webex/commands/whoami.ts +31 -0
  305. package/src/platforms/webex/credential-manager.test.ts +0 -1
  306. package/src/platforms/wechatbot/cli.ts +2 -1
  307. package/src/platforms/wechatbot/commands/index.ts +1 -0
  308. package/src/platforms/wechatbot/commands/whoami.test.ts +109 -0
  309. package/src/platforms/wechatbot/commands/whoami.ts +43 -0
  310. package/src/platforms/whatsapp/cli.ts +2 -1
  311. package/src/platforms/whatsapp/client.ts +156 -24
  312. package/src/platforms/whatsapp/commands/auth.ts +176 -70
  313. package/src/platforms/whatsapp/commands/index.ts +1 -0
  314. package/src/platforms/whatsapp/commands/shared.ts +2 -2
  315. package/src/platforms/whatsapp/commands/whoami.test.ts +59 -0
  316. package/src/platforms/whatsapp/commands/whoami.ts +21 -0
  317. package/src/platforms/whatsapp/ensure-auth.ts +2 -2
  318. package/src/platforms/whatsappbot/cli.ts +2 -1
  319. package/src/platforms/whatsappbot/commands/index.ts +1 -0
  320. package/src/platforms/whatsappbot/commands/whoami.test.ts +100 -0
  321. package/src/platforms/whatsappbot/commands/whoami.ts +57 -0
  322. package/src/shared/utils/qr.ts +92 -0
  323. package/src/tui/adapters/whatsapp-adapter.ts +19 -16
  324. package/dist/src/platforms/kakaotalk/commands/profile.d.ts +0 -3
  325. package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +0 -1
  326. package/dist/src/platforms/kakaotalk/commands/profile.js.map +0 -1
  327. package/dist/src/platforms/line/commands/profile.d.ts +0 -3
  328. package/dist/src/platforms/line/commands/profile.d.ts.map +0 -1
  329. package/dist/src/platforms/line/commands/profile.js.map +0 -1
@@ -0,0 +1,44 @@
1
+ import { Command } from 'commander'
2
+
3
+ import { formatOutput } from '@/shared/utils/output'
4
+
5
+ import type { BotOption } from './shared'
6
+ import { getClient } from './shared'
7
+
8
+ interface WhoamiResult {
9
+ id?: string
10
+ username?: string
11
+ global_name?: string
12
+ avatar?: string
13
+ bot?: boolean
14
+ error?: string
15
+ }
16
+
17
+ export async function whoamiAction(options: BotOption): Promise<WhoamiResult> {
18
+ try {
19
+ const client = await getClient(options)
20
+ const info = await client.testAuth()
21
+ return {
22
+ id: info.id,
23
+ username: info.username,
24
+ global_name: info.global_name,
25
+ avatar: info.avatar,
26
+ bot: info.bot,
27
+ }
28
+ } catch (error) {
29
+ return { error: (error as Error).message }
30
+ }
31
+ }
32
+
33
+ function cliOutput(result: WhoamiResult, pretty?: boolean): void {
34
+ console.log(formatOutput(result, pretty))
35
+ if (result.error) process.exit(1)
36
+ }
37
+
38
+ export const whoamiCommand = new Command('whoami')
39
+ .description('Show current authenticated bot')
40
+ .option('--bot <id>', 'Bot ID to use')
41
+ .option('--pretty', 'Pretty print JSON output')
42
+ .action(async (opts: BotOption) => {
43
+ cliOutput(await whoamiAction(opts), opts.pretty)
44
+ })
@@ -4,7 +4,7 @@ import type { Command as CommandType } from 'commander'
4
4
  import { Command } from 'commander'
5
5
 
6
6
  import pkg from '../../../package.json' with { type: 'json' }
7
- import { authCommand, chatCommand, messageCommand } from './commands/index'
7
+ import { authCommand, chatCommand, messageCommand, whoamiCommand } from './commands/index'
8
8
  import { ensureInstagramAuth } from './ensure-auth'
9
9
 
10
10
  function isAuthCommand(command: CommandType): boolean {
@@ -31,6 +31,7 @@ program.hook('preAction', async (_thisCommand, actionCommand) => {
31
31
  program.addCommand(authCommand)
32
32
  program.addCommand(chatCommand)
33
33
  program.addCommand(messageCommand)
34
+ program.addCommand(whoamiCommand)
34
35
 
35
36
  program.parse(process.argv)
36
37
 
@@ -410,6 +410,19 @@ export class InstagramClient {
410
410
  return this.userId
411
411
  }
412
412
 
413
+ async getProfile(): Promise<{ user_id: string; username: string; full_name: string | null; profile_pic_url: string | null }> {
414
+ if (!this.userId) {
415
+ throw new InstagramError('Not authenticated. Call login() first.', 'not_authenticated')
416
+ }
417
+ const account = await this.credentialManager.getAccount()
418
+ return {
419
+ user_id: this.userId,
420
+ username: account?.username ?? '',
421
+ full_name: account?.full_name ?? null,
422
+ profile_pic_url: account?.profile_pic_url ?? null,
423
+ }
424
+ }
425
+
413
426
  private async preLoginFlow(): Promise<{ keyId: string; publicKey: string } | null> {
414
427
  const url = `${IG_BASE_URL}/qe/sync/`
415
428
  const headers = this.buildHeaders()
@@ -1,4 +1,4 @@
1
- import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
1
+ import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
2
2
 
3
3
  const originalConsoleLog = console.log
4
4
  import type { Command } from 'commander'
@@ -34,10 +34,6 @@ function resetCommandState(cmd: Command): void {
34
34
  }
35
35
  }
36
36
 
37
- afterAll(() => {
38
- mock.restore()
39
- })
40
-
41
37
  describe('chat commands', () => {
42
38
  let consoleLogSpy: ReturnType<typeof mock>
43
39
  let processExitSpy: ReturnType<typeof spyOn>
@@ -1,3 +1,4 @@
1
1
  export { authCommand } from './auth'
2
2
  export { chatCommand } from './chat'
3
3
  export { messageCommand } from './message'
4
+ export { whoamiCommand } from './whoami'
@@ -1,4 +1,4 @@
1
- import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
1
+ import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
2
2
 
3
3
  const originalConsoleLog = console.log
4
4
  import type { Command } from 'commander'
@@ -32,10 +32,6 @@ function resetCommandState(cmd: Command): void {
32
32
  }
33
33
  }
34
34
 
35
- afterAll(() => {
36
- mock.restore()
37
- })
38
-
39
35
  describe('message commands', () => {
40
36
  let consoleLogSpy: ReturnType<typeof mock>
41
37
  let processExitSpy: ReturnType<typeof spyOn>
@@ -0,0 +1,60 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'
2
+
3
+ import { InstagramClient } from '../client'
4
+ import * as sharedModule from './shared'
5
+ import { whoamiAction } from './whoami'
6
+
7
+ let withInstagramClientSpy: ReturnType<typeof spyOn>
8
+ let getProfileSpy: ReturnType<typeof spyOn>
9
+ let consoleLogSpy: ReturnType<typeof spyOn>
10
+
11
+ describe('whoami command', () => {
12
+ beforeEach(() => {
13
+ getProfileSpy = spyOn(InstagramClient.prototype, 'getProfile').mockResolvedValue({
14
+ user_id: '987654321',
15
+ username: 'testuser',
16
+ full_name: 'Test User',
17
+ profile_pic_url: 'https://example.com/pic.jpg',
18
+ })
19
+ withInstagramClientSpy = spyOn(sharedModule, 'withInstagramClient').mockImplementation(
20
+ async (_opts, fn) => {
21
+ const fakeClient = Object.create(InstagramClient.prototype) as InstagramClient
22
+ return fn(fakeClient)
23
+ },
24
+ )
25
+ consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
26
+ })
27
+
28
+ afterEach(() => {
29
+ getProfileSpy?.mockRestore()
30
+ withInstagramClientSpy?.mockRestore()
31
+ consoleLogSpy?.mockRestore()
32
+ })
33
+
34
+ test('outputs profile information', async () => {
35
+ await whoamiAction({})
36
+
37
+ expect(consoleLogSpy).toHaveBeenCalledTimes(1)
38
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
39
+ expect(output.user_id).toBe('987654321')
40
+ expect(output.username).toBe('testuser')
41
+ expect(output.full_name).toBe('Test User')
42
+ expect(output.profile_pic_url).toBe('https://example.com/pic.jpg')
43
+ })
44
+
45
+ test('outputs profile with null optional fields', async () => {
46
+ getProfileSpy.mockResolvedValue({
47
+ user_id: '987654321',
48
+ username: 'testuser',
49
+ full_name: null,
50
+ profile_pic_url: null,
51
+ })
52
+
53
+ await whoamiAction({})
54
+
55
+ expect(consoleLogSpy).toHaveBeenCalledTimes(1)
56
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
57
+ expect(output.full_name).toBeNull()
58
+ expect(output.profile_pic_url).toBeNull()
59
+ })
60
+ })
@@ -0,0 +1,21 @@
1
+ import { Command } from 'commander'
2
+
3
+ import { handleError } from '@/shared/utils/error-handler'
4
+ import { formatOutput } from '@/shared/utils/output'
5
+
6
+ import { withInstagramClient } from './shared'
7
+
8
+ export async function whoamiAction(options: { account?: string; pretty?: boolean }): Promise<void> {
9
+ try {
10
+ const profile = await withInstagramClient(options, (client) => client.getProfile())
11
+ console.log(formatOutput(profile, options.pretty))
12
+ } catch (error) {
13
+ handleError(error as Error)
14
+ }
15
+ }
16
+
17
+ export const whoamiCommand = new Command('whoami')
18
+ .description('Show current authenticated user')
19
+ .option('--account <id>', 'Use a specific Instagram account')
20
+ .option('--pretty', 'Pretty print JSON output')
21
+ .action(whoamiAction)
@@ -4,7 +4,7 @@ import type { Command as CommandType } from 'commander'
4
4
  import { Command } from 'commander'
5
5
 
6
6
  import pkg from '../../../package.json' with { type: 'json' }
7
- import { authCommand, chatCommand, messageCommand, profileCommand } from './commands/index'
7
+ import { authCommand, chatCommand, messageCommand, whoamiCommand } from './commands/index'
8
8
  import { ensureKakaoAuth } from './ensure-auth'
9
9
 
10
10
  function isAuthCommand(command: CommandType): boolean {
@@ -31,7 +31,7 @@ program.hook('preAction', async (_thisCommand, actionCommand) => {
31
31
  program.addCommand(authCommand)
32
32
  program.addCommand(chatCommand)
33
33
  program.addCommand(messageCommand)
34
- program.addCommand(profileCommand)
34
+ program.addCommand(whoamiCommand)
35
35
 
36
36
  program.parse(process.argv)
37
37
 
@@ -5,6 +5,8 @@ import { KakaoTalkClient, KakaoTalkError } from './client'
5
5
  // Mock LocoSession at module level
6
6
  const mockLogin = mock(() => Promise.resolve({}))
7
7
  const mockGetChatList = mock(() => Promise.resolve({}))
8
+ const mockGetChatLogs = mock(() => Promise.resolve({}))
9
+ const mockGetChatInfo = mock(() => Promise.resolve({}))
8
10
  const mockSyncMessages = mock(() => Promise.resolve({}))
9
11
  const mockSendMessage = mock(() => Promise.resolve({}))
10
12
  const mockClose = mock(() => {})
@@ -14,6 +16,8 @@ mock.module('./protocol/session', () => ({
14
16
  LocoSession: class MockLocoSession {
15
17
  login = mockLogin
16
18
  getChatList = mockGetChatList
19
+ getChatLogs = mockGetChatLogs
20
+ getChatInfo = mockGetChatInfo
17
21
  syncMessages = mockSyncMessages
18
22
  sendMessage = mockSendMessage
19
23
  close = mockClose
@@ -28,6 +32,8 @@ function makeLong(n: number): { low: number; high: number } {
28
32
  function resetAllMocks() {
29
33
  mockLogin.mockReset()
30
34
  mockGetChatList.mockReset()
35
+ mockGetChatLogs.mockReset()
36
+ mockGetChatInfo.mockReset()
31
37
  mockSyncMessages.mockReset()
32
38
  mockSendMessage.mockReset()
33
39
  mockClose.mockReset()
@@ -67,6 +73,8 @@ describe('KakaoTalkClient', () => {
67
73
  beforeEach(() => {
68
74
  resetAllMocks()
69
75
  mockLogin.mockResolvedValue(DEFAULT_LOGIN_RESULT)
76
+ mockGetChatLogs.mockResolvedValue({ body: { status: 0, chatLogs: [], eof: true } })
77
+ mockGetChatInfo.mockResolvedValue({ body: { l: makeLong(99999) } })
70
78
  })
71
79
 
72
80
  afterEach(() => {
@@ -316,13 +324,14 @@ describe('KakaoTalkClient', () => {
316
324
 
317
325
  describe('getMessages', () => {
318
326
  test('returns formatted messages', async () => {
319
- mockSyncMessages.mockResolvedValueOnce({
327
+ mockGetChatLogs.mockResolvedValueOnce({
320
328
  body: {
329
+ status: 0,
321
330
  chatLogs: [
322
- { logId: makeLong(10), type: 1, authorId: 42, message: 'hello', sendAt: 1700000001 },
323
- { logId: makeLong(11), type: 1, authorId: 43, message: 'world', sendAt: 1700000002 },
331
+ { logId: makeLong(10), chatId: 100, type: 1, authorId: 42, message: 'hello', sendAt: 1700000001 },
332
+ { logId: makeLong(11), chatId: 100, type: 1, authorId: 43, message: 'world', sendAt: 1700000002 },
324
333
  ],
325
- isOK: true,
334
+ eof: true,
326
335
  },
327
336
  })
328
337
 
@@ -351,14 +360,15 @@ describe('KakaoTalkClient', () => {
351
360
  test('respects count option', async () => {
352
361
  const logs = Array.from({ length: 50 }, (_, i) => ({
353
362
  logId: makeLong(i + 1),
363
+ chatId: 100,
354
364
  type: 1,
355
365
  authorId: 1,
356
366
  message: `msg-${i}`,
357
367
  sendAt: 1700000000 + i,
358
368
  }))
359
369
 
360
- mockSyncMessages.mockResolvedValueOnce({
361
- body: { chatLogs: logs, isOK: true },
370
+ mockGetChatLogs.mockResolvedValueOnce({
371
+ body: { status: 0, chatLogs: logs, eof: true },
362
372
  })
363
373
 
364
374
  const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
@@ -373,13 +383,14 @@ describe('KakaoTalkClient', () => {
373
383
  })
374
384
 
375
385
  test('sorts messages by sent_at ascending', async () => {
376
- mockSyncMessages.mockResolvedValueOnce({
386
+ mockGetChatLogs.mockResolvedValueOnce({
377
387
  body: {
388
+ status: 0,
378
389
  chatLogs: [
379
- { logId: makeLong(2), type: 1, authorId: 1, message: 'second', sendAt: 200 },
380
- { logId: makeLong(1), type: 1, authorId: 1, message: 'first', sendAt: 100 },
390
+ { logId: makeLong(2), chatId: 100, type: 1, authorId: 1, message: 'second', sendAt: 200 },
391
+ { logId: makeLong(1), chatId: 100, type: 1, authorId: 1, message: 'first', sendAt: 100 },
381
392
  ],
382
- isOK: true,
393
+ eof: true,
383
394
  },
384
395
  })
385
396
 
@@ -556,8 +567,8 @@ describe('KakaoTalkClient', () => {
556
567
  })
557
568
 
558
569
  test('reuses session across multiple calls', async () => {
559
- mockSyncMessages.mockResolvedValue({
560
- body: { chatLogs: [], isOK: true },
570
+ mockGetChatLogs.mockResolvedValue({
571
+ body: { status: 0, chatLogs: [], eof: true },
561
572
  })
562
573
 
563
574
  const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
@@ -574,8 +585,8 @@ describe('KakaoTalkClient', () => {
574
585
  mockLogin.mockImplementation(
575
586
  () => new Promise((resolve) => setTimeout(() => resolve(DEFAULT_LOGIN_RESULT), 50)),
576
587
  )
577
- mockSyncMessages.mockResolvedValue({
578
- body: { chatLogs: [], isOK: true },
588
+ mockGetChatLogs.mockResolvedValue({
589
+ body: { status: 0, chatLogs: [], eof: true },
579
590
  })
580
591
 
581
592
  const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
@@ -1,10 +1,14 @@
1
1
  import { Long } from 'bson'
2
+ import { existsSync } from 'node:fs'
3
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises'
4
+ import { homedir } from 'node:os'
5
+ import { join } from 'node:path'
2
6
 
3
7
  import { warn } from '@/shared/utils/stderr'
4
8
 
5
9
  import { APP_VERSION, LANG, OS } from './protocol/config'
6
10
  import { LocoSession } from './protocol/session'
7
- import type { ChatListResponse, LoginListResponse } from './protocol/types'
11
+ import type { ChatListResponse, LoginListResponse, SyncState } from './protocol/types'
8
12
  import type { KakaoChat, KakaoMessage, KakaoProfile, KakaoSendResult } from './types'
9
13
 
10
14
  export class KakaoTalkError extends Error {
@@ -74,6 +78,14 @@ function matchesSearch(chat: ChatData, term: string): boolean {
74
78
  return names.some((n) => n.toLowerCase().includes(lower))
75
79
  }
76
80
 
81
+ function findMaxLogId(logs: Array<Record<string, unknown>>, field: string): Long | null {
82
+ return logs.reduce<Long | null>((max, log) => {
83
+ const current = bsonToLong(log[field])
84
+ if (!current) return max
85
+ return !max || current.greaterThan(max) ? current : max
86
+ }, null)
87
+ }
88
+
77
89
  function collectChats(chatDatas: ChatData[], into: ChatData[], seen: Set<string>): void {
78
90
  for (const chat of chatDatas) {
79
91
  const id = String(chat.c)
@@ -92,6 +104,129 @@ function wrapError(error: unknown, code: string): KakaoTalkError {
92
104
 
93
105
  const MAX_PAGES = 50
94
106
 
107
+ const CONFIG_DIR = join(homedir(), '.config', 'agent-messenger')
108
+
109
+ function syncStatePath(deviceUuid: string): string {
110
+ return join(CONFIG_DIR, `kakaotalk-sync-state-${deviceUuid}.json`)
111
+ }
112
+
113
+ async function loadSyncState(deviceUuid: string): Promise<SyncState | undefined> {
114
+ const path = syncStatePath(deviceUuid)
115
+ if (!existsSync(path)) return undefined
116
+ const content = await readFile(path, 'utf-8')
117
+ const parsed = JSON.parse(content) as Partial<SyncState>
118
+
119
+ if (
120
+ parsed.version !== 2 ||
121
+ typeof parsed.revision !== 'number' ||
122
+ !Array.isArray(parsed.chatIds) ||
123
+ !Array.isArray(parsed.maxIds) ||
124
+ parsed.chatIds.length !== parsed.maxIds.length ||
125
+ !parsed.lastTokenId ||
126
+ typeof parsed.lbk !== 'number'
127
+ ) {
128
+ return undefined
129
+ }
130
+
131
+ return parsed as SyncState
132
+ }
133
+
134
+ async function saveSyncState(deviceUuid: string, state: SyncState): Promise<void> {
135
+ await mkdir(CONFIG_DIR, { recursive: true })
136
+ const path = syncStatePath(deviceUuid)
137
+ await writeFile(path, JSON.stringify(state, null, 2))
138
+ await chmod(path, 0o600)
139
+ }
140
+
141
+ function toLongLike(v: unknown): { low: number; high: number } {
142
+ if (v && typeof v === 'object' && 'low' in v && 'high' in v) {
143
+ const { low, high } = v as { low: number; high: number }
144
+ return { low, high }
145
+ }
146
+ if (typeof v === 'number') {
147
+ const big = BigInt(v)
148
+ return { low: Number(big & 0xffffffffn), high: Number((big >> 32n) & 0xffffffffn) }
149
+ }
150
+ return { low: 0, high: 0 }
151
+ }
152
+
153
+ function buildSyncState(loginResult: LoginListResponse, previousRevision: number): SyncState {
154
+ const chatDatas = (loginResult.chatDatas ?? []) as Array<Record<string, unknown>>
155
+ return {
156
+ version: 2,
157
+ revision: typeof loginResult.revision === 'number' ? loginResult.revision : previousRevision,
158
+ chatIds: chatDatas.map((chat) => toLongLike(chat.c)),
159
+ maxIds: chatDatas.map((chat) => toLongLike(chat.ll)),
160
+ lastTokenId: toLongLike(loginResult.lastTokenId),
161
+ lbk: typeof loginResult.lbk === 'number' ? loginResult.lbk : 0,
162
+ }
163
+ }
164
+
165
+ function deleteFromSyncState(state: SyncState, chatId: string): void {
166
+ const index = state.chatIds.findIndex((entry) => longToString(entry) === chatId)
167
+ if (index === -1) return
168
+
169
+ state.chatIds.splice(index, 1)
170
+ state.maxIds.splice(index, 1)
171
+ }
172
+
173
+ function upsertSyncState(state: SyncState, chatId: unknown, maxId: unknown): void {
174
+ const chatIdString = longToString(chatId)
175
+ const nextChatId = toLongLike(chatId)
176
+ const nextMaxId = toLongLike(maxId)
177
+ const index = state.chatIds.findIndex((entry) => longToString(entry) === chatIdString)
178
+
179
+ if (index === -1) {
180
+ state.chatIds.push(nextChatId)
181
+ state.maxIds.push(nextMaxId)
182
+ return
183
+ }
184
+
185
+ state.chatIds[index] = nextChatId
186
+ state.maxIds[index] = nextMaxId
187
+ }
188
+
189
+ function mergeSyncState(previous: SyncState | undefined, loginResult: LoginListResponse): SyncState {
190
+ const next = previous
191
+ ? {
192
+ version: 2 as const,
193
+ revision: previous.revision,
194
+ chatIds: [...previous.chatIds],
195
+ maxIds: [...previous.maxIds],
196
+ lastTokenId: previous.lastTokenId,
197
+ lbk: previous.lbk,
198
+ }
199
+ : buildSyncState(loginResult, 0)
200
+
201
+ next.revision = typeof loginResult.revision === 'number' ? loginResult.revision : next.revision
202
+ next.lastTokenId = toLongLike(loginResult.lastTokenId)
203
+ next.lbk = typeof loginResult.lbk === 'number' ? loginResult.lbk : next.lbk
204
+
205
+ const delChatIds = Array.isArray(loginResult.delChatIds) ? loginResult.delChatIds : []
206
+ for (const chatId of delChatIds) {
207
+ deleteFromSyncState(next, longToString(chatId))
208
+ }
209
+
210
+ const chatDatas = Array.isArray(loginResult.chatDatas) ? loginResult.chatDatas : []
211
+ for (const chat of chatDatas) {
212
+ upsertSyncState(next, chat.c, chat.ll)
213
+ }
214
+
215
+ return next
216
+ }
217
+
218
+ function formatMessages(logs: Array<Record<string, unknown>>, count: number): KakaoMessage[] {
219
+ logs.sort((a, b) => (a.sendAt as number) - (b.sendAt as number))
220
+
221
+ return logs.slice(-count).map((log) => ({
222
+ log_id: longToString(log.logId),
223
+ type: log.type as number,
224
+ author_id: log.authorId as number,
225
+ message: log.message as string,
226
+ sent_at: log.sendAt as number,
227
+ }))
228
+ }
229
+
95
230
  export class KakaoTalkClient {
96
231
  private oauthToken: string | null = null
97
232
  private userId: string | null = null
@@ -179,7 +314,11 @@ export class KakaoTalkClient {
179
314
  private async connect(): Promise<SessionState> {
180
315
  const session = new LocoSession()
181
316
  try {
182
- const loginResult = await session.login(this.oauthToken!, this.userId!, this.deviceUuid!)
317
+ const syncState = await loadSyncState(this.deviceUuid!)
318
+ const loginResult = await session.login(this.oauthToken!, this.userId!, this.deviceUuid!, syncState)
319
+
320
+ const newSyncState = mergeSyncState(syncState, loginResult)
321
+ await saveSyncState(this.deviceUuid!, newSyncState)
183
322
 
184
323
  session.onClose(() => {
185
324
  if (this.state?.session === session) {
@@ -253,21 +392,62 @@ export class KakaoTalkClient {
253
392
  }
254
393
 
255
394
  async getMessages(chatId: string, options?: { count?: number; from?: string }): Promise<KakaoMessage[]> {
256
- return this.executeWithReconnect(async ({ session, loginResult }) => {
395
+ return this.executeWithReconnect(async ({ session }) => {
257
396
  try {
258
- const rawChats = (loginResult.chatDatas ?? []) as ChatData[]
259
- const chat = rawChats.find((c) => String(c.c) === chatId)
260
- const lastLogId = chat?.ll as { high: number; low: number } | undefined
261
- const maxLogId = lastLogId ? new Long(lastLogId.low, lastLogId.high) : undefined
262
-
263
397
  const count = options?.count ?? 20
264
398
  const cursor = options?.from ? parseLong(options.from) : undefined
265
399
 
266
400
  const cid = parseLong(chatId)
267
- const startCursor = cursor ?? Long.fromNumber(0)
401
+
268
402
  const allMessages: Array<Record<string, unknown>> = []
269
403
  const seenLogIds = new Set<string>()
270
- let cur = startCursor
404
+ let cur = cursor ?? Long.fromNumber(0)
405
+
406
+ try {
407
+ for (let page = 0; page < MAX_PAGES; page++) {
408
+ const response = await session.getChatLogs([cid], [cur])
409
+ const responseStatus = response.body.status
410
+ if (typeof responseStatus === 'number' && responseStatus !== 0) {
411
+ throw new Error(`MCHATLOGS failed: ${responseStatus}`)
412
+ }
413
+
414
+ const batch = ((response.body.chatLogs ?? []) as Array<Record<string, unknown>>).filter(
415
+ (log) => longToString(log.chatId) === chatId,
416
+ )
417
+ if (batch.length === 0) {
418
+ return formatMessages(allMessages, count)
419
+ }
420
+
421
+ for (const log of batch) {
422
+ const lid = longToString(log.logId)
423
+ if (!seenLogIds.has(lid)) {
424
+ seenLogIds.add(lid)
425
+ allMessages.push(log)
426
+ }
427
+ }
428
+
429
+ const maxLog = findMaxLogId(batch, 'logId')
430
+ if (!maxLog || maxLog.equals(cur) || response.body.eof) {
431
+ return formatMessages(allMessages, count)
432
+ }
433
+
434
+ cur = maxLog
435
+ }
436
+ } catch {
437
+ allMessages.length = 0
438
+ seenLogIds.clear()
439
+ cur = cursor ?? Long.fromNumber(0)
440
+ }
441
+
442
+ if (allMessages.length > 0) {
443
+ warn(`[agent-kakaotalk] Warning: message fetch capped at ${MAX_PAGES} pages. Results may be incomplete.`)
444
+ return formatMessages(allMessages, count)
445
+ }
446
+
447
+ // Fetch fresh lastLogId via CHATONROOM (not the stale login-time snapshot)
448
+ const chatInfo = await session.getChatInfo(cid)
449
+ const chatBody = chatInfo.body as Record<string, unknown>
450
+ const maxLogId = bsonToLong(chatBody.l)
271
451
 
272
452
  let reachedEnd = false
273
453
  for (let page = 0; page < MAX_PAGES; page++) {
@@ -283,11 +463,7 @@ export class KakaoTalkClient {
283
463
  }
284
464
  }
285
465
 
286
- const maxLog = batch.reduce<Long | null>((max, l) => {
287
- const lid = l.logId as { high: number; low: number }
288
- const long = new Long(lid.low, lid.high)
289
- return !max || long.greaterThan(max) ? long : max
290
- }, null)
466
+ const maxLog = findMaxLogId(batch, 'logId')
291
467
 
292
468
  if (!maxLog || maxLog.equals(cur) || response.body.isOK) { reachedEnd = true; break }
293
469
  cur = maxLog
@@ -296,15 +472,7 @@ export class KakaoTalkClient {
296
472
  warn(`[agent-kakaotalk] Warning: message fetch capped at ${MAX_PAGES} pages. Results may be incomplete.`)
297
473
  }
298
474
 
299
- allMessages.sort((a, b) => (a.sendAt as number) - (b.sendAt as number))
300
-
301
- return allMessages.slice(-count).map((log) => ({
302
- log_id: longToString(log.logId),
303
- type: log.type as number,
304
- author_id: log.authorId as number,
305
- message: log.message as string,
306
- sent_at: log.sendAt as number,
307
- }))
475
+ return formatMessages(allMessages, count)
308
476
  } catch (error) {
309
477
  throw wrapError(error, 'get_messages_failed')
310
478
  }
@@ -353,9 +521,15 @@ export class KakaoTalkClient {
353
521
  const profile = profileData.profile as Record<string, unknown> | undefined
354
522
 
355
523
  let accountDisplayId: string | null = null
524
+ let accountEmail: string | null = null
525
+ let pstnNumber: string | null = null
526
+ let emailVerified: boolean | null = null
356
527
  if (settingsRes.ok) {
357
528
  const settingsData = await settingsRes.json() as Record<string, unknown>
358
529
  accountDisplayId = (settingsData.accountDisplayId as string) || null
530
+ accountEmail = (settingsData.accountEmail as string) || null
531
+ pstnNumber = (settingsData.pstnNumber as string) || null
532
+ emailVerified = typeof settingsData.emailVerified === 'boolean' ? settingsData.emailVerified : null
359
533
  }
360
534
 
361
535
  return {
@@ -363,8 +537,14 @@ export class KakaoTalkClient {
363
537
  nickname: (profile?.nickName as string) || '',
364
538
  profile_image_url: (profile?.profileImageUrl as string) || null,
365
539
  original_profile_image_url: (profile?.originalProfileImageUrl as string) || null,
540
+ background_image_url: (profile?.backgroundImageUrl as string) || null,
541
+ original_background_image_url: (profile?.originalBackgroundImageUrl as string) || null,
542
+ fullname: (profile?.fullname as string) || null,
366
543
  status_message: (profile?.statusMessage as string) || null,
367
544
  account_display_id: accountDisplayId,
545
+ account_email: accountEmail,
546
+ pstn_number: pstnNumber,
547
+ email_verified: emailVerified,
368
548
  }
369
549
  } catch (error) {
370
550
  throw wrapError(error, 'get_profile_failed')
@@ -1,4 +1,4 @@
1
1
  export { authCommand } from './auth'
2
2
  export { chatCommand } from './chat'
3
3
  export { messageCommand } from './message'
4
- export { profileCommand } from './profile'
4
+ export { whoamiCommand } from './whoami'