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
@@ -1,41 +1,57 @@
1
- import { expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
2
  import { DiscordClient } from '../client'
3
3
  import { DiscordCredentialManager } from '../credential-manager'
4
4
  import { DiscordTokenExtractor } from '../token-extractor'
5
5
 
6
- // Mock modules
7
- mock.module('../token-extractor', () => ({
8
- DiscordTokenExtractor: mock(() => ({
9
- extract: mock(async () => ({
10
- token: 'test-token-123',
11
- })),
12
- })),
13
- }))
6
+ let extractorExtractSpy: ReturnType<typeof spyOn>
7
+ let clientTestAuthSpy: ReturnType<typeof spyOn>
8
+ let clientListGuildsSpy: ReturnType<typeof spyOn>
9
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
10
+ let credManagerSaveSpy: ReturnType<typeof spyOn>
11
+ let credManagerClearTokenSpy: ReturnType<typeof spyOn>
14
12
 
15
- mock.module('../client', () => ({
16
- DiscordClient: mock((_token: string) => ({
17
- testAuth: mock(async () => ({
18
- id: 'user-123',
19
- username: 'testuser',
20
- })),
21
- listGuilds: mock(async () => [
22
- { id: 'guild-1', name: 'Guild One' },
23
- { id: 'guild-2', name: 'Guild Two' },
24
- ]),
25
- })),
26
- }))
13
+ beforeEach(() => {
14
+ // Spy on DiscordTokenExtractor.prototype.extract
15
+ extractorExtractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue({
16
+ token: 'test-token-123',
17
+ })
27
18
 
28
- mock.module('../credential-manager', () => ({
29
- DiscordCredentialManager: mock(() => ({
30
- load: mock(async () => ({
31
- token: null,
32
- current_guild: null,
33
- guilds: {},
34
- })),
35
- save: mock(async () => {}),
36
- clearToken: mock(async () => {}),
37
- })),
38
- }))
19
+ // Spy on DiscordClient.prototype methods
20
+ clientTestAuthSpy = spyOn(DiscordClient.prototype, 'testAuth').mockResolvedValue({
21
+ id: 'user-123',
22
+ username: 'testuser',
23
+ })
24
+
25
+ clientListGuildsSpy = spyOn(DiscordClient.prototype, 'listGuilds').mockResolvedValue([
26
+ { id: 'guild-1', name: 'Guild One' },
27
+ { id: 'guild-2', name: 'Guild Two' },
28
+ ])
29
+
30
+ // Spy on DiscordCredentialManager.prototype methods
31
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
32
+ token: null,
33
+ current_guild: null,
34
+ guilds: {},
35
+ })
36
+
37
+ credManagerSaveSpy = spyOn(DiscordCredentialManager.prototype, 'save').mockResolvedValue(
38
+ undefined
39
+ )
40
+
41
+ credManagerClearTokenSpy = spyOn(
42
+ DiscordCredentialManager.prototype,
43
+ 'clearToken'
44
+ ).mockResolvedValue(undefined)
45
+ })
46
+
47
+ afterEach(() => {
48
+ extractorExtractSpy?.mockRestore()
49
+ clientTestAuthSpy?.mockRestore()
50
+ clientListGuildsSpy?.mockRestore()
51
+ credManagerLoadSpy?.mockRestore()
52
+ credManagerSaveSpy?.mockRestore()
53
+ credManagerClearTokenSpy?.mockRestore()
54
+ })
39
55
 
