agent-messenger 1.0.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.
- package/.claude/commands/release.md +92 -0
- package/.claude-plugin/README.md +144 -0
- package/.claude-plugin/marketplace.json +37 -0
- package/.claude-plugin/plugin.json +17 -0
- package/.github/workflows/ci.yml +30 -0
- package/CLAUDE.md +106 -0
- package/CONTRIBUTING.md +131 -0
- package/README.md +140 -0
- package/biome.json +34 -0
- package/bun.lock +252 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +140 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/channel.d.ts +3 -0
- package/dist/commands/channel.d.ts.map +1 -0
- package/dist/commands/channel.js +118 -0
- package/dist/commands/channel.js.map +1 -0
- package/dist/commands/file.d.ts +3 -0
- package/dist/commands/file.d.ts.map +1 -0
- package/dist/commands/file.js +113 -0
- package/dist/commands/file.js.map +1 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +9 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/message.d.ts +3 -0
- package/dist/commands/message.d.ts.map +1 -0
- package/dist/commands/message.js +214 -0
- package/dist/commands/message.js.map +1 -0
- package/dist/commands/reaction.d.ts +3 -0
- package/dist/commands/reaction.d.ts.map +1 -0
- package/dist/commands/reaction.js +100 -0
- package/dist/commands/reaction.js.map +1 -0
- package/dist/commands/snapshot.d.ts +3 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +88 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/user.d.ts +3 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +96 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/commands/workspace.d.ts +3 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +89 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/lib/credential-manager.d.ts +13 -0
- package/dist/lib/credential-manager.d.ts.map +1 -0
- package/dist/lib/credential-manager.js +58 -0
- package/dist/lib/credential-manager.js.map +1 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/ref-manager.d.ts +26 -0
- package/dist/lib/ref-manager.d.ts.map +1 -0
- package/dist/lib/ref-manager.js +92 -0
- package/dist/lib/ref-manager.js.map +1 -0
- package/dist/lib/slack-client.d.ts +37 -0
- package/dist/lib/slack-client.d.ts.map +1 -0
- package/dist/lib/slack-client.js +379 -0
- package/dist/lib/slack-client.js.map +1 -0
- package/dist/lib/token-extractor.d.ts +28 -0
- package/dist/lib/token-extractor.d.ts.map +1 -0
- package/dist/lib/token-extractor.js +401 -0
- package/dist/lib/token-extractor.js.map +1 -0
- package/dist/package.json +37 -0
- package/dist/src/cli.d.ts +5 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +22 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/platforms/discord/cli.d.ts +5 -0
- package/dist/src/platforms/discord/cli.d.ts.map +1 -0
- package/dist/src/platforms/discord/cli.js +22 -0
- package/dist/src/platforms/discord/cli.js.map +1 -0
- package/dist/src/platforms/discord/client.d.ts +34 -0
- package/dist/src/platforms/discord/client.d.ts.map +1 -0
- package/dist/src/platforms/discord/client.js +187 -0
- package/dist/src/platforms/discord/client.js.map +1 -0
- package/dist/src/platforms/discord/client.test.d.ts +2 -0
- package/dist/src/platforms/discord/client.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/client.test.js +367 -0
- package/dist/src/platforms/discord/client.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/auth.d.ts +13 -0
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/auth.js +155 -0
- package/dist/src/platforms/discord/commands/auth.js.map +1 -0
- package/dist/src/platforms/discord/commands/auth.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/auth.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/auth.test.js +65 -0
- package/dist/src/platforms/discord/commands/auth.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/channel.d.ts +13 -0
- package/dist/src/platforms/discord/commands/channel.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/channel.js +99 -0
- package/dist/src/platforms/discord/commands/channel.js.map +1 -0
- package/dist/src/platforms/discord/commands/channel.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/channel.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/channel.test.js +136 -0
- package/dist/src/platforms/discord/commands/channel.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/file.d.ts +13 -0
- package/dist/src/platforms/discord/commands/file.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/file.js +99 -0
- package/dist/src/platforms/discord/commands/file.js.map +1 -0
- package/dist/src/platforms/discord/commands/file.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/file.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/file.test.js +83 -0
- package/dist/src/platforms/discord/commands/file.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/guild.d.ts +15 -0
- package/dist/src/platforms/discord/commands/guild.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/guild.js +102 -0
- package/dist/src/platforms/discord/commands/guild.js.map +1 -0
- package/dist/src/platforms/discord/commands/guild.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/guild.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/guild.test.js +100 -0
- package/dist/src/platforms/discord/commands/guild.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/index.d.ts +9 -0
- package/dist/src/platforms/discord/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/index.js +9 -0
- package/dist/src/platforms/discord/commands/index.js.map +1 -0
- package/dist/src/platforms/discord/commands/message.d.ts +17 -0
- package/dist/src/platforms/discord/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/message.js +131 -0
- package/dist/src/platforms/discord/commands/message.js.map +1 -0
- package/dist/src/platforms/discord/commands/message.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/message.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/message.test.js +91 -0
- package/dist/src/platforms/discord/commands/message.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/reaction.d.ts +12 -0
- package/dist/src/platforms/discord/commands/reaction.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/reaction.js +99 -0
- package/dist/src/platforms/discord/commands/reaction.js.map +1 -0
- package/dist/src/platforms/discord/commands/reaction.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/reaction.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/reaction.test.js +115 -0
- package/dist/src/platforms/discord/commands/reaction.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/snapshot.d.ts +9 -0
- package/dist/src/platforms/discord/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/snapshot.js +80 -0
- package/dist/src/platforms/discord/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/discord/commands/snapshot.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/snapshot.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/snapshot.test.js +25 -0
- package/dist/src/platforms/discord/commands/snapshot.test.js.map +1 -0
- package/dist/src/platforms/discord/commands/user.d.ts +3 -0
- package/dist/src/platforms/discord/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/user.js +94 -0
- package/dist/src/platforms/discord/commands/user.js.map +1 -0
- package/dist/src/platforms/discord/commands/user.test.d.ts +2 -0
- package/dist/src/platforms/discord/commands/user.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/commands/user.test.js +103 -0
- package/dist/src/platforms/discord/commands/user.test.js.map +1 -0
- package/dist/src/platforms/discord/credential-manager.d.ts +33 -0
- package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/discord/credential-manager.js +73 -0
- package/dist/src/platforms/discord/credential-manager.js.map +1 -0
- package/dist/src/platforms/discord/credential-manager.test.d.ts +2 -0
- package/dist/src/platforms/discord/credential-manager.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/credential-manager.test.js +136 -0
- package/dist/src/platforms/discord/credential-manager.test.js.map +1 -0
- package/dist/src/platforms/discord/token-extractor.d.ts +55 -0
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/discord/token-extractor.js +462 -0
- package/dist/src/platforms/discord/token-extractor.js.map +1 -0
- package/dist/src/platforms/discord/token-extractor.test.d.ts +2 -0
- package/dist/src/platforms/discord/token-extractor.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/token-extractor.test.js +789 -0
- package/dist/src/platforms/discord/token-extractor.test.js.map +1 -0
- package/dist/src/platforms/discord/types.d.ts +251 -0
- package/dist/src/platforms/discord/types.d.ts.map +1 -0
- package/dist/src/platforms/discord/types.js +74 -0
- package/dist/src/platforms/discord/types.js.map +1 -0
- package/dist/src/platforms/discord/types.test.d.ts +2 -0
- package/dist/src/platforms/discord/types.test.d.ts.map +1 -0
- package/dist/src/platforms/discord/types.test.js +211 -0
- package/dist/src/platforms/discord/types.test.js.map +1 -0
- package/dist/src/platforms/slack/cli.d.ts +5 -0
- package/dist/src/platforms/slack/cli.d.ts.map +1 -0
- package/dist/src/platforms/slack/cli.js +22 -0
- package/dist/src/platforms/slack/cli.js.map +1 -0
- package/dist/src/platforms/slack/client.d.ts +47 -0
- package/dist/src/platforms/slack/client.d.ts.map +1 -0
- package/dist/src/platforms/slack/client.js +412 -0
- package/dist/src/platforms/slack/client.js.map +1 -0
- package/dist/src/platforms/slack/commands/auth.d.ts +3 -0
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/auth.js +156 -0
- package/dist/src/platforms/slack/commands/auth.js.map +1 -0
- package/dist/src/platforms/slack/commands/channel.d.ts +3 -0
- package/dist/src/platforms/slack/commands/channel.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/channel.js +118 -0
- package/dist/src/platforms/slack/commands/channel.js.map +1 -0
- package/dist/src/platforms/slack/commands/file.d.ts +3 -0
- package/dist/src/platforms/slack/commands/file.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/file.js +113 -0
- package/dist/src/platforms/slack/commands/file.js.map +1 -0
- package/dist/src/platforms/slack/commands/index.d.ts +9 -0
- package/dist/src/platforms/slack/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/index.js +9 -0
- package/dist/src/platforms/slack/commands/index.js.map +1 -0
- package/dist/src/platforms/slack/commands/message.d.ts +3 -0
- package/dist/src/platforms/slack/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/message.js +263 -0
- package/dist/src/platforms/slack/commands/message.js.map +1 -0
- package/dist/src/platforms/slack/commands/reaction.d.ts +3 -0
- package/dist/src/platforms/slack/commands/reaction.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/reaction.js +100 -0
- package/dist/src/platforms/slack/commands/reaction.js.map +1 -0
- package/dist/src/platforms/slack/commands/snapshot.d.ts +3 -0
- package/dist/src/platforms/slack/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/snapshot.js +87 -0
- package/dist/src/platforms/slack/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/slack/commands/user.d.ts +3 -0
- package/dist/src/platforms/slack/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/user.js +96 -0
- package/dist/src/platforms/slack/commands/user.js.map +1 -0
- package/dist/src/platforms/slack/commands/workspace.d.ts +3 -0
- package/dist/src/platforms/slack/commands/workspace.d.ts.map +1 -0
- package/dist/src/platforms/slack/commands/workspace.js +89 -0
- package/dist/src/platforms/slack/commands/workspace.js.map +1 -0
- package/dist/src/platforms/slack/credential-manager.d.ts +13 -0
- package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/slack/credential-manager.js +58 -0
- package/dist/src/platforms/slack/credential-manager.js.map +1 -0
- package/dist/src/platforms/slack/index.d.ts +3 -0
- package/dist/src/platforms/slack/index.d.ts.map +1 -0
- package/dist/src/platforms/slack/index.js +3 -0
- package/dist/src/platforms/slack/index.js.map +1 -0
- package/dist/src/platforms/slack/token-extractor.d.ts +28 -0
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/slack/token-extractor.js +401 -0
- package/dist/src/platforms/slack/token-extractor.js.map +1 -0
- package/dist/src/platforms/slack/types.d.ts +369 -0
- package/dist/src/platforms/slack/types.d.ts.map +1 -0
- package/dist/src/platforms/slack/types.js +92 -0
- package/dist/src/platforms/slack/types.js.map +1 -0
- package/dist/src/shared/utils/concurrency.d.ts +2 -0
- package/dist/src/shared/utils/concurrency.d.ts.map +1 -0
- package/dist/src/shared/utils/concurrency.js +14 -0
- package/dist/src/shared/utils/concurrency.js.map +1 -0
- package/dist/src/shared/utils/concurrency.test.d.ts +2 -0
- package/dist/src/shared/utils/concurrency.test.d.ts.map +1 -0
- package/dist/src/shared/utils/concurrency.test.js +39 -0
- package/dist/src/shared/utils/concurrency.test.js.map +1 -0
- package/dist/src/shared/utils/error-handler.d.ts +2 -0
- package/dist/src/shared/utils/error-handler.d.ts.map +1 -0
- package/dist/src/shared/utils/error-handler.js +5 -0
- package/dist/src/shared/utils/error-handler.js.map +1 -0
- package/dist/src/shared/utils/output.d.ts +2 -0
- package/dist/src/shared/utils/output.d.ts.map +1 -0
- package/dist/src/shared/utils/output.js +4 -0
- package/dist/src/shared/utils/output.js.map +1 -0
- package/dist/tests/cli.test.d.ts +2 -0
- package/dist/tests/cli.test.d.ts.map +1 -0
- package/dist/tests/cli.test.js +83 -0
- package/dist/tests/cli.test.js.map +1 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/CURRENT +1 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOCK +0 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG +3 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG.old +1 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/MANIFEST-000004 +0 -0
- package/dist/tests/commands/auth.test.d.ts +2 -0
- package/dist/tests/commands/auth.test.d.ts.map +1 -0
- package/dist/tests/commands/auth.test.js +304 -0
- package/dist/tests/commands/auth.test.js.map +1 -0
- package/dist/tests/commands/channel.test.d.ts +2 -0
- package/dist/tests/commands/channel.test.d.ts.map +1 -0
- package/dist/tests/commands/channel.test.js +166 -0
- package/dist/tests/commands/channel.test.js.map +1 -0
- package/dist/tests/commands/file.test.d.ts +2 -0
- package/dist/tests/commands/file.test.d.ts.map +1 -0
- package/dist/tests/commands/file.test.js +175 -0
- package/dist/tests/commands/file.test.js.map +1 -0
- package/dist/tests/commands/message.test.d.ts +2 -0
- package/dist/tests/commands/message.test.d.ts.map +1 -0
- package/dist/tests/commands/message.test.js +293 -0
- package/dist/tests/commands/message.test.js.map +1 -0
- package/dist/tests/commands/reaction.test.d.ts +2 -0
- package/dist/tests/commands/reaction.test.d.ts.map +1 -0
- package/dist/tests/commands/reaction.test.js +84 -0
- package/dist/tests/commands/reaction.test.js.map +1 -0
- package/dist/tests/commands/snapshot.test.d.ts +2 -0
- package/dist/tests/commands/snapshot.test.d.ts.map +1 -0
- package/dist/tests/commands/snapshot.test.js +280 -0
- package/dist/tests/commands/snapshot.test.js.map +1 -0
- package/dist/tests/commands/user.test.d.ts +2 -0
- package/dist/tests/commands/user.test.d.ts.map +1 -0
- package/dist/tests/commands/user.test.js +117 -0
- package/dist/tests/commands/user.test.js.map +1 -0
- package/dist/tests/commands/workspace.test.d.ts +2 -0
- package/dist/tests/commands/workspace.test.d.ts.map +1 -0
- package/dist/tests/commands/workspace.test.js +453 -0
- package/dist/tests/commands/workspace.test.js.map +1 -0
- package/dist/tests/credential-manager.test.d.ts +2 -0
- package/dist/tests/credential-manager.test.d.ts.map +1 -0
- package/dist/tests/credential-manager.test.js +199 -0
- package/dist/tests/credential-manager.test.js.map +1 -0
- package/dist/tests/slack-client.test.d.ts +2 -0
- package/dist/tests/slack-client.test.d.ts.map +1 -0
- package/dist/tests/slack-client.test.js +741 -0
- package/dist/tests/slack-client.test.js.map +1 -0
- package/dist/tests/types.test.d.ts +2 -0
- package/dist/tests/types.test.d.ts.map +1 -0
- package/dist/tests/types.test.js +215 -0
- package/dist/tests/types.test.js.map +1 -0
- package/dist/types/index.d.ts +369 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +92 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/error-handler.d.ts +2 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +5 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/output.d.ts +2 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +4 -0
- package/dist/utils/output.js.map +1 -0
- package/docs/discord.md +182 -0
- package/docs/slack.md +160 -0
- package/package.json +37 -0
- package/skills/agent-discord/SKILL.md +273 -0
- package/skills/agent-discord/references/authentication.md +294 -0
- package/skills/agent-discord/references/common-patterns.md +455 -0
- package/skills/agent-discord/templates/guild-summary.sh +167 -0
- package/skills/agent-discord/templates/monitor-channel.sh +180 -0
- package/skills/agent-discord/templates/post-message.sh +173 -0
- package/skills/agent-slack/SKILL.md +268 -0
- package/skills/agent-slack/references/authentication.md +332 -0
- package/skills/agent-slack/references/common-patterns.md +527 -0
- package/skills/agent-slack/templates/monitor-channel.sh +186 -0
- package/skills/agent-slack/templates/post-message.sh +130 -0
- package/skills/agent-slack/templates/workspace-summary.sh +149 -0
- package/src/cli.ts +29 -0
- package/src/platforms/discord/cli.ts +36 -0
- package/src/platforms/discord/client.test.ts +456 -0
- package/src/platforms/discord/client.ts +281 -0
- package/src/platforms/discord/commands/auth.test.ts +72 -0
- package/src/platforms/discord/commands/auth.ts +206 -0
- package/src/platforms/discord/commands/channel.test.ts +153 -0
- package/src/platforms/discord/commands/channel.ts +127 -0
- package/src/platforms/discord/commands/file.test.ts +98 -0
- package/src/platforms/discord/commands/file.ts +134 -0
- package/src/platforms/discord/commands/guild.test.ts +117 -0
- package/src/platforms/discord/commands/guild.ts +129 -0
- package/src/platforms/discord/commands/index.ts +8 -0
- package/src/platforms/discord/commands/message.test.ts +107 -0
- package/src/platforms/discord/commands/message.ts +182 -0
- package/src/platforms/discord/commands/reaction.test.ts +123 -0
- package/src/platforms/discord/commands/reaction.ts +156 -0
- package/src/platforms/discord/commands/snapshot.test.ts +29 -0
- package/src/platforms/discord/commands/snapshot.ts +104 -0
- package/src/platforms/discord/commands/user.test.ts +115 -0
- package/src/platforms/discord/commands/user.ts +124 -0
- package/src/platforms/discord/credential-manager.test.ts +173 -0
- package/src/platforms/discord/credential-manager.ts +95 -0
- package/src/platforms/discord/token-extractor.test.ts +918 -0
- package/src/platforms/discord/token-extractor.ts +549 -0
- package/src/platforms/discord/types.test.ts +245 -0
- package/src/platforms/discord/types.ts +158 -0
- package/src/platforms/slack/cli.ts +36 -0
- package/src/platforms/slack/client.ts +466 -0
- package/src/platforms/slack/commands/auth.ts +211 -0
- package/src/platforms/slack/commands/channel.ts +158 -0
- package/src/platforms/slack/commands/file.ts +153 -0
- package/src/platforms/slack/commands/index.ts +8 -0
- package/src/platforms/slack/commands/message.ts +369 -0
- package/src/platforms/slack/commands/reaction.ts +166 -0
- package/src/platforms/slack/commands/snapshot.ts +114 -0
- package/src/platforms/slack/commands/user.ts +110 -0
- package/src/platforms/slack/commands/workspace.ts +111 -0
- package/src/platforms/slack/credential-manager.ts +74 -0
- package/src/platforms/slack/index.ts +2 -0
- package/src/platforms/slack/token-extractor.ts +496 -0
- package/src/platforms/slack/types.ts +193 -0
- package/src/shared/utils/concurrency.test.ts +53 -0
- package/src/shared/utils/concurrency.ts +20 -0
- package/src/shared/utils/error-handler.ts +4 -0
- package/src/shared/utils/output.ts +3 -0
- package/tests/cli.test.ts +94 -0
- package/tests/commands/auth.test.ts +383 -0
- package/tests/commands/channel.test.ts +185 -0
- package/tests/commands/file.test.ts +204 -0
- package/tests/commands/message.test.ts +344 -0
- package/tests/commands/reaction.test.ts +101 -0
- package/tests/commands/snapshot.test.ts +308 -0
- package/tests/commands/user.test.ts +138 -0
- package/tests/commands/workspace.test.ts +528 -0
- package/tests/credential-manager.test.ts +241 -0
- package/tests/slack-client.test.ts +916 -0
- package/tests/types.test.ts +241 -0
- package/tsconfig.json +36 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
2
|
+
import { SlackClient, SlackError } from '../src/platforms/slack/client';
|
|
3
|
+
const mockWebClient = {
|
|
4
|
+
conversations: {
|
|
5
|
+
list: mock(() => Promise.resolve({ ok: true, channels: [] })),
|
|
6
|
+
info: mock(() => Promise.resolve({ ok: true, channel: {} })),
|
|
7
|
+
history: mock(() => Promise.resolve({ ok: true, messages: [] })),
|
|
8
|
+
replies: mock(() => Promise.resolve({ ok: true, messages: [], has_more: false })),
|
|
9
|
+
},
|
|
10
|
+
chat: {
|
|
11
|
+
postMessage: mock(() => Promise.resolve({ ok: true, ts: '123.456', message: {} })),
|
|
12
|
+
update: mock(() => Promise.resolve({ ok: true, ts: '123.456', message: {} })),
|
|
13
|
+
delete: mock(() => Promise.resolve({ ok: true })),
|
|
14
|
+
},
|
|
15
|
+
reactions: {
|
|
16
|
+
add: mock(() => Promise.resolve({ ok: true })),
|
|
17
|
+
remove: mock(() => Promise.resolve({ ok: true })),
|
|
18
|
+
},
|
|
19
|
+
users: {
|
|
20
|
+
list: mock(() => Promise.resolve({ ok: true, members: [] })),
|
|
21
|
+
info: mock(() => Promise.resolve({ ok: true, user: {} })),
|
|
22
|
+
},
|
|
23
|
+
files: {
|
|
24
|
+
uploadV2: mock(() => Promise.resolve({ ok: true, file: {} })),
|
|
25
|
+
list: mock(() => Promise.resolve({ ok: true, files: [] })),
|
|
26
|
+
},
|
|
27
|
+
auth: {
|
|
28
|
+
test: mock(() => Promise.resolve({ ok: true, user_id: 'U123', team_id: 'T123' })),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
function resetMocks() {
|
|
32
|
+
for (const group of Object.values(mockWebClient)) {
|
|
33
|
+
for (const fn of Object.values(group)) {
|
|
34
|
+
if (typeof fn.mockReset === 'function') {
|
|
35
|
+
fn.mockReset();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
describe('SlackClient', () => {
|
|
41
|
+
describe('constructor', () => {
|
|
42
|
+
test('throws SlackError when token is empty', () => {
|
|
43
|
+
expect(() => new SlackClient('', 'xoxd-cookie')).toThrow(SlackError);
|
|
44
|
+
expect(() => new SlackClient('', 'xoxd-cookie')).toThrow('Token is required');
|
|
45
|
+
});
|
|
46
|
+
test('throws SlackError when cookie is empty', () => {
|
|
47
|
+
expect(() => new SlackClient('xoxc-token', '')).toThrow(SlackError);
|
|
48
|
+
expect(() => new SlackClient('xoxc-token', '')).toThrow('Cookie is required');
|
|
49
|
+
});
|
|
50
|
+
test('throws SlackError when both token and cookie are empty', () => {
|
|
51
|
+
expect(() => new SlackClient('', '')).toThrow(SlackError);
|
|
52
|
+
});
|
|
53
|
+
test('creates client successfully with valid token and cookie', () => {
|
|
54
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
55
|
+
expect(client).toBeInstanceOf(SlackClient);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('testAuth', () => {
|
|
59
|
+
beforeEach(() => resetMocks());
|
|
60
|
+
test('returns auth info on success', async () => {
|
|
61
|
+
mockWebClient.auth.test.mockResolvedValue({
|
|
62
|
+
ok: true,
|
|
63
|
+
user_id: 'U123',
|
|
64
|
+
team_id: 'T123',
|
|
65
|
+
user: 'testuser',
|
|
66
|
+
team: 'Test Team',
|
|
67
|
+
});
|
|
68
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
69
|
+
// @ts-expect-error - accessing private property for testing
|
|
70
|
+
client.client = mockWebClient;
|
|
71
|
+
const result = await client.testAuth();
|
|
72
|
+
expect(result.user_id).toBe('U123');
|
|
73
|
+
expect(result.team_id).toBe('T123');
|
|
74
|
+
});
|
|
75
|
+
test('throws SlackError on API failure', async () => {
|
|
76
|
+
mockWebClient.auth.test.mockResolvedValue({
|
|
77
|
+
ok: false,
|
|
78
|
+
error: 'invalid_auth',
|
|
79
|
+
});
|
|
80
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
81
|
+
// @ts-expect-error - accessing private property for testing
|
|
82
|
+
client.client = mockWebClient;
|
|
83
|
+
await expect(client.testAuth()).rejects.toThrow(SlackError);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('listChannels', () => {
|
|
87
|
+
beforeEach(() => resetMocks());
|
|
88
|
+
test('returns list of channels', async () => {
|
|
89
|
+
mockWebClient.conversations.list.mockResolvedValue({
|
|
90
|
+
ok: true,
|
|
91
|
+
channels: [
|
|
92
|
+
{
|
|
93
|
+
id: 'C123',
|
|
94
|
+
name: 'general',
|
|
95
|
+
is_private: false,
|
|
96
|
+
is_archived: false,
|
|
97
|
+
created: 1234567890,
|
|
98
|
+
creator: 'U123',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'C456',
|
|
102
|
+
name: 'random',
|
|
103
|
+
is_private: false,
|
|
104
|
+
is_archived: false,
|
|
105
|
+
created: 1234567891,
|
|
106
|
+
creator: 'U123',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
111
|
+
// @ts-expect-error - accessing private property for testing
|
|
112
|
+
client.client = mockWebClient;
|
|
113
|
+
const channels = await client.listChannels();
|
|
114
|
+
expect(channels).toHaveLength(2);
|
|
115
|
+
expect(channels[0].id).toBe('C123');
|
|
116
|
+
expect(channels[1].name).toBe('random');
|
|
117
|
+
});
|
|
118
|
+
test('handles pagination automatically', async () => {
|
|
119
|
+
mockWebClient.conversations.list
|
|
120
|
+
.mockResolvedValueOnce({
|
|
121
|
+
ok: true,
|
|
122
|
+
channels: [
|
|
123
|
+
{
|
|
124
|
+
id: 'C123',
|
|
125
|
+
name: 'general',
|
|
126
|
+
is_private: false,
|
|
127
|
+
is_archived: false,
|
|
128
|
+
created: 1234567890,
|
|
129
|
+
creator: 'U123',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
response_metadata: { next_cursor: 'cursor123' },
|
|
133
|
+
})
|
|
134
|
+
.mockResolvedValueOnce({
|
|
135
|
+
ok: true,
|
|
136
|
+
channels: [
|
|
137
|
+
{
|
|
138
|
+
id: 'C456',
|
|
139
|
+
name: 'random',
|
|
140
|
+
is_private: false,
|
|
141
|
+
is_archived: false,
|
|
142
|
+
created: 1234567891,
|
|
143
|
+
creator: 'U123',
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
148
|
+
// @ts-expect-error - accessing private property for testing
|
|
149
|
+
client.client = mockWebClient;
|
|
150
|
+
const channels = await client.listChannels();
|
|
151
|
+
expect(channels).toHaveLength(2);
|
|
152
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(2);
|
|
153
|
+
});
|
|
154
|
+
test('throws SlackError on API failure', async () => {
|
|
155
|
+
mockWebClient.conversations.list.mockResolvedValue({
|
|
156
|
+
ok: false,
|
|
157
|
+
error: 'channel_not_found',
|
|
158
|
+
});
|
|
159
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
160
|
+
// @ts-expect-error - accessing private property for testing
|
|
161
|
+
client.client = mockWebClient;
|
|
162
|
+
await expect(client.listChannels()).rejects.toThrow(SlackError);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('getChannel', () => {
|
|
166
|
+
beforeEach(() => resetMocks());
|
|
167
|
+
test('returns channel info', async () => {
|
|
168
|
+
mockWebClient.conversations.info.mockResolvedValue({
|
|
169
|
+
ok: true,
|
|
170
|
+
channel: {
|
|
171
|
+
id: 'C123',
|
|
172
|
+
name: 'general',
|
|
173
|
+
is_private: false,
|
|
174
|
+
is_archived: false,
|
|
175
|
+
created: 1234567890,
|
|
176
|
+
creator: 'U123',
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
180
|
+
// @ts-expect-error - accessing private property for testing
|
|
181
|
+
client.client = mockWebClient;
|
|
182
|
+
const channel = await client.getChannel('C123');
|
|
183
|
+
expect(channel.id).toBe('C123');
|
|
184
|
+
expect(channel.name).toBe('general');
|
|
185
|
+
});
|
|
186
|
+
test('throws SlackError when channel not found', async () => {
|
|
187
|
+
mockWebClient.conversations.info.mockResolvedValue({
|
|
188
|
+
ok: false,
|
|
189
|
+
error: 'channel_not_found',
|
|
190
|
+
});
|
|
191
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
192
|
+
// @ts-expect-error - accessing private property for testing
|
|
193
|
+
client.client = mockWebClient;
|
|
194
|
+
await expect(client.getChannel('C999')).rejects.toThrow(SlackError);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('getMessages', () => {
|
|
198
|
+
beforeEach(() => resetMocks());
|
|
199
|
+
test('returns messages with default limit of 20', async () => {
|
|
200
|
+
const messages = Array.from({ length: 20 }, (_, i) => ({
|
|
201
|
+
ts: `123.${i}`,
|
|
202
|
+
text: `Message ${i}`,
|
|
203
|
+
type: 'message',
|
|
204
|
+
}));
|
|
205
|
+
mockWebClient.conversations.history.mockResolvedValue({
|
|
206
|
+
ok: true,
|
|
207
|
+
messages,
|
|
208
|
+
});
|
|
209
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
210
|
+
// @ts-expect-error - accessing private property for testing
|
|
211
|
+
client.client = mockWebClient;
|
|
212
|
+
const result = await client.getMessages('C123');
|
|
213
|
+
expect(result).toHaveLength(20);
|
|
214
|
+
expect(mockWebClient.conversations.history).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', limit: 20 }));
|
|
215
|
+
});
|
|
216
|
+
test('respects custom limit', async () => {
|
|
217
|
+
mockWebClient.conversations.history.mockResolvedValue({
|
|
218
|
+
ok: true,
|
|
219
|
+
messages: [{ ts: '123.456', text: 'Hello', type: 'message' }],
|
|
220
|
+
});
|
|
221
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
222
|
+
// @ts-expect-error - accessing private property for testing
|
|
223
|
+
client.client = mockWebClient;
|
|
224
|
+
await client.getMessages('C123', 50);
|
|
225
|
+
expect(mockWebClient.conversations.history).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', limit: 50 }));
|
|
226
|
+
});
|
|
227
|
+
test('throws SlackError on API failure', async () => {
|
|
228
|
+
mockWebClient.conversations.history.mockResolvedValue({
|
|
229
|
+
ok: false,
|
|
230
|
+
error: 'channel_not_found',
|
|
231
|
+
});
|
|
232
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
233
|
+
// @ts-expect-error - accessing private property for testing
|
|
234
|
+
client.client = mockWebClient;
|
|
235
|
+
await expect(client.getMessages('C999')).rejects.toThrow(SlackError);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
describe('sendMessage', () => {
|
|
239
|
+
beforeEach(() => resetMocks());
|
|
240
|
+
test('sends message to channel', async () => {
|
|
241
|
+
mockWebClient.chat.postMessage.mockResolvedValue({
|
|
242
|
+
ok: true,
|
|
243
|
+
ts: '123.456',
|
|
244
|
+
message: { ts: '123.456', text: 'Hello', type: 'message' },
|
|
245
|
+
});
|
|
246
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
247
|
+
// @ts-expect-error - accessing private property for testing
|
|
248
|
+
client.client = mockWebClient;
|
|
249
|
+
const message = await client.sendMessage('C123', 'Hello');
|
|
250
|
+
expect(message.ts).toBe('123.456');
|
|
251
|
+
expect(mockWebClient.chat.postMessage).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', text: 'Hello' }));
|
|
252
|
+
});
|
|
253
|
+
test('sends message to thread', async () => {
|
|
254
|
+
mockWebClient.chat.postMessage.mockResolvedValue({
|
|
255
|
+
ok: true,
|
|
256
|
+
ts: '123.789',
|
|
257
|
+
message: { ts: '123.789', text: 'Reply', type: 'message', thread_ts: '123.456' },
|
|
258
|
+
});
|
|
259
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
260
|
+
// @ts-expect-error - accessing private property for testing
|
|
261
|
+
client.client = mockWebClient;
|
|
262
|
+
const message = await client.sendMessage('C123', 'Reply', '123.456');
|
|
263
|
+
expect(message.thread_ts).toBe('123.456');
|
|
264
|
+
expect(mockWebClient.chat.postMessage).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', text: 'Reply', thread_ts: '123.456' }));
|
|
265
|
+
});
|
|
266
|
+
test('throws SlackError on API failure', async () => {
|
|
267
|
+
mockWebClient.chat.postMessage.mockResolvedValue({
|
|
268
|
+
ok: false,
|
|
269
|
+
error: 'channel_not_found',
|
|
270
|
+
});
|
|
271
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
272
|
+
// @ts-expect-error - accessing private property for testing
|
|
273
|
+
client.client = mockWebClient;
|
|
274
|
+
await expect(client.sendMessage('C999', 'Hello')).rejects.toThrow(SlackError);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
describe('updateMessage', () => {
|
|
278
|
+
beforeEach(() => resetMocks());
|
|
279
|
+
test('updates message text', async () => {
|
|
280
|
+
mockWebClient.chat.update.mockResolvedValue({
|
|
281
|
+
ok: true,
|
|
282
|
+
ts: '123.456',
|
|
283
|
+
message: { ts: '123.456', text: 'Updated', type: 'message' },
|
|
284
|
+
});
|
|
285
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
286
|
+
// @ts-expect-error - accessing private property for testing
|
|
287
|
+
client.client = mockWebClient;
|
|
288
|
+
const message = await client.updateMessage('C123', '123.456', 'Updated');
|
|
289
|
+
expect(message.text).toBe('Updated');
|
|
290
|
+
expect(mockWebClient.chat.update).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', ts: '123.456', text: 'Updated' }));
|
|
291
|
+
});
|
|
292
|
+
test('throws SlackError on API failure', async () => {
|
|
293
|
+
mockWebClient.chat.update.mockResolvedValue({
|
|
294
|
+
ok: false,
|
|
295
|
+
error: 'message_not_found',
|
|
296
|
+
});
|
|
297
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
298
|
+
// @ts-expect-error - accessing private property for testing
|
|
299
|
+
client.client = mockWebClient;
|
|
300
|
+
await expect(client.updateMessage('C123', '999.999', 'Updated')).rejects.toThrow(SlackError);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
describe('deleteMessage', () => {
|
|
304
|
+
beforeEach(() => resetMocks());
|
|
305
|
+
test('deletes message', async () => {
|
|
306
|
+
mockWebClient.chat.delete.mockResolvedValue({ ok: true });
|
|
307
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
308
|
+
// @ts-expect-error - accessing private property for testing
|
|
309
|
+
client.client = mockWebClient;
|
|
310
|
+
await expect(client.deleteMessage('C123', '123.456')).resolves.toBeUndefined();
|
|
311
|
+
expect(mockWebClient.chat.delete).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', ts: '123.456' }));
|
|
312
|
+
});
|
|
313
|
+
test('throws SlackError on API failure', async () => {
|
|
314
|
+
mockWebClient.chat.delete.mockResolvedValue({
|
|
315
|
+
ok: false,
|
|
316
|
+
error: 'message_not_found',
|
|
317
|
+
});
|
|
318
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
319
|
+
// @ts-expect-error - accessing private property for testing
|
|
320
|
+
client.client = mockWebClient;
|
|
321
|
+
await expect(client.deleteMessage('C123', '999.999')).rejects.toThrow(SlackError);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
describe('addReaction', () => {
|
|
325
|
+
beforeEach(() => resetMocks());
|
|
326
|
+
test('adds reaction to message', async () => {
|
|
327
|
+
mockWebClient.reactions.add.mockResolvedValue({ ok: true });
|
|
328
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
329
|
+
// @ts-expect-error - accessing private property for testing
|
|
330
|
+
client.client = mockWebClient;
|
|
331
|
+
await expect(client.addReaction('C123', '123.456', 'thumbsup')).resolves.toBeUndefined();
|
|
332
|
+
expect(mockWebClient.reactions.add).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', timestamp: '123.456', name: 'thumbsup' }));
|
|
333
|
+
});
|
|
334
|
+
test('throws SlackError on API failure', async () => {
|
|
335
|
+
mockWebClient.reactions.add.mockResolvedValue({
|
|
336
|
+
ok: false,
|
|
337
|
+
error: 'already_reacted',
|
|
338
|
+
});
|
|
339
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
340
|
+
// @ts-expect-error - accessing private property for testing
|
|
341
|
+
client.client = mockWebClient;
|
|
342
|
+
await expect(client.addReaction('C123', '123.456', 'thumbsup')).rejects.toThrow(SlackError);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
describe('removeReaction', () => {
|
|
346
|
+
beforeEach(() => resetMocks());
|
|
347
|
+
test('removes reaction from message', async () => {
|
|
348
|
+
mockWebClient.reactions.remove.mockResolvedValue({ ok: true });
|
|
349
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
350
|
+
// @ts-expect-error - accessing private property for testing
|
|
351
|
+
client.client = mockWebClient;
|
|
352
|
+
await expect(client.removeReaction('C123', '123.456', 'thumbsup')).resolves.toBeUndefined();
|
|
353
|
+
expect(mockWebClient.reactions.remove).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', timestamp: '123.456', name: 'thumbsup' }));
|
|
354
|
+
});
|
|
355
|
+
test('throws SlackError on API failure', async () => {
|
|
356
|
+
mockWebClient.reactions.remove.mockResolvedValue({
|
|
357
|
+
ok: false,
|
|
358
|
+
error: 'no_reaction',
|
|
359
|
+
});
|
|
360
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
361
|
+
// @ts-expect-error - accessing private property for testing
|
|
362
|
+
client.client = mockWebClient;
|
|
363
|
+
await expect(client.removeReaction('C123', '123.456', 'thumbsup')).rejects.toThrow(SlackError);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
describe('listUsers', () => {
|
|
367
|
+
beforeEach(() => resetMocks());
|
|
368
|
+
test('returns list of users', async () => {
|
|
369
|
+
mockWebClient.users.list.mockResolvedValue({
|
|
370
|
+
ok: true,
|
|
371
|
+
members: [
|
|
372
|
+
{
|
|
373
|
+
id: 'U123',
|
|
374
|
+
name: 'alice',
|
|
375
|
+
real_name: 'Alice',
|
|
376
|
+
is_admin: false,
|
|
377
|
+
is_owner: false,
|
|
378
|
+
is_bot: false,
|
|
379
|
+
is_app_user: false,
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
id: 'U456',
|
|
383
|
+
name: 'bob',
|
|
384
|
+
real_name: 'Bob',
|
|
385
|
+
is_admin: true,
|
|
386
|
+
is_owner: false,
|
|
387
|
+
is_bot: false,
|
|
388
|
+
is_app_user: false,
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
});
|
|
392
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
393
|
+
// @ts-expect-error - accessing private property for testing
|
|
394
|
+
client.client = mockWebClient;
|
|
395
|
+
const users = await client.listUsers();
|
|
396
|
+
expect(users).toHaveLength(2);
|
|
397
|
+
expect(users[0].name).toBe('alice');
|
|
398
|
+
expect(users[1].is_admin).toBe(true);
|
|
399
|
+
});
|
|
400
|
+
test('handles pagination automatically', async () => {
|
|
401
|
+
mockWebClient.users.list
|
|
402
|
+
.mockResolvedValueOnce({
|
|
403
|
+
ok: true,
|
|
404
|
+
members: [
|
|
405
|
+
{
|
|
406
|
+
id: 'U123',
|
|
407
|
+
name: 'alice',
|
|
408
|
+
real_name: 'Alice',
|
|
409
|
+
is_admin: false,
|
|
410
|
+
is_owner: false,
|
|
411
|
+
is_bot: false,
|
|
412
|
+
is_app_user: false,
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
response_metadata: { next_cursor: 'cursor123' },
|
|
416
|
+
})
|
|
417
|
+
.mockResolvedValueOnce({
|
|
418
|
+
ok: true,
|
|
419
|
+
members: [
|
|
420
|
+
{
|
|
421
|
+
id: 'U456',
|
|
422
|
+
name: 'bob',
|
|
423
|
+
real_name: 'Bob',
|
|
424
|
+
is_admin: false,
|
|
425
|
+
is_owner: false,
|
|
426
|
+
is_bot: false,
|
|
427
|
+
is_app_user: false,
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
});
|
|
431
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
432
|
+
// @ts-expect-error - accessing private property for testing
|
|
433
|
+
client.client = mockWebClient;
|
|
434
|
+
const users = await client.listUsers();
|
|
435
|
+
expect(users).toHaveLength(2);
|
|
436
|
+
expect(mockWebClient.users.list).toHaveBeenCalledTimes(2);
|
|
437
|
+
});
|
|
438
|
+
test('throws SlackError on API failure', async () => {
|
|
439
|
+
mockWebClient.users.list.mockResolvedValue({
|
|
440
|
+
ok: false,
|
|
441
|
+
error: 'invalid_auth',
|
|
442
|
+
});
|
|
443
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
444
|
+
// @ts-expect-error - accessing private property for testing
|
|
445
|
+
client.client = mockWebClient;
|
|
446
|
+
await expect(client.listUsers()).rejects.toThrow(SlackError);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
describe('getUser', () => {
|
|
450
|
+
beforeEach(() => resetMocks());
|
|
451
|
+
test('returns user info', async () => {
|
|
452
|
+
mockWebClient.users.info.mockResolvedValue({
|
|
453
|
+
ok: true,
|
|
454
|
+
user: {
|
|
455
|
+
id: 'U123',
|
|
456
|
+
name: 'alice',
|
|
457
|
+
real_name: 'Alice',
|
|
458
|
+
is_admin: false,
|
|
459
|
+
is_owner: false,
|
|
460
|
+
is_bot: false,
|
|
461
|
+
is_app_user: false,
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
465
|
+
// @ts-expect-error - accessing private property for testing
|
|
466
|
+
client.client = mockWebClient;
|
|
467
|
+
const user = await client.getUser('U123');
|
|
468
|
+
expect(user.id).toBe('U123');
|
|
469
|
+
expect(user.name).toBe('alice');
|
|
470
|
+
});
|
|
471
|
+
test('throws SlackError when user not found', async () => {
|
|
472
|
+
mockWebClient.users.info.mockResolvedValue({
|
|
473
|
+
ok: false,
|
|
474
|
+
error: 'user_not_found',
|
|
475
|
+
});
|
|
476
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
477
|
+
// @ts-expect-error - accessing private property for testing
|
|
478
|
+
client.client = mockWebClient;
|
|
479
|
+
await expect(client.getUser('U999')).rejects.toThrow(SlackError);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
describe('uploadFile', () => {
|
|
483
|
+
beforeEach(() => resetMocks());
|
|
484
|
+
test('uploads file to channels', async () => {
|
|
485
|
+
mockWebClient.files.uploadV2.mockResolvedValue({
|
|
486
|
+
ok: true,
|
|
487
|
+
file: {
|
|
488
|
+
id: 'F123',
|
|
489
|
+
name: 'test.txt',
|
|
490
|
+
title: 'test.txt',
|
|
491
|
+
mimetype: 'text/plain',
|
|
492
|
+
size: 100,
|
|
493
|
+
url_private: 'https://...',
|
|
494
|
+
created: 1234567890,
|
|
495
|
+
user: 'U123',
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
499
|
+
// @ts-expect-error - accessing private property for testing
|
|
500
|
+
client.client = mockWebClient;
|
|
501
|
+
const file = await client.uploadFile(['C123'], Buffer.from('test'), 'test.txt');
|
|
502
|
+
expect(file.id).toBe('F123');
|
|
503
|
+
expect(mockWebClient.files.uploadV2).toHaveBeenCalledWith(expect.objectContaining({ channel_id: 'C123', filename: 'test.txt' }));
|
|
504
|
+
});
|
|
505
|
+
test('throws SlackError on API failure', async () => {
|
|
506
|
+
mockWebClient.files.uploadV2.mockResolvedValue({
|
|
507
|
+
ok: false,
|
|
508
|
+
error: 'file_upload_failed',
|
|
509
|
+
});
|
|
510
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
511
|
+
// @ts-expect-error - accessing private property for testing
|
|
512
|
+
client.client = mockWebClient;
|
|
513
|
+
await expect(client.uploadFile(['C123'], Buffer.from('test'), 'test.txt')).rejects.toThrow(SlackError);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
describe('listFiles', () => {
|
|
517
|
+
beforeEach(() => resetMocks());
|
|
518
|
+
test('returns list of files', async () => {
|
|
519
|
+
mockWebClient.files.list.mockResolvedValue({
|
|
520
|
+
ok: true,
|
|
521
|
+
files: [
|
|
522
|
+
{
|
|
523
|
+
id: 'F123',
|
|
524
|
+
name: 'test.txt',
|
|
525
|
+
title: 'test.txt',
|
|
526
|
+
mimetype: 'text/plain',
|
|
527
|
+
size: 100,
|
|
528
|
+
url_private: 'https://...',
|
|
529
|
+
created: 1234567890,
|
|
530
|
+
user: 'U123',
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
});
|
|
534
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
535
|
+
// @ts-expect-error - accessing private property for testing
|
|
536
|
+
client.client = mockWebClient;
|
|
537
|
+
const files = await client.listFiles();
|
|
538
|
+
expect(files).toHaveLength(1);
|
|
539
|
+
expect(files[0].name).toBe('test.txt');
|
|
540
|
+
});
|
|
541
|
+
test('filters by channel when provided', async () => {
|
|
542
|
+
mockWebClient.files.list.mockResolvedValue({
|
|
543
|
+
ok: true,
|
|
544
|
+
files: [],
|
|
545
|
+
});
|
|
546
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
547
|
+
// @ts-expect-error - accessing private property for testing
|
|
548
|
+
client.client = mockWebClient;
|
|
549
|
+
await client.listFiles('C123');
|
|
550
|
+
expect(mockWebClient.files.list).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123' }));
|
|
551
|
+
});
|
|
552
|
+
test('throws SlackError on API failure', async () => {
|
|
553
|
+
mockWebClient.files.list.mockResolvedValue({
|
|
554
|
+
ok: false,
|
|
555
|
+
error: 'invalid_auth',
|
|
556
|
+
});
|
|
557
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
558
|
+
// @ts-expect-error - accessing private property for testing
|
|
559
|
+
client.client = mockWebClient;
|
|
560
|
+
await expect(client.listFiles()).rejects.toThrow(SlackError);
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
describe('getThreadReplies', () => {
|
|
564
|
+
beforeEach(() => resetMocks());
|
|
565
|
+
test('returns thread replies including parent message', async () => {
|
|
566
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
567
|
+
ok: true,
|
|
568
|
+
messages: [
|
|
569
|
+
{
|
|
570
|
+
ts: '123.456',
|
|
571
|
+
text: 'Parent message',
|
|
572
|
+
type: 'message',
|
|
573
|
+
user: 'U123',
|
|
574
|
+
thread_ts: '123.456',
|
|
575
|
+
reply_count: 2,
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
ts: '123.457',
|
|
579
|
+
text: 'First reply',
|
|
580
|
+
type: 'message',
|
|
581
|
+
user: 'U456',
|
|
582
|
+
thread_ts: '123.456',
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
ts: '123.458',
|
|
586
|
+
text: 'Second reply',
|
|
587
|
+
type: 'message',
|
|
588
|
+
user: 'U789',
|
|
589
|
+
thread_ts: '123.456',
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
has_more: false,
|
|
593
|
+
});
|
|
594
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
595
|
+
// @ts-expect-error - accessing private property for testing
|
|
596
|
+
client.client = mockWebClient;
|
|
597
|
+
const result = await client.getThreadReplies('C123', '123.456');
|
|
598
|
+
expect(result.messages).toHaveLength(3);
|
|
599
|
+
expect(result.messages[0].text).toBe('Parent message');
|
|
600
|
+
expect(result.messages[0].reply_count).toBe(2);
|
|
601
|
+
expect(result.messages[1].text).toBe('First reply');
|
|
602
|
+
expect(result.messages[2].text).toBe('Second reply');
|
|
603
|
+
expect(result.has_more).toBe(false);
|
|
604
|
+
});
|
|
605
|
+
test('respects limit parameter', async () => {
|
|
606
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
607
|
+
ok: true,
|
|
608
|
+
messages: [{ ts: '123.456', text: 'Hello', type: 'message' }],
|
|
609
|
+
has_more: false,
|
|
610
|
+
});
|
|
611
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
612
|
+
// @ts-expect-error - accessing private property for testing
|
|
613
|
+
client.client = mockWebClient;
|
|
614
|
+
await client.getThreadReplies('C123', '123.456', { limit: 50 });
|
|
615
|
+
expect(mockWebClient.conversations.replies).toHaveBeenCalledWith(expect.objectContaining({ channel: 'C123', ts: '123.456', limit: 50 }));
|
|
616
|
+
});
|
|
617
|
+
test('passes optional oldest and latest parameters', async () => {
|
|
618
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
619
|
+
ok: true,
|
|
620
|
+
messages: [],
|
|
621
|
+
has_more: false,
|
|
622
|
+
});
|
|
623
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
624
|
+
// @ts-expect-error - accessing private property for testing
|
|
625
|
+
client.client = mockWebClient;
|
|
626
|
+
await client.getThreadReplies('C123', '123.456', {
|
|
627
|
+
oldest: '123.400',
|
|
628
|
+
latest: '123.500',
|
|
629
|
+
});
|
|
630
|
+
expect(mockWebClient.conversations.replies).toHaveBeenCalledWith(expect.objectContaining({
|
|
631
|
+
channel: 'C123',
|
|
632
|
+
ts: '123.456',
|
|
633
|
+
oldest: '123.400',
|
|
634
|
+
latest: '123.500',
|
|
635
|
+
}));
|
|
636
|
+
});
|
|
637
|
+
test('returns pagination info when has_more is true', async () => {
|
|
638
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
639
|
+
ok: true,
|
|
640
|
+
messages: [{ ts: '123.456', text: 'Hello', type: 'message' }],
|
|
641
|
+
has_more: true,
|
|
642
|
+
response_metadata: { next_cursor: 'cursor123' },
|
|
643
|
+
});
|
|
644
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
645
|
+
// @ts-expect-error - accessing private property for testing
|
|
646
|
+
client.client = mockWebClient;
|
|
647
|
+
const result = await client.getThreadReplies('C123', '123.456');
|
|
648
|
+
expect(result.has_more).toBe(true);
|
|
649
|
+
expect(result.next_cursor).toBe('cursor123');
|
|
650
|
+
});
|
|
651
|
+
test('throws SlackError when thread not found', async () => {
|
|
652
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
653
|
+
ok: false,
|
|
654
|
+
error: 'thread_not_found',
|
|
655
|
+
});
|
|
656
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
657
|
+
// @ts-expect-error - accessing private property for testing
|
|
658
|
+
client.client = mockWebClient;
|
|
659
|
+
await expect(client.getThreadReplies('C123', '999.999')).rejects.toThrow(SlackError);
|
|
660
|
+
});
|
|
661
|
+
test('throws SlackError when channel not found', async () => {
|
|
662
|
+
mockWebClient.conversations.replies.mockResolvedValue({
|
|
663
|
+
ok: false,
|
|
664
|
+
error: 'channel_not_found',
|
|
665
|
+
});
|
|
666
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
667
|
+
// @ts-expect-error - accessing private property for testing
|
|
668
|
+
client.client = mockWebClient;
|
|
669
|
+
await expect(client.getThreadReplies('C999', '123.456')).rejects.toThrow(SlackError);
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
describe('rate limiting', () => {
|
|
673
|
+
beforeEach(() => resetMocks());
|
|
674
|
+
test('retries on rate limit error with exponential backoff', async () => {
|
|
675
|
+
const rateLimitError = new Error('Rate limited');
|
|
676
|
+
rateLimitError.code = 'slack_webapi_rate_limited_error';
|
|
677
|
+
rateLimitError.retryAfter = 0.001;
|
|
678
|
+
mockWebClient.conversations.list
|
|
679
|
+
.mockRejectedValueOnce(rateLimitError)
|
|
680
|
+
.mockRejectedValueOnce(rateLimitError)
|
|
681
|
+
.mockResolvedValueOnce({
|
|
682
|
+
ok: true,
|
|
683
|
+
channels: [
|
|
684
|
+
{
|
|
685
|
+
id: 'C123',
|
|
686
|
+
name: 'general',
|
|
687
|
+
is_private: false,
|
|
688
|
+
is_archived: false,
|
|
689
|
+
created: 1234567890,
|
|
690
|
+
creator: 'U123',
|
|
691
|
+
},
|
|
692
|
+
],
|
|
693
|
+
});
|
|
694
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
695
|
+
// @ts-expect-error - accessing private property for testing
|
|
696
|
+
client.client = mockWebClient;
|
|
697
|
+
const channels = await client.listChannels();
|
|
698
|
+
expect(channels).toHaveLength(1);
|
|
699
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(3);
|
|
700
|
+
});
|
|
701
|
+
test('throws SlackError after max retries (3)', async () => {
|
|
702
|
+
const rateLimitError = new Error('Rate limited');
|
|
703
|
+
rateLimitError.code = 'slack_webapi_rate_limited_error';
|
|
704
|
+
rateLimitError.retryAfter = 0.001;
|
|
705
|
+
mockWebClient.conversations.list.mockRejectedValue(rateLimitError);
|
|
706
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
707
|
+
// @ts-expect-error - accessing private property for testing
|
|
708
|
+
client.client = mockWebClient;
|
|
709
|
+
await expect(client.listChannels()).rejects.toThrow(SlackError);
|
|
710
|
+
// Initial call + 3 retries = 4 total calls
|
|
711
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(4);
|
|
712
|
+
});
|
|
713
|
+
test('does not retry on non-rate-limit errors', async () => {
|
|
714
|
+
const otherError = new Error('Some other error');
|
|
715
|
+
otherError.code = 'some_other_error';
|
|
716
|
+
mockWebClient.conversations.list.mockRejectedValue(otherError);
|
|
717
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie');
|
|
718
|
+
// @ts-expect-error - accessing private property for testing
|
|
719
|
+
client.client = mockWebClient;
|
|
720
|
+
await expect(client.listChannels()).rejects.toThrow(SlackError);
|
|
721
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(1);
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
describe('SlackError', () => {
|
|
725
|
+
test('is an instance of Error', () => {
|
|
726
|
+
const error = new SlackError('test error', 'test_code');
|
|
727
|
+
expect(error).toBeInstanceOf(Error);
|
|
728
|
+
expect(error).toBeInstanceOf(SlackError);
|
|
729
|
+
});
|
|
730
|
+
test('has message and code properties', () => {
|
|
731
|
+
const error = new SlackError('test error', 'test_code');
|
|
732
|
+
expect(error.message).toBe('test error');
|
|
733
|
+
expect(error.code).toBe('test_code');
|
|
734
|
+
});
|
|
735
|
+
test('has name property set to SlackError', () => {
|
|
736
|
+
const error = new SlackError('test error', 'test_code');
|
|
737
|
+
expect(error.name).toBe('SlackError');
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
//# sourceMappingURL=slack-client.test.js.map
|