agent-messenger 2.20.4 → 2.21.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 (115) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +8 -5
  3. package/bun.lock +2 -2
  4. package/dist/package.json +10 -2
  5. package/dist/src/cli.d.ts.map +1 -1
  6. package/dist/src/cli.js +3 -0
  7. package/dist/src/cli.js.map +1 -1
  8. package/dist/src/platforms/webexbot/cli.d.ts +5 -0
  9. package/dist/src/platforms/webexbot/cli.d.ts.map +1 -0
  10. package/dist/src/platforms/webexbot/cli.js +30 -0
  11. package/dist/src/platforms/webexbot/cli.js.map +1 -0
  12. package/dist/src/platforms/webexbot/client.d.ts +41 -0
  13. package/dist/src/platforms/webexbot/client.d.ts.map +1 -0
  14. package/dist/src/platforms/webexbot/client.js +66 -0
  15. package/dist/src/platforms/webexbot/client.js.map +1 -0
  16. package/dist/src/platforms/webexbot/commands/auth.d.ts +28 -0
  17. package/dist/src/platforms/webexbot/commands/auth.d.ts.map +1 -0
  18. package/dist/src/platforms/webexbot/commands/auth.js +166 -0
  19. package/dist/src/platforms/webexbot/commands/auth.js.map +1 -0
  20. package/dist/src/platforms/webexbot/commands/index.d.ts +7 -0
  21. package/dist/src/platforms/webexbot/commands/index.d.ts.map +1 -0
  22. package/dist/src/platforms/webexbot/commands/index.js +7 -0
  23. package/dist/src/platforms/webexbot/commands/index.js.map +1 -0
  24. package/dist/src/platforms/webexbot/commands/listen.d.ts +12 -0
  25. package/dist/src/platforms/webexbot/commands/listen.d.ts.map +1 -0
  26. package/dist/src/platforms/webexbot/commands/listen.js +85 -0
  27. package/dist/src/platforms/webexbot/commands/listen.js.map +1 -0
  28. package/dist/src/platforms/webexbot/commands/member.d.ts +19 -0
  29. package/dist/src/platforms/webexbot/commands/member.d.ts.map +1 -0
  30. package/dist/src/platforms/webexbot/commands/member.js +33 -0
  31. package/dist/src/platforms/webexbot/commands/member.js.map +1 -0
  32. package/dist/src/platforms/webexbot/commands/message.d.ts +37 -0
  33. package/dist/src/platforms/webexbot/commands/message.d.ts.map +1 -0
  34. package/dist/src/platforms/webexbot/commands/message.js +142 -0
  35. package/dist/src/platforms/webexbot/commands/message.js.map +1 -0
  36. package/dist/src/platforms/webexbot/commands/shared.d.ts +9 -0
  37. package/dist/src/platforms/webexbot/commands/shared.d.ts.map +1 -0
  38. package/dist/src/platforms/webexbot/commands/shared.js +13 -0
  39. package/dist/src/platforms/webexbot/commands/shared.js.map +1 -0
  40. package/dist/src/platforms/webexbot/commands/space.d.ts +28 -0
  41. package/dist/src/platforms/webexbot/commands/space.d.ts.map +1 -0
  42. package/dist/src/platforms/webexbot/commands/space.js +61 -0
  43. package/dist/src/platforms/webexbot/commands/space.js.map +1 -0
  44. package/dist/src/platforms/webexbot/commands/whoami.d.ts +16 -0
  45. package/dist/src/platforms/webexbot/commands/whoami.d.ts.map +1 -0
  46. package/dist/src/platforms/webexbot/commands/whoami.js +29 -0
  47. package/dist/src/platforms/webexbot/commands/whoami.js.map +1 -0
  48. package/dist/src/platforms/webexbot/credential-manager.d.ts +17 -0
  49. package/dist/src/platforms/webexbot/credential-manager.d.ts.map +1 -0
  50. package/dist/src/platforms/webexbot/credential-manager.js +120 -0
  51. package/dist/src/platforms/webexbot/credential-manager.js.map +1 -0
  52. package/dist/src/platforms/webexbot/index.d.ts +9 -0
  53. package/dist/src/platforms/webexbot/index.d.ts.map +1 -0
  54. package/dist/src/platforms/webexbot/index.js +6 -0
  55. package/dist/src/platforms/webexbot/index.js.map +1 -0
  56. package/dist/src/platforms/webexbot/listener.d.ts +44 -0
  57. package/dist/src/platforms/webexbot/listener.d.ts.map +1 -0
  58. package/dist/src/platforms/webexbot/listener.js +214 -0
  59. package/dist/src/platforms/webexbot/listener.js.map +1 -0
  60. package/dist/src/platforms/webexbot/types.d.ts +60 -0
  61. package/dist/src/platforms/webexbot/types.d.ts.map +1 -0
  62. package/dist/src/platforms/webexbot/types.js +28 -0
  63. package/dist/src/platforms/webexbot/types.js.map +1 -0
  64. package/dist/src/platforms/webexbot/wdm-discovery.d.ts +4 -0
  65. package/dist/src/platforms/webexbot/wdm-discovery.d.ts.map +1 -0
  66. package/dist/src/platforms/webexbot/wdm-discovery.js +36 -0
  67. package/dist/src/platforms/webexbot/wdm-discovery.js.map +1 -0
  68. package/docs/content/docs/cli/meta.json +1 -0
  69. package/docs/content/docs/cli/webexbot.mdx +290 -0
  70. package/docs/content/docs/sdk/meta.json +1 -0
  71. package/docs/content/docs/sdk/webexbot.mdx +340 -0
  72. package/docs/src/app/page.tsx +115 -19
  73. package/package.json +10 -2
  74. package/skills/agent-channeltalk/SKILL.md +1 -1
  75. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  76. package/skills/agent-discord/SKILL.md +1 -1
  77. package/skills/agent-discordbot/SKILL.md +1 -1
  78. package/skills/agent-instagram/SKILL.md +1 -1
  79. package/skills/agent-kakaotalk/SKILL.md +1 -1
  80. package/skills/agent-line/SKILL.md +1 -1
  81. package/skills/agent-slack/SKILL.md +1 -1
  82. package/skills/agent-slackbot/SKILL.md +1 -1
  83. package/skills/agent-teams/SKILL.md +1 -1
  84. package/skills/agent-telegram/SKILL.md +1 -1
  85. package/skills/agent-telegrambot/SKILL.md +1 -1
  86. package/skills/agent-webex/SKILL.md +1 -1
  87. package/skills/agent-webexbot/SKILL.md +361 -0
  88. package/skills/agent-webexbot/references/authentication.md +225 -0
  89. package/skills/agent-webexbot/references/common-patterns.md +590 -0
  90. package/skills/agent-wechatbot/SKILL.md +1 -1
  91. package/skills/agent-whatsapp/SKILL.md +1 -1
  92. package/skills/agent-whatsappbot/SKILL.md +1 -1
  93. package/src/cli.ts +4 -0
  94. package/src/platforms/webex/typings/webex-message-handler.d.ts +360 -29
  95. package/src/platforms/webexbot/cli.ts +42 -0
  96. package/src/platforms/webexbot/client.ts +87 -0
  97. package/src/platforms/webexbot/commands/auth.test.ts +185 -0
  98. package/src/platforms/webexbot/commands/auth.ts +210 -0
  99. package/src/platforms/webexbot/commands/index.ts +6 -0
  100. package/src/platforms/webexbot/commands/listen.test.ts +20 -0
  101. package/src/platforms/webexbot/commands/listen.ts +104 -0
  102. package/src/platforms/webexbot/commands/member.ts +51 -0
  103. package/src/platforms/webexbot/commands/message.ts +197 -0
  104. package/src/platforms/webexbot/commands/shared.ts +22 -0
  105. package/src/platforms/webexbot/commands/space.ts +88 -0
  106. package/src/platforms/webexbot/commands/whoami.ts +43 -0
  107. package/src/platforms/webexbot/credential-manager.test.ts +182 -0
  108. package/src/platforms/webexbot/credential-manager.ts +149 -0
  109. package/src/platforms/webexbot/index.ts +8 -0
  110. package/src/platforms/webexbot/listener.test.ts +234 -0
  111. package/src/platforms/webexbot/listener.ts +255 -0
  112. package/src/platforms/webexbot/types.test.ts +87 -0
  113. package/src/platforms/webexbot/types.ts +72 -0
  114. package/src/platforms/webexbot/wdm-discovery.test.ts +97 -0
  115. package/src/platforms/webexbot/wdm-discovery.ts +43 -0
