agent-messenger 1.4.0 → 1.6.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.
Files changed (111) hide show
  1. package/.claude-plugin/README.md +38 -14
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.github/workflows/ci.yml +3 -0
  4. package/CONTRIBUTING.md +24 -1
  5. package/README.md +12 -8
  6. package/dist/package.json +1 -1
  7. package/dist/src/platforms/discord/cli.d.ts.map +1 -1
  8. package/dist/src/platforms/discord/cli.js +8 -1
  9. package/dist/src/platforms/discord/cli.js.map +1 -1
  10. package/dist/src/platforms/discord/commands/file.d.ts.map +1 -1
  11. package/dist/src/platforms/discord/commands/file.js +13 -7
  12. package/dist/src/platforms/discord/commands/file.js.map +1 -1
  13. package/dist/src/platforms/discord/commands/friend.d.ts.map +1 -1
  14. package/dist/src/platforms/discord/commands/friend.js +30 -30
  15. package/dist/src/platforms/discord/commands/friend.js.map +1 -1
  16. package/dist/src/platforms/discord/commands/index.d.ts +7 -0
  17. package/dist/src/platforms/discord/commands/index.d.ts.map +1 -1
  18. package/dist/src/platforms/discord/commands/index.js +7 -0
  19. package/dist/src/platforms/discord/commands/index.js.map +1 -1
  20. package/dist/src/platforms/discord/commands/snapshot.d.ts.map +1 -1
  21. package/dist/src/platforms/discord/commands/snapshot.js +1 -2
  22. package/dist/src/platforms/discord/commands/snapshot.js.map +1 -1
  23. package/dist/src/platforms/slack/commands/file.d.ts.map +1 -1
  24. package/dist/src/platforms/slack/commands/file.js +10 -4
  25. package/dist/src/platforms/slack/commands/file.js.map +1 -1
  26. package/dist/src/platforms/slack/commands/sections.d.ts.map +1 -1
  27. package/dist/src/platforms/slack/commands/sections.js +5 -6
  28. package/dist/src/platforms/slack/commands/sections.js.map +1 -1
  29. package/dist/src/platforms/slack/commands/snapshot.d.ts.map +1 -1
  30. package/dist/src/platforms/slack/commands/snapshot.js +1 -2
  31. package/dist/src/platforms/slack/commands/snapshot.js.map +1 -1
  32. package/dist/src/platforms/slack/commands/user.js +8 -8
  33. package/dist/src/platforms/slackbot/commands/reaction.js +2 -2
  34. package/dist/src/platforms/slackbot/commands/reaction.js.map +1 -1
  35. package/dist/src/platforms/teams/cli.d.ts.map +1 -1
  36. package/dist/src/platforms/teams/cli.js +7 -1
  37. package/dist/src/platforms/teams/cli.js.map +1 -1
  38. package/dist/src/platforms/teams/commands/auth.d.ts +3 -0
  39. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  40. package/dist/src/platforms/teams/commands/auth.js +208 -107
  41. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  42. package/dist/src/platforms/teams/commands/channel.js +9 -9
  43. package/dist/src/platforms/teams/commands/channel.js.map +1 -1
  44. package/dist/src/platforms/teams/commands/file.js +21 -21
  45. package/dist/src/platforms/teams/commands/file.js.map +1 -1
  46. package/dist/src/platforms/teams/commands/message.js +12 -12
  47. package/dist/src/platforms/teams/commands/message.js.map +1 -1
  48. package/dist/src/platforms/teams/commands/reaction.js +6 -6
  49. package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
  50. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
  51. package/dist/src/platforms/teams/commands/snapshot.js +6 -6
  52. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -1
  53. package/dist/src/platforms/teams/commands/team.d.ts.map +1 -1
  54. package/dist/src/platforms/teams/commands/team.js +21 -25
  55. package/dist/src/platforms/teams/commands/team.js.map +1 -1
  56. package/dist/src/platforms/teams/commands/user.js +9 -9
  57. package/dist/src/platforms/teams/commands/user.js.map +1 -1
  58. package/dist/src/platforms/teams/credential-manager.d.ts +15 -2
  59. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
  60. package/dist/src/platforms/teams/credential-manager.js +110 -28
  61. package/dist/src/platforms/teams/credential-manager.js.map +1 -1
  62. package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
  63. package/dist/src/platforms/teams/ensure-auth.js +46 -16
  64. package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
  65. package/dist/src/platforms/teams/token-extractor.d.ts +8 -2
  66. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  67. package/dist/src/platforms/teams/token-extractor.js +36 -24
  68. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  69. package/dist/src/platforms/teams/types.d.ts +121 -0
  70. package/dist/src/platforms/teams/types.d.ts.map +1 -1
  71. package/dist/src/platforms/teams/types.js +16 -0
  72. package/dist/src/platforms/teams/types.js.map +1 -1
  73. package/e2e/README.md +1 -1
  74. package/package.json +1 -1
  75. package/skills/agent-discord/SKILL.md +5 -0
  76. package/skills/agent-slack/SKILL.md +11 -17
  77. package/skills/agent-teams/SKILL.md +21 -24
  78. package/skills/agent-teams/references/common-patterns.md +63 -49
  79. package/src/platforms/discord/cli.ts +14 -0
  80. package/src/platforms/discord/commands/file.ts +13 -7
  81. package/src/platforms/discord/commands/friend.ts +34 -34
  82. package/src/platforms/discord/commands/index.ts +7 -0
  83. package/src/platforms/discord/commands/snapshot.ts +1 -2
  84. package/src/platforms/slack/commands/file.ts +12 -4
  85. package/src/platforms/slack/commands/sections.ts +8 -9
  86. package/src/platforms/slack/commands/snapshot.ts +1 -2
  87. package/src/platforms/slack/commands/user.ts +8 -8
  88. package/src/platforms/slackbot/commands/reaction.ts +2 -2
  89. package/src/platforms/teams/cli.ts +7 -0
  90. package/src/platforms/teams/commands/auth.test.ts +6 -5
  91. package/src/platforms/teams/commands/auth.ts +283 -120
  92. package/src/platforms/teams/commands/channel.test.ts +10 -4
  93. package/src/platforms/teams/commands/channel.ts +9 -9
  94. package/src/platforms/teams/commands/file.test.ts +9 -3
  95. package/src/platforms/teams/commands/file.ts +21 -21
  96. package/src/platforms/teams/commands/message.test.ts +9 -3
  97. package/src/platforms/teams/commands/message.ts +12 -12
  98. package/src/platforms/teams/commands/reaction.test.ts +9 -3
  99. package/src/platforms/teams/commands/reaction.ts +6 -6
  100. package/src/platforms/teams/commands/snapshot.ts +6 -6
  101. package/src/platforms/teams/commands/team.test.ts +19 -12
  102. package/src/platforms/teams/commands/team.ts +22 -28
  103. package/src/platforms/teams/commands/user.ts +9 -9
  104. package/src/platforms/teams/credential-manager.test.ts +30 -24
  105. package/src/platforms/teams/credential-manager.ts +106 -31
  106. package/src/platforms/teams/ensure-auth.test.ts +125 -26
  107. package/src/platforms/teams/ensure-auth.ts +47 -15
  108. package/src/platforms/teams/token-extractor.test.ts +116 -85
  109. package/src/platforms/teams/token-extractor.ts +65 -97
  110. package/src/platforms/teams/types.test.ts +31 -13
  111. package/src/platforms/teams/types.ts +45 -0
