agent-messenger 1.0.0 → 1.2.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 (512) hide show
  1. package/.claude/commands/release.md +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.github/workflows/ci.yml +1 -1
  4. package/.github/workflows/e2e.yml.disabled +69 -0
  5. package/CONTRIBUTING.md +2 -2
  6. package/README.md +18 -15
  7. package/biome.json +34 -2
  8. package/bun.lock +63 -0
  9. package/dist/package.json +8 -4
  10. package/dist/src/cli.d.ts.map +1 -1
  11. package/dist/src/cli.js +4 -1
  12. package/dist/src/cli.js.map +1 -1
  13. package/dist/src/platforms/discord/cli.js +4 -4
  14. package/dist/src/platforms/discord/cli.js.map +1 -1
  15. package/dist/src/platforms/discord/client.d.ts +26 -5
  16. package/dist/src/platforms/discord/client.d.ts.map +1 -1
  17. package/dist/src/platforms/discord/client.js +115 -17
  18. package/dist/src/platforms/discord/client.js.map +1 -1
  19. package/dist/src/platforms/discord/commands/auth.js +16 -16
  20. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  21. package/dist/src/platforms/discord/commands/channel.js +4 -4
  22. package/dist/src/platforms/discord/commands/channel.js.map +1 -1
  23. package/dist/src/platforms/discord/commands/dm.d.ts +9 -0
  24. package/dist/src/platforms/discord/commands/dm.d.ts.map +1 -0
  25. package/dist/src/platforms/discord/commands/dm.js +68 -0
  26. package/dist/src/platforms/discord/commands/dm.js.map +1 -0
  27. package/dist/src/platforms/discord/commands/friend.d.ts +3 -0
  28. package/dist/src/platforms/discord/commands/friend.d.ts.map +1 -0
  29. package/dist/src/platforms/discord/commands/friend.js +39 -0
  30. package/dist/src/platforms/discord/commands/friend.js.map +1 -0
  31. package/dist/src/platforms/discord/commands/index.d.ts +1 -1
  32. package/dist/src/platforms/discord/commands/index.d.ts.map +1 -1
  33. package/dist/src/platforms/discord/commands/index.js +1 -1
  34. package/dist/src/platforms/discord/commands/index.js.map +1 -1
  35. package/dist/src/platforms/discord/commands/member.d.ts +3 -0
  36. package/dist/src/platforms/discord/commands/member.d.ts.map +1 -0
  37. package/dist/src/platforms/discord/commands/member.js +47 -0
  38. package/dist/src/platforms/discord/commands/member.js.map +1 -0
  39. package/dist/src/platforms/discord/commands/mention.d.ts +8 -0
  40. package/dist/src/platforms/discord/commands/mention.d.ts.map +1 -0
  41. package/dist/src/platforms/discord/commands/mention.js +47 -0
  42. package/dist/src/platforms/discord/commands/mention.js.map +1 -0
  43. package/dist/src/platforms/discord/commands/message.d.ts +13 -0
  44. package/dist/src/platforms/discord/commands/message.d.ts.map +1 -1
  45. package/dist/src/platforms/discord/commands/message.js +95 -1
  46. package/dist/src/platforms/discord/commands/message.js.map +1 -1
  47. package/dist/src/platforms/discord/commands/note.d.ts +3 -0
  48. package/dist/src/platforms/discord/commands/note.d.ts.map +1 -0
  49. package/dist/src/platforms/discord/commands/note.js +56 -0
  50. package/dist/src/platforms/discord/commands/note.js.map +1 -0
  51. package/dist/src/platforms/discord/commands/profile.d.ts +3 -0
  52. package/dist/src/platforms/discord/commands/profile.d.ts.map +1 -0
  53. package/dist/src/platforms/discord/commands/profile.js +47 -0
  54. package/dist/src/platforms/discord/commands/profile.js.map +1 -0
  55. package/dist/src/platforms/discord/commands/server.d.ts +15 -0
  56. package/dist/src/platforms/discord/commands/server.d.ts.map +1 -0
  57. package/dist/src/platforms/discord/commands/server.js +102 -0
  58. package/dist/src/platforms/discord/commands/server.js.map +1 -0
  59. package/dist/src/platforms/discord/commands/snapshot.js +10 -10
  60. package/dist/src/platforms/discord/commands/snapshot.js.map +1 -1
  61. package/dist/src/platforms/discord/commands/thread.d.ts +10 -0
  62. package/dist/src/platforms/discord/commands/thread.d.ts.map +1 -0
  63. package/dist/src/platforms/discord/commands/thread.js +67 -0
  64. package/dist/src/platforms/discord/commands/thread.js.map +1 -0
  65. package/dist/src/platforms/discord/commands/user.d.ts.map +1 -1
  66. package/dist/src/platforms/discord/commands/user.js +14 -5
  67. package/dist/src/platforms/discord/commands/user.js.map +1 -1
  68. package/dist/src/platforms/discord/credential-manager.d.ts +13 -13
  69. package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
  70. package/dist/src/platforms/discord/credential-manager.js +28 -22
  71. package/dist/src/platforms/discord/credential-manager.js.map +1 -1
  72. package/dist/src/platforms/discord/super-properties.d.ts +4 -0
  73. package/dist/src/platforms/discord/super-properties.d.ts.map +1 -0
  74. package/dist/src/platforms/discord/super-properties.js +50 -0
  75. package/dist/src/platforms/discord/super-properties.js.map +1 -0
  76. package/dist/src/platforms/discord/token-extractor.d.ts +6 -1
  77. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  78. package/dist/src/platforms/discord/token-extractor.js +27 -2
  79. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  80. package/dist/src/platforms/discord/types.d.ts +389 -22
  81. package/dist/src/platforms/discord/types.d.ts.map +1 -1
  82. package/dist/src/platforms/discord/types.js +46 -4
  83. package/dist/src/platforms/discord/types.js.map +1 -1
  84. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  85. package/dist/src/platforms/slack/cli.js +7 -2
  86. package/dist/src/platforms/slack/cli.js.map +1 -1
  87. package/dist/src/platforms/slack/client.d.ts +19 -1
  88. package/dist/src/platforms/slack/client.d.ts.map +1 -1
  89. package/dist/src/platforms/slack/client.js +134 -0
  90. package/dist/src/platforms/slack/client.js.map +1 -1
  91. package/dist/src/platforms/slack/commands/activity.d.ts +3 -0
  92. package/dist/src/platforms/slack/commands/activity.d.ts.map +1 -0
  93. package/dist/src/platforms/slack/commands/activity.js +40 -0
  94. package/dist/src/platforms/slack/commands/activity.js.map +1 -0
  95. package/dist/src/platforms/slack/commands/drafts.d.ts +3 -0
  96. package/dist/src/platforms/slack/commands/drafts.d.ts.map +1 -0
  97. package/dist/src/platforms/slack/commands/drafts.js +45 -0
  98. package/dist/src/platforms/slack/commands/drafts.js.map +1 -0
  99. package/dist/src/platforms/slack/commands/index.d.ts +5 -0
  100. package/dist/src/platforms/slack/commands/index.d.ts.map +1 -1
  101. package/dist/src/platforms/slack/commands/index.js +5 -0
  102. package/dist/src/platforms/slack/commands/index.js.map +1 -1
  103. package/dist/src/platforms/slack/commands/saved.d.ts +3 -0
  104. package/dist/src/platforms/slack/commands/saved.d.ts.map +1 -0
  105. package/dist/src/platforms/slack/commands/saved.js +54 -0
  106. package/dist/src/platforms/slack/commands/saved.js.map +1 -0
  107. package/dist/src/platforms/slack/commands/sections.d.ts +3 -0
  108. package/dist/src/platforms/slack/commands/sections.d.ts.map +1 -0
  109. package/dist/src/platforms/slack/commands/sections.js +37 -0
  110. package/dist/src/platforms/slack/commands/sections.js.map +1 -0
  111. package/dist/src/platforms/slack/commands/unread.d.ts +12 -0
  112. package/dist/src/platforms/slack/commands/unread.d.ts.map +1 -0
  113. package/dist/src/platforms/slack/commands/unread.js +89 -0
  114. package/dist/src/platforms/slack/commands/unread.js.map +1 -0
  115. package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
  116. package/dist/src/platforms/slack/credential-manager.js +20 -6
  117. package/dist/src/platforms/slack/credential-manager.js.map +1 -1
  118. package/dist/src/platforms/slack/token-extractor.d.ts +8 -1
  119. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  120. package/dist/src/platforms/slack/token-extractor.js +83 -11
  121. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  122. package/dist/src/platforms/slack/types.d.ts +52 -0
  123. package/dist/src/platforms/slack/types.d.ts.map +1 -1
  124. package/dist/src/platforms/slack/types.js.map +1 -1
  125. package/dist/src/platforms/teams/cli.d.ts.map +1 -0
  126. package/dist/{cli.js → src/platforms/teams/cli.js} +11 -10
  127. package/dist/src/platforms/teams/cli.js.map +1 -0
  128. package/dist/src/platforms/teams/client.d.ts +32 -0
  129. package/dist/src/platforms/teams/client.d.ts.map +1 -0
  130. package/dist/src/platforms/teams/client.js +202 -0
  131. package/dist/src/platforms/teams/client.js.map +1 -0
  132. package/dist/src/platforms/teams/commands/auth.d.ts +14 -0
  133. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -0
  134. package/dist/src/platforms/teams/commands/auth.js +176 -0
  135. package/dist/src/platforms/teams/commands/auth.js.map +1 -0
  136. package/dist/src/platforms/teams/commands/channel.d.ts +13 -0
  137. package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -0
  138. package/dist/src/platforms/teams/commands/channel.js +97 -0
  139. package/dist/src/platforms/teams/commands/channel.js.map +1 -0
  140. package/dist/src/platforms/teams/commands/file.d.ts +12 -0
  141. package/dist/src/platforms/teams/commands/file.d.ts.map +1 -0
  142. package/dist/src/platforms/teams/commands/file.js +104 -0
  143. package/dist/src/platforms/teams/commands/file.js.map +1 -0
  144. package/dist/{commands → src/platforms/teams/commands}/index.d.ts +5 -2
  145. package/dist/src/platforms/teams/commands/index.d.ts.map +1 -0
  146. package/dist/{commands → src/platforms/teams/commands}/index.js +5 -2
  147. package/dist/src/platforms/teams/commands/index.js.map +1 -0
  148. package/dist/src/platforms/teams/commands/message.d.ts +17 -0
  149. package/dist/src/platforms/teams/commands/message.d.ts.map +1 -0
  150. package/dist/src/platforms/teams/commands/message.js +133 -0
  151. package/dist/src/platforms/teams/commands/message.js.map +1 -0
  152. package/dist/src/platforms/teams/commands/reaction.d.ts +9 -0
  153. package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -0
  154. package/dist/src/platforms/teams/commands/reaction.js +68 -0
  155. package/dist/src/platforms/teams/commands/reaction.js.map +1 -0
  156. package/dist/src/platforms/teams/commands/snapshot.d.ts +10 -0
  157. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -0
  158. package/dist/src/platforms/teams/commands/snapshot.js +85 -0
  159. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -0
  160. package/dist/src/platforms/teams/commands/team.d.ts +18 -0
  161. package/dist/src/platforms/teams/commands/team.d.ts.map +1 -0
  162. package/dist/src/platforms/teams/commands/team.js +130 -0
  163. package/dist/src/platforms/teams/commands/team.js.map +1 -0
  164. package/dist/src/platforms/teams/commands/user.d.ts.map +1 -0
  165. package/dist/src/platforms/teams/commands/user.js +88 -0
  166. package/dist/src/platforms/teams/commands/user.js.map +1 -0
  167. package/dist/src/platforms/teams/credential-manager.d.ts +18 -0
  168. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -0
  169. package/dist/src/platforms/teams/credential-manager.js +81 -0
  170. package/dist/src/platforms/teams/credential-manager.js.map +1 -0
  171. package/dist/src/platforms/teams/index.d.ts +4 -0
  172. package/dist/src/platforms/teams/index.d.ts.map +1 -0
  173. package/dist/src/platforms/teams/index.js +6 -0
  174. package/dist/src/platforms/teams/index.js.map +1 -0
  175. package/dist/src/platforms/teams/token-extractor.d.ts +41 -0
  176. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -0
  177. package/dist/src/platforms/teams/token-extractor.js +360 -0
  178. package/dist/src/platforms/teams/token-extractor.js.map +1 -0
  179. package/dist/src/platforms/teams/types.d.ts +209 -0
  180. package/dist/src/platforms/teams/types.d.ts.map +1 -0
  181. package/dist/src/platforms/teams/types.js +65 -0
  182. package/dist/src/platforms/teams/types.js.map +1 -0
  183. package/dist/src/shared/utils/derived-key-cache.d.ts +20 -0
  184. package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -0
  185. package/dist/src/shared/utils/derived-key-cache.js +52 -0
  186. package/dist/src/shared/utils/derived-key-cache.js.map +1 -0
  187. package/docs/README.md +113 -0
  188. package/docs/biome.json +8 -0
  189. package/docs/bun.lock +1426 -0
  190. package/docs/content/docs/agent-skills.mdx +98 -0
  191. package/docs/content/docs/index.mdx +124 -0
  192. package/docs/{discord.md → content/docs/integrations/discord.mdx} +46 -21
  193. package/docs/content/docs/integrations/meta.json +5 -0
  194. package/docs/{slack.md → content/docs/integrations/slack.mdx} +5 -4
  195. package/docs/content/docs/integrations/teams.mdx +322 -0
  196. package/docs/content/docs/meta.json +8 -0
  197. package/docs/content/docs/quick-start.mdx +103 -0
  198. package/docs/eslint.config.mjs +30 -0
  199. package/docs/next.config.ts +10 -0
  200. package/docs/package.json +42 -0
  201. package/docs/postcss.config.mjs +7 -0
  202. package/docs/public/file.svg +1 -0
  203. package/docs/public/globe.svg +1 -0
  204. package/docs/public/next.svg +1 -0
  205. package/docs/public/vercel.svg +1 -0
  206. package/docs/public/window.svg +1 -0
  207. package/docs/source.config.ts +11 -0
  208. package/docs/src/app/api/search/route.ts +4 -0
  209. package/docs/src/app/docs/[[...slug]]/page.tsx +53 -0
  210. package/docs/src/app/docs/layout.tsx +21 -0
  211. package/docs/src/app/globals.css +10 -0
  212. package/docs/src/app/icon.png +0 -0
  213. package/docs/src/app/layout.config.tsx +7 -0
  214. package/docs/src/app/layout.tsx +35 -0
  215. package/docs/src/app/page.tsx +489 -0
  216. package/docs/src/lib/source.ts +15 -0
  217. package/docs/src/mdx-components.tsx +18 -0
  218. package/docs/tsconfig.json +36 -0
  219. package/e2e/README.md +256 -0
  220. package/e2e/config.ts +51 -0
  221. package/e2e/discord.e2e.test.ts +252 -0
  222. package/e2e/helpers.ts +107 -0
  223. package/e2e/slack.e2e.test.ts +309 -0
  224. package/package.json +8 -4
  225. package/scripts/postbuild.ts +15 -0
  226. package/skills/agent-discord/SKILL.md +96 -30
  227. package/skills/agent-discord/references/authentication.md +27 -27
  228. package/skills/agent-discord/references/common-patterns.md +15 -15
  229. package/skills/agent-discord/templates/post-message.sh +6 -6
  230. package/skills/agent-discord/templates/{guild-summary.sh → server-summary.sh} +20 -20
  231. package/skills/agent-slack/SKILL.md +53 -0
  232. package/skills/agent-teams/SKILL.md +292 -0
  233. package/skills/agent-teams/references/authentication.md +375 -0
  234. package/skills/agent-teams/references/common-patterns.md +596 -0
  235. package/skills/agent-teams/templates/monitor-channel.sh +239 -0
  236. package/skills/agent-teams/templates/post-message.sh +224 -0
  237. package/skills/agent-teams/templates/team-summary.sh +210 -0
  238. package/src/cli.ts +4 -0
  239. package/src/platforms/discord/cli.ts +3 -3
  240. package/src/platforms/discord/client.test.ts +15 -15
  241. package/src/platforms/discord/client.ts +163 -17
  242. package/src/platforms/discord/commands/auth.test.ts +53 -37
  243. package/src/platforms/discord/commands/auth.ts +16 -16
  244. package/src/platforms/discord/commands/channel.test.ts +58 -46
  245. package/src/platforms/discord/commands/channel.ts +4 -4
  246. package/src/platforms/discord/commands/dm.test.ts +146 -0
  247. package/src/platforms/discord/commands/dm.ts +85 -0
  248. package/src/platforms/discord/commands/file.test.ts +40 -53
  249. package/src/platforms/discord/commands/friend.test.ts +134 -0
  250. package/src/platforms/discord/commands/friend.ts +45 -0
  251. package/src/platforms/discord/commands/index.ts +1 -1
  252. package/src/platforms/discord/commands/member.test.ts +98 -0
  253. package/src/platforms/discord/commands/member.ts +59 -0
  254. package/src/platforms/discord/commands/mention.test.ts +129 -0
  255. package/src/platforms/discord/commands/mention.ts +59 -0
  256. package/src/platforms/discord/commands/message.test.ts +139 -44
  257. package/src/platforms/discord/commands/message.ts +134 -1
  258. package/src/platforms/discord/commands/note.test.ts +84 -0
  259. package/src/platforms/discord/commands/note.ts +73 -0
  260. package/src/platforms/discord/commands/profile.test.ts +107 -0
  261. package/src/platforms/discord/commands/profile.ts +55 -0
  262. package/src/platforms/discord/commands/reaction.test.ts +54 -42
  263. package/src/platforms/discord/commands/server.test.ts +137 -0
  264. package/src/platforms/discord/commands/{guild.ts → server.ts} +31 -31
  265. package/src/platforms/discord/commands/snapshot.test.ts +1 -1
  266. package/src/platforms/discord/commands/snapshot.ts +10 -10
  267. package/src/platforms/discord/commands/thread.test.ts +121 -0
  268. package/src/platforms/discord/commands/thread.ts +92 -0
  269. package/src/platforms/discord/commands/user.test.ts +8 -8
  270. package/src/platforms/discord/commands/user.ts +16 -5
  271. package/src/platforms/discord/credential-manager.test.ts +137 -136
  272. package/src/platforms/discord/credential-manager.ts +35 -26
  273. package/src/platforms/discord/super-properties.ts +55 -0
  274. package/src/platforms/discord/token-extractor.test.ts +133 -383
  275. package/src/platforms/discord/token-extractor.ts +37 -3
  276. package/src/platforms/discord/types.test.ts +8 -8
  277. package/src/platforms/discord/types.ts +144 -8
  278. package/{tests → src/platforms/slack}/cli.test.ts +3 -3
  279. package/src/platforms/slack/cli.ts +10 -0
  280. package/{tests/slack-client.test.ts → src/platforms/slack/client.test.ts} +1 -1
  281. package/src/platforms/slack/client.ts +172 -1
  282. package/src/platforms/slack/commands/activity.test.ts +147 -0
  283. package/src/platforms/slack/commands/activity.ts +65 -0
  284. package/{tests → src/platforms/slack}/commands/auth.test.ts +25 -13
  285. package/{tests → src/platforms/slack}/commands/channel.test.ts +2 -2
  286. package/src/platforms/slack/commands/drafts.test.ts +136 -0
  287. package/src/platforms/slack/commands/drafts.ts +62 -0
  288. package/{tests → src/platforms/slack}/commands/file.test.ts +2 -2
  289. package/src/platforms/slack/commands/index.ts +5 -0
  290. package/{tests → src/platforms/slack}/commands/message.test.ts +2 -2
  291. package/{tests → src/platforms/slack}/commands/reaction.test.ts +1 -1
  292. package/src/platforms/slack/commands/saved.test.ts +140 -0
  293. package/src/platforms/slack/commands/saved.ts +71 -0
  294. package/src/platforms/slack/commands/sections.test.ts +80 -0
  295. package/src/platforms/slack/commands/sections.ts +50 -0
  296. package/{tests → src/platforms/slack}/commands/snapshot.test.ts +117 -105
  297. package/src/platforms/slack/commands/unread.test.ts +139 -0
  298. package/src/platforms/slack/commands/unread.ts +129 -0
  299. package/{tests → src/platforms/slack}/commands/user.test.ts +3 -3
  300. package/{tests → src/platforms/slack}/commands/workspace.test.ts +44 -95
  301. package/{tests → src/platforms/slack}/credential-manager.test.ts +2 -2
  302. package/src/platforms/slack/credential-manager.ts +22 -7
  303. package/src/platforms/slack/token-extractor-node-test.ts +40 -0
  304. package/src/platforms/slack/token-extractor-node.test.ts +10 -0
  305. package/src/platforms/slack/token-extractor.ts +93 -12
  306. package/{tests → src/platforms/slack}/types.test.ts +1 -1
  307. package/src/platforms/slack/types.ts +58 -0
  308. package/src/platforms/teams/cli.ts +36 -0
  309. package/src/platforms/teams/client.test.ts +500 -0
  310. package/src/platforms/teams/client.ts +365 -0
  311. package/src/platforms/teams/commands/auth.test.ts +99 -0
  312. package/src/platforms/teams/commands/auth.ts +232 -0
  313. package/src/platforms/teams/commands/channel.test.ts +147 -0
  314. package/src/platforms/teams/commands/channel.ts +129 -0
  315. package/src/platforms/teams/commands/file.test.ts +88 -0
  316. package/src/platforms/teams/commands/file.ts +144 -0
  317. package/src/platforms/teams/commands/index.ts +12 -0
  318. package/src/platforms/teams/commands/message.test.ts +110 -0
  319. package/src/platforms/teams/commands/message.ts +188 -0
  320. package/src/platforms/teams/commands/reaction.test.ts +87 -0
  321. package/src/platforms/teams/commands/reaction.ts +104 -0
  322. package/src/platforms/teams/commands/snapshot.test.ts +35 -0
  323. package/src/platforms/teams/commands/snapshot.ts +115 -0
  324. package/src/platforms/teams/commands/team.test.ts +157 -0
  325. package/src/platforms/teams/commands/team.ts +164 -0
  326. package/src/platforms/teams/commands/user.test.ts +83 -0
  327. package/src/platforms/teams/commands/user.ts +112 -0
  328. package/src/platforms/teams/credential-manager.test.ts +178 -0
  329. package/src/platforms/teams/credential-manager.ts +92 -0
  330. package/src/platforms/teams/index.ts +5 -0
  331. package/src/platforms/teams/token-extractor.test.ts +429 -0
  332. package/src/platforms/teams/token-extractor.ts +490 -0
  333. package/src/platforms/teams/types.test.ts +226 -0
  334. package/src/platforms/teams/types.ts +140 -0
  335. package/src/shared/utils/derived-key-cache.test.ts +136 -0
  336. package/src/shared/utils/derived-key-cache.ts +63 -0
  337. package/tsconfig.json +1 -1
  338. package/dist/cli.d.ts.map +0 -1
  339. package/dist/cli.js.map +0 -1
  340. package/dist/commands/auth.d.ts +0 -3
  341. package/dist/commands/auth.d.ts.map +0 -1
  342. package/dist/commands/auth.js +0 -140
  343. package/dist/commands/auth.js.map +0 -1
  344. package/dist/commands/channel.d.ts +0 -3
  345. package/dist/commands/channel.d.ts.map +0 -1
  346. package/dist/commands/channel.js +0 -118
  347. package/dist/commands/channel.js.map +0 -1
  348. package/dist/commands/file.d.ts +0 -3
  349. package/dist/commands/file.d.ts.map +0 -1
  350. package/dist/commands/file.js +0 -113
  351. package/dist/commands/file.js.map +0 -1
  352. package/dist/commands/index.d.ts.map +0 -1
  353. package/dist/commands/index.js.map +0 -1
  354. package/dist/commands/message.d.ts +0 -3
  355. package/dist/commands/message.d.ts.map +0 -1
  356. package/dist/commands/message.js +0 -214
  357. package/dist/commands/message.js.map +0 -1
  358. package/dist/commands/reaction.d.ts +0 -3
  359. package/dist/commands/reaction.d.ts.map +0 -1
  360. package/dist/commands/reaction.js +0 -100
  361. package/dist/commands/reaction.js.map +0 -1
  362. package/dist/commands/snapshot.d.ts +0 -3
  363. package/dist/commands/snapshot.d.ts.map +0 -1
  364. package/dist/commands/snapshot.js +0 -88
  365. package/dist/commands/snapshot.js.map +0 -1
  366. package/dist/commands/user.d.ts.map +0 -1
  367. package/dist/commands/user.js +0 -96
  368. package/dist/commands/user.js.map +0 -1
  369. package/dist/commands/workspace.d.ts +0 -3
  370. package/dist/commands/workspace.d.ts.map +0 -1
  371. package/dist/commands/workspace.js +0 -89
  372. package/dist/commands/workspace.js.map +0 -1
  373. package/dist/lib/credential-manager.d.ts +0 -13
  374. package/dist/lib/credential-manager.d.ts.map +0 -1
  375. package/dist/lib/credential-manager.js +0 -58
  376. package/dist/lib/credential-manager.js.map +0 -1
  377. package/dist/lib/index.d.ts +0 -3
  378. package/dist/lib/index.d.ts.map +0 -1
  379. package/dist/lib/index.js +0 -3
  380. package/dist/lib/index.js.map +0 -1
  381. package/dist/lib/ref-manager.d.ts +0 -26
  382. package/dist/lib/ref-manager.d.ts.map +0 -1
  383. package/dist/lib/ref-manager.js +0 -92
  384. package/dist/lib/ref-manager.js.map +0 -1
  385. package/dist/lib/slack-client.d.ts +0 -37
  386. package/dist/lib/slack-client.d.ts.map +0 -1
  387. package/dist/lib/slack-client.js +0 -379
  388. package/dist/lib/slack-client.js.map +0 -1
  389. package/dist/lib/token-extractor.d.ts +0 -28
  390. package/dist/lib/token-extractor.d.ts.map +0 -1
  391. package/dist/lib/token-extractor.js +0 -401
  392. package/dist/lib/token-extractor.js.map +0 -1
  393. package/dist/src/platforms/discord/client.test.d.ts +0 -2
  394. package/dist/src/platforms/discord/client.test.d.ts.map +0 -1
  395. package/dist/src/platforms/discord/client.test.js +0 -367
  396. package/dist/src/platforms/discord/client.test.js.map +0 -1
  397. package/dist/src/platforms/discord/commands/auth.test.d.ts +0 -2
  398. package/dist/src/platforms/discord/commands/auth.test.d.ts.map +0 -1
  399. package/dist/src/platforms/discord/commands/auth.test.js +0 -65
  400. package/dist/src/platforms/discord/commands/auth.test.js.map +0 -1
  401. package/dist/src/platforms/discord/commands/channel.test.d.ts +0 -2
  402. package/dist/src/platforms/discord/commands/channel.test.d.ts.map +0 -1
  403. package/dist/src/platforms/discord/commands/channel.test.js +0 -136
  404. package/dist/src/platforms/discord/commands/channel.test.js.map +0 -1
  405. package/dist/src/platforms/discord/commands/file.test.d.ts +0 -2
  406. package/dist/src/platforms/discord/commands/file.test.d.ts.map +0 -1
  407. package/dist/src/platforms/discord/commands/file.test.js +0 -83
  408. package/dist/src/platforms/discord/commands/file.test.js.map +0 -1
  409. package/dist/src/platforms/discord/commands/guild.test.d.ts +0 -2
  410. package/dist/src/platforms/discord/commands/guild.test.d.ts.map +0 -1
  411. package/dist/src/platforms/discord/commands/guild.test.js +0 -100
  412. package/dist/src/platforms/discord/commands/guild.test.js.map +0 -1
  413. package/dist/src/platforms/discord/commands/message.test.d.ts +0 -2
  414. package/dist/src/platforms/discord/commands/message.test.d.ts.map +0 -1
  415. package/dist/src/platforms/discord/commands/message.test.js +0 -91
  416. package/dist/src/platforms/discord/commands/message.test.js.map +0 -1
  417. package/dist/src/platforms/discord/commands/reaction.test.d.ts +0 -2
  418. package/dist/src/platforms/discord/commands/reaction.test.d.ts.map +0 -1
  419. package/dist/src/platforms/discord/commands/reaction.test.js +0 -115
  420. package/dist/src/platforms/discord/commands/reaction.test.js.map +0 -1
  421. package/dist/src/platforms/discord/commands/snapshot.test.d.ts +0 -2
  422. package/dist/src/platforms/discord/commands/snapshot.test.d.ts.map +0 -1
  423. package/dist/src/platforms/discord/commands/snapshot.test.js +0 -25
  424. package/dist/src/platforms/discord/commands/snapshot.test.js.map +0 -1
  425. package/dist/src/platforms/discord/commands/user.test.d.ts +0 -2
  426. package/dist/src/platforms/discord/commands/user.test.d.ts.map +0 -1
  427. package/dist/src/platforms/discord/commands/user.test.js +0 -103
  428. package/dist/src/platforms/discord/commands/user.test.js.map +0 -1
  429. package/dist/src/platforms/discord/credential-manager.test.d.ts +0 -2
  430. package/dist/src/platforms/discord/credential-manager.test.d.ts.map +0 -1
  431. package/dist/src/platforms/discord/credential-manager.test.js +0 -136
  432. package/dist/src/platforms/discord/credential-manager.test.js.map +0 -1
  433. package/dist/src/platforms/discord/token-extractor.test.d.ts +0 -2
  434. package/dist/src/platforms/discord/token-extractor.test.d.ts.map +0 -1
  435. package/dist/src/platforms/discord/token-extractor.test.js +0 -789
  436. package/dist/src/platforms/discord/token-extractor.test.js.map +0 -1
  437. package/dist/src/platforms/discord/types.test.d.ts +0 -2
  438. package/dist/src/platforms/discord/types.test.d.ts.map +0 -1
  439. package/dist/src/platforms/discord/types.test.js +0 -211
  440. package/dist/src/platforms/discord/types.test.js.map +0 -1
  441. package/dist/src/shared/utils/concurrency.test.d.ts +0 -2
  442. package/dist/src/shared/utils/concurrency.test.d.ts.map +0 -1
  443. package/dist/src/shared/utils/concurrency.test.js +0 -39
  444. package/dist/src/shared/utils/concurrency.test.js.map +0 -1
  445. package/dist/tests/cli.test.d.ts +0 -2
  446. package/dist/tests/cli.test.d.ts.map +0 -1
  447. package/dist/tests/cli.test.js +0 -83
  448. package/dist/tests/cli.test.js.map +0 -1
  449. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/CURRENT +0 -1
  450. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOCK +0 -0
  451. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG +0 -3
  452. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG.old +0 -1
  453. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/MANIFEST-000004 +0 -0
  454. package/dist/tests/commands/auth.test.d.ts +0 -2
  455. package/dist/tests/commands/auth.test.d.ts.map +0 -1
  456. package/dist/tests/commands/auth.test.js +0 -304
  457. package/dist/tests/commands/auth.test.js.map +0 -1
  458. package/dist/tests/commands/channel.test.d.ts +0 -2
  459. package/dist/tests/commands/channel.test.d.ts.map +0 -1
  460. package/dist/tests/commands/channel.test.js +0 -166
  461. package/dist/tests/commands/channel.test.js.map +0 -1
  462. package/dist/tests/commands/file.test.d.ts +0 -2
  463. package/dist/tests/commands/file.test.d.ts.map +0 -1
  464. package/dist/tests/commands/file.test.js +0 -175
  465. package/dist/tests/commands/file.test.js.map +0 -1
  466. package/dist/tests/commands/message.test.d.ts +0 -2
  467. package/dist/tests/commands/message.test.d.ts.map +0 -1
  468. package/dist/tests/commands/message.test.js +0 -293
  469. package/dist/tests/commands/message.test.js.map +0 -1
  470. package/dist/tests/commands/reaction.test.d.ts +0 -2
  471. package/dist/tests/commands/reaction.test.d.ts.map +0 -1
  472. package/dist/tests/commands/reaction.test.js +0 -84
  473. package/dist/tests/commands/reaction.test.js.map +0 -1
  474. package/dist/tests/commands/snapshot.test.d.ts +0 -2
  475. package/dist/tests/commands/snapshot.test.d.ts.map +0 -1
  476. package/dist/tests/commands/snapshot.test.js +0 -280
  477. package/dist/tests/commands/snapshot.test.js.map +0 -1
  478. package/dist/tests/commands/user.test.d.ts +0 -2
  479. package/dist/tests/commands/user.test.d.ts.map +0 -1
  480. package/dist/tests/commands/user.test.js +0 -117
  481. package/dist/tests/commands/user.test.js.map +0 -1
  482. package/dist/tests/commands/workspace.test.d.ts +0 -2
  483. package/dist/tests/commands/workspace.test.d.ts.map +0 -1
  484. package/dist/tests/commands/workspace.test.js +0 -453
  485. package/dist/tests/commands/workspace.test.js.map +0 -1
  486. package/dist/tests/credential-manager.test.d.ts +0 -2
  487. package/dist/tests/credential-manager.test.d.ts.map +0 -1
  488. package/dist/tests/credential-manager.test.js +0 -199
  489. package/dist/tests/credential-manager.test.js.map +0 -1
  490. package/dist/tests/slack-client.test.d.ts +0 -2
  491. package/dist/tests/slack-client.test.d.ts.map +0 -1
  492. package/dist/tests/slack-client.test.js +0 -741
  493. package/dist/tests/slack-client.test.js.map +0 -1
  494. package/dist/tests/types.test.d.ts +0 -2
  495. package/dist/tests/types.test.d.ts.map +0 -1
  496. package/dist/tests/types.test.js +0 -215
  497. package/dist/tests/types.test.js.map +0 -1
  498. package/dist/types/index.d.ts +0 -369
  499. package/dist/types/index.d.ts.map +0 -1
  500. package/dist/types/index.js +0 -92
  501. package/dist/types/index.js.map +0 -1
  502. package/dist/utils/error-handler.d.ts +0 -2
  503. package/dist/utils/error-handler.d.ts.map +0 -1
  504. package/dist/utils/error-handler.js +0 -5
  505. package/dist/utils/error-handler.js.map +0 -1
  506. package/dist/utils/output.d.ts +0 -2
  507. package/dist/utils/output.d.ts.map +0 -1
  508. package/dist/utils/output.js +0 -4
  509. package/dist/utils/output.js.map +0 -1
  510. package/src/platforms/discord/commands/guild.test.ts +0 -117
  511. /package/dist/{cli.d.ts → src/platforms/teams/cli.d.ts} +0 -0
  512. /package/dist/{commands → src/platforms/teams/commands}/user.d.ts +0 -0
