agent-messenger 2.8.0 → 2.10.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-plugin/plugin.json +1 -1
- package/README.md +0 -11
- package/dist/package.json +1 -1
- package/dist/src/platforms/channeltalk/commands/snapshot.d.ts +4 -2
- package/dist/src/platforms/channeltalk/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/snapshot.js +86 -31
- package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/snapshot.d.ts +3 -1
- package/dist/src/platforms/channeltalkbot/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/snapshot.js +110 -60
- package/dist/src/platforms/channeltalkbot/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/discord/commands/snapshot.d.ts +1 -0
- package/dist/src/platforms/discord/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/snapshot.js +48 -34
- package/dist/src/platforms/discord/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/discordbot/commands/snapshot.d.ts +2 -0
- package/dist/src/platforms/discordbot/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/commands/snapshot.js +46 -34
- package/dist/src/platforms/discordbot/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/slack/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/snapshot.js +75 -55
- package/dist/src/platforms/slack/commands/snapshot.js.map +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 +1 -0
- package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/snapshot.js +50 -32
- 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 +67 -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/commands/snapshot.d.ts +1 -0
- package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/snapshot.js +14 -7
- package/dist/src/platforms/webex/commands/snapshot.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/docs/content/docs/agent-skills.mdx +0 -10
- package/docs/content/docs/cli/channeltalk.mdx +18 -8
- package/docs/content/docs/cli/channeltalkbot.mdx +16 -6
- package/docs/content/docs/cli/discord.mdx +23 -7
- package/docs/content/docs/cli/discordbot.mdx +23 -7
- package/docs/content/docs/cli/slack.mdx +24 -7
- package/docs/content/docs/cli/teams.mdx +24 -8
- package/docs/content/docs/cli/webex.mdx +15 -2
- package/e2e/webex.e2e.test.ts +57 -0
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +19 -9
- package/skills/agent-channeltalk/references/common-patterns.md +10 -9
- package/skills/agent-channeltalkbot/SKILL.md +19 -9
- package/skills/agent-channeltalkbot/references/common-patterns.md +10 -9
- package/skills/agent-discord/SKILL.md +18 -9
- package/skills/agent-discord/references/common-patterns.md +8 -7
- package/skills/agent-discordbot/SKILL.md +18 -9
- 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 +19 -10
- package/skills/agent-slack/references/common-patterns.md +4 -7
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +18 -9
- package/skills/agent-teams/references/common-patterns.md +9 -7
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +13 -4
- package/skills/agent-webex/references/common-patterns.md +8 -2
- 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/channeltalk/commands/snapshot.test.ts +58 -26
- package/src/platforms/channeltalk/commands/snapshot.ts +107 -33
- package/src/platforms/channeltalkbot/commands/snapshot.test.ts +26 -8
- package/src/platforms/channeltalkbot/commands/snapshot.ts +131 -64
- package/src/platforms/discord/commands/snapshot.test.ts +1 -1
- package/src/platforms/discord/commands/snapshot.ts +58 -42
- package/src/platforms/discordbot/commands/snapshot.test.ts +40 -18
- package/src/platforms/discordbot/commands/snapshot.ts +54 -37
- package/src/platforms/slack/commands/snapshot.test.ts +63 -8
- package/src/platforms/slack/commands/snapshot.ts +98 -66
- 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.test.ts +1 -1
- package/src/platforms/teams/commands/snapshot.ts +59 -39
- 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.ts +77 -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/commands/snapshot.test.ts +14 -1
- package/src/platforms/webex/commands/snapshot.ts +17 -9
- 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
|
@@ -11,6 +11,7 @@ import type { TeamsChannel } from '../types'
|
|
|
11
11
|
export async function snapshotAction(options: {
|
|
12
12
|
channelsOnly?: boolean
|
|
13
13
|
usersOnly?: boolean
|
|
14
|
+
full?: boolean
|
|
14
15
|
limit?: number
|
|
15
16
|
teamId?: string
|
|
16
17
|
pretty?: boolean
|
|
@@ -33,8 +34,12 @@ export async function snapshotAction(options: {
|
|
|
33
34
|
process.exit(1)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const client = await new TeamsClient().login({
|
|
37
|
-
|
|
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
|
|
|
@@ -45,47 +50,60 @@ export async function snapshotAction(options: {
|
|
|
45
50
|
description: team.description,
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const isFull = options.full || options.channelsOnly || options.usersOnly
|
|
54
|
+
if (isFull) {
|
|
55
|
+
const messageLimit = options.limit || 20
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
name: ch.name,
|
|
54
|
-
type: ch.type,
|
|
55
|
-
}))
|
|
57
|
+
if (!options.usersOnly) {
|
|
58
|
+
const channels = await client.listChannels(teamId)
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const messages = await client.getMessages(teamId, channel.id, messageLimit)
|
|
62
|
-
return messages.map((msg) => ({
|
|
63
|
-
...msg,
|
|
64
|
-
channel_name: channel.name,
|
|
65
|
-
}))
|
|
66
|
-
},
|
|
67
|
-
5,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
snapshot.recent_messages = channelMessages.flat().map((msg) => ({
|
|
71
|
-
channel_id: msg.channel_id,
|
|
72
|
-
channel_name: msg.channel_name,
|
|
73
|
-
id: msg.id,
|
|
74
|
-
author: msg.author.displayName,
|
|
75
|
-
content: msg.content,
|
|
76
|
-
timestamp: msg.timestamp,
|
|
60
|
+
snapshot.channels = channels.map((ch) => ({
|
|
61
|
+
id: ch.id,
|
|
62
|
+
name: ch.name,
|
|
63
|
+
type: ch.type,
|
|
77
64
|
}))
|
|
65
|
+
|
|
66
|
+
if (!options.channelsOnly) {
|
|
67
|
+
const channelMessages = await parallelMap(
|
|
68
|
+
channels,
|
|
69
|
+
async (channel: TeamsChannel) => {
|
|
70
|
+
const messages = await client.getMessages(teamId, channel.id, messageLimit)
|
|
71
|
+
return messages.map((msg) => ({
|
|
72
|
+
...msg,
|
|
73
|
+
channel_name: channel.name,
|
|
74
|
+
}))
|
|
75
|
+
},
|
|
76
|
+
5,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
snapshot.recent_messages = channelMessages.flat().map((msg) => ({
|
|
80
|
+
channel_id: msg.channel_id,
|
|
81
|
+
channel_name: msg.channel_name,
|
|
82
|
+
id: msg.id,
|
|
83
|
+
author: msg.author.displayName,
|
|
84
|
+
content: msg.content,
|
|
85
|
+
timestamp: msg.timestamp,
|
|
86
|
+
}))
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
|
-
}
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
if (!options.channelsOnly) {
|
|
91
|
+
const users = await client.listUsers(teamId)
|
|
92
|
+
|
|
93
|
+
snapshot.members = users.map((u) => ({
|
|
94
|
+
id: u.id,
|
|
95
|
+
displayName: u.displayName,
|
|
96
|
+
email: u.email || null,
|
|
97
|
+
}))
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
if (!options.usersOnly) {
|
|
101
|
+
const channels = await client.listChannels(teamId)
|
|
102
|
+
snapshot.channels = channels.map((ch) => ({ id: ch.id, name: ch.name }))
|
|
103
|
+
}
|
|
83
104
|
|
|
84
|
-
snapshot.
|
|
85
|
-
|
|
86
|
-
displayName: u.displayName,
|
|
87
|
-
email: u.email || null,
|
|
88
|
-
}))
|
|
105
|
+
snapshot.hint =
|
|
106
|
+
"Use 'message list <channel>' for messages, 'channel info <channel>' for channel details, 'user list' for members."
|
|
89
107
|
}
|
|
90
108
|
|
|
91
109
|
console.log(formatOutput(snapshot, options.pretty))
|
|
@@ -95,16 +113,18 @@ export async function snapshotAction(options: {
|
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
export const snapshotCommand = new Command('snapshot')
|
|
98
|
-
.description('Get
|
|
116
|
+
.description('Get team overview for AI agents (brief by default, use --full for comprehensive data)')
|
|
117
|
+
.option('--full', 'Include messages and members (verbose)')
|
|
99
118
|
.option('--channels-only', 'Include only channels (exclude messages and members)')
|
|
100
119
|
.option('--users-only', 'Include only members (exclude channels and messages)')
|
|
101
|
-
.option('--limit <n>', 'Number of recent messages per channel (default: 20)', '20')
|
|
120
|
+
.option('--limit <n>', 'Number of recent messages per channel with --full (default: 20)', '20')
|
|
102
121
|
.option('--team-id <id>', 'Team ID (defaults to current team)')
|
|
103
122
|
.option('--pretty', 'Pretty print JSON output')
|
|
104
123
|
.action(async (options) => {
|
|
105
124
|
await snapshotAction({
|
|
106
125
|
channelsOnly: options.channelsOnly,
|
|
107
126
|
usersOnly: options.usersOnly,
|
|
127
|
+
full: options.full,
|
|
108
128
|
limit: parseInt(options.limit, 10),
|
|
109
129
|
teamId: options.teamId,
|
|
110
130
|
pretty: options.pretty,
|
|
@@ -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,
|
|
@@ -55,9 +55,11 @@ export class TeamsTokenExtractor {
|
|
|
55
55
|
private platform: NodeJS.Platform
|
|
56
56
|
private decryptor: ChromiumCookieDecryptor
|
|
57
57
|
private cookieReader: ChromiumCookieReader
|
|
58
|
+
private debugLog: ((message: string) => void) | null
|
|
58
59
|
|
|
59
|
-
constructor(platform?: NodeJS.Platform, keyCache?: DerivedKeyCache) {
|
|
60
|
+
constructor(platform?: NodeJS.Platform, keyCache?: DerivedKeyCache, debugLog?: (message: string) => void) {
|
|
60
61
|
this.platform = platform ?? process.platform
|
|
62
|
+
this.debugLog = debugLog ?? null
|
|
61
63
|
|
|
62
64
|
const resolvedKeyCache = keyCache ?? new DerivedKeyCache()
|
|
63
65
|
this.decryptor = new ChromiumCookieDecryptor({
|
|
@@ -69,6 +71,10 @@ export class TeamsTokenExtractor {
|
|
|
69
71
|
this.cookieReader = new ChromiumCookieReader()
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
private debug(message: string): void {
|
|
75
|
+
this.debugLog?.(message)
|
|
76
|
+
}
|
|
77
|
+
|
|
72
78
|
getDesktopCookiesPaths(): TeamsCookiePath[] {
|
|
73
79
|
switch (this.platform) {
|
|
74
80
|
case 'darwin': {
|
|
@@ -197,17 +203,38 @@ export class TeamsTokenExtractor {
|
|
|
197
203
|
private async extractFromCookiesDB(): Promise<ExtractedTeamsToken[]> {
|
|
198
204
|
const results: ExtractedTeamsToken[] = []
|
|
199
205
|
const seenAccountTypes = new Set<TeamsAccountType>()
|
|
206
|
+
const allPaths = this.getTeamsCookiesPaths()
|
|
207
|
+
|
|
208
|
+
this.debug(`Scanning ${allPaths.length} candidate cookie path(s)`)
|
|
209
|
+
|
|
210
|
+
for (const { path: dbPath, accountType } of allPaths) {
|
|
211
|
+
if (!dbPath) continue
|
|
212
|
+
|
|
213
|
+
if (!existsSync(dbPath)) {
|
|
214
|
+
this.debug(` [skip] ${dbPath} (not found)`)
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (seenAccountTypes.has(accountType)) {
|
|
219
|
+
this.debug(` [skip] ${dbPath} (already have ${accountType} account)`)
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
200
222
|
|
|
201
|
-
|
|
202
|
-
if (!dbPath || !existsSync(dbPath) || seenAccountTypes.has(accountType)) continue
|
|
223
|
+
this.debug(` [try] ${dbPath} (${accountType})`)
|
|
203
224
|
|
|
204
225
|
const token = await this.copyAndExtract(dbPath)
|
|
205
226
|
if (token && this.isValidSkypeToken(token)) {
|
|
227
|
+
this.debug(` [ok] Extracted valid token (${token.length} chars)`)
|
|
206
228
|
results.push({ token, accountType })
|
|
207
229
|
seenAccountTypes.add(accountType)
|
|
230
|
+
} else if (token) {
|
|
231
|
+
this.debug(` [fail] Token too short (${token.length} chars, need >=50)`)
|
|
232
|
+
} else {
|
|
233
|
+
this.debug(` [fail] No token extracted`)
|
|
208
234
|
}
|
|
209
235
|
}
|
|
210
236
|
|
|
237
|
+
this.debug(`Extraction complete: ${results.length} token(s) found`)
|
|
211
238
|
return results
|
|
212
239
|
}
|
|
213
240
|
|
|
@@ -216,11 +243,26 @@ export class TeamsTokenExtractor {
|
|
|
216
243
|
|
|
217
244
|
try {
|
|
218
245
|
tempPath = this.copyDatabaseToTemp(dbPath, dbPath)
|
|
219
|
-
|
|
220
|
-
|
|
246
|
+
|
|
247
|
+
let localStatePath: string | undefined
|
|
248
|
+
if (this.platform === 'win32') {
|
|
249
|
+
localStatePath = findLocalStatePath(dbPath) ?? undefined
|
|
250
|
+
if (localStatePath) {
|
|
251
|
+
this.debug(` Local State (from cookie path): ${localStatePath}`)
|
|
252
|
+
} else {
|
|
253
|
+
localStatePath = this.getLocalStatePath()
|
|
254
|
+
if (existsSync(localStatePath)) {
|
|
255
|
+
this.debug(` Local State (fallback): ${localStatePath}`)
|
|
256
|
+
} else {
|
|
257
|
+
this.debug(` Local State not found (tried fallback: ${localStatePath})`)
|
|
258
|
+
localStatePath = undefined
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
221
262
|
|
|
222
263
|
return await this.extractFromSQLite(tempPath, localStatePath)
|
|
223
|
-
} catch {
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.debug(` Copy/extract error: ${(error as Error).message}`)
|
|
224
266
|
return null
|
|
225
267
|
} finally {
|
|
226
268
|
this.cleanupTempFile(tempPath)
|
|
@@ -231,27 +273,50 @@ export class TeamsTokenExtractor {
|
|
|
231
273
|
try {
|
|
232
274
|
for (const hostPattern of TEAMS_HOST_PATTERNS) {
|
|
233
275
|
const sql = `
|
|
234
|
-
SELECT encrypted_value
|
|
276
|
+
SELECT value, encrypted_value
|
|
235
277
|
FROM cookies
|
|
236
278
|
WHERE name = '${SKYPETOKEN_COOKIE_NAME}'
|
|
237
279
|
AND host_key LIKE '%${hostPattern}%'
|
|
238
280
|
LIMIT 1
|
|
239
281
|
`
|
|
240
282
|
|
|
241
|
-
type CookieRow = { encrypted_value?: Uint8Array | Buffer } | null
|
|
283
|
+
type CookieRow = { value?: string; encrypted_value?: Uint8Array | Buffer } | null
|
|
242
284
|
|
|
243
285
|
const row = await this.cookieReader.queryFirst<CookieRow>(dbPath, sql)
|
|
244
|
-
if (!row
|
|
286
|
+
if (!row) continue
|
|
287
|
+
|
|
288
|
+
if (row.value && row.value.length >= 50) {
|
|
289
|
+
this.debug(` Found plaintext cookie for ${hostPattern} (${row.value.length} chars)`)
|
|
290
|
+
return row.value
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!row.encrypted_value || row.encrypted_value.length === 0) {
|
|
294
|
+
this.debug(` No cookie data for ${hostPattern}`)
|
|
295
|
+
continue
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const encBuf = Buffer.from(row.encrypted_value)
|
|
299
|
+
const isEncrypted = this.isEncryptedValue(encBuf)
|
|
300
|
+
this.debug(
|
|
301
|
+
` Found cookie for ${hostPattern}: ${encBuf.length} bytes, encrypted=${isEncrypted}, prefix=${encBuf.subarray(0, 3).toString('utf8')}`,
|
|
302
|
+
)
|
|
245
303
|
|
|
246
|
-
const decryptedBuf = this.decryptor.decryptCookieRaw(
|
|
247
|
-
if (!decryptedBuf)
|
|
304
|
+
const decryptedBuf = this.decryptor.decryptCookieRaw(encBuf, localStatePath)
|
|
305
|
+
if (!decryptedBuf) {
|
|
306
|
+
this.debug(` Decryption failed`)
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
248
309
|
|
|
310
|
+
this.debug(` Decrypted: ${decryptedBuf.length} bytes`)
|
|
249
311
|
const token = this.postProcessDecrypted(decryptedBuf)
|
|
250
312
|
if (this.isValidSkypeToken(token)) return token
|
|
313
|
+
|
|
314
|
+
this.debug(` Post-process result not a valid token (${token.length} chars)`)
|
|
251
315
|
}
|
|
252
316
|
|
|
253
317
|
return null
|
|
254
|
-
} catch {
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.debug(` SQLite query error: ${(error as Error).message}`)
|
|
255
320
|
return null
|
|
256
321
|
}
|
|
257
322
|
}
|
|
@@ -203,6 +203,7 @@ test('TeamsConfigSchema validates config with multiple accounts', () => {
|
|
|
203
203
|
work: {
|
|
204
204
|
token: 'work_token',
|
|
205
205
|
token_expires_at: '2024-01-01T00:00:00.000Z',
|
|
206
|
+
region: 'emea',
|
|
206
207
|
account_type: 'work',
|
|
207
208
|
user_name: 'Work User',
|
|
208
209
|
current_team: '19:abc123@thread.tacv2',
|
|
@@ -224,6 +225,22 @@ test('TeamsConfigSchema validates config with multiple accounts', () => {
|
|
|
224
225
|
expect(result.success).toBe(true)
|
|
225
226
|
})
|
|
226
227
|
|
|
228
|
+
test('TeamsConfigSchema rejects invalid region', () => {
|
|
229
|
+
const result = TeamsConfigSchema.safeParse({
|
|
230
|
+
current_account: 'work',
|
|
231
|
+
accounts: {
|
|
232
|
+
work: {
|
|
233
|
+
token: 'token_value',
|
|
234
|
+
region: 'invalid',
|
|
235
|
+
account_type: 'work',
|
|
236
|
+
current_team: null,
|
|
237
|
+
teams: {},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
expect(result.success).toBe(false)
|
|
242
|
+
})
|
|
243
|
+
|
|
227
244
|
test('TeamsConfigSchema rejects missing required fields', () => {
|
|
228
245
|
const result = TeamsConfigSchema.safeParse({
|
|
229
246
|
current_account: null,
|
|
@@ -55,9 +55,12 @@ export interface TeamsCredentials {
|
|
|
55
55
|
|
|
56
56
|
export type TeamsAccountType = 'work' | 'personal'
|
|
57
57
|
|
|
58
|
+
export type TeamsRegion = 'amer' | 'emea' | 'apac'
|
|
59
|
+
|
|
58
60
|
export interface TeamsAccount {
|
|
59
61
|
token: string
|
|
60
62
|
token_expires_at?: string
|
|
63
|
+
region?: TeamsRegion
|
|
61
64
|
account_type: TeamsAccountType
|
|
62
65
|
user_name?: string
|
|
63
66
|
current_team: string | null
|
|
@@ -141,9 +144,12 @@ export const TeamsCredentialsSchema = z.object({
|
|
|
141
144
|
|
|
142
145
|
export const TeamsAccountTypeSchema = z.enum(['work', 'personal'])
|
|
143
146
|
|
|
147
|
+
export const TeamsRegionSchema = z.enum(['amer', 'emea', 'apac'])
|
|
148
|
+
|
|
144
149
|
export const TeamsAccountSchema = z.object({
|
|
145
150
|
token: z.string(),
|
|
146
151
|
token_expires_at: z.string().optional(),
|
|
152
|
+
region: TeamsRegionSchema.optional(),
|
|
147
153
|
account_type: TeamsAccountTypeSchema,
|
|
148
154
|
user_name: z.string().optional(),
|
|
149
155
|
current_team: z.string().nullable(),
|