40
56
  test('extract: calls DiscordTokenExtractor', async () => {
41
57
  const extractor = new DiscordTokenExtractor()
@@ -1,15 +1,22 @@
1
- import { expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
2
  import { DiscordClient } from '../client'
3
-
4
- // Mock modules
5
- mock.module('../client', () => ({
6
- DiscordClient: mock((_token: string) => ({
7
- listChannels: mock(async (guildId: string) => [
8
- { id: 'ch-1', guild_id: guildId, name: 'general', type: 0, topic: 'General discussion' },
9
- { id: 'ch-2', guild_id: guildId, name: 'announcements', type: 0, topic: 'Announcements' },
10
- { id: 'ch-3', guild_id: guildId, name: 'voice-channel', type: 2, topic: null },
11
- ]),
12
- getChannel: mock(async (channelId: string) => {
3
+ import { DiscordCredentialManager } from '../credential-manager'
4
+
5
+ let clientListChannelsSpy: ReturnType<typeof spyOn>
6
+ let clientGetChannelSpy: ReturnType<typeof spyOn>
7
+ let clientGetMessagesSpy: ReturnType<typeof spyOn>
8
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
9
+
10
+ beforeEach(() => {
11
+ // Spy on DiscordClient.prototype methods
12
+ clientListChannelsSpy = spyOn(DiscordClient.prototype, 'listChannels').mockResolvedValue([
13
+ { id: 'ch-1', guild_id: 'guild-1', name: 'general', type: 0, topic: 'General discussion' },
14
+ { id: 'ch-2', guild_id: 'guild-1', name: 'announcements', type: 0, topic: 'Announcements' },
15
+ { id: 'ch-3', guild_id: 'guild-1', name: 'voice-channel', type: 2, topic: undefined },
16
+ ])
17
+
18
+ clientGetChannelSpy = spyOn(DiscordClient.prototype, 'getChannel').mockImplementation(
19
+ async (channelId: string) => {
13
20
  if (channelId === 'ch-1') {
14
21
  return {
15
22
  id: 'ch-1',
@@ -29,37 +36,42 @@ mock.module('../client', () => ({
29
36
  }
30
37
  }
31
38
  throw new Error('Channel not found')
32
- }),
33
- getMessages: mock(async (channelId: string, _limit: number) => [
34
- {
35
- id: 'msg-1',
36
- channel_id: channelId,
37
- author: { id: 'user-1', username: 'alice' },
38
- content: 'Hello world',
39
- timestamp: '2024-01-29T10:00:00Z',
40
- },
41
- {
42
- id: 'msg-2',
43
- channel_id: channelId,
44
- author: { id: 'user-2', username: 'bob' },
45
- content: 'Hi there',
46
- timestamp: '2024-01-29T09:00:00Z',
47
- },
48
- ]),
49
- })),
50
- }))
51
-
52
- mock.module('../credential-manager', () => ({
53
- DiscordCredentialManager: mock(() => ({
54
- load: mock(async () => ({
55
- token: 'test-token',
56
- current_guild: 'guild-1',
57
- guilds: {
58
- 'guild-1': { guild_id: 'guild-1', guild_name: 'Guild One' },
59
- },
60
- })),
61
- })),
62
- }))
39
+ }
40
+ )
41
+
42
+ clientGetMessagesSpy = spyOn(DiscordClient.prototype, 'getMessages').mockResolvedValue([
43
+ {
44
+ id: 'msg-1',
45
+ channel_id: 'ch-1',
46
+ author: { id: 'user-1', username: 'alice' },
47
+ content: 'Hello world',
48
+ timestamp: '2024-01-29T10:00:00Z',
49
+ },
50
+ {
51
+ id: 'msg-2',
52
+ channel_id: 'ch-1',
53
+ author: { id: 'user-2', username: 'bob' },
54
+ content: 'Hi there',
55
+ timestamp: '2024-01-29T09:00:00Z',
56
+ },
57
+ ])
58
+
59
+ // Spy on DiscordCredentialManager.prototype methods
60
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
61
+ token: 'test-token',
62
+ current_guild: 'guild-1',
63
+ guilds: {
64
+ 'guild-1': { guild_id: 'guild-1', guild_name: 'Guild One' },
65
+ },
66
+ })
67
+ })
68
+
69
+ afterEach(() => {
70
+ clientListChannelsSpy?.mockRestore()
71
+ clientGetChannelSpy?.mockRestore()
72
+ clientGetMessagesSpy?.mockRestore()
73
+ credManagerLoadSpy?.mockRestore()
74
+ })
63
75
 
64
76
  test('list: returns text channels (type=0) from guild', async () => {
65
77
  // given: discord client with channels
@@ -1,64 +1,51 @@
1
- import { beforeEach, expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { DiscordClient } from '../client'
3
+ import { DiscordCredentialManager } from '../credential-manager'
2
4
  import { infoAction, listAction, uploadAction } from './file'
3
5
 
4
- mock.module('../client', () => ({
5
- DiscordClient: mock(() => ({
6
- uploadFile: mock(async () => ({
6
+ let clientUploadFileSpy: ReturnType<typeof spyOn>
7
+ let clientListFilesSpy: ReturnType<typeof spyOn>
8
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
9
+
10
+ beforeEach(() => {
11
+ // Spy on DiscordClient.prototype methods
12
+ clientUploadFileSpy = spyOn(DiscordClient.prototype, 'uploadFile').mockResolvedValue({
13
+ id: 'file_123',
14
+ filename: 'test.pdf',
15
+ size: 1024,
16
+ url: 'https://cdn.discordapp.com/attachments/123/456/test.pdf',
17
+ content_type: 'application/pdf',
18
+ })
19
+
20
+ clientListFilesSpy = spyOn(DiscordClient.prototype, 'listFiles').mockResolvedValue([
21
+ {
7
22
  id: 'file_123',
8
23
  filename: 'test.pdf',
9
24
  size: 1024,
10
25
  url: 'https://cdn.discordapp.com/attachments/123/456/test.pdf',
11
26
  content_type: 'application/pdf',
12
- })),
13
- listFiles: mock(async () => [
14
- {
15
- id: 'file_123',
16
- filename: 'test.pdf',
17
- size: 1024,
18
- url: 'https://cdn.discordapp.com/attachments/123/456/test.pdf',
19
- content_type: 'application/pdf',
20
- },
21
- {
22
- id: 'file_124',
23
- filename: 'image.png',
24
- size: 2048,
25
- url: 'https://cdn.discordapp.com/attachments/123/457/image.png',
26
- content_type: 'image/png',
27
- },
28
- ]),
29
- })),
30
- }))
31
-
32
- mock.module('../credential-manager', () => ({
33
- DiscordCredentialManager: mock(() => ({
34
- load: mock(async () => ({
35
- token: 'test_token',
36
- current_guild: 'guild_123',
37
- guilds: {},
38
- })),
39
- })),
40
- }))
41
-
42
- mock.module('../../../shared/utils/output', () => ({
43
- formatOutput: (data: any, pretty?: boolean) => JSON.stringify(data, null, pretty ? 2 : 0),
44
- }))
45
-
46
- mock.module('../../../shared/utils/error-handler', () => ({
47
- handleError: (error: Error) => {
48
- console.error(error.message)
49
- },
50
- }))
51
-
52
- mock.module('node:fs', () => ({
53
- readFileSync: () => Buffer.from('test file content'),
54
- }))
55
-
56
- mock.module('node:path', () => ({
57
- resolve: (path: string) => path,
58
- }))
27
+ },
28
+ {
29
+ id: 'file_124',
30
+ filename: 'image.png',
31
+ size: 2048,
32
+ url: 'https://cdn.discordapp.com/attachments/123/457/image.png',
33
+ content_type: 'image/png',
34
+ },
35
+ ])
36
+
37
+ // Spy on DiscordCredentialManager.prototype methods
38
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
39
+ token: 'test_token',
40
+ current_guild: 'guild_123',
41
+ guilds: {},
42
+ })
43
+ })
59
44
 
60
- beforeEach(() => {
61
- mock.restore()
45
+ afterEach(() => {
46
+ clientUploadFileSpy?.mockRestore()
47
+ clientListFilesSpy?.mockRestore()
48
+ credManagerLoadSpy?.mockRestore()
62
49
  })
63
50
 
64
51
  test('upload: sends multipart request and returns file info', async () => {
@@ -1,15 +1,22 @@
1
- import { expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
2
  import { DiscordClient } from '../client'
3
3
  import { DiscordCredentialManager } from '../credential-manager'
4
4
 
5
- // Mock modules
6
- mock.module('../client', () => ({
7
- DiscordClient: mock((_token: string) => ({
8
- listGuilds: mock(async () => [
9
- { id: 'guild-1', name: 'Guild One', icon: 'icon1', owner: true },
10
- { id: 'guild-2', name: 'Guild Two', icon: 'icon2', owner: false },
11
- ]),
12
- getGuild: mock(async (guildId: string) => {
5
+ let clientListGuildsSpy: ReturnType<typeof spyOn>
6
+ let clientGetGuildSpy: ReturnType<typeof spyOn>
7
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
8
+ let credManagerSetCurrentGuildSpy: ReturnType<typeof spyOn>
9
+ let credManagerGetCurrentGuildSpy: ReturnType<typeof spyOn>
10
+
11
+ beforeEach(() => {
12
+ // Spy on DiscordClient.prototype methods
13
+ clientListGuildsSpy = spyOn(DiscordClient.prototype, 'listGuilds').mockResolvedValue([
14
+ { id: 'guild-1', name: 'Guild One', icon: 'icon1', owner: true },
15
+ { id: 'guild-2', name: 'Guild Two', icon: 'icon2', owner: false },
16
+ ])
17
+
18
+ clientGetGuildSpy = spyOn(DiscordClient.prototype, 'getGuild').mockImplementation(
19
+ async (guildId: string) => {
13
20
  if (guildId === 'guild-1') {
14
21
  return { id: 'guild-1', name: 'Guild One', icon: 'icon1', owner: true }
15
22
  }
@@ -17,24 +24,37 @@ mock.module('../client', () => ({
17
24
  return { id: 'guild-2', name: 'Guild Two', icon: 'icon2', owner: false }
18
25
  }
19
26
  throw new Error('Guild not found')
20
- }),
21
- })),
22
- }))
23
-
24
- mock.module('../credential-manager', () => ({
25
- DiscordCredentialManager: mock(() => ({
26
- load: mock(async () => ({
27
- token: 'test-token',
28
- current_guild: 'guild-1',
29
- guilds: {
30
- 'guild-1': { guild_id: 'guild-1', guild_name: 'Guild One' },
31
- 'guild-2': { guild_id: 'guild-2', guild_name: 'Guild Two' },
32
- },
33
- })),
34
- setCurrentGuild: mock(async () => {}),
35
- getCurrentGuild: mock(async () => 'guild-1'),
36
- })),
37
- }))
27
+ }
28
+ )
29
+
30
+ // Spy on DiscordCredentialManager.prototype methods
31
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
32
+ token: 'test-token',
33
+ current_guild: 'guild-1',
34
+ guilds: {
35
+ 'guild-1': { guild_id: 'guild-1', guild_name: 'Guild One' },
36
+ 'guild-2': { guild_id: 'guild-2', guild_name: 'Guild Two' },
37
+ },
38
+ })
39
+
40
+ credManagerSetCurrentGuildSpy = spyOn(
41
+ DiscordCredentialManager.prototype,
42
+ 'setCurrentGuild'
43
+ ).mockResolvedValue(undefined)
44
+
45
+ credManagerGetCurrentGuildSpy = spyOn(
46
+ DiscordCredentialManager.prototype,
47
+ 'getCurrentGuild'
48
+ ).mockResolvedValue('guild-1')
49
+ })
50
+
51
+ afterEach(() => {
52
+ clientListGuildsSpy?.mockRestore()
53
+ clientGetGuildSpy?.mockRestore()
54
+ credManagerLoadSpy?.mockRestore()
55
+ credManagerSetCurrentGuildSpy?.mockRestore()
56
+ credManagerGetCurrentGuildSpy?.mockRestore()
57
+ })
38
58
 
39
59
  test('list: returns guilds with current marker', async () => {
40
60
  // given: credential manager with guilds
@@ -1,64 +1,67 @@
1
- import { beforeEach, expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { DiscordClient } from '../client'
3
+ import { DiscordCredentialManager } from '../credential-manager'
2
4
  import { deleteAction, getAction, listAction, sendAction } from './message'
3
5
 
4
- mock.module('../client', () => ({
5
- DiscordClient: mock(() => ({
6
- sendMessage: mock(async () => ({
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
+
12
+ beforeEach(() => {
13
+ // Spy on DiscordClient.prototype methods
14
+ clientSendMessageSpy = spyOn(DiscordClient.prototype, 'sendMessage').mockResolvedValue({
15
+ id: 'msg_123',
16
+ channel_id: 'ch_456',
17
+ author: { id: 'user_789', username: 'testuser' },
18
+ content: 'Hello world',
19
+ timestamp: '2025-01-29T10:00:00Z',
20
+ })
21
+
22
+ clientGetMessagesSpy = spyOn(DiscordClient.prototype, 'getMessages').mockResolvedValue([
23
+ {
7
24
  id: 'msg_123',
8
25
  channel_id: 'ch_456',
9
26
  author: { id: 'user_789', username: 'testuser' },
10
27
  content: 'Hello world',
11
28
  timestamp: '2025-01-29T10:00:00Z',
12
- })),
13
- getMessages: mock(async () => [
14
- {
15
- id: 'msg_123',
16
- channel_id: 'ch_456',
17
- author: { id: 'user_789', username: 'testuser' },
18
- content: 'Hello world',
19
- timestamp: '2025-01-29T10:00:00Z',
20
- },
21
- {
22
- id: 'msg_124',
23
- channel_id: 'ch_456',
24
- author: { id: 'user_789', username: 'testuser' },
25
- content: 'Second message',
26
- timestamp: '2025-01-29T10:01:00Z',
27
- },
28
- ]),
29
- getMessage: mock(async () => ({
30
- id: 'msg_123',
29
+ },
30
+ {
31
+ id: 'msg_124',
31
32
  channel_id: 'ch_456',
32
33
  author: { id: 'user_789', username: 'testuser' },
33
- content: 'Hello world',
34
- timestamp: '2025-01-29T10:00:00Z',
35
- })),
36
- deleteMessage: mock(async () => undefined),
37
- })),
38
- }))
39
-
40
- mock.module('../credential-manager', () => ({
41
- DiscordCredentialManager: mock(() => ({
42
- load: mock(async () => ({
43
- token: 'test_token',
44
- current_guild: 'guild_123',
45
- guilds: {},
46
- })),
47
- })),
48
- }))
49
-
50
- mock.module('../../../shared/utils/output', () => ({
51
- formatOutput: (data: any, pretty?: boolean) => JSON.stringify(data, null, pretty ? 2 : 0),
52
- }))
53
-
54
- mock.module('../../../shared/utils/error-handler', () => ({
55
- handleError: (error: Error) => {
56
- console.error(error.message)
57
- },
58
- }))
34
+ content: 'Second message',
35
+ timestamp: '2025-01-29T10:01:00Z',
36
+ },
37
+ ])
38
+
39
+ clientGetMessageSpy = spyOn(DiscordClient.prototype, 'getMessage').mockResolvedValue({
40
+ id: 'msg_123',
41
+ channel_id: 'ch_456',
42
+ author: { id: 'user_789', username: 'testuser' },
43
+ content: 'Hello world',
44
+ timestamp: '2025-01-29T10:00:00Z',
45
+ })
46
+
47
+ clientDeleteMessageSpy = spyOn(DiscordClient.prototype, 'deleteMessage').mockResolvedValue(
48
+ undefined
49
+ )
50
+
51
+ // Spy on DiscordCredentialManager.prototype methods
52
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
53
+ token: 'test_token',
54
+ current_guild: 'guild_123',
55
+ guilds: {},
56
+ })
57
+ })
59
58
 
60
- beforeEach(() => {
61
- mock.restore()
59
+ afterEach(() => {
60
+ clientSendMessageSpy?.mockRestore()
61
+ clientGetMessagesSpy?.mockRestore()
62
+ clientGetMessageSpy?.mockRestore()
63
+ clientDeleteMessageSpy?.mockRestore()
64
+ credManagerLoadSpy?.mockRestore()
62
65
  })
63
66
 
64
67
  test('send: returns message with id', async () => {
@@ -1,38 +1,53 @@
1
- import { expect, mock, test } from 'bun:test'
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { DiscordClient } from '../client'
3
+ import { DiscordCredentialManager } from '../credential-manager'
2
4
  import { addAction, listAction, removeAction } from './reaction'
3
5
 
4
- // Mock modules
5
- mock.module('../client', () => ({
6
- DiscordClient: mock(() => ({
7
- addReaction: mock(async () => {}),
8
- removeReaction: mock(async () => {}),
9
- getMessage: mock(async () => ({
10
- id: 'msg123',
11
- channel_id: 'ch123',
12
- author: { id: 'user123', username: 'testuser' },
13
- content: 'test message',
14
- timestamp: '2024-01-01T00:00:00Z',
15
- reactions: [
16
- {
17
- emoji: { name: 'thumbsup', id: undefined },
18
- count: 2,
19
- },
20
- {
21
- emoji: { name: 'heart', id: undefined },
22
- count: 1,
23
- },
24
- ],
25
- })),
26
- })),
27
- }))
28
-
29
- mock.module('../credential-manager', () => ({
30
- DiscordCredentialManager: mock(() => ({
31
- load: mock(async () => ({
32
- token: 'test-token',
33
- })),
34
- })),
35
- }))
6
+ let clientAddReactionSpy: ReturnType<typeof spyOn>
7
+ let clientRemoveReactionSpy: ReturnType<typeof spyOn>
8
+ let clientGetMessageSpy: ReturnType<typeof spyOn>
9
+ let credManagerLoadSpy: ReturnType<typeof spyOn>
10
+
11
+ beforeEach(() => {
12
+ // Spy on DiscordClient.prototype methods
13
+ clientAddReactionSpy = spyOn(DiscordClient.prototype, 'addReaction').mockResolvedValue(undefined)
14
+
15
+ clientRemoveReactionSpy = spyOn(DiscordClient.prototype, 'removeReaction').mockResolvedValue(
16
+ undefined
17
+ )
18
+
19
+ clientGetMessageSpy = spyOn(DiscordClient.prototype, 'getMessage').mockResolvedValue({
20
+ id: 'msg123',
21
+ channel_id: 'ch123',
22
+ author: { id: 'user123', username: 'testuser' },
23
+ content: 'test message',
24
+ timestamp: '2024-01-01T00:00:00Z',
25
+ reactions: [
26
+ {
27
+ emoji: { name: 'thumbsup', id: undefined },
28
+ count: 2,
29
+ },
30
+ {
31
+ emoji: { name: 'heart', id: undefined },
32
+ count: 1,
33
+ },
34
+ ],
35
+ } as any)
36
+
37
+ // Spy on DiscordCredentialManager.prototype methods
38
+ credManagerLoadSpy = spyOn(DiscordCredentialManager.prototype, 'load').mockResolvedValue({
39
+ token: 'test-token',
40
+ current_guild: null,
41
+ guilds: {},
42
+ })
43
+ })
44
+
45
+ afterEach(() => {
46
+ clientAddReactionSpy?.mockRestore()
47
+ clientRemoveReactionSpy?.mockRestore()
48
+ clientGetMessageSpy?.mockRestore()
49
+ credManagerLoadSpy?.mockRestore()
50
+ })
36
51
 
37
52
  test('add: sends correct PUT request with emoji', async () => {
38
53
  const consoleSpy = mock((_msg: string) => {})
@@ -91,15 +106,12 @@ test('list: extracts reactions from message', async () => {
91
106
  })
92
107
 
93
108
  test('add: handles missing token gracefully', async () => {
94
- const credManagerMock = mock(() => ({
95
- load: mock(async () => ({
96
- token: null,
97
- })),
98
- }))
99
-
100
- mock.module('../credential-manager', () => ({
101
- DiscordCredentialManager: credManagerMock,
102
- }))
109
+ // Temporarily override the credential manager spy to return null token
110
+ credManagerLoadSpy?.mockResolvedValue({
111
+ token: null,
112
+ current_guild: null,
113
+ guilds: {},
114
+ })
103
115
 
104
116
  const consoleSpy = mock((_msg: string) => {})
105
117
  const originalLog = console.log
@@ -3,6 +3,7 @@ import { handleError } from '../../../shared/utils/error-handler'
3
3
  import { formatOutput } from '../../../shared/utils/output'
4
4
  import { DiscordClient } from '../client'
5
5
  import { DiscordCredentialManager } from '../credential-manager'
6
+ import type { DiscordUser } from '../types'
6
7
 
7
8
  async function listAction(options: { pretty?: boolean }): Promise<void> {
8
9
  try {
@@ -56,7 +57,17 @@ async function infoAction(userId: string, options: { pretty?: boolean }): Promis
56
57
  }
57
58
 
58
59
  const client = new DiscordClient(config.token)
59
- const user = await client.getUser(userId)
60
+
61
+ // Check if requesting current user - use @me endpoint (works with user tokens)
62
+ const me = await client.testAuth()
63
+ let user: DiscordUser
64
+
65
+ if (userId === me.id) {
66
+ user = me
67
+ } else {
68
+ // For other users, try the regular endpoint (requires bot token)
69
+ user = await client.getUser(userId)
70
+ }
60
71
 
61
72
  const output = {
62
73
  id: user.id,