@@ -1,45 +1,376 @@
1
1
  export {}
2
2
 
3
3
  declare module 'webex-message-handler' {
4
- import type * as jose from 'node-jose'
4
+ import { EventEmitter } from 'events'
5
5
 
6
- type Logger = Record<string, (...args: unknown[]) => void>
7
- type HttpRequest = { url: string; method: string; headers: Record<string, string>; body?: string }
8
- type HttpResponse = { status: number; ok: boolean; json(): Promise<unknown>; text(): Promise<string> }
9
- type HttpDo = (req: HttpRequest) => Promise<HttpResponse>
6
+ import type { JWK } from 'node-jose'
7
+
8
+ export interface Logger {
9
+ debug(message: string, ...args: unknown[]): void
10
+ info(message: string, ...args: unknown[]): void
11
+ warn(message: string, ...args: unknown[]): void
12
+ error(message: string, ...args: unknown[]): void
13
+ }
10
14
 
11
15
  export const noopLogger: Logger
12
16
  export const consoleLogger: Logger
13
17
 
18
+ export type NetworkMode = 'native' | 'injected'
19
+
20
+ export interface FetchRequest {
21
+ url: string
22
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE'
23
+ headers: Record<string, string>
24
+ body?: string
25
+ }
26
+
27
+ export interface FetchResponse {
28
+ status: number
29
+ ok: boolean
30
+ json(): Promise<unknown>
31
+ text(): Promise<string>
32
+ }
33
+
34
+ export type FetchFunction = (request: FetchRequest) => Promise<FetchResponse>
35
+
36
+ export interface InjectedWebSocket {
37
+ send(data: string): void
38
+ close(code?: number): void
39
+ readonly readyState: number
40
+ on(event: 'message', listener: (data: string) => void): void
41
+ on(event: 'open', listener: () => void): void
42
+ on(event: 'close', listener: (code: number, reason: string) => void): void
43
+ on(event: 'error', listener: (error: Error) => void): void
44
+ }
45
+
46
+ export type WebSocketFactory = (url: string) => InjectedWebSocket
47
+
48
+ export interface WebexMessageHandlerConfig {
49
+ token: string
50
+ logger?: Logger
51
+ /** Networking mode: 'native' uses built-in fetch/WebSocket, 'injected' uses provided functions */
52
+ mode?: NetworkMode
53
+ /**
54
+ * Optional undici Dispatcher for native mode proxy support (HTTP + WebSocket).
55
+ * A single `ProxyAgent` proxies both `fetch()` and the native `WebSocket`.
56
+ * Example: `new ProxyAgent('http://proxy:8080')`
57
+ */
58
+ dispatcher?: object
59
+ /** Custom fetch function for all HTTP requests (injected mode) */
60
+ fetch?: FetchFunction
61
+ /** Custom WebSocket factory (injected mode) */
62
+ webSocketFactory?: WebSocketFactory
63
+ /** Automatically filter out messages sent by this bot to prevent loops (default: true) */
64
+ ignoreSelfMessages?: boolean
65
+ /** Ping interval in ms (default: 15000) */
66
+ pingInterval?: number
67
+ /** Pong timeout in ms (default: 14000) */
68
+ pongTimeout?: number
69
+ /** Max reconnect backoff in ms (default: 32000) */
70
+ reconnectBackoffMax?: number
71
+ /** Max reconnect attempts before giving up (default: 10) */
72
+ maxReconnectAttempts?: number
73
+ /** Optional metrics callback for timing events (no overhead if not set) */
74
+ metricsCallback?: MetricsCallback
75
+ }
76
+
77
+ export interface PersonInfo {
78
+ /** Person's unique ID */
79
+ id: string
80
+ /** Person's email address */
81
+ emails: string[]
82
+ /** Person's display name */
83
+ displayName: string
84
+ /** Person type (person or bot) */
85
+ type: 'person' | 'bot'
86
+ }
87
+
88
+ export interface DeviceRegistration {
89
+ /** The Mercury WebSocket URL */
90
+ webSocketUrl: string
91
+ /** The device URL (used as clientId for KMS) */
92
+ deviceUrl: string
93
+ /** The bot's user ID */
94
+ userId: string
95
+ /** Service catalog from WDM */
96
+ services: Record<string, string>
97
+ /** Encryption service URL extracted from services */
98
+ encryptionServiceUrl: string
99
+ }
100
+
101
+ export interface MercuryActor {
102
+ id: string
103
+ objectType: string
104
+ emailAddress?: string
105
+ }
106
+
107
+ export interface MercuryObject {
108
+ id: string
109
+ objectType: string
110
+ displayName?: string
111
+ content?: string
112
+ encryptionKeyUrl?: string
113
+ /** Card form input values (present on cardAction/submit activities). */
114
+ inputs?: Record<string, unknown>
115
+ /** File URLs attached to the message (present on file-share messages). */
116
+ files?: string[]
117
+ }
118
+
119
+ export interface MercuryTarget {
120
+ id: string
121
+ objectType: string
122
+ encryptionKeyUrl?: string
123
+ tags?: string[]
124
+ }
125
+
126
+ export interface MercuryParent {
127
+ id: string
128
+ type: string
129
+ }
130
+
131
+ export interface MercuryActivity {
132
+ id: string
133
+ /** Full Conversation-service activity URL, when present on the raw activity. */
134
+ url?: string
135
+ verb: string
136
+ actor: MercuryActor
137
+ object: MercuryObject
138
+ target: MercuryTarget
139
+ published: string
140
+ encryptionKeyUrl?: string
141
+ parent?: MercuryParent
142
+ }
143
+
144
+ export interface MercuryEnvelope {
145
+ id: string
146
+ data: {
147
+ eventType: string
148
+ activity: MercuryActivity
149
+ }
150
+ timestamp: number
151
+ trackingId: string
152
+ sequenceNumber?: number
153
+ }
154
+
155
+ export interface DecryptedMessage {
156
+ /** Mercury activity UUID. Works as parentId for threaded replies. */
157
+ id: string
158
+ /**
159
+ * Full Conversation-service activity URL, when present on the raw Mercury
160
+ * activity (e.g. for an outbound "acknowledge" read-receipt). Undefined if
161
+ * Mercury did not include it.
162
+ */
163
+ url?: string
164
+ /** Parent activity UUID for threaded replies. Undefined if not a thread reply. */
165
+ parentId?: string
166
+ roomId: string
167
+ personId: string
168
+ personEmail: string
169
+ text: string
170
+ html?: string
171
+ created: string
172
+ roomType?: string
173
+ /** Person UUIDs mentioned via @mention in the message. */
174
+ mentionedPeople: string[]
175
+ /** Group mention types (e.g. "all") in the message. */
176
+ mentionedGroups: string[]
177
+ /** File URLs attached to the message. Empty if no files. */
178
+ files: string[]
179
+ raw: MercuryActivity
180
+ }
181
+
182
+ export interface DeletedMessage {
183
+ messageId: string
184
+ roomId: string
185
+ personId: string
186
+ }
187
+
188
+ export interface MembershipActivity {
189
+ /** Activity ID. */
190
+ id: string
191
+ /** ID of the person who performed the action. */
192
+ actorId: string
193
+ /** ID of the member affected. */
194
+ personId: string
195
+ /** Conversation/space ID. */
196
+ roomId: string
197
+ /** Membership action: "add", "leave", "assignModerator", or "unassignModerator". */
198
+ action: string
199
+ /** ISO 8601 timestamp. */
200
+ created: string
201
+ /** "direct", "group", or undefined. */
202
+ roomType?: string
203
+ /** Full raw activity for advanced use. */
204
+ raw: MercuryActivity
205
+ }
206
+
207
+ export interface AttachmentAction {
208
+ /** Activity ID. */
209
+ id: string
210
+ /** ID of the message the card was attached to. */
211
+ messageId: string
212
+ /** ID of the person who submitted the card. */
213
+ personId: string
214
+ /** Email of the person who submitted the card. */
215
+ personEmail: string
216
+ /** Conversation/space ID. */
217
+ roomId: string
218
+ /** Card form input values. */
219
+ inputs: Record<string, unknown>
220
+ /** ISO 8601 timestamp. */
221
+ created: string
222
+ /** Full raw activity for advanced use. */
223
+ raw: MercuryActivity
224
+ }
225
+
226
+ export interface RoomActivity {
227
+ /** Activity ID. */
228
+ id: string
229
+ /** Conversation/space ID. */
230
+ roomId: string
231
+ /** ID of the person who performed the action. */
232
+ actorId: string
233
+ /** Room action: "created" or "updated". */
234
+ action: string
235
+ /** ISO 8601 timestamp. */
236
+ created: string
237
+ /** Full raw activity for advanced use. */
238
+ raw: MercuryActivity
239
+ }
240
+
241
+ export type ConnectionStatus = 'connected' | 'connecting' | 'reconnecting' | 'disconnected'
242
+
243
+ export interface HandlerStatus {
244
+ /** Overall connection state. */
245
+ status: ConnectionStatus
246
+ /** Whether the WebSocket is currently open. */
247
+ webSocketOpen: boolean
248
+ /** Whether the KMS encryption context is initialized. */
249
+ kmsInitialized: boolean
250
+ /** Whether the device is registered with WDM. */
251
+ deviceRegistered: boolean
252
+ /** Current auto-reconnect attempt number (0 if not reconnecting). */
253
+ reconnectAttempt: number
254
+ }
255
+
256
+ export interface MetricsEvent {
257
+ /** Metric name: "connect", "kms_fetch", or "decrypt". */
258
+ name: string
259
+ /** Duration in milliseconds. */
260
+ durationMs: number
261
+ /** Whether the operation succeeded. */
262
+ success: boolean
263
+ /** Optional context metadata (e.g., key URI for kms_fetch). */
264
+ metadata?: Record<string, string>
265
+ }
266
+
267
+ export type MetricsCallback = (event: MetricsEvent) => void
268
+
269
+ export interface WebexMessageHandlerEvents {
270
+ 'message:created': (msg: DecryptedMessage) => void
271
+ 'message:updated': (msg: DecryptedMessage) => void
272
+ 'message:deleted': (data: DeletedMessage) => void
273
+ 'membership:created': (activity: MembershipActivity) => void
274
+ 'attachmentAction:created': (action: AttachmentAction) => void
275
+ 'room:created': (activity: RoomActivity) => void
276
+ 'room:updated': (activity: RoomActivity) => void
277
+ connected: () => void
278
+ disconnected: (reason: string) => void
279
+ reconnecting: (attempt: number) => void
280
+ error: (err: Error) => void
281
+ }
282
+
283
+ interface TypedEventEmitter<T> {
284
+ on<K extends keyof T>(event: K, listener: T[K]): this
285
+ emit<K extends keyof T>(
286
+ event: K,
287
+ ...args: Parameters<T[K] extends (...a: infer P) => unknown ? (...a: P) => unknown : never>
288
+ ): boolean
289
+ off<K extends keyof T>(event: K, listener: T[K]): this
290
+ once<K extends keyof T>(event: K, listener: T[K]): this
291
+ removeAllListeners<K extends keyof T>(event?: K): this
292
+ }
293
+
294
+ export class WebexMessageHandler extends EventEmitter implements TypedEventEmitter<WebexMessageHandlerEvents> {
295
+ constructor(config: WebexMessageHandlerConfig)
296
+ connect(): Promise<void>
297
+ disconnect(): Promise<void>
298
+ reconnect(newToken: string): Promise<void>
299
+ get connected(): boolean
300
+ status(): HandlerStatus
301
+ deviceRegistration(): DeviceRegistration | null
302
+ serviceUrl(name: string): string | undefined
303
+ on<K extends keyof WebexMessageHandlerEvents>(event: K, listener: WebexMessageHandlerEvents[K]): this
304
+ emit<K extends keyof WebexMessageHandlerEvents>(
305
+ event: K,
306
+ ...args: Parameters<
307
+ WebexMessageHandlerEvents[K] extends (...a: infer P) => unknown ? (...a: P) => unknown : never
308
+ >
309
+ ): boolean
310
+ off<K extends keyof WebexMessageHandlerEvents>(event: K, listener: WebexMessageHandlerEvents[K]): this
311
+ once<K extends keyof WebexMessageHandlerEvents>(event: K, listener: WebexMessageHandlerEvents[K]): this
312
+ removeAllListeners<K extends keyof WebexMessageHandlerEvents>(event?: K): this
313
+ }
314
+
315
+ type HttpDoFn = (request: FetchRequest) => Promise<FetchResponse>
316
+
317
+ export interface DeviceManagerOptions {
318
+ logger?: Logger
319
+ httpDo: HttpDoFn
320
+ }
321
+
14
322
  export class DeviceManager {
15
- constructor(options: { logger: Logger; httpDo: HttpDo })
16
- register(token: string): Promise<{
17
- webSocketUrl: string
18
- deviceUrl: string
19
- userId: string
20
- services: unknown
21
- encryptionServiceUrl: string
22
- }>
23
- }
24
-
25
- export class MercurySocket {
26
- constructor(options: { logger: Logger; wsFactory: (url: string) => unknown })
27
- on(event: 'kms:response', handler: (data: unknown) => void): void
28
- connect(webSocketUrl: string, token: string): Promise<void>
323
+ constructor(options: DeviceManagerOptions)
324
+ register(token: string): Promise<DeviceRegistration>
325
+ refresh(token: string): Promise<DeviceRegistration>
326
+ unregister(token: string): Promise<void>
327
+ }
328
+
329
+ export interface MercurySocketOptions {
330
+ logger?: Logger
331
+ wsFactory: WebSocketFactory
332
+ pingInterval?: number
333
+ pongTimeout?: number
334
+ reconnectBackoffMax?: number
335
+ maxReconnectAttempts?: number
336
+ }
337
+
338
+ export class MercurySocket extends EventEmitter {
339
+ constructor(options: MercurySocketOptions)
340
+ connect(url: string, token: string): Promise<void>
29
341
  disconnect(): Promise<void>
342
+ get connected(): boolean
343
+ get currentReconnectAttempts(): number
344
+ on(event: 'kms:response', handler: (data: unknown) => void): this
345
+ }
346
+
347
+ export interface KmsClientConfig {
348
+ token: string
349
+ deviceUrl: string
350
+ userId: string
351
+ encryptionServiceUrl: string
352
+ logger?: Logger
353
+ httpDo: HttpDoFn
30
354
  }
31
355
 
32
356
  export class KmsClient {
33
- constructor(options: {
34
- token: string
35
- deviceUrl: string
36
- userId: string
37
- encryptionServiceUrl: string
38
- logger: Logger
39
- httpDo: HttpDo
40
- })
41
- initialize(): Promise<void>
42
- getKey(keyUri: string): Promise<jose.JWK.Key | null>
357
+ constructor(config: KmsClientConfig)
43
358
  handleKmsMessage(data: unknown): void
359
+ initialize(): Promise<void>
360
+ getKey(keyUri: string): Promise<JWK.Key>
361
+ }
362
+
363
+ export class MessageDecryptor {
364
+ constructor({ kmsClient, logger }: { kmsClient: KmsClient; logger?: Logger })
365
+ decryptActivity(activity: MercuryActivity): Promise<MercuryActivity>
366
+ }
367
+
368
+ export interface ParsedMentions {
369
+ mentionedPeople: string[]
370
+ mentionedGroups: string[]
44
371
  }
372
+
373
+ export function parseMentions(html: string | undefined | null): ParsedMentions
374
+ export function toRestId(uuid: string, type: 'MESSAGE' | 'PEOPLE' | 'ROOM'): string
375
+ export function fromRestId(restId: string): string
45
376
  }
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from 'commander'
4
+
5
+ import pkg from '../../../package.json' with { type: 'json' }
6
+ import {
7
+ authCommand,
8
+ listenCommand,
9
+ memberCommand,
10
+ messageCommand,
11
+ spaceCommand,
12
+ whoamiCommand,
13
+ } from './commands/index'
14
+
15
+ const program = new Command()
16
+
17
+ program
18
+ .name('agent-webexbot')
19
+ .description('CLI tool for Webex bot integration using bot tokens')
20
+ .version(pkg.version)
21
+ .option('--pretty', 'Pretty-print JSON output')
22
+ .option('--bot <id>', 'Bot ID to use')
23
+ .hook('preAction', (thisCmd, actionCmd) => {
24
+ for (const [key, value] of Object.entries(thisCmd.opts())) {
25
+ if (value === undefined) continue
26
+ const source = actionCmd.getOptionValueSource(key)
27
+ if (source === undefined || source === 'default') {
28
+ actionCmd.setOptionValue(key, value)
29
+ }
30
+ }
31
+ })
32
+
33
+ program.addCommand(authCommand)
34
+ program.addCommand(whoamiCommand)
35
+ program.addCommand(messageCommand)
36
+ program.addCommand(spaceCommand)
37
+ program.addCommand(memberCommand)
38
+ program.addCommand(listenCommand)
39
+
40
+ program.parseAsync(process.argv)
41
+
42
+ export default program
@@ -0,0 +1,87 @@
1
+ import { WebexClient } from '../webex/client'
2
+ import type { WebexMembership, WebexMessage, WebexPerson, WebexSpace } from '../webex/types'
3
+ import { WebexBotError } from './types'
4
+
5
+ export class WebexBotClient {
6
+ private client = new WebexClient()
7
+ private token: string | null = null
8
+
9
+ async login(credentials?: { token: string }): Promise<this> {
10
+ if (credentials) {
11
+ if (!credentials.token) {
12
+ throw new WebexBotError('Token is required', 'missing_token')
13
+ }
14
+ this.token = credentials.token
15
+ await this.client.login({ token: credentials.token })
16
+ return this
17
+ }
18
+
19
+ const { WebexBotCredentialManager } = await import('./credential-manager')
20
+ const credManager = new WebexBotCredentialManager()
21
+ const creds = await credManager.getCredentials()
22
+ if (!creds?.token) {
23
+ throw new WebexBotError('No Webex bot credentials found. Run "auth set <token>" first.', 'no_credentials')
24
+ }
25
+ return this.login({ token: creds.token })
26
+ }
27
+
28
+ getToken(): string {
29
+ if (!this.token) {
30
+ throw new WebexBotError('Not authenticated. Call .login() first.', 'not_authenticated')
31
+ }
32
+ return this.token
33
+ }
34
+
35
+ async testAuth(): Promise<WebexPerson> {
36
+ return this.client.testAuth()
37
+ }
38
+
39
+ async listSpaces(options?: { type?: string; max?: number }): Promise<WebexSpace[]> {
40
+ return this.client.listSpaces(options)
41
+ }
42
+
43
+ async getSpace(spaceId: string): Promise<WebexSpace> {
44
+ return this.client.getSpace(spaceId)
45
+ }
46
+
47
+ async sendMessage(roomId: string, text: string, options?: { markdown?: boolean }): Promise<WebexMessage> {
48
+ return this.client.sendMessage(roomId, text, options)
49
+ }
50
+
51
+ async sendDirectMessage(personEmail: string, text: string, options?: { markdown?: boolean }): Promise<WebexMessage> {
52
+ return this.client.sendDirectMessage(personEmail, text, options)
53
+ }
54
+
55
+ async listMessages(roomId: string, options?: { max?: number }): Promise<WebexMessage[]> {
56
+ return this.client.listMessages(roomId, options)
57
+ }
58
+
59
+ async getMessage(messageId: string): Promise<WebexMessage> {
60
+ return this.client.getMessage(messageId)
61
+ }
62
+
63
+ async deleteMessage(messageId: string): Promise<void> {
64
+ return this.client.deleteMessage(messageId)
65
+ }
66
+
67
+ async editMessage(
68
+ messageId: string,
69
+ roomId: string,
70
+ text: string,
71
+ options?: { markdown?: boolean },
72
+ ): Promise<WebexMessage> {
73
+ return this.client.editMessage(messageId, roomId, text, options)
74
+ }
75
+
76
+ async listPeople(options?: { email?: string; displayName?: string; max?: number }): Promise<WebexPerson[]> {
77
+ return this.client.listPeople(options)
78
+ }
79
+
80
+ async listMyMemberships(options?: { max?: number }): Promise<WebexMembership[]> {
81
+ return this.client.listMyMemberships(options)
82
+ }
83
+
84
+ async listMemberships(roomId: string, options?: { max?: number }): Promise<WebexMembership[]> {
85
+ return this.client.listMemberships(roomId, options)
86
+ }
87
+ }