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
@@ -13,21 +13,27 @@ export interface WebexSpace {
13
13
 
14
14
  export interface WebexMessage {
15
15
  id: string
16
+ ref: string
16
17
  roomId: string
18
+ roomRef: string
17
19
  roomType: 'group' | 'direct'
18
20
  text?: string
19
21
  markdown?: string
20
22
  html?: string
21
23
  files?: string[]
22
24
  personId: string
25
+ personRef: string
23
26
  personEmail: string
24
27
  created: string
25
28
  parentId?: string
29
+ parentRef?: string
26
30
  mentionedPeople?: string[]
31
+ mentionedPeopleRefs?: string[]
27
32
  }
28
33
 
29
34
  export interface WebexPerson {
30
35
  id: string
36
+ ref: string
31
37
  emails: string[]
32
38
  displayName: string
33
39
  nickName?: string
@@ -35,14 +41,18 @@ export interface WebexPerson {
35
41
  lastName?: string
36
42
  avatar?: string
37
43
  orgId: string
44
+ orgRef: string
38
45
  type: 'person' | 'bot'
39
46
  created: string
40
47
  }
41
48
 
42
49
  export interface WebexMembership {
43
50
  id: string
51
+ ref: string
44
52
  roomId: string
53
+ roomRef: string
45
54
  personId: string
55
+ personRef: string
46
56
  personEmail: string
47
57
  personDisplayName: string
48
58
  isModerator: boolean
@@ -84,21 +94,27 @@ export const WebexSpaceSchema = z.object({
84
94
 
85
95
  export const WebexMessageSchema = z.object({
86
96
  id: z.string(),
97
+ ref: z.string(),
87
98
  roomId: z.string(),
99
+ roomRef: z.string(),
88
100
  roomType: z.enum(['group', 'direct']),
89
101
  text: z.string().optional(),
90
102
  markdown: z.string().optional(),
91
103
  html: z.string().optional(),
92
104
  files: z.array(z.string()).optional(),
93
105
  personId: z.string(),
106
+ personRef: z.string(),
94
107
  personEmail: z.string(),
95
108
  created: z.string(),
96
109
  parentId: z.string().optional(),
110
+ parentRef: z.string().optional(),
97
111
  mentionedPeople: z.array(z.string()).optional(),
112
+ mentionedPeopleRefs: z.array(z.string()).optional(),
98
113
  })
99
114
 
100
115
  export const WebexPersonSchema = z.object({
101
116
  id: z.string(),
117
+ ref: z.string(),
102
118
  emails: z.array(z.string()),
103
119
  displayName: z.string(),
104
120
  nickName: z.string().optional(),
@@ -106,14 +122,18 @@ export const WebexPersonSchema = z.object({
106
122
  lastName: z.string().optional(),
107
123
  avatar: z.string().optional(),
108
124
  orgId: z.string(),
125
+ orgRef: z.string(),
109
126
  type: z.enum(['person', 'bot']),
110
127
  created: z.string(),
111
128
  })
112
129
 
113
130
  export const WebexMembershipSchema = z.object({
114
131
  id: z.string(),
132
+ ref: z.string(),
115
133
  roomId: z.string(),
134
+ roomRef: z.string(),
116
135
  personId: z.string(),
136
+ personRef: z.string(),
117
137
  personEmail: z.string(),
118
138
  personDisplayName: z.string(),
119
139
  isModerator: z.boolean(),
@@ -155,6 +155,8 @@ declare module 'webex-message-handler' {
155
155
  export interface DecryptedMessage {
156
156
  /** Mercury activity UUID. Works as parentId for threaded replies. */
157
157
  id: string
158
+ /** Raw UUID ref for `id`. */
159
+ ref: string
158
160
  /**
159
161
  * Full Conversation-service activity URL, when present on the raw Mercury
160
162
  * activity (e.g. for an outbound "acknowledge" read-receipt). Undefined if
@@ -163,8 +165,14 @@ declare module 'webex-message-handler' {
163
165
  url?: string
164
166
  /** Parent activity UUID for threaded replies. Undefined if not a thread reply. */
165
167
  parentId?: string
168
+ /** Raw UUID ref for `parentId`. Undefined when `parentId` is absent. */
169
+ parentRef?: string
166
170
  roomId: string
171
+ /** Raw UUID ref for `roomId`. */
172
+ roomRef: string
167
173
  personId: string
174
+ /** Raw UUID ref for `personId`. */
175
+ personRef: string
168
176
  personEmail: string
169
177
  text: string
170
178
  html?: string
@@ -172,6 +180,8 @@ declare module 'webex-message-handler' {
172
180
  roomType?: string
173
181
  /** Person UUIDs mentioned via @mention in the message. */
174
182
  mentionedPeople: string[]
183
+ /** Raw UUID refs for `mentionedPeople`, index-aligned. */
184
+ mentionedPeopleRefs: string[]
175
185
  /** Group mention types (e.g. "all") in the message. */
176
186
  mentionedGroups: string[]
177
187
  /** File URLs attached to the message. Empty if no files. */
@@ -181,19 +191,33 @@ declare module 'webex-message-handler' {
181
191
 
182
192
  export interface DeletedMessage {
183
193
  messageId: string
194
+ /** Raw UUID ref for `messageId`. */
195
+ messageRef: string
184
196
  roomId: string
197
+ /** Raw UUID ref for `roomId`. */
198
+ roomRef: string
185
199
  personId: string
200
+ /** Raw UUID ref for `personId`. */
201
+ personRef: string
186
202
  }
187
203
 
188
204
  export interface MembershipActivity {
189
- /** Activity ID. */
205
+ /** Activity ID (raw Mercury activity UUID). */
190
206
  id: string
207
+ /** Raw UUID ref for `id`. Equal to `id` since the activity id is already raw. */
208
+ ref: string
191
209
  /** ID of the person who performed the action. */
192
210
  actorId: string
211
+ /** Raw UUID ref for `actorId`. */
212
+ actorRef: string
193
213
  /** ID of the member affected. */
194
214
  personId: string
215
+ /** Raw UUID ref for `personId`. */
216
+ personRef: string
195
217
  /** Conversation/space ID. */
196
218
  roomId: string
219
+ /** Raw UUID ref for `roomId`. */
220
+ roomRef: string
197
221
  /** Membership action: "add", "leave", "assignModerator", or "unassignModerator". */
198
222
  action: string
199
223
  /** ISO 8601 timestamp. */
@@ -207,14 +231,22 @@ declare module 'webex-message-handler' {
207
231
  export interface AttachmentAction {
208
232
  /** Activity ID. */
209
233
  id: string
234
+ /** Raw UUID ref for `id`. */
235
+ ref: string
210
236
  /** ID of the message the card was attached to. */
211
237
  messageId: string
238
+ /** Raw UUID ref for `messageId`. Empty when `messageId` is empty. */
239
+ messageRef: string
212
240
  /** ID of the person who submitted the card. */
213
241
  personId: string
242
+ /** Raw UUID ref for `personId`. */
243
+ personRef: string
214
244
  /** Email of the person who submitted the card. */
215
245
  personEmail: string
216
246
  /** Conversation/space ID. */
217
247
  roomId: string
248
+ /** Raw UUID ref for `roomId`. */
249
+ roomRef: string
218
250
  /** Card form input values. */
219
251
  inputs: Record<string, unknown>
220
252
  /** ISO 8601 timestamp. */
@@ -224,12 +256,18 @@ declare module 'webex-message-handler' {
224
256
  }
225
257
 
226
258
  export interface RoomActivity {
227
- /** Activity ID. */
259
+ /** Activity ID (raw Mercury activity UUID). */
228
260
  id: string
261
+ /** Raw UUID ref for `id`. Equal to `id` since the activity id is already raw. */
262
+ ref: string
229
263
  /** Conversation/space ID. */
230
264
  roomId: string
265
+ /** Raw UUID ref for `roomId`. */
266
+ roomRef: string
231
267
  /** ID of the person who performed the action. */
232
268
  actorId: string
269
+ /** Raw UUID ref for `actorId`. */
270
+ actorRef: string
233
271
  /** Room action: "created" or "updated". */
234
272
  action: string
235
273
  /** ISO 8601 timestamp. */
@@ -2,33 +2,10 @@ import { WebexClient } from '../webex/client'
2
2
  import type { WebexMembership, WebexMessage, WebexPerson, WebexSpace } from '../webex/types'
3
3
  import { WebexBotError } from './types'
4
4
 
5
- interface DecodedWebexId {
6
- cluster: string
7
- type: string
8
- uuid: string
9
- }
10
-
11
- // Webex REST ids are base64(url) of `ciscospark://<cluster>/<TYPE>/<uuid>`; the
12
- // cluster correction needs all three parts, not just the <uuid> `fromRestId` returns.
13
- function decodeWebexId(restId: string): DecodedWebexId | null {
14
- if (!restId) return null
15
- const decoded = Buffer.from(restId, 'base64').toString('utf-8')
16
- const match = decoded.match(/^ciscospark:\/\/([^/]+)\/([^/]+)\/(.+)$/)
17
- if (!match) return null
18
- return { cluster: match[1], type: match[2], uuid: match[3] }
19
- }
20
-
21
5
  export class WebexBotClient {
22
- private client = new WebexClient()
6
+ private client = new WebexClient({ roomResolutionWarningPrefix: '[webexbot]' })
23
7
  private token: string | null = null
24
8
 
25
- // The listener flattens room ids to `ciscospark://us/ROOM/<uuid>`, but team/group
26
- // rooms live on `ciscospark://urn:TEAM:<cluster>/ROOM/<uuid>` — a cluster the bare
27
- // uuid cannot recover. Cache the real clustered id per uuid and dedupe concurrent
28
- // lookups so a burst of calls triggers a single `listSpaces`.
29
- private clusteredRoomIds = new Map<string, string>()
30
- private roomIdLookups = new Map<string, Promise<string>>()
31
-
32
9
  async login(credentials?: { token: string }): Promise<this> {
33
10
  if (credentials) {
34
11
  if (!credentials.token) {
@@ -64,7 +41,7 @@ export class WebexBotClient {
64
41
  }
65
42
 
66
43
  async getSpace(spaceId: string): Promise<WebexSpace> {
67
- return this.client.getSpace(await this.resolveRoomId(spaceId))
44
+ return this.client.getSpace(spaceId)
68
45
  }
69
46
 
70
47
  async sendMessage(
@@ -72,7 +49,7 @@ export class WebexBotClient {
72
49
  text: string,
73
50
  options?: { markdown?: boolean; parentId?: string; files?: string[] },
74
51
  ): Promise<WebexMessage> {
75
- return this.client.sendMessage(await this.resolveRoomId(roomId), text, options)
52
+ return this.client.sendMessage(roomId, text, options)
76
53
  }
77
54
 
78
55
  async sendDirectMessage(personEmail: string, text: string, options?: { markdown?: boolean }): Promise<WebexMessage> {
@@ -80,14 +57,14 @@ export class WebexBotClient {
80
57
  }
81
58
 
82
59
  async listMessages(roomId: string, options?: { max?: number; parentId?: string }): Promise<WebexMessage[]> {
83
- const resolvedRoomId = await this.resolveRoomId(roomId)
60
+ const resolvedRoomId = await this.client.resolveRoomId(roomId)
84
61
  const space = await this.client.getSpace(resolvedRoomId)
85
62
  const messageOptions = space.type === 'group' ? { ...options, mentionedPeople: 'me' } : options
86
63
  return this.client.listMessages(resolvedRoomId, messageOptions)
87
64
  }
88
65
 
89
66
  async listReplies(roomId: string, parentId: string, options?: { max?: number }): Promise<WebexMessage[]> {
90
- return this.client.listMessages(await this.resolveRoomId(roomId), { ...options, parentId })
67
+ return this.client.listMessages(roomId, { ...options, parentId })
91
68
  }
92
69
 
93
70
  async getMessage(messageId: string): Promise<WebexMessage> {
@@ -108,7 +85,7 @@ export class WebexBotClient {
108
85
  text: string,
109
86
  options?: { markdown?: boolean },
110
87
  ): Promise<WebexMessage> {
111
- return this.client.editMessage(messageId, await this.resolveRoomId(roomId), text, options)
88
+ return this.client.editMessage(messageId, roomId, text, options)
112
89
  }
113
90
 
114
91
  async listPeople(options?: { email?: string; displayName?: string; max?: number }): Promise<WebexPerson[]> {
@@ -124,7 +101,7 @@ export class WebexBotClient {
124
101
  }
125
102
 
126
103
  async listMemberships(roomId: string, options?: { max?: number }): Promise<WebexMembership[]> {
127
- return this.client.listMemberships(await this.resolveRoomId(roomId), options)
104
+ return this.client.listMemberships(roomId, options)
128
105
  }
129
106
 
130
107
  async uploadFile(
@@ -132,53 +109,10 @@ export class WebexBotClient {
132
109
  file: { content: Blob; filename: string },
133
110
  options?: { text?: string; markdown?: boolean; parentId?: string },
134
111
  ): Promise<WebexMessage> {
135
- return this.client.uploadFile(await this.resolveRoomId(roomId), file, options)
112
+ return this.client.uploadFile(roomId, file, options)
136
113
  }
137
114
 
138
115
  async downloadContent(contentRef: string): Promise<{ data: ArrayBuffer; filename: string; contentType: string }> {
139
116
  return this.client.downloadContent(contentRef)
140
117
  }
141
-
142
- private async resolveRoomId(roomId: string): Promise<string> {
143
- const decoded = decodeWebexId(roomId)
144
- // Already cluster-qualified or undecodable: nothing to correct.
145
- if (!decoded || decoded.cluster.startsWith('urn:')) return roomId
146
-
147
- const { uuid } = decoded
148
- const cached = this.clusteredRoomIds.get(uuid)
149
- if (cached) return cached
150
-
151
- const inFlight = this.roomIdLookups.get(uuid)
152
- if (inFlight) return inFlight
153
-
154
- const lookup = this.lookupRoomId(uuid, roomId)
155
- this.roomIdLookups.set(uuid, lookup)
156
- try {
157
- return await lookup
158
- } finally {
159
- this.roomIdLookups.delete(uuid)
160
- }
161
- }
162
-
163
- private async lookupRoomId(uuid: string, fallback: string): Promise<string> {
164
- try {
165
- // Page through every room the bot belongs to (largest page size, following
166
- // `Link` pages), stopping as soon as the trailing UUID matches.
167
- for await (const room of this.client.iterateSpaces({ max: 1000 })) {
168
- if (decodeWebexId(room.id)?.uuid === uuid) {
169
- this.clusteredRoomIds.set(uuid, room.id)
170
- return room.id
171
- }
172
- }
173
- } catch {
174
- // Network/auth failure: fail open to the un-corrected id rather than block the call.
175
- return fallback
176
- }
177
-
178
- console.warn(
179
- `[webexbot] Could not resolve clustered room id for ${uuid}; falling back to the un-clustered id. ` +
180
- 'Room-scoped calls may fail if this room lives on a non-default Webex cluster.',
181
- )
182
- return fallback
183
- }
184
118
  }
@@ -10,7 +10,9 @@ import { getClient } from './shared'
10
10
 
11
11
  interface FileResult {
12
12
  id?: string
13
+ ref?: string
13
14
  roomId?: string
15
+ roomRef?: string
14
16
  files?: string[]
15
17
  created?: string
16
18
  downloaded?: string
@@ -37,7 +39,9 @@ export async function uploadAction(
37
39
 
38
40
  return {
39
41
  id: message.id,
42
+ ref: message.ref,
40
43
  roomId: message.roomId,
44
+ roomRef: message.roomRef,
41
45
  files: message.files,
42
46
  created: message.created,
43
47
  }
@@ -8,7 +8,9 @@ import { getClient } from './shared'
8
8
  interface MemberResult {
9
9
  members?: Array<{
10
10
  id: string
11
+ ref: string
11
12
  personId: string
13
+ personRef: string
12
14
  personEmail: string
13
15
  personDisplayName: string
14
16
  isModerator: boolean
@@ -26,7 +28,9 @@ export async function listAction(space: string, options: BotOption & { max?: str
26
28
  return {
27
29
  members: members.map((m) => ({
28
30
  id: m.id,
31
+ ref: m.ref,
29
32
  personId: m.personId,
33
+ personRef: m.personRef,
30
34
  personEmail: m.personEmail,
31
35
  personDisplayName: m.personDisplayName,
32
36
  isModerator: m.isModerator,
@@ -8,7 +8,9 @@ import { getClient } from './shared'
8
8
 
9
9
  interface MessageResult {
10
10
  id?: string
11
+ ref?: string
11
12
  roomId?: string
13
+ roomRef?: string
12
14
  text?: string
13
15
  markdown?: string
14
16
  html?: string
@@ -16,7 +18,9 @@ interface MessageResult {
16
18
  created?: string
17
19
  messages?: Array<{
18
20
  id: string
21
+ ref: string
19
22
  roomId: string
23
+ roomRef: string
20
24
  text?: string
21
25
  personEmail: string
22
26
  created: string
@@ -28,7 +32,9 @@ interface MessageResult {
28
32
  function formatMessage(message: WebexMessage): MessageResult {
29
33
  return {
30
34
  id: message.id,
35
+ ref: message.ref,
31
36
  roomId: message.roomId,
37
+ roomRef: message.roomRef,
32
38
  text: message.text,
33
39
  markdown: message.markdown,
34
40
  html: message.html,
@@ -81,7 +87,9 @@ export async function repliesAction(
81
87
  return {
82
88
  messages: messages.map((msg) => ({
83
89
  id: msg.id,
90
+ ref: msg.ref,
84
91
  roomId: msg.roomId,
92
+ roomRef: msg.roomRef,
85
93
  text: msg.text,
86
94
  personEmail: msg.personEmail,
87
95
  created: msg.created,
@@ -116,7 +124,9 @@ export async function listAction(space: string, options: BotOption & { max?: str
116
124
  return {
117
125
  messages: messages.map((msg) => ({
118
126
  id: msg.id,
127
+ ref: msg.ref,
119
128
  roomId: msg.roomId,
129
+ roomRef: msg.roomRef,
120
130
  text: msg.text,
121
131
  personEmail: msg.personEmail,
122
132
  created: msg.created,
@@ -2,17 +2,20 @@ import { Command } from 'commander'
2
2
 
3
3
  import { cliOutput } from '@/shared/utils/cli-output'
4
4
 
5
+ import { toRef } from '../../webex/id-normalizer'
5
6
  import type { BotOption } from './shared'
6
7
  import { getClient } from './shared'
7
8
 
8
9
  interface SnapshotResult {
9
10
  bot?: {
10
11
  id: string
12
+ ref: string
11
13
  displayName: string
12
14
  emails: string[]
13
15
  }
14
16
  spaces?: Array<{
15
17
  id: string
18
+ ref: string
16
19
  title: string
17
20
  type?: 'group' | 'direct'
18
21
  lastActivity?: string
@@ -31,12 +34,19 @@ export async function snapshotAction(options: BotOption & { full?: boolean; max?
31
34
  const result: SnapshotResult = {
32
35
  bot: {
33
36
  id: me.id,
37
+ ref: me.ref,
34
38
  displayName: me.displayName,
35
39
  emails: me.emails,
36
40
  },
37
41
  spaces: options.full
38
- ? spaces.map((s) => ({ id: s.id, title: s.title, type: s.type, lastActivity: s.lastActivity }))
39
- : spaces.map((s) => ({ id: s.id, title: s.title })),
42
+ ? spaces.map((s) => ({
43
+ id: s.id,
44
+ ref: toRef(s.id),
45
+ title: s.title,
46
+ type: s.type,
47
+ lastActivity: s.lastActivity,
48
+ }))
49
+ : spaces.map((s) => ({ id: s.id, ref: toRef(s.id), title: s.title })),
40
50
  }
41
51
 
42
52
  if (!options.full) {
@@ -2,20 +2,25 @@ import { Command } from 'commander'
2
2
 
3
3
  import { cliOutput } from '@/shared/utils/cli-output'
4
4
 
5
+ import { toRef } from '../../webex/id-normalizer'
5
6
  import type { BotOption } from './shared'
6
7
  import { getClient } from './shared'
7
8
 
8
9
  interface SpaceResult {
9
10
  id?: string
11
+ ref?: string
10
12
  title?: string
11
13
  type?: 'group' | 'direct'
12
14
  isLocked?: boolean
13
15
  teamId?: string | null
16
+ teamRef?: string | null
14
17
  lastActivity?: string
15
18
  created?: string
16
19
  creatorId?: string
20
+ creatorRef?: string
17
21
  spaces?: Array<{
18
22
  id: string
23
+ ref: string
19
24
  title: string
20
25
  type: 'group' | 'direct'
21
26
  lastActivity: string
@@ -33,6 +38,7 @@ export async function listAction(options: BotOption & { max?: string; type?: str
33
38
  return {
34
39
  spaces: spaces.map((s) => ({
35
40
  id: s.id,
41
+ ref: toRef(s.id),
36
42
  title: s.title,
37
43
  type: s.type,
38
44
  lastActivity: s.lastActivity,
@@ -50,13 +56,16 @@ export async function infoAction(spaceId: string, options: BotOption): Promise<S
50
56
  const space = await client.getSpace(spaceId)
51
57
  return {
52
58
  id: space.id,
59
+ ref: toRef(space.id),
53
60
  title: space.title,
54
61
  type: space.type,
55
62
  isLocked: space.isLocked,
56
63
  teamId: space.teamId || null,
64
+ teamRef: space.teamId ? toRef(space.teamId) : null,
57
65
  lastActivity: space.lastActivity,
58
66
  created: space.created,
59
67
  creatorId: space.creatorId,
68
+ creatorRef: toRef(space.creatorId),
60
69
  }
61
70
  } catch (error) {
62
71
  return { error: (error as Error).message }
@@ -4,13 +4,19 @@ import { mkdir } from 'node:fs/promises'
4
4
  import { tmpdir } from 'node:os'
5
5
  import { join } from 'node:path'
6
6
 
7
+ const restId = (type: string, ref: string) => Buffer.from(`ciscospark://us/${type}/${ref}`).toString('base64url')
8
+ const personId = restId('PEOPLE', 'p1')
9
+ const orgId = restId('ORGANIZATION', 'o1')
10
+
7
11
  const mockListPeople = mock(() =>
8
12
  Promise.resolve([
9
13
  {
10
- id: 'p1',
14
+ id: personId,
15
+ ref: 'p1',
11
16
  emails: ['alice@example.com'],
12
17
  displayName: 'Alice',
13
- orgId: 'o1',
18
+ orgId,
19
+ orgRef: 'o1',
14
20
  type: 'person' as const,
15
21
  created: '',
16
22
  },
@@ -19,10 +25,12 @@ const mockListPeople = mock(() =>
19
25
 
20
26
  const mockGetPerson = mock(() =>
21
27
  Promise.resolve({
22
- id: 'p1',
28
+ id: personId,
29
+ ref: 'p1',
23
30
  emails: ['alice@example.com'],
24
31
  displayName: 'Alice',
25
- orgId: 'o1',
32
+ orgId,
33
+ orgRef: 'o1',
26
34
  type: 'person' as const,
27
35
  created: '2024-01-01T00:00:00Z',
28
36
  }),
@@ -65,13 +73,15 @@ describe('webexbot user commands', () => {
65
73
 
66
74
  expect(result.users).toHaveLength(1)
67
75
  expect(result.users?.[0].displayName).toBe('Alice')
76
+ expect(result.users?.[0].ref).toBe('p1')
68
77
  expect(mockListPeople).toHaveBeenCalled()
69
78
  })
70
79
 
71
80
  it('gets a person by id', async () => {
72
81
  const result = await infoAction('p1', { _credManager: manager })
73
82
 
74
- expect(result.id).toBe('p1')
83
+ expect(result.id).toBe(personId)
84
+ expect(result.ref).toBe('p1')
75
85
  expect(result.displayName).toBe('Alice')
76
86
  })
77
87
  })
@@ -8,6 +8,7 @@ import { getClient } from './shared'
8
8
 
9
9
  interface UserResult {
10
10
  id?: string
11
+ ref?: string
11
12
  emails?: string[]
12
13
  displayName?: string
13
14
  nickName?: string
@@ -15,10 +16,12 @@ interface UserResult {
15
16
  lastName?: string
16
17
  avatar?: string
17
18
  orgId?: string
19
+ orgRef?: string
18
20
  type?: 'person' | 'bot'
19
21
  created?: string
20
22
  users?: Array<{
21
23
  id: string
24
+ ref: string
22
25
  emails: string[]
23
26
  displayName: string
24
27
  type: 'person' | 'bot'
@@ -29,6 +32,7 @@ interface UserResult {
29
32
  function formatPerson(person: WebexPerson): UserResult {
30
33
  return {
31
34
  id: person.id,
35
+ ref: person.ref,
32
36
  emails: person.emails,
33
37
  displayName: person.displayName,
34
38
  nickName: person.nickName,
@@ -36,6 +40,7 @@ function formatPerson(person: WebexPerson): UserResult {
36
40
  lastName: person.lastName,
37
41
  avatar: person.avatar,
38
42
  orgId: person.orgId,
43
+ orgRef: person.orgRef,
39
44
  type: person.type,
40
45
  created: person.created,
41
46
  }
@@ -52,6 +57,7 @@ export async function listAction(
52
57
  return {
53
58
  users: people.map((p) => ({
54
59
  id: p.id,
60
+ ref: p.ref,
55
61
  emails: p.emails,
56
62
  displayName: p.displayName,
57
63
  type: p.type,
@@ -7,10 +7,12 @@ import { getClient } from './shared'
7
7
 
8
8
  interface WhoamiResult {
9
9
  id?: string
10
+ ref?: string
10
11
  emails?: string[]
11
12
  displayName?: string
12
13
  avatar?: string
13
14
  orgId?: string
15
+ orgRef?: string
14
16
  type?: 'person' | 'bot'
15
17
  created?: string
16
18
  error?: string
@@ -22,10 +24,12 @@ export async function whoamiAction(options: BotOption): Promise<WhoamiResult> {
22
24
  const info = await client.testAuth()
23
25
  return {
24
26
  id: info.id,
27
+ ref: info.ref,
25
28
  emails: info.emails,
26
29
  displayName: info.displayName,
27
30
  avatar: info.avatar,
28
31
  orgId: info.orgId,
32
+ orgRef: info.orgRef,
29
33
  type: info.type,
30
34
  created: info.created,
31
35
  }
@@ -1,7 +1,7 @@
1
1
  export { WebexBotClient } from './client'
2
2
  export { WebexBotCredentialManager } from './credential-manager'
3
- export { fromRestId, toRestId } from '../webex/id-normalizer'
4
- export type { WebexRestIdType } from '../webex/id-normalizer'
3
+ export { decodeWebexId, fromRestId, toRef, toRestId } from '../webex/id-normalizer'
4
+ export type { DecodedWebexId, WebexRestIdType } from '../webex/id-normalizer'
5
5
  export { WebexBotListener } from './listener'
6
6
  export type { WebexBotListenerOptions } from './listener'
7
7
  export type { WebexBotConfig, WebexBotCredentials, WebexBotEntry, WebexBotListenerEventMap } from './types'
@@ -1,12 +1,15 @@
1
1
  export interface UnifiedChannel {
2
2
  id: string
3
+ ref?: string
3
4
  name: string
4
5
  parentId?: string
5
6
  }
6
7
 
7
8
  export interface UnifiedMessage {
8
9
  id: string
10
+ ref?: string
9
11
  channelId: string
12
+ channelRef?: string
10
13
  author: string
11
14
  content: string
12
15
  timestamp: string