agent-messenger 2.23.1 → 2.23.3

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 (130) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/package.json +1 -1
  3. package/dist/src/platforms/webex/client.d.ts +18 -0
  4. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  5. package/dist/src/platforms/webex/client.js +202 -49
  6. package/dist/src/platforms/webex/client.js.map +1 -1
  7. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  8. package/dist/src/platforms/webex/commands/auth.js +9 -6
  9. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  10. package/dist/src/platforms/webex/commands/member.d.ts.map +1 -1
  11. package/dist/src/platforms/webex/commands/member.js +2 -0
  12. package/dist/src/platforms/webex/commands/member.js.map +1 -1
  13. package/dist/src/platforms/webex/commands/message.d.ts.map +1 -1
  14. package/dist/src/platforms/webex/commands/message.js +2 -0
  15. package/dist/src/platforms/webex/commands/message.js.map +1 -1
  16. package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -1
  17. package/dist/src/platforms/webex/commands/snapshot.js +3 -1
  18. package/dist/src/platforms/webex/commands/snapshot.js.map +1 -1
  19. package/dist/src/platforms/webex/commands/space.d.ts.map +1 -1
  20. package/dist/src/platforms/webex/commands/space.js +5 -0
  21. package/dist/src/platforms/webex/commands/space.js.map +1 -1
  22. package/dist/src/platforms/webex/commands/whoami.d.ts.map +1 -1
  23. package/dist/src/platforms/webex/commands/whoami.js +2 -0
  24. package/dist/src/platforms/webex/commands/whoami.js.map +1 -1
  25. package/dist/src/platforms/webex/id-normalizer.d.ts +11 -0
  26. package/dist/src/platforms/webex/id-normalizer.d.ts.map +1 -1
  27. package/dist/src/platforms/webex/id-normalizer.js +102 -20
  28. package/dist/src/platforms/webex/id-normalizer.js.map +1 -1
  29. package/dist/src/platforms/webex/index.d.ts +2 -2
  30. package/dist/src/platforms/webex/index.d.ts.map +1 -1
  31. package/dist/src/platforms/webex/index.js +1 -1
  32. package/dist/src/platforms/webex/index.js.map +1 -1
  33. package/dist/src/platforms/webex/types.d.ts +20 -0
  34. package/dist/src/platforms/webex/types.d.ts.map +1 -1
  35. package/dist/src/platforms/webex/types.js +10 -0
  36. package/dist/src/platforms/webex/types.js.map +1 -1
  37. package/dist/src/platforms/webexbot/client.d.ts +0 -4
  38. package/dist/src/platforms/webexbot/client.d.ts.map +1 -1
  39. package/dist/src/platforms/webexbot/client.js +8 -65
  40. package/dist/src/platforms/webexbot/client.js.map +1 -1
  41. package/dist/src/platforms/webexbot/commands/file.d.ts +2 -0
  42. package/dist/src/platforms/webexbot/commands/file.d.ts.map +1 -1
  43. package/dist/src/platforms/webexbot/commands/file.js +2 -0
  44. package/dist/src/platforms/webexbot/commands/file.js.map +1 -1
  45. package/dist/src/platforms/webexbot/commands/member.d.ts +2 -0
  46. package/dist/src/platforms/webexbot/commands/member.d.ts.map +1 -1
  47. package/dist/src/platforms/webexbot/commands/member.js +2 -0
  48. package/dist/src/platforms/webexbot/commands/member.js.map +1 -1
  49. package/dist/src/platforms/webexbot/commands/message.d.ts +4 -0
  50. package/dist/src/platforms/webexbot/commands/message.d.ts.map +1 -1
  51. package/dist/src/platforms/webexbot/commands/message.js +6 -0
  52. package/dist/src/platforms/webexbot/commands/message.js.map +1 -1
  53. package/dist/src/platforms/webexbot/commands/snapshot.d.ts +2 -0
  54. package/dist/src/platforms/webexbot/commands/snapshot.d.ts.map +1 -1
  55. package/dist/src/platforms/webexbot/commands/snapshot.js +10 -2
  56. package/dist/src/platforms/webexbot/commands/snapshot.js.map +1 -1
  57. package/dist/src/platforms/webexbot/commands/space.d.ts +4 -0
  58. package/dist/src/platforms/webexbot/commands/space.d.ts.map +1 -1
  59. package/dist/src/platforms/webexbot/commands/space.js +5 -0
  60. package/dist/src/platforms/webexbot/commands/space.js.map +1 -1
  61. package/dist/src/platforms/webexbot/commands/user.d.ts +3 -0
  62. package/dist/src/platforms/webexbot/commands/user.d.ts.map +1 -1
  63. package/dist/src/platforms/webexbot/commands/user.js +3 -0
  64. package/dist/src/platforms/webexbot/commands/user.js.map +1 -1
  65. package/dist/src/platforms/webexbot/commands/whoami.d.ts +2 -0
  66. package/dist/src/platforms/webexbot/commands/whoami.d.ts.map +1 -1
  67. package/dist/src/platforms/webexbot/commands/whoami.js +2 -0
  68. package/dist/src/platforms/webexbot/commands/whoami.js.map +1 -1
  69. package/dist/src/platforms/webexbot/index.d.ts +2 -2
  70. package/dist/src/platforms/webexbot/index.d.ts.map +1 -1
  71. package/dist/src/platforms/webexbot/index.js +1 -1
  72. package/dist/src/platforms/webexbot/index.js.map +1 -1
  73. package/dist/src/tui/adapters/types.d.ts +3 -0
  74. package/dist/src/tui/adapters/types.d.ts.map +1 -1
  75. package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -1
  76. package/dist/src/tui/adapters/webex-adapter.js +4 -0
  77. package/dist/src/tui/adapters/webex-adapter.js.map +1 -1
  78. package/docs/content/docs/cli/webex.mdx +2 -2
  79. package/package.json +1 -1
  80. package/skills/agent-channeltalk/SKILL.md +1 -1
  81. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  82. package/skills/agent-discord/SKILL.md +1 -1
  83. package/skills/agent-discordbot/SKILL.md +1 -1
  84. package/skills/agent-instagram/SKILL.md +1 -1
  85. package/skills/agent-kakaotalk/SKILL.md +1 -1
  86. package/skills/agent-line/SKILL.md +1 -1
  87. package/skills/agent-slack/SKILL.md +1 -1
  88. package/skills/agent-slackbot/SKILL.md +1 -1
  89. package/skills/agent-teams/SKILL.md +1 -1
  90. package/skills/agent-telegram/SKILL.md +1 -1
  91. package/skills/agent-telegrambot/SKILL.md +1 -1
  92. package/skills/agent-webex/SKILL.md +3 -3
  93. package/skills/agent-webexbot/SKILL.md +2 -2
  94. package/skills/agent-webexbot/references/common-patterns.md +1 -1
  95. package/skills/agent-wechatbot/SKILL.md +1 -1
  96. package/skills/agent-whatsapp/SKILL.md +1 -1
  97. package/skills/agent-whatsappbot/SKILL.md +1 -1
  98. package/src/platforms/webex/client.test.ts +94 -6
  99. package/src/platforms/webex/client.ts +226 -44
  100. package/src/platforms/webex/commands/auth.test.ts +3 -1
  101. package/src/platforms/webex/commands/auth.ts +12 -7
  102. package/src/platforms/webex/commands/member.test.ts +24 -8
  103. package/src/platforms/webex/commands/member.ts +2 -0
  104. package/src/platforms/webex/commands/message.test.ts +37 -23
  105. package/src/platforms/webex/commands/message.ts +2 -0
  106. package/src/platforms/webex/commands/snapshot.test.ts +18 -10
  107. package/src/platforms/webex/commands/snapshot.ts +3 -1
  108. package/src/platforms/webex/commands/space.test.ts +36 -17
  109. package/src/platforms/webex/commands/space.ts +5 -0
  110. package/src/platforms/webex/commands/whoami.test.ts +16 -6
  111. package/src/platforms/webex/commands/whoami.ts +2 -0
  112. package/src/platforms/webex/id-normalizer.test.ts +282 -2
  113. package/src/platforms/webex/id-normalizer.ts +112 -20
  114. package/src/platforms/webex/index.ts +2 -2
  115. package/src/platforms/webex/listener.test.ts +3 -0
  116. package/src/platforms/webex/types.test.ts +20 -0
  117. package/src/platforms/webex/types.ts +20 -0
  118. package/src/platforms/webex/typings/webex-message-handler.d.ts +40 -2
  119. package/src/platforms/webexbot/client.ts +8 -74
  120. package/src/platforms/webexbot/commands/file.ts +4 -0
  121. package/src/platforms/webexbot/commands/member.ts +4 -0
  122. package/src/platforms/webexbot/commands/message.ts +10 -0
  123. package/src/platforms/webexbot/commands/snapshot.ts +12 -2
  124. package/src/platforms/webexbot/commands/space.ts +9 -0
  125. package/src/platforms/webexbot/commands/user.test.ts +15 -5
  126. package/src/platforms/webexbot/commands/user.ts +6 -0
  127. package/src/platforms/webexbot/commands/whoami.ts +4 -0
  128. package/src/platforms/webexbot/index.ts +2 -2
  129. package/src/tui/adapters/types.ts +3 -0
  130. package/src/tui/adapters/webex-adapter.ts +4 -0