@@ -47,9 +47,15 @@ beforeEach(() => {
47
47
  clientDeleteMessageSpy = spyOn(TeamsClient.prototype, 'deleteMessage').mockResolvedValue(undefined)
48
48
 
49
49
  credManagerLoadSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue({
50
- token: 'test_token',
51
- current_team: 'team_123',
52
- teams: { team_123: { team_id: 'team_123', team_name: 'Test Team' } },
50
+ current_account: 'work',
51
+ accounts: {
52
+ work: {
53
+ token: 'test_token',
54
+ account_type: 'work' as const,
55
+ current_team: 'team_123',
56
+ teams: { team_123: { team_id: 'team_123', team_name: 'Test Team' } },
57
+ },
58
+ },
53
59
  })
54
60
  })
55
61
 
@@ -13,14 +13,14 @@ export async function sendAction(
13
13
  ): Promise<void> {
14
14
  try {
15
15
  const credManager = new TeamsCredentialManager()
16
- const config = await credManager.loadConfig()
16
+ const cred = await credManager.getTokenWithExpiry()
17
17
 
18
- if (!config?.token) {
18
+ if (!cred) {
19
19
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
20
20
  process.exit(1)
21
21
  }
22
22
 
23
- const client = new TeamsClient(config.token, config.token_expires_at)
23
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
24
24
  const message = await client.sendMessage(teamId, channelId, content)
25
25
 
26
26
  const output = {
@@ -43,14 +43,14 @@ export async function listAction(
43
43
  ): Promise<void> {
44
44
  try {
45
45
  const credManager = new TeamsCredentialManager()
46
- const config = await credManager.loadConfig()
46
+ const cred = await credManager.getTokenWithExpiry()
47
47
 
48
- if (!config?.token) {
48
+ if (!cred) {
49
49
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
50
50
  process.exit(1)
51
51
  }
52
52
 
53
- const client = new TeamsClient(config.token, config.token_expires_at)
53
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
54
54
  const limit = options.limit || 50
55
55
  const messages = await client.getMessages(teamId, channelId, limit)
56
56
 
@@ -75,14 +75,14 @@ export async function getAction(
75
75
  ): Promise<void> {
76
76
  try {
77
77
  const credManager = new TeamsCredentialManager()
78
- const config = await credManager.loadConfig()
78
+ const cred = await credManager.getTokenWithExpiry()
79
79
 
80
- if (!config?.token) {
80
+ if (!cred) {
81
81
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
82
82
  process.exit(1)
83
83
  }
84
84
 
85
- const client = new TeamsClient(config.token, config.token_expires_at)
85
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
86
86
  const message = await client.getMessage(teamId, channelId, messageId)
87
87
 
88
88
  if (!message) {
@@ -111,9 +111,9 @@ export async function deleteAction(
111
111
  ): Promise<void> {
112
112
  try {
113
113
  const credManager = new TeamsCredentialManager()
114
- const config = await credManager.loadConfig()
114
+ const cred = await credManager.getTokenWithExpiry()
115
115
 
116
- if (!config?.token) {
116
+ if (!cred) {
117
117
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
118
118
  process.exit(1)
119
119
  }
@@ -123,7 +123,7 @@ export async function deleteAction(
123
123
  process.exit(0)
124
124
  }
125
125
 
126
- const client = new TeamsClient(config.token, config.token_expires_at)
126
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
127
127
  await client.deleteMessage(teamId, channelId, messageId)
128
128
 
129
129
  console.log(formatOutput({ deleted: messageId }, options.pretty))
@@ -11,9 +11,15 @@ beforeEach(() => {
11
11
  clientAddReactionSpy = spyOn(TeamsClient.prototype, 'addReaction').mockResolvedValue(undefined)
12
12
  clientRemoveReactionSpy = spyOn(TeamsClient.prototype, 'removeReaction').mockResolvedValue(undefined)
13
13
  credManagerLoadConfigSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue({
14
- token: 'test-token',
15
- current_team: null,
16
- teams: {},
14
+ current_account: 'work',
15
+ accounts: {
16
+ work: {
17
+ token: 'test-token',
18
+ account_type: 'work' as const,
19
+ current_team: null,
20
+ teams: {},
21
+ },
22
+ },
17
23
  })
18
24
  })
19
25
 
@@ -13,14 +13,14 @@ export async function addAction(
13
13
  ): Promise<void> {
14
14
  try {
15
15
  const credManager = new TeamsCredentialManager()
16
- const config = await credManager.loadConfig()
16
+ const cred = await credManager.getTokenWithExpiry()
17
17
 
18
- if (!config?.token) {
18
+ if (!cred) {
19
19
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
20
20
  process.exit(1)
21
21
  }
22
22
 
23
- const client = new TeamsClient(config.token, config.token_expires_at)
23
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
24
24
  await client.addReaction(teamId, channelId, messageId, emoji)
25
25
 
26
26
  console.log(
@@ -49,14 +49,14 @@ export async function removeAction(
49
49
  ): Promise<void> {
50
50
  try {
51
51
  const credManager = new TeamsCredentialManager()
52
- const config = await credManager.loadConfig()
52
+ const cred = await credManager.getTokenWithExpiry()
53
53
 
54
- if (!config?.token) {
54
+ if (!cred) {
55
55
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
56
56
  process.exit(1)
57
57
  }
58
58
 
59
- const client = new TeamsClient(config.token, config.token_expires_at)
59
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
60
60
  await client.removeReaction(teamId, channelId, messageId, emoji)
61
61
 
62
62
  console.log(
@@ -15,14 +15,15 @@ export async function snapshotAction(options: {
15
15
  }): Promise<void> {
16
16
  try {
17
17
  const credManager = new TeamsCredentialManager()
18
- const config = await credManager.loadConfig()
18
+ const cred = await credManager.getTokenWithExpiry()
19
19
 
20
- if (!config?.token) {
20
+ if (!cred) {
21
21
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
22
22
  process.exit(1)
23
23
  }
24
24
 
25
- const teamId = options.teamId || config.current_team
25
+ const currentTeam = await credManager.getCurrentTeam()
26
+ const teamId = options.teamId || currentTeam?.team_id
26
27
  if (!teamId) {
27
28
  console.log(
28
29
  formatOutput({ error: 'No current team set. Run "team switch" first or use --team-id.' }, options.pretty),
@@ -30,7 +31,7 @@ export async function snapshotAction(options: {
30
31
  process.exit(1)
31
32
  }
32
33
 
33
- const client = new TeamsClient(config.token, config.token_expires_at)
34
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
34
35
  const messageLimit = options.limit || 20
35
36
 
36
37
  const snapshot: Record<string, unknown> = {}
@@ -91,8 +92,7 @@ export async function snapshotAction(options: {
91
92
  }
92
93
  }
93
94
 
94
- export const snapshotCommand = new Command()
95
- .name('snapshot')
95
+ export const snapshotCommand = new Command('snapshot')
96
96
  .description('Get comprehensive team state for AI agents')
97
97
  .option('--channels-only', 'Include only channels (exclude messages and members)')
98
98
  .option('--users-only', 'Include only members (exclude channels and messages)')
@@ -26,11 +26,17 @@ beforeEach(() => {
26
26
  })
27
27
 
28
28
  credManagerLoadConfigSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue({
29
- token: 'test-token',
30
- current_team: 'team-1',
31
- teams: {
32
- 'team-1': { team_id: 'team-1', team_name: 'Team One' },
33
- 'team-2': { team_id: 'team-2', team_name: 'Team Two' },
29
+ current_account: 'work',
30
+ accounts: {
31
+ work: {
32
+ token: 'test-token',
33
+ account_type: 'work' as const,
34
+ current_team: 'team-1',
35
+ teams: {
36
+ 'team-1': { team_id: 'team-1', team_name: 'Team One' },
37
+ 'team-2': { team_id: 'team-2', team_name: 'Team Two' },
38
+ },
39
+ },
34
40
  },
35
41
  })
36
42
 
@@ -59,12 +65,13 @@ test('list: returns teams with current marker', async () => {
59
65
  const config = await credManager.loadConfig()
60
66
 
61
67
  // when: checking teams
62
- expect(config?.teams).toBeDefined()
63
- expect(Object.keys(config!.teams)).toHaveLength(2)
68
+ const account = config!.accounts.work
69
+ expect(account.teams).toBeDefined()
70
+ expect(Object.keys(account.teams)).toHaveLength(2)
64
71
 
65
72
  // then: teams are returned
66
- expect(config!.teams['team-1']).toBeDefined()
67
- expect(config!.teams['team-2']).toBeDefined()
73
+ expect(account.teams['team-1']).toBeDefined()
74
+ expect(account.teams['team-2']).toBeDefined()
68
75
  })
69
76
 
70
77
  test('list: marks current team', async () => {
@@ -77,7 +84,7 @@ test('list: marks current team', async () => {
77
84
  expect(current?.team_id).toBe('team-1')
78
85
 
79
86
  // then: current team is marked
80
- expect(config!.current_team).toBe('team-1')
87
+ expect(config!.accounts.work.current_team).toBe('team-1')
81
88
  })
82
89
 
83
90
  test('info: returns team details', async () => {
@@ -129,7 +136,7 @@ test('current: returns current team info', async () => {
129
136
 
130
137
  // then: current team is returned
131
138
  expect(current?.team_id).toBe('team-1')
132
- expect(config!.current_team).toBe('team-1')
139
+ expect(config!.accounts.work.current_team).toBe('team-1')
133
140
  })
134
141
 
135
142
  test('remove: removes team from config', async () => {
@@ -138,7 +145,7 @@ test('remove: removes team from config', async () => {
138
145
  const config = await credManager.loadConfig()
139
146
 
140
147
  // when: removing team
141
- delete config!.teams['team-2']
148
+ delete config!.accounts.work.teams['team-2']
142
149
  await credManager.saveConfig(config!)
143
150
 
144
151
  // then: saveConfig is called
@@ -7,13 +7,13 @@ import { TeamsCredentialManager } from '../credential-manager'
7
7
  export async function listAction(options: { pretty?: boolean }): Promise<void> {
8
8
  try {
9
9
  const credManager = new TeamsCredentialManager()
10
- const config = await credManager.loadConfig()
11
- const teams = config?.teams ? Object.values(config.teams) : []
10
+ const account = await credManager.getCurrentAccount()
11
+ const teams = account?.teams ? Object.values(account.teams) : []
12
12
 
13
13
  const output = teams.map((team) => ({
14
14
  id: team.team_id,
15
15
  name: team.team_name,
16
- current: team.team_id === config?.current_team,
16
+ current: team.team_id === account?.current_team,
17
17
  }))
18
18
 
19
19
  console.log(formatOutput(output, options.pretty))
@@ -25,14 +25,14 @@ export async function listAction(options: { pretty?: boolean }): Promise<void> {
25
25
  export async function infoAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
26
26
  try {
27
27
  const credManager = new TeamsCredentialManager()
28
- const config = await credManager.loadConfig()
28
+ const cred = await credManager.getTokenWithExpiry()
29
29
 
30
- if (!config?.token) {
30
+ if (!cred) {
31
31
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
32
32
  process.exit(1)
33
33
  }
34
34
 
35
- const client = new TeamsClient(config.token, config.token_expires_at)
35
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
36
36
  const team = await client.getTeam(teamId)
37
37
 
38
38
  const output = {
@@ -50,14 +50,14 @@ export async function infoAction(teamId: string, options: { pretty?: boolean }):
50
50
  export async function switchAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
51
51
  try {
52
52
  const credManager = new TeamsCredentialManager()
53
- const config = await credManager.loadConfig()
53
+ const account = await credManager.getCurrentAccount()
54
54
 
55
- if (!config?.teams?.[teamId]) {
55
+ if (!account?.teams?.[teamId]) {
56
56
  console.log(formatOutput({ error: `Team not found: ${teamId}` }, options.pretty))
57
57
  process.exit(1)
58
58
  }
59
59
 
60
- const team = config.teams[teamId]
60
+ const team = account.teams[teamId]
61
61
  await credManager.setCurrentTeam(teamId, team.team_name)
62
62
  console.log(formatOutput({ current: teamId }, options.pretty))
63
63
  } catch (error) {
@@ -68,26 +68,14 @@ export async function switchAction(teamId: string, options: { pretty?: boolean }
68
68
  export async function currentAction(options: { pretty?: boolean }): Promise<void> {
69
69
  try {
70
70
  const credManager = new TeamsCredentialManager()
71
- const config = await credManager.loadConfig()
71
+ const currentTeam = await credManager.getCurrentTeam()
72
72
 
73
- if (!config?.current_team) {
73
+ if (!currentTeam) {
74
74
  console.log(formatOutput({ error: 'No current team set. Run "auth extract" first.' }, options.pretty))
75
75
  process.exit(1)
76
76
  }
77
77
 
78
- const team = config.teams[config.current_team]
79
-
80
- if (!team) {
81
- console.log(formatOutput({ error: 'Current team not found in configuration.' }, options.pretty))
82
- process.exit(1)
83
- }
84
-
85
- const output = {
86
- team_id: team.team_id,
87
- team_name: team.team_name,
88
- }
89
-
90
- console.log(formatOutput(output, options.pretty))
78
+ console.log(formatOutput({ team_id: currentTeam.team_id, team_name: currentTeam.team_name }, options.pretty))
91
79
  } catch (error) {
92
80
  handleError(error as Error)
93
81
  }
@@ -103,15 +91,21 @@ export async function removeAction(teamId: string, options: { pretty?: boolean }
103
91
  process.exit(1)
104
92
  }
105
93
 
106
- if (!config.teams[teamId]) {
94
+ const account = await credManager.getCurrentAccount()
95
+ if (!account) {
96
+ console.log(formatOutput({ error: 'No active account.' }, options.pretty))
97
+ process.exit(1)
98
+ }
99
+
100
+ if (!account.teams[teamId]) {
107
101
  console.log(formatOutput({ error: `Team not found: ${teamId}` }, options.pretty))
108
102
  process.exit(1)
109
103
  }
110
104
 
111
- delete config.teams[teamId]
105
+ delete account.teams[teamId]
112
106
 
113
- if (config.current_team === teamId) {
114
- config.current_team = null
107
+ if (account.current_team === teamId) {
108
+ account.current_team = null
115
109
  }
116
110
 
117
111
  await credManager.saveConfig(config)
@@ -7,14 +7,14 @@ import { TeamsCredentialManager } from '../credential-manager'
7
7
  async function listAction(teamId: string, options: { pretty?: boolean }): Promise<void> {
8
8
  try {
9
9
  const credManager = new TeamsCredentialManager()
10
- const config = await credManager.loadConfig()
10
+ const cred = await credManager.getTokenWithExpiry()
11
11
 
12
- if (!config?.token) {
12
+ if (!cred) {
13
13
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
14
14
  process.exit(1)
15
15
  }
16
16
 
17
- const client = new TeamsClient(config.token, config.token_expires_at)
17
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
18
18
  const users = await client.listUsers(teamId)
19
19
 
20
20
  const output = users.map((user) => ({
@@ -33,14 +33,14 @@ async function listAction(teamId: string, options: { pretty?: boolean }): Promis
33
33
  async function infoAction(userId: string, options: { pretty?: boolean }): Promise<void> {
34
34
  try {
35
35
  const credManager = new TeamsCredentialManager()
36
- const config = await credManager.loadConfig()
36
+ const cred = await credManager.getTokenWithExpiry()
37
37
 
38
- if (!config?.token) {
38
+ if (!cred) {
39
39
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
40
40
  process.exit(1)
41
41
  }
42
42
 
43
- const client = new TeamsClient(config.token, config.token_expires_at)
43
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
44
44
  const user = await client.getUser(userId)
45
45
 
46
46
  const output = {
@@ -59,14 +59,14 @@ async function infoAction(userId: string, options: { pretty?: boolean }): Promis
59
59
  async function meAction(options: { pretty?: boolean }): Promise<void> {
60
60
  try {
61
61
  const credManager = new TeamsCredentialManager()
62
- const config = await credManager.loadConfig()
62
+ const cred = await credManager.getTokenWithExpiry()
63
63
 
64
- if (!config?.token) {
64
+ if (!cred) {
65
65
  console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
66
66
  process.exit(1)
67
67
  }
68
68
 
69
- const client = new TeamsClient(config.token, config.token_expires_at)
69
+ const client = new TeamsClient(cred.token, cred.tokenExpiresAt)
70
70
  const user = await client.testAuth()
71
71
 
72
72
  const output = {
@@ -33,10 +33,16 @@ describe('TeamsCredentialManager', () => {
33
33
  testDirs.push(testConfigDir)
34
34
  const manager = new TeamsCredentialManager(testConfigDir)
35
35
  const config = {
36
- token: 'test-token',
37
- current_team: 'team-123',
38
- teams: {
39
- 'team-123': { team_id: 'team-123', team_name: 'Test Team' },
36
+ current_account: 'work',
37
+ accounts: {
38
+ work: {
39
+ token: 'test-token',
40
+ current_team: 'team-123',
41
+ account_type: 'work' as const,
42
+ teams: {
43
+ 'team-123': { team_id: 'team-123', team_name: 'Test Team' },
44
+ },
45
+ },
40
46
  },
41
47
  }
42
48
 
@@ -59,7 +65,7 @@ describe('TeamsCredentialManager', () => {
59
65
 
60
66
  test('setToken saves token to config', async () => {
61
67
  const manager = setup()
62
- await manager.setToken('test-token-123')
68
+ await manager.setToken('test-token-123', 'work')
63
69
 
64
70
  const token = await manager.getToken()
65
71
  expect(token).toBe('test-token-123')
@@ -68,11 +74,11 @@ describe('TeamsCredentialManager', () => {
68
74
  test('setToken saves token with expiry', async () => {
69
75
  const manager = setup()
70
76
  const expiresAt = '2025-12-31T23:59:59Z'
71
- await manager.setToken('test-token-123', expiresAt)
77
+ await manager.setToken('test-token-123', 'work', expiresAt)
72
78
 
73
79
  const config = await manager.loadConfig()
74
- expect(config?.token).toBe('test-token-123')
75
- expect(config?.token_expires_at).toBe(expiresAt)
80
+ expect(config?.accounts?.work?.token).toBe('test-token-123')
81
+ expect(config?.accounts?.work?.token_expires_at).toBe(expiresAt)
76
82
  })
77
83
 
78
84
  test('getCurrentTeam returns null when not set', async () => {
@@ -83,10 +89,10 @@ describe('TeamsCredentialManager', () => {
83
89
 
84
90
  test('getCurrentTeam returns null when current_team is set but team not in teams record', async () => {
85
91
  const manager = setup()
86
- await manager.setToken('test-token')
92
+ await manager.setToken('test-token', 'work')
87
93
  const config = await manager.loadConfig()
88
- if (config) {
89
- config.current_team = 'non-existent-team'
94
+ if (config?.accounts?.work) {
95
+ config.accounts.work.current_team = 'non-existent-team'
90
96
  await manager.saveConfig(config)
91
97
  }
92
98
 
@@ -96,7 +102,7 @@ describe('TeamsCredentialManager', () => {
96
102
 
97
103
  test('setCurrentTeam saves team info', async () => {
98
104
  const manager = setup()
99
- await manager.setToken('test-token')
105
+ await manager.setToken('test-token', 'work')
100
106
  await manager.setCurrentTeam('team-456', 'My Team')
101
107
 
102
108
  const team = await manager.getCurrentTeam()
@@ -105,7 +111,7 @@ describe('TeamsCredentialManager', () => {
105
111
 
106
112
  test('setCurrentTeam updates existing team', async () => {
107
113
  const manager = setup()
108
- await manager.setToken('test-token')
114
+ await manager.setToken('test-token', 'work')
109
115
  await manager.setCurrentTeam('team-1', 'Team One')
110
116
  await manager.setCurrentTeam('team-2', 'Team Two')
111
117
 
@@ -113,13 +119,13 @@ describe('TeamsCredentialManager', () => {
113
119
  expect(team).toEqual({ team_id: 'team-2', team_name: 'Team Two' })
114
120
 
115
121
  const config = await manager.loadConfig()
116
- expect(config?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
117
- expect(config?.teams['team-2']).toEqual({ team_id: 'team-2', team_name: 'Team Two' })
122
+ expect(config?.accounts?.work?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
123
+ expect(config?.accounts?.work?.teams['team-2']).toEqual({ team_id: 'team-2', team_name: 'Team Two' })
118
124
  })
119
125
 
120
126
  test('clearCredentials removes all credentials', async () => {
121
127
  const manager = setup()
122
- await manager.setToken('test-token', '2025-12-31T23:59:59Z')
128
+ await manager.setToken('test-token', 'work', '2025-12-31T23:59:59Z')
123
129
  await manager.setCurrentTeam('team-123', 'Test Team')
124
130
 
125
131
  await manager.clearCredentials()
@@ -136,7 +142,7 @@ describe('TeamsCredentialManager', () => {
136
142
 
137
143
  test('isTokenExpired returns true when no token_expires_at is set', async () => {
138
144
  const manager = setup()
139
- await manager.setToken('test-token')
145
+ await manager.setToken('test-token', 'work')
140
146
 
141
147
  const expired = await manager.isTokenExpired()
142
148
  expect(expired).toBe(true)
@@ -145,7 +151,7 @@ describe('TeamsCredentialManager', () => {
145
151
  test('isTokenExpired returns true when token is expired', async () => {
146
152
  const manager = setup()
147
153
  const pastDate = new Date(Date.now() - 60000).toISOString()
148
- await manager.setToken('test-token', pastDate)
154
+ await manager.setToken('test-token', 'work', pastDate)
149
155
 
150
156
  const expired = await manager.isTokenExpired()
151
157
  expect(expired).toBe(true)
@@ -154,7 +160,7 @@ describe('TeamsCredentialManager', () => {
154
160
  test('isTokenExpired returns false when token is not expired', async () => {
155
161
  const manager = setup()
156
162
  const futureDate = new Date(Date.now() + 3600000).toISOString()
157
- await manager.setToken('test-token', futureDate)
163
+ await manager.setToken('test-token', 'work', futureDate)
158
164
 
159
165
  const expired = await manager.isTokenExpired()
160
166
  expect(expired).toBe(false)
@@ -162,14 +168,14 @@ describe('TeamsCredentialManager', () => {
162
168
 
163
169
  test('multiple operations preserve existing data', async () => {
164
170
  const manager = setup()
165
- await manager.setToken('token-1', '2025-12-31T23:59:59Z')
171
+ await manager.setToken('token-1', 'work', '2025-12-31T23:59:59Z')
166
172
  await manager.setCurrentTeam('team-1', 'Team One')
167
173
 
168
- await manager.setToken('token-2')
174
+ await manager.setToken('token-2', 'work')
169
175
 
170
176
  const config = await manager.loadConfig()
171
- expect(config?.token).toBe('token-2')
172
- expect(config?.current_team).toBe('team-1')
173
- expect(config?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
177
+ expect(config?.accounts?.work?.token).toBe('token-2')
178
+ expect(config?.accounts?.work?.current_team).toBe('team-1')
179
+ expect(config?.accounts?.work?.teams['team-1']).toEqual({ team_id: 'team-1', team_name: 'Team One' })
174
180
  })
175
181
  })