agent-messenger 2.10.2 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.env.template +4 -1
  3. package/README.md +77 -27
  4. package/bun.lock +26 -0
  5. package/dist/package.json +21 -1
  6. package/dist/src/platforms/channeltalk/commands/auth.d.ts +2 -1
  7. package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
  8. package/dist/src/platforms/channeltalk/commands/auth.js +5 -3
  9. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  10. package/dist/src/platforms/channeltalk/token-extractor.d.ts +2 -1
  11. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  12. package/dist/src/platforms/channeltalk/token-extractor.js +22 -6
  13. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  14. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  15. package/dist/src/platforms/channeltalkbot/cli.js +11 -1
  16. package/dist/src/platforms/channeltalkbot/cli.js.map +1 -1
  17. package/dist/src/platforms/channeltalkbot/commands/auth.d.ts.map +1 -1
  18. package/dist/src/platforms/channeltalkbot/commands/auth.js +1 -5
  19. package/dist/src/platforms/channeltalkbot/commands/auth.js.map +1 -1
  20. package/dist/src/platforms/channeltalkbot/commands/bot.d.ts.map +1 -1
  21. package/dist/src/platforms/channeltalkbot/commands/bot.js +1 -6
  22. package/dist/src/platforms/channeltalkbot/commands/bot.js.map +1 -1
  23. package/dist/src/platforms/channeltalkbot/commands/chat.d.ts.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/commands/chat.js +1 -6
  25. package/dist/src/platforms/channeltalkbot/commands/chat.js.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/commands/group.d.ts.map +1 -1
  27. package/dist/src/platforms/channeltalkbot/commands/group.js +1 -6
  28. package/dist/src/platforms/channeltalkbot/commands/group.js.map +1 -1
  29. package/dist/src/platforms/channeltalkbot/commands/manager.d.ts.map +1 -1
  30. package/dist/src/platforms/channeltalkbot/commands/manager.js +1 -6
  31. package/dist/src/platforms/channeltalkbot/commands/manager.js.map +1 -1
  32. package/dist/src/platforms/channeltalkbot/commands/message.d.ts.map +1 -1
  33. package/dist/src/platforms/channeltalkbot/commands/message.js +1 -6
  34. package/dist/src/platforms/channeltalkbot/commands/message.js.map +1 -1
  35. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts.map +1 -1
  36. package/dist/src/platforms/channeltalkbot/commands/whoami.js +1 -6
  37. package/dist/src/platforms/channeltalkbot/commands/whoami.js.map +1 -1
  38. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts +5 -0
  39. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
  40. package/dist/src/platforms/channeltalkbot/credential-manager.js +34 -4
  41. package/dist/src/platforms/channeltalkbot/credential-manager.js.map +1 -1
  42. package/dist/src/platforms/discord/commands/auth.d.ts +1 -0
  43. package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
  44. package/dist/src/platforms/discord/commands/auth.js +3 -1
  45. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  46. package/dist/src/platforms/discord/listener.d.ts +2 -0
  47. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  48. package/dist/src/platforms/discord/listener.js +51 -21
  49. package/dist/src/platforms/discord/listener.js.map +1 -1
  50. package/dist/src/platforms/discord/token-extractor.d.ts +2 -1
  51. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  52. package/dist/src/platforms/discord/token-extractor.js +21 -6
  53. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  54. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  55. package/dist/src/platforms/discordbot/cli.js +12 -1
  56. package/dist/src/platforms/discordbot/cli.js.map +1 -1
  57. package/dist/src/platforms/discordbot/client.d.ts +3 -0
  58. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  59. package/dist/src/platforms/discordbot/client.js +3 -0
  60. package/dist/src/platforms/discordbot/client.js.map +1 -1
  61. package/dist/src/platforms/discordbot/commands/auth.d.ts.map +1 -1
  62. package/dist/src/platforms/discordbot/commands/auth.js +1 -5
  63. package/dist/src/platforms/discordbot/commands/auth.js.map +1 -1
  64. package/dist/src/platforms/discordbot/commands/message.d.ts.map +1 -1
  65. package/dist/src/platforms/discordbot/commands/message.js +1 -6
  66. package/dist/src/platforms/discordbot/commands/message.js.map +1 -1
  67. package/dist/src/platforms/discordbot/commands/server.d.ts.map +1 -1
  68. package/dist/src/platforms/discordbot/commands/server.js +1 -4
  69. package/dist/src/platforms/discordbot/commands/server.js.map +1 -1
  70. package/dist/src/platforms/discordbot/commands/whoami.d.ts.map +1 -1
  71. package/dist/src/platforms/discordbot/commands/whoami.js +1 -6
  72. package/dist/src/platforms/discordbot/commands/whoami.js.map +1 -1
  73. package/dist/src/platforms/discordbot/index.d.ts +3 -1
  74. package/dist/src/platforms/discordbot/index.d.ts.map +1 -1
  75. package/dist/src/platforms/discordbot/index.js +2 -1
  76. package/dist/src/platforms/discordbot/index.js.map +1 -1
  77. package/dist/src/platforms/discordbot/listener.d.ts +43 -0
  78. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -0
  79. package/dist/src/platforms/discordbot/listener.js +292 -0
  80. package/dist/src/platforms/discordbot/listener.js.map +1 -0
  81. package/dist/src/platforms/discordbot/types.d.ts +161 -0
  82. package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
  83. package/dist/src/platforms/discordbot/types.js +34 -0
  84. package/dist/src/platforms/discordbot/types.js.map +1 -1
  85. package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
  86. package/dist/src/platforms/instagram/commands/auth.js +3 -1
  87. package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
  88. package/dist/src/platforms/instagram/token-extractor.d.ts +2 -1
  89. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
  90. package/dist/src/platforms/instagram/token-extractor.js +11 -2
  91. package/dist/src/platforms/instagram/token-extractor.js.map +1 -1
  92. package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
  93. package/dist/src/platforms/slack/commands/auth.js +4 -2
  94. package/dist/src/platforms/slack/commands/auth.js.map +1 -1
  95. package/dist/src/platforms/slack/token-extractor.d.ts +4 -1
  96. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  97. package/dist/src/platforms/slack/token-extractor.js +64 -15
  98. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  99. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  100. package/dist/src/platforms/slackbot/cli.js +15 -3
  101. package/dist/src/platforms/slackbot/cli.js.map +1 -1
  102. package/dist/src/platforms/slackbot/client.d.ts +22 -1
  103. package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
  104. package/dist/src/platforms/slackbot/client.js +104 -1
  105. package/dist/src/platforms/slackbot/client.js.map +1 -1
  106. package/dist/src/platforms/slackbot/commands/auth.d.ts.map +1 -1
  107. package/dist/src/platforms/slackbot/commands/auth.js +1 -5
  108. package/dist/src/platforms/slackbot/commands/auth.js.map +1 -1
  109. package/dist/src/platforms/slackbot/commands/file.d.ts +3 -0
  110. package/dist/src/platforms/slackbot/commands/file.d.ts.map +1 -0
  111. package/dist/src/platforms/slackbot/commands/file.js +164 -0
  112. package/dist/src/platforms/slackbot/commands/file.js.map +1 -0
  113. package/dist/src/platforms/slackbot/commands/index.d.ts +1 -0
  114. package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -1
  115. package/dist/src/platforms/slackbot/commands/index.js +1 -0
  116. package/dist/src/platforms/slackbot/commands/index.js.map +1 -1
  117. package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -1
  118. package/dist/src/platforms/slackbot/commands/message.js +19 -0
  119. package/dist/src/platforms/slackbot/commands/message.js.map +1 -1
  120. package/dist/src/platforms/slackbot/commands/whoami.d.ts.map +1 -1
  121. package/dist/src/platforms/slackbot/commands/whoami.js +1 -6
  122. package/dist/src/platforms/slackbot/commands/whoami.js.map +1 -1
  123. package/dist/src/platforms/slackbot/credential-manager.d.ts +1 -0
  124. package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
  125. package/dist/src/platforms/slackbot/credential-manager.js +30 -2
  126. package/dist/src/platforms/slackbot/credential-manager.js.map +1 -1
  127. package/dist/src/platforms/slackbot/index.d.ts +4 -1
  128. package/dist/src/platforms/slackbot/index.d.ts.map +1 -1
  129. package/dist/src/platforms/slackbot/index.js +1 -0
  130. package/dist/src/platforms/slackbot/index.js.map +1 -1
  131. package/dist/src/platforms/slackbot/listener.d.ts +44 -0
  132. package/dist/src/platforms/slackbot/listener.d.ts.map +1 -0
  133. package/dist/src/platforms/slackbot/listener.js +313 -0
  134. package/dist/src/platforms/slackbot/listener.js.map +1 -0
  135. package/dist/src/platforms/slackbot/types.d.ts +196 -1
  136. package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
  137. package/dist/src/platforms/slackbot/types.js +4 -1
  138. package/dist/src/platforms/slackbot/types.js.map +1 -1
  139. package/dist/src/platforms/teams/commands/auth.d.ts +1 -0
  140. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  141. package/dist/src/platforms/teams/commands/auth.js +37 -6
  142. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  143. package/dist/src/platforms/teams/ensure-auth.js +31 -9
  144. package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
  145. package/dist/src/platforms/teams/token-extractor.d.ts +4 -1
  146. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  147. package/dist/src/platforms/teams/token-extractor.js +71 -29
  148. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  149. package/dist/src/platforms/webex/commands/auth.d.ts +1 -0
  150. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  151. package/dist/src/platforms/webex/commands/auth.js +3 -1
  152. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  153. package/dist/src/platforms/webex/token-extractor.d.ts +3 -1
  154. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  155. package/dist/src/platforms/webex/token-extractor.js +16 -2
  156. package/dist/src/platforms/webex/token-extractor.js.map +1 -1
  157. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
  158. package/dist/src/platforms/wechatbot/cli.js +11 -1
  159. package/dist/src/platforms/wechatbot/cli.js.map +1 -1
  160. package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -1
  161. package/dist/src/platforms/wechatbot/commands/auth.js +1 -5
  162. package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -1
  163. package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -1
  164. package/dist/src/platforms/wechatbot/commands/message.js +1 -6
  165. package/dist/src/platforms/wechatbot/commands/message.js.map +1 -1
  166. package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -1
  167. package/dist/src/platforms/wechatbot/commands/template.js +1 -6
  168. package/dist/src/platforms/wechatbot/commands/template.js.map +1 -1
  169. package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -1
  170. package/dist/src/platforms/wechatbot/commands/user.js +1 -6
  171. package/dist/src/platforms/wechatbot/commands/user.js.map +1 -1
  172. package/dist/src/platforms/wechatbot/commands/whoami.d.ts.map +1 -1
  173. package/dist/src/platforms/wechatbot/commands/whoami.js +1 -6
  174. package/dist/src/platforms/wechatbot/commands/whoami.js.map +1 -1
  175. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  176. package/dist/src/platforms/whatsappbot/cli.js +11 -1
  177. package/dist/src/platforms/whatsappbot/cli.js.map +1 -1
  178. package/dist/src/platforms/whatsappbot/commands/auth.d.ts.map +1 -1
  179. package/dist/src/platforms/whatsappbot/commands/auth.js +1 -5
  180. package/dist/src/platforms/whatsappbot/commands/auth.js.map +1 -1
  181. package/dist/src/platforms/whatsappbot/commands/message.d.ts.map +1 -1
  182. package/dist/src/platforms/whatsappbot/commands/message.js +1 -6
  183. package/dist/src/platforms/whatsappbot/commands/message.js.map +1 -1
  184. package/dist/src/platforms/whatsappbot/commands/template.d.ts.map +1 -1
  185. package/dist/src/platforms/whatsappbot/commands/template.js +1 -6
  186. package/dist/src/platforms/whatsappbot/commands/template.js.map +1 -1
  187. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts.map +1 -1
  188. package/dist/src/platforms/whatsappbot/commands/whoami.js +1 -6
  189. package/dist/src/platforms/whatsappbot/commands/whoami.js.map +1 -1
  190. package/dist/src/shared/chromium/browsers.d.ts +8 -0
  191. package/dist/src/shared/chromium/browsers.d.ts.map +1 -1
  192. package/dist/src/shared/chromium/browsers.js +58 -3
  193. package/dist/src/shared/chromium/browsers.js.map +1 -1
  194. package/dist/src/shared/chromium/cli-options.d.ts +5 -0
  195. package/dist/src/shared/chromium/cli-options.d.ts.map +1 -0
  196. package/dist/src/shared/chromium/cli-options.js +8 -0
  197. package/dist/src/shared/chromium/cli-options.js.map +1 -0
  198. package/dist/src/shared/chromium/index.d.ts +3 -1
  199. package/dist/src/shared/chromium/index.d.ts.map +1 -1
  200. package/dist/src/shared/chromium/index.js +2 -1
  201. package/dist/src/shared/chromium/index.js.map +1 -1
  202. package/dist/src/shared/utils/cli-output.d.ts +7 -0
  203. package/dist/src/shared/utils/cli-output.d.ts.map +1 -0
  204. package/dist/src/shared/utils/cli-output.js +7 -0
  205. package/dist/src/shared/utils/cli-output.js.map +1 -0
  206. package/dist/src/tui/app.d.ts.map +1 -1
  207. package/dist/src/tui/app.js +73 -20
  208. package/dist/src/tui/app.js.map +1 -1
  209. package/docs/content/docs/cli/channeltalk.mdx +4 -0
  210. package/docs/content/docs/cli/discord.mdx +5 -0
  211. package/docs/content/docs/cli/instagram.mdx +3 -0
  212. package/docs/content/docs/cli/slack.mdx +5 -0
  213. package/docs/content/docs/cli/slackbot.mdx +60 -22
  214. package/docs/content/docs/cli/teams.mdx +5 -0
  215. package/docs/content/docs/cli/webex.mdx +3 -0
  216. package/docs/content/docs/sdk/channeltalkbot.mdx +38 -1
  217. package/docs/content/docs/sdk/discordbot.mdx +501 -0
  218. package/docs/content/docs/sdk/meta.json +2 -0
  219. package/docs/content/docs/sdk/slackbot.mdx +576 -0
  220. package/e2e/README.md +1 -1
  221. package/e2e/config.ts +9 -4
  222. package/examples/discordbot-listen.ts +65 -0
  223. package/examples/slackbot-listen.ts +65 -0
  224. package/package.json +21 -1
  225. package/skills/agent-channeltalk/SKILL.md +5 -1
  226. package/skills/agent-channeltalk/references/authentication.md +5 -1
  227. package/skills/agent-channeltalkbot/SKILL.md +17 -3
  228. package/skills/agent-channeltalkbot/references/authentication.md +7 -5
  229. package/skills/agent-discord/SKILL.md +5 -1
  230. package/skills/agent-discord/references/authentication.md +7 -1
  231. package/skills/agent-discordbot/SKILL.md +13 -2
  232. package/skills/agent-discordbot/references/common-patterns.md +1 -1
  233. package/skills/agent-instagram/SKILL.md +7 -1
  234. package/skills/agent-instagram/references/authentication.md +6 -0
  235. package/skills/agent-kakaotalk/SKILL.md +1 -1
  236. package/skills/agent-line/SKILL.md +1 -1
  237. package/skills/agent-slack/SKILL.md +5 -1
  238. package/skills/agent-slack/references/authentication.md +7 -1
  239. package/skills/agent-slackbot/SKILL.md +56 -4
  240. package/skills/agent-slackbot/references/authentication.md +4 -0
  241. package/skills/agent-teams/SKILL.md +5 -1
  242. package/skills/agent-teams/references/authentication.md +7 -1
  243. package/skills/agent-telegram/SKILL.md +1 -1
  244. package/skills/agent-webex/SKILL.md +7 -1
  245. package/skills/agent-webex/references/authentication.md +6 -0
  246. package/skills/agent-wechatbot/SKILL.md +16 -1
  247. package/skills/agent-wechatbot/references/authentication.md +219 -0
  248. package/skills/agent-wechatbot/references/common-patterns.md +358 -0
  249. package/skills/agent-wechatbot/templates/account-summary.sh +122 -0
  250. package/skills/agent-wechatbot/templates/post-message.sh +122 -0
  251. package/skills/agent-wechatbot/templates/send-template.sh +152 -0
  252. package/skills/agent-whatsapp/SKILL.md +1 -1
  253. package/skills/agent-whatsappbot/SKILL.md +30 -1
  254. package/src/platforms/channeltalk/commands/auth.test.ts +15 -3
  255. package/src/platforms/channeltalk/commands/auth.ts +15 -5
  256. package/src/platforms/channeltalk/token-extractor.ts +24 -5
  257. package/src/platforms/channeltalkbot/cli.ts +9 -0
  258. package/src/platforms/channeltalkbot/commands/auth.ts +1 -5
  259. package/src/platforms/channeltalkbot/commands/bot.ts +1 -6
  260. package/src/platforms/channeltalkbot/commands/chat.ts +1 -6
  261. package/src/platforms/channeltalkbot/commands/group.ts +1 -6
  262. package/src/platforms/channeltalkbot/commands/manager.ts +1 -6
  263. package/src/platforms/channeltalkbot/commands/message.ts +1 -6
  264. package/src/platforms/channeltalkbot/commands/whoami.test.ts +2 -0
  265. package/src/platforms/channeltalkbot/commands/whoami.ts +1 -6
  266. package/src/platforms/channeltalkbot/credential-manager.test.ts +96 -2
  267. package/src/platforms/channeltalkbot/credential-manager.ts +37 -4
  268. package/src/platforms/discord/commands/auth.ts +13 -2
  269. package/src/platforms/discord/listener.test.ts +59 -1
  270. package/src/platforms/discord/listener.ts +43 -19
  271. package/src/platforms/discord/token-extractor.ts +30 -6
  272. package/src/platforms/discordbot/cli.ts +10 -0
  273. package/src/platforms/discordbot/client.ts +4 -0
  274. package/src/platforms/discordbot/commands/auth.ts +1 -5
  275. package/src/platforms/discordbot/commands/message.ts +1 -6
  276. package/src/platforms/discordbot/commands/server.ts +1 -5
  277. package/src/platforms/discordbot/commands/whoami.ts +1 -6
  278. package/src/platforms/discordbot/index.test.ts +82 -0
  279. package/src/platforms/discordbot/index.ts +27 -9
  280. package/src/platforms/discordbot/listener.test.ts +1002 -0
  281. package/src/platforms/discordbot/listener.ts +321 -0
  282. package/src/platforms/discordbot/types.ts +163 -0
  283. package/src/platforms/instagram/commands/auth.ts +9 -1
  284. package/src/platforms/instagram/token-extractor.ts +13 -1
  285. package/src/platforms/slack/commands/auth.ts +11 -2
  286. package/src/platforms/slack/token-extractor.test.ts +96 -0
  287. package/src/platforms/slack/token-extractor.ts +76 -13
  288. package/src/platforms/slackbot/cli.ts +13 -1
  289. package/src/platforms/slackbot/client.test.ts +274 -0
  290. package/src/platforms/slackbot/client.ts +130 -2
  291. package/src/platforms/slackbot/commands/auth.ts +1 -5
  292. package/src/platforms/slackbot/commands/file.test.ts +201 -0
  293. package/src/platforms/slackbot/commands/file.ts +212 -0
  294. package/src/platforms/slackbot/commands/index.ts +1 -0
  295. package/src/platforms/slackbot/commands/message.ts +22 -0
  296. package/src/platforms/slackbot/commands/whoami.ts +1 -6
  297. package/src/platforms/slackbot/credential-manager.test.ts +62 -2
  298. package/src/platforms/slackbot/credential-manager.ts +32 -2
  299. package/src/platforms/slackbot/index.test.ts +59 -0
  300. package/src/platforms/slackbot/index.ts +31 -7
  301. package/src/platforms/slackbot/listener.test.ts +1012 -0
  302. package/src/platforms/slackbot/listener.ts +362 -0
  303. package/src/platforms/slackbot/types.ts +224 -1
  304. package/src/platforms/teams/commands/auth.test.ts +1 -1
  305. package/src/platforms/teams/commands/auth.ts +66 -7
  306. package/src/platforms/teams/ensure-auth.test.ts +56 -5
  307. package/src/platforms/teams/ensure-auth.ts +39 -11
  308. package/src/platforms/teams/token-extractor.test.ts +146 -24
  309. package/src/platforms/teams/token-extractor.ts +87 -29
  310. package/src/platforms/webex/commands/auth.ts +13 -2
  311. package/src/platforms/webex/token-extractor.ts +25 -3
  312. package/src/platforms/wechatbot/cli.ts +9 -0
  313. package/src/platforms/wechatbot/commands/auth.ts +1 -5
  314. package/src/platforms/wechatbot/commands/message.ts +1 -6
  315. package/src/platforms/wechatbot/commands/template.ts +1 -6
  316. package/src/platforms/wechatbot/commands/user.ts +1 -6
  317. package/src/platforms/wechatbot/commands/whoami.ts +1 -6
  318. package/src/platforms/whatsappbot/cli.ts +9 -0
  319. package/src/platforms/whatsappbot/commands/auth.ts +1 -5
  320. package/src/platforms/whatsappbot/commands/message.ts +1 -6
  321. package/src/platforms/whatsappbot/commands/template.ts +1 -6
  322. package/src/platforms/whatsappbot/commands/whoami.ts +1 -6
  323. package/src/shared/chromium/browsers.test.ts +80 -0
  324. package/src/shared/chromium/browsers.ts +72 -3
  325. package/src/shared/chromium/cli-options.test.ts +22 -0
  326. package/src/shared/chromium/cli-options.ts +12 -0
  327. package/src/shared/chromium/index.ts +3 -0
  328. package/src/shared/utils/cli-output.test.ts +57 -0
  329. package/src/shared/utils/cli-output.ts +8 -0
  330. package/src/tui/app.ts +129 -20
