agent-messenger 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/release.md +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/e2e.yml.disabled +69 -0
- package/README.md +16 -14
- package/biome.json +33 -1
- package/bun.lock +63 -0
- package/dist/package.json +8 -4
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +4 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/platforms/discord/cli.js +1 -1
- package/dist/src/platforms/discord/client.d.ts.map +1 -1
- package/dist/src/platforms/discord/client.js +3 -3
- package/dist/src/platforms/discord/client.js.map +1 -1
- package/dist/src/platforms/discord/commands/user.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/user.js +10 -1
- package/dist/src/platforms/discord/commands/user.js.map +1 -1
- package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/discord/credential-manager.js +18 -12
- package/dist/src/platforms/discord/credential-manager.js.map +1 -1
- package/dist/src/platforms/slack/cli.js +1 -1
- package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/slack/credential-manager.js +20 -6
- package/dist/src/platforms/slack/credential-manager.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +34 -9
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/cli.d.ts.map +1 -0
- package/dist/{cli.js → src/platforms/teams/cli.js} +11 -10
- package/dist/src/platforms/teams/cli.js.map +1 -0
- package/dist/src/platforms/teams/client.d.ts +32 -0
- package/dist/src/platforms/teams/client.d.ts.map +1 -0
- package/dist/src/platforms/teams/client.js +202 -0
- package/dist/src/platforms/teams/client.js.map +1 -0
- package/dist/src/platforms/teams/commands/auth.d.ts +14 -0
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/auth.js +176 -0
- package/dist/src/platforms/teams/commands/auth.js.map +1 -0
- package/dist/src/platforms/teams/commands/channel.d.ts +13 -0
- package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/channel.js +97 -0
- package/dist/src/platforms/teams/commands/channel.js.map +1 -0
- package/dist/src/platforms/teams/commands/file.d.ts +12 -0
- package/dist/src/platforms/teams/commands/file.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/file.js +104 -0
- package/dist/src/platforms/teams/commands/file.js.map +1 -0
- package/dist/{commands → src/platforms/teams/commands}/index.d.ts +5 -2
- package/dist/src/platforms/teams/commands/index.d.ts.map +1 -0
- package/dist/{commands → src/platforms/teams/commands}/index.js +5 -2
- package/dist/src/platforms/teams/commands/index.js.map +1 -0
- package/dist/src/platforms/teams/commands/message.d.ts +17 -0
- package/dist/src/platforms/teams/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/message.js +133 -0
- package/dist/src/platforms/teams/commands/message.js.map +1 -0
- package/dist/src/platforms/teams/commands/reaction.d.ts +9 -0
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/reaction.js +68 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -0
- package/dist/src/platforms/teams/commands/snapshot.d.ts +10 -0
- package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/snapshot.js +85 -0
- package/dist/src/platforms/teams/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/teams/commands/team.d.ts +18 -0
- package/dist/src/platforms/teams/commands/team.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/team.js +130 -0
- package/dist/src/platforms/teams/commands/team.js.map +1 -0
- package/dist/src/platforms/teams/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/user.js +88 -0
- package/dist/src/platforms/teams/commands/user.js.map +1 -0
- package/dist/src/platforms/teams/credential-manager.d.ts +18 -0
- package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/teams/credential-manager.js +81 -0
- package/dist/src/platforms/teams/credential-manager.js.map +1 -0
- package/dist/src/platforms/teams/index.d.ts +4 -0
- package/dist/src/platforms/teams/index.d.ts.map +1 -0
- package/dist/src/platforms/teams/index.js +6 -0
- package/dist/src/platforms/teams/index.js.map +1 -0
- package/dist/src/platforms/teams/token-extractor.d.ts +36 -0
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/teams/token-extractor.js +335 -0
- package/dist/src/platforms/teams/token-extractor.js.map +1 -0
- package/dist/src/platforms/teams/types.d.ts +209 -0
- package/dist/src/platforms/teams/types.d.ts.map +1 -0
- package/dist/src/platforms/teams/types.js +65 -0
- package/dist/src/platforms/teams/types.js.map +1 -0
- package/docs/teams.md +321 -0
- package/e2e/README.md +256 -0
- package/e2e/config.ts +45 -0
- package/e2e/discord.e2e.test.ts +252 -0
- package/e2e/helpers.ts +107 -0
- package/e2e/slack.e2e.test.ts +309 -0
- package/package.json +8 -4
- package/scripts/postbuild.ts +15 -0
- package/skills/agent-teams/SKILL.md +292 -0
- package/skills/agent-teams/references/authentication.md +375 -0
- package/skills/agent-teams/references/common-patterns.md +596 -0
- package/skills/agent-teams/templates/monitor-channel.sh +239 -0
- package/skills/agent-teams/templates/post-message.sh +224 -0
- package/skills/agent-teams/templates/team-summary.sh +210 -0
- package/src/cli.ts +4 -0
- package/src/platforms/discord/client.ts +3 -3
- package/src/platforms/discord/commands/auth.test.ts +48 -32
- package/src/platforms/discord/commands/channel.test.ts +54 -42
- package/src/platforms/discord/commands/file.test.ts +40 -53
- package/src/platforms/discord/commands/guild.test.ts +47 -27
- package/src/platforms/discord/commands/message.test.ts +54 -51
- package/src/platforms/discord/commands/reaction.test.ts +54 -42
- package/src/platforms/discord/commands/user.ts +12 -1
- package/src/platforms/discord/credential-manager.test.ts +137 -136
- package/src/platforms/discord/credential-manager.ts +20 -13
- package/src/platforms/discord/token-extractor.test.ts +133 -383
- package/{tests → src/platforms/slack}/cli.test.ts +3 -3
- package/{tests/slack-client.test.ts → src/platforms/slack/client.test.ts} +1 -1
- package/{tests → src/platforms/slack}/commands/auth.test.ts +25 -13
- package/{tests → src/platforms/slack}/commands/channel.test.ts +2 -2
- package/{tests → src/platforms/slack}/commands/file.test.ts +2 -2
- package/{tests → src/platforms/slack}/commands/message.test.ts +2 -2
- package/{tests → src/platforms/slack}/commands/reaction.test.ts +1 -1
- package/{tests → src/platforms/slack}/commands/snapshot.test.ts +117 -105
- package/{tests → src/platforms/slack}/commands/user.test.ts +3 -3
- package/{tests → src/platforms/slack}/commands/workspace.test.ts +44 -95
- package/{tests → src/platforms/slack}/credential-manager.test.ts +2 -2
- package/src/platforms/slack/credential-manager.ts +22 -7
- package/src/platforms/slack/token-extractor-node-test.ts +40 -0
- package/src/platforms/slack/token-extractor-node.test.ts +10 -0
- package/src/platforms/slack/token-extractor.ts +36 -10
- package/{tests → src/platforms/slack}/types.test.ts +1 -1
- package/src/platforms/teams/cli.ts +36 -0
- package/src/platforms/teams/client.test.ts +500 -0
- package/src/platforms/teams/client.ts +365 -0
- package/src/platforms/teams/commands/auth.test.ts +99 -0
- package/src/platforms/teams/commands/auth.ts +232 -0
- package/src/platforms/teams/commands/channel.test.ts +147 -0
- package/src/platforms/teams/commands/channel.ts +129 -0
- package/src/platforms/teams/commands/file.test.ts +88 -0
- package/src/platforms/teams/commands/file.ts +144 -0
- package/src/platforms/teams/commands/index.ts +12 -0
- package/src/platforms/teams/commands/message.test.ts +110 -0
- package/src/platforms/teams/commands/message.ts +188 -0
- package/src/platforms/teams/commands/reaction.test.ts +87 -0
- package/src/platforms/teams/commands/reaction.ts +104 -0
- package/src/platforms/teams/commands/snapshot.test.ts +35 -0
- package/src/platforms/teams/commands/snapshot.ts +115 -0
- package/src/platforms/teams/commands/team.test.ts +157 -0
- package/src/platforms/teams/commands/team.ts +164 -0
- package/src/platforms/teams/commands/user.test.ts +83 -0
- package/src/platforms/teams/commands/user.ts +112 -0
- package/src/platforms/teams/credential-manager.test.ts +178 -0
- package/src/platforms/teams/credential-manager.ts +92 -0
- package/src/platforms/teams/index.ts +5 -0
- package/src/platforms/teams/token-extractor.test.ts +429 -0
- package/src/platforms/teams/token-extractor.ts +462 -0
- package/src/platforms/teams/types.test.ts +226 -0
- package/src/platforms/teams/types.ts +140 -0
- package/tsconfig.json +1 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/auth.d.ts +0 -3
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js +0 -140
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/channel.d.ts +0 -3
- package/dist/commands/channel.d.ts.map +0 -1
- package/dist/commands/channel.js +0 -118
- package/dist/commands/channel.js.map +0 -1
- package/dist/commands/file.d.ts +0 -3
- package/dist/commands/file.d.ts.map +0 -1
- package/dist/commands/file.js +0 -113
- package/dist/commands/file.js.map +0 -1
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/message.d.ts +0 -3
- package/dist/commands/message.d.ts.map +0 -1
- package/dist/commands/message.js +0 -214
- package/dist/commands/message.js.map +0 -1
- package/dist/commands/reaction.d.ts +0 -3
- package/dist/commands/reaction.d.ts.map +0 -1
- package/dist/commands/reaction.js +0 -100
- package/dist/commands/reaction.js.map +0 -1
- package/dist/commands/snapshot.d.ts +0 -3
- package/dist/commands/snapshot.d.ts.map +0 -1
- package/dist/commands/snapshot.js +0 -88
- package/dist/commands/snapshot.js.map +0 -1
- package/dist/commands/user.d.ts.map +0 -1
- package/dist/commands/user.js +0 -96
- package/dist/commands/user.js.map +0 -1
- package/dist/commands/workspace.d.ts +0 -3
- package/dist/commands/workspace.d.ts.map +0 -1
- package/dist/commands/workspace.js +0 -89
- package/dist/commands/workspace.js.map +0 -1
- package/dist/lib/credential-manager.d.ts +0 -13
- package/dist/lib/credential-manager.d.ts.map +0 -1
- package/dist/lib/credential-manager.js +0 -58
- package/dist/lib/credential-manager.js.map +0 -1
- package/dist/lib/index.d.ts +0 -3
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/index.js +0 -3
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/ref-manager.d.ts +0 -26
- package/dist/lib/ref-manager.d.ts.map +0 -1
- package/dist/lib/ref-manager.js +0 -92
- package/dist/lib/ref-manager.js.map +0 -1
- package/dist/lib/slack-client.d.ts +0 -37
- package/dist/lib/slack-client.d.ts.map +0 -1
- package/dist/lib/slack-client.js +0 -379
- package/dist/lib/slack-client.js.map +0 -1
- package/dist/lib/token-extractor.d.ts +0 -28
- package/dist/lib/token-extractor.d.ts.map +0 -1
- package/dist/lib/token-extractor.js +0 -401
- package/dist/lib/token-extractor.js.map +0 -1
- package/dist/src/platforms/discord/client.test.d.ts +0 -2
- package/dist/src/platforms/discord/client.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/client.test.js +0 -367
- package/dist/src/platforms/discord/client.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/auth.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/auth.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/auth.test.js +0 -65
- package/dist/src/platforms/discord/commands/auth.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/channel.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/channel.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/channel.test.js +0 -136
- package/dist/src/platforms/discord/commands/channel.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/file.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/file.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/file.test.js +0 -83
- package/dist/src/platforms/discord/commands/file.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/guild.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/guild.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/guild.test.js +0 -100
- package/dist/src/platforms/discord/commands/guild.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/message.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/message.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/message.test.js +0 -91
- package/dist/src/platforms/discord/commands/message.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/reaction.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/reaction.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/reaction.test.js +0 -115
- package/dist/src/platforms/discord/commands/reaction.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/snapshot.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/snapshot.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/snapshot.test.js +0 -25
- package/dist/src/platforms/discord/commands/snapshot.test.js.map +0 -1
- package/dist/src/platforms/discord/commands/user.test.d.ts +0 -2
- package/dist/src/platforms/discord/commands/user.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/commands/user.test.js +0 -103
- package/dist/src/platforms/discord/commands/user.test.js.map +0 -1
- package/dist/src/platforms/discord/credential-manager.test.d.ts +0 -2
- package/dist/src/platforms/discord/credential-manager.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/credential-manager.test.js +0 -136
- package/dist/src/platforms/discord/credential-manager.test.js.map +0 -1
- package/dist/src/platforms/discord/token-extractor.test.d.ts +0 -2
- package/dist/src/platforms/discord/token-extractor.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/token-extractor.test.js +0 -789
- package/dist/src/platforms/discord/token-extractor.test.js.map +0 -1
- package/dist/src/platforms/discord/types.test.d.ts +0 -2
- package/dist/src/platforms/discord/types.test.d.ts.map +0 -1
- package/dist/src/platforms/discord/types.test.js +0 -211
- package/dist/src/platforms/discord/types.test.js.map +0 -1
- package/dist/src/shared/utils/concurrency.test.d.ts +0 -2
- package/dist/src/shared/utils/concurrency.test.d.ts.map +0 -1
- package/dist/src/shared/utils/concurrency.test.js +0 -39
- package/dist/src/shared/utils/concurrency.test.js.map +0 -1
- package/dist/tests/cli.test.d.ts +0 -2
- package/dist/tests/cli.test.d.ts.map +0 -1
- package/dist/tests/cli.test.js +0 -83
- package/dist/tests/cli.test.js.map +0 -1
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/CURRENT +0 -1
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOCK +0 -0
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG +0 -3
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG.old +0 -1
- package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/MANIFEST-000004 +0 -0
- package/dist/tests/commands/auth.test.d.ts +0 -2
- package/dist/tests/commands/auth.test.d.ts.map +0 -1
- package/dist/tests/commands/auth.test.js +0 -304
- package/dist/tests/commands/auth.test.js.map +0 -1
- package/dist/tests/commands/channel.test.d.ts +0 -2
- package/dist/tests/commands/channel.test.d.ts.map +0 -1
- package/dist/tests/commands/channel.test.js +0 -166
- package/dist/tests/commands/channel.test.js.map +0 -1
- package/dist/tests/commands/file.test.d.ts +0 -2
- package/dist/tests/commands/file.test.d.ts.map +0 -1
- package/dist/tests/commands/file.test.js +0 -175
- package/dist/tests/commands/file.test.js.map +0 -1
- package/dist/tests/commands/message.test.d.ts +0 -2
- package/dist/tests/commands/message.test.d.ts.map +0 -1
- package/dist/tests/commands/message.test.js +0 -293
- package/dist/tests/commands/message.test.js.map +0 -1
- package/dist/tests/commands/reaction.test.d.ts +0 -2
- package/dist/tests/commands/reaction.test.d.ts.map +0 -1
- package/dist/tests/commands/reaction.test.js +0 -84
- package/dist/tests/commands/reaction.test.js.map +0 -1
- package/dist/tests/commands/snapshot.test.d.ts +0 -2
- package/dist/tests/commands/snapshot.test.d.ts.map +0 -1
- package/dist/tests/commands/snapshot.test.js +0 -280
- package/dist/tests/commands/snapshot.test.js.map +0 -1
- package/dist/tests/commands/user.test.d.ts +0 -2
- package/dist/tests/commands/user.test.d.ts.map +0 -1
- package/dist/tests/commands/user.test.js +0 -117
- package/dist/tests/commands/user.test.js.map +0 -1
- package/dist/tests/commands/workspace.test.d.ts +0 -2
- package/dist/tests/commands/workspace.test.d.ts.map +0 -1
- package/dist/tests/commands/workspace.test.js +0 -453
- package/dist/tests/commands/workspace.test.js.map +0 -1
- package/dist/tests/credential-manager.test.d.ts +0 -2
- package/dist/tests/credential-manager.test.d.ts.map +0 -1
- package/dist/tests/credential-manager.test.js +0 -199
- package/dist/tests/credential-manager.test.js.map +0 -1
- package/dist/tests/slack-client.test.d.ts +0 -2
- package/dist/tests/slack-client.test.d.ts.map +0 -1
- package/dist/tests/slack-client.test.js +0 -741
- package/dist/tests/slack-client.test.js.map +0 -1
- package/dist/tests/types.test.d.ts +0 -2
- package/dist/tests/types.test.d.ts.map +0 -1
- package/dist/tests/types.test.js +0 -215
- package/dist/tests/types.test.js.map +0 -1
- package/dist/types/index.d.ts +0 -369
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -92
- package/dist/types/index.js.map +0 -1
- package/dist/utils/error-handler.d.ts +0 -2
- package/dist/utils/error-handler.d.ts.map +0 -1
- package/dist/utils/error-handler.js +0 -5
- package/dist/utils/error-handler.js.map +0 -1
- package/dist/utils/output.d.ts +0 -2
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/output.js +0 -4
- package/dist/utils/output.js.map +0 -1
- /package/dist/{cli.d.ts → src/platforms/teams/cli.d.ts} +0 -0
- /package/dist/{commands → src/platforms/teams/commands}/user.d.ts +0 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import { createDecipheriv, pbkdf2Sync } from 'node:crypto'
|
|
3
|
+
import { copyFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs'
|
|
4
|
+
import { homedir, tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
|
|
7
|
+
export interface ExtractedTeamsToken {
|
|
8
|
+
token: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface KeychainVariant {
|
|
12
|
+
service: string
|
|
13
|
+
account: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TEAMS_PROCESS_NAMES: Record<string, string> = {
|
|
17
|
+
darwin: 'Microsoft Teams',
|
|
18
|
+
win32: 'Teams.exe',
|
|
19
|
+
linux: 'teams',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const SKYPETOKEN_COOKIE_NAME = 'skypetoken_asm'
|
|
23
|
+
const TEAMS_HOST_PATTERNS = [
|
|
24
|
+
'.asyncgw.teams.microsoft.com',
|
|
25
|
+
'.asm.skype.com',
|
|
26
|
+
'teams.microsoft.com',
|
|
27
|
+
'teams.live.com',
|
|
28
|
+
'.microsoft.com',
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
export class TeamsTokenExtractor {
|
|
32
|
+
private platform: NodeJS.Platform
|
|
33
|
+
|
|
34
|
+
constructor(platform?: NodeJS.Platform) {
|
|
35
|
+
this.platform = platform ?? process.platform
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getTeamsCookiesPaths(): string[] {
|
|
39
|
+
switch (this.platform) {
|
|
40
|
+
case 'darwin':
|
|
41
|
+
return [
|
|
42
|
+
join(
|
|
43
|
+
homedir(),
|
|
44
|
+
'Library',
|
|
45
|
+
'Containers',
|
|
46
|
+
'com.microsoft.teams2',
|
|
47
|
+
'Data',
|
|
48
|
+
'Library',
|
|
49
|
+
'Application Support',
|
|
50
|
+
'Microsoft',
|
|
51
|
+
'MSTeams',
|
|
52
|
+
'EBWebView',
|
|
53
|
+
'WV2Profile_tfw',
|
|
54
|
+
'Cookies'
|
|
55
|
+
),
|
|
56
|
+
join(
|
|
57
|
+
homedir(),
|
|
58
|
+
'Library',
|
|
59
|
+
'Containers',
|
|
60
|
+
'com.microsoft.teams2',
|
|
61
|
+
'Data',
|
|
62
|
+
'Library',
|
|
63
|
+
'Application Support',
|
|
64
|
+
'Microsoft',
|
|
65
|
+
'MSTeams',
|
|
66
|
+
'EBWebView',
|
|
67
|
+
'WV2Profile_tfl',
|
|
68
|
+
'Cookies'
|
|
69
|
+
),
|
|
70
|
+
join(
|
|
71
|
+
homedir(),
|
|
72
|
+
'Library',
|
|
73
|
+
'Containers',
|
|
74
|
+
'com.microsoft.teams2',
|
|
75
|
+
'Data',
|
|
76
|
+
'Library',
|
|
77
|
+
'Application Support',
|
|
78
|
+
'Microsoft',
|
|
79
|
+
'MSTeams',
|
|
80
|
+
'EBWebView',
|
|
81
|
+
'Default',
|
|
82
|
+
'Cookies'
|
|
83
|
+
),
|
|
84
|
+
join(homedir(), 'Library', 'Application Support', 'Microsoft', 'Teams', 'Cookies'),
|
|
85
|
+
]
|
|
86
|
+
case 'linux':
|
|
87
|
+
return [join(homedir(), '.config', 'Microsoft', 'Microsoft Teams', 'Cookies')]
|
|
88
|
+
case 'win32': {
|
|
89
|
+
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
90
|
+
const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
|
|
91
|
+
return [
|
|
92
|
+
// New Teams (MSIX/Store) - WebView2 profile paths
|
|
93
|
+
join(
|
|
94
|
+
localAppData,
|
|
95
|
+
'Packages',
|
|
96
|
+
'MSTeams_8wekyb3d8bbwe',
|
|
97
|
+
'LocalCache',
|
|
98
|
+
'Microsoft',
|
|
99
|
+
'MSTeams',
|
|
100
|
+
'EBWebView',
|
|
101
|
+
'WV2Profile_tfw',
|
|
102
|
+
'Cookies'
|
|
103
|
+
),
|
|
104
|
+
join(
|
|
105
|
+
localAppData,
|
|
106
|
+
'Packages',
|
|
107
|
+
'MSTeams_8wekyb3d8bbwe',
|
|
108
|
+
'LocalCache',
|
|
109
|
+
'Microsoft',
|
|
110
|
+
'MSTeams',
|
|
111
|
+
'EBWebView',
|
|
112
|
+
'WV2Profile_tfl',
|
|
113
|
+
'Cookies'
|
|
114
|
+
),
|
|
115
|
+
join(
|
|
116
|
+
localAppData,
|
|
117
|
+
'Packages',
|
|
118
|
+
'MSTeams_8wekyb3d8bbwe',
|
|
119
|
+
'LocalCache',
|
|
120
|
+
'Microsoft',
|
|
121
|
+
'MSTeams',
|
|
122
|
+
'EBWebView',
|
|
123
|
+
'Default',
|
|
124
|
+
'Cookies'
|
|
125
|
+
),
|
|
126
|
+
// Classic Teams fallback
|
|
127
|
+
join(appdata, 'Microsoft', 'Teams', 'Cookies'),
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
default:
|
|
131
|
+
return []
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getLocalStatePath(): string {
|
|
136
|
+
switch (this.platform) {
|
|
137
|
+
case 'darwin':
|
|
138
|
+
return join(
|
|
139
|
+
homedir(),
|
|
140
|
+
'Library',
|
|
141
|
+
'Application Support',
|
|
142
|
+
'Microsoft',
|
|
143
|
+
'Teams',
|
|
144
|
+
'Local State'
|
|
145
|
+
)
|
|
146
|
+
case 'linux':
|
|
147
|
+
return join(homedir(), '.config', 'Microsoft', 'Microsoft Teams', 'Local State')
|
|
148
|
+
case 'win32': {
|
|
149
|
+
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
150
|
+
const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
|
|
151
|
+
const newTeamsPath = join(
|
|
152
|
+
localAppData,
|
|
153
|
+
'Packages',
|
|
154
|
+
'MSTeams_8wekyb3d8bbwe',
|
|
155
|
+
'LocalCache',
|
|
156
|
+
'Microsoft',
|
|
157
|
+
'MSTeams',
|
|
158
|
+
'EBWebView',
|
|
159
|
+
'Local State'
|
|
160
|
+
)
|
|
161
|
+
if (existsSync(newTeamsPath)) return newTeamsPath
|
|
162
|
+
return join(appdata, 'Microsoft', 'Teams', 'Local State')
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
return ''
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getKeychainVariants(): KeychainVariant[] {
|
|
170
|
+
return [
|
|
171
|
+
// New Teams (com.microsoft.teams2) keychain entry - try first
|
|
172
|
+
{ service: 'Microsoft Teams Safe Storage', account: 'Microsoft Teams' },
|
|
173
|
+
// Work/school variant
|
|
174
|
+
{
|
|
175
|
+
service: 'Microsoft Teams (work or school) Safe Storage',
|
|
176
|
+
account: 'Microsoft Teams (work or school)',
|
|
177
|
+
},
|
|
178
|
+
// Edge WebView2 fallback
|
|
179
|
+
{ service: 'Microsoft Edge Safe Storage', account: 'Microsoft Edge' },
|
|
180
|
+
// Classic Teams fallback
|
|
181
|
+
{ service: 'Teams Safe Storage', account: 'Teams' },
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
isValidSkypeToken(token: string): boolean {
|
|
186
|
+
if (!token || token.length === 0) return false
|
|
187
|
+
// Skype tokens are typically JWT format or long base64 strings (50+ chars)
|
|
188
|
+
return token.length >= 50
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
isEncryptedValue(value: Buffer): boolean {
|
|
192
|
+
if (!value || value.length < 4) return false
|
|
193
|
+
const prefix = value.subarray(0, 3).toString('utf8')
|
|
194
|
+
return prefix === 'v10' || prefix === 'v11'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async extract(): Promise<ExtractedTeamsToken | null> {
|
|
198
|
+
// Extract from Cookies database (works for both New Teams and Classic Teams)
|
|
199
|
+
const cookieToken = await this.extractFromCookiesDB()
|
|
200
|
+
if (cookieToken && this.isValidSkypeToken(cookieToken)) {
|
|
201
|
+
return { token: cookieToken }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private async extractFromCookiesDB(): Promise<string | null> {
|
|
208
|
+
const dbPaths = this.getTeamsCookiesPaths()
|
|
209
|
+
|
|
210
|
+
for (const dbPath of dbPaths) {
|
|
211
|
+
if (!dbPath || !existsSync(dbPath)) continue
|
|
212
|
+
|
|
213
|
+
// Try copy-first strategy to handle file locking
|
|
214
|
+
const token = await this.copyAndExtract(dbPath)
|
|
215
|
+
if (token) return token
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return null
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private async copyAndExtract(dbPath: string): Promise<string | null> {
|
|
222
|
+
const tempPath = join(tmpdir(), `teams-cookies-${Date.now()}`)
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
this.copyDatabaseToTemp(dbPath, tempPath)
|
|
226
|
+
const token = await this.extractFromSQLite(tempPath)
|
|
227
|
+
this.cleanupTempFile(tempPath)
|
|
228
|
+
return token
|
|
229
|
+
} catch {
|
|
230
|
+
// File locked or copy failed
|
|
231
|
+
this.cleanupTempFile(tempPath)
|
|
232
|
+
return null
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private copyDatabaseToTemp(sourcePath: string, destPath: string): string {
|
|
237
|
+
copyFileSync(sourcePath, destPath)
|
|
238
|
+
return destPath
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private cleanupTempFile(tempPath: string): void {
|
|
242
|
+
try {
|
|
243
|
+
if (existsSync(tempPath)) {
|
|
244
|
+
unlinkSync(tempPath)
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
247
|
+
// Ignore cleanup errors
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private async extractFromSQLite(dbPath: string): Promise<string | null> {
|
|
252
|
+
try {
|
|
253
|
+
for (const hostPattern of TEAMS_HOST_PATTERNS) {
|
|
254
|
+
const sql = `
|
|
255
|
+
SELECT encrypted_value
|
|
256
|
+
FROM cookies
|
|
257
|
+
WHERE name = '${SKYPETOKEN_COOKIE_NAME}'
|
|
258
|
+
AND host_key LIKE '%${hostPattern}%'
|
|
259
|
+
LIMIT 1
|
|
260
|
+
`
|
|
261
|
+
|
|
262
|
+
type CookieRow = { encrypted_value?: Uint8Array | Buffer } | null
|
|
263
|
+
|
|
264
|
+
let row: CookieRow
|
|
265
|
+
if (typeof globalThis.Bun !== 'undefined') {
|
|
266
|
+
const { Database } = require('bun:sqlite')
|
|
267
|
+
const db = new Database(dbPath, { readonly: true })
|
|
268
|
+
row = db.query(sql).get() as CookieRow
|
|
269
|
+
db.close()
|
|
270
|
+
} else {
|
|
271
|
+
const Database = require('better-sqlite3')
|
|
272
|
+
const db = new Database(dbPath, { readonly: true })
|
|
273
|
+
row = db.prepare(sql).get() as CookieRow
|
|
274
|
+
db.close()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (row?.encrypted_value) {
|
|
278
|
+
const decrypted = this.decryptCookie(Buffer.from(row.encrypted_value))
|
|
279
|
+
if (decrypted && this.isValidSkypeToken(decrypted)) {
|
|
280
|
+
return decrypted
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return null
|
|
286
|
+
} catch {
|
|
287
|
+
return null
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private decryptCookie(encryptedValue: Buffer): string | null {
|
|
292
|
+
if (!this.isEncryptedValue(encryptedValue)) {
|
|
293
|
+
// Not encrypted, return as-is
|
|
294
|
+
return encryptedValue.toString('utf8')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (this.platform === 'win32') {
|
|
298
|
+
return this.decryptWindowsCookie(encryptedValue)
|
|
299
|
+
} else if (this.platform === 'darwin') {
|
|
300
|
+
return this.decryptMacCookie(encryptedValue)
|
|
301
|
+
} else if (this.platform === 'linux') {
|
|
302
|
+
return this.decryptLinuxCookie(encryptedValue)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return null
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private decryptWindowsCookie(encryptedData: Buffer): string | null {
|
|
309
|
+
try {
|
|
310
|
+
const localStatePath = this.getLocalStatePath()
|
|
311
|
+
if (!existsSync(localStatePath)) return null
|
|
312
|
+
|
|
313
|
+
const localState = JSON.parse(readFileSync(localStatePath, 'utf8'))
|
|
314
|
+
const encryptedKey = Buffer.from(localState.os_crypt.encrypted_key, 'base64')
|
|
315
|
+
|
|
316
|
+
// Remove DPAPI prefix (5 bytes)
|
|
317
|
+
const dpapiBlobKey = encryptedKey.subarray(5)
|
|
318
|
+
const masterKey = this.decryptDPAPI(dpapiBlobKey)
|
|
319
|
+
if (!masterKey) return null
|
|
320
|
+
|
|
321
|
+
return this.decryptAESGCM(encryptedData, masterKey)
|
|
322
|
+
} catch {
|
|
323
|
+
return null
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private decryptDPAPI(encryptedBlob: Buffer): Buffer | null {
|
|
328
|
+
try {
|
|
329
|
+
const b64 = encryptedBlob.toString('base64')
|
|
330
|
+
const psScript = `
|
|
331
|
+
Add-Type -AssemblyName System.Security
|
|
332
|
+
$bytes = [Convert]::FromBase64String('${b64}')
|
|
333
|
+
$decrypted = [Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, 'CurrentUser')
|
|
334
|
+
[Convert]::ToBase64String($decrypted)
|
|
335
|
+
`.replace(/\n/g, ' ')
|
|
336
|
+
|
|
337
|
+
const result = execSync(`powershell -Command "${psScript}"`, { encoding: 'utf8' })
|
|
338
|
+
return Buffer.from(result.trim(), 'base64')
|
|
339
|
+
} catch {
|
|
340
|
+
return null
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private decryptMacCookie(encryptedData: Buffer): string | null {
|
|
345
|
+
const password = this.getKeychainPassword()
|
|
346
|
+
if (!password) return null
|
|
347
|
+
|
|
348
|
+
// Derive key using PBKDF2 (Chromium uses 1003 iterations, 16 byte key for AES-128-CBC)
|
|
349
|
+
const key = pbkdf2Sync(password, 'saltysalt', 1003, 16, 'sha1')
|
|
350
|
+
return this.decryptAESCBC(encryptedData, key)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private decryptLinuxCookie(encryptedData: Buffer): string | null {
|
|
354
|
+
// Linux uses a hardcoded password 'peanuts' for Chromium-based apps
|
|
355
|
+
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
356
|
+
return this.decryptAESCBC(encryptedData, key)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private getKeychainPassword(): string | null {
|
|
360
|
+
const variants = this.getKeychainVariants()
|
|
361
|
+
|
|
362
|
+
for (const variant of variants) {
|
|
363
|
+
const password = this.execSecurityCommand(variant.service, variant.account)
|
|
364
|
+
if (password) return password
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return null
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private execSecurityCommand(service: string, account: string): string | null {
|
|
371
|
+
try {
|
|
372
|
+
// Escape double quotes in service/account to prevent command injection
|
|
373
|
+
const safeService = service.replace(/"/g, '\\"')
|
|
374
|
+
const safeAccount = account.replace(/"/g, '\\"')
|
|
375
|
+
const result = execSync(
|
|
376
|
+
`security find-generic-password -s "${safeService}" -a "${safeAccount}" -w 2>/dev/null`,
|
|
377
|
+
{ encoding: 'utf8' }
|
|
378
|
+
)
|
|
379
|
+
return result.trim()
|
|
380
|
+
} catch {
|
|
381
|
+
return null
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private decryptAESCBC(encryptedData: Buffer, key: Buffer): string | null {
|
|
386
|
+
try {
|
|
387
|
+
const ciphertext = encryptedData.subarray(3)
|
|
388
|
+
const iv = Buffer.alloc(16, 0x20)
|
|
389
|
+
|
|
390
|
+
const decipher = createDecipheriv('aes-128-cbc', key, iv)
|
|
391
|
+
decipher.setAutoPadding(true)
|
|
392
|
+
|
|
393
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
|
394
|
+
const decryptedStr = decrypted.toString('utf8')
|
|
395
|
+
|
|
396
|
+
// Chromium v24+ prepends a 32-byte integrity hash before the actual value
|
|
397
|
+
// Look for JWT token start (eyJ) or other token patterns
|
|
398
|
+
const jwtStart = decryptedStr.indexOf('eyJ')
|
|
399
|
+
if (jwtStart > 0 && jwtStart <= 32) {
|
|
400
|
+
return decryptedStr.substring(jwtStart)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// If no JWT prefix found but decryption succeeded, check if first 32 bytes are garbage
|
|
404
|
+
if (decrypted.length > 32) {
|
|
405
|
+
const possibleToken = decryptedStr.substring(32)
|
|
406
|
+
if (possibleToken.length > 50 && /^[A-Za-z0-9._-]+$/.test(possibleToken.substring(0, 50))) {
|
|
407
|
+
return possibleToken
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return decryptedStr
|
|
412
|
+
} catch {
|
|
413
|
+
return null
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private decryptAESGCM(encryptedData: Buffer, key: Buffer): string | null {
|
|
418
|
+
try {
|
|
419
|
+
// Format: v10 (3 bytes) + IV (12 bytes) + ciphertext + auth tag (16 bytes)
|
|
420
|
+
if (encryptedData.length < 3 + 12 + 16) return null
|
|
421
|
+
|
|
422
|
+
const iv = encryptedData.subarray(3, 15)
|
|
423
|
+
const authTag = encryptedData.subarray(-16)
|
|
424
|
+
const ciphertext = encryptedData.subarray(15, -16)
|
|
425
|
+
|
|
426
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv)
|
|
427
|
+
decipher.setAuthTag(authTag)
|
|
428
|
+
|
|
429
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
|
430
|
+
return decrypted.toString('utf8')
|
|
431
|
+
} catch {
|
|
432
|
+
return null
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async isTeamsRunning(): Promise<boolean> {
|
|
437
|
+
const processName = this.getProcessName()
|
|
438
|
+
return this.checkProcessRunning(processName)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private getProcessName(): string {
|
|
442
|
+
return TEAMS_PROCESS_NAMES[this.platform] || TEAMS_PROCESS_NAMES.linux
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private checkProcessRunning(processName: string): boolean {
|
|
446
|
+
try {
|
|
447
|
+
if (this.platform === 'win32') {
|
|
448
|
+
const result = execSync(`tasklist /FI "IMAGENAME eq ${processName}" 2>nul`, {
|
|
449
|
+
encoding: 'utf8',
|
|
450
|
+
})
|
|
451
|
+
return result.toLowerCase().includes(processName.toLowerCase())
|
|
452
|
+
} else {
|
|
453
|
+
const result = execSync(`pgrep -f "${processName}" 2>/dev/null || true`, {
|
|
454
|
+
encoding: 'utf8',
|
|
455
|
+
})
|
|
456
|
+
return result.trim().length > 0
|
|
457
|
+
}
|
|
458
|
+
} catch {
|
|
459
|
+
return false
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
TeamsChannelSchema,
|
|
4
|
+
TeamsConfigSchema,
|
|
5
|
+
TeamsCredentialsSchema,
|
|
6
|
+
TeamsError,
|
|
7
|
+
TeamsFileSchema,
|
|
8
|
+
TeamsMessageSchema,
|
|
9
|
+
TeamsReactionSchema,
|
|
10
|
+
TeamsTeamSchema,
|
|
11
|
+
TeamsUserSchema,
|
|
12
|
+
} from './types'
|
|
13
|
+
|
|
14
|
+
// TeamsTeamSchema tests
|
|
15
|
+
test('TeamsTeamSchema validates correct team', () => {
|
|
16
|
+
const result = TeamsTeamSchema.safeParse({
|
|
17
|
+
id: '19:abc123@thread.tacv2',
|
|
18
|
+
name: 'Test Team',
|
|
19
|
+
})
|
|
20
|
+
expect(result.success).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('TeamsTeamSchema validates team with optional description', () => {
|
|
24
|
+
const result = TeamsTeamSchema.safeParse({
|
|
25
|
+
id: '19:abc123@thread.tacv2',
|
|
26
|
+
name: 'Test Team',
|
|
27
|
+
description: 'A test team',
|
|
28
|
+
})
|
|
29
|
+
expect(result.success).toBe(true)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('TeamsTeamSchema rejects missing id', () => {
|
|
33
|
+
const result = TeamsTeamSchema.safeParse({
|
|
34
|
+
name: 'Test Team',
|
|
35
|
+
})
|
|
36
|
+
expect(result.success).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('TeamsTeamSchema rejects missing name', () => {
|
|
40
|
+
const result = TeamsTeamSchema.safeParse({
|
|
41
|
+
id: '19:abc123@thread.tacv2',
|
|
42
|
+
})
|
|
43
|
+
expect(result.success).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// TeamsChannelSchema tests
|
|
47
|
+
test('TeamsChannelSchema validates correct channel', () => {
|
|
48
|
+
const result = TeamsChannelSchema.safeParse({
|
|
49
|
+
id: '19:channel123@thread.tacv2',
|
|
50
|
+
team_id: '19:abc123@thread.tacv2',
|
|
51
|
+
name: 'General',
|
|
52
|
+
type: 'standard',
|
|
53
|
+
})
|
|
54
|
+
expect(result.success).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('TeamsChannelSchema rejects missing required fields', () => {
|
|
58
|
+
const result = TeamsChannelSchema.safeParse({
|
|
59
|
+
id: '19:channel123@thread.tacv2',
|
|
60
|
+
name: 'General',
|
|
61
|
+
})
|
|
62
|
+
expect(result.success).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// TeamsMessageSchema tests
|
|
66
|
+
test('TeamsMessageSchema validates correct message', () => {
|
|
67
|
+
const result = TeamsMessageSchema.safeParse({
|
|
68
|
+
id: '1234567890123',
|
|
69
|
+
channel_id: '19:channel123@thread.tacv2',
|
|
70
|
+
author: {
|
|
71
|
+
id: 'user123',
|
|
72
|
+
displayName: 'Test User',
|
|
73
|
+
},
|
|
74
|
+
content: 'Hello world',
|
|
75
|
+
timestamp: '2024-01-01T00:00:00.000Z',
|
|
76
|
+
})
|
|
77
|
+
expect(result.success).toBe(true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('TeamsMessageSchema rejects missing required fields', () => {
|
|
81
|
+
const result = TeamsMessageSchema.safeParse({
|
|
82
|
+
id: '1234567890123',
|
|
83
|
+
channel_id: '19:channel123@thread.tacv2',
|
|
84
|
+
content: 'Hello world',
|
|
85
|
+
})
|
|
86
|
+
expect(result.success).toBe(false)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// TeamsUserSchema tests
|
|
90
|
+
test('TeamsUserSchema validates correct user', () => {
|
|
91
|
+
const result = TeamsUserSchema.safeParse({
|
|
92
|
+
id: 'user123',
|
|
93
|
+
displayName: 'Test User',
|
|
94
|
+
})
|
|
95
|
+
expect(result.success).toBe(true)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('TeamsUserSchema validates user with optional fields', () => {
|
|
99
|
+
const result = TeamsUserSchema.safeParse({
|
|
100
|
+
id: 'user123',
|
|
101
|
+
displayName: 'Test User',
|
|
102
|
+
email: 'test@example.com',
|
|
103
|
+
userPrincipalName: 'test@example.onmicrosoft.com',
|
|
104
|
+
})
|
|
105
|
+
expect(result.success).toBe(true)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('TeamsUserSchema rejects missing required fields', () => {
|
|
109
|
+
const result = TeamsUserSchema.safeParse({
|
|
110
|
+
id: 'user123',
|
|
111
|
+
})
|
|
112
|
+
expect(result.success).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// TeamsReactionSchema tests
|
|
116
|
+
test('TeamsReactionSchema validates correct reaction', () => {
|
|
117
|
+
const result = TeamsReactionSchema.safeParse({
|
|
118
|
+
emoji: 'like',
|
|
119
|
+
count: 5,
|
|
120
|
+
})
|
|
121
|
+
expect(result.success).toBe(true)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('TeamsReactionSchema rejects missing required fields', () => {
|
|
125
|
+
const result = TeamsReactionSchema.safeParse({
|
|
126
|
+
emoji: 'like',
|
|
127
|
+
})
|
|
128
|
+
expect(result.success).toBe(false)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// TeamsFileSchema tests
|
|
132
|
+
test('TeamsFileSchema validates correct file', () => {
|
|
133
|
+
const result = TeamsFileSchema.safeParse({
|
|
134
|
+
id: 'file123',
|
|
135
|
+
name: 'document.pdf',
|
|
136
|
+
size: 1024,
|
|
137
|
+
url: 'https://teams.microsoft.com/files/...',
|
|
138
|
+
})
|
|
139
|
+
expect(result.success).toBe(true)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('TeamsFileSchema validates file with optional contentType', () => {
|
|
143
|
+
const result = TeamsFileSchema.safeParse({
|
|
144
|
+
id: 'file123',
|
|
145
|
+
name: 'document.pdf',
|
|
146
|
+
size: 1024,
|
|
147
|
+
url: 'https://teams.microsoft.com/files/...',
|
|
148
|
+
contentType: 'application/pdf',
|
|
149
|
+
})
|
|
150
|
+
expect(result.success).toBe(true)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('TeamsFileSchema rejects missing required fields', () => {
|
|
154
|
+
const result = TeamsFileSchema.safeParse({
|
|
155
|
+
id: 'file123',
|
|
156
|
+
name: 'document.pdf',
|
|
157
|
+
})
|
|
158
|
+
expect(result.success).toBe(false)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// TeamsCredentialsSchema tests
|
|
162
|
+
test('TeamsCredentialsSchema validates correct credentials', () => {
|
|
163
|
+
const result = TeamsCredentialsSchema.safeParse({
|
|
164
|
+
token: 'skypetoken_value',
|
|
165
|
+
})
|
|
166
|
+
expect(result.success).toBe(true)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('TeamsCredentialsSchema validates credentials with cookie', () => {
|
|
170
|
+
const result = TeamsCredentialsSchema.safeParse({
|
|
171
|
+
token: 'skypetoken_value',
|
|
172
|
+
cookie: 'skypetoken_asm_value',
|
|
173
|
+
})
|
|
174
|
+
expect(result.success).toBe(true)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test('TeamsCredentialsSchema rejects missing token', () => {
|
|
178
|
+
const result = TeamsCredentialsSchema.safeParse({})
|
|
179
|
+
expect(result.success).toBe(false)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// TeamsConfigSchema tests
|
|
183
|
+
test('TeamsConfigSchema validates correct config', () => {
|
|
184
|
+
const result = TeamsConfigSchema.safeParse({
|
|
185
|
+
current_team: null,
|
|
186
|
+
token: 'token_value',
|
|
187
|
+
teams: {},
|
|
188
|
+
})
|
|
189
|
+
expect(result.success).toBe(true)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('TeamsConfigSchema validates config with token_expires_at', () => {
|
|
193
|
+
const result = TeamsConfigSchema.safeParse({
|
|
194
|
+
current_team: '19:abc123@thread.tacv2',
|
|
195
|
+
token: 'token_value',
|
|
196
|
+
token_expires_at: '2024-01-01T00:00:00.000Z',
|
|
197
|
+
teams: {
|
|
198
|
+
'19:abc123@thread.tacv2': {
|
|
199
|
+
team_id: '19:abc123@thread.tacv2',
|
|
200
|
+
team_name: 'Test Team',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
expect(result.success).toBe(true)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('TeamsConfigSchema rejects missing required fields', () => {
|
|
208
|
+
const result = TeamsConfigSchema.safeParse({
|
|
209
|
+
current_team: null,
|
|
210
|
+
token: 'token_value',
|
|
211
|
+
})
|
|
212
|
+
expect(result.success).toBe(false)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// TeamsError tests
|
|
216
|
+
test('TeamsError has correct name and code', () => {
|
|
217
|
+
const error = new TeamsError('Test error', 'TEST_CODE')
|
|
218
|
+
expect(error.name).toBe('TeamsError')
|
|
219
|
+
expect(error.message).toBe('Test error')
|
|
220
|
+
expect(error.code).toBe('TEST_CODE')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test('TeamsError is instance of Error', () => {
|
|
224
|
+
const error = new TeamsError('Test error', 'TEST_CODE')
|
|
225
|
+
expect(error instanceof Error).toBe(true)
|
|
226
|
+
})
|