@@ -0,0 +1,490 @@
1
+ import { execSync } from 'node:child_process'
2
+ import { createDecipheriv, pbkdf2Sync } from 'node:crypto'
3
+ import { copyFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs'
4
+ import { homedir, tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+ import { DerivedKeyCache } from '../../shared/utils/derived-key-cache'
7
+
8
+ export interface ExtractedTeamsToken {
9
+ token: string
10
+ }
11
+
12
+ interface KeychainVariant {
13
+ service: string
14
+ account: string
15
+ }
16
+
17
+ const TEAMS_PROCESS_NAMES: Record<string, string> = {
18
+ darwin: 'Microsoft Teams',
19
+ win32: 'Teams.exe',
20
+ linux: 'teams',
21
+ }
22
+
23
+ const SKYPETOKEN_COOKIE_NAME = 'skypetoken_asm'
24
+ const TEAMS_HOST_PATTERNS = [
25
+ '.asyncgw.teams.microsoft.com',
26
+ '.asm.skype.com',
27
+ 'teams.microsoft.com',
28
+ 'teams.live.com',
29
+ '.microsoft.com',
30
+ ]
31
+
32
+ export class TeamsTokenExtractor {
33
+ private platform: NodeJS.Platform
34
+ private keyCache: DerivedKeyCache
35
+ private cachedKey: Buffer | null = null
36
+
37
+ constructor(platform?: NodeJS.Platform, keyCache?: DerivedKeyCache) {
38
+ this.platform = platform ?? process.platform
39
+ this.keyCache = keyCache ?? new DerivedKeyCache()
40
+ }
41
+
42
+ getTeamsCookiesPaths(): string[] {
43
+ switch (this.platform) {
44
+ case 'darwin':
45
+ return [
46
+ join(
47
+ homedir(),
48
+ 'Library',
49
+ 'Containers',
50
+ 'com.microsoft.teams2',
51
+ 'Data',
52
+ 'Library',
53
+ 'Application Support',
54
+ 'Microsoft',
55
+ 'MSTeams',
56
+ 'EBWebView',
57
+ 'WV2Profile_tfw',
58
+ 'Cookies'
59
+ ),
60
+ join(
61
+ homedir(),
62
+ 'Library',
63
+ 'Containers',
64
+ 'com.microsoft.teams2',
65
+ 'Data',
66
+ 'Library',
67
+ 'Application Support',
68
+ 'Microsoft',
69
+ 'MSTeams',
70
+ 'EBWebView',
71
+ 'WV2Profile_tfl',
72
+ 'Cookies'
73
+ ),
74
+ join(
75
+ homedir(),
76
+ 'Library',
77
+ 'Containers',
78
+ 'com.microsoft.teams2',
79
+ 'Data',
80
+ 'Library',
81
+ 'Application Support',
82
+ 'Microsoft',
83
+ 'MSTeams',
84
+ 'EBWebView',
85
+ 'Default',
86
+ 'Cookies'
87
+ ),
88
+ join(homedir(), 'Library', 'Application Support', 'Microsoft', 'Teams', 'Cookies'),
89
+ ]
90
+ case 'linux':
91
+ return [join(homedir(), '.config', 'Microsoft', 'Microsoft Teams', 'Cookies')]
92
+ case 'win32': {
93
+ const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
94
+ const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
95
+ return [
96
+ // New Teams (MSIX/Store) - WebView2 profile paths
97
+ join(
98
+ localAppData,
99
+ 'Packages',
100
+ 'MSTeams_8wekyb3d8bbwe',
101
+ 'LocalCache',
102
+ 'Microsoft',
103
+ 'MSTeams',
104
+ 'EBWebView',
105
+ 'WV2Profile_tfw',
106
+ 'Cookies'
107
+ ),
108
+ join(
109
+ localAppData,
110
+ 'Packages',
111
+ 'MSTeams_8wekyb3d8bbwe',
112
+ 'LocalCache',
113
+ 'Microsoft',
114
+ 'MSTeams',
115
+ 'EBWebView',
116
+ 'WV2Profile_tfl',
117
+ 'Cookies'
118
+ ),
119
+ join(
120
+ localAppData,
121
+ 'Packages',
122
+ 'MSTeams_8wekyb3d8bbwe',
123
+ 'LocalCache',
124
+ 'Microsoft',
125
+ 'MSTeams',
126
+ 'EBWebView',
127
+ 'Default',
128
+ 'Cookies'
129
+ ),
130
+ // Classic Teams fallback
131
+ join(appdata, 'Microsoft', 'Teams', 'Cookies'),
132
+ ]
133
+ }
134
+ default:
135
+ return []
136
+ }
137
+ }
138
+
139
+ getLocalStatePath(): string {
140
+ switch (this.platform) {
141
+ case 'darwin':
142
+ return join(
143
+ homedir(),
144
+ 'Library',
145
+ 'Application Support',
146
+ 'Microsoft',
147
+ 'Teams',
148
+ 'Local State'
149
+ )
150
+ case 'linux':
151
+ return join(homedir(), '.config', 'Microsoft', 'Microsoft Teams', 'Local State')
152
+ case 'win32': {
153
+ const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
154
+ const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
155
+ const newTeamsPath = join(
156
+ localAppData,
157
+ 'Packages',
158
+ 'MSTeams_8wekyb3d8bbwe',
159
+ 'LocalCache',
160
+ 'Microsoft',
161
+ 'MSTeams',
162
+ 'EBWebView',
163
+ 'Local State'
164
+ )
165
+ if (existsSync(newTeamsPath)) return newTeamsPath
166
+ return join(appdata, 'Microsoft', 'Teams', 'Local State')
167
+ }
168
+ default:
169
+ return ''
170
+ }
171
+ }
172
+
173
+ getKeychainVariants(): KeychainVariant[] {
174
+ return [
175
+ // New Teams (com.microsoft.teams2) keychain entry - try first
176
+ { service: 'Microsoft Teams Safe Storage', account: 'Microsoft Teams' },
177
+ // Work/school variant
178
+ {
179
+ service: 'Microsoft Teams (work or school) Safe Storage',
180
+ account: 'Microsoft Teams (work or school)',
181
+ },
182
+ // Edge WebView2 fallback
183
+ { service: 'Microsoft Edge Safe Storage', account: 'Microsoft Edge' },
184
+ // Classic Teams fallback
185
+ { service: 'Teams Safe Storage', account: 'Teams' },
186
+ ]
187
+ }
188
+
189
+ isValidSkypeToken(token: string): boolean {
190
+ if (!token || token.length === 0) return false
191
+ // Skype tokens are typically JWT format or long base64 strings (50+ chars)
192
+ return token.length >= 50
193
+ }
194
+
195
+ isEncryptedValue(value: Buffer): boolean {
196
+ if (!value || value.length < 4) return false
197
+ const prefix = value.subarray(0, 3).toString('utf8')
198
+ return prefix === 'v10' || prefix === 'v11'
199
+ }
200
+
201
+ async extract(): Promise<ExtractedTeamsToken | null> {
202
+ await this.loadCachedKey()
203
+
204
+ const cookieToken = await this.extractFromCookiesDB()
205
+ if (cookieToken && this.isValidSkypeToken(cookieToken)) {
206
+ return { token: cookieToken }
207
+ }
208
+
209
+ return null
210
+ }
211
+
212
+ private async loadCachedKey(): Promise<void> {
213
+ if (this.platform !== 'darwin') return
214
+
215
+ const cached = await this.keyCache.get('teams')
216
+ if (cached) {
217
+ this.cachedKey = cached
218
+ }
219
+ }
220
+
221
+ async clearKeyCache(): Promise<void> {
222
+ await this.keyCache.clear('teams')
223
+ this.cachedKey = null
224
+ }
225
+
226
+ private async extractFromCookiesDB(): Promise<string | null> {
227
+ const dbPaths = this.getTeamsCookiesPaths()
228
+
229
+ for (const dbPath of dbPaths) {
230
+ if (!dbPath || !existsSync(dbPath)) continue
231
+
232
+ // Try copy-first strategy to handle file locking
233
+ const token = await this.copyAndExtract(dbPath)
234
+ if (token) return token
235
+ }
236
+
237
+ return null
238
+ }
239
+
240
+ private async copyAndExtract(dbPath: string): Promise<string | null> {
241
+ const tempPath = join(tmpdir(), `teams-cookies-${Date.now()}`)
242
+
243
+ try {
244
+ this.copyDatabaseToTemp(dbPath, tempPath)
245
+ const token = await this.extractFromSQLite(tempPath)
246
+ this.cleanupTempFile(tempPath)
247
+ return token
248
+ } catch {
249
+ // File locked or copy failed
250
+ this.cleanupTempFile(tempPath)
251
+ return null
252
+ }
253
+ }
254
+
255
+ private copyDatabaseToTemp(sourcePath: string, destPath: string): string {
256
+ copyFileSync(sourcePath, destPath)
257
+ return destPath
258
+ }
259
+
260
+ private cleanupTempFile(tempPath: string): void {
261
+ try {
262
+ if (existsSync(tempPath)) {
263
+ unlinkSync(tempPath)
264
+ }
265
+ } catch {
266
+ // Ignore cleanup errors
267
+ }
268
+ }
269
+
270
+ private async extractFromSQLite(dbPath: string): Promise<string | null> {
271
+ try {
272
+ for (const hostPattern of TEAMS_HOST_PATTERNS) {
273
+ const sql = `
274
+ SELECT encrypted_value
275
+ FROM cookies
276
+ WHERE name = '${SKYPETOKEN_COOKIE_NAME}'
277
+ AND host_key LIKE '%${hostPattern}%'
278
+ LIMIT 1
279
+ `
280
+
281
+ type CookieRow = { encrypted_value?: Uint8Array | Buffer } | null
282
+
283
+ let row: CookieRow
284
+ if (typeof globalThis.Bun !== 'undefined') {
285
+ const { Database } = require('bun:sqlite')
286
+ const db = new Database(dbPath, { readonly: true })
287
+ row = db.query(sql).get() as CookieRow
288
+ db.close()
289
+ } else {
290
+ const Database = require('better-sqlite3')
291
+ const db = new Database(dbPath, { readonly: true })
292
+ row = db.prepare(sql).get() as CookieRow
293
+ db.close()
294
+ }
295
+
296
+ if (row?.encrypted_value) {
297
+ const decrypted = this.decryptCookie(Buffer.from(row.encrypted_value))
298
+ if (decrypted && this.isValidSkypeToken(decrypted)) {
299
+ return decrypted
300
+ }
301
+ }
302
+ }
303
+
304
+ return null
305
+ } catch {
306
+ return null
307
+ }
308
+ }
309
+
310
+ private decryptCookie(encryptedValue: Buffer): string | null {
311
+ if (!this.isEncryptedValue(encryptedValue)) {
312
+ // Not encrypted, return as-is
313
+ return encryptedValue.toString('utf8')
314
+ }
315
+
316
+ if (this.platform === 'win32') {
317
+ return this.decryptWindowsCookie(encryptedValue)
318
+ } else if (this.platform === 'darwin') {
319
+ return this.decryptMacCookie(encryptedValue)
320
+ } else if (this.platform === 'linux') {
321
+ return this.decryptLinuxCookie(encryptedValue)
322
+ }
323
+
324
+ return null
325
+ }
326
+
327
+ private decryptWindowsCookie(encryptedData: Buffer): string | null {
328
+ try {
329
+ const localStatePath = this.getLocalStatePath()
330
+ if (!existsSync(localStatePath)) return null
331
+
332
+ const localState = JSON.parse(readFileSync(localStatePath, 'utf8'))
333
+ const encryptedKey = Buffer.from(localState.os_crypt.encrypted_key, 'base64')
334
+
335
+ // Remove DPAPI prefix (5 bytes)
336
+ const dpapiBlobKey = encryptedKey.subarray(5)
337
+ const masterKey = this.decryptDPAPI(dpapiBlobKey)
338
+ if (!masterKey) return null
339
+
340
+ return this.decryptAESGCM(encryptedData, masterKey)
341
+ } catch {
342
+ return null
343
+ }
344
+ }
345
+
346
+ private decryptDPAPI(encryptedBlob: Buffer): Buffer | null {
347
+ try {
348
+ const b64 = encryptedBlob.toString('base64')
349
+ const psScript = `
350
+ Add-Type -AssemblyName System.Security
351
+ $bytes = [Convert]::FromBase64String('${b64}')
352
+ $decrypted = [Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, 'CurrentUser')
353
+ [Convert]::ToBase64String($decrypted)
354
+ `.replace(/\n/g, ' ')
355
+
356
+ const result = execSync(`powershell -Command "${psScript}"`, { encoding: 'utf8' })
357
+ return Buffer.from(result.trim(), 'base64')
358
+ } catch {
359
+ return null
360
+ }
361
+ }
362
+
363
+ private decryptMacCookie(encryptedData: Buffer): string | null {
364
+ if (this.cachedKey) {
365
+ const decrypted = this.decryptAESCBC(encryptedData, this.cachedKey)
366
+ if (decrypted) return decrypted
367
+ }
368
+
369
+ const password = this.getKeychainPassword()
370
+ if (!password) return null
371
+
372
+ const key = pbkdf2Sync(password, 'saltysalt', 1003, 16, 'sha1')
373
+ const decrypted = this.decryptAESCBC(encryptedData, key)
374
+ if (decrypted) {
375
+ this.cachedKey = key
376
+ this.keyCache.set('teams', key).catch(() => {})
377
+ }
378
+ return decrypted
379
+ }
380
+
381
+ private decryptLinuxCookie(encryptedData: Buffer): string | null {
382
+ // Linux uses a hardcoded password 'peanuts' for Chromium-based apps
383
+ const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
384
+ return this.decryptAESCBC(encryptedData, key)
385
+ }
386
+
387
+ private getKeychainPassword(): string | null {
388
+ const variants = this.getKeychainVariants()
389
+
390
+ for (const variant of variants) {
391
+ const password = this.execSecurityCommand(variant.service, variant.account)
392
+ if (password) return password
393
+ }
394
+
395
+ return null
396
+ }
397
+
398
+ private execSecurityCommand(service: string, account: string): string | null {
399
+ try {
400
+ // Escape double quotes in service/account to prevent command injection
401
+ const safeService = service.replace(/"/g, '\\"')
402
+ const safeAccount = account.replace(/"/g, '\\"')
403
+ const result = execSync(
404
+ `security find-generic-password -s "${safeService}" -a "${safeAccount}" -w 2>/dev/null`,
405
+ { encoding: 'utf8' }
406
+ )
407
+ return result.trim()
408
+ } catch {
409
+ return null
410
+ }
411
+ }
412
+
413
+ private decryptAESCBC(encryptedData: Buffer, key: Buffer): string | null {
414
+ try {
415
+ const ciphertext = encryptedData.subarray(3)
416
+ const iv = Buffer.alloc(16, 0x20)
417
+
418
+ const decipher = createDecipheriv('aes-128-cbc', key, iv)
419
+ decipher.setAutoPadding(true)
420
+
421
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
422
+ const decryptedStr = decrypted.toString('utf8')
423
+
424
+ // Chromium v24+ prepends a 32-byte integrity hash before the actual value
425
+ // Look for JWT token start (eyJ) or other token patterns
426
+ const jwtStart = decryptedStr.indexOf('eyJ')
427
+ if (jwtStart > 0 && jwtStart <= 32) {
428
+ return decryptedStr.substring(jwtStart)
429
+ }
430
+
431
+ // If no JWT prefix found but decryption succeeded, check if first 32 bytes are garbage
432
+ if (decrypted.length > 32) {
433
+ const possibleToken = decryptedStr.substring(32)
434
+ if (possibleToken.length > 50 && /^[A-Za-z0-9._-]+$/.test(possibleToken.substring(0, 50))) {
435
+ return possibleToken
436
+ }
437
+ }
438
+
439
+ return decryptedStr
440
+ } catch {
441
+ return null
442
+ }
443
+ }
444
+
445
+ private decryptAESGCM(encryptedData: Buffer, key: Buffer): string | null {
446
+ try {
447
+ // Format: v10 (3 bytes) + IV (12 bytes) + ciphertext + auth tag (16 bytes)
448
+ if (encryptedData.length < 3 + 12 + 16) return null
449
+
450
+ const iv = encryptedData.subarray(3, 15)
451
+ const authTag = encryptedData.subarray(-16)
452
+ const ciphertext = encryptedData.subarray(15, -16)
453
+
454
+ const decipher = createDecipheriv('aes-256-gcm', key, iv)
455
+ decipher.setAuthTag(authTag)
456
+
457
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
458
+ return decrypted.toString('utf8')
459
+ } catch {
460
+ return null
461
+ }
462
+ }
463
+
464
+ async isTeamsRunning(): Promise<boolean> {
465
+ const processName = this.getProcessName()
466
+ return this.checkProcessRunning(processName)
467
+ }
468
+
469
+ private getProcessName(): string {
470
+ return TEAMS_PROCESS_NAMES[this.platform] || TEAMS_PROCESS_NAMES.linux
471
+ }
472
+
473
+ private checkProcessRunning(processName: string): boolean {
474
+ try {
475
+ if (this.platform === 'win32') {
476
+ const result = execSync(`tasklist /FI "IMAGENAME eq ${processName}" 2>nul`, {
477
+ encoding: 'utf8',
478
+ })
479
+ return result.toLowerCase().includes(processName.toLowerCase())
480
+ } else {
481
+ const result = execSync(`pgrep -f "${processName}" 2>/dev/null || true`, {
482
+ encoding: 'utf8',
483
+ })
484
+ return result.trim().length > 0
485
+ }
486
+ } catch {
487
+ return false
488
+ }
489
+ }
490
+ }
@@ -0,0 +1,226 @@
1
+ import { expect, test } from 'bun:test'
2
+ import {
3
+ TeamsChannelSchema,
4
+ TeamsConfigSchema,
5
+ TeamsCredentialsSchema,
6
+ TeamsError,
7
+ TeamsFileSchema,
8
+ TeamsMessageSchema,
9
+ TeamsReactionSchema,
10
+ TeamsTeamSchema,
11
+ TeamsUserSchema,
12
+ } from './types'
13
+
14
+ // TeamsTeamSchema tests
15
+ test('TeamsTeamSchema validates correct team', () => {
16
+ const result = TeamsTeamSchema.safeParse({
17
+ id: '19:abc123@thread.tacv2',
18
+ name: 'Test Team',
19
+ })
20
+ expect(result.success).toBe(true)
21
+ })
22
+
23
+ test('TeamsTeamSchema validates team with optional description', () => {
24
+ const result = TeamsTeamSchema.safeParse({
25
+ id: '19:abc123@thread.tacv2',
26
+ name: 'Test Team',
27
+ description: 'A test team',
28
+ })
29
+ expect(result.success).toBe(true)
30
+ })
31
+
32
+ test('TeamsTeamSchema rejects missing id', () => {
33
+ const result = TeamsTeamSchema.safeParse({
34
+ name: 'Test Team',
35
+ })
36
+ expect(result.success).toBe(false)
37
+ })
38
+
39
+ test('TeamsTeamSchema rejects missing name', () => {
40
+ const result = TeamsTeamSchema.safeParse({
41
+ id: '19:abc123@thread.tacv2',
42
+ })
43
+ expect(result.success).toBe(false)
44
+ })
45
+
46
+ // TeamsChannelSchema tests
47
+ test('TeamsChannelSchema validates correct channel', () => {
48
+ const result = TeamsChannelSchema.safeParse({
49
+ id: '19:channel123@thread.tacv2',
50
+ team_id: '19:abc123@thread.tacv2',
51
+ name: 'General',
52
+ type: 'standard',
53
+ })
54
+ expect(result.success).toBe(true)
55
+ })
56
+
57
+ test('TeamsChannelSchema rejects missing required fields', () => {
58
+ const result = TeamsChannelSchema.safeParse({
59
+ id: '19:channel123@thread.tacv2',
60
+ name: 'General',
61
+ })
62
+ expect(result.success).toBe(false)
63
+ })
64
+
65
+ // TeamsMessageSchema tests
66
+ test('TeamsMessageSchema validates correct message', () => {
67
+ const result = TeamsMessageSchema.safeParse({
68
+ id: '1234567890123',
69
+ channel_id: '19:channel123@thread.tacv2',
70
+ author: {
71
+ id: 'user123',
72
+ displayName: 'Test User',
73
+ },
74
+ content: 'Hello world',
75
+ timestamp: '2024-01-01T00:00:00.000Z',
76
+ })
77
+ expect(result.success).toBe(true)
78
+ })
79
+
80
+ test('TeamsMessageSchema rejects missing required fields', () => {
81
+ const result = TeamsMessageSchema.safeParse({
82
+ id: '1234567890123',
83
+ channel_id: '19:channel123@thread.tacv2',
84
+ content: 'Hello world',
85
+ })
86
+ expect(result.success).toBe(false)
87
+ })
88
+
89
+ // TeamsUserSchema tests
90
+ test('TeamsUserSchema validates correct user', () => {
91
+ const result = TeamsUserSchema.safeParse({
92
+ id: 'user123',
93
+ displayName: 'Test User',
94
+ })
95
+ expect(result.success).toBe(true)
96
+ })
97
+
98
+ test('TeamsUserSchema validates user with optional fields', () => {
99
+ const result = TeamsUserSchema.safeParse({
100
+ id: 'user123',
101
+ displayName: 'Test User',
102
+ email: 'test@example.com',
103
+ userPrincipalName: 'test@example.onmicrosoft.com',
104
+ })
105
+ expect(result.success).toBe(true)
106
+ })
107
+
108
+ test('TeamsUserSchema rejects missing required fields', () => {
109
+ const result = TeamsUserSchema.safeParse({
110
+ id: 'user123',
111
+ })
112
+ expect(result.success).toBe(false)
113
+ })
114
+
115
+ // TeamsReactionSchema tests
116
+ test('TeamsReactionSchema validates correct reaction', () => {
117
+ const result = TeamsReactionSchema.safeParse({
118
+ emoji: 'like',
119
+ count: 5,
120
+ })
121
+ expect(result.success).toBe(true)
122
+ })
123
+
124
+ test('TeamsReactionSchema rejects missing required fields', () => {
125
+ const result = TeamsReactionSchema.safeParse({
126
+ emoji: 'like',
127
+ })
128
+ expect(result.success).toBe(false)
129
+ })
130
+
131
+ // TeamsFileSchema tests
132
+ test('TeamsFileSchema validates correct file', () => {
133
+ const result = TeamsFileSchema.safeParse({
134
+ id: 'file123',
135
+ name: 'document.pdf',
136
+ size: 1024,
137
+ url: 'https://teams.microsoft.com/files/...',
138
+ })
139
+ expect(result.success).toBe(true)
140
+ })
141
+
142
+ test('TeamsFileSchema validates file with optional contentType', () => {
143
+ const result = TeamsFileSchema.safeParse({
144
+ id: 'file123',
145
+ name: 'document.pdf',
146
+ size: 1024,
147
+ url: 'https://teams.microsoft.com/files/...',
148
+ contentType: 'application/pdf',
149
+ })
150
+ expect(result.success).toBe(true)
151
+ })
152
+
153
+ test('TeamsFileSchema rejects missing required fields', () => {
154
+ const result = TeamsFileSchema.safeParse({
155
+ id: 'file123',
156
+ name: 'document.pdf',
157
+ })
158
+ expect(result.success).toBe(false)
159
+ })
160
+
161
+ // TeamsCredentialsSchema tests
162
+ test('TeamsCredentialsSchema validates correct credentials', () => {
163
+ const result = TeamsCredentialsSchema.safeParse({
164
+ token: 'skypetoken_value',
165
+ })
166
+ expect(result.success).toBe(true)
167
+ })
168
+
169
+ test('TeamsCredentialsSchema validates credentials with cookie', () => {
170
+ const result = TeamsCredentialsSchema.safeParse({
171
+ token: 'skypetoken_value',
172
+ cookie: 'skypetoken_asm_value',
173
+ })
174
+ expect(result.success).toBe(true)
175
+ })
176
+
177
+ test('TeamsCredentialsSchema rejects missing token', () => {
178
+ const result = TeamsCredentialsSchema.safeParse({})
179
+ expect(result.success).toBe(false)
180
+ })
181
+
182
+ // TeamsConfigSchema tests
183
+ test('TeamsConfigSchema validates correct config', () => {
184
+ const result = TeamsConfigSchema.safeParse({
185
+ current_team: null,
186
+ token: 'token_value',
187
+ teams: {},
188
+ })
189
+ expect(result.success).toBe(true)
190
+ })
191
+
192
+ test('TeamsConfigSchema validates config with token_expires_at', () => {
193
+ const result = TeamsConfigSchema.safeParse({
194
+ current_team: '19:abc123@thread.tacv2',
195
+ token: 'token_value',
196
+ token_expires_at: '2024-01-01T00:00:00.000Z',
197
+ teams: {
198
+ '19:abc123@thread.tacv2': {
199
+ team_id: '19:abc123@thread.tacv2',
200
+ team_name: 'Test Team',
201
+ },
202
+ },
203
+ })
204
+ expect(result.success).toBe(true)
205
+ })
206
+
207
+ test('TeamsConfigSchema rejects missing required fields', () => {
208
+ const result = TeamsConfigSchema.safeParse({
209
+ current_team: null,
210
+ token: 'token_value',
211
+ })
212
+ expect(result.success).toBe(false)
213
+ })
214
+
215
+ // TeamsError tests
216
+ test('TeamsError has correct name and code', () => {
217
+ const error = new TeamsError('Test error', 'TEST_CODE')
218
+ expect(error.name).toBe('TeamsError')
219
+ expect(error.message).toBe('Test error')
220
+ expect(error.code).toBe('TEST_CODE')
221
+ })
222
+
223
+ test('TeamsError is instance of Error', () => {
224
+ const error = new TeamsError('Test error', 'TEST_CODE')
225
+ expect(error instanceof Error).toBe(true)
226
+ })