agent-messenger 2.9.0 → 2.10.1
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-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/teams/client.d.ts +9 -1
- package/dist/src/platforms/teams/client.d.ts.map +1 -1
- package/dist/src/platforms/teams/client.js +69 -18
- package/dist/src/platforms/teams/client.js.map +1 -1
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/auth.js +7 -2
- package/dist/src/platforms/teams/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/channel.js +18 -3
- package/dist/src/platforms/teams/commands/channel.js.map +1 -1
- package/dist/src/platforms/teams/commands/file.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/file.js +18 -3
- package/dist/src/platforms/teams/commands/file.js.map +1 -1
- package/dist/src/platforms/teams/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/message.js +24 -4
- package/dist/src/platforms/teams/commands/message.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +12 -2
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/snapshot.js +6 -1
- package/dist/src/platforms/teams/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/teams/commands/team.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/team.js +6 -1
- package/dist/src/platforms/teams/commands/team.js.map +1 -1
- package/dist/src/platforms/teams/commands/user.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/user.js +18 -3
- package/dist/src/platforms/teams/commands/user.js.map +1 -1
- package/dist/src/platforms/teams/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/whoami.js +6 -1
- package/dist/src/platforms/teams/commands/whoami.js.map +1 -1
- package/dist/src/platforms/teams/credential-manager.d.ts +3 -1
- package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/teams/credential-manager.js +6 -1
- package/dist/src/platforms/teams/credential-manager.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.js +7 -2
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts +3 -1
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +73 -10
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/types.d.ts +17 -0
- package/dist/src/platforms/teams/types.d.ts.map +1 -1
- package/dist/src/platforms/teams/types.js +2 -0
- package/dist/src/platforms/teams/types.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +3 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +58 -13
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +61 -10
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/webex/credential-manager.js +18 -6
- package/dist/src/platforms/webex/credential-manager.js.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -1
- package/dist/src/platforms/webex/encryption.js +3 -1
- package/dist/src/platforms/webex/encryption.js.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +10 -2
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +1 -0
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/token-extractor.js +21 -4
- package/dist/src/platforms/webex/token-extractor.js.map +1 -1
- package/e2e/webex.e2e.test.ts +57 -0
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/teams/client.test.ts +34 -30
- package/src/platforms/teams/client.ts +92 -20
- package/src/platforms/teams/commands/auth.test.ts +6 -2
- package/src/platforms/teams/commands/auth.ts +7 -2
- package/src/platforms/teams/commands/channel.test.ts +6 -6
- package/src/platforms/teams/commands/channel.ts +18 -3
- package/src/platforms/teams/commands/file.ts +18 -3
- package/src/platforms/teams/commands/message.ts +24 -4
- package/src/platforms/teams/commands/reaction.ts +12 -2
- package/src/platforms/teams/commands/snapshot.ts +6 -1
- package/src/platforms/teams/commands/team.test.ts +2 -2
- package/src/platforms/teams/commands/team.ts +6 -1
- package/src/platforms/teams/commands/user.ts +18 -3
- package/src/platforms/teams/commands/whoami.ts +6 -1
- package/src/platforms/teams/credential-manager.test.ts +25 -0
- package/src/platforms/teams/credential-manager.ts +13 -3
- package/src/platforms/teams/ensure-auth.test.ts +6 -1
- package/src/platforms/teams/ensure-auth.ts +7 -2
- package/src/platforms/teams/token-extractor.test.ts +112 -98
- package/src/platforms/teams/token-extractor.ts +83 -12
- package/src/platforms/teams/types.test.ts +17 -0
- package/src/platforms/teams/types.ts +6 -0
- package/src/platforms/webex/client.test.ts +157 -13
- package/src/platforms/webex/client.ts +64 -15
- package/src/platforms/webex/commands/auth.test.ts +122 -1
- package/src/platforms/webex/commands/auth.ts +72 -17
- package/src/platforms/webex/credential-manager.test.ts +63 -0
- package/src/platforms/webex/credential-manager.ts +22 -8
- package/src/platforms/webex/encryption.test.ts +54 -0
- package/src/platforms/webex/encryption.ts +3 -1
- package/src/platforms/webex/ensure-auth.ts +10 -2
- package/src/platforms/webex/token-extractor.test.ts +32 -3
- package/src/platforms/webex/token-extractor.ts +26 -5
|
@@ -16,7 +16,12 @@ export async function listAction(teamId: string, options: { pretty?: boolean }):
|
|
|
16
16
|
process.exit(1)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const client = await new TeamsClient().login({
|
|
19
|
+
const client = await new TeamsClient().login({
|
|
20
|
+
token: cred.token,
|
|
21
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
22
|
+
accountType: cred.accountType,
|
|
23
|
+
region: cred.region,
|
|
24
|
+
})
|
|
20
25
|
const channels = await client.listChannels(teamId)
|
|
21
26
|
|
|
22
27
|
const output = channels.map((ch) => ({
|
|
@@ -42,7 +47,12 @@ export async function infoAction(teamId: string, channelId: string, options: { p
|
|
|
42
47
|
process.exit(1)
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const client = await new TeamsClient().login({
|
|
50
|
+
const client = await new TeamsClient().login({
|
|
51
|
+
token: cred.token,
|
|
52
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
53
|
+
accountType: cred.accountType,
|
|
54
|
+
region: cred.region,
|
|
55
|
+
})
|
|
46
56
|
const channel = await client.getChannel(teamId, channelId)
|
|
47
57
|
|
|
48
58
|
const output = {
|
|
@@ -72,7 +82,12 @@ export async function historyAction(
|
|
|
72
82
|
process.exit(1)
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
const client = await new TeamsClient().login({
|
|
85
|
+
const client = await new TeamsClient().login({
|
|
86
|
+
token: cred.token,
|
|
87
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
88
|
+
accountType: cred.accountType,
|
|
89
|
+
region: cred.region,
|
|
90
|
+
})
|
|
76
91
|
const messages = await client.getMessages(teamId, channelId, options.limit || 50)
|
|
77
92
|
|
|
78
93
|
const output = messages.map((msg) => ({
|
|
@@ -24,7 +24,12 @@ export async function uploadAction(
|
|
|
24
24
|
process.exit(1)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const client = await new TeamsClient().login({
|
|
27
|
+
const client = await new TeamsClient().login({
|
|
28
|
+
token: cred.token,
|
|
29
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
30
|
+
accountType: cred.accountType,
|
|
31
|
+
region: cred.region,
|
|
32
|
+
})
|
|
28
33
|
const filePath = resolve(path)
|
|
29
34
|
const file = await client.uploadFile(teamId, channelId, filePath)
|
|
30
35
|
|
|
@@ -52,7 +57,12 @@ export async function listAction(teamId: string, channelId: string, options: { p
|
|
|
52
57
|
process.exit(1)
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
const client = await new TeamsClient().login({
|
|
60
|
+
const client = await new TeamsClient().login({
|
|
61
|
+
token: cred.token,
|
|
62
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
63
|
+
accountType: cred.accountType,
|
|
64
|
+
region: cred.region,
|
|
65
|
+
})
|
|
56
66
|
const files = await client.listFiles(teamId, channelId)
|
|
57
67
|
|
|
58
68
|
const output = files.map((file: TeamsFile) => ({
|
|
@@ -84,7 +94,12 @@ export async function infoAction(
|
|
|
84
94
|
process.exit(1)
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
const client = await new TeamsClient().login({
|
|
97
|
+
const client = await new TeamsClient().login({
|
|
98
|
+
token: cred.token,
|
|
99
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
100
|
+
accountType: cred.accountType,
|
|
101
|
+
region: cred.region,
|
|
102
|
+
})
|
|
88
103
|
const files = await client.listFiles(teamId, channelId)
|
|
89
104
|
const fileData = files.find((f) => f.id === fileId)
|
|
90
105
|
|
|
@@ -22,7 +22,12 @@ export async function sendAction(
|
|
|
22
22
|
process.exit(1)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const client = await new TeamsClient().login({
|
|
25
|
+
const client = await new TeamsClient().login({
|
|
26
|
+
token: cred.token,
|
|
27
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
28
|
+
accountType: cred.accountType,
|
|
29
|
+
region: cred.region,
|
|
30
|
+
})
|
|
26
31
|
const message = await client.sendMessage(teamId, channelId, content)
|
|
27
32
|
|
|
28
33
|
const output = {
|
|
@@ -52,7 +57,12 @@ export async function listAction(
|
|
|
52
57
|
process.exit(1)
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
const client = await new TeamsClient().login({
|
|
60
|
+
const client = await new TeamsClient().login({
|
|
61
|
+
token: cred.token,
|
|
62
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
63
|
+
accountType: cred.accountType,
|
|
64
|
+
region: cred.region,
|
|
65
|
+
})
|
|
56
66
|
const limit = options.limit || 50
|
|
57
67
|
const messages = await client.getMessages(teamId, channelId, limit)
|
|
58
68
|
|
|
@@ -84,7 +94,12 @@ export async function getAction(
|
|
|
84
94
|
process.exit(1)
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
const client = await new TeamsClient().login({
|
|
97
|
+
const client = await new TeamsClient().login({
|
|
98
|
+
token: cred.token,
|
|
99
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
100
|
+
accountType: cred.accountType,
|
|
101
|
+
region: cred.region,
|
|
102
|
+
})
|
|
88
103
|
const message = await client.getMessage(teamId, channelId, messageId)
|
|
89
104
|
|
|
90
105
|
if (!message) {
|
|
@@ -125,7 +140,12 @@ export async function deleteAction(
|
|
|
125
140
|
process.exit(0)
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
const client = await new TeamsClient().login({
|
|
143
|
+
const client = await new TeamsClient().login({
|
|
144
|
+
token: cred.token,
|
|
145
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
146
|
+
accountType: cred.accountType,
|
|
147
|
+
region: cred.region,
|
|
148
|
+
})
|
|
129
149
|
await client.deleteMessage(teamId, channelId, messageId)
|
|
130
150
|
|
|
131
151
|
console.log(formatOutput({ deleted: messageId }, options.pretty))
|
|
@@ -23,7 +23,12 @@ export async function addAction(
|
|
|
23
23
|
return
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const client = await new TeamsClient().login({
|
|
26
|
+
const client = await new TeamsClient().login({
|
|
27
|
+
token: cred.token,
|
|
28
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
29
|
+
accountType: cred.accountType,
|
|
30
|
+
region: cred.region,
|
|
31
|
+
})
|
|
27
32
|
await client.addReaction(teamId, channelId, messageId, emoji)
|
|
28
33
|
|
|
29
34
|
console.log(
|
|
@@ -60,7 +65,12 @@ export async function removeAction(
|
|
|
60
65
|
return
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
const client = await new TeamsClient().login({
|
|
68
|
+
const client = await new TeamsClient().login({
|
|
69
|
+
token: cred.token,
|
|
70
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
71
|
+
accountType: cred.accountType,
|
|
72
|
+
region: cred.region,
|
|
73
|
+
})
|
|
64
74
|
await client.removeReaction(teamId, channelId, messageId, emoji)
|
|
65
75
|
|
|
66
76
|
console.log(
|
|
@@ -34,7 +34,12 @@ export async function snapshotAction(options: {
|
|
|
34
34
|
process.exit(1)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const client = await new TeamsClient().login({
|
|
37
|
+
const client = await new TeamsClient().login({
|
|
38
|
+
token: cred.token,
|
|
39
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
40
|
+
accountType: cred.accountType,
|
|
41
|
+
region: cred.region,
|
|
42
|
+
})
|
|
38
43
|
|
|
39
44
|
const snapshot: Record<string, unknown> = {}
|
|
40
45
|
|
|
@@ -90,7 +90,7 @@ test('list: marks current team', async () => {
|
|
|
90
90
|
|
|
91
91
|
test('info: returns team details', async () => {
|
|
92
92
|
// given: teams client with team data
|
|
93
|
-
const client = await new TeamsClient().login({ token: 'test-token' })
|
|
93
|
+
const client = await new TeamsClient().login({ token: 'test-token', region: 'emea' })
|
|
94
94
|
const team = await client.getTeam('team-1')
|
|
95
95
|
|
|
96
96
|
// when: getting team info
|
|
@@ -104,7 +104,7 @@ test('info: returns team details', async () => {
|
|
|
104
104
|
|
|
105
105
|
test('info: throws error for non-existent team', async () => {
|
|
106
106
|
// given: teams client
|
|
107
|
-
const client = await new TeamsClient().login({ token: 'test-token' })
|
|
107
|
+
const client = await new TeamsClient().login({ token: 'test-token', region: 'emea' })
|
|
108
108
|
|
|
109
109
|
// when: getting non-existent team
|
|
110
110
|
// then: error is thrown
|
|
@@ -34,7 +34,12 @@ export async function infoAction(teamId: string, options: { pretty?: boolean }):
|
|
|
34
34
|
process.exit(1)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const client = await new TeamsClient().login({
|
|
37
|
+
const client = await new TeamsClient().login({
|
|
38
|
+
token: cred.token,
|
|
39
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
40
|
+
accountType: cred.accountType,
|
|
41
|
+
region: cred.region,
|
|
42
|
+
})
|
|
38
43
|
const team = await client.getTeam(teamId)
|
|
39
44
|
|
|
40
45
|
const output = {
|
|
@@ -16,7 +16,12 @@ async function listAction(teamId: string, options: { pretty?: boolean }): Promis
|
|
|
16
16
|
process.exit(1)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const client = await new TeamsClient().login({
|
|
19
|
+
const client = await new TeamsClient().login({
|
|
20
|
+
token: cred.token,
|
|
21
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
22
|
+
accountType: cred.accountType,
|
|
23
|
+
region: cred.region,
|
|
24
|
+
})
|
|
20
25
|
const users = await client.listUsers(teamId)
|
|
21
26
|
|
|
22
27
|
const output = users.map((user) => ({
|
|
@@ -42,7 +47,12 @@ async function infoAction(userId: string, options: { pretty?: boolean }): Promis
|
|
|
42
47
|
process.exit(1)
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const client = await new TeamsClient().login({
|
|
50
|
+
const client = await new TeamsClient().login({
|
|
51
|
+
token: cred.token,
|
|
52
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
53
|
+
accountType: cred.accountType,
|
|
54
|
+
region: cred.region,
|
|
55
|
+
})
|
|
46
56
|
const user = await client.getUser(userId)
|
|
47
57
|
|
|
48
58
|
const output = {
|
|
@@ -68,7 +78,12 @@ async function meAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
68
78
|
process.exit(1)
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
const client = await new TeamsClient().login({
|
|
81
|
+
const client = await new TeamsClient().login({
|
|
82
|
+
token: cred.token,
|
|
83
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
84
|
+
accountType: cred.accountType,
|
|
85
|
+
region: cred.region,
|
|
86
|
+
})
|
|
72
87
|
const user = await client.testAuth()
|
|
73
88
|
|
|
74
89
|
const output = {
|
|
@@ -16,7 +16,12 @@ export async function whoamiAction(options: { pretty?: boolean }): Promise<void>
|
|
|
16
16
|
return process.exit(1)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const client = await new TeamsClient().login({
|
|
19
|
+
const client = await new TeamsClient().login({
|
|
20
|
+
token: cred.token,
|
|
21
|
+
tokenExpiresAt: cred.tokenExpiresAt,
|
|
22
|
+
accountType: cred.accountType,
|
|
23
|
+
region: cred.region,
|
|
24
|
+
})
|
|
20
25
|
const user = await client.testAuth()
|
|
21
26
|
|
|
22
27
|
const output = {
|
|
@@ -88,6 +88,31 @@ describe('TeamsCredentialManager', () => {
|
|
|
88
88
|
expect(team).toBeNull()
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
test('getTokenWithExpiry includes region', async () => {
|
|
92
|
+
const manager = setup()
|
|
93
|
+
await manager.saveConfig({
|
|
94
|
+
current_account: 'work',
|
|
95
|
+
accounts: {
|
|
96
|
+
work: {
|
|
97
|
+
token: 'test-token',
|
|
98
|
+
token_expires_at: '2025-12-31T23:59:59Z',
|
|
99
|
+
region: 'emea',
|
|
100
|
+
account_type: 'work',
|
|
101
|
+
current_team: null,
|
|
102
|
+
teams: {},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const token = await manager.getTokenWithExpiry()
|
|
108
|
+
expect(token).toEqual({
|
|
109
|
+
token: 'test-token',
|
|
110
|
+
tokenExpiresAt: '2025-12-31T23:59:59Z',
|
|
111
|
+
accountType: 'work',
|
|
112
|
+
region: 'emea',
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
91
116
|
test('getCurrentTeam returns null when current_team is set but team not in teams record', async () => {
|
|
92
117
|
const manager = setup()
|
|
93
118
|
await manager.setToken('test-token', 'work')
|
|
@@ -3,7 +3,7 @@ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
|
3
3
|
import { homedir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
|
|
6
|
-
import type { TeamsAccount, TeamsAccountType, TeamsConfig, TeamsConfigLegacy } from './types'
|
|
6
|
+
import type { TeamsAccount, TeamsAccountType, TeamsConfig, TeamsConfigLegacy, TeamsRegion } from './types'
|
|
7
7
|
|
|
8
8
|
export class TeamsCredentialManager {
|
|
9
9
|
static accountOverride?: TeamsAccountType
|
|
@@ -78,12 +78,22 @@ export class TeamsCredentialManager {
|
|
|
78
78
|
return this.resolveCurrentAccount(config)?.token ?? null
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
async getTokenWithExpiry(): Promise<{
|
|
81
|
+
async getTokenWithExpiry(): Promise<{
|
|
82
|
+
token: string
|
|
83
|
+
tokenExpiresAt?: string
|
|
84
|
+
accountType?: TeamsAccountType
|
|
85
|
+
region?: TeamsRegion
|
|
86
|
+
} | null> {
|
|
82
87
|
const config = await this.loadConfig()
|
|
83
88
|
if (!config) return null
|
|
84
89
|
const account = this.resolveCurrentAccount(config)
|
|
85
90
|
if (!account?.token) return null
|
|
86
|
-
return {
|
|
91
|
+
return {
|
|
92
|
+
token: account.token,
|
|
93
|
+
tokenExpiresAt: account.token_expires_at,
|
|
94
|
+
accountType: account.account_type,
|
|
95
|
+
region: account.region,
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
async setToken(token: string, accountType: TeamsAccountType, expiresAt?: string): Promise<void> {
|
|
@@ -10,6 +10,7 @@ let extractSpy: ReturnType<typeof spyOn>
|
|
|
10
10
|
let testAuthSpy: ReturnType<typeof spyOn>
|
|
11
11
|
let listTeamsSpy: ReturnType<typeof spyOn>
|
|
12
12
|
let saveConfigSpy: ReturnType<typeof spyOn>
|
|
13
|
+
let getRegionSpy: ReturnType<typeof spyOn>
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
15
16
|
loadConfigSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue(null)
|
|
@@ -28,6 +29,8 @@ beforeEach(() => {
|
|
|
28
29
|
{ id: 'team-2', name: 'Team Two' },
|
|
29
30
|
])
|
|
30
31
|
|
|
32
|
+
getRegionSpy = spyOn(TeamsClient.prototype, 'getRegion').mockReturnValue('emea')
|
|
33
|
+
|
|
31
34
|
saveConfigSpy = spyOn(TeamsCredentialManager.prototype, 'saveConfig').mockResolvedValue(undefined)
|
|
32
35
|
})
|
|
33
36
|
|
|
@@ -37,6 +40,7 @@ afterEach(() => {
|
|
|
37
40
|
testAuthSpy?.mockRestore()
|
|
38
41
|
listTeamsSpy?.mockRestore()
|
|
39
42
|
saveConfigSpy?.mockRestore()
|
|
43
|
+
getRegionSpy?.mockRestore()
|
|
40
44
|
})
|
|
41
45
|
|
|
42
46
|
describe('ensureTeamsAuth', () => {
|
|
@@ -78,6 +82,7 @@ describe('ensureTeamsAuth', () => {
|
|
|
78
82
|
accounts: expect.objectContaining({
|
|
79
83
|
work: expect.objectContaining({
|
|
80
84
|
token: 'test-teams-token',
|
|
85
|
+
region: 'emea',
|
|
81
86
|
current_team: 'team-1',
|
|
82
87
|
teams: {
|
|
83
88
|
'team-1': { team_id: 'team-1', team_name: 'Team One' },
|
|
@@ -215,7 +220,7 @@ describe('ensureTeamsAuth', () => {
|
|
|
215
220
|
current_account: 'work',
|
|
216
221
|
accounts: expect.objectContaining({
|
|
217
222
|
work: expect.objectContaining({ token: 'work-token', current_team: 'team-w' }),
|
|
218
|
-
personal: expect.objectContaining({ token: 'personal-token', current_team: 'team-p' }),
|
|
223
|
+
personal: expect.objectContaining({ token: 'personal-token', region: 'emea', current_team: 'team-p' }),
|
|
219
224
|
}),
|
|
220
225
|
}),
|
|
221
226
|
)
|
|
@@ -23,11 +23,15 @@ export async function ensureTeamsAuth(): Promise<void> {
|
|
|
23
23
|
|
|
24
24
|
for (const { token, accountType } of extracted) {
|
|
25
25
|
try {
|
|
26
|
-
const client = await new TeamsClient().login({
|
|
26
|
+
const client = await new TeamsClient().login({
|
|
27
|
+
token,
|
|
28
|
+
accountType,
|
|
29
|
+
region: config?.accounts[accountType]?.region,
|
|
30
|
+
})
|
|
27
31
|
await client.testAuth()
|
|
28
32
|
|
|
29
33
|
const teams = await client.listTeams()
|
|
30
|
-
if (teams.length === 0) continue
|
|
34
|
+
if (accountType !== 'personal' && teams.length === 0) continue
|
|
31
35
|
|
|
32
36
|
const teamMap: Record<string, { team_id: string; team_name: string }> = {}
|
|
33
37
|
for (const team of teams) {
|
|
@@ -38,6 +42,7 @@ export async function ensureTeamsAuth(): Promise<void> {
|
|
|
38
42
|
const account: TeamsAccount = {
|
|
39
43
|
token,
|
|
40
44
|
token_expires_at: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
45
|
+
region: client.getRegion(),
|
|
41
46
|
account_type: accountType,
|
|
42
47
|
user_name: existing?.user_name,
|
|
43
48
|
current_team: existing?.current_team ?? teams[0].id,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, spyOn, test } from 'bun:test'
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { homedir, tmpdir } from 'node:os'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
|
|
5
6
|
import { TeamsTokenExtractor } from './token-extractor'
|
|
@@ -16,58 +17,25 @@ describe('TeamsTokenExtractor', () => {
|
|
|
16
17
|
const darwinExtractor = new TeamsTokenExtractor('darwin')
|
|
17
18
|
const paths = darwinExtractor.getDesktopCookiesPaths()
|
|
18
19
|
|
|
20
|
+
const darwinEbWebView = join(
|
|
21
|
+
homedir(),
|
|
22
|
+
'Library',
|
|
23
|
+
'Containers',
|
|
24
|
+
'com.microsoft.teams2',
|
|
25
|
+
'Data',
|
|
26
|
+
'Library',
|
|
27
|
+
'Application Support',
|
|
28
|
+
'Microsoft',
|
|
29
|
+
'MSTeams',
|
|
30
|
+
'EBWebView',
|
|
31
|
+
)
|
|
19
32
|
expect(paths).toEqual([
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'Data',
|
|
27
|
-
'Library',
|
|
28
|
-
'Application Support',
|
|
29
|
-
'Microsoft',
|
|
30
|
-
'MSTeams',
|
|
31
|
-
'EBWebView',
|
|
32
|
-
'WV2Profile_tfw',
|
|
33
|
-
'Cookies',
|
|
34
|
-
),
|
|
35
|
-
accountType: 'work',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
path: join(
|
|
39
|
-
homedir(),
|
|
40
|
-
'Library',
|
|
41
|
-
'Containers',
|
|
42
|
-
'com.microsoft.teams2',
|
|
43
|
-
'Data',
|
|
44
|
-
'Library',
|
|
45
|
-
'Application Support',
|
|
46
|
-
'Microsoft',
|
|
47
|
-
'MSTeams',
|
|
48
|
-
'EBWebView',
|
|
49
|
-
'WV2Profile_tfl',
|
|
50
|
-
'Cookies',
|
|
51
|
-
),
|
|
52
|
-
accountType: 'personal',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
path: join(
|
|
56
|
-
homedir(),
|
|
57
|
-
'Library',
|
|
58
|
-
'Containers',
|
|
59
|
-
'com.microsoft.teams2',
|
|
60
|
-
'Data',
|
|
61
|
-
'Library',
|
|
62
|
-
'Application Support',
|
|
63
|
-
'Microsoft',
|
|
64
|
-
'MSTeams',
|
|
65
|
-
'EBWebView',
|
|
66
|
-
'Default',
|
|
67
|
-
'Cookies',
|
|
68
|
-
),
|
|
69
|
-
accountType: 'work',
|
|
70
|
-
},
|
|
33
|
+
{ path: join(darwinEbWebView, 'WV2Profile_tfw', 'Cookies'), accountType: 'work' },
|
|
34
|
+
{ path: join(darwinEbWebView, 'WV2Profile_tfw', 'Network', 'Cookies'), accountType: 'work' },
|
|
35
|
+
{ path: join(darwinEbWebView, 'WV2Profile_tfl', 'Cookies'), accountType: 'personal' },
|
|
36
|
+
{ path: join(darwinEbWebView, 'WV2Profile_tfl', 'Network', 'Cookies'), accountType: 'personal' },
|
|
37
|
+
{ path: join(darwinEbWebView, 'Default', 'Cookies'), accountType: 'work' },
|
|
38
|
+
{ path: join(darwinEbWebView, 'Default', 'Network', 'Cookies'), accountType: 'work' },
|
|
71
39
|
{
|
|
72
40
|
path: join(homedir(), 'Library', 'Application Support', 'Microsoft', 'Teams', 'Cookies'),
|
|
73
41
|
accountType: 'work',
|
|
@@ -93,53 +61,23 @@ describe('TeamsTokenExtractor', () => {
|
|
|
93
61
|
|
|
94
62
|
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
95
63
|
const appdata = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')
|
|
64
|
+
const winEbWebView = join(
|
|
65
|
+
localAppData,
|
|
66
|
+
'Packages',
|
|
67
|
+
'MSTeams_8wekyb3d8bbwe',
|
|
68
|
+
'LocalCache',
|
|
69
|
+
'Microsoft',
|
|
70
|
+
'MSTeams',
|
|
71
|
+
'EBWebView',
|
|
72
|
+
)
|
|
96
73
|
expect(paths).toEqual([
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
'MSTeams',
|
|
105
|
-
'EBWebView',
|
|
106
|
-
'WV2Profile_tfw',
|
|
107
|
-
'Cookies',
|
|
108
|
-
),
|
|
109
|
-
accountType: 'work',
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
path: join(
|
|
113
|
-
localAppData,
|
|
114
|
-
'Packages',
|
|
115
|
-
'MSTeams_8wekyb3d8bbwe',
|
|
116
|
-
'LocalCache',
|
|
117
|
-
'Microsoft',
|
|
118
|
-
'MSTeams',
|
|
119
|
-
'EBWebView',
|
|
120
|
-
'WV2Profile_tfl',
|
|
121
|
-
'Cookies',
|
|
122
|
-
),
|
|
123
|
-
accountType: 'personal',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
path: join(
|
|
127
|
-
localAppData,
|
|
128
|
-
'Packages',
|
|
129
|
-
'MSTeams_8wekyb3d8bbwe',
|
|
130
|
-
'LocalCache',
|
|
131
|
-
'Microsoft',
|
|
132
|
-
'MSTeams',
|
|
133
|
-
'EBWebView',
|
|
134
|
-
'Default',
|
|
135
|
-
'Cookies',
|
|
136
|
-
),
|
|
137
|
-
accountType: 'work',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
path: join(appdata, 'Microsoft', 'Teams', 'Cookies'),
|
|
141
|
-
accountType: 'work',
|
|
142
|
-
},
|
|
74
|
+
{ path: join(winEbWebView, 'WV2Profile_tfw', 'Cookies'), accountType: 'work' },
|
|
75
|
+
{ path: join(winEbWebView, 'WV2Profile_tfw', 'Network', 'Cookies'), accountType: 'work' },
|
|
76
|
+
{ path: join(winEbWebView, 'WV2Profile_tfl', 'Cookies'), accountType: 'personal' },
|
|
77
|
+
{ path: join(winEbWebView, 'WV2Profile_tfl', 'Network', 'Cookies'), accountType: 'personal' },
|
|
78
|
+
{ path: join(winEbWebView, 'Default', 'Cookies'), accountType: 'work' },
|
|
79
|
+
{ path: join(winEbWebView, 'Default', 'Network', 'Cookies'), accountType: 'work' },
|
|
80
|
+
{ path: join(appdata, 'Microsoft', 'Teams', 'Cookies'), accountType: 'work' },
|
|
143
81
|
])
|
|
144
82
|
})
|
|
145
83
|
|
|
@@ -400,6 +338,82 @@ describe('TeamsTokenExtractor', () => {
|
|
|
400
338
|
})
|
|
401
339
|
})
|
|
402
340
|
|
|
341
|
+
describe('extractFromCookiesDB (Network/Cookies fallback)', () => {
|
|
342
|
+
const mockToken = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature_here'
|
|
343
|
+
let workDir: string
|
|
344
|
+
|
|
345
|
+
beforeEach(() => {
|
|
346
|
+
workDir = mkdtempSync(join(tmpdir(), 'teams-extractor-test-'))
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
const cleanup = () => rmSync(workDir, { recursive: true, force: true })
|
|
350
|
+
|
|
351
|
+
// Regression for #156: if only Network/Cookies exists, missing sibling must not poison accountType.
|
|
352
|
+
test('falls through to Network/Cookies when Cookies is missing', async () => {
|
|
353
|
+
// given: only Network/Cookies exists on disk for WV2Profile_tfl
|
|
354
|
+
const profileDir = join(workDir, 'WV2Profile_tfl')
|
|
355
|
+
const networkDir = join(profileDir, 'Network')
|
|
356
|
+
mkdirSync(networkDir, { recursive: true })
|
|
357
|
+
const cookiesPath = join(profileDir, 'Cookies')
|
|
358
|
+
const networkCookiesPath = join(networkDir, 'Cookies')
|
|
359
|
+
writeFileSync(networkCookiesPath, '')
|
|
360
|
+
|
|
361
|
+
const winExtractor = new TeamsTokenExtractor('win32')
|
|
362
|
+
const getPathsSpy = spyOn(winExtractor, 'getTeamsCookiesPaths').mockReturnValue([
|
|
363
|
+
{ path: cookiesPath, accountType: 'personal' },
|
|
364
|
+
{ path: networkCookiesPath, accountType: 'personal' },
|
|
365
|
+
])
|
|
366
|
+
const tried: string[] = []
|
|
367
|
+
const copyAndExtractSpy = spyOn(winExtractor as any, 'copyAndExtract').mockImplementation(async (...args) => {
|
|
368
|
+
const path = args[0] as string
|
|
369
|
+
tried.push(path)
|
|
370
|
+
return mockToken
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
// when
|
|
374
|
+
const results = await (winExtractor as any).extractFromCookiesDB()
|
|
375
|
+
|
|
376
|
+
// then: the Cookies path was skipped (never passed to copyAndExtract),
|
|
377
|
+
// the Network/Cookies sibling was tried, and the token was returned.
|
|
378
|
+
expect(tried).toEqual([networkCookiesPath])
|
|
379
|
+
expect(results).toEqual([{ token: mockToken, accountType: 'personal' }])
|
|
380
|
+
|
|
381
|
+
getPathsSpy.mockRestore()
|
|
382
|
+
copyAndExtractSpy.mockRestore()
|
|
383
|
+
cleanup()
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
test('a missing path does not mark the account type as seen', async () => {
|
|
387
|
+
// given: work account has Cookies missing but Network/Cookies present
|
|
388
|
+
const workProfile = join(workDir, 'WV2Profile_tfw')
|
|
389
|
+
const workNetworkDir = join(workProfile, 'Network')
|
|
390
|
+
mkdirSync(workNetworkDir, { recursive: true })
|
|
391
|
+
const workCookies = join(workProfile, 'Cookies')
|
|
392
|
+
const workNetworkCookies = join(workNetworkDir, 'Cookies')
|
|
393
|
+
writeFileSync(workNetworkCookies, '')
|
|
394
|
+
|
|
395
|
+
const winExtractor = new TeamsTokenExtractor('win32')
|
|
396
|
+
const getPathsSpy = spyOn(winExtractor, 'getTeamsCookiesPaths').mockReturnValue([
|
|
397
|
+
{ path: workCookies, accountType: 'work' },
|
|
398
|
+
{ path: workNetworkCookies, accountType: 'work' },
|
|
399
|
+
])
|
|
400
|
+
const copyAndExtractSpy = spyOn(winExtractor as any, 'copyAndExtract').mockResolvedValue(mockToken)
|
|
401
|
+
|
|
402
|
+
// when
|
|
403
|
+
const results = await (winExtractor as any).extractFromCookiesDB()
|
|
404
|
+
|
|
405
|
+
// then: missing first path did not block the sibling; work token extracted
|
|
406
|
+
expect(results).toHaveLength(1)
|
|
407
|
+
expect(results[0].accountType).toBe('work')
|
|
408
|
+
expect(copyAndExtractSpy).toHaveBeenCalledTimes(1)
|
|
409
|
+
expect(copyAndExtractSpy).toHaveBeenCalledWith(workNetworkCookies)
|
|
410
|
+
|
|
411
|
+
getPathsSpy.mockRestore()
|
|
412
|
+
copyAndExtractSpy.mockRestore()
|
|
413
|
+
cleanup()
|
|
414
|
+
})
|
|
415
|
+
})
|
|
416
|
+
|
|
403
417
|
describe('copyAndExtract', () => {
|
|
404
418
|
test('attempts to copy database to temp location', async () => {
|
|
405
419
|
const darwinExtractor = new TeamsTokenExtractor('darwin')
|