@@ -0,0 +1,321 @@
1
+ import { EventEmitter } from 'events'
2
+
3
+ import WebSocket from 'ws'
4
+
5
+ import type { DiscordBotClient } from './client'
6
+ import type { DiscordBotListenerEventMap, DiscordGatewayGenericEvent } from './types'
7
+ import { DiscordGatewayOpcode, DiscordIntent } from './types'
8
+
9
+ const GATEWAY_URL = 'wss://gateway.discord.gg/?v=10&encoding=json'
10
+ const GATEWAY_QUERY = '?v=10&encoding=json'
11
+ const RECONNECT_BASE_DELAY = 1_000
12
+ const RECONNECT_MAX_DELAY = 30_000
13
+ const NON_RECOVERABLE_CLOSE_CODES = [4004, 4010, 4011, 4012, 4013, 4014]
14
+ const SESSION_RESET_CLOSE_CODES = [4007, 4009]
15
+
16
+ const DEFAULT_INTENTS =
17
+ DiscordIntent.Guilds |
18
+ DiscordIntent.GuildMessages |
19
+ DiscordIntent.GuildMessageReactions |
20
+ DiscordIntent.GuildMessageTyping |
21
+ DiscordIntent.DirectMessages |
22
+ DiscordIntent.DirectMessageReactions |
23
+ DiscordIntent.DirectMessageTyping
24
+
25
+ type EventKey = keyof DiscordBotListenerEventMap
26
+
27
+ export class DiscordBotListener {
28
+ private client: DiscordBotClient
29
+ private intents: number
30
+ private running = false
31
+ private ws: WebSocket | null = null
32
+ private emitter = new EventEmitter()
33
+ private heartbeatTimer: ReturnType<typeof setInterval> | null = null
34
+ private heartbeatAckReceived = true
35
+ private heartbeatJitterTimer: ReturnType<typeof setTimeout> | null = null
36
+ private invalidSessionTimer: ReturnType<typeof setTimeout> | null = null
37
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null
38
+ private reconnectAttempts = 0
39
+ private sequence: number | null = null
40
+ private sessionId: string | null = null
41
+ private resumeGatewayUrl: string | null = null
42
+ private token: string | null = null
43
+ private cachedUser: { id: string; username: string } | null = null
44
+ private generation = 0
45
+
46
+ constructor(client: DiscordBotClient, options?: { intents?: number }) {
47
+ this.client = client
48
+ this.intents = options?.intents ?? DEFAULT_INTENTS
49
+ }
50
+
51
+ async start(): Promise<void> {
52
+ if (this.running) return
53
+ this.running = true
54
+ this.reconnectAttempts = 0
55
+ this.generation++
56
+ await this.connect(this.generation)
57
+ }
58
+
59
+ stop(): void {
60
+ this.running = false
61
+ this.generation++
62
+ this.clearTimers()
63
+ if (this.ws) {
64
+ this.ws.close()
65
+ this.ws = null
66
+ }
67
+ this.sequence = null
68
+ this.sessionId = null
69
+ this.resumeGatewayUrl = null
70
+ this.token = null
71
+ this.cachedUser = null
72
+ }
73
+
74
+ on<K extends EventKey>(event: K, listener: (...args: DiscordBotListenerEventMap[K]) => void): this {
75
+ this.emitter.on(event, listener as (...args: any[]) => void)
76
+ return this
77
+ }
78
+
79
+ off<K extends EventKey>(event: K, listener: (...args: DiscordBotListenerEventMap[K]) => void): this {
80
+ this.emitter.off(event, listener as (...args: any[]) => void)
81
+ return this
82
+ }
83
+
84
+ once<K extends EventKey>(event: K, listener: (...args: DiscordBotListenerEventMap[K]) => void): this {
85
+ this.emitter.once(event, listener as (...args: any[]) => void)
86
+ return this
87
+ }
88
+
89
+ private isCurrent(generation: number, ws?: WebSocket): boolean {
90
+ if (generation !== this.generation || !this.running) return false
91
+ if (ws !== undefined && this.ws !== ws) return false
92
+ return true
93
+ }
94
+
95
+ private async connect(generation: number): Promise<void> {
96
+ if (!this.isCurrent(generation)) return
97
+
98
+ try {
99
+ const { token } = await this.client.gatewayConnect()
100
+ if (!this.isCurrent(generation)) return
101
+
102
+ this.token = token
103
+
104
+ const url = this.resumeGatewayUrl ? `${this.resumeGatewayUrl}${GATEWAY_QUERY}` : GATEWAY_URL
105
+ const ws = new WebSocket(url)
106
+ this.ws = ws
107
+
108
+ ws.on('open', () => {
109
+ if (!this.isCurrent(generation, ws)) {
110
+ ws.close()
111
+ return
112
+ }
113
+ })
114
+
115
+ ws.on('message', (raw) => {
116
+ if (!this.isCurrent(generation, ws)) return
117
+ try {
118
+ const data = JSON.parse(raw.toString())
119
+ this.handleMessage(data, generation, ws)
120
+ } catch {
121
+ // malformed gateway frame; ignore and let heartbeat handle liveness
122
+ }
123
+ })
124
+
125
+ ws.on('close', (code) => {
126
+ if (!this.isCurrent(generation, ws)) return
127
+ this.clearTimers()
128
+ this.ws = null
129
+ if (NON_RECOVERABLE_CLOSE_CODES.includes(code)) {
130
+ this.emitter.emit('error', new Error(`Discord gateway closed with non-recoverable code ${code}`))
131
+ this.running = false
132
+ return
133
+ }
134
+ if (SESSION_RESET_CLOSE_CODES.includes(code)) {
135
+ this.sequence = null
136
+ this.sessionId = null
137
+ this.resumeGatewayUrl = null
138
+ }
139
+ if (this.running) {
140
+ this.emitter.emit('disconnected')
141
+ this.scheduleReconnect()
142
+ }
143
+ })
144
+
145
+ ws.on('error', (err) => {
146
+ if (!this.isCurrent(generation, ws)) return
147
+ this.emitter.emit('error', err instanceof Error ? err : new Error(String(err)))
148
+ })
149
+ } catch (error) {
150
+ if (!this.isCurrent(generation)) return
151
+ this.emitter.emit('error', error instanceof Error ? error : new Error(String(error)))
152
+ if (this.running) {
153
+ this.scheduleReconnect()
154
+ }
155
+ }
156
+ }
157
+
158
+ private handleMessage(data: { op: number; d: any; s?: number; t?: string }, generation: number, ws: WebSocket): void {
159
+ if (!this.isCurrent(generation, ws)) return
160
+ const { op, d, s, t } = data
161
+
162
+ switch (op) {
163
+ case DiscordGatewayOpcode.Hello:
164
+ this.startHeartbeat(d.heartbeat_interval, generation, ws)
165
+ if (this.sessionId) {
166
+ this.sendResume()
167
+ } else {
168
+ this.sendIdentify()
169
+ }
170
+ break
171
+
172
+ case DiscordGatewayOpcode.HeartbeatACK:
173
+ this.heartbeatAckReceived = true
174
+ break
175
+
176
+ case DiscordGatewayOpcode.Dispatch:
177
+ if (typeof s === 'number') this.sequence = s
178
+ if (t) this.handleDispatch(t, d)
179
+ break
180
+
181
+ case DiscordGatewayOpcode.Reconnect:
182
+ this.reconnectAttempts = 0
183
+ ws.close()
184
+ break
185
+
186
+ case DiscordGatewayOpcode.InvalidSession: {
187
+ if (d === true) {
188
+ const delay = 1000 + Math.random() * 4000
189
+ this.invalidSessionTimer = setTimeout(() => {
190
+ this.invalidSessionTimer = null
191
+ if (this.isCurrent(generation, ws)) ws.close()
192
+ }, delay)
193
+ } else {
194
+ this.sequence = null
195
+ this.sessionId = null
196
+ this.resumeGatewayUrl = null
197
+ ws.close()
198
+ }
199
+ break
200
+ }
201
+
202
+ case DiscordGatewayOpcode.Heartbeat:
203
+ this.sendHeartbeat()
204
+ break
205
+ }
206
+ }
207
+
208
+ private handleDispatch(t: string, d: any): void {
209
+ if (t === 'READY') {
210
+ this.sessionId = d.session_id
211
+ this.resumeGatewayUrl = d.resume_gateway_url
212
+ this.cachedUser = d.user
213
+ this.reconnectAttempts = 0
214
+ this.emitter.emit('connected', { user: d.user, sessionId: d.session_id })
215
+ return
216
+ }
217
+
218
+ if (t === 'RESUMED') {
219
+ this.reconnectAttempts = 0
220
+ this.emitter.emit('connected', { user: this.cachedUser!, sessionId: this.sessionId! })
221
+ return
222
+ }
223
+
224
+ const eventType = t.toLowerCase()
225
+ const event: DiscordGatewayGenericEvent = { ...d, type: t }
226
+ this.emitter.emit(eventType, event)
227
+ this.emitter.emit('discord_event', event)
228
+ }
229
+
230
+ private sendIdentify(): void {
231
+ this.ws?.send(
232
+ JSON.stringify({
233
+ op: DiscordGatewayOpcode.Identify,
234
+ d: {
235
+ token: this.token,
236
+ intents: this.intents,
237
+ properties: {
238
+ os: 'linux',
239
+ browser: 'agent-messenger',
240
+ device: 'agent-messenger',
241
+ },
242
+ },
243
+ }),
244
+ )
245
+ }
246
+
247
+ private sendResume(): void {
248
+ this.ws?.send(
249
+ JSON.stringify({
250
+ op: DiscordGatewayOpcode.Resume,
251
+ d: {
252
+ token: this.token,
253
+ session_id: this.sessionId,
254
+ seq: this.sequence,
255
+ },
256
+ }),
257
+ )
258
+ }
259
+
260
+ private sendHeartbeat(): void {
261
+ this.ws?.send(JSON.stringify({ op: DiscordGatewayOpcode.Heartbeat, d: this.sequence }))
262
+ }
263
+
264
+ private startHeartbeat(interval: number, generation: number, ws: WebSocket): void {
265
+ this.clearHeartbeatTimers()
266
+ this.heartbeatAckReceived = true
267
+
268
+ this.heartbeatJitterTimer = setTimeout(() => {
269
+ this.heartbeatJitterTimer = null
270
+ if (!this.isCurrent(generation, ws)) return
271
+ this.heartbeatAckReceived = false
272
+ this.sendHeartbeat()
273
+
274
+ this.heartbeatTimer = setInterval(() => {
275
+ if (!this.isCurrent(generation, ws)) {
276
+ this.clearHeartbeatTimers()
277
+ return
278
+ }
279
+ if (!this.heartbeatAckReceived) {
280
+ ws.close()
281
+ return
282
+ }
283
+ this.heartbeatAckReceived = false
284
+ this.sendHeartbeat()
285
+ }, interval)
286
+ }, Math.random() * interval)
287
+ }
288
+
289
+ private scheduleReconnect(): void {
290
+ const delay = Math.min(RECONNECT_BASE_DELAY * 2 ** this.reconnectAttempts, RECONNECT_MAX_DELAY)
291
+ this.reconnectAttempts++
292
+ const generation = this.generation
293
+ this.reconnectTimer = setTimeout(() => {
294
+ this.reconnectTimer = null
295
+ this.connect(generation)
296
+ }, delay)
297
+ }
298
+
299
+ private clearHeartbeatTimers(): void {
300
+ if (this.heartbeatTimer) {
301
+ clearInterval(this.heartbeatTimer)
302
+ this.heartbeatTimer = null
303
+ }
304
+ if (this.heartbeatJitterTimer) {
305
+ clearTimeout(this.heartbeatJitterTimer)
306
+ this.heartbeatJitterTimer = null
307
+ }
308
+ }
309
+
310
+ private clearTimers(): void {
311
+ this.clearHeartbeatTimers()
312
+ if (this.invalidSessionTimer) {
313
+ clearTimeout(this.invalidSessionTimer)
314
+ this.invalidSessionTimer = null
315
+ }
316
+ if (this.reconnectTimer) {
317
+ clearTimeout(this.reconnectTimer)
318
+ this.reconnectTimer = null
319
+ }
320
+ }
321
+ }
@@ -187,3 +187,166 @@ export const DiscordFileSchema = z.object({
187
187
  height: z.number().optional(),
188
188
  width: z.number().optional(),
189
189
  })
