agent-messenger 1.0.0 → 1.1.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 (330) 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/README.md +16 -14
  6. package/biome.json +33 -1
  7. package/bun.lock +63 -0
  8. package/dist/package.json +8 -4
  9. package/dist/src/cli.d.ts.map +1 -1
  10. package/dist/src/cli.js +4 -1
  11. package/dist/src/cli.js.map +1 -1
  12. package/dist/src/platforms/discord/cli.js +1 -1
  13. package/dist/src/platforms/discord/client.d.ts.map +1 -1
  14. package/dist/src/platforms/discord/client.js +3 -3
  15. package/dist/src/platforms/discord/client.js.map +1 -1
  16. package/dist/src/platforms/discord/commands/user.d.ts.map +1 -1
  17. package/dist/src/platforms/discord/commands/user.js +10 -1
  18. package/dist/src/platforms/discord/commands/user.js.map +1 -1
  19. package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
  20. package/dist/src/platforms/discord/credential-manager.js +18 -12
  21. package/dist/src/platforms/discord/credential-manager.js.map +1 -1
  22. package/dist/src/platforms/slack/cli.js +1 -1
  23. package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
  24. package/dist/src/platforms/slack/credential-manager.js +20 -6
  25. package/dist/src/platforms/slack/credential-manager.js.map +1 -1
  26. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  27. package/dist/src/platforms/slack/token-extractor.js +34 -9
  28. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  29. package/dist/src/platforms/teams/cli.d.ts.map +1 -0
  30. package/dist/{cli.js → src/platforms/teams/cli.js} +11 -10
  31. package/dist/src/platforms/teams/cli.js.map +1 -0
  32. package/dist/src/platforms/teams/client.d.ts +32 -0
  33. package/dist/src/platforms/teams/client.d.ts.map +1 -0
  34. package/dist/src/platforms/teams/client.js +202 -0
  35. package/dist/src/platforms/teams/client.js.map +1 -0
  36. package/dist/src/platforms/teams/commands/auth.d.ts +14 -0
  37. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -0
  38. package/dist/src/platforms/teams/commands/auth.js +176 -0
  39. package/dist/src/platforms/teams/commands/auth.js.map +1 -0
  40. package/dist/src/platforms/teams/commands/channel.d.ts +13 -0
  41. package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -0
  42. package/dist/src/platforms/teams/commands/channel.js +97 -0
  43. package/dist/src/platforms/teams/commands/channel.js.map +1 -0
  44. package/dist/src/platforms/teams/commands/file.d.ts +12 -0
  45. package/dist/src/platforms/teams/commands/file.d.ts.map +1 -0
  46. package/dist/src/platforms/teams/commands/file.js +104 -0
  47. package/dist/src/platforms/teams/commands/file.js.map +1 -0
  48. package/dist/{commands → src/platforms/teams/commands}/index.d.ts +5 -2
  49. package/dist/src/platforms/teams/commands/index.d.ts.map +1 -0
  50. package/dist/{commands → src/platforms/teams/commands}/index.js +5 -2
  51. package/dist/src/platforms/teams/commands/index.js.map +1 -0
  52. package/dist/src/platforms/teams/commands/message.d.ts +17 -0
  53. package/dist/src/platforms/teams/commands/message.d.ts.map +1 -0
  54. package/dist/src/platforms/teams/commands/message.js +133 -0
  55. package/dist/src/platforms/teams/commands/message.js.map +1 -0
  56. package/dist/src/platforms/teams/commands/reaction.d.ts +9 -0
  57. package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -0
  58. package/dist/src/platforms/teams/commands/reaction.js +68 -0
  59. package/dist/src/platforms/teams/commands/reaction.js.map +1 -0
  60. package/dist/src/platforms/teams/commands/snapshot.d.ts +10 -0
  61. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -0
  62. package/dist/src/platforms/teams/commands/snapshot.js +85 -0
  63. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -0
  64. package/dist/src/platforms/teams/commands/team.d.ts +18 -0
  65. package/dist/src/platforms/teams/commands/team.d.ts.map +1 -0
  66. package/dist/src/platforms/teams/commands/team.js +130 -0
  67. package/dist/src/platforms/teams/commands/team.js.map +1 -0
  68. package/dist/src/platforms/teams/commands/user.d.ts.map +1 -0
  69. package/dist/src/platforms/teams/commands/user.js +88 -0
  70. package/dist/src/platforms/teams/commands/user.js.map +1 -0
  71. package/dist/src/platforms/teams/credential-manager.d.ts +18 -0
  72. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -0
  73. package/dist/src/platforms/teams/credential-manager.js +81 -0
  74. package/dist/src/platforms/teams/credential-manager.js.map +1 -0
  75. package/dist/src/platforms/teams/index.d.ts +4 -0
  76. package/dist/src/platforms/teams/index.d.ts.map +1 -0
  77. package/dist/src/platforms/teams/index.js +6 -0
  78. package/dist/src/platforms/teams/index.js.map +1 -0
  79. package/dist/src/platforms/teams/token-extractor.d.ts +36 -0
  80. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -0
  81. package/dist/src/platforms/teams/token-extractor.js +335 -0
  82. package/dist/src/platforms/teams/token-extractor.js.map +1 -0
  83. package/dist/src/platforms/teams/types.d.ts +209 -0
  84. package/dist/src/platforms/teams/types.d.ts.map +1 -0
  85. package/dist/src/platforms/teams/types.js +65 -0
  86. package/dist/src/platforms/teams/types.js.map +1 -0
  87. package/docs/teams.md +321 -0
  88. package/e2e/README.md +256 -0
  89. package/e2e/config.ts +45 -0
  90. package/e2e/discord.e2e.test.ts +252 -0
  91. package/e2e/helpers.ts +107 -0
  92. package/e2e/slack.e2e.test.ts +309 -0
  93. package/package.json +8 -4
  94. package/scripts/postbuild.ts +15 -0
  95. package/skills/agent-teams/SKILL.md +292 -0
  96. package/skills/agent-teams/references/authentication.md +375 -0
  97. package/skills/agent-teams/references/common-patterns.md +596 -0
  98. package/skills/agent-teams/templates/monitor-channel.sh +239 -0
  99. package/skills/agent-teams/templates/post-message.sh +224 -0
  100. package/skills/agent-teams/templates/team-summary.sh +210 -0
  101. package/src/cli.ts +4 -0
  102. package/src/platforms/discord/client.ts +3 -3
  103. package/src/platforms/discord/commands/auth.test.ts +48 -32
  104. package/src/platforms/discord/commands/channel.test.ts +54 -42
  105. package/src/platforms/discord/commands/file.test.ts +40 -53
  106. package/src/platforms/discord/commands/guild.test.ts +47 -27
  107. package/src/platforms/discord/commands/message.test.ts +54 -51
  108. package/src/platforms/discord/commands/reaction.test.ts +54 -42
  109. package/src/platforms/discord/commands/user.ts +12 -1
  110. package/src/platforms/discord/credential-manager.test.ts +137 -136
  111. package/src/platforms/discord/credential-manager.ts +20 -13
  112. package/src/platforms/discord/token-extractor.test.ts +133 -383
  113. package/{tests → src/platforms/slack}/cli.test.ts +3 -3
  114. package/{tests/slack-client.test.ts → src/platforms/slack/client.test.ts} +1 -1
  115. package/{tests → src/platforms/slack}/commands/auth.test.ts +25 -13
  116. package/{tests → src/platforms/slack}/commands/channel.test.ts +2 -2
  117. package/{tests → src/platforms/slack}/commands/file.test.ts +2 -2
  118. package/{tests → src/platforms/slack}/commands/message.test.ts +2 -2
  119. package/{tests → src/platforms/slack}/commands/reaction.test.ts +1 -1
  120. package/{tests → src/platforms/slack}/commands/snapshot.test.ts +117 -105
  121. package/{tests → src/platforms/slack}/commands/user.test.ts +3 -3
  122. package/{tests → src/platforms/slack}/commands/workspace.test.ts +44 -95
  123. package/{tests → src/platforms/slack}/credential-manager.test.ts +2 -2
  124. package/src/platforms/slack/credential-manager.ts +22 -7
  125. package/src/platforms/slack/token-extractor-node-test.ts +40 -0
  126. package/src/platforms/slack/token-extractor-node.test.ts +10 -0
  127. package/src/platforms/slack/token-extractor.ts +36 -10
  128. package/{tests → src/platforms/slack}/types.test.ts +1 -1
  129. package/src/platforms/teams/cli.ts +36 -0
  130. package/src/platforms/teams/client.test.ts +500 -0
  131. package/src/platforms/teams/client.ts +365 -0
  132. package/src/platforms/teams/commands/auth.test.ts +99 -0
  133. package/src/platforms/teams/commands/auth.ts +232 -0
  134. package/src/platforms/teams/commands/channel.test.ts +147 -0
  135. package/src/platforms/teams/commands/channel.ts +129 -0
  136. package/src/platforms/teams/commands/file.test.ts +88 -0
  137. package/src/platforms/teams/commands/file.ts +144 -0
  138. package/src/platforms/teams/commands/index.ts +12 -0
  139. package/src/platforms/teams/commands/message.test.ts +110 -0
  140. package/src/platforms/teams/commands/message.ts +188 -0
  141. package/src/platforms/teams/commands/reaction.test.ts +87 -0
  142. package/src/platforms/teams/commands/reaction.ts +104 -0
  143. package/src/platforms/teams/commands/snapshot.test.ts +35 -0
  144. package/src/platforms/teams/commands/snapshot.ts +115 -0
  145. package/src/platforms/teams/commands/team.test.ts +157 -0
  146. package/src/platforms/teams/commands/team.ts +164 -0
  147. package/src/platforms/teams/commands/user.test.ts +83 -0
  148. package/src/platforms/teams/commands/user.ts +112 -0
  149. package/src/platforms/teams/credential-manager.test.ts +178 -0
  150. package/src/platforms/teams/credential-manager.ts +92 -0
  151. package/src/platforms/teams/index.ts +5 -0
  152. package/src/platforms/teams/token-extractor.test.ts +429 -0
  153. package/src/platforms/teams/token-extractor.ts +462 -0
  154. package/src/platforms/teams/types.test.ts +226 -0
  155. package/src/platforms/teams/types.ts +140 -0
  156. package/tsconfig.json +1 -1
  157. package/dist/cli.d.ts.map +0 -1
  158. package/dist/cli.js.map +0 -1
  159. package/dist/commands/auth.d.ts +0 -3
  160. package/dist/commands/auth.d.ts.map +0 -1
  161. package/dist/commands/auth.js +0 -140
  162. package/dist/commands/auth.js.map +0 -1
  163. package/dist/commands/channel.d.ts +0 -3
  164. package/dist/commands/channel.d.ts.map +0 -1
  165. package/dist/commands/channel.js +0 -118
  166. package/dist/commands/channel.js.map +0 -1
  167. package/dist/commands/file.d.ts +0 -3
  168. package/dist/commands/file.d.ts.map +0 -1
  169. package/dist/commands/file.js +0 -113
  170. package/dist/commands/file.js.map +0 -1
  171. package/dist/commands/index.d.ts.map +0 -1
  172. package/dist/commands/index.js.map +0 -1
  173. package/dist/commands/message.d.ts +0 -3
  174. package/dist/commands/message.d.ts.map +0 -1
  175. package/dist/commands/message.js +0 -214
  176. package/dist/commands/message.js.map +0 -1
  177. package/dist/commands/reaction.d.ts +0 -3
  178. package/dist/commands/reaction.d.ts.map +0 -1
  179. package/dist/commands/reaction.js +0 -100
  180. package/dist/commands/reaction.js.map +0 -1
  181. package/dist/commands/snapshot.d.ts +0 -3
  182. package/dist/commands/snapshot.d.ts.map +0 -1
  183. package/dist/commands/snapshot.js +0 -88
  184. package/dist/commands/snapshot.js.map +0 -1
  185. package/dist/commands/user.d.ts.map +0 -1
  186. package/dist/commands/user.js +0 -96
  187. package/dist/commands/user.js.map +0 -1
  188. package/dist/commands/workspace.d.ts +0 -3
  189. package/dist/commands/workspace.d.ts.map +0 -1
  190. package/dist/commands/workspace.js +0 -89
  191. package/dist/commands/workspace.js.map +0 -1
  192. package/dist/lib/credential-manager.d.ts +0 -13
  193. package/dist/lib/credential-manager.d.ts.map +0 -1
  194. package/dist/lib/credential-manager.js +0 -58
  195. package/dist/lib/credential-manager.js.map +0 -1
  196. package/dist/lib/index.d.ts +0 -3
  197. package/dist/lib/index.d.ts.map +0 -1
  198. package/dist/lib/index.js +0 -3
  199. package/dist/lib/index.js.map +0 -1
  200. package/dist/lib/ref-manager.d.ts +0 -26
  201. package/dist/lib/ref-manager.d.ts.map +0 -1
  202. package/dist/lib/ref-manager.js +0 -92
  203. package/dist/lib/ref-manager.js.map +0 -1
  204. package/dist/lib/slack-client.d.ts +0 -37
  205. package/dist/lib/slack-client.d.ts.map +0 -1
  206. package/dist/lib/slack-client.js +0 -379
  207. package/dist/lib/slack-client.js.map +0 -1
  208. package/dist/lib/token-extractor.d.ts +0 -28
  209. package/dist/lib/token-extractor.d.ts.map +0 -1
  210. package/dist/lib/token-extractor.js +0 -401
  211. package/dist/lib/token-extractor.js.map +0 -1
  212. package/dist/src/platforms/discord/client.test.d.ts +0 -2
  213. package/dist/src/platforms/discord/client.test.d.ts.map +0 -1
  214. package/dist/src/platforms/discord/client.test.js +0 -367
  215. package/dist/src/platforms/discord/client.test.js.map +0 -1
  216. package/dist/src/platforms/discord/commands/auth.test.d.ts +0 -2
  217. package/dist/src/platforms/discord/commands/auth.test.d.ts.map +0 -1
  218. package/dist/src/platforms/discord/commands/auth.test.js +0 -65
  219. package/dist/src/platforms/discord/commands/auth.test.js.map +0 -1
  220. package/dist/src/platforms/discord/commands/channel.test.d.ts +0 -2
  221. package/dist/src/platforms/discord/commands/channel.test.d.ts.map +0 -1
  222. package/dist/src/platforms/discord/commands/channel.test.js +0 -136
  223. package/dist/src/platforms/discord/commands/channel.test.js.map +0 -1
  224. package/dist/src/platforms/discord/commands/file.test.d.ts +0 -2
  225. package/dist/src/platforms/discord/commands/file.test.d.ts.map +0 -1
  226. package/dist/src/platforms/discord/commands/file.test.js +0 -83
  227. package/dist/src/platforms/discord/commands/file.test.js.map +0 -1
  228. package/dist/src/platforms/discord/commands/guild.test.d.ts +0 -2
  229. package/dist/src/platforms/discord/commands/guild.test.d.ts.map +0 -1
  230. package/dist/src/platforms/discord/commands/guild.test.js +0 -100
  231. package/dist/src/platforms/discord/commands/guild.test.js.map +0 -1
  232. package/dist/src/platforms/discord/commands/message.test.d.ts +0 -2
  233. package/dist/src/platforms/discord/commands/message.test.d.ts.map +0 -1
  234. package/dist/src/platforms/discord/commands/message.test.js +0 -91
  235. package/dist/src/platforms/discord/commands/message.test.js.map +0 -1
  236. package/dist/src/platforms/discord/commands/reaction.test.d.ts +0 -2
  237. package/dist/src/platforms/discord/commands/reaction.test.d.ts.map +0 -1
  238. package/dist/src/platforms/discord/commands/reaction.test.js +0 -115
  239. package/dist/src/platforms/discord/commands/reaction.test.js.map +0 -1
  240. package/dist/src/platforms/discord/commands/snapshot.test.d.ts +0 -2
  241. package/dist/src/platforms/discord/commands/snapshot.test.d.ts.map +0 -1
  242. package/dist/src/platforms/discord/commands/snapshot.test.js +0 -25
  243. package/dist/src/platforms/discord/commands/snapshot.test.js.map +0 -1
  244. package/dist/src/platforms/discord/commands/user.test.d.ts +0 -2
  245. package/dist/src/platforms/discord/commands/user.test.d.ts.map +0 -1
  246. package/dist/src/platforms/discord/commands/user.test.js +0 -103
  247. package/dist/src/platforms/discord/commands/user.test.js.map +0 -1
  248. package/dist/src/platforms/discord/credential-manager.test.d.ts +0 -2
  249. package/dist/src/platforms/discord/credential-manager.test.d.ts.map +0 -1
  250. package/dist/src/platforms/discord/credential-manager.test.js +0 -136
  251. package/dist/src/platforms/discord/credential-manager.test.js.map +0 -1
  252. package/dist/src/platforms/discord/token-extractor.test.d.ts +0 -2
  253. package/dist/src/platforms/discord/token-extractor.test.d.ts.map +0 -1
  254. package/dist/src/platforms/discord/token-extractor.test.js +0 -789
  255. package/dist/src/platforms/discord/token-extractor.test.js.map +0 -1
  256. package/dist/src/platforms/discord/types.test.d.ts +0 -2
  257. package/dist/src/platforms/discord/types.test.d.ts.map +0 -1
  258. package/dist/src/platforms/discord/types.test.js +0 -211
  259. package/dist/src/platforms/discord/types.test.js.map +0 -1
  260. package/dist/src/shared/utils/concurrency.test.d.ts +0 -2
  261. package/dist/src/shared/utils/concurrency.test.d.ts.map +0 -1
  262. package/dist/src/shared/utils/concurrency.test.js +0 -39
  263. package/dist/src/shared/utils/concurrency.test.js.map +0 -1
  264. package/dist/tests/cli.test.d.ts +0 -2
  265. package/dist/tests/cli.test.d.ts.map +0 -1
  266. package/dist/tests/cli.test.js +0 -83
  267. package/dist/tests/cli.test.js.map +0 -1
  268. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/CURRENT +0 -1
  269. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOCK +0 -0
  270. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG +0 -3
  271. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG.old +0 -1
  272. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/MANIFEST-000004 +0 -0
  273. package/dist/tests/commands/auth.test.d.ts +0 -2
  274. package/dist/tests/commands/auth.test.d.ts.map +0 -1
  275. package/dist/tests/commands/auth.test.js +0 -304
  276. package/dist/tests/commands/auth.test.js.map +0 -1
  277. package/dist/tests/commands/channel.test.d.ts +0 -2
  278. package/dist/tests/commands/channel.test.d.ts.map +0 -1
  279. package/dist/tests/commands/channel.test.js +0 -166
  280. package/dist/tests/commands/channel.test.js.map +0 -1
  281. package/dist/tests/commands/file.test.d.ts +0 -2
  282. package/dist/tests/commands/file.test.d.ts.map +0 -1
  283. package/dist/tests/commands/file.test.js +0 -175
  284. package/dist/tests/commands/file.test.js.map +0 -1
  285. package/dist/tests/commands/message.test.d.ts +0 -2
  286. package/dist/tests/commands/message.test.d.ts.map +0 -1
  287. package/dist/tests/commands/message.test.js +0 -293
  288. package/dist/tests/commands/message.test.js.map +0 -1
  289. package/dist/tests/commands/reaction.test.d.ts +0 -2
  290. package/dist/tests/commands/reaction.test.d.ts.map +0 -1
  291. package/dist/tests/commands/reaction.test.js +0 -84
  292. package/dist/tests/commands/reaction.test.js.map +0 -1
  293. package/dist/tests/commands/snapshot.test.d.ts +0 -2
  294. package/dist/tests/commands/snapshot.test.d.ts.map +0 -1
  295. package/dist/tests/commands/snapshot.test.js +0 -280
  296. package/dist/tests/commands/snapshot.test.js.map +0 -1
  297. package/dist/tests/commands/user.test.d.ts +0 -2
  298. package/dist/tests/commands/user.test.d.ts.map +0 -1
  299. package/dist/tests/commands/user.test.js +0 -117
  300. package/dist/tests/commands/user.test.js.map +0 -1
  301. package/dist/tests/commands/workspace.test.d.ts +0 -2
  302. package/dist/tests/commands/workspace.test.d.ts.map +0 -1
  303. package/dist/tests/commands/workspace.test.js +0 -453
  304. package/dist/tests/commands/workspace.test.js.map +0 -1
  305. package/dist/tests/credential-manager.test.d.ts +0 -2
  306. package/dist/tests/credential-manager.test.d.ts.map +0 -1
  307. package/dist/tests/credential-manager.test.js +0 -199
  308. package/dist/tests/credential-manager.test.js.map +0 -1
  309. package/dist/tests/slack-client.test.d.ts +0 -2
  310. package/dist/tests/slack-client.test.d.ts.map +0 -1
  311. package/dist/tests/slack-client.test.js +0 -741
  312. package/dist/tests/slack-client.test.js.map +0 -1
  313. package/dist/tests/types.test.d.ts +0 -2
  314. package/dist/tests/types.test.d.ts.map +0 -1
  315. package/dist/tests/types.test.js +0 -215
  316. package/dist/tests/types.test.js.map +0 -1
  317. package/dist/types/index.d.ts +0 -369
  318. package/dist/types/index.d.ts.map +0 -1
  319. package/dist/types/index.js +0 -92
  320. package/dist/types/index.js.map +0 -1
  321. package/dist/utils/error-handler.d.ts +0 -2
  322. package/dist/utils/error-handler.d.ts.map +0 -1
  323. package/dist/utils/error-handler.js +0 -5
  324. package/dist/utils/error-handler.js.map +0 -1
  325. package/dist/utils/output.d.ts +0 -2
  326. package/dist/utils/output.d.ts.map +0 -1
  327. package/dist/utils/output.js +0 -4
  328. package/dist/utils/output.js.map +0 -1
  329. /package/dist/{cli.d.ts → src/platforms/teams/cli.d.ts} +0 -0
  330. /package/dist/{commands → src/platforms/teams/commands}/user.d.ts +0 -0
