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,918 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from 'bun:test'
|
|
2
|
+
import { execSync, spawn } from 'node:child_process'
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
|
|
7
|
+
import { CDP_PORT, DiscordTokenExtractor, TOKEN_EXTRACTION_JS } from './token-extractor'
|
|
8
|
+
|
|
9
|
+
// Mock modules
|
|
10
|
+
mock.module('node:fs', () => ({
|
|
11
|
+
existsSync: mock(() => false),
|
|
12
|
+
readdirSync: mock(() => []),
|
|
13
|
+
readFileSync: mock(() => Buffer.from('')),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
const mockSpawn = mock(() => ({
|
|
17
|
+
unref: mock(() => {}),
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
mock.module('node:child_process', () => ({
|
|
21
|
+
execSync: mock(() => ''),
|
|
22
|
+
spawn: mockSpawn,
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
describe('DiscordTokenExtractor', () => {
|
|
26
|
+
let extractor: DiscordTokenExtractor
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
extractor = new DiscordTokenExtractor()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('getDiscordDirs', () => {
|
|
33
|
+
test('returns darwin paths on macOS', () => {
|
|
34
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin')
|
|
35
|
+
const dirs = darwinExtractor.getDiscordDirs()
|
|
36
|
+
|
|
37
|
+
expect(dirs).toContain(join(homedir(), 'Library', 'Application Support', 'Discord'))
|
|
38
|
+
expect(dirs).toContain(join(homedir(), 'Library', 'Application Support', 'discordcanary'))
|
|
39
|
+
expect(dirs).toContain(join(homedir(), 'Library', 'Application Support', 'discordptb'))
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('returns linux paths on Linux', () => {
|
|
43
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
44
|
+
const dirs = linuxExtractor.getDiscordDirs()
|
|
45
|
+
|
|
46
|
+
expect(dirs).toContain(join(homedir(), '.config', 'discord'))
|
|
47
|
+
expect(dirs).toContain(join(homedir(), '.config', 'discordcanary'))
|
|
48
|
+
expect(dirs).toContain(join(homedir(), '.config', 'discordptb'))
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('returns win32 paths on Windows', () => {
|
|
52
|
+
const winExtractor = new DiscordTokenExtractor('win32')
|
|
53
|
+
const dirs = winExtractor.getDiscordDirs()
|
|
54
|
+
|
|
55
|
+
const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
|
|
56
|
+
expect(dirs).toContain(join(appdata, 'Discord'))
|
|
57
|
+
expect(dirs).toContain(join(appdata, 'discordcanary'))
|
|
58
|
+
expect(dirs).toContain(join(appdata, 'discordptb'))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('returns multiple paths for all 3 variants', () => {
|
|
62
|
+
const dirs = extractor.getDiscordDirs()
|
|
63
|
+
expect(dirs.length).toBe(3)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('token patterns', () => {
|
|
68
|
+
test('validates standard token format (base64.base64.base64)', () => {
|
|
69
|
+
// Token: base64(user_id).base64(timestamp).base64(hmac)
|
|
70
|
+
const validToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.abcdefghijklmnopqrstuvwxyz1234567890'
|
|
71
|
+
expect(extractor.isValidToken(validToken)).toBe(true)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('validates MFA token format', () => {
|
|
75
|
+
const mfaToken = `mfa.${'a'.repeat(84)}`
|
|
76
|
+
expect(extractor.isValidToken(mfaToken)).toBe(true)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('rejects invalid tokens', () => {
|
|
80
|
+
expect(extractor.isValidToken('')).toBe(false)
|
|
81
|
+
expect(extractor.isValidToken('invalid')).toBe(false)
|
|
82
|
+
expect(extractor.isValidToken('xoxc-123')).toBe(false)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('detects encrypted tokens by prefix', () => {
|
|
86
|
+
const encryptedToken = 'dQw4w9WgXcQ:' + 'encrypted_data'
|
|
87
|
+
expect(extractor.isEncryptedToken(encryptedToken)).toBe(true)
|
|
88
|
+
expect(extractor.isEncryptedToken('MTIzNDU2.xxx.yyy')).toBe(false)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('extract', () => {
|
|
93
|
+
test('returns null when no Discord directories exist on linux', async () => {
|
|
94
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
95
|
+
mockExistsSync.mockImplementation(() => false)
|
|
96
|
+
|
|
97
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
98
|
+
const result = await linuxExtractor.extract()
|
|
99
|
+
expect(result).toBeNull()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('extracts token from LevelDB files on linux', async () => {
|
|
103
|
+
const mockToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.abcdefghijklmnopqrstuvwxyz1234567890'
|
|
104
|
+
const ldbContent = Buffer.from(`some_data"${mockToken}"more_data`)
|
|
105
|
+
|
|
106
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
107
|
+
mockExistsSync.mockImplementation((path: string) => {
|
|
108
|
+
if (path.includes('discord') || path.includes('leveldb')) return true
|
|
109
|
+
if (path.includes('Local Storage')) return true
|
|
110
|
+
return false
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const mockReaddirSync = readdirSync as unknown as ReturnType<typeof mock>
|
|
114
|
+
mockReaddirSync.mockImplementation((path: string) => {
|
|
115
|
+
if (path.includes('leveldb')) return ['000001.ldb']
|
|
116
|
+
if (path.includes('Local Storage')) return ['leveldb']
|
|
117
|
+
return []
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const mockReadFileSync = readFileSync as unknown as ReturnType<typeof mock>
|
|
121
|
+
mockReadFileSync.mockImplementation(() => ldbContent)
|
|
122
|
+
|
|
123
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
124
|
+
const result = await linuxExtractor.extract()
|
|
125
|
+
|
|
126
|
+
expect(result).not.toBeNull()
|
|
127
|
+
expect(result?.token).toBe(mockToken)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('tries LevelDB first on macOS, CDP as fallback', async () => {
|
|
131
|
+
const levelDbToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.leveldb_token_123456789012345'
|
|
132
|
+
const ldbContent = Buffer.from(`some_data"${levelDbToken}"more_data`)
|
|
133
|
+
|
|
134
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
135
|
+
mockExistsSync.mockImplementation((path: string) => {
|
|
136
|
+
if (path.includes('Discord') || path.includes('leveldb')) return true
|
|
137
|
+
if (path.includes('Local Storage')) return true
|
|
138
|
+
return false
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const mockReaddirSync = readdirSync as unknown as ReturnType<typeof mock>
|
|
142
|
+
mockReaddirSync.mockImplementation((path: string) => {
|
|
143
|
+
if (path.includes('leveldb')) return ['000001.ldb']
|
|
144
|
+
if (path.includes('Local Storage')) return ['leveldb']
|
|
145
|
+
return []
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const mockReadFileSync = readFileSync as unknown as ReturnType<typeof mock>
|
|
149
|
+
mockReadFileSync.mockImplementation(() => ldbContent)
|
|
150
|
+
|
|
151
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0)
|
|
152
|
+
const result = await darwinExtractor.extract()
|
|
153
|
+
|
|
154
|
+
expect(result).not.toBeNull()
|
|
155
|
+
expect(result?.token).toBe(levelDbToken)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('falls back to leveldb on macOS when CDP fails', async () => {
|
|
159
|
+
const mockToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.leveldb_fallback_token_12345'
|
|
160
|
+
const ldbContent = Buffer.from(`some_data"${mockToken}"more_data`)
|
|
161
|
+
|
|
162
|
+
const originalFetch = globalThis.fetch
|
|
163
|
+
globalThis.fetch = mock(async () => ({
|
|
164
|
+
ok: true,
|
|
165
|
+
json: async () => [],
|
|
166
|
+
})) as unknown as typeof fetch
|
|
167
|
+
|
|
168
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
169
|
+
mockExistsSync.mockImplementation((path: string) => {
|
|
170
|
+
if (path.includes('/Applications/')) return false
|
|
171
|
+
if (path.includes('Discord') || path.includes('leveldb')) return true
|
|
172
|
+
if (path.includes('Local Storage')) return true
|
|
173
|
+
return false
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const mockReaddirSync = readdirSync as unknown as ReturnType<typeof mock>
|
|
177
|
+
mockReaddirSync.mockImplementation((path: string) => {
|
|
178
|
+
if (path.includes('leveldb')) return ['000001.ldb']
|
|
179
|
+
if (path.includes('Local Storage')) return ['leveldb']
|
|
180
|
+
return []
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const mockReadFileSync = readFileSync as unknown as ReturnType<typeof mock>
|
|
184
|
+
mockReadFileSync.mockImplementation(() => ldbContent)
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0)
|
|
188
|
+
const result = await darwinExtractor.extract()
|
|
189
|
+
|
|
190
|
+
expect(result).not.toBeNull()
|
|
191
|
+
expect(result?.token).toBe(mockToken)
|
|
192
|
+
} finally {
|
|
193
|
+
globalThis.fetch = originalFetch
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('returns first valid token found across variants on linux', async () => {
|
|
198
|
+
const mockToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.first_token_found_1234567890'
|
|
199
|
+
const ldbContent = Buffer.from(`"${mockToken}"`)
|
|
200
|
+
|
|
201
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
202
|
+
mockExistsSync.mockImplementation(() => true)
|
|
203
|
+
|
|
204
|
+
const mockReaddirSync = readdirSync as unknown as ReturnType<typeof mock>
|
|
205
|
+
mockReaddirSync.mockImplementation((path: string) => {
|
|
206
|
+
if (path.includes('leveldb')) return ['test.ldb']
|
|
207
|
+
if (path.includes('Local Storage')) return ['leveldb']
|
|
208
|
+
return []
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const mockReadFileSync = readFileSync as unknown as ReturnType<typeof mock>
|
|
212
|
+
mockReadFileSync.mockImplementation(() => ldbContent)
|
|
213
|
+
|
|
214
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
215
|
+
const result = await linuxExtractor.extract()
|
|
216
|
+
|
|
217
|
+
expect(result).not.toBeNull()
|
|
218
|
+
expect(typeof result?.token).toBe('string')
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
describe('encrypted token handling', () => {
|
|
223
|
+
test('decrypts Windows DPAPI encrypted token', async () => {
|
|
224
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
225
|
+
const decryptedKey = Buffer.from('0'.repeat(32), 'hex').toString('base64')
|
|
226
|
+
|
|
227
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
228
|
+
if (cmd.includes('powershell') && cmd.includes('ProtectedData')) {
|
|
229
|
+
return `${decryptedKey}\n`
|
|
230
|
+
}
|
|
231
|
+
return ''
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const winExtractor = new DiscordTokenExtractor('win32')
|
|
235
|
+
|
|
236
|
+
// Mock Local State file reading
|
|
237
|
+
const mockReadFileSync = readFileSync as unknown as ReturnType<typeof mock>
|
|
238
|
+
mockReadFileSync.mockImplementation((path: string) => {
|
|
239
|
+
if (path.includes('Local State')) {
|
|
240
|
+
return JSON.stringify({
|
|
241
|
+
os_crypt: {
|
|
242
|
+
encrypted_key: Buffer.from(`DPAPI${'x'.repeat(32)}`).toString('base64'),
|
|
243
|
+
},
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
return Buffer.from('')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Test that DPAPI decryption is called
|
|
250
|
+
const encryptedToken = `dQw4w9WgXcQ:${Buffer.from('test').toString('base64')}`
|
|
251
|
+
expect(winExtractor.isEncryptedToken(encryptedToken)).toBe(true)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
test('decrypts macOS Keychain encrypted token', async () => {
|
|
255
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
256
|
+
const keychainPassword = 'test_password'
|
|
257
|
+
|
|
258
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
259
|
+
if (cmd.includes('security find-generic-password')) {
|
|
260
|
+
if (cmd.includes('Discord Safe Storage')) {
|
|
261
|
+
return keychainPassword
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
throw new Error('Not found')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
const macExtractor = new DiscordTokenExtractor('darwin')
|
|
268
|
+
|
|
269
|
+
// Verify keychain command patterns
|
|
270
|
+
expect(macExtractor.getKeychainVariants()).toEqual([
|
|
271
|
+
{ service: 'discord Safe Storage', account: 'discord Key' },
|
|
272
|
+
{ service: 'discordcanary Safe Storage', account: 'discordcanary Key' },
|
|
273
|
+
{ service: 'discordptb Safe Storage', account: 'discordptb Key' },
|
|
274
|
+
{ service: 'Discord Safe Storage', account: 'Discord' },
|
|
275
|
+
{ service: 'Discord Canary Safe Storage', account: 'Discord Canary' },
|
|
276
|
+
{ service: 'Discord PTB Safe Storage', account: 'Discord PTB' },
|
|
277
|
+
])
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
describe('variant detection', () => {
|
|
282
|
+
test('identifies Discord Stable', () => {
|
|
283
|
+
expect(extractor.getVariantFromPath('/path/to/Discord')).toBe('stable')
|
|
284
|
+
expect(extractor.getVariantFromPath('/path/to/discord')).toBe('stable')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
test('identifies Discord Canary', () => {
|
|
288
|
+
expect(extractor.getVariantFromPath('/path/to/discordcanary')).toBe('canary')
|
|
289
|
+
expect(extractor.getVariantFromPath('/path/to/Discord Canary')).toBe('canary')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test('identifies Discord PTB', () => {
|
|
293
|
+
expect(extractor.getVariantFromPath('/path/to/discordptb')).toBe('ptb')
|
|
294
|
+
expect(extractor.getVariantFromPath('/path/to/Discord PTB')).toBe('ptb')
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
describe('process management', () => {
|
|
299
|
+
describe('isDiscordRunning', () => {
|
|
300
|
+
test('returns true when Discord process is found on macOS', async () => {
|
|
301
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
302
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
303
|
+
if (cmd.includes('pgrep') && cmd.includes('Discord')) {
|
|
304
|
+
return '12345\n'
|
|
305
|
+
}
|
|
306
|
+
return ''
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
310
|
+
const result = await darwinExtractor.isDiscordRunning('stable')
|
|
311
|
+
expect(result).toBe(true)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('returns false when no Discord process is found', async () => {
|
|
315
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
316
|
+
mockExecSync.mockImplementation(() => '')
|
|
317
|
+
|
|
318
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
319
|
+
const result = await darwinExtractor.isDiscordRunning('stable')
|
|
320
|
+
expect(result).toBe(false)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
test('checks all variants when no specific variant provided', async () => {
|
|
324
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
325
|
+
const checkedProcesses: string[] = []
|
|
326
|
+
|
|
327
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
328
|
+
if (cmd.includes('pgrep')) {
|
|
329
|
+
const match = cmd.match(/pgrep -f "([^"]+)"/)
|
|
330
|
+
if (match) checkedProcesses.push(match[1])
|
|
331
|
+
}
|
|
332
|
+
return ''
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
336
|
+
await darwinExtractor.isDiscordRunning()
|
|
337
|
+
|
|
338
|
+
expect(checkedProcesses).toContain('Discord')
|
|
339
|
+
expect(checkedProcesses).toContain('Discord Canary')
|
|
340
|
+
expect(checkedProcesses).toContain('Discord PTB')
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
test('returns true when Windows process is found', async () => {
|
|
344
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
345
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
346
|
+
if (cmd.includes('tasklist') && cmd.includes('Discord.exe')) {
|
|
347
|
+
return 'Discord.exe 12345 Console 1 123,456 K\n'
|
|
348
|
+
}
|
|
349
|
+
return ''
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
const winExtractor = new DiscordTokenExtractor('win32', 0, 0)
|
|
353
|
+
const result = await winExtractor.isDiscordRunning('stable')
|
|
354
|
+
expect(result).toBe(true)
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
describe('killDiscord', () => {
|
|
359
|
+
test('kills Discord process on macOS using pkill', async () => {
|
|
360
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
361
|
+
const killedProcesses: string[] = []
|
|
362
|
+
|
|
363
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
364
|
+
if (cmd.includes('pkill')) {
|
|
365
|
+
const match = cmd.match(/pkill -f "([^"]+)"/)
|
|
366
|
+
if (match) killedProcesses.push(match[1])
|
|
367
|
+
}
|
|
368
|
+
return ''
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
372
|
+
await darwinExtractor.killDiscord('stable')
|
|
373
|
+
|
|
374
|
+
expect(killedProcesses).toContain('Discord')
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
test('kills Discord process on Windows using taskkill', async () => {
|
|
378
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
379
|
+
const killedProcesses: string[] = []
|
|
380
|
+
|
|
381
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
382
|
+
if (cmd.includes('taskkill')) {
|
|
383
|
+
const match = cmd.match(/taskkill \/F \/IM "([^"]+)"/)
|
|
384
|
+
if (match) killedProcesses.push(match[1])
|
|
385
|
+
}
|
|
386
|
+
return ''
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
const winExtractor = new DiscordTokenExtractor('win32', 0, 0)
|
|
390
|
+
await winExtractor.killDiscord('stable')
|
|
391
|
+
|
|
392
|
+
expect(killedProcesses).toContain('Discord.exe')
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
test('kills all variants when no specific variant provided', async () => {
|
|
396
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
397
|
+
const killedProcesses: string[] = []
|
|
398
|
+
|
|
399
|
+
mockExecSync.mockImplementation((cmd: string) => {
|
|
400
|
+
if (cmd.includes('pkill')) {
|
|
401
|
+
const match = cmd.match(/pkill -f "([^"]+)"/)
|
|
402
|
+
if (match) killedProcesses.push(match[1])
|
|
403
|
+
}
|
|
404
|
+
return ''
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
408
|
+
await darwinExtractor.killDiscord()
|
|
409
|
+
|
|
410
|
+
expect(killedProcesses).toContain('Discord')
|
|
411
|
+
expect(killedProcesses).toContain('Discord Canary')
|
|
412
|
+
expect(killedProcesses).toContain('Discord PTB')
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
describe('launchDiscordWithDebug', () => {
|
|
417
|
+
test('throws error when Discord app not found', async () => {
|
|
418
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
419
|
+
mockExistsSync.mockImplementation(() => false)
|
|
420
|
+
|
|
421
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
422
|
+
|
|
423
|
+
await expect(darwinExtractor.launchDiscordWithDebug('stable')).rejects.toThrow(
|
|
424
|
+
'Discord stable not found'
|
|
425
|
+
)
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
test('launches Discord with remote debugging port on macOS', async () => {
|
|
429
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
430
|
+
mockExistsSync.mockImplementation((path: string) => {
|
|
431
|
+
return path.includes('/Applications/Discord.app')
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
435
|
+
mockExecSync.mockImplementation(() => '')
|
|
436
|
+
|
|
437
|
+
let spawnedPath = ''
|
|
438
|
+
let spawnedArgs: string[] = []
|
|
439
|
+
const mockSpawnFn = spawn as unknown as ReturnType<typeof mock>
|
|
440
|
+
mockSpawnFn.mockImplementation((path: string, args: string[]) => {
|
|
441
|
+
spawnedPath = path
|
|
442
|
+
spawnedArgs = args
|
|
443
|
+
return { unref: () => {} }
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
447
|
+
await darwinExtractor.launchDiscordWithDebug('stable', 9222)
|
|
448
|
+
|
|
449
|
+
expect(spawnedPath).toBe('/Applications/Discord.app/Contents/MacOS/Discord')
|
|
450
|
+
expect(spawnedArgs).toContain('--remote-debugging-port=9222')
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
test('uses default CDP port when not specified', async () => {
|
|
454
|
+
const mockExistsSync = existsSync as unknown as ReturnType<typeof mock>
|
|
455
|
+
mockExistsSync.mockImplementation(() => true)
|
|
456
|
+
|
|
457
|
+
const mockExecSync = execSync as unknown as ReturnType<typeof mock>
|
|
458
|
+
mockExecSync.mockImplementation(() => '')
|
|
459
|
+
|
|
460
|
+
let spawnedArgs: string[] = []
|
|
461
|
+
const mockSpawnFn = spawn as unknown as ReturnType<typeof mock>
|
|
462
|
+
mockSpawnFn.mockImplementation((_path: string, args: string[]) => {
|
|
463
|
+
spawnedArgs = args
|
|
464
|
+
return { unref: () => {} }
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0, 0)
|
|
468
|
+
await darwinExtractor.launchDiscordWithDebug('stable')
|
|
469
|
+
|
|
470
|
+
expect(spawnedArgs).toContain(`--remote-debugging-port=${CDP_PORT}`)
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
describe('CDP client methods', () => {
|
|
476
|
+
describe('discoverCDPTargets', () => {
|
|
477
|
+
test('returns empty array when CDP endpoint is not reachable', async () => {
|
|
478
|
+
const originalFetch = globalThis.fetch
|
|
479
|
+
globalThis.fetch = mock(async () => {
|
|
480
|
+
throw new Error('Connection refused')
|
|
481
|
+
}) as unknown as typeof fetch
|
|
482
|
+
|
|
483
|
+
try {
|
|
484
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
485
|
+
const targets = await extractor.discoverCDPTargets(19999)
|
|
486
|
+
expect(targets).toEqual([])
|
|
487
|
+
} finally {
|
|
488
|
+
globalThis.fetch = originalFetch
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
test('returns targets from CDP endpoint', async () => {
|
|
493
|
+
const mockTargets = [
|
|
494
|
+
{
|
|
495
|
+
id: '1',
|
|
496
|
+
type: 'page',
|
|
497
|
+
title: 'Discord',
|
|
498
|
+
url: 'https://discord.com/app',
|
|
499
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
500
|
+
},
|
|
501
|
+
]
|
|
502
|
+
|
|
503
|
+
const originalFetch = globalThis.fetch
|
|
504
|
+
globalThis.fetch = mock(async () => ({
|
|
505
|
+
ok: true,
|
|
506
|
+
json: async () => mockTargets,
|
|
507
|
+
})) as unknown as typeof fetch
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
511
|
+
const targets = await extractor.discoverCDPTargets(9222)
|
|
512
|
+
expect(targets).toEqual(mockTargets)
|
|
513
|
+
} finally {
|
|
514
|
+
globalThis.fetch = originalFetch
|
|
515
|
+
}
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
test('returns empty array on HTTP error', async () => {
|
|
519
|
+
const originalFetch = globalThis.fetch
|
|
520
|
+
globalThis.fetch = mock(async () => ({
|
|
521
|
+
ok: false,
|
|
522
|
+
status: 500,
|
|
523
|
+
})) as unknown as typeof fetch
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
527
|
+
const targets = await extractor.discoverCDPTargets(9222)
|
|
528
|
+
expect(targets).toEqual([])
|
|
529
|
+
} finally {
|
|
530
|
+
globalThis.fetch = originalFetch
|
|
531
|
+
}
|
|
532
|
+
})
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
describe('findDiscordPageTarget', () => {
|
|
536
|
+
test('finds target by discord.com URL', () => {
|
|
537
|
+
const targets = [
|
|
538
|
+
{
|
|
539
|
+
id: '1',
|
|
540
|
+
type: 'page',
|
|
541
|
+
title: 'Discord',
|
|
542
|
+
url: 'https://discord.com/app',
|
|
543
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
id: '2',
|
|
547
|
+
type: 'background_page',
|
|
548
|
+
title: 'background',
|
|
549
|
+
url: 'about:blank',
|
|
550
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/2',
|
|
551
|
+
},
|
|
552
|
+
]
|
|
553
|
+
|
|
554
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
555
|
+
const target = extractor.findDiscordPageTarget(targets)
|
|
556
|
+
|
|
557
|
+
expect(target).not.toBeNull()
|
|
558
|
+
expect(target?.id).toBe('1')
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
test('finds target by Discord title', () => {
|
|
562
|
+
const targets = [
|
|
563
|
+
{
|
|
564
|
+
id: '1',
|
|
565
|
+
type: 'page',
|
|
566
|
+
title: 'Discord - Chat',
|
|
567
|
+
url: 'https://app.discord.com/channels',
|
|
568
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
569
|
+
},
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
573
|
+
const target = extractor.findDiscordPageTarget(targets)
|
|
574
|
+
|
|
575
|
+
expect(target).not.toBeNull()
|
|
576
|
+
expect(target?.id).toBe('1')
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
test('returns null when no Discord page found', () => {
|
|
580
|
+
const targets = [
|
|
581
|
+
{
|
|
582
|
+
id: '1',
|
|
583
|
+
type: 'background_page',
|
|
584
|
+
title: 'background',
|
|
585
|
+
url: 'about:blank',
|
|
586
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
587
|
+
},
|
|
588
|
+
]
|
|
589
|
+
|
|
590
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
591
|
+
const target = extractor.findDiscordPageTarget(targets)
|
|
592
|
+
|
|
593
|
+
expect(target).toBeNull()
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
test('returns null for empty targets', () => {
|
|
597
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
598
|
+
const target = extractor.findDiscordPageTarget([])
|
|
599
|
+
expect(target).toBeNull()
|
|
600
|
+
})
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
describe('executeJSViaCDP', () => {
|
|
604
|
+
test('executes JavaScript and returns result', async () => {
|
|
605
|
+
const mockToken = 'test_token_12345'
|
|
606
|
+
|
|
607
|
+
const mockWebSocket = class {
|
|
608
|
+
onopen: (() => void) | null = null
|
|
609
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
610
|
+
onerror: ((error: unknown) => void) | null = null
|
|
611
|
+
|
|
612
|
+
constructor() {
|
|
613
|
+
setTimeout(() => {
|
|
614
|
+
this.onopen?.()
|
|
615
|
+
}, 10)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
send(data: string) {
|
|
619
|
+
const message = JSON.parse(data)
|
|
620
|
+
setTimeout(() => {
|
|
621
|
+
this.onmessage?.({
|
|
622
|
+
data: JSON.stringify({
|
|
623
|
+
id: message.id,
|
|
624
|
+
result: { result: { value: mockToken } },
|
|
625
|
+
}),
|
|
626
|
+
})
|
|
627
|
+
}, 10)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
close() {}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const originalWebSocket = globalThis.WebSocket
|
|
634
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
635
|
+
|
|
636
|
+
try {
|
|
637
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
638
|
+
const result = await extractor.executeJSViaCDP(
|
|
639
|
+
'ws://localhost:9222/devtools/page/1',
|
|
640
|
+
TOKEN_EXTRACTION_JS
|
|
641
|
+
)
|
|
642
|
+
expect(result).toBe(mockToken)
|
|
643
|
+
} finally {
|
|
644
|
+
globalThis.WebSocket = originalWebSocket
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
|
|
648
|
+
test('rejects on CDP error response', async () => {
|
|
649
|
+
const mockWebSocket = class {
|
|
650
|
+
onopen: (() => void) | null = null
|
|
651
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
652
|
+
onerror: ((error: unknown) => void) | null = null
|
|
653
|
+
|
|
654
|
+
constructor() {
|
|
655
|
+
setTimeout(() => {
|
|
656
|
+
this.onopen?.()
|
|
657
|
+
}, 10)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
send(data: string) {
|
|
661
|
+
const message = JSON.parse(data)
|
|
662
|
+
setTimeout(() => {
|
|
663
|
+
this.onmessage?.({
|
|
664
|
+
data: JSON.stringify({
|
|
665
|
+
id: message.id,
|
|
666
|
+
error: { code: -32000, message: 'Evaluation failed' },
|
|
667
|
+
}),
|
|
668
|
+
})
|
|
669
|
+
}, 10)
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
close() {}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const originalWebSocket = globalThis.WebSocket
|
|
676
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
677
|
+
|
|
678
|
+
try {
|
|
679
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
680
|
+
await expect(
|
|
681
|
+
extractor.executeJSViaCDP('ws://localhost:9222/devtools/page/1', TOKEN_EXTRACTION_JS)
|
|
682
|
+
).rejects.toThrow('Evaluation failed')
|
|
683
|
+
} finally {
|
|
684
|
+
globalThis.WebSocket = originalWebSocket
|
|
685
|
+
}
|
|
686
|
+
})
|
|
687
|
+
|
|
688
|
+
test('rejects on WebSocket error', async () => {
|
|
689
|
+
const mockWebSocket = class {
|
|
690
|
+
onopen: (() => void) | null = null
|
|
691
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
692
|
+
onerror: ((error: unknown) => void) | null = null
|
|
693
|
+
|
|
694
|
+
constructor() {
|
|
695
|
+
setTimeout(() => {
|
|
696
|
+
this.onerror?.(new Error('Connection failed'))
|
|
697
|
+
}, 10)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
send() {}
|
|
701
|
+
close() {}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const originalWebSocket = globalThis.WebSocket
|
|
705
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
709
|
+
await expect(
|
|
710
|
+
extractor.executeJSViaCDP('ws://localhost:9222/devtools/page/1', TOKEN_EXTRACTION_JS)
|
|
711
|
+
).rejects.toThrow()
|
|
712
|
+
} finally {
|
|
713
|
+
globalThis.WebSocket = originalWebSocket
|
|
714
|
+
}
|
|
715
|
+
})
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
describe('extractViaCDP', () => {
|
|
719
|
+
test('returns null when no CDP targets available', async () => {
|
|
720
|
+
const originalFetch = globalThis.fetch
|
|
721
|
+
globalThis.fetch = mock(async () => ({
|
|
722
|
+
ok: true,
|
|
723
|
+
json: async () => [],
|
|
724
|
+
})) as unknown as typeof fetch
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
728
|
+
const result = await extractor.extractViaCDP(9222)
|
|
729
|
+
expect(result).toBeNull()
|
|
730
|
+
} finally {
|
|
731
|
+
globalThis.fetch = originalFetch
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
test('returns null when no Discord page target found', async () => {
|
|
736
|
+
const originalFetch = globalThis.fetch
|
|
737
|
+
globalThis.fetch = mock(async () => ({
|
|
738
|
+
ok: true,
|
|
739
|
+
json: async () => [
|
|
740
|
+
{
|
|
741
|
+
id: '1',
|
|
742
|
+
type: 'background_page',
|
|
743
|
+
title: 'background',
|
|
744
|
+
url: 'about:blank',
|
|
745
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
746
|
+
},
|
|
747
|
+
],
|
|
748
|
+
})) as unknown as typeof fetch
|
|
749
|
+
|
|
750
|
+
try {
|
|
751
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
752
|
+
const result = await extractor.extractViaCDP(9222)
|
|
753
|
+
expect(result).toBeNull()
|
|
754
|
+
} finally {
|
|
755
|
+
globalThis.fetch = originalFetch
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
test('extracts token via CDP when Discord is running with debug port', async () => {
|
|
760
|
+
const mockToken = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.GrE5dA.abcdefghijklmnopqrstuvwxyz1234567890'
|
|
761
|
+
|
|
762
|
+
const originalFetch = globalThis.fetch
|
|
763
|
+
globalThis.fetch = mock(async () => ({
|
|
764
|
+
ok: true,
|
|
765
|
+
json: async () => [
|
|
766
|
+
{
|
|
767
|
+
id: '1',
|
|
768
|
+
type: 'page',
|
|
769
|
+
title: 'Discord',
|
|
770
|
+
url: 'https://discord.com/app',
|
|
771
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
772
|
+
},
|
|
773
|
+
],
|
|
774
|
+
})) as unknown as typeof fetch
|
|
775
|
+
|
|
776
|
+
const mockWebSocket = class {
|
|
777
|
+
onopen: (() => void) | null = null
|
|
778
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
779
|
+
onerror: ((error: unknown) => void) | null = null
|
|
780
|
+
|
|
781
|
+
constructor() {
|
|
782
|
+
setTimeout(() => this.onopen?.(), 10)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
send(data: string) {
|
|
786
|
+
const message = JSON.parse(data)
|
|
787
|
+
setTimeout(() => {
|
|
788
|
+
this.onmessage?.({
|
|
789
|
+
data: JSON.stringify({
|
|
790
|
+
id: message.id,
|
|
791
|
+
result: { result: { value: mockToken } },
|
|
792
|
+
}),
|
|
793
|
+
})
|
|
794
|
+
}, 10)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
close() {}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const originalWebSocket = globalThis.WebSocket
|
|
801
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
802
|
+
|
|
803
|
+
try {
|
|
804
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
805
|
+
const result = await extractor.extractViaCDP(9222)
|
|
806
|
+
expect(result).toBe(mockToken)
|
|
807
|
+
} finally {
|
|
808
|
+
globalThis.fetch = originalFetch
|
|
809
|
+
globalThis.WebSocket = originalWebSocket
|
|
810
|
+
}
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
test('returns null when token extraction JS fails', async () => {
|
|
814
|
+
const originalFetch = globalThis.fetch
|
|
815
|
+
globalThis.fetch = mock(async () => ({
|
|
816
|
+
ok: true,
|
|
817
|
+
json: async () => [
|
|
818
|
+
{
|
|
819
|
+
id: '1',
|
|
820
|
+
type: 'page',
|
|
821
|
+
title: 'Discord',
|
|
822
|
+
url: 'https://discord.com/app',
|
|
823
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
})) as unknown as typeof fetch
|
|
827
|
+
|
|
828
|
+
const mockWebSocket = class {
|
|
829
|
+
onopen: (() => void) | null = null
|
|
830
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
831
|
+
onerror: ((error: unknown) => void) | null = null
|
|
832
|
+
|
|
833
|
+
constructor() {
|
|
834
|
+
setTimeout(() => this.onopen?.(), 10)
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
send(data: string) {
|
|
838
|
+
const message = JSON.parse(data)
|
|
839
|
+
setTimeout(() => {
|
|
840
|
+
this.onmessage?.({
|
|
841
|
+
data: JSON.stringify({
|
|
842
|
+
id: message.id,
|
|
843
|
+
error: { code: -32000, message: 'Cannot find module' },
|
|
844
|
+
}),
|
|
845
|
+
})
|
|
846
|
+
}, 10)
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
close() {}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
const originalWebSocket = globalThis.WebSocket
|
|
853
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
854
|
+
|
|
855
|
+
try {
|
|
856
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
857
|
+
const result = await extractor.extractViaCDP(9222)
|
|
858
|
+
expect(result).toBeNull()
|
|
859
|
+
} finally {
|
|
860
|
+
globalThis.fetch = originalFetch
|
|
861
|
+
globalThis.WebSocket = originalWebSocket
|
|
862
|
+
}
|
|
863
|
+
})
|
|
864
|
+
|
|
865
|
+
test('returns null when returned value is not a valid token', async () => {
|
|
866
|
+
const originalFetch = globalThis.fetch
|
|
867
|
+
globalThis.fetch = mock(async () => ({
|
|
868
|
+
ok: true,
|
|
869
|
+
json: async () => [
|
|
870
|
+
{
|
|
871
|
+
id: '1',
|
|
872
|
+
type: 'page',
|
|
873
|
+
title: 'Discord',
|
|
874
|
+
url: 'https://discord.com/app',
|
|
875
|
+
webSocketDebuggerUrl: 'ws://localhost:9222/devtools/page/1',
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
})) as unknown as typeof fetch
|
|
879
|
+
|
|
880
|
+
const mockWebSocket = class {
|
|
881
|
+
onopen: (() => void) | null = null
|
|
882
|
+
onmessage: ((event: { data: string }) => void) | null = null
|
|
883
|
+
onerror: ((error: unknown) => void) | null = null
|
|
884
|
+
|
|
885
|
+
constructor() {
|
|
886
|
+
setTimeout(() => this.onopen?.(), 10)
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
send(data: string) {
|
|
890
|
+
const message = JSON.parse(data)
|
|
891
|
+
setTimeout(() => {
|
|
892
|
+
this.onmessage?.({
|
|
893
|
+
data: JSON.stringify({
|
|
894
|
+
id: message.id,
|
|
895
|
+
result: { result: { value: 'not_a_valid_token' } },
|
|
896
|
+
}),
|
|
897
|
+
})
|
|
898
|
+
}, 10)
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
close() {}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const originalWebSocket = globalThis.WebSocket
|
|
905
|
+
globalThis.WebSocket = mockWebSocket as unknown as typeof WebSocket
|
|
906
|
+
|
|
907
|
+
try {
|
|
908
|
+
const extractor = new DiscordTokenExtractor('darwin')
|
|
909
|
+
const result = await extractor.extractViaCDP(9222)
|
|
910
|
+
expect(result).toBeNull()
|
|
911
|
+
} finally {
|
|
912
|
+
globalThis.fetch = originalFetch
|
|
913
|
+
globalThis.WebSocket = originalWebSocket
|
|
914
|
+
}
|
|
915
|
+
})
|
|
916
|
+
})
|
|
917
|
+
})
|
|
918
|
+
})
|