190
+
191
+ export const DiscordGatewayOpcode = {
192
+ Dispatch: 0,
193
+ Heartbeat: 1,
194
+ Identify: 2,
195
+ PresenceUpdate: 3,
196
+ VoiceStateUpdate: 4,
197
+ Resume: 6,
198
+ Reconnect: 7,
199
+ RequestGuildMembers: 8,
200
+ InvalidSession: 9,
201
+ Hello: 10,
202
+ HeartbeatACK: 11,
203
+ } as const
204
+
205
+ export const DiscordIntent = {
206
+ Guilds: 1 << 0,
207
+ GuildMembers: 1 << 1, // privileged
208
+ GuildModeration: 1 << 2,
209
+ GuildEmojisAndStickers: 1 << 3,
210
+ GuildIntegrations: 1 << 4,
211
+ GuildWebhooks: 1 << 5,
212
+ GuildInvites: 1 << 6,
213
+ GuildVoiceStates: 1 << 7,
214
+ GuildPresences: 1 << 8, // privileged
215
+ GuildMessages: 1 << 9,
216
+ GuildMessageReactions: 1 << 10,
217
+ GuildMessageTyping: 1 << 11,
218
+ DirectMessages: 1 << 12,
219
+ DirectMessageReactions: 1 << 13,
220
+ DirectMessageTyping: 1 << 14,
221
+ MessageContent: 1 << 15, // privileged
222
+ GuildScheduledEvents: 1 << 16,
223
+ AutoModerationConfiguration: 1 << 20,
224
+ AutoModerationExecution: 1 << 21,
225
+ } as const
226
+
227
+ export interface DiscordGatewayMessageCreateEvent {
228
+ type: 'MESSAGE_CREATE'
229
+ id: string
230
+ channel_id: string
231
+ guild_id?: string
232
+ author: { id: string; username: string; bot?: boolean }
233
+ content: string
234
+ timestamp: string
235
+ edited_timestamp?: string
236
+ mentions?: DiscordUser[]
237
+ attachments?: DiscordFile[]
238
+ }
239
+
240
+ export interface DiscordGatewayMessageUpdateEvent {
241
+ type: 'MESSAGE_UPDATE'
242
+ id: string
243
+ channel_id: string
244
+ guild_id?: string
245
+ content?: string
246
+ edited_timestamp?: string
247
+ }
248
+
249
+ export interface DiscordGatewayMessageDeleteEvent {
250
+ type: 'MESSAGE_DELETE'
251
+ id: string
252
+ channel_id: string
253
+ guild_id?: string
254
+ }
255
+
256
+ export interface DiscordGatewayReactionEvent {
257
+ type: 'MESSAGE_REACTION_ADD' | 'MESSAGE_REACTION_REMOVE'
258
+ user_id: string
259
+ channel_id: string
260
+ message_id: string
261
+ guild_id?: string
262
+ emoji: { id?: string; name: string }
263
+ }
264
+
265
+ export interface DiscordGatewayMemberEvent {
266
+ type: 'GUILD_MEMBER_ADD' | 'GUILD_MEMBER_REMOVE'
267
+ guild_id: string
268
+ user: { id: string; username: string }
269
+ }
270
+
271
+ export interface DiscordGatewayTypingEvent {
272
+ type: 'TYPING_START'
273
+ user_id: string
274
+ channel_id: string
275
+ guild_id?: string
276
+ timestamp: number
277
+ }
278
+
279
+ export interface DiscordGatewayPresenceEvent {
280
+ type: 'PRESENCE_UPDATE'
281
+ user: { id: string }
282
+ guild_id: string
283
+ status: 'online' | 'idle' | 'dnd' | 'offline'
284
+ activities?: Array<{ name: string; type: number }>
285
+ }
286
+
287
+ export interface DiscordGatewayChannelEvent {
288
+ type: 'CHANNEL_CREATE' | 'CHANNEL_UPDATE' | 'CHANNEL_DELETE'
289
+ id: string
290
+ guild_id?: string
291
+ name?: string
292
+ }
293
+
294
+ export interface DiscordGatewayGuildEvent {
295
+ type: 'GUILD_CREATE' | 'GUILD_UPDATE' | 'GUILD_DELETE'
296
+ id: string
297
+ name?: string
298
+ unavailable?: boolean
299
+ }
300
+
301
+ export interface DiscordGatewayInteractionEvent {
302
+ type: 'INTERACTION_CREATE'
303
+ id: string
304
+ application_id: string
305
+ token: string
306
+ data?: Record<string, unknown>
307
+ channel_id?: string
308
+ guild_id?: string
309
+ member?: Record<string, unknown>
310
+ user?: { id: string; username: string }
311
+ }
312
+
313
+ export interface DiscordGatewayGenericEvent {
314
+ type: string
315
+ [key: string]: unknown
316
+ }
317
+
318
+ export type DiscordGatewayEvent =
319
+ | DiscordGatewayMessageCreateEvent
320
+ | DiscordGatewayMessageUpdateEvent
321
+ | DiscordGatewayMessageDeleteEvent
322
+ | DiscordGatewayReactionEvent
323
+ | DiscordGatewayMemberEvent
324
+ | DiscordGatewayTypingEvent
325
+ | DiscordGatewayPresenceEvent
326
+ | DiscordGatewayChannelEvent
327
+ | DiscordGatewayGuildEvent
328
+ | DiscordGatewayInteractionEvent
329
+ | DiscordGatewayGenericEvent
330
+
331
+ export interface DiscordBotListenerEventMap {
332
+ message_create: [event: DiscordGatewayMessageCreateEvent]
333
+ message_update: [event: DiscordGatewayMessageUpdateEvent]
334
+ message_delete: [event: DiscordGatewayMessageDeleteEvent]
335
+ message_reaction_add: [event: DiscordGatewayReactionEvent]
336
+ message_reaction_remove: [event: DiscordGatewayReactionEvent]
337
+ guild_member_add: [event: DiscordGatewayMemberEvent]
338
+ guild_member_remove: [event: DiscordGatewayMemberEvent]
339
+ presence_update: [event: DiscordGatewayPresenceEvent]
340
+ typing_start: [event: DiscordGatewayTypingEvent]
341
+ channel_create: [event: DiscordGatewayChannelEvent]
342
+ channel_update: [event: DiscordGatewayChannelEvent]
343
+ channel_delete: [event: DiscordGatewayChannelEvent]
344
+ guild_create: [event: DiscordGatewayGuildEvent]
345
+ guild_update: [event: DiscordGatewayGuildEvent]
346
+ guild_delete: [event: DiscordGatewayGuildEvent]
347
+ interaction_create: [event: DiscordGatewayInteractionEvent]
348
+ discord_event: [event: DiscordGatewayGenericEvent]
349
+ connected: [info: { user: { id: string; username: string }; sessionId: string }]
350
+ disconnected: []
351
+ error: [error: Error]
352
+ }
@@ -4,6 +4,7 @@ import { Writable } from 'node:stream'
4
4
 