@@ -0,0 +1,147 @@
1
+ import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
+ import { TeamsClient } from '../client'
3
+ import { TeamsCredentialManager } from '../credential-manager'
4
+
5
+ let clientListChannelsSpy: ReturnType<typeof spyOn>
6
+ let clientGetChannelSpy: ReturnType<typeof spyOn>
7
+ let clientGetMessagesSpy: ReturnType<typeof spyOn>
8
+ let credManagerLoadConfigSpy: ReturnType<typeof spyOn>
9
+
10
+ beforeEach(() => {
11
+ clientListChannelsSpy = spyOn(TeamsClient.prototype, 'listChannels').mockResolvedValue([
12
+ { id: 'ch-1', team_id: 'team-1', name: 'General', type: 'standard' },
13
+ { id: 'ch-2', team_id: 'team-1', name: 'Announcements', type: 'standard' },
14
+ ])
15
+
16
+ clientGetChannelSpy = spyOn(TeamsClient.prototype, 'getChannel').mockImplementation(
17
+ async (teamId: string, channelId: string) => {
18
+ if (channelId === 'ch-1') {
19
+ return { id: 'ch-1', team_id: teamId, name: 'General', type: 'standard' }
20
+ }
21
+ if (channelId === 'ch-2') {
22
+ return { id: 'ch-2', team_id: teamId, name: 'Announcements', type: 'standard' }
23
+ }
24
+ throw new Error('Channel not found')
25
+ }
26
+ )
27
+
28
+ clientGetMessagesSpy = spyOn(TeamsClient.prototype, 'getMessages').mockResolvedValue([
29
+ {
30
+ id: 'msg-1',
31
+ channel_id: 'ch-1',
32
+ author: { id: 'user-1', displayName: 'Alice' },
33
+ content: 'Hello world',
34
+ timestamp: '2024-01-29T10:00:00Z',
35
+ },
36
+ {
37
+ id: 'msg-2',
38
+ channel_id: 'ch-1',
39
+ author: { id: 'user-2', displayName: 'Bob' },
40
+ content: 'Hi there',
41
+ timestamp: '2024-01-29T09:00:00Z',
42
+ },
43
+ ])
44
+
45
+ credManagerLoadConfigSpy = spyOn(
46
+ TeamsCredentialManager.prototype,
47
+ 'loadConfig'
48
+ ).mockResolvedValue({
49
+ token: 'test-token',
50
+ current_team: 'team-1',
51
+ teams: {
52
+ 'team-1': { team_id: 'team-1', team_name: 'Team One' },
53
+ },
54
+ })
55
+ })
56
+
57
+ afterEach(() => {
58
+ clientListChannelsSpy?.mockRestore()
59
+ clientGetChannelSpy?.mockRestore()
60
+ clientGetMessagesSpy?.mockRestore()
61
+ credManagerLoadConfigSpy?.mockRestore()
62
+ })
63
+
64
+ test('list: returns channels from team', async () => {
65
+ // given
66
+ const client = new TeamsClient('test-token')
67
+
68
+ // when
69
+ const channels = await client.listChannels('team-1')
70
+
71
+ // then
72
+ expect(channels).toHaveLength(2)
73
+ expect(channels[0].name).toBe('General')
74
+ expect(channels[1].name).toBe('Announcements')
75
+ })
76
+
77
+ test('list: includes channel metadata', async () => {
78
+ // given
79
+ const client = new TeamsClient('test-token')
80
+ const channels = await client.listChannels('team-1')
81
+
82
+ // when
83
+ const channel = channels[0]
84
+
85
+ // then
86
+ expect(channel.id).toBeDefined()
87
+ expect(channel.name).toBeDefined()
88
+ expect(channel.type).toBeDefined()
89
+ expect(channel.team_id).toBe('team-1')
90
+ })
91
+
92
+ test('info: returns channel details', async () => {
93
+ // given
94
+ const client = new TeamsClient('test-token')
95
+
96
+ // when
97
+ const channel = await client.getChannel('team-1', 'ch-1')
98
+
99
+ // then
100
+ expect(channel.id).toBe('ch-1')
101
+ expect(channel.name).toBe('General')
102
+ expect(channel.type).toBe('standard')
103
+ })
104
+
105
+ test('info: throws error for non-existent channel', async () => {
106
+ // given
107
+ const client = new TeamsClient('test-token')
108
+
109
+ // when/then
110
+ try {
111
+ await client.getChannel('team-1', 'non-existent')
112
+ expect(true).toBe(false)
113
+ } catch (error) {
114
+ expect((error as Error).message).toContain('Channel not found')
115
+ }
116
+ })
117
+
118
+ test('history: returns messages', async () => {
119
+ // given
120
+ const client = new TeamsClient('test-token')
121
+
122
+ // when
123
+ const messages = await client.getMessages('team-1', 'ch-1', 50)
124
+
125
+ // then
126
+ expect(messages).toHaveLength(2)
127
+ expect(messages[0].id).toBe('msg-1')
128
+ expect(messages[0].author.displayName).toBe('Alice')
129
+ expect(messages[1].id).toBe('msg-2')
130
+ expect(messages[1].author.displayName).toBe('Bob')
131
+ })
132
+
133
+ test('history: includes message metadata', async () => {
134
+ // given
135
+ const client = new TeamsClient('test-token')
136
+ const messages = await client.getMessages('team-1', 'ch-1', 50)
137
+
138
+ // when
139
+ const message = messages[0]
140
+
141
+ // then
142
+ expect(message.id).toBeDefined()
143
+ expect(message.content).toBeDefined()
144
+ expect(message.author).toBeDefined()
145
+ expect(message.author.displayName).toBeDefined()
146
+ expect(message.timestamp).toBeDefined()
147
+ })
@@ -0,0 +1,129 @@
1
+ import { Command } from 'commander'
2
+ import { handleError } from '../../../shared/utils/error-handler'
3
+ import { formatOutput } from '../../../shared/utils/output'
4
+ import { TeamsClient } from '../client'
5
+ import { TeamsCredentialManager } from '../credential-manager'
6
+
7
+ export async function listAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
8
+ try {
9
+ const credManager = new TeamsCredentialManager()
10
+ const config = await credManager.loadConfig()
11
+
12
+ if (!config?.token) {
13
+ console.log(
14
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
15
+ )
16
+ process.exit(1)
17
+ }
18
+
19
+ const client = new TeamsClient(config.token, config.token_expires_at)
20
+ const channels = await client.listChannels(teamId)
21
+
22
+ const output = channels.map((ch) => ({
23
+ id: ch.id,
24
+ name: ch.name,
25
+ type: ch.type,
26
+ team_id: ch.team_id,
27
+ }))
28
+
29
+ console.log(formatOutput(output, options.pretty))
30
+ } catch (error) {
31
+ handleError(error as Error)
32
+ }
33
+ }
34
+
35
+ export async function infoAction(
36
+ teamId: string,
37
+ channelId: string,
38
+ options: { pretty?: boolean }
39
+ ): Promise<void> {
40
+ try {
41
+ const credManager = new TeamsCredentialManager()
42
+ const config = await credManager.loadConfig()
43
+
44
+ if (!config?.token) {
45
+ console.log(
46
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
47
+ )
48
+ process.exit(1)
49
+ }
50
+
51
+ const client = new TeamsClient(config.token, config.token_expires_at)
52
+ const channel = await client.getChannel(teamId, channelId)
53
+
54
+ const output = {
55
+ id: channel.id,
56
+ name: channel.name,
57
+ type: channel.type,
58
+ team_id: channel.team_id,
59
+ }
60
+
61
+ console.log(formatOutput(output, options.pretty))
62
+ } catch (error) {
63
+ handleError(error as Error)
64
+ }
65
+ }
66
+
67
+ export async function historyAction(
68
+ teamId: string,
69
+ channelId: string,
70
+ options: { limit?: number; pretty?: boolean }
71
+ ): Promise<void> {
72
+ try {
73
+ const credManager = new TeamsCredentialManager()
74
+ const config = await credManager.loadConfig()
75
+
76
+ if (!config?.token) {
77
+ console.log(
78
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
79
+ )
80
+ process.exit(1)
81
+ }
82
+
83
+ const client = new TeamsClient(config.token, config.token_expires_at)
84
+ const messages = await client.getMessages(teamId, channelId, options.limit || 50)
85
+
86
+ const output = messages.map((msg) => ({
87
+ id: msg.id,
88
+ author: msg.author.displayName,
89
+ content: msg.content,
90
+ timestamp: msg.timestamp,
91
+ }))
92
+
93
+ console.log(formatOutput(output, options.pretty))
94
+ } catch (error) {
95
+ handleError(error as Error)
96
+ }
97
+ }
98
+
99
+ export const channelCommand = new Command('channel')
100
+ .description('Channel commands')
101
+ .addCommand(
102
+ new Command('list')
103
+ .description('List channels in a team')
104
+ .argument('<team-id>', 'Team ID')
105
+ .option('--pretty', 'Pretty print JSON output')
106
+ .action(listAction)
107
+ )
108
+ .addCommand(
109
+ new Command('info')
110
+ .description('Get channel info')
111
+ .argument('<team-id>', 'Team ID')
112
+ .argument('<channel-id>', 'Channel ID')
113
+ .option('--pretty', 'Pretty print JSON output')
114
+ .action(infoAction)
115
+ )
116
+ .addCommand(
117
+ new Command('history')
118
+ .description('Get channel message history')
119
+ .argument('<team-id>', 'Team ID')
120
+ .argument('<channel-id>', 'Channel ID')
121
+ .option('--limit <n>', 'Number of messages to fetch', '50')
122
+ .option('--pretty', 'Pretty print JSON output')
123
+ .action((teamId, channelId, options) => {
124
+ return historyAction(teamId, channelId, {
125
+ limit: parseInt(options.limit, 10),
126
+ pretty: options.pretty,
127
+ })
128
+ })
129
+ )
@@ -0,0 +1,88 @@
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { TeamsClient } from '../client'
3
+ import { TeamsCredentialManager } from '../credential-manager'
4
+ import { infoAction, listAction, uploadAction } from './file'
5
+
6
+ let clientUploadFileSpy: ReturnType<typeof spyOn>
7
+ let clientListFilesSpy: ReturnType<typeof spyOn>
8
+ let credManagerLoadConfigSpy: ReturnType<typeof spyOn>
9
+ const originalConsoleLog = console.log
10
+
11
+ beforeEach(() => {
12
+ clientUploadFileSpy = spyOn(TeamsClient.prototype, 'uploadFile').mockResolvedValue({
13
+ id: 'file_123',
14
+ name: 'test.pdf',
15
+ size: 1024,
16
+ url: 'https://teams.microsoft.com/files/123/test.pdf',
17
+ contentType: 'application/pdf',
18
+ })
19
+
20
+ clientListFilesSpy = spyOn(TeamsClient.prototype, 'listFiles').mockResolvedValue([
21
+ {
22
+ id: 'file_123',
23
+ name: 'test.pdf',
24
+ size: 1024,
25
+ url: 'https://teams.microsoft.com/files/123/test.pdf',
26
+ contentType: 'application/pdf',
27
+ },
28
+ {
29
+ id: 'file_124',
30
+ name: 'image.png',
31
+ size: 2048,
32
+ url: 'https://teams.microsoft.com/files/124/image.png',
33
+ contentType: 'image/png',
34
+ },
35
+ ])
36
+
37
+ credManagerLoadConfigSpy = spyOn(
38
+ TeamsCredentialManager.prototype,
39
+ 'loadConfig'
40
+ ).mockResolvedValue({
41
+ token: 'test_token',
42
+ current_team: 'team_123',
43
+ teams: { team_123: { team_id: 'team_123', team_name: 'Test Team' } },
44
+ })
45
+ })
46
+
47
+ afterEach(() => {
48
+ clientUploadFileSpy?.mockRestore()
49
+ clientListFilesSpy?.mockRestore()
50
+ credManagerLoadConfigSpy?.mockRestore()
51
+ console.log = originalConsoleLog
52
+ })
53
+
54
+ test('upload: sends multipart request and returns file info', async () => {
55
+ const consoleSpy = mock((_msg: string) => {})
56
+ console.log = consoleSpy
57
+
58
+ await uploadAction('team_123', 'ch_456', '/path/to/test.pdf', { pretty: false })
59
+
60
+ expect(consoleSpy).toHaveBeenCalled()
61
+ const output = consoleSpy.mock.calls[0][0]
62
+ expect(output).toContain('file_123')
63
+ expect(output).toContain('test.pdf')
64
+ })
65
+
66
+ test('list: returns files in channel', async () => {
67
+ const consoleSpy = mock((_msg: string) => {})
68
+ console.log = consoleSpy
69
+
70
+ await listAction('team_123', 'ch_456', { pretty: false })
71
+
72
+ expect(consoleSpy).toHaveBeenCalled()
73
+ const output = consoleSpy.mock.calls[0][0]
74
+ expect(output).toContain('file_123')
75
+ expect(output).toContain('file_124')
76
+ })
77
+
78
+ test('info: returns single file details', async () => {
79
+ const consoleSpy = mock((_msg: string) => {})
80
+ console.log = consoleSpy
81
+
82
+ await infoAction('team_123', 'ch_456', 'file_123', { pretty: false })
83
+
84
+ expect(consoleSpy).toHaveBeenCalled()
85
+ const output = consoleSpy.mock.calls[0][0]
86
+ expect(output).toContain('file_123')
87
+ expect(output).toContain('test.pdf')
88
+ })
@@ -0,0 +1,144 @@
1
+ import { resolve } from 'node:path'
2
+ import { Command } from 'commander'
3
+ import { handleError } from '../../../shared/utils/error-handler'
4
+ import { formatOutput } from '../../../shared/utils/output'
5
+ import { TeamsClient } from '../client'
6
+ import { TeamsCredentialManager } from '../credential-manager'
7
+ import type { TeamsFile } from '../types'
8
+
9
+ export async function uploadAction(
10
+ teamId: string,
11
+ channelId: string,
12
+ path: string,
13
+ options: { pretty?: boolean }
14
+ ): Promise<void> {
15
+ try {
16
+ const credManager = new TeamsCredentialManager()
17
+ const config = await credManager.loadConfig()
18
+
19
+ if (!config?.token) {
20
+ console.log(
21
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
22
+ )
23
+ process.exit(1)
24
+ }
25
+
26
+ const client = new TeamsClient(config.token, config.token_expires_at)
27
+ const filePath = resolve(path)
28
+ const file = await client.uploadFile(teamId, channelId, filePath)
29
+
30
+ const output = {
31
+ id: file.id,
32
+ name: file.name,
33
+ size: file.size,
34
+ url: file.url,
35
+ content_type: file.contentType || null,
36
+ }
37
+
38
+ console.log(formatOutput(output, options.pretty))
39
+ } catch (error) {
40
+ handleError(error as Error)
41
+ }
42
+ }
43
+
44
+ export async function listAction(
45
+ teamId: string,
46
+ channelId: string,
47
+ options: { pretty?: boolean }
48
+ ): Promise<void> {
49
+ try {
50
+ const credManager = new TeamsCredentialManager()
51
+ const config = await credManager.loadConfig()
52
+
53
+ if (!config?.token) {
54
+ console.log(
55
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
56
+ )
57
+ process.exit(1)
58
+ }
59
+
60
+ const client = new TeamsClient(config.token, config.token_expires_at)
61
+ const files = await client.listFiles(teamId, channelId)
62
+
63
+ const output = files.map((file: TeamsFile) => ({
64
+ id: file.id,
65
+ name: file.name,
66
+ size: file.size,
67
+ url: file.url,
68
+ content_type: file.contentType || null,
69
+ }))
70
+
71
+ console.log(formatOutput(output, options.pretty))
72
+ } catch (error) {
73
+ handleError(error as Error)
74
+ }
75
+ }
76
+
77
+ export async function infoAction(
78
+ teamId: string,
79
+ channelId: string,
80
+ fileId: string,
81
+ options: { pretty?: boolean }
82
+ ): Promise<void> {
83
+ try {
84
+ const credManager = new TeamsCredentialManager()
85
+ const config = await credManager.loadConfig()
86
+
87
+ if (!config?.token) {
88
+ console.log(
89
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
90
+ )
91
+ process.exit(1)
92
+ }
93
+
94
+ const client = new TeamsClient(config.token, config.token_expires_at)
95
+ const files = await client.listFiles(teamId, channelId)
96
+ const fileData = files.find((f) => f.id === fileId)
97
+
98
+ if (!fileData) {
99
+ console.log(formatOutput({ error: `File not found: ${fileId}` }, options.pretty))
100
+ process.exit(1)
101
+ }
102
+
103
+ const output = {
104
+ id: fileData.id,
105
+ name: fileData.name,
106
+ size: fileData.size,
107
+ url: fileData.url,
108
+ content_type: fileData.contentType || null,
109
+ }
110
+
111
+ console.log(formatOutput(output, options.pretty))
112
+ } catch (error) {
113
+ handleError(error as Error)
114
+ }
115
+ }
116
+
117
+ export const fileCommand = new Command('file')
118
+ .description('file commands')
119
+ .addCommand(
120
+ new Command('upload')
121
+ .description('upload file to channel')
122
+ .argument('<team>', 'team ID')
123
+ .argument('<channel>', 'channel ID')
124
+ .argument('<path>', 'file path')
125
+ .option('--pretty', 'Pretty print JSON output')
126
+ .action(uploadAction)
127
+ )
128
+ .addCommand(
129
+ new Command('list')
130
+ .description('list files in channel')
131
+ .argument('<team>', 'team ID')
132
+ .argument('<channel>', 'channel ID')
133
+ .option('--pretty', 'Pretty print JSON output')
134
+ .action(listAction)
135
+ )
136
+ .addCommand(
137
+ new Command('info')
138
+ .description('show file details')
139
+ .argument('<team>', 'team ID')
140
+ .argument('<channel>', 'channel ID')
141
+ .argument('<file>', 'file ID')
142
+ .option('--pretty', 'Pretty print JSON output')
143
+ .action(infoAction)
144
+ )
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Command registry for Teams CLI
3
+ */
4
+
5
+ export { authCommand } from './auth'
6
+ export { channelCommand } from './channel'
7
+ export { fileCommand } from './file'
8
+ export { messageCommand } from './message'
9
+ export { reactionCommand } from './reaction'
10
+ export { snapshotAction, snapshotCommand } from './snapshot'
11
+ export { teamCommand } from './team'
12
+ export { userCommand } from './user'
@@ -0,0 +1,110 @@
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { TeamsClient } from '../client'
3
+ import { TeamsCredentialManager } from '../credential-manager'
4
+ import { deleteAction, getAction, listAction, sendAction } from './message'
5
+
6
+ let clientSendMessageSpy: ReturnType<typeof spyOn>
7
+ let clientGetMessagesSpy: ReturnType<typeof spyOn>
8
+ let clientGetMessageSpy: ReturnType<typeof spyOn>
9
+ let clientDeleteMessageSpy: ReturnType<typeof spyOn>
10
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
11
+ const originalConsoleLog = console.log
12
+
13
+ beforeEach(() => {
14
+ clientSendMessageSpy = spyOn(TeamsClient.prototype, 'sendMessage').mockResolvedValue({
15
+ id: 'msg_123',
16
+ channel_id: 'ch_456',
17
+ author: { id: 'user_789', displayName: 'Test User' },
18
+ content: 'Hello world',
19
+ timestamp: '2025-01-29T10:00:00Z',
20
+ })
21
+
22
+ clientGetMessagesSpy = spyOn(TeamsClient.prototype, 'getMessages').mockResolvedValue([
23
+ {
24
+ id: 'msg_123',
25
+ channel_id: 'ch_456',
26
+ author: { id: 'user_789', displayName: 'Test User' },
27
+ content: 'Hello world',
28
+ timestamp: '2025-01-29T10:00:00Z',
29
+ },
30
+ {
31
+ id: 'msg_124',
32
+ channel_id: 'ch_456',
33
+ author: { id: 'user_789', displayName: 'Test User' },
34
+ content: 'Second message',
35
+ timestamp: '2025-01-29T10:01:00Z',
36
+ },
37
+ ])
38
+
39
+ clientGetMessageSpy = spyOn(TeamsClient.prototype, 'getMessage').mockResolvedValue({
40
+ id: 'msg_123',
41
+ channel_id: 'ch_456',
42
+ author: { id: 'user_789', displayName: 'Test User' },
43
+ content: 'Hello world',
44
+ timestamp: '2025-01-29T10:00:00Z',
45
+ })
46
+
47
+ clientDeleteMessageSpy = spyOn(TeamsClient.prototype, 'deleteMessage').mockResolvedValue(
48
+ undefined
49
+ )
50
+
51
+ credManagerLoadSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue({
52
+ token: 'test_token',
53
+ current_team: 'team_123',
54
+ teams: { team_123: { team_id: 'team_123', team_name: 'Test Team' } },
55
+ })
56
+ })
57
+
58
+ afterEach(() => {
59
+ clientSendMessageSpy?.mockRestore()
60
+ clientGetMessagesSpy?.mockRestore()
61
+ clientGetMessageSpy?.mockRestore()
62
+ clientDeleteMessageSpy?.mockRestore()
63
+ credManagerLoadSpy?.mockRestore()
64
+ console.log = originalConsoleLog
65
+ })
66
+
67
+ test('send: returns message with id', async () => {
68
+ const consoleSpy = mock((_msg: string) => {})
69
+ console.log = consoleSpy
70
+
71
+ await sendAction('team_123', 'ch_456', 'Hello world', { pretty: false })
72
+
73
+ expect(consoleSpy).toHaveBeenCalled()
74
+ const output = consoleSpy.mock.calls[0][0]
75
+ expect(output).toContain('msg_123')
76
+ })
77
+
78
+ test('list: returns array of messages', async () => {
79
+ const consoleSpy = mock((_msg: string) => {})
80
+ console.log = consoleSpy
81
+
82
+ await listAction('team_123', 'ch_456', { limit: 50, pretty: false })
83
+
84
+ expect(consoleSpy).toHaveBeenCalled()
85
+ const output = consoleSpy.mock.calls[0][0]
86
+ expect(output).toContain('msg_123')
87
+ expect(output).toContain('msg_124')
88
+ })
89
+
90
+ test('get: returns single message', async () => {
91
+ const consoleSpy = mock((_msg: string) => {})
92
+ console.log = consoleSpy
93
+
94
+ await getAction('team_123', 'ch_456', 'msg_123', { pretty: false })
95
+
96
+ expect(consoleSpy).toHaveBeenCalled()
97
+ const output = consoleSpy.mock.calls[0][0]
98
+ expect(output).toContain('msg_123')
99
+ })
100
+
101
+ test('delete: returns success', async () => {
102
+ const consoleSpy = mock((_msg: string) => {})
103
+ console.log = consoleSpy
104
+
105
+ await deleteAction('team_123', 'ch_456', 'msg_123', { force: true, pretty: false })
106
+
107
+ expect(consoleSpy).toHaveBeenCalled()
108
+ const output = consoleSpy.mock.calls[0][0]
109
+ expect(output).toContain('deleted')
110
+ })