@@ -14,6 +14,7 @@ import { WebexClient } from '../client'
14
14
  import { WebexCredentialManager } from '../credential-manager'
15
15
  import { loginWithPassword, WEB_CLIENT_ID, WEB_CLIENT_SECRET } from '../password-login'
16
16
  import { WebexTokenExtractor } from '../token-extractor'
17
+ import type { WebexPerson } from '../types'
17
18
  import { WebexError } from '../types'
18
19
 
19
20
  interface ResolvedCredentials {
@@ -21,6 +22,10 @@ interface ResolvedCredentials {
21
22
  clientSecret: string
22
23
  }
23
24
 
25
+ function formatAuthUser(person: WebexPerson) {
26
+ return { id: person.id, ref: person.ref, displayName: person.displayName, emails: person.emails }
27
+ }
28
+
24
29
  async function openBrowser(url: string): Promise<void> {
25
30
  const { exec } = await import('node:child_process')
26
31
  const command =
@@ -170,7 +175,7 @@ async function loginWithToken(credManager: WebexCredentialManager, token: string
170
175
  console.log(
171
176
  formatOutput(
172
177
  {
173
- user: { id: person.id, displayName: person.displayName, emails: person.emails },
178
+ user: formatAuthUser(person),
174
179
  authenticated: true,
175
180
  },
176
181
  pretty,
@@ -205,7 +210,7 @@ async function loginWithEmailPassword(
205
210
  formatOutput(
206
211
  {
207
212
  authenticated: true,
208
- user: { id: person.id, displayName: person.displayName, emails: person.emails },
213
+ user: formatAuthUser(person),
209
214
  },
210
215
  options.pretty,
211
216
  ),
@@ -259,7 +264,7 @@ export async function oauthAction(options: OAuthOptions): Promise<void> {
259
264
  console.log(
260
265
  formatOutput(
261
266
  {
262
- user: { id: person.id, displayName: person.displayName, emails: person.emails },
267
+ user: formatAuthUser(person),
263
268
  authenticated: true,
264
269
  },
265
270
  options.pretty,
@@ -308,7 +313,7 @@ async function finishDeviceGrant(credManager: WebexCredentialManager, options: O
308
313
  formatOutput(
309
314
  {
310
315
  authenticated: true,
311
- user: { id: person.id, displayName: person.displayName, emails: person.emails },
316
+ user: formatAuthUser(person),
312
317
  },
313
318
  options.pretty,
314
319
  ),
@@ -364,7 +369,7 @@ export async function statusAction(options: { pretty?: boolean }): Promise<void>
364
369
  formatOutput(
365
370
  {
366
371
  authenticated: true,
367
- user: { id: person.id, displayName: person.displayName, emails: person.emails },
372
+ user: formatAuthUser(person),
368
373
  },
369
374
  options.pretty,
370
375
  ),
@@ -436,11 +441,11 @@ export async function extractAction(options: {
436
441
  tokenType: 'extracted',
437
442
  })
438
443
 
439
- let person: { id: string; displayName: string; emails: string[] } | null = null
444
+ let person: ReturnType<typeof formatAuthUser> | null = null
440
445
  try {
441
446
  const result = await client.testAuth()
442
447
  if (result.id) {
443
- person = { id: result.id, displayName: result.displayName, emails: result.emails }
448
+ person = formatAuthUser(result)
444
449
  }
445
450
  } catch (authError) {
446
451
  const isAuthFailure =
@@ -3,20 +3,32 @@ import { afterEach, beforeEach, describe, expect, spyOn, it } from 'bun:test'
3
3
  import { WebexClient } from '../client'
4
4
  import { WebexError } from '../types'
5
5
 
6
+ const restId = (type: string, ref: string) => Buffer.from(`ciscospark://us/${type}/${ref}`).toString('base64url')
7
+ const member1Id = restId('MEMBERSHIP', 'person-1:room-1')
8
+ const member2Id = restId('MEMBERSHIP', 'person-2:room-1')
9
+ const person1Id = restId('PEOPLE', 'person-1')
10
+ const person2Id = restId('PEOPLE', 'person-2')
11
+
6
12
  const mockMembers = [
7
13
  {
8
- id: 'mem-1',
14
+ id: member1Id,
15
+ ref: 'person-1:room-1',
9
16
  roomId: 'room-1',
10
- personId: 'person-1',
17
+ roomRef: 'room-1',
18
+ personId: person1Id,
19
+ personRef: 'person-1',
11
20
  personEmail: 'alice@example.com',
12
21
  personDisplayName: 'Alice',
13
22
  isModerator: true,
14
23
  created: '2024-01-01T00:00:00.000Z',
15
24
  },
16
25
  {
17
- id: 'mem-2',
26
+ id: member2Id,
27
+ ref: 'person-2:room-1',
18
28
  roomId: 'room-1',
19
- personId: 'person-2',
29
+ roomRef: 'room-1',
30
+ personId: person2Id,
31
+ personRef: 'person-2',
20
32
  personEmail: 'bob@example.com',
21
33
  personDisplayName: 'Bob',
22
34
  isModerator: false,
@@ -66,16 +78,20 @@ describe('member commands', () => {
66
78
  expect(consoleSpy).toHaveBeenCalledWith(
67
79
  JSON.stringify([
68
80
  {
69
- id: 'mem-1',
70
- personId: 'person-1',
81
+ id: member1Id,
82
+ ref: 'person-1:room-1',
83
+ personId: person1Id,
84
+ personRef: 'person-1',
71
85
  personEmail: 'alice@example.com',
72
86
  personDisplayName: 'Alice',
73
87
  isModerator: true,
74
88
  created: '2024-01-01T00:00:00.000Z',
75
89
  },
76
90
  {
77
- id: 'mem-2',
78
- personId: 'person-2',
91
+ id: member2Id,
92
+ ref: 'person-2:room-1',
93
+ personId: person2Id,
94
+ personRef: 'person-2',
79
95
  personEmail: 'bob@example.com',
80
96
  personDisplayName: 'Bob',
81
97
  isModerator: false,
@@ -12,7 +12,9 @@ export async function listAction(spaceId: string, options: { limit?: number; pre
12
12
 
13
13
  const output = members.map((m) => ({
14
14
  id: m.id,
15
+ ref: m.ref,
15
16
  personId: m.personId,
17
+ personRef: m.personRef,
16
18
  personEmail: m.personEmail,
17
19
  personDisplayName: m.personDisplayName,
18
20
  isModerator: m.isModerator,
@@ -1,26 +1,38 @@
1
1
  import { afterEach, beforeEach, expect, spyOn, it } from 'bun:test'
2
2
 
3
3
  import { WebexClient } from '../client'
4
+ import { toRestId } from '../id-normalizer'
4
5
  import { WebexError } from '../types'
5
6
 
7
+ const messageId = toRestId('msg_123', 'MESSAGE')
8
+ const message2Id = toRestId('msg_124', 'MESSAGE')
9
+ const roomId = toRestId('space_456', 'ROOM')
10
+ const personId = toRestId('person_789', 'PEOPLE')
11
+
6
12
  const mockMessage = {
7
- id: 'msg_123',
8
- roomId: 'space_456',
13
+ id: messageId,
14
+ ref: 'msg_123',
15
+ roomId,
16
+ roomRef: 'space_456',
9
17
  roomType: 'group' as const,
10
18
  text: 'Hello world',
11
19
  html: '<p>Hello <a href="https://example.com">world</a></p>',
12
- personId: 'person_789',
20
+ personId,
21
+ personRef: 'person_789',
13
22
  personEmail: 'user@example.com',
14
23
  created: '2025-01-29T10:00:00.000Z',
15
24
  }
16
25
 
17
26
  const mockMessage2 = {
18
- id: 'msg_124',
19
- roomId: 'space_456',
27
+ id: message2Id,
28
+ ref: 'msg_124',
29
+ roomId,
30
+ roomRef: 'space_456',
20
31
  roomType: 'group' as const,
21
32
  text: 'Second message',
22
33
  html: '<p>Second message</p>',
23
- personId: 'person_789',
34
+ personId,
35
+ personRef: 'person_789',
24
36
  personEmail: 'user@example.com',
25
37
  created: '2025-01-29T10:01:00.000Z',
26
38
  }
@@ -78,10 +90,12 @@ it('calls sendMessage with correct args and outputs result', async () => {
78
90
  markdown: undefined,
79
91
  })
80
92
  expect(consoleLogSpy).toHaveBeenCalled()
81
- const output = consoleLogSpy.mock.calls[0][0]
82
- expect(output).toContain('msg_123')
83
- expect(output).toContain('space_456')
84
- expect(output).toContain('user@example.com')
93
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
94
+ expect(output.id).toBe(messageId)
95
+ expect(output.ref).toBe('msg_123')
96
+ expect(output.roomId).toBe(roomId)
97
+ expect(output.roomRef).toBe('space_456')
98
+ expect(output.personEmail).toBe('user@example.com')
85
99
  expect(mockDispose).toHaveBeenCalled()
86
100
  })
87
101
 
@@ -115,8 +129,8 @@ it('calls sendDirectMessage with email and text', async () => {
115
129
  markdown: undefined,
116
130
  })
117
131
  expect(consoleLogSpy).toHaveBeenCalled()
118
- const output = consoleLogSpy.mock.calls[0][0]
119
- expect(output).toContain('msg_123')
132
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
133
+ expect(output.ref).toBe('msg_123')
120
134
  })
121
135
 
122
136
  it('passes markdown option to sendDirectMessage when --markdown flag is set', async () => {
@@ -132,10 +146,10 @@ it('calls listMessages with limit and outputs array', async () => {
132
146
 
133
147
  expect(mockListMessages).toHaveBeenCalledWith('space_456', { max: 50 })
134
148
  expect(consoleLogSpy).toHaveBeenCalled()
135
- const output = consoleLogSpy.mock.calls[0][0]
136
- expect(output).toContain('msg_123')
137
- expect(output).toContain('msg_124')
138
- expect(output).toContain('https://example.com')
149
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
150
+ expect(output[0].ref).toBe('msg_123')
151
+ expect(output[1].ref).toBe('msg_124')
152
+ expect(output[0].html).toContain('https://example.com')
139
153
  })
140
154
 
141
155
  it('calls getMessage with correct id and outputs result', async () => {
@@ -143,10 +157,10 @@ it('calls getMessage with correct id and outputs result', async () => {
143
157
 
144
158
  expect(mockGetMessage).toHaveBeenCalledWith('msg_123')
145
159
  expect(consoleLogSpy).toHaveBeenCalled()
146
- const output = consoleLogSpy.mock.calls[0][0]
147
- expect(output).toContain('msg_123')
148
- expect(output).toContain('user@example.com')
149
- expect(output).toContain('https://example.com')
160
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
161
+ expect(output.ref).toBe('msg_123')
162
+ expect(output.personEmail).toBe('user@example.com')
163
+ expect(output.html).toContain('https://example.com')
150
164
  })
151
165
 
152
166
  it('calls deleteMessage and outputs deleted id when --force flag is set', async () => {
@@ -178,9 +192,9 @@ it('calls editMessage with roomId in args and outputs result', async () => {
178
192
  markdown: undefined,
179
193
  })
180
194
  expect(consoleLogSpy).toHaveBeenCalled()
181
- const output = consoleLogSpy.mock.calls[0][0]
182
- expect(output).toContain('msg_123')
183
- expect(output).toContain('Updated message')
195
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string)
196
+ expect(output.ref).toBe('msg_123')
197
+ expect(output.text).toBe('Updated message')
184
198
  expect(mockDispose).toHaveBeenCalled()
185
199
  })
186
200
 
@@ -9,7 +9,9 @@ import type { WebexMessage } from '../types'
9
9
  function formatMessageOutput(message: WebexMessage) {
10
10
  return {
11
11
  id: message.id,
12
+ ref: message.ref,
12
13
  roomId: message.roomId,
14
+ roomRef: message.roomRef,
13
15
  text: message.text,
14
16
  html: message.html,
15
17
  personEmail: message.personEmail,
@@ -3,32 +3,38 @@ import { afterEach, beforeEach, describe, expect, spyOn, it } from 'bun:test'
3
3
  import { WebexClient } from '../client'
4
4
  import { WebexError } from '../types'
5
5
 
6
+ const restId = (type: string, ref: string) => Buffer.from(`ciscospark://us/${type}/${ref}`).toString('base64url')
7
+ const space1Id = restId('ROOM', 'space-1')
8
+ const space2Id = restId('ROOM', 'space-2')
9
+ const member1Id = restId('MEMBERSHIP', 'person-1:space-1')
10
+ const person1Id = restId('PEOPLE', 'person-1')
11
+
6
12
  const mockSpaces = [
7
13
  {
8
- id: 'space-1',
14
+ id: space1Id,
9
15
  title: 'General',
10
16
  type: 'group',
11
17
  isLocked: false,
12
18
  lastActivity: '2024-01-15T00:00:00.000Z',
13
19
  created: '2024-01-01T00:00:00.000Z',
14
- creatorId: 'person-1',
20
+ creatorId: person1Id,
15
21
  },
16
22
  {
17
- id: 'space-2',
23
+ id: space2Id,
18
24
  title: 'Random',
19
25
  type: 'group',
20
26
  isLocked: false,
21
27
  lastActivity: '2024-01-14T00:00:00.000Z',
22
28
  created: '2024-01-01T00:00:00.000Z',
23
- creatorId: 'person-1',
29
+ creatorId: person1Id,
24
30
  },
25
31
  ]
26
32
 
27
33
  const mockMyMemberships = [
28
34
  {
29
- id: 'mem-1',
30
- roomId: 'space-1',
31
- personId: 'person-1',
35
+ id: member1Id,
36
+ roomId: space1Id,
37
+ personId: person1Id,
32
38
  personEmail: 'alice@example.com',
33
39
  personDisplayName: 'Alice',
34
40
  isModerator: true,
@@ -77,7 +83,8 @@ describe('snapshot command', () => {
77
83
  expect(consoleSpy).toHaveBeenCalled()
78
84
  const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
79
85
  expect(output.spaces).toHaveLength(1)
80
- expect(output.spaces[0].id).toBe('space-1')
86
+ expect(output.spaces[0].id).toBe(space1Id)
87
+ expect(output.spaces[0].ref).toBe('space-1')
81
88
  expect(output.spaces[0].title).toBe('General')
82
89
  expect(output.spaces[0].type).toBeUndefined()
83
90
  expect(output.hint).toBeDefined()
@@ -89,7 +96,8 @@ describe('snapshot command', () => {
89
96
  expect(consoleSpy).toHaveBeenCalled()
90
97
  const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
91
98
  expect(output.spaces).toHaveLength(1)
92
- expect(output.spaces[0].id).toBe('space-1')
99
+ expect(output.spaces[0].id).toBe(space1Id)
100
+ expect(output.spaces[0].ref).toBe('space-1')
93
101
  expect(output.spaces[0].title).toBe('General')
94
102
  expect(output.spaces[0].type).toBe('group')
95
103
  expect(output.spaces[0].lastActivity).toBe('2024-01-15T00:00:00.000Z')
@@ -101,7 +109,7 @@ describe('snapshot command', () => {
101
109
 
102
110
  const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
103
111
  expect(output.spaces).toHaveLength(1)
104
- expect(output.spaces[0].id).toBe('space-1')
112
+ expect(output.spaces[0].id).toBe(space1Id)
105
113
  })
106
114
 
107
115
  it('exits with code 1 when not authenticated', async () => {
@@ -4,6 +4,7 @@ import { handleError } from '@/shared/utils/error-handler'
4
4
  import { formatOutput } from '@/shared/utils/output'
5
5
 
6
6
  import { WebexClient } from '../client'
7
+ import { toRef } from '../id-normalizer'
7
8
 
8
9
  export async function snapshotAction(options: { full?: boolean; pretty?: boolean }): Promise<void> {
9
10
  try {
@@ -19,11 +20,12 @@ export async function snapshotAction(options: { full?: boolean; pretty?: boolean
19
20
  spaces: options.full
20
21
  ? spaces.map((s) => ({
21
22
  id: s.id,
23
+ ref: toRef(s.id),
22
24
  title: s.title,
23
25
  type: s.type,
24
26
  lastActivity: s.lastActivity,
25
27
  }))
26
- : spaces.map((s) => ({ id: s.id, title: s.title })),
28
+ : spaces.map((s) => ({ id: s.id, ref: toRef(s.id), title: s.title })),
27
29
  }
28
30
 
29
31
  if (!options.full) {
@@ -3,36 +3,43 @@ import { afterEach, beforeEach, describe, expect, spyOn, it } from 'bun:test'
3
3
  import { WebexClient } from '../client'
4
4
  import { WebexError } from '../types'
5
5
 
6
+ const restId = (type: string, ref: string) => Buffer.from(`ciscospark://us/${type}/${ref}`).toString('base64url')
7
+ const space1Id = restId('ROOM', 'space-1')
8
+ const space2Id = restId('ROOM', 'space-2')
9
+ const person1Id = restId('PEOPLE', 'person-1')
10
+ const person2Id = restId('PEOPLE', 'person-2')
11
+ const teamId = restId('TEAM', 'team-abc')
12
+
6
13
  const mockSpaces = [
7
14
  {
8
- id: 'space-1',
15
+ id: space1Id,
9
16
  title: 'General',
10
17
  type: 'group' as const,
11
18
  isLocked: false,
12
19
  lastActivity: '2024-01-02T00:00:00.000Z',
13
20
  created: '2024-01-01T00:00:00.000Z',
14
- creatorId: 'person-1',
21
+ creatorId: person1Id,
15
22
  },
16
23
  {
17
- id: 'space-2',
24
+ id: space2Id,
18
25
  title: 'Direct with Alice',
19
26
  type: 'direct' as const,
20
27
  isLocked: false,
21
28
  lastActivity: '2024-01-03T00:00:00.000Z',
22
29
  created: '2024-01-01T00:00:00.000Z',
23
- creatorId: 'person-2',
30
+ creatorId: person2Id,
24
31
  },
25
32
  ]
26
33
 
27
34
  const mockSpace = {
28
- id: 'space-1',
35
+ id: space1Id,
29
36
  title: 'General',
30
37
  type: 'group' as const,
31
38
  isLocked: false,
32
- teamId: 'team-abc',
39
+ teamId,
33
40
  lastActivity: '2024-01-02T00:00:00.000Z',
34
41
  created: '2024-01-01T00:00:00.000Z',
35
- creatorId: 'person-1',
42
+ creatorId: person1Id,
36
43
  }
37
44
 
38
45
  import { infoAction, listAction } from './space'
@@ -79,14 +86,16 @@ describe('listAction', () => {
79
86
  expect(consoleLogSpy).toHaveBeenCalledWith(
80
87
  JSON.stringify([
81
88
  {
82
- id: 'space-1',
89
+ id: space1Id,
90
+ ref: 'space-1',
83
91
  title: 'General',
84
92
  type: 'group',
85
93
  lastActivity: '2024-01-02T00:00:00.000Z',
86
94
  created: '2024-01-01T00:00:00.000Z',
87
95
  },
88
96
  {
89
- id: 'space-2',
97
+ id: space2Id,
98
+ ref: 'space-2',
90
99
  title: 'Direct with Alice',
91
100
  type: 'direct',
92
101
  lastActivity: '2024-01-03T00:00:00.000Z',
@@ -115,14 +124,16 @@ describe('listAction', () => {
115
124
  JSON.stringify(
116
125
  [
117
126
  {
118
- id: 'space-1',
127
+ id: space1Id,
128
+ ref: 'space-1',
119
129
  title: 'General',
120
130
  type: 'group',
121
131
  lastActivity: '2024-01-02T00:00:00.000Z',
122
132
  created: '2024-01-01T00:00:00.000Z',
123
133
  },
124
134
  {
125
- id: 'space-2',
135
+ id: space2Id,
136
+ ref: 'space-2',
126
137
  title: 'Direct with Alice',
127
138
  type: 'direct',
128
139
  lastActivity: '2024-01-03T00:00:00.000Z',
@@ -152,14 +163,17 @@ describe('infoAction', () => {
152
163
  expect(mockGetSpace).toHaveBeenCalledWith('space-1')
153
164
  expect(consoleLogSpy).toHaveBeenCalledWith(
154
165
  JSON.stringify({
155
- id: 'space-1',
166
+ id: space1Id,
167
+ ref: 'space-1',
156
168
  title: 'General',
157
169
  type: 'group',
158
170
  isLocked: false,
159
- teamId: 'team-abc',
171
+ teamId,
172
+ teamRef: 'team-abc',
160
173
  lastActivity: '2024-01-02T00:00:00.000Z',
161
174
  created: '2024-01-01T00:00:00.000Z',
162
- creatorId: 'person-1',
175
+ creatorId: person1Id,
176
+ creatorRef: 'person-1',
163
177
  }),
164
178
  )
165
179
  })
@@ -171,8 +185,10 @@ describe('infoAction', () => {
171
185
 
172
186
  const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string) as {
173
187
  teamId: null
188
+ teamRef: null
174
189
  }
175
190
  expect(output.teamId).toBeNull()
191
+ expect(output.teamRef).toBeNull()
176
192
  })
177
193
 
178
194
  it('outputs pretty-printed JSON when pretty is true', async () => {
@@ -181,14 +197,17 @@ describe('infoAction', () => {
181
197
  expect(consoleLogSpy).toHaveBeenCalledWith(
182
198
  JSON.stringify(
183
199
  {
184
- id: 'space-1',
200
+ id: space1Id,
201
+ ref: 'space-1',
185
202
  title: 'General',
186
203
  type: 'group',
187
204
  isLocked: false,
188
- teamId: 'team-abc',
205
+ teamId,
206
+ teamRef: 'team-abc',
189
207
  lastActivity: '2024-01-02T00:00:00.000Z',
190
208
  created: '2024-01-01T00:00:00.000Z',
191
- creatorId: 'person-1',
209
+ creatorId: person1Id,
210
+ creatorRef: 'person-1',
192
211
  },
193
212
  null,
194
213
  2,
@@ -4,6 +4,7 @@ import { handleError } from '@/shared/utils/error-handler'
4
4
  import { formatOutput } from '@/shared/utils/output'
5
5
 
6
6
  import { WebexClient } from '../client'
7
+ import { toRef } from '../id-normalizer'
7
8
 
8
9
  export async function listAction(options: { type?: string; limit?: number; pretty?: boolean }): Promise<void> {
9
10
  try {
@@ -11,6 +12,7 @@ export async function listAction(options: { type?: string; limit?: number; prett
11
12
  const spaces = await client.listSpaces({ type: options.type, max: options.limit })
12
13
  const output = spaces.map((s) => ({
13
14
  id: s.id,
15
+ ref: toRef(s.id),
14
16
  title: s.title,
15
17
  type: s.type,
16
18
  lastActivity: s.lastActivity,
@@ -28,13 +30,16 @@ export async function infoAction(spaceId: string, options: { pretty?: boolean })
28
30
  const space = await client.getSpace(spaceId)
29
31
  const output = {
30
32
  id: space.id,
33
+ ref: toRef(space.id),
31
34
  title: space.title,
32
35
  type: space.type,
33
36
  isLocked: space.isLocked,
34
37
  teamId: space.teamId || null,
38
+ teamRef: space.teamId ? toRef(space.teamId) : null,
35
39
  lastActivity: space.lastActivity,
36
40
  created: space.created,
37
41
  creatorId: space.creatorId,
42
+ creatorRef: toRef(space.creatorId),
38
43
  }
39
44
  console.log(formatOutput(output, options.pretty))
40
45
  } catch (error) {
@@ -1,18 +1,24 @@
1
1
  import { afterEach, beforeEach, expect, spyOn, it } from 'bun:test'
2
2
 
3
3
  import { WebexClient } from '../client'
4
+ import { toRestId } from '../id-normalizer'
4
5
  import { WebexError } from '../types'
5
6
  import { whoamiCommand } from './whoami'
6
7
 
8
+ const orgId = Buffer.from('ciscospark://us/ORGANIZATION/org-123').toString('base64url')
9
+ const personId = toRestId('person-123', 'PEOPLE')
10
+
7
11
  const mockUser = {
8
- id: 'person-123',
12
+ id: personId,
13
+ ref: 'person-123',
9
14
  emails: ['test@example.com'],
10
15
  displayName: 'Test User',
11
16
  nickName: 'Testy',
12
17
  firstName: 'Test',
13
18
  lastName: 'User',
14
19
  avatar: 'https://example.com/avatar.jpg',
15
- orgId: 'org-123',
20
+ orgId,
21
+ orgRef: 'org-123',
16
22
  type: 'person' as const,
17
23
  created: '2024-01-01T00:00:00.000Z',
18
24
  }
@@ -58,14 +64,16 @@ it('whoami calls testAuth and outputs user fields', async () => {
58
64
  // then: outputs all expected fields
59
65
  expect(consoleLogSpy).toHaveBeenCalledWith(
60
66
  JSON.stringify({
61
- id: 'person-123',
67
+ id: personId,
68
+ ref: 'person-123',
62
69
  emails: ['test@example.com'],
63
70
  displayName: 'Test User',
64
71
  nickName: 'Testy',
65
72
  firstName: 'Test',
66
73
  lastName: 'User',
67
74
  avatar: 'https://example.com/avatar.jpg',
68
- orgId: 'org-123',
75
+ orgId,
76
+ orgRef: 'org-123',
69
77
  type: 'person',
70
78
  }),
71
79
  )
@@ -80,14 +88,16 @@ it('whoami outputs pretty-printed JSON when --pretty flag is passed', async () =
80
88
  expect(consoleLogSpy).toHaveBeenCalledWith(
81
89
  JSON.stringify(
82
90
  {
83
- id: 'person-123',
91
+ id: personId,
92
+ ref: 'person-123',
84
93
  emails: ['test@example.com'],
85
94
  displayName: 'Test User',
86
95
  nickName: 'Testy',
87
96
  firstName: 'Test',
88
97
  lastName: 'User',
89
98
  avatar: 'https://example.com/avatar.jpg',
90
- orgId: 'org-123',
99
+ orgId,
100
+ orgRef: 'org-123',
91
101
  type: 'person',
92
102
  },
93
103
  null,
@@ -12,6 +12,7 @@ export async function whoamiAction(options: { pretty?: boolean }): Promise<void>
12
12
 
13
13
  const output = {
14
14
  id: user.id,
15
+ ref: user.ref,
15
16
  emails: user.emails,
16
17
  displayName: user.displayName,
17
18
  nickName: user.nickName,
@@ -19,6 +20,7 @@ export async function whoamiAction(options: { pretty?: boolean }): Promise<void>
19
20
  lastName: user.lastName,
20
21
  avatar: user.avatar,
21
22
  orgId: user.orgId,
23
+ orgRef: user.orgRef,
22
24
  type: user.type,
23
25
  }
24
26
  console.log(formatOutput(output, options.pretty))