agent-messenger 2.7.0 → 2.8.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 (511) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.github/workflows/ci.yml +6 -0
  3. package/.oxfmtrc.json +13 -1
  4. package/.oxlintrc.json +10 -1
  5. package/README.md +1 -1
  6. package/dist/package.json +59 -58
  7. package/dist/src/platforms/channeltalk/client.d.ts.map +1 -1
  8. package/dist/src/platforms/channeltalk/client.js +2 -2
  9. package/dist/src/platforms/channeltalk/client.js.map +1 -1
  10. package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
  11. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  12. package/dist/src/platforms/channeltalk/commands/message.d.ts.map +1 -1
  13. package/dist/src/platforms/channeltalk/commands/message.js.map +1 -1
  14. package/dist/src/platforms/channeltalk/commands/snapshot.d.ts.map +1 -1
  15. package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
  16. package/dist/src/platforms/channeltalk/ensure-auth.d.ts.map +1 -1
  17. package/dist/src/platforms/channeltalk/ensure-auth.js.map +1 -1
  18. package/dist/src/platforms/channeltalk/token-extractor.d.ts +9 -23
  19. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  20. package/dist/src/platforms/channeltalk/token-extractor.js +109 -341
  21. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  22. package/dist/src/platforms/channeltalkbot/client.d.ts +1 -1
  23. package/dist/src/platforms/channeltalkbot/client.d.ts.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/client.js +4 -4
  25. package/dist/src/platforms/channeltalkbot/client.js.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/commands/auth.d.ts.map +1 -1
  27. package/dist/src/platforms/channeltalkbot/commands/auth.js +4 -1
  28. package/dist/src/platforms/channeltalkbot/commands/auth.js.map +1 -1
  29. package/dist/src/platforms/channeltalkbot/commands/shared.d.ts.map +1 -1
  30. package/dist/src/platforms/channeltalkbot/commands/shared.js.map +1 -1
  31. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  32. package/dist/src/platforms/discord/commands/whoami.d.ts.map +1 -1
  33. package/dist/src/platforms/discord/commands/whoami.js.map +1 -1
  34. package/dist/src/platforms/discord/token-extractor.d.ts +2 -10
  35. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  36. package/dist/src/platforms/discord/token-extractor.js +38 -172
  37. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  38. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  39. package/dist/src/platforms/discordbot/client.js.map +1 -1
  40. package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
  41. package/dist/src/platforms/instagram/cli.js +1 -4
  42. package/dist/src/platforms/instagram/cli.js.map +1 -1
  43. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  44. package/dist/src/platforms/instagram/client.js +8 -7
  45. package/dist/src/platforms/instagram/client.js.map +1 -1
  46. package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
  47. package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
  48. package/dist/src/platforms/instagram/commands/chat.d.ts.map +1 -1
  49. package/dist/src/platforms/instagram/commands/chat.js.map +1 -1
  50. package/dist/src/platforms/instagram/commands/message.d.ts.map +1 -1
  51. package/dist/src/platforms/instagram/commands/message.js.map +1 -1
  52. package/dist/src/platforms/instagram/commands/shared.d.ts.map +1 -1
  53. package/dist/src/platforms/instagram/commands/shared.js.map +1 -1
  54. package/dist/src/platforms/instagram/credential-manager.d.ts.map +1 -1
  55. package/dist/src/platforms/instagram/credential-manager.js +1 -1
  56. package/dist/src/platforms/instagram/credential-manager.js.map +1 -1
  57. package/dist/src/platforms/instagram/ensure-auth.d.ts.map +1 -1
  58. package/dist/src/platforms/instagram/ensure-auth.js.map +1 -1
  59. package/dist/src/platforms/instagram/token-extractor.d.ts +7 -19
  60. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
  61. package/dist/src/platforms/instagram/token-extractor.js +44 -270
  62. package/dist/src/platforms/instagram/token-extractor.js.map +1 -1
  63. package/dist/src/platforms/instagram/types.d.ts.map +1 -1
  64. package/dist/src/platforms/instagram/types.js +4 -2
  65. package/dist/src/platforms/instagram/types.js.map +1 -1
  66. package/dist/src/platforms/kakaotalk/auth/kakao-login.d.ts.map +1 -1
  67. package/dist/src/platforms/kakaotalk/auth/kakao-login.js +18 -4
  68. package/dist/src/platforms/kakaotalk/auth/kakao-login.js.map +1 -1
  69. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  70. package/dist/src/platforms/kakaotalk/client.js +3 -3
  71. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  72. package/dist/src/platforms/kakaotalk/commands/auth.d.ts.map +1 -1
  73. package/dist/src/platforms/kakaotalk/commands/auth.js +15 -9
  74. package/dist/src/platforms/kakaotalk/commands/auth.js.map +1 -1
  75. package/dist/src/platforms/kakaotalk/commands/shared.d.ts.map +1 -1
  76. package/dist/src/platforms/kakaotalk/commands/shared.js.map +1 -1
  77. package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
  78. package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
  79. package/dist/src/platforms/kakaotalk/protocol/crypto.js +1 -1
  80. package/dist/src/platforms/kakaotalk/protocol/crypto.js.map +1 -1
  81. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  82. package/dist/src/platforms/kakaotalk/protocol/session.js +1 -3
  83. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  84. package/dist/src/platforms/kakaotalk/token-extractor.js +5 -2
  85. package/dist/src/platforms/kakaotalk/token-extractor.js.map +1 -1
  86. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  87. package/dist/src/platforms/kakaotalk/types.js +4 -2
  88. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  89. package/dist/src/platforms/line/cli.d.ts.map +1 -1
  90. package/dist/src/platforms/line/cli.js +1 -4
  91. package/dist/src/platforms/line/cli.js.map +1 -1
  92. package/dist/src/platforms/line/client.d.ts.map +1 -1
  93. package/dist/src/platforms/line/client.js +5 -13
  94. package/dist/src/platforms/line/client.js.map +1 -1
  95. package/dist/src/platforms/line/commands/chat.d.ts.map +1 -1
  96. package/dist/src/platforms/line/commands/chat.js.map +1 -1
  97. package/dist/src/platforms/line/commands/message.d.ts.map +1 -1
  98. package/dist/src/platforms/line/commands/message.js.map +1 -1
  99. package/dist/src/platforms/line/listener.js +1 -1
  100. package/dist/src/platforms/line/listener.js.map +1 -1
  101. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  102. package/dist/src/platforms/slack/cli.js.map +1 -1
  103. package/dist/src/platforms/slack/client-mappers.d.ts +14 -0
  104. package/dist/src/platforms/slack/client-mappers.d.ts.map +1 -0
  105. package/dist/src/platforms/slack/client-mappers.js +245 -0
  106. package/dist/src/platforms/slack/client-mappers.js.map +1 -0
  107. package/dist/src/platforms/slack/client.d.ts +0 -1
  108. package/dist/src/platforms/slack/client.d.ts.map +1 -1
  109. package/dist/src/platforms/slack/client.js +41 -455
  110. package/dist/src/platforms/slack/client.js.map +1 -1
  111. package/dist/src/platforms/slack/commands/channel.d.ts.map +1 -1
  112. package/dist/src/platforms/slack/commands/channel.js.map +1 -1
  113. package/dist/src/platforms/slack/commands/emoji.d.ts.map +1 -1
  114. package/dist/src/platforms/slack/commands/emoji.js +1 -3
  115. package/dist/src/platforms/slack/commands/emoji.js.map +1 -1
  116. package/dist/src/platforms/slack/commands/message.d.ts.map +1 -1
  117. package/dist/src/platforms/slack/commands/message.js.map +1 -1
  118. package/dist/src/platforms/slack/commands/reminder.d.ts.map +1 -1
  119. package/dist/src/platforms/slack/commands/reminder.js.map +1 -1
  120. package/dist/src/platforms/slack/commands/user.d.ts.map +1 -1
  121. package/dist/src/platforms/slack/commands/user.js.map +1 -1
  122. package/dist/src/platforms/slack/commands/usergroup.d.ts.map +1 -1
  123. package/dist/src/platforms/slack/commands/usergroup.js.map +1 -1
  124. package/dist/src/platforms/slack/commands/whoami.d.ts.map +1 -1
  125. package/dist/src/platforms/slack/commands/whoami.js.map +1 -1
  126. package/dist/src/platforms/slack/token-extractor.d.ts +2 -6
  127. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  128. package/dist/src/platforms/slack/token-extractor.js +35 -229
  129. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  130. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  131. package/dist/src/platforms/slackbot/cli.js +1 -1
  132. package/dist/src/platforms/slackbot/cli.js.map +1 -1
  133. package/dist/src/platforms/teams/client.d.ts.map +1 -1
  134. package/dist/src/platforms/teams/client.js +1 -1
  135. package/dist/src/platforms/teams/client.js.map +1 -1
  136. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  137. package/dist/src/platforms/teams/commands/auth.js +4 -1
  138. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  139. package/dist/src/platforms/teams/commands/whoami.d.ts.map +1 -1
  140. package/dist/src/platforms/teams/commands/whoami.js.map +1 -1
  141. package/dist/src/platforms/teams/token-extractor.d.ts +6 -18
  142. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  143. package/dist/src/platforms/teams/token-extractor.js +71 -324
  144. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  145. package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
  146. package/dist/src/platforms/telegram/cli.js +1 -4
  147. package/dist/src/platforms/telegram/cli.js.map +1 -1
  148. package/dist/src/platforms/telegram/client.d.ts.map +1 -1
  149. package/dist/src/platforms/telegram/client.js.map +1 -1
  150. package/dist/src/platforms/telegram/commands/auth.d.ts.map +1 -1
  151. package/dist/src/platforms/telegram/commands/auth.js +1 -1
  152. package/dist/src/platforms/telegram/commands/auth.js.map +1 -1
  153. package/dist/src/platforms/telegram/commands/chat.d.ts.map +1 -1
  154. package/dist/src/platforms/telegram/commands/chat.js.map +1 -1
  155. package/dist/src/platforms/telegram/commands/message.d.ts.map +1 -1
  156. package/dist/src/platforms/telegram/commands/message.js.map +1 -1
  157. package/dist/src/platforms/telegram/commands/whoami.js +1 -1
  158. package/dist/src/platforms/telegram/commands/whoami.js.map +1 -1
  159. package/dist/src/platforms/telegram/credential-manager.d.ts.map +1 -1
  160. package/dist/src/platforms/telegram/credential-manager.js +6 -2
  161. package/dist/src/platforms/telegram/credential-manager.js.map +1 -1
  162. package/dist/src/platforms/telegram/my-telegram-org.js.map +1 -1
  163. package/dist/src/platforms/webex/cli.d.ts.map +1 -1
  164. package/dist/src/platforms/webex/cli.js +1 -4
  165. package/dist/src/platforms/webex/cli.js.map +1 -1
  166. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  167. package/dist/src/platforms/webex/client.js +3 -7
  168. package/dist/src/platforms/webex/client.js.map +1 -1
  169. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  170. package/dist/src/platforms/webex/commands/auth.js +1 -3
  171. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  172. package/dist/src/platforms/webex/commands/member.d.ts.map +1 -1
  173. package/dist/src/platforms/webex/commands/member.js +1 -3
  174. package/dist/src/platforms/webex/commands/member.js.map +1 -1
  175. package/dist/src/platforms/webex/commands/message.d.ts.map +1 -1
  176. package/dist/src/platforms/webex/commands/message.js.map +1 -1
  177. package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -1
  178. package/dist/src/platforms/webex/commands/snapshot.js.map +1 -1
  179. package/dist/src/platforms/webex/commands/space.d.ts.map +1 -1
  180. package/dist/src/platforms/webex/commands/space.js.map +1 -1
  181. package/dist/src/platforms/webex/commands/whoami.d.ts.map +1 -1
  182. package/dist/src/platforms/webex/commands/whoami.js.map +1 -1
  183. package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
  184. package/dist/src/platforms/webex/credential-manager.js.map +1 -1
  185. package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
  186. package/dist/src/platforms/webex/ensure-auth.js +1 -3
  187. package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
  188. package/dist/src/platforms/webex/index.d.ts +1 -1
  189. package/dist/src/platforms/webex/index.d.ts.map +1 -1
  190. package/dist/src/platforms/webex/index.js.map +1 -1
  191. package/dist/src/platforms/webex/markdown-to-html.js.map +1 -1
  192. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  193. package/dist/src/platforms/webex/token-extractor.js +5 -14
  194. package/dist/src/platforms/webex/token-extractor.js.map +1 -1
  195. package/dist/src/platforms/wechatbot/client.d.ts.map +1 -1
  196. package/dist/src/platforms/wechatbot/client.js.map +1 -1
  197. package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -1
  198. package/dist/src/platforms/wechatbot/commands/message.js.map +1 -1
  199. package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
  200. package/dist/src/platforms/whatsapp/cli.js +1 -4
  201. package/dist/src/platforms/whatsapp/cli.js.map +1 -1
  202. package/dist/src/platforms/whatsapp/commands/chat.d.ts.map +1 -1
  203. package/dist/src/platforms/whatsapp/commands/chat.js.map +1 -1
  204. package/dist/src/platforms/whatsapp/commands/message.d.ts.map +1 -1
  205. package/dist/src/platforms/whatsapp/commands/message.js.map +1 -1
  206. package/dist/src/platforms/whatsapp/commands/shared.d.ts.map +1 -1
  207. package/dist/src/platforms/whatsapp/commands/shared.js.map +1 -1
  208. package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
  209. package/dist/src/platforms/whatsapp/credential-manager.js +1 -1
  210. package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
  211. package/dist/src/platforms/whatsapp/ensure-auth.d.ts.map +1 -1
  212. package/dist/src/platforms/whatsapp/ensure-auth.js.map +1 -1
  213. package/dist/src/platforms/whatsappbot/client.d.ts.map +1 -1
  214. package/dist/src/platforms/whatsappbot/client.js +2 -2
  215. package/dist/src/platforms/whatsappbot/client.js.map +1 -1
  216. package/dist/src/platforms/whatsappbot/commands/auth.d.ts.map +1 -1
  217. package/dist/src/platforms/whatsappbot/commands/auth.js +4 -1
  218. package/dist/src/platforms/whatsappbot/commands/auth.js.map +1 -1
  219. package/dist/src/platforms/whatsappbot/commands/message.d.ts.map +1 -1
  220. package/dist/src/platforms/whatsappbot/commands/message.js.map +1 -1
  221. package/dist/src/platforms/whatsappbot/commands/shared.d.ts.map +1 -1
  222. package/dist/src/platforms/whatsappbot/commands/shared.js.map +1 -1
  223. package/dist/src/shared/chromium/browsers.d.ts +7 -0
  224. package/dist/src/shared/chromium/browsers.d.ts.map +1 -0
  225. package/dist/src/shared/chromium/browsers.js +89 -0
  226. package/dist/src/shared/chromium/browsers.js.map +1 -0
  227. package/dist/src/shared/chromium/cookie-reader.d.ts +20 -0
  228. package/dist/src/shared/chromium/cookie-reader.d.ts.map +1 -0
  229. package/dist/src/shared/chromium/cookie-reader.js +99 -0
  230. package/dist/src/shared/chromium/cookie-reader.js.map +1 -0
  231. package/dist/src/shared/chromium/decryptor.d.ts +42 -0
  232. package/dist/src/shared/chromium/decryptor.d.ts.map +1 -0
  233. package/dist/src/shared/chromium/decryptor.js +205 -0
  234. package/dist/src/shared/chromium/decryptor.js.map +1 -0
  235. package/dist/src/shared/chromium/index.d.ts +6 -0
  236. package/dist/src/shared/chromium/index.d.ts.map +1 -0
  237. package/dist/src/shared/chromium/index.js +4 -0
  238. package/dist/src/shared/chromium/index.js.map +1 -0
  239. package/dist/src/shared/chromium/types.d.ts +11 -0
  240. package/dist/src/shared/chromium/types.d.ts.map +1 -0
  241. package/dist/src/shared/chromium/types.js +2 -0
  242. package/dist/src/shared/chromium/types.js.map +1 -0
  243. package/dist/src/shared/utils/derived-key-cache.d.ts +1 -1
  244. package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
  245. package/dist/src/shared/utils/linux-keyring.js +4 -1
  246. package/dist/src/shared/utils/linux-keyring.js.map +1 -1
  247. package/dist/src/tui/adapters/kakaotalk-adapter.d.ts.map +1 -1
  248. package/dist/src/tui/adapters/kakaotalk-adapter.js +6 -1
  249. package/dist/src/tui/adapters/kakaotalk-adapter.js.map +1 -1
  250. package/dist/src/tui/adapters/telegram-adapter.js +1 -1
  251. package/dist/src/tui/adapters/telegram-adapter.js.map +1 -1
  252. package/dist/src/tui/adapters/webex-adapter.js +1 -1
  253. package/dist/src/tui/adapters/webex-adapter.js.map +1 -1
  254. package/dist/src/tui/app.d.ts.map +1 -1
  255. package/dist/src/tui/app.js +112 -23
  256. package/dist/src/tui/app.js.map +1 -1
  257. package/dist/src/tui/utils.d.ts.map +1 -1
  258. package/dist/src/tui/utils.js +11 -13
  259. package/dist/src/tui/utils.js.map +1 -1
  260. package/dist/src/tui/views/channel-picker.d.ts.map +1 -1
  261. package/dist/src/tui/views/channel-picker.js.map +1 -1
  262. package/dist/src/tui/views/workspace-picker.d.ts.map +1 -1
  263. package/dist/src/tui/views/workspace-picker.js.map +1 -1
  264. package/docs/content/docs/cli/channeltalk.mdx +24 -22
  265. package/docs/content/docs/cli/channeltalkbot.mdx +7 -7
  266. package/docs/content/docs/cli/instagram.mdx +4 -4
  267. package/docs/content/docs/cli/kakaotalk.mdx +9 -8
  268. package/docs/content/docs/cli/line.mdx +14 -14
  269. package/docs/content/docs/cli/webex.mdx +19 -19
  270. package/docs/content/docs/cli/wechatbot.mdx +12 -11
  271. package/docs/content/docs/cli/whatsapp.mdx +5 -4
  272. package/docs/content/docs/cli/whatsappbot.mdx +11 -11
  273. package/docs/content/docs/index.mdx +7 -7
  274. package/docs/content/docs/meta.json +1 -9
  275. package/docs/content/docs/sdk/channeltalk.mdx +5 -6
  276. package/docs/content/docs/sdk/channeltalkbot.mdx +6 -12
  277. package/docs/content/docs/sdk/discord.mdx +36 -43
  278. package/docs/content/docs/sdk/instagram.mdx +18 -18
  279. package/docs/content/docs/sdk/kakaotalk.mdx +27 -18
  280. package/docs/content/docs/sdk/line.mdx +8 -13
  281. package/docs/content/docs/sdk/meta.json +14 -1
  282. package/docs/content/docs/sdk/slack.mdx +36 -42
  283. package/docs/content/docs/sdk/teams.mdx +2 -8
  284. package/docs/content/docs/sdk/webex.mdx +2 -12
  285. package/docs/content/docs/sdk/wechatbot.mdx +1 -5
  286. package/docs/content/docs/sdk/whatsapp.mdx +10 -19
  287. package/docs/content/docs/sdk/whatsappbot.mdx +2 -10
  288. package/docs/content/docs/tui.mdx +23 -23
  289. package/docs/src/app/page.tsx +353 -108
  290. package/e2e/channeltalkbot.e2e.test.ts +1 -5
  291. package/e2e/config.ts +6 -2
  292. package/package.json +59 -58
  293. package/scripts/prepublish.ts +1 -3
  294. package/skills/agent-channeltalk/SKILL.md +1 -1
  295. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  296. package/skills/agent-discord/SKILL.md +1 -1
  297. package/skills/agent-discordbot/SKILL.md +1 -1
  298. package/skills/agent-instagram/SKILL.md +1 -1
  299. package/skills/agent-kakaotalk/SKILL.md +1 -1
  300. package/skills/agent-line/SKILL.md +1 -1
  301. package/skills/agent-slack/SKILL.md +1 -1
  302. package/skills/agent-slackbot/SKILL.md +1 -1
  303. package/skills/agent-teams/SKILL.md +1 -1
  304. package/skills/agent-telegram/SKILL.md +1 -1
  305. package/skills/agent-webex/SKILL.md +1 -1
  306. package/skills/agent-wechatbot/SKILL.md +1 -1
  307. package/skills/agent-whatsapp/SKILL.md +1 -1
  308. package/skills/agent-whatsappbot/SKILL.md +1 -1
  309. package/src/platforms/channeltalk/client.test.ts +116 -29
  310. package/src/platforms/channeltalk/client.ts +26 -6
  311. package/src/platforms/channeltalk/commands/auth.test.ts +5 -5
  312. package/src/platforms/channeltalk/commands/auth.ts +19 -5
  313. package/src/platforms/channeltalk/commands/message.test.ts +2 -6
  314. package/src/platforms/channeltalk/commands/message.ts +5 -1
  315. package/src/platforms/channeltalk/commands/snapshot.test.ts +19 -4
  316. package/src/platforms/channeltalk/commands/snapshot.ts +5 -1
  317. package/src/platforms/channeltalk/ensure-auth.test.ts +20 -17
  318. package/src/platforms/channeltalk/ensure-auth.ts +6 -7
  319. package/src/platforms/channeltalk/index.ts +0 -1
  320. package/src/platforms/channeltalk/token-extractor.test.ts +33 -25
  321. package/src/platforms/channeltalk/token-extractor.ts +120 -372
  322. package/src/platforms/channeltalkbot/client.test.ts +1 -3
  323. package/src/platforms/channeltalkbot/client.ts +39 -13
  324. package/src/platforms/channeltalkbot/commands/auth.test.ts +3 -1
  325. package/src/platforms/channeltalkbot/commands/auth.ts +4 -1
  326. package/src/platforms/channeltalkbot/commands/bot.test.ts +13 -5
  327. package/src/platforms/channeltalkbot/commands/message.test.ts +12 -6
  328. package/src/platforms/channeltalkbot/commands/shared.ts +6 -2
  329. package/src/platforms/channeltalkbot/commands/snapshot.test.ts +17 -5
  330. package/src/platforms/channeltalkbot/credential-manager.test.ts +1 -1
  331. package/src/platforms/channeltalkbot/index.test.ts +0 -2
  332. package/src/platforms/channeltalkbot/index.ts +0 -1
  333. package/src/platforms/discord/commands/auth.test.ts +6 -4
  334. package/src/platforms/discord/commands/auth.ts +14 -14
  335. package/src/platforms/discord/commands/whoami.test.ts +2 -4
  336. package/src/platforms/discord/commands/whoami.ts +2 -0
  337. package/src/platforms/discord/ensure-auth.test.ts +5 -3
  338. package/src/platforms/discord/index.ts +0 -1
  339. package/src/platforms/discord/listener.test.ts +7 -1
  340. package/src/platforms/discord/token-extractor.test.ts +18 -12
  341. package/src/platforms/discord/token-extractor.ts +46 -190
  342. package/src/platforms/discordbot/client.ts +1 -4
  343. package/src/platforms/discordbot/commands/auth.test.ts +3 -1
  344. package/src/platforms/discordbot/commands/channel.test.ts +3 -1
  345. package/src/platforms/discordbot/commands/message.test.ts +3 -1
  346. package/src/platforms/discordbot/commands/server.test.ts +3 -1
  347. package/src/platforms/discordbot/commands/snapshot.test.ts +3 -1
  348. package/src/platforms/discordbot/commands/thread.test.ts +3 -1
  349. package/src/platforms/discordbot/commands/user.test.ts +3 -1
  350. package/src/platforms/instagram/cli.ts +1 -4
  351. package/src/platforms/instagram/client.test.ts +3 -8
  352. package/src/platforms/instagram/client.ts +39 -34
  353. package/src/platforms/instagram/commands/auth.test.ts +13 -12
  354. package/src/platforms/instagram/commands/auth.ts +136 -71
  355. package/src/platforms/instagram/commands/chat.test.ts +21 -24
  356. package/src/platforms/instagram/commands/chat.ts +2 -0
  357. package/src/platforms/instagram/commands/message.test.ts +29 -24
  358. package/src/platforms/instagram/commands/message.ts +3 -4
  359. package/src/platforms/instagram/commands/shared.ts +2 -5
  360. package/src/platforms/instagram/commands/whoami.test.ts +4 -6
  361. package/src/platforms/instagram/credential-manager.ts +2 -6
  362. package/src/platforms/instagram/ensure-auth.test.ts +1 -4
  363. package/src/platforms/instagram/ensure-auth.ts +6 -3
  364. package/src/platforms/instagram/listener.test.ts +7 -3
  365. package/src/platforms/instagram/token-extractor.test.ts +4 -16
  366. package/src/platforms/instagram/token-extractor.ts +55 -309
  367. package/src/platforms/instagram/types.test.ts +2 -6
  368. package/src/platforms/instagram/types.ts +4 -2
  369. package/src/platforms/kakaotalk/auth/kakao-login.ts +30 -8
  370. package/src/platforms/kakaotalk/client.test.ts +37 -25
  371. package/src/platforms/kakaotalk/client.ts +23 -12
  372. package/src/platforms/kakaotalk/commands/auth.test.ts +6 -18
  373. package/src/platforms/kakaotalk/commands/auth.ts +101 -47
  374. package/src/platforms/kakaotalk/commands/chat.test.ts +8 -11
  375. package/src/platforms/kakaotalk/commands/message.test.ts +15 -24
  376. package/src/platforms/kakaotalk/commands/shared.ts +1 -0
  377. package/src/platforms/kakaotalk/commands/whoami.test.ts +6 -10
  378. package/src/platforms/kakaotalk/credential-manager.test.ts +1 -4
  379. package/src/platforms/kakaotalk/index.test.ts +1 -0
  380. package/src/platforms/kakaotalk/index.ts +0 -2
  381. package/src/platforms/kakaotalk/listener.test.ts +7 -1
  382. package/src/platforms/kakaotalk/protocol/connection.ts +4 -1
  383. package/src/platforms/kakaotalk/protocol/crypto.ts +1 -1
  384. package/src/platforms/kakaotalk/protocol/session.ts +12 -6
  385. package/src/platforms/kakaotalk/token-extractor.ts +5 -5
  386. package/src/platforms/kakaotalk/types.ts +8 -7
  387. package/src/platforms/line/cli.ts +1 -4
  388. package/src/platforms/line/client.ts +12 -20
  389. package/src/platforms/line/commands/auth.test.ts +2 -1
  390. package/src/platforms/line/commands/chat.test.ts +2 -1
  391. package/src/platforms/line/commands/chat.ts +1 -4
  392. package/src/platforms/line/commands/friend.test.ts +2 -1
  393. package/src/platforms/line/commands/message.test.ts +2 -1
  394. package/src/platforms/line/commands/message.ts +2 -9
  395. package/src/platforms/line/commands/whoami.test.ts +2 -1
  396. package/src/platforms/line/credential-manager.test.ts +1 -2
  397. package/src/platforms/line/index.test.ts +1 -0
  398. package/src/platforms/line/listener.ts +1 -1
  399. package/src/platforms/line/types.test.ts +1 -0
  400. package/src/platforms/slack/cli.ts +3 -1
  401. package/src/platforms/slack/client-mappers.ts +297 -0
  402. package/src/platforms/slack/client.test.ts +532 -17
  403. package/src/platforms/slack/client.ts +69 -458
  404. package/src/platforms/slack/commands/channel.ts +1 -4
  405. package/src/platforms/slack/commands/emoji.test.ts +6 -4
  406. package/src/platforms/slack/commands/emoji.ts +20 -22
  407. package/src/platforms/slack/commands/message.ts +6 -1
  408. package/src/platforms/slack/commands/pin.test.ts +14 -12
  409. package/src/platforms/slack/commands/reminder.ts +7 -6
  410. package/src/platforms/slack/commands/user.ts +6 -1
  411. package/src/platforms/slack/commands/usergroup.test.ts +3 -3
  412. package/src/platforms/slack/commands/usergroup.ts +10 -7
  413. package/src/platforms/slack/commands/whoami.test.ts +1 -1
  414. package/src/platforms/slack/commands/whoami.ts +2 -0
  415. package/src/platforms/slack/index.ts +0 -2
  416. package/src/platforms/slack/listener.test.ts +1 -0
  417. package/src/platforms/slack/token-extractor.test.ts +7 -12
  418. package/src/platforms/slack/token-extractor.ts +47 -255
  419. package/src/platforms/slackbot/cli.ts +8 -1
  420. package/src/platforms/slackbot/commands/auth.test.ts +3 -1
  421. package/src/platforms/teams/client.ts +1 -1
  422. package/src/platforms/teams/commands/auth.test.ts +1 -1
  423. package/src/platforms/teams/commands/auth.ts +4 -1
  424. package/src/platforms/teams/commands/whoami.test.ts +2 -4
  425. package/src/platforms/teams/commands/whoami.ts +2 -0
  426. package/src/platforms/teams/index.ts +0 -1
  427. package/src/platforms/teams/token-extractor.ts +82 -350
  428. package/src/platforms/telegram/app-config.test.ts +1 -0
  429. package/src/platforms/telegram/chat-utils.test.ts +5 -1
  430. package/src/platforms/telegram/cli.ts +2 -4
  431. package/src/platforms/telegram/client.test.ts +16 -3
  432. package/src/platforms/telegram/client.ts +14 -4
  433. package/src/platforms/telegram/commands/auth.test.ts +1 -0
  434. package/src/platforms/telegram/commands/auth.ts +3 -4
  435. package/src/platforms/telegram/commands/chat.test.ts +2 -5
  436. package/src/platforms/telegram/commands/chat.ts +1 -0
  437. package/src/platforms/telegram/commands/message.test.ts +2 -5
  438. package/src/platforms/telegram/commands/message.ts +1 -0
  439. package/src/platforms/telegram/commands/shared.test.ts +1 -0
  440. package/src/platforms/telegram/commands/whoami.test.ts +5 -7
  441. package/src/platforms/telegram/commands/whoami.ts +1 -1
  442. package/src/platforms/telegram/credential-manager.test.ts +1 -0
  443. package/src/platforms/telegram/credential-manager.ts +11 -2
  444. package/src/platforms/telegram/my-telegram-org.ts +6 -2
  445. package/src/platforms/telegram/types.test.ts +1 -0
  446. package/src/platforms/webex/app-config.test.ts +1 -0
  447. package/src/platforms/webex/cli.ts +1 -4
  448. package/src/platforms/webex/client.test.ts +4 -12
  449. package/src/platforms/webex/client.ts +14 -52
  450. package/src/platforms/webex/commands/auth.test.ts +7 -1
  451. package/src/platforms/webex/commands/auth.ts +12 -15
  452. package/src/platforms/webex/commands/member.test.ts +1 -3
  453. package/src/platforms/webex/commands/member.ts +14 -19
  454. package/src/platforms/webex/commands/message.ts +4 -15
  455. package/src/platforms/webex/commands/snapshot.test.ts +28 -3
  456. package/src/platforms/webex/commands/snapshot.ts +3 -3
  457. package/src/platforms/webex/commands/space.test.ts +3 -3
  458. package/src/platforms/webex/commands/space.ts +2 -9
  459. package/src/platforms/webex/commands/whoami.test.ts +12 -5
  460. package/src/platforms/webex/commands/whoami.ts +2 -0
  461. package/src/platforms/webex/credential-manager.ts +11 -2
  462. package/src/platforms/webex/ensure-auth.ts +1 -3
  463. package/src/platforms/webex/index.ts +1 -7
  464. package/src/platforms/webex/markdown-to-html.test.ts +6 -18
  465. package/src/platforms/webex/markdown-to-html.ts +8 -8
  466. package/src/platforms/webex/token-extractor.ts +6 -29
  467. package/src/platforms/wechatbot/client.test.ts +6 -2
  468. package/src/platforms/wechatbot/client.ts +6 -1
  469. package/src/platforms/wechatbot/commands/auth.test.ts +3 -7
  470. package/src/platforms/wechatbot/commands/message.test.ts +1 -4
  471. package/src/platforms/wechatbot/commands/message.ts +5 -1
  472. package/src/platforms/wechatbot/commands/template.test.ts +1 -4
  473. package/src/platforms/wechatbot/commands/user.test.ts +2 -7
  474. package/src/platforms/whatsapp/cli.ts +1 -4
  475. package/src/platforms/whatsapp/commands/auth.test.ts +19 -22
  476. package/src/platforms/whatsapp/commands/chat.test.ts +21 -24
  477. package/src/platforms/whatsapp/commands/chat.ts +2 -0
  478. package/src/platforms/whatsapp/commands/message.test.ts +22 -24
  479. package/src/platforms/whatsapp/commands/message.ts +3 -5
  480. package/src/platforms/whatsapp/commands/shared.ts +2 -5
  481. package/src/platforms/whatsapp/commands/whoami.test.ts +2 -2
  482. package/src/platforms/whatsapp/credential-manager.ts +2 -6
  483. package/src/platforms/whatsapp/ensure-auth.test.ts +1 -4
  484. package/src/platforms/whatsapp/ensure-auth.ts +14 -6
  485. package/src/platforms/whatsapp/index.ts +0 -2
  486. package/src/platforms/whatsappbot/client.test.ts +13 -7
  487. package/src/platforms/whatsappbot/client.ts +18 -4
  488. package/src/platforms/whatsappbot/commands/auth.ts +4 -1
  489. package/src/platforms/whatsappbot/commands/message.test.ts +12 -2
  490. package/src/platforms/whatsappbot/commands/message.ts +16 -3
  491. package/src/platforms/whatsappbot/commands/shared.ts +3 -1
  492. package/src/platforms/whatsappbot/commands/whoami.test.ts +1 -3
  493. package/src/platforms/whatsappbot/index.ts +0 -2
  494. package/src/shared/chromium/browsers.test.ts +274 -0
  495. package/src/shared/chromium/browsers.ts +86 -0
  496. package/src/shared/chromium/cookie-reader.test.ts +274 -0
  497. package/src/shared/chromium/cookie-reader.ts +111 -0
  498. package/src/shared/chromium/decryptor.test.ts +449 -0
  499. package/src/shared/chromium/decryptor.ts +227 -0
  500. package/src/shared/chromium/index.ts +11 -0
  501. package/src/shared/chromium/types.ts +11 -0
  502. package/src/shared/utils/derived-key-cache.ts +1 -1
  503. package/src/shared/utils/linux-keyring.ts +4 -4
  504. package/src/tui/adapters/kakaotalk-adapter.ts +6 -1
  505. package/src/tui/adapters/telegram-adapter.ts +1 -1
  506. package/src/tui/adapters/webex-adapter.ts +1 -1
  507. package/src/tui/app.ts +149 -59
  508. package/src/tui/utils.test.ts +144 -145
  509. package/src/tui/utils.ts +27 -29
  510. package/src/tui/views/channel-picker.ts +1 -1
  511. package/src/tui/views/workspace-picker.ts +1 -1
