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,500 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2
+ import { unlinkSync } from 'node:fs'
3
+ import { TeamsClient } from './client'
4
+ import { TeamsError } from './types'
5
+
6
+ const TEMP_FILES_TO_CLEANUP = ['/tmp/test-teams-upload.txt']
7
+
8
+ describe('TeamsClient', () => {
9
+ const originalFetch = globalThis.fetch
10
+ let fetchCalls: Array<{ url: string; options?: RequestInit }> = []
11
+ let fetchResponses: Response[] = []
12
+ let fetchIndex = 0
13
+
14
+ beforeEach(() => {
15
+ fetchCalls = []
16
+ fetchResponses = []
17
+ fetchIndex = 0
18
+ ;(globalThis as any).fetch = async (
19
+ url: string | URL | Request,
20
+ options?: RequestInit
21
+ ): Promise<Response> => {
22
+ fetchCalls.push({ url: url.toString(), options })
23
+ const response = fetchResponses[fetchIndex]
24
+ fetchIndex++
25
+ if (!response) {
26
+ throw new Error('No mock response configured')
27
+ }
28
+ return response
29
+ }
30
+ })
31
+
32
+ afterEach(() => {
33
+ globalThis.fetch = originalFetch
34
+ for (const file of TEMP_FILES_TO_CLEANUP) {
35
+ try {
36
+ unlinkSync(file)
37
+ } catch {
38
+ // File may not exist, ignore
39
+ }
40
+ }
41
+ })
42
+
43
+ const mockResponse = (body: unknown, status = 200, headers: Record<string, string> = {}) => {
44
+ const defaultHeaders: Record<string, string> = {
45
+ 'Content-Type': 'application/json',
46
+ 'X-RateLimit-Remaining': '10',
47
+ 'X-RateLimit-Reset': String(Date.now() / 1000 + 60),
48
+ ...headers,
49
+ }
50
+ fetchResponses.push(
51
+ new Response(body === null ? null : JSON.stringify(body), {
52
+ status,
53
+ headers: defaultHeaders,
54
+ })
55
+ )
56
+ }
57
+
58
+ describe('constructor', () => {
59
+ test('requires token', () => {
60
+ expect(() => new TeamsClient('')).toThrow(TeamsError)
61
+ expect(() => new TeamsClient('')).toThrow('Token is required')
62
+ })
63
+
64
+ test('accepts valid token', () => {
65
+ const client = new TeamsClient('test-token')
66
+ expect(client).toBeInstanceOf(TeamsClient)
67
+ })
68
+
69
+ test('accepts token with expiry time', () => {
70
+ const expiresAt = new Date(Date.now() + 3600000).toISOString()
71
+ const client = new TeamsClient('test-token', expiresAt)
72
+ expect(client).toBeInstanceOf(TeamsClient)
73
+ })
74
+ })
75
+
76
+ describe('token expiry', () => {
77
+ test('throws when token is expired', async () => {
78
+ const expiredAt = new Date(Date.now() - 1000).toISOString()
79
+ const client = new TeamsClient('expired-token', expiredAt)
80
+
81
+ await expect(client.testAuth()).rejects.toThrow(TeamsError)
82
+ await expect(client.testAuth()).rejects.toThrow('Token has expired')
83
+ })
84
+
85
+ test('works when token is not expired', async () => {
86
+ const expiresAt = new Date(Date.now() + 3600000).toISOString()
87
+ mockResponse({
88
+ userDetails: JSON.stringify({ name: 'Test User' }),
89
+ locale: 'en-us',
90
+ })
91
+
92
+ const client = new TeamsClient('valid-token', expiresAt)
93
+ const user = await client.testAuth()
94
+
95
+ expect(user.id).toBe('ME')
96
+ expect(user.displayName).toBe('Test User')
97
+ })
98
+ })
99
+
100
+ describe('testAuth', () => {
101
+ test('returns current user info', async () => {
102
+ mockResponse({
103
+ userDetails: JSON.stringify({ name: 'Test User' }),
104
+ locale: 'en-us',
105
+ })
106
+
107
+ const client = new TeamsClient('test-token')
108
+ const user = await client.testAuth()
109
+
110
+ expect(user.id).toBe('ME')
111
+ expect(user.displayName).toBe('Test User')
112
+ expect(fetchCalls.length).toBe(1)
113
+ expect(fetchCalls[0].url).toBe(
114
+ 'https://emea.ng.msg.teams.microsoft.com/v1/users/ME/properties'
115
+ )
116
+ expect(fetchCalls[0].options?.headers).toMatchObject({
117
+ 'X-Skypetoken': 'test-token',
118
+ })
119
+ })
120
+
121
+ test('throws TeamsError on API error', async () => {
122
+ mockResponse({ message: 'Unauthorized', code: 'unauthorized' }, 401)
123
+
124
+ const client = new TeamsClient('bad-token')
125
+ await expect(client.testAuth()).rejects.toThrow(TeamsError)
126
+ })
127
+ })
128
+
129
+ describe('listTeams', () => {
130
+ test('returns list of teams from conversations', async () => {
131
+ mockResponse({
132
+ conversations: [
133
+ {
134
+ id: '19:abc@thread.tacv2',
135
+ threadProperties: {
136
+ groupId: '111',
137
+ spaceThreadTopic: 'Team One',
138
+ productThreadType: 'TeamsChannel',
139
+ threadType: 'space',
140
+ },
141
+ },
142
+ {
143
+ id: '19:def@thread.tacv2',
144
+ threadProperties: {
145
+ groupId: '222',
146
+ spaceThreadTopic: 'Team Two',
147
+ productThreadType: 'TeamsPrivateChannel',
148
+ threadType: 'space',
149
+ },
150
+ },
151
+ {
152
+ id: '19:chat@thread.v2',
153
+ threadProperties: {
154
+ threadType: 'chat',
155
+ },
156
+ },
157
+ ],
158
+ })
159
+
160
+ const client = new TeamsClient('test-token')
161
+ const teams = await client.listTeams()
162
+
163
+ expect(teams).toHaveLength(2)
164
+ expect(teams[0].id).toBe('111')
165
+ expect(teams[0].name).toBe('Team One')
166
+ expect(teams[1].id).toBe('222')
167
+ expect(teams[1].name).toBe('Team Two')
168
+ expect(fetchCalls[0].url).toBe(
169
+ 'https://emea.ng.msg.teams.microsoft.com/v1/users/ME/conversations'
170
+ )
171
+ })
172
+ })
173
+
174
+ describe('getTeam', () => {
175
+ test('returns team info', async () => {
176
+ mockResponse({ id: '111', name: 'Test Team', description: 'A test team' })
177
+
178
+ const client = new TeamsClient('test-token')
179
+ const team = await client.getTeam('111')
180
+
181
+ expect(team.id).toBe('111')
182
+ expect(team.name).toBe('Test Team')
183
+ expect(fetchCalls[0].url).toBe('https://teams.microsoft.com/api/csa/api/v1/teams/111')
184
+ })
185
+ })
186
+
187
+ describe('listChannels', () => {
188
+ test('returns list of channels for team', async () => {
189
+ mockResponse([
190
+ { id: 'ch1', team_id: '111', name: 'General', type: 'standard' },
191
+ { id: 'ch2', team_id: '111', name: 'Random', type: 'standard' },
192
+ ])
193
+
194
+ const client = new TeamsClient('test-token')
195
+ const channels = await client.listChannels('111')
196
+
197
+ expect(channels).toHaveLength(2)
198
+ expect(channels[0].name).toBe('General')
199
+ expect(fetchCalls[0].url).toBe(
200
+ 'https://teams.microsoft.com/api/csa/api/v1/teams/111/channels'
201
+ )
202
+ })
203
+ })
204
+
205
+ describe('getChannel', () => {
206
+ test('returns channel info', async () => {
207
+ mockResponse({ id: 'ch1', team_id: '111', name: 'General', type: 'standard' })
208
+
209
+ const client = new TeamsClient('test-token')
210
+ const channel = await client.getChannel('111', 'ch1')
211
+
212
+ expect(channel.id).toBe('ch1')
213
+ expect(channel.name).toBe('General')
214
+ expect(fetchCalls[0].url).toBe(
215
+ 'https://teams.microsoft.com/api/csa/api/v1/teams/111/channels/ch1'
216
+ )
217
+ })
218
+ })
219
+
220
+ describe('sendMessage', () => {
221
+ test('sends message to channel', async () => {
222
+ mockResponse({
223
+ id: 'msg1',
224
+ channel_id: 'ch1',
225
+ author: { id: '123', displayName: 'Test User' },
226
+ content: 'Hello world',
227
+ timestamp: '2024-01-01T00:00:00.000Z',
228
+ })
229
+
230
+ const client = new TeamsClient('test-token')
231
+ const message = await client.sendMessage('111', 'ch1', 'Hello world')
232
+
233
+ expect(message.content).toBe('Hello world')
234
+ expect(fetchCalls[0].url).toBe(
235
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages'
236
+ )
237
+ expect(fetchCalls[0].options?.method).toBe('POST')
238
+ expect(fetchCalls[0].options?.body).toBe(JSON.stringify({ content: 'Hello world' }))
239
+ })
240
+ })
241
+
242
+ describe('getMessages', () => {
243
+ test('returns messages from channel', async () => {
244
+ mockResponse([
245
+ {
246
+ id: 'msg1',
247
+ channel_id: 'ch1',
248
+ author: { id: '123', displayName: 'User 1' },
249
+ content: 'Message 1',
250
+ timestamp: '2024-01-01T00:00:00.000Z',
251
+ },
252
+ ])
253
+
254
+ const client = new TeamsClient('test-token')
255
+ const messages = await client.getMessages('111', 'ch1', 50)
256
+
257
+ expect(messages).toHaveLength(1)
258
+ expect(messages[0].content).toBe('Message 1')
259
+ expect(fetchCalls[0].url).toBe(
260
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages?limit=50'
261
+ )
262
+ })
263
+
264
+ test('uses default limit of 50', async () => {
265
+ mockResponse([])
266
+
267
+ const client = new TeamsClient('test-token')
268
+ await client.getMessages('111', 'ch1')
269
+
270
+ expect(fetchCalls[0].url).toBe(
271
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages?limit=50'
272
+ )
273
+ })
274
+ })
275
+
276
+ describe('getMessage', () => {
277
+ test('returns single message', async () => {
278
+ mockResponse({
279
+ id: 'msg1',
280
+ channel_id: 'ch1',
281
+ author: { id: '123', displayName: 'User 1' },
282
+ content: 'Message 1',
283
+ timestamp: '2024-01-01T00:00:00.000Z',
284
+ })
285
+
286
+ const client = new TeamsClient('test-token')
287
+ const message = await client.getMessage('111', 'ch1', 'msg1')
288
+
289
+ expect(message.id).toBe('msg1')
290
+ expect(fetchCalls[0].url).toBe(
291
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages/msg1'
292
+ )
293
+ })
294
+ })
295
+
296
+ describe('deleteMessage', () => {
297
+ test('deletes message', async () => {
298
+ mockResponse(null, 204)
299
+
300
+ const client = new TeamsClient('test-token')
301
+ await client.deleteMessage('111', 'ch1', 'msg1')
302
+
303
+ expect(fetchCalls[0].url).toBe(
304
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages/msg1'
305
+ )
306
+ expect(fetchCalls[0].options?.method).toBe('DELETE')
307
+ })
308
+ })
309
+
310
+ describe('addReaction', () => {
311
+ test('adds reaction to message', async () => {
312
+ mockResponse(null, 204)
313
+
314
+ const client = new TeamsClient('test-token')
315
+ await client.addReaction('111', 'ch1', 'msg1', 'like')
316
+
317
+ expect(fetchCalls[0].url).toBe(
318
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages/msg1/reactions'
319
+ )
320
+ expect(fetchCalls[0].options?.method).toBe('POST')
321
+ expect(fetchCalls[0].options?.body).toBe(JSON.stringify({ emoji: 'like' }))
322
+ })
323
+ })
324
+
325
+ describe('removeReaction', () => {
326
+ test('removes reaction from message', async () => {
327
+ mockResponse(null, 204)
328
+
329
+ const client = new TeamsClient('test-token')
330
+ await client.removeReaction('111', 'ch1', 'msg1', 'like')
331
+
332
+ expect(fetchCalls[0].url).toBe(
333
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/messages/msg1/reactions/like'
334
+ )
335
+ expect(fetchCalls[0].options?.method).toBe('DELETE')
336
+ })
337
+ })
338
+
339
+ describe('listUsers', () => {
340
+ test('returns list of team members', async () => {
341
+ mockResponse([
342
+ { id: 'u1', displayName: 'User 1', email: 'user1@example.com' },
343
+ { id: 'u2', displayName: 'User 2', email: 'user2@example.com' },
344
+ ])
345
+
346
+ const client = new TeamsClient('test-token')
347
+ const users = await client.listUsers('111')
348
+
349
+ expect(users).toHaveLength(2)
350
+ expect(users[0].displayName).toBe('User 1')
351
+ expect(fetchCalls[0].url).toBe('https://teams.microsoft.com/api/csa/api/v1/teams/111/members')
352
+ })
353
+ })
354
+
355
+ describe('getUser', () => {
356
+ test('returns user info', async () => {
357
+ mockResponse({ id: 'u1', displayName: 'Test User', email: 'test@example.com' })
358
+
359
+ const client = new TeamsClient('test-token')
360
+ const user = await client.getUser('u1')
361
+
362
+ expect(user.id).toBe('u1')
363
+ expect(user.displayName).toBe('Test User')
364
+ expect(fetchCalls[0].url).toBe('https://teams.microsoft.com/api/csa/api/v1/users/u1')
365
+ })
366
+ })
367
+
368
+ describe('uploadFile', () => {
369
+ test('uploads file to channel', async () => {
370
+ const tempFile = '/tmp/test-teams-upload.txt'
371
+ await Bun.write(tempFile, 'test content')
372
+
373
+ mockResponse({
374
+ id: 'file1',
375
+ name: 'test-teams-upload.txt',
376
+ size: 12,
377
+ url: 'https://teams.microsoft.com/files/file1',
378
+ })
379
+
380
+ const client = new TeamsClient('test-token')
381
+ const file = await client.uploadFile('111', 'ch1', tempFile)
382
+
383
+ expect(file.name).toBe('test-teams-upload.txt')
384
+ expect(fetchCalls[0].url).toBe(
385
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/files'
386
+ )
387
+ expect(fetchCalls[0].options?.method).toBe('POST')
388
+ })
389
+ })
390
+
391
+ describe('listFiles', () => {
392
+ test('returns files from channel', async () => {
393
+ mockResponse([
394
+ { id: 'file1', name: 'doc.pdf', size: 1024, url: 'https://example.com/doc.pdf' },
395
+ { id: 'file2', name: 'image.png', size: 2048, url: 'https://example.com/image.png' },
396
+ ])
397
+
398
+ const client = new TeamsClient('test-token')
399
+ const files = await client.listFiles('111', 'ch1')
400
+
401
+ expect(files).toHaveLength(2)
402
+ expect(files[0].name).toBe('doc.pdf')
403
+ expect(fetchCalls[0].url).toBe(
404
+ 'https://teams.microsoft.com/api/csa/emea/api/v2/teams/111/channels/ch1/files'
405
+ )
406
+ })
407
+ })
408
+
409
+ describe('rate limiting', () => {
410
+ test('waits when bucket is exhausted before making request', async () => {
411
+ mockResponse({ userDetails: JSON.stringify({ name: 'User 1' }), locale: 'en-us' }, 200, {
412
+ 'X-RateLimit-Remaining': '0',
413
+ 'X-RateLimit-Reset': String(Date.now() / 1000 + 0.1),
414
+ })
415
+ mockResponse({ userDetails: JSON.stringify({ name: 'User 2' }), locale: 'en-us' }, 200, {
416
+ 'X-RateLimit-Remaining': '10',
417
+ 'X-RateLimit-Reset': String(Date.now() / 1000 + 60),
418
+ })
419
+
420
+ const client = new TeamsClient('test-token')
421
+ await client.testAuth()
422
+
423
+ const startTime = Date.now()
424
+ await client.testAuth()
425
+ const elapsed = Date.now() - startTime
426
+
427
+ expect(elapsed).toBeGreaterThanOrEqual(50)
428
+ expect(fetchCalls.length).toBe(2)
429
+ })
430
+
431
+ test('retries on 429 with Retry-After header', async () => {
432
+ mockResponse({ message: 'Rate limited' }, 429, { 'Retry-After': '0.1' })
433
+ mockResponse({ userDetails: JSON.stringify({ name: 'User' }), locale: 'en-us' })
434
+
435
+ const client = new TeamsClient('test-token')
436
+ const user = await client.testAuth()
437
+
438
+ expect(user.id).toBe('ME')
439
+ expect(fetchCalls.length).toBe(2)
440
+ })
441
+
442
+ test('throws after max retries exceeded', async () => {
443
+ for (let i = 0; i <= 3; i++) {
444
+ mockResponse({ message: 'Rate limited' }, 429, { 'Retry-After': '0.01' })
445
+ }
446
+
447
+ const client = new TeamsClient('test-token')
448
+ await expect(client.testAuth()).rejects.toThrow(TeamsError)
449
+ expect(fetchCalls.length).toBeLessThanOrEqual(4)
450
+ })
451
+ })
452
+
453
+ describe('retry logic', () => {
454
+ test('retries on 500 server error', async () => {
455
+ mockResponse({ message: 'Internal Server Error' }, 500)
456
+ mockResponse({ userDetails: JSON.stringify({ name: 'User' }), locale: 'en-us' })
457
+
458
+ const client = new TeamsClient('test-token')
459
+ const user = await client.testAuth()
460
+
461
+ expect(user.id).toBe('ME')
462
+ expect(fetchCalls.length).toBe(2)
463
+ })
464
+
465
+ test('does not retry on 4xx client errors (except 429)', async () => {
466
+ mockResponse({ message: 'Not Found' }, 404)
467
+
468
+ const client = new TeamsClient('test-token')
469
+ await expect(client.testAuth()).rejects.toThrow(TeamsError)
470
+ expect(fetchCalls.length).toBe(1)
471
+ })
472
+
473
+ test('exponential backoff increases delay', async () => {
474
+ mockResponse({ message: 'Error' }, 500)
475
+ mockResponse({ message: 'Error' }, 500)
476
+ mockResponse({ userDetails: JSON.stringify({ name: 'User' }), locale: 'en-us' })
477
+
478
+ const client = new TeamsClient('test-token')
479
+ const startTime = Date.now()
480
+ await client.testAuth()
481
+ const elapsed = Date.now() - startTime
482
+
483
+ expect(elapsed).toBeGreaterThanOrEqual(150)
484
+ expect(fetchCalls.length).toBe(3)
485
+ })
486
+ })
487
+
488
+ describe('bucket key normalization', () => {
489
+ test('normalizes team and channel IDs in routes', async () => {
490
+ mockResponse([])
491
+ mockResponse([])
492
+
493
+ const client = new TeamsClient('test-token')
494
+ await client.getMessages('team1', 'ch1')
495
+ await client.getMessages('team2', 'ch2')
496
+
497
+ expect(fetchCalls.length).toBe(2)
498
+ })
499
+ })
500
+ })