5
5
  import { Command } from 'commander'
6
6
 
7
+ import { collectBrowserProfileOption } from '@/shared/chromium'
7
8
  import { handleError } from '@/shared/utils/error-handler'
8
9
  import { formatOutput } from '@/shared/utils/output'
9
10
  import { info, warn, error as stderrError, debug } from '@/shared/utils/stderr'
@@ -385,11 +386,12 @@ async function useAction(accountId: string, options: { pretty?: boolean }): Prom
385
386
  }
386
387
  }
387
388
 
388
- async function extractAction(options: { pretty?: boolean; debug?: boolean }): Promise<void> {
389
+ async function extractAction(options: { pretty?: boolean; debug?: boolean; browserProfile?: string[] }): Promise<void> {
389
390
  try {
390
391
  const extractor = new InstagramTokenExtractor(
391
392
  undefined,
392
393
  options.debug ? (msg) => debug(`[debug] ${msg}`) : undefined,
394
+ options.browserProfile,
393
395
  )
394
396
 
395
397
  if (options.debug) {
@@ -536,6 +538,12 @@ export const authCommand = new Command('auth')
536
538
  .description('Extract Instagram cookies from browser (Chrome, Edge, Arc, Brave)')
537
539
  .option('--pretty', 'Pretty print JSON output')
538
540
  .option('--debug', 'Show debug output')
541
+ .option(
542
+ '--browser-profile <path>',
543
+ 'Additional Chromium profile/user-data directory to scan (repeatable, comma-separated supported)',
544
+ collectBrowserProfileOption,
545
+ [],
546
+ )
539
547
  .action(extractAction),
540
548
  )
541
549
  .addCommand(
@@ -10,6 +10,7 @@ import {
10
10
  discoverBrowserProfileDirs,
11
11
  findLocalStatePath,
12
12
  getBrowserBasePath,
13
+ getAgentBrowserProfileDirs,
13
14
  } from '@/shared/chromium'
14
15
  import type { KeychainVariant } from '@/shared/chromium'
15
16
 
@@ -30,10 +31,12 @@ export class InstagramTokenExtractor {
30
31
  private debugLog: ((message: string) => void) | null
31
32
  private decryptor: ChromiumCookieDecryptor
32
33
  private cookieReader: ChromiumCookieReader
34
+ private customBrowserProfileDirs: string[]
33
35
 
34
- constructor(platform?: NodeJS.Platform, debugLog?: (message: string) => void) {
36
+ constructor(platform?: NodeJS.Platform, debugLog?: (message: string) => void, customBrowserProfileDirs?: string[]) {
35
37
  this.platform = platform ?? process.platform
36
38
  this.debugLog = debugLog ?? null
39
+ this.customBrowserProfileDirs = customBrowserProfileDirs ?? []
37
40
  this.decryptor = new ChromiumCookieDecryptor({ platform: this.platform })
38
41
  this.cookieReader = new ChromiumCookieReader()
39
42
  }
@@ -52,6 +55,11 @@ export class InstagramTokenExtractor {
52
55
  }
53
56
  }
54
57
 
58
+ for (const profileDir of getAgentBrowserProfileDirs({ customProfileDirs: this.customBrowserProfileDirs })) {
59
+ paths.push(join(profileDir, 'Cookies'))
60
+ paths.push(join(profileDir, 'Network', 'Cookies'))
61
+ }
62
+
55
63
  return paths
56
64
  }
57
65
 
@@ -65,6 +73,10 @@ export class InstagramTokenExtractor {
65
73
  paths.push(join(browserBase, 'Local State'))
66
74
  }
67
75
 
76
+ for (const profileDir of getAgentBrowserProfileDirs({ customProfileDirs: this.customBrowserProfileDirs })) {
77
+ paths.push(join(profileDir, 'Local State'))
78
+ }
79
+
68
80
  return paths
69
81
  }
70
82
 
@@ -1,5 +1,7 @@
1
1
  import { Command } from 'commander'
2
2
 
3
+ import { collectBrowserProfileOption } from '@/shared/chromium'
4
+ import type { BrowserProfileOption } from '@/shared/chromium'
3
5
  import { handleError } from '@/shared/utils/error-handler'
4
6
  import { formatOutput } from '@/shared/utils/output'
5
7
  import { debug } from '@/shared/utils/stderr'
@@ -19,13 +21,14 @@ async function extractAction(options: {
19
21
  pretty?: boolean
20
22
  debug?: boolean
21
23
  unsafelyShowSecrets?: boolean
24
+ browserProfile?: string[]
22
25
  }): Promise<void> {
23
26
  try {
24
27
  if (options.unsafelyShowSecrets) {
25
28
  options.debug = true
26
29
  }
27
30
  const debugLog = options.debug ? (msg: string) => debug(`[debug] ${msg}`) : undefined
28
- const extractor = new TokenExtractor(undefined, undefined, undefined, debugLog)
31
+ const extractor = new TokenExtractor(undefined, undefined, undefined, debugLog, options.browserProfile)
29
32
 
30
33
  if (process.platform === 'darwin') {
31
34
  console.log('')
@@ -246,8 +249,14 @@ export const authCommand = new Command('auth')
246
249
  .description('Extract tokens from Slack desktop app or a supported Chromium browser')
247
250
  .option('--pretty', 'Pretty print JSON output')
248
251
  .option('--debug', 'Show debug output for troubleshooting')
252
+ .option(
253
+ '--browser-profile <path>',
254
+ 'Additional Chromium profile/user-data directory to scan (repeatable, comma-separated supported)',
255
+ collectBrowserProfileOption,
256
+ [],
257
+ )
249
258
  .option('--unsafely-show-secrets', 'Show full token and cookie values in debug output')
250
- .action(extractAction),
259
+ .action((options: BrowserProfileOption & Parameters<typeof extractAction>[0]) => extractAction(options)),
251
260
  )
252
261
  .addCommand(
253
262
  new Command('logout')