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,164 @@
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(options: { pretty?: boolean }): Promise<void> {
8
+ try {
9
+ const credManager = new TeamsCredentialManager()
10
+ const config = await credManager.loadConfig()
11
+ const teams = config?.teams ? Object.values(config.teams) : []
12
+
13
+ const output = teams.map((team) => ({
14
+ id: team.team_id,
15
+ name: team.team_name,
16
+ current: team.team_id === config?.current_team,
17
+ }))
18
+
19
+ console.log(formatOutput(output, options.pretty))
20
+ } catch (error) {
21
+ handleError(error as Error)
22
+ }
23
+ }
24
+
25
+ export async function infoAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
26
+ try {
27
+ const credManager = new TeamsCredentialManager()
28
+ const config = await credManager.loadConfig()
29
+
30
+ if (!config?.token) {
31
+ console.log(
32
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
33
+ )
34
+ process.exit(1)
35
+ }
36
+
37
+ const client = new TeamsClient(config.token, config.token_expires_at)
38
+ const team = await client.getTeam(teamId)
39
+
40
+ const output = {
41
+ id: team.id,
42
+ name: team.name,
43
+ description: team.description,
44
+ }
45
+
46
+ console.log(formatOutput(output, options.pretty))
47
+ } catch (error) {
48
+ handleError(error as Error)
49
+ }
50
+ }
51
+
52
+ export async function switchAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
53
+ try {
54
+ const credManager = new TeamsCredentialManager()
55
+ const config = await credManager.loadConfig()
56
+
57
+ if (!config?.teams?.[teamId]) {
58
+ console.log(formatOutput({ error: `Team not found: ${teamId}` }, options.pretty))
59
+ process.exit(1)
60
+ }
61
+
62
+ const team = config.teams[teamId]
63
+ await credManager.setCurrentTeam(teamId, team.team_name)
64
+ console.log(formatOutput({ current: teamId }, options.pretty))
65
+ } catch (error) {
66
+ handleError(error as Error)
67
+ }
68
+ }
69
+
70
+ export async function currentAction(options: { pretty?: boolean }): Promise<void> {
71
+ try {
72
+ const credManager = new TeamsCredentialManager()
73
+ const config = await credManager.loadConfig()
74
+
75
+ if (!config?.current_team) {
76
+ console.log(
77
+ formatOutput({ error: 'No current team set. Run "auth extract" first.' }, options.pretty)
78
+ )
79
+ process.exit(1)
80
+ }
81
+
82
+ const team = config.teams[config.current_team]
83
+
84
+ if (!team) {
85
+ console.log(
86
+ formatOutput({ error: 'Current team not found in configuration.' }, options.pretty)
87
+ )
88
+ process.exit(1)
89
+ }
90
+
91
+ const output = {
92
+ team_id: team.team_id,
93
+ team_name: team.team_name,
94
+ }
95
+
96
+ console.log(formatOutput(output, options.pretty))
97
+ } catch (error) {
98
+ handleError(error as Error)
99
+ }
100
+ }
101
+
102
+ export async function removeAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
103
+ try {
104
+ const credManager = new TeamsCredentialManager()
105
+ const config = await credManager.loadConfig()
106
+
107
+ if (!config) {
108
+ console.log(formatOutput({ error: 'No configuration found.' }, options.pretty))
109
+ process.exit(1)
110
+ }
111
+
112
+ if (!config.teams[teamId]) {
113
+ console.log(formatOutput({ error: `Team not found: ${teamId}` }, options.pretty))
114
+ process.exit(1)
115
+ }
116
+
117
+ delete config.teams[teamId]
118
+
119
+ if (config.current_team === teamId) {
120
+ config.current_team = null
121
+ }
122
+
123
+ await credManager.saveConfig(config)
124
+ console.log(formatOutput({ removed: teamId }, options.pretty))
125
+ } catch (error) {
126
+ handleError(error as Error)
127
+ }
128
+ }
129
+
130
+ export const teamCommand = new Command('team')
131
+ .description('Team management commands')
132
+ .addCommand(
133
+ new Command('list')
134
+ .description('List all teams')
135
+ .option('--pretty', 'Pretty print JSON output')
136
+ .action(listAction)
137
+ )
138
+ .addCommand(
139
+ new Command('info')
140
+ .description('Get team info')
141
+ .argument('<team-id>', 'Team ID')
142
+ .option('--pretty', 'Pretty print JSON output')
143
+ .action(infoAction)
144
+ )
145
+ .addCommand(
146
+ new Command('switch')
147
+ .description('Switch to team')
148
+ .argument('<team-id>', 'Team ID')
149
+ .option('--pretty', 'Pretty print JSON output')
150
+ .action(switchAction)
151
+ )
152
+ .addCommand(
153
+ new Command('current')
154
+ .description('Show current team')
155
+ .option('--pretty', 'Pretty print JSON output')
156
+ .action(currentAction)
157
+ )
158
+ .addCommand(
159
+ new Command('remove')
160
+ .description('Remove team from config')
161
+ .argument('<team-id>', 'Team ID')
162
+ .option('--pretty', 'Pretty print JSON output')
163
+ .action(removeAction)
164
+ )
@@ -0,0 +1,83 @@
1
+ import { expect, mock, test } from 'bun:test'
2
+
3
+ // Mock TeamsClient
4
+ const mockClient = {
5
+ testAuth: mock(async () => ({
6
+ id: 'user123',
7
+ displayName: 'Test User',
8
+ email: 'test@example.com',
9
+ userPrincipalName: 'test@example.com',
10
+ })),
11
+ getUser: mock(async (userId: string) => ({
12
+ id: userId,
13
+ displayName: 'Test User',
14
+ email: 'test@example.com',
15
+ userPrincipalName: 'test@example.com',
16
+ })),
17
+ listUsers: mock(async (_teamId: string) => [
18
+ {
19
+ id: 'user1',
20
+ displayName: 'Alice',
21
+ email: 'alice@example.com',
22
+ },
23
+ {
24
+ id: 'user2',
25
+ displayName: 'Bob',
26
+ email: 'bob@example.com',
27
+ },
28
+ ]),
29
+ }
30
+
31
+ test('me returns current user info', async () => {
32
+ // given: authenticated user
33
+ const user = await mockClient.testAuth()
34
+
35
+ // when: getting current user
36
+ const result = {
37
+ id: user.id,
38
+ displayName: user.displayName,
39
+ email: user.email,
40
+ userPrincipalName: user.userPrincipalName,
41
+ }
42
+
43
+ // then: returns user object
44
+ expect(result.id).toBe('user123')
45
+ expect(result.displayName).toBe('Test User')
46
+ expect(result.email).toBe('test@example.com')
47
+ })
48
+
49
+ test('info returns user details by id', async () => {
50
+ // given: user id
51
+ const userId = 'user123'
52
+
53
+ // when: getting user info
54
+ const user = await mockClient.getUser(userId)
55
+ const result = {
56
+ id: user.id,
57
+ displayName: user.displayName,
58
+ email: user.email,
59
+ userPrincipalName: user.userPrincipalName,
60
+ }
61
+
62
+ // then: returns user object
63
+ expect(result.id).toBe('user123')
64
+ expect(result.displayName).toBe('Test User')
65
+ })
66
+
67
+ test('list returns team members', async () => {
68
+ // given: team id
69
+ const teamId = 'team123'
70
+
71
+ // when: listing users
72
+ const users = await mockClient.listUsers(teamId)
73
+ const result = users.map((u) => ({
74
+ id: u.id,
75
+ displayName: u.displayName,
76
+ email: u.email,
77
+ }))
78
+
79
+ // then: returns array of users
80
+ expect(result).toHaveLength(2)
81
+ expect(result[0].displayName).toBe('Alice')
82
+ expect(result[1].displayName).toBe('Bob')
83
+ })
@@ -0,0 +1,112 @@
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
+ 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 users = await client.listUsers(teamId)
21
+
22
+ const output = users.map((user) => ({
23
+ id: user.id,
24
+ displayName: user.displayName,
25
+ email: user.email,
26
+ userPrincipalName: user.userPrincipalName,
27
+ }))
28
+
29
+ console.log(formatOutput(output, options.pretty))
30
+ } catch (error) {
31
+ handleError(error as Error)
32
+ }
33
+ }
34
+
35
+ async function infoAction(userId: string, options: { pretty?: boolean }): Promise<void> {
36
+ try {
37
+ const credManager = new TeamsCredentialManager()
38
+ const config = await credManager.loadConfig()
39
+
40
+ if (!config?.token) {
41
+ console.log(
42
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
43
+ )
44
+ process.exit(1)
45
+ }
46
+
47
+ const client = new TeamsClient(config.token, config.token_expires_at)
48
+ const user = await client.getUser(userId)
49
+
50
+ const output = {
51
+ id: user.id,
52
+ displayName: user.displayName,
53
+ email: user.email,
54
+ userPrincipalName: user.userPrincipalName,
55
+ }
56
+
57
+ console.log(formatOutput(output, options.pretty))
58
+ } catch (error) {
59
+ handleError(error as Error)
60
+ }
61
+ }
62
+
63
+ async function meAction(options: { pretty?: boolean }): Promise<void> {
64
+ try {
65
+ const credManager = new TeamsCredentialManager()
66
+ const config = await credManager.loadConfig()
67
+
68
+ if (!config?.token) {
69
+ console.log(
70
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
71
+ )
72
+ process.exit(1)
73
+ }
74
+
75
+ const client = new TeamsClient(config.token, config.token_expires_at)
76
+ const user = await client.testAuth()
77
+
78
+ const output = {
79
+ id: user.id,
80
+ displayName: user.displayName,
81
+ email: user.email,
82
+ userPrincipalName: user.userPrincipalName,
83
+ }
84
+
85
+ console.log(formatOutput(output, options.pretty))
86
+ } catch (error) {
87
+ handleError(error as Error)
88
+ }
89
+ }
90
+
91
+ export const userCommand = new Command('user')
92
+ .description('User commands')
93
+ .addCommand(
94
+ new Command('list')
95
+ .description('List team members')
96
+ .argument('<team-id>', 'Team ID')
97
+ .option('--pretty', 'Pretty print JSON output')
98
+ .action(listAction)
99
+ )
100
+ .addCommand(
101
+ new Command('info')
102
+ .description('Get user info')
103
+ .argument('<user-id>', 'User ID')
104
+ .option('--pretty', 'Pretty print JSON output')
105
+ .action(infoAction)
106
+ )
107
+ .addCommand(
108
+ new Command('me')
109
+ .description('Show current authenticated user')
110
+ .option('--pretty', 'Pretty print JSON output')
111
+ .action(meAction)
112
+ )
@@ -0,0 +1,178 @@
1
+ import { afterAll, describe, expect, test } from 'bun:test'
2
+ import { existsSync, rmSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+ import { TeamsCredentialManager } from './credential-manager'
5
+
6
+ const testDirs: string[] = []
7
+
8
+ function setup(): TeamsCredentialManager {
9
+ const testConfigDir = join(
10
+ import.meta.dir,
11
+ `.test-teams-config-${Date.now()}-${Math.random().toString(36).slice(2)}`
12
+ )
13
+ testDirs.push(testConfigDir)
14
+ return new TeamsCredentialManager(testConfigDir)
15
+ }
16
+
17
+ afterAll(() => {
18
+ for (const dir of testDirs) {
19
+ rmSync(dir, { recursive: true, force: true })
20
+ }
21
+ })
22
+
23
+ describe('TeamsCredentialManager', () => {
24
+ test('loadConfig returns null when file does not exist', async () => {
25
+ const manager = setup()
26
+ const config = await manager.loadConfig()
27
+
28
+ expect(config).toBeNull()
29
+ })
30
+
31
+ test('saveConfig creates config file with correct permissions', async () => {
32
+ const testConfigDir = join(
33
+ import.meta.dir,
34
+ `.test-teams-config-${Date.now()}-${Math.random().toString(36).slice(2)}`
35
+ )
36
+ testDirs.push(testConfigDir)
37
+ const manager = new TeamsCredentialManager(testConfigDir)
38
+ const config = {
39
+ token: 'test-token',
40
+ current_team: 'team-123',
41
+ teams: {
42
+ 'team-123': { team_id: 'team-123', team_name: 'Test Team' },
43
+ },
44
+ }
45
+
46
+ await manager.saveConfig(config)
47
+
48
+ const credentialsPath = join(testConfigDir, 'teams-credentials.json')
49
+ expect(existsSync(credentialsPath)).toBe(true)
50
+
51
+ const file = Bun.file(credentialsPath)
52
+ const content = await file.text()
53
+ const loaded = JSON.parse(content)
54
+ expect(loaded).toEqual(config)
55
+ })
56
+
57
+ test('getToken returns null when not authenticated', async () => {
58
+ const manager = setup()
59
+ const token = await manager.getToken()
60
+ expect(token).toBeNull()
61
+ })
62
+
63
+ test('setToken saves token to config', async () => {
64
+ const manager = setup()
65
+ await manager.setToken('test-token-123')
66
+
67
+ const token = await manager.getToken()
68
+ expect(token).toBe('test-token-123')
69
+ })
70
+
71
+ test('setToken saves token with expiry', async () => {
72
+ const manager = setup()
73
+ const expiresAt = '2025-12-31T23:59:59Z'
74
+ await manager.setToken('test-token-123', expiresAt)
75
+
76
+ const config = await manager.loadConfig()
77
+ expect(config?.token).toBe('test-token-123')
78
+ expect(config?.token_expires_at).toBe(expiresAt)
79
+ })
80
+
81
+ test('getCurrentTeam returns null when not set', async () => {
82
+ const manager = setup()
83
+ const team = await manager.getCurrentTeam()
84
+ expect(team).toBeNull()
85
+ })
86
+
87
+ test('getCurrentTeam returns null when current_team is set but team not in teams record', async () => {
88
+ const manager = setup()
89
+ await manager.setToken('test-token')
90
+ const config = await manager.loadConfig()
91
+ if (config) {
92
+ config.current_team = 'non-existent-team'
93
+ await manager.saveConfig(config)
94
+ }
95
+
96
+ const team = await manager.getCurrentTeam()
97
+ expect(team).toBeNull()
98
+ })
99
+
100
+ test('setCurrentTeam saves team info', async () => {
101
+ const manager = setup()
102
+ await manager.setToken('test-token')
103
+ await manager.setCurrentTeam('team-456', 'My Team')
104
+
105
+ const team = await manager.getCurrentTeam()
106
+ expect(team).toEqual({ team_id: 'team-456', team_name: 'My Team' })
107
+ })
108
+
109
+ test('setCurrentTeam updates existing team', async () => {
110
+ const manager = setup()
111
+ await manager.setToken('test-token')
112
+ await manager.setCurrentTeam('team-1', 'Team One')
113
+ await manager.setCurrentTeam('team-2', 'Team Two')
114
+
115
+ const team = await manager.getCurrentTeam()
116
+ expect(team).toEqual({ team_id: 'team-2', team_name: 'Team Two' })
117
+
118
+ const config = await manager.loadConfig()
119
+ expect(config?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
120
+ expect(config?.teams['team-2']).toEqual({ team_id: 'team-2', team_name: 'Team Two' })
121
+ })
122
+
123
+ test('clearCredentials removes all credentials', async () => {
124
+ const manager = setup()
125
+ await manager.setToken('test-token', '2025-12-31T23:59:59Z')
126
+ await manager.setCurrentTeam('team-123', 'Test Team')
127
+
128
+ await manager.clearCredentials()
129
+
130
+ const config = await manager.loadConfig()
131
+ expect(config).toBeNull()
132
+ })
133
+
134
+ test('isTokenExpired returns true when no config exists', async () => {
135
+ const manager = setup()
136
+ const expired = await manager.isTokenExpired()
137
+ expect(expired).toBe(true)
138
+ })
139
+
140
+ test('isTokenExpired returns true when no token_expires_at is set', async () => {
141
+ const manager = setup()
142
+ await manager.setToken('test-token')
143
+
144
+ const expired = await manager.isTokenExpired()
145
+ expect(expired).toBe(true)
146
+ })
147
+
148
+ test('isTokenExpired returns true when token is expired', async () => {
149
+ const manager = setup()
150
+ const pastDate = new Date(Date.now() - 60000).toISOString()
151
+ await manager.setToken('test-token', pastDate)
152
+
153
+ const expired = await manager.isTokenExpired()
154
+ expect(expired).toBe(true)
155
+ })
156
+
157
+ test('isTokenExpired returns false when token is not expired', async () => {
158
+ const manager = setup()
159
+ const futureDate = new Date(Date.now() + 3600000).toISOString()
160
+ await manager.setToken('test-token', futureDate)
161
+
162
+ const expired = await manager.isTokenExpired()
163
+ expect(expired).toBe(false)
164
+ })
165
+
166
+ test('multiple operations preserve existing data', async () => {
167
+ const manager = setup()
168
+ await manager.setToken('token-1', '2025-12-31T23:59:59Z')
169
+ await manager.setCurrentTeam('team-1', 'Team One')
170
+
171
+ await manager.setToken('token-2')
172
+
173
+ const config = await manager.loadConfig()
174
+ expect(config?.token).toBe('token-2')
175
+ expect(config?.current_team).toBe('team-1')
176
+ expect(config?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
177
+ })
178
+ })
@@ -0,0 +1,92 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
3
+ import { homedir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import type { TeamsConfig } from './types'
6
+
7
+ export class TeamsCredentialManager {
8
+ private configDir: string
9
+ private credentialsPath: string
10
+
11
+ constructor(configDir?: string) {
12
+ this.configDir = configDir ?? join(homedir(), '.config', 'agent-messenger')
13
+ this.credentialsPath = join(this.configDir, 'teams-credentials.json')
14
+ }
15
+
16
+ async loadConfig(): Promise<TeamsConfig | null> {
17
+ if (!existsSync(this.credentialsPath)) {
18
+ return null
19
+ }
20
+
21
+ try {
22
+ const content = await readFile(this.credentialsPath, 'utf-8')
23
+ return JSON.parse(content) as TeamsConfig
24
+ } catch {
25
+ return null
26
+ }
27
+ }
28
+
29
+ async saveConfig(config: TeamsConfig): Promise<void> {
30
+ await mkdir(this.configDir, { recursive: true })
31
+ await writeFile(this.credentialsPath, JSON.stringify(config, null, 2), { mode: 0o600 })
32
+ }
33
+
34
+ async getToken(): Promise<string | null> {
35
+ const config = await this.loadConfig()
36
+ return config?.token ?? null
37
+ }
38
+
39
+ async setToken(token: string, expiresAt?: string): Promise<void> {
40
+ let config = await this.loadConfig()
41
+ if (!config) {
42
+ config = {
43
+ token,
44
+ current_team: null,
45
+ teams: {},
46
+ }
47
+ }
48
+ config.token = token
49
+ if (expiresAt !== undefined) {
50
+ config.token_expires_at = expiresAt
51
+ }
52
+ await this.saveConfig(config)
53
+ }
54
+
55
+ async getCurrentTeam(): Promise<{ team_id: string; team_name: string } | null> {
56
+ const config = await this.loadConfig()
57
+ if (!config?.current_team) {
58
+ return null
59
+ }
60
+ return config.teams[config.current_team] ?? null
61
+ }
62
+
63
+ async setCurrentTeam(teamId: string, teamName: string): Promise<void> {
64
+ let config = await this.loadConfig()
65
+ if (!config) {
66
+ config = {
67
+ token: '',
68
+ current_team: null,
69
+ teams: {},
70
+ }
71
+ }
72
+ config.current_team = teamId
73
+ config.teams[teamId] = { team_id: teamId, team_name: teamName }
74
+ await this.saveConfig(config)
75
+ }
76
+
77
+ async clearCredentials(): Promise<void> {
78
+ if (existsSync(this.credentialsPath)) {
79
+ await rm(this.credentialsPath)
80
+ }
81
+ }
82
+
83
+ async isTokenExpired(): Promise<boolean> {
84
+ const config = await this.loadConfig()
85
+ if (!config?.token_expires_at) {
86
+ return true
87
+ }
88
+
89
+ const expiresAt = new Date(config.token_expires_at)
90
+ return expiresAt.getTime() <= Date.now()
91
+ }
92
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Public exports for Teams platform module
3
+ */
4
+
5
+ // TODO: Implement following src/platforms/discord pattern