@@ -3,7 +3,14 @@
3
3
  import { Command } from 'commander'
4
4
 
5
5
  import pkg from '../../../package.json' with { type: 'json' }
6
- import { authCommand, channelCommand, messageCommand, reactionCommand, userCommand, whoamiCommand } from './commands/index'
6
+ import {
7
+ authCommand,
8
+ channelCommand,
9
+ messageCommand,
10
+ reactionCommand,
11
+ userCommand,
12
+ whoamiCommand,
13
+ } from './commands/index'
7
14
 
8
15
  const program = new Command()
9
16
 
@@ -17,7 +17,9 @@ const mockTestAuth = mock(() =>
17
17
 
18
18
  mock.module('../client', () => ({
19
19
  SlackBotClient: class MockSlackBotClient {
20
- async login(_credentials?: any) { return this }
20
+ async login(_credentials?: any) {
21
+ return this
22
+ }
21
23
  testAuth = mockTestAuth
22
24
  },
23
25
  }))
@@ -1,9 +1,9 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { basename } from 'node:path'
3
3
 
4
+ import { TeamsCredentialManager } from './credential-manager'
4
5
  import type { TeamsChannel, TeamsFile, TeamsMessage, TeamsTeam, TeamsUser } from './types'
5
6
  import { TeamsError } from './types'
6
- import { TeamsCredentialManager } from './credential-manager'
7
7
 
8
8
  interface RateLimitBucket {
9
9
  remaining: number
@@ -1,9 +1,9 @@
1
1
  import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
2
 
3
- import { getNoTeamsTokenFoundMessage } from './auth'
4
3
  import { TeamsClient } from '../client'
5
4
  import { TeamsCredentialManager } from '../credential-manager'
6
5
  import { TeamsTokenExtractor } from '../token-extractor'
6
+ import { getNoTeamsTokenFoundMessage } from './auth'
7
7
 
8
8
  let extractorExtractSpy: ReturnType<typeof spyOn>
9
9
  let clientTestAuthSpy: ReturnType<typeof spyOn>
@@ -292,7 +292,10 @@ export async function statusAction(options: { pretty?: boolean }): Promise<void>
292
292
 
293
293
  if (!isExpired) {
294
294
  try {
295
- const client = await new TeamsClient().login({ token: account.token, tokenExpiresAt: account.token_expires_at ?? undefined })
295
+ const client = await new TeamsClient().login({
296
+ token: account.token,
297
+ tokenExpiresAt: account.token_expires_at ?? undefined,
298
+ })
296
299
  const authInfo = await client.testAuth()
297
300
  displayName = authInfo.displayName
298
301
  valid = true
@@ -1,8 +1,8 @@
1
1
  import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
2
 
3
3
  import { TeamsClient } from '@/platforms/teams/client'
4
- import { TeamsCredentialManager } from '@/platforms/teams/credential-manager'
5
4
  import { whoamiAction, whoamiCommand } from '@/platforms/teams/commands/whoami'
5
+ import { TeamsCredentialManager } from '@/platforms/teams/credential-manager'
6
6
 
7
7
  let credManagerSpy: ReturnType<typeof spyOn>
8
8
  let clientTestAuthSpy: ReturnType<typeof spyOn>
@@ -76,8 +76,6 @@ test('whoami exits with error when not authenticated', async () => {
76
76
 
77
77
  await whoamiAction({})
78
78
 
79
- expect(consoleLogSpy).toHaveBeenCalledWith(
80
- JSON.stringify({ error: 'Not authenticated. Run "auth extract" first.' }),
81
- )
79
+ expect(consoleLogSpy).toHaveBeenCalledWith(JSON.stringify({ error: 'Not authenticated. Run "auth extract" first.' }))
82
80
  expect(processExitSpy).toHaveBeenCalledWith(1)
83
81
  })
@@ -1,6 +1,8 @@
1
1
  import { Command } from 'commander'
2
+
2
3
  import { handleError } from '@/shared/utils/error-handler'
3
4
  import { formatOutput } from '@/shared/utils/output'
5
+
4
6
  import { TeamsClient } from '../client'
5
7
  import { TeamsCredentialManager } from '../credential-manager'
6
8
 
@@ -27,4 +27,3 @@ export {
27
27
  TeamsTeamSchema,
28
28
  TeamsUserSchema,
29
29
  } from './types'
30
-
@@ -1,16 +1,22 @@
1
1
  import { execSync } from 'node:child_process'
2
- import { createDecipheriv, pbkdf2Sync } from 'node:crypto'
3
- import { copyFileSync, existsSync, readFileSync, readdirSync, unlinkSync } from 'node:fs'
4
- import { createRequire } from 'node:module'
5
- import { homedir, tmpdir } from 'node:os'
2
+ import { existsSync } from 'node:fs'
3
+ import { homedir } from 'node:os'
6
4
  import { join } from 'node:path'
7
5
 
6
+ import {
7
+ BROWSER_KEYCHAIN_VARIANTS,
8
+ CHROMIUM_BROWSERS,
9
+ ChromiumCookieDecryptor,
10
+ ChromiumCookieReader,
11
+ discoverBrowserProfileDirs,
12
+ findLocalStatePath,
13
+ getBrowserBasePath,
14
+ } from '@/shared/chromium'
15
+ import type { KeychainVariant } from '@/shared/chromium'
8
16
  import { DerivedKeyCache } from '@/shared/utils/derived-key-cache'
9
17
 
10
18
  import type { TeamsAccountType } from './types'
11
19
 
12
- const require = createRequire(import.meta.url)
13
-
14
20
  export interface ExtractedTeamsToken {
15
21
  token: string
16
22
  accountType: TeamsAccountType
@@ -21,18 +27,6 @@ interface TeamsCookiePath {
21
27
  accountType: TeamsAccountType
22
28
  }
23
29
 
24
- interface KeychainVariant {
25
- service: string
26
- account: string
27
- }
28
-
29
- interface BrowserConfig {
30
- name: string
31
- darwin: string
32
- linux: string
33
- win32: string
34
- }
35
-
36
30
  const TEAMS_PROCESS_NAMES: Record<string, string> = {
37
31
  darwin: 'Microsoft Teams',
38
32
  win32: 'Teams.exe',
@@ -48,59 +42,31 @@ const TEAMS_HOST_PATTERNS = [
48
42
  '.microsoft.com',
49
43
  ]
50
44
 
51
- const BROWSERS: BrowserConfig[] = [
52
- {
53
- name: 'Chrome',
54
- darwin: join('Google', 'Chrome'),
55
- linux: 'google-chrome',
56
- win32: join('Google', 'Chrome', 'User Data'),
57
- },
58
- {
59
- name: 'Chrome Canary',
60
- darwin: join('Google', 'Chrome Canary'),
61
- linux: 'google-chrome-unstable',
62
- win32: join('Google', 'Chrome SxS', 'User Data'),
63
- },
64
- {
65
- name: 'Edge',
66
- darwin: 'Microsoft Edge',
67
- linux: 'microsoft-edge',
68
- win32: join('Microsoft', 'Edge', 'User Data'),
69
- },
70
- {
71
- name: 'Arc',
72
- darwin: join('Arc', 'User Data'),
73
- linux: '',
74
- win32: join('Arc', 'User Data'),
75
- },
76
- {
77
- name: 'Brave',
78
- darwin: join('BraveSoftware', 'Brave-Browser'),
79
- linux: join('BraveSoftware', 'Brave-Browser'),
80
- win32: join('BraveSoftware', 'Brave-Browser', 'User Data'),
81
- },
45
+ const TEAMS_KEYCHAIN_VARIANTS: KeychainVariant[] = [
46
+ { service: 'Microsoft Teams Safe Storage', account: 'Microsoft Teams' },
82
47
  {
83
- name: 'Vivaldi',
84
- darwin: 'Vivaldi',
85
- linux: 'vivaldi',
86
- win32: join('Vivaldi', 'User Data'),
87
- },
88
- {
89
- name: 'Chromium',
90
- darwin: 'Chromium',
91
- linux: 'chromium',
92
- win32: join('Chromium', 'User Data'),
48
+ service: 'Microsoft Teams (work or school) Safe Storage',
49
+ account: 'Microsoft Teams (work or school)',
93
50
  },
51
+ { service: 'Teams Safe Storage', account: 'Teams' },
94
52
  ]
95
53
 
96
54
  export class TeamsTokenExtractor {
97
55
  private platform: NodeJS.Platform
98
- private keyCache: DerivedKeyCache
99
- private cachedKey: Buffer | null = null
56
+ private decryptor: ChromiumCookieDecryptor
57
+ private cookieReader: ChromiumCookieReader
100
58
 
101
59
  constructor(platform?: NodeJS.Platform, keyCache?: DerivedKeyCache) {
102
60
  this.platform = platform ?? process.platform
103
- this.keyCache = keyCache ?? new DerivedKeyCache()
61
+
62
+ const resolvedKeyCache = keyCache ?? new DerivedKeyCache()
63
+ this.decryptor = new ChromiumCookieDecryptor({
64
+ platform: this.platform,
65
+ appKeychainVariants: TEAMS_KEYCHAIN_VARIANTS,
66
+ keyCache: resolvedKeyCache,
67
+ keyCachePlatform: 'teams',
68
+ })
69
+ this.cookieReader = new ChromiumCookieReader()
104
70
  }
105
71
 
106
72
  getDesktopCookiesPaths(): TeamsCookiePath[] {
@@ -162,12 +128,11 @@ export class TeamsTokenExtractor {
162
128
  getBrowserCookiesPaths(): TeamsCookiePath[] {
163
129
  const paths: TeamsCookiePath[] = []
164
130
 
165
- for (const browser of BROWSERS) {
166
- const browserBase = this.getBrowserBasePath(browser)
131
+ for (const browser of CHROMIUM_BROWSERS) {
132
+ const browserBase = getBrowserBasePath(browser, this.platform)
167
133
  if (!browserBase) continue
168
134
 
169
- const profileDirs = this.discoverProfileDirs(browserBase)
170
- for (const profileDir of profileDirs) {
135
+ for (const profileDir of discoverBrowserProfileDirs(browserBase)) {
171
136
  paths.push({ path: join(profileDir, 'Cookies'), accountType: 'work' })
172
137
  paths.push({ path: join(profileDir, 'Network', 'Cookies'), accountType: 'work' })
173
138
  }
@@ -177,63 +142,7 @@ export class TeamsTokenExtractor {
177
142
  }
178
143
 
179
144
  getTeamsCookiesPaths(): TeamsCookiePath[] {
180
- const desktopPaths = this.getDesktopCookiesPaths()
181
- const browserPaths = this.getBrowserCookiesPaths()
182
- return [...desktopPaths, ...browserPaths]
183
- }
184
-
185
- private getBrowserBasePath(browser: BrowserConfig): string | null {
186
- let relative: string
187
-
188
- switch (this.platform) {
189
- case 'darwin':
190
- relative = browser.darwin
191
- if (!relative) return null
192
- return join(homedir(), 'Library', 'Application Support', relative)
193
- case 'linux':
194
- relative = browser.linux
195
- if (!relative) return null
196
- return join(homedir(), '.config', relative)
197
- case 'win32':
198
- relative = browser.win32
199
- if (!relative) return null
200
- return join(
201
- process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'),
202
- relative,
203
- )
204
- default:
205
- return null
206
- }
207
- }
208
-
209
- private discoverProfileDirs(browserBase: string): string[] {
210
- const dirs: string[] = []
211
-
212
- dirs.push(join(browserBase, 'Default'))
213
-
214
- if (!existsSync(browserBase)) return dirs
215
-
216
- try {
217
- const entries = readdirSync(browserBase, { withFileTypes: true })
218
- for (const entry of entries) {
219
- if (!entry.isDirectory()) continue
220
- if (!/^Profile \d+$/i.test(entry.name)) continue
221
- dirs.push(join(browserBase, entry.name))
222
- }
223
- } catch {}
224
-
225
- return dirs
226
- }
227
-
228
- private findLocalStateForCookiePath(cookiePath: string): string | null {
229
- const parts = cookiePath.split(/[/\\]/)
230
- for (let levels = 2; levels <= 4; levels++) {
231
- if (parts.length < levels) break
232
- const base = parts.slice(0, parts.length - levels).join('/')
233
- const candidate = join(base, 'Local State')
234
- if (existsSync(candidate)) return candidate
235
- }
236
- return null
145
+ return [...this.getDesktopCookiesPaths(), ...this.getBrowserCookiesPaths()]
237
146
  }
238
147
 
239
148
  getLocalStatePath(): string {
@@ -264,65 +173,33 @@ export class TeamsTokenExtractor {
264
173
  }
265
174
 
266
175
  getKeychainVariants(): KeychainVariant[] {
267
- return [
268
- // Teams-specific keychain entries
269
- { service: 'Microsoft Teams Safe Storage', account: 'Microsoft Teams' },
270
- {
271
- service: 'Microsoft Teams (work or school) Safe Storage',
272
- account: 'Microsoft Teams (work or school)',
273
- },
274
- { service: 'Microsoft Edge Safe Storage', account: 'Microsoft Edge' },
275
- { service: 'Teams Safe Storage', account: 'Teams' },
276
- // Browser keychain entries for fallback
277
- { service: 'Chrome Safe Storage', account: 'Chrome' },
278
- { service: 'Chrome Canary Safe Storage', account: 'Chrome Canary' },
279
- { service: 'Arc Safe Storage', account: 'Arc' },
280
- { service: 'Brave Safe Storage', account: 'Brave' },
281
- { service: 'Vivaldi Safe Storage', account: 'Vivaldi' },
282
- { service: 'Chromium Safe Storage', account: 'Chromium' },
283
- ]
176
+ return [...TEAMS_KEYCHAIN_VARIANTS, ...BROWSER_KEYCHAIN_VARIANTS]
284
177
  }
285
178
 
286
179
  isValidSkypeToken(token: string): boolean {
287
180
  if (!token || token.length === 0) return false
288
- // Skype tokens are typically JWT format or long base64 strings (50+ chars)
289
181
  return token.length >= 50
290
182
  }
291
183
 
292
184
  isEncryptedValue(value: Buffer): boolean {
293
- if (!value || value.length < 4) return false
294
- const prefix = value.subarray(0, 3).toString('utf8')
295
- return prefix === 'v10' || prefix === 'v11'
185
+ return this.decryptor.isEncryptedValue(value)
296
186
  }
297
187
 
298
188
  async extract(): Promise<ExtractedTeamsToken[]> {
299
- await this.loadCachedKey()
189
+ await this.decryptor.loadCachedKey()
300
190
  return this.extractFromCookiesDB()
301
191
  }
302
192
 
303
- private async loadCachedKey(): Promise<void> {
304
- if (this.platform !== 'darwin') return
305
-
306
- const cached = await this.keyCache.get('teams')
307
- if (cached) {
308
- this.cachedKey = cached
309
- }
310
- }
311
-
312
193
  async clearKeyCache(): Promise<void> {
313
- await this.keyCache.clear('teams')
314
- this.cachedKey = null
194
+ await this.decryptor.clearKeyCache()
315
195
  }
316
196
 
317
197
  private async extractFromCookiesDB(): Promise<ExtractedTeamsToken[]> {
318
- const cookiePaths = this.getTeamsCookiesPaths()
319
198
  const results: ExtractedTeamsToken[] = []
320
199
  const seenAccountTypes = new Set<TeamsAccountType>()
321
200
 
322
- for (const { path: dbPath, accountType } of cookiePaths) {
323
- if (!dbPath || !existsSync(dbPath)) continue
324
- // Skip fallback paths if we already have a token for this account type
325
- if (seenAccountTypes.has(accountType)) continue
201
+ for (const { path: dbPath, accountType } of this.getTeamsCookiesPaths()) {
202
+ if (!dbPath || !existsSync(dbPath) || seenAccountTypes.has(accountType)) continue
326
203
 
327
204
  const token = await this.copyAndExtract(dbPath)
328
205
  if (token && this.isValidSkypeToken(token)) {
@@ -335,37 +212,18 @@ export class TeamsTokenExtractor {
335
212
  }
336
213
 
337
214
  private async copyAndExtract(dbPath: string): Promise<string | null> {
338
- const tempPath = join(tmpdir(), `teams-cookies-${Date.now()}`)
215
+ let tempPath = dbPath
339
216
 
340
217
  try {
341
- this.copyDatabaseToTemp(dbPath, tempPath)
342
- // For Windows: find the Local State relative to the cookie path so browser cookies
343
- // use the browser's own Local State instead of the Teams app Local State.
218
+ tempPath = this.copyDatabaseToTemp(dbPath, dbPath)
344
219
  const localStatePath =
345
- this.platform === 'win32'
346
- ? (this.findLocalStateForCookiePath(dbPath) ?? this.getLocalStatePath())
347
- : undefined
348
- const token = await this.extractFromSQLite(tempPath, localStatePath)
349
- this.cleanupTempFile(tempPath)
350
- return token
351
- } catch {
352
- this.cleanupTempFile(tempPath)
353
- return null
354
- }
355
- }
356
-
357
- private copyDatabaseToTemp(sourcePath: string, destPath: string): string {
358
- copyFileSync(sourcePath, destPath)
359
- return destPath
360
- }
220
+ this.platform === 'win32' ? (findLocalStatePath(dbPath) ?? this.getLocalStatePath()) : undefined
361
221
 
362
- private cleanupTempFile(tempPath: string): void {
363
- try {
364
- if (existsSync(tempPath)) {
365
- unlinkSync(tempPath)
366
- }
222
+ return await this.extractFromSQLite(tempPath, localStatePath)
367
223
  } catch {
368
- // Ignore cleanup errors
224
+ return null
225
+ } finally {
226
+ this.cleanupTempFile(tempPath)
369
227
  }
370
228
  }
371
229
 
@@ -382,25 +240,14 @@ export class TeamsTokenExtractor {
382
240
 
383
241
  type CookieRow = { encrypted_value?: Uint8Array | Buffer } | null
384
242
 
385
- let row: CookieRow
386
- if (typeof globalThis.Bun !== 'undefined') {
387
- const { Database } = require('bun:sqlite')
388
- const db = new Database(dbPath, { readonly: true })
389
- row = db.query(sql).get() as CookieRow
390
- db.close()
391
- } else {
392
- const Database = require('better-sqlite3')
393
- const db = new Database(dbPath, { readonly: true })
394
- row = db.prepare(sql).get() as CookieRow
395
- db.close()
396
- }
397
-
398
- if (row?.encrypted_value) {
399
- const decrypted = this.decryptCookie(Buffer.from(row.encrypted_value), localStatePath)
400
- if (decrypted && this.isValidSkypeToken(decrypted)) {
401
- return decrypted
402
- }
403
- }
243
+ const row = await this.cookieReader.queryFirst<CookieRow>(dbPath, sql)
244
+ if (!row?.encrypted_value) continue
245
+
246
+ const decryptedBuf = this.decryptor.decryptCookieRaw(Buffer.from(row.encrypted_value), localStatePath)
247
+ if (!decryptedBuf) continue
248
+
249
+ const token = this.postProcessDecrypted(decryptedBuf)
250
+ if (this.isValidSkypeToken(token)) return token
404
251
  }
405
252
 
406
253
  return null
@@ -409,85 +256,33 @@ export class TeamsTokenExtractor {
409
256
  }
410
257
  }
411
258
 
412
- private decryptCookie(encryptedValue: Buffer, localStatePath?: string): string | null {
413
- if (!this.isEncryptedValue(encryptedValue)) {
414
- // Not encrypted, return as-is
415
- return encryptedValue.toString('utf8')
416
- }
417
-
418
- if (this.platform === 'win32') {
419
- return this.decryptWindowsCookie(encryptedValue, localStatePath)
420
- } else if (this.platform === 'darwin') {
421
- return this.decryptMacCookie(encryptedValue)
422
- } else if (this.platform === 'linux') {
423
- return this.decryptLinuxCookie(encryptedValue)
424
- }
425
-
426
- return null
427
- }
428
-
429
- private decryptWindowsCookie(encryptedData: Buffer, localStatePath?: string): string | null {
430
- try {
431
- const statePath = localStatePath ?? this.getLocalStatePath()
432
- if (!existsSync(statePath)) return null
259
+ private postProcessDecrypted(raw: Buffer): string {
260
+ const stripped = ChromiumCookieDecryptor.stripIntegrityHash(raw)
261
+ if (stripped !== raw) return stripped.toString('utf8')
433
262
 
434
- const localState = JSON.parse(readFileSync(statePath, 'utf8'))
435
- const encryptedKey = Buffer.from(localState.os_crypt.encrypted_key, 'base64')
263
+ const str = raw.toString('utf8')
436
264
 
437
- // Remove DPAPI prefix (5 bytes)
438
- const dpapiBlobKey = encryptedKey.subarray(5)
439
- const masterKey = this.decryptDPAPI(dpapiBlobKey)
440
- if (!masterKey) return null
265
+ const jwtStart = str.indexOf('eyJ')
266
+ if (jwtStart > 0 && jwtStart <= 32) return str.substring(jwtStart)
441
267
 
442
- return this.decryptAESGCM(encryptedData, masterKey)
443
- } catch {
444
- return null
268
+ if (str.length > 32) {
269
+ const possibleToken = str.substring(32)
270
+ if (possibleToken.length > 50 && /^[A-Za-z0-9._-]+$/.test(possibleToken.substring(0, 50))) {
271
+ return possibleToken
272
+ }
445
273
  }
446
- }
447
274
 
448
- private decryptDPAPI(encryptedBlob: Buffer): Buffer | null {
449
- try {
450
- const b64 = encryptedBlob.toString('base64')
451
- const psScript = `
452
- Add-Type -AssemblyName System.Security
453
- $bytes = [Convert]::FromBase64String('${b64}')
454
- $decrypted = [Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, 'CurrentUser')
455
- [Convert]::ToBase64String($decrypted)
456
- `.replace(/\n/g, ' ')
457
-
458
- const result = execSync(`powershell -Command "${psScript}"`, { encoding: 'utf8' })
459
- return Buffer.from(result.trim(), 'base64')
460
- } catch {
461
- return null
462
- }
275
+ return str
463
276
  }
464
277
 
465
- private decryptMacCookie(encryptedData: Buffer): string | null {
466
- if (this.cachedKey) {
467
- const decrypted = this.decryptAESCBC(encryptedData, this.cachedKey)
468
- if (decrypted) return decrypted
469
- }
470
-
471
- for (const variant of this.getKeychainVariants()) {
472
- const password = this.execSecurityCommand(variant.service, variant.account)
473
- if (!password) continue
474
-
475
- const key = pbkdf2Sync(password, 'saltysalt', 1003, 16, 'sha1')
476
- const decrypted = this.decryptAESCBC(encryptedData, key)
477
- if (decrypted) {
478
- this.cachedKey = key
479
- this.keyCache.set('teams', key).catch(() => {})
480
- return decrypted
481
- }
482
- }
483
-
484
- return null
278
+ private copyDatabaseToTemp(sourcePath: string, _destPath: string): string {
279
+ return sourcePath
485
280
  }
486
281
 
487
- private decryptLinuxCookie(encryptedData: Buffer): string | null {
488
- // Linux uses a hardcoded password 'peanuts' for Chromium-based apps
489
- const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
490
- return this.decryptAESCBC(encryptedData, key)
282
+ private cleanupTempFile(_tempPath: string): void {}
283
+
284
+ private decryptAESGCM(encryptedData: Buffer, key: Buffer): string | null {
285
+ return this.decryptor.decryptAESGCM(encryptedData, key)
491
286
  }
492
287
 
493
288
  private getKeychainPassword(): string | null {
@@ -495,88 +290,25 @@ export class TeamsTokenExtractor {
495
290
  const password = this.execSecurityCommand(variant.service, variant.account)
496
291
  if (password) return password
497
292
  }
293
+
498
294
  return null
499
295
  }
500
296
 
501
297
  private execSecurityCommand(service: string, account: string): string | null {
502
298
  try {
503
- // Escape double quotes in service/account to prevent command injection
504
299
  const safeService = service.replace(/"/g, '\\"')
505
300
  const safeAccount = account.replace(/"/g, '\\"')
506
- const result = execSync(
507
- `security find-generic-password -s "${safeService}" -a "${safeAccount}" -w 2>/dev/null`,
508
- { encoding: 'utf8' },
509
- )
510
- return result.trim()
511
- } catch {
512
- return null
513
- }
514
- }
515
-
516
- private decryptAESCBC(encryptedData: Buffer, key: Buffer): string | null {
517
- try {
518
- const ciphertext = encryptedData.subarray(3)
519
- const iv = Buffer.alloc(16, 0x20)
520
-
521
- const decipher = createDecipheriv('aes-128-cbc', key, iv)
522
- decipher.setAutoPadding(true)
523
-
524
- const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
525
-
526
- // Chromium v130+ prepends a 32-byte integrity hash before the actual cookie value.
527
- // Detect by checking if the first bytes contain non-printable characters.
528
- if (decrypted.length > 32) {
529
- const hasNonPrintablePrefix = decrypted.subarray(0, 32).some((b) => b < 0x20 || b > 0x7e)
530
- if (hasNonPrintablePrefix) {
531
- return decrypted.subarray(32).toString('utf8')
532
- }
533
- }
534
-
535
- const decryptedStr = decrypted.toString('utf8')
536
-
537
- // Chromium v24+ prepends a 32-byte integrity hash before the actual value
538
- // Look for JWT token start (eyJ) or other token patterns
539
- const jwtStart = decryptedStr.indexOf('eyJ')
540
- if (jwtStart > 0 && jwtStart <= 32) {
541
- return decryptedStr.substring(jwtStart)
542
- }
543
-
544
- // If no JWT prefix found but decryption succeeded, check if first 32 bytes are garbage
545
- if (decrypted.length > 32) {
546
- const possibleToken = decryptedStr.substring(32)
547
- if (possibleToken.length > 50 && /^[A-Za-z0-9._-]+$/.test(possibleToken.substring(0, 50))) {
548
- return possibleToken
549
- }
550
- }
551
-
552
- return decryptedStr
553
- } catch {
554
- return null
555
- }
556
- }
557
-
558
- private decryptAESGCM(encryptedData: Buffer, key: Buffer): string | null {
559
- try {
560
- // Format: v10 (3 bytes) + IV (12 bytes) + ciphertext + auth tag (16 bytes)
561
- if (encryptedData.length < 3 + 12 + 16) return null
562
-
563
- const iv = encryptedData.subarray(3, 15)
564
- const authTag = encryptedData.subarray(-16)
565
- const ciphertext = encryptedData.subarray(15, -16)
566
-
567
- const decipher = createDecipheriv('aes-256-gcm', key, iv)
568
- decipher.setAuthTag(authTag)
569
-
570
- const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
571
- return decrypted.toString('utf8')
301
+ const result = execSync(`security find-generic-password -s "${safeService}" -a "${safeAccount}" -w 2>/dev/null`, {
302
+ encoding: 'utf8',
303
+ })
304
+ return result.trim() || null
572
305
  } catch {
573
306
  return null
574
307
  }
575
308
  }
576
309
 
577
310
  async isTeamsRunning(): Promise<boolean> {
578
- const processName = this.getProcessName()
579
- return this.checkProcessRunning(processName)
311
+ return this.checkProcessRunning(this.getProcessName())
580
312
  }
581
313
 
582
314
  private getProcessName(): string {
@@ -590,12 +322,12 @@ export class TeamsTokenExtractor {
590
322
  encoding: 'utf8',
591
323
  })
592
324
  return result.toLowerCase().includes(processName.toLowerCase())
593
- } else {
594
- const result = execSync(`pgrep -f "${processName}" 2>/dev/null || true`, {
595
- encoding: 'utf8',
596
- })
597
- return result.trim().length > 0
598
325
  }
326
+
327
+ const result = execSync(`pgrep -f "${processName}" 2>/dev/null || true`, {
328
+ encoding: 'utf8',
329
+ })
330
+ return result.trim().length > 0
599
331
  } catch {
600
332
  return false
601
333
  }
@@ -1,4 +1,5 @@
1
1
  import { beforeEach, describe, expect, test } from 'bun:test'
2
+
2
3
  import { getTelegramAppCredentials } from './app-config'
3
4
 
4
5
  const ENV_KEYS = [