agent-messenger 2.20.5 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +8 -5
- package/dist/package.json +9 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +3 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +19 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +81 -1
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webexbot/cli.d.ts +5 -0
- package/dist/src/platforms/webexbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/cli.js +33 -0
- package/dist/src/platforms/webexbot/cli.js.map +1 -0
- package/dist/src/platforms/webexbot/client.d.ts +61 -0
- package/dist/src/platforms/webexbot/client.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/client.js +80 -0
- package/dist/src/platforms/webexbot/client.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/webexbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/auth.js +166 -0
- package/dist/src/platforms/webexbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/file.d.ts +22 -0
- package/dist/src/platforms/webexbot/commands/file.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/file.js +64 -0
- package/dist/src/platforms/webexbot/commands/file.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/index.d.ts +10 -0
- package/dist/src/platforms/webexbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/index.js +10 -0
- package/dist/src/platforms/webexbot/commands/index.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/listen.d.ts +12 -0
- package/dist/src/platforms/webexbot/commands/listen.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/listen.js +85 -0
- package/dist/src/platforms/webexbot/commands/listen.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/member.d.ts +19 -0
- package/dist/src/platforms/webexbot/commands/member.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/member.js +33 -0
- package/dist/src/platforms/webexbot/commands/member.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/message.d.ts +44 -0
- package/dist/src/platforms/webexbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/message.js +193 -0
- package/dist/src/platforms/webexbot/commands/message.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/webexbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/shared.js +13 -0
- package/dist/src/platforms/webexbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/snapshot.d.ts +24 -0
- package/dist/src/platforms/webexbot/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/snapshot.js +37 -0
- package/dist/src/platforms/webexbot/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/space.d.ts +28 -0
- package/dist/src/platforms/webexbot/commands/space.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/space.js +61 -0
- package/dist/src/platforms/webexbot/commands/space.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/user.d.ts +30 -0
- package/dist/src/platforms/webexbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/user.js +66 -0
- package/dist/src/platforms/webexbot/commands/user.js.map +1 -0
- package/dist/src/platforms/webexbot/commands/whoami.d.ts +16 -0
- package/dist/src/platforms/webexbot/commands/whoami.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/commands/whoami.js +29 -0
- package/dist/src/platforms/webexbot/commands/whoami.js.map +1 -0
- package/dist/src/platforms/webexbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/webexbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/credential-manager.js +120 -0
- package/dist/src/platforms/webexbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/webexbot/index.d.ts +9 -0
- package/dist/src/platforms/webexbot/index.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/index.js +6 -0
- package/dist/src/platforms/webexbot/index.js.map +1 -0
- package/dist/src/platforms/webexbot/listener.d.ts +44 -0
- package/dist/src/platforms/webexbot/listener.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/listener.js +214 -0
- package/dist/src/platforms/webexbot/listener.js.map +1 -0
- package/dist/src/platforms/webexbot/types.d.ts +60 -0
- package/dist/src/platforms/webexbot/types.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/types.js +28 -0
- package/dist/src/platforms/webexbot/types.js.map +1 -0
- package/dist/src/platforms/webexbot/wdm-discovery.d.ts +4 -0
- package/dist/src/platforms/webexbot/wdm-discovery.d.ts.map +1 -0
- package/dist/src/platforms/webexbot/wdm-discovery.js +36 -0
- package/dist/src/platforms/webexbot/wdm-discovery.js.map +1 -0
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/webexbot.mdx +292 -0
- package/docs/content/docs/sdk/meta.json +1 -0
- package/docs/content/docs/sdk/webexbot.mdx +342 -0
- package/docs/src/app/page.tsx +115 -19
- package/package.json +9 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/SKILL.md +414 -0
- package/skills/agent-webexbot/references/authentication.md +225 -0
- package/skills/agent-webexbot/references/common-patterns.md +708 -0
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/cli.ts +4 -0
- package/src/platforms/webex/client.test.ts +10 -0
- package/src/platforms/webex/client.ts +97 -3
- package/src/platforms/webex/typings/webex-message-handler.d.ts +360 -29
- package/src/platforms/webexbot/cli.ts +48 -0
- package/src/platforms/webexbot/client.test.ts +198 -0
- package/src/platforms/webexbot/client.ts +113 -0
- package/src/platforms/webexbot/commands/auth.test.ts +185 -0
- package/src/platforms/webexbot/commands/auth.ts +210 -0
- package/src/platforms/webexbot/commands/file.ts +104 -0
- package/src/platforms/webexbot/commands/index.ts +9 -0
- package/src/platforms/webexbot/commands/listen.test.ts +20 -0
- package/src/platforms/webexbot/commands/listen.ts +104 -0
- package/src/platforms/webexbot/commands/member.ts +51 -0
- package/src/platforms/webexbot/commands/message.ts +263 -0
- package/src/platforms/webexbot/commands/shared.ts +22 -0
- package/src/platforms/webexbot/commands/snapshot.ts +60 -0
- package/src/platforms/webexbot/commands/space.ts +88 -0
- package/src/platforms/webexbot/commands/user.test.ts +77 -0
- package/src/platforms/webexbot/commands/user.ts +98 -0
- package/src/platforms/webexbot/commands/whoami.ts +43 -0
- package/src/platforms/webexbot/credential-manager.test.ts +182 -0
- package/src/platforms/webexbot/credential-manager.ts +149 -0
- package/src/platforms/webexbot/index.ts +8 -0
- package/src/platforms/webexbot/listener.test.ts +234 -0
- package/src/platforms/webexbot/listener.ts +255 -0
- package/src/platforms/webexbot/types.test.ts +87 -0
- package/src/platforms/webexbot/types.ts +72 -0
- package/src/platforms/webexbot/wdm-discovery.test.ts +97 -0
- package/src/platforms/webexbot/wdm-discovery.ts +43 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { cliOutput } from '@/shared/utils/cli-output'
|
|
4
|
+
import { formatOutput } from '@/shared/utils/output'
|
|
5
|
+
|
|
6
|
+
import { WebexBotClient } from '../client'
|
|
7
|
+
import { WebexBotCredentialManager } from '../credential-manager'
|
|
8
|
+
|
|
9
|
+
interface ActionOptions {
|
|
10
|
+
pretty?: boolean
|
|
11
|
+
bot?: string
|
|
12
|
+
_credManager?: WebexBotCredentialManager
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ActionResult {
|
|
16
|
+
success?: boolean
|
|
17
|
+
error?: string
|
|
18
|
+
bot_id?: string
|
|
19
|
+
bot_name?: string
|
|
20
|
+
valid?: boolean
|
|
21
|
+
bots?: Array<{
|
|
22
|
+
bot_id: string
|
|
23
|
+
bot_name: string
|
|
24
|
+
is_current: boolean
|
|
25
|
+
}>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function setAction(token: string, options: ActionOptions): Promise<ActionResult> {
|
|
29
|
+
try {
|
|
30
|
+
const client = await new WebexBotClient().login({ token })
|
|
31
|
+
const authInfo = await client.testAuth()
|
|
32
|
+
|
|
33
|
+
if (authInfo.type !== 'bot') {
|
|
34
|
+
return { error: 'Token is not a bot token. Use agent-webex for user tokens.' }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const botId = options.bot || authInfo.id || 'default'
|
|
38
|
+
const botName = authInfo.displayName || botId
|
|
39
|
+
|
|
40
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
41
|
+
await credManager.setCredentials({
|
|
42
|
+
token,
|
|
43
|
+
bot_id: botId,
|
|
44
|
+
bot_name: botName,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
bot_id: botId,
|
|
50
|
+
bot_name: botName,
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return { error: (error as Error).message }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function clearAction(options: ActionOptions): Promise<ActionResult> {
|
|
58
|
+
try {
|
|
59
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
60
|
+
await credManager.clearCredentials()
|
|
61
|
+
return { success: true }
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return { error: (error as Error).message }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function statusAction(options: ActionOptions): Promise<ActionResult> {
|
|
68
|
+
try {
|
|
69
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
70
|
+
const creds = await credManager.getCredentials(options.bot)
|
|
71
|
+
|
|
72
|
+
if (!creds) {
|
|
73
|
+
return {
|
|
74
|
+
valid: false,
|
|
75
|
+
error: options.bot
|
|
76
|
+
? `Bot "${options.bot}" not found. Run "auth list" to see available bots.`
|
|
77
|
+
: 'No credentials configured. Run "auth set <token>" first.',
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let valid = false
|
|
82
|
+
let authInfo: { id: string; displayName: string; type: 'person' | 'bot' } | null = null
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const client = await new WebexBotClient().login({ token: creds.token })
|
|
86
|
+
authInfo = await client.testAuth()
|
|
87
|
+
valid = authInfo.type === 'bot'
|
|
88
|
+
} catch {
|
|
89
|
+
valid = false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
valid,
|
|
94
|
+
bot_id: authInfo?.id ?? creds.bot_id,
|
|
95
|
+
bot_name: authInfo?.displayName ?? creds.bot_name,
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return { error: (error as Error).message }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function listAction(options: ActionOptions): Promise<ActionResult> {
|
|
103
|
+
try {
|
|
104
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
105
|
+
const all = await credManager.listAll()
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
bots: all.map((b) => ({
|
|
109
|
+
bot_id: b.bot_id,
|
|
110
|
+
bot_name: b.bot_name,
|
|
111
|
+
is_current: b.is_current,
|
|
112
|
+
})),
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return { error: (error as Error).message }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function useAction(botId: string, options: ActionOptions): Promise<ActionResult> {
|
|
120
|
+
try {
|
|
121
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
122
|
+
const found = await credManager.setCurrent(botId)
|
|
123
|
+
|
|
124
|
+
if (!found) {
|
|
125
|
+
return { error: `Bot "${botId}" not found. Run "auth list" to see available bots.` }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const creds = await credManager.getCredentials()
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
bot_id: creds?.bot_id,
|
|
132
|
+
bot_name: creds?.bot_name,
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return { error: (error as Error).message }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function removeAction(botId: string, options: ActionOptions): Promise<ActionResult> {
|
|
140
|
+
try {
|
|
141
|
+
const credManager = options._credManager ?? new WebexBotCredentialManager()
|
|
142
|
+
const removed = await credManager.removeBot(botId)
|
|
143
|
+
|
|
144
|
+
if (!removed) {
|
|
145
|
+
return { error: `Bot "${botId}" not found. Run "auth list" to see available bots.` }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { success: true }
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return { error: (error as Error).message }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const authCommand = new Command('auth')
|
|
155
|
+
.description('Bot authentication commands')
|
|
156
|
+
.addCommand(
|
|
157
|
+
new Command('set')
|
|
158
|
+
.description('Set bot token')
|
|
159
|
+
.argument('<token>', 'Bot token')
|
|
160
|
+
.option('--bot <id>', 'Bot identifier for switching later')
|
|
161
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
162
|
+
.action(async (token: string, opts: { bot?: string; pretty?: boolean }) => {
|
|
163
|
+
cliOutput(await setAction(token, opts), opts.pretty)
|
|
164
|
+
}),
|
|
165
|
+
)
|
|
166
|
+
.addCommand(
|
|
167
|
+
new Command('clear')
|
|
168
|
+
.description('Clear all stored credentials')
|
|
169
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
170
|
+
.action(async (opts: { pretty?: boolean }) => {
|
|
171
|
+
cliOutput(await clearAction(opts), opts.pretty)
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
.addCommand(
|
|
175
|
+
new Command('status')
|
|
176
|
+
.description('Show authentication status')
|
|
177
|
+
.option('--bot <id>', 'Check specific bot (default: current)')
|
|
178
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
179
|
+
.action(async (opts: { bot?: string; pretty?: boolean }) => {
|
|
180
|
+
const result = await statusAction(opts)
|
|
181
|
+
console.log(formatOutput(result, opts.pretty))
|
|
182
|
+
if (!result.valid) process.exit(1)
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
.addCommand(
|
|
186
|
+
new Command('list')
|
|
187
|
+
.description('List all stored bots')
|
|
188
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
189
|
+
.action(async (opts: { pretty?: boolean }) => {
|
|
190
|
+
cliOutput(await listAction(opts), opts.pretty)
|
|
191
|
+
}),
|
|
192
|
+
)
|
|
193
|
+
.addCommand(
|
|
194
|
+
new Command('use')
|
|
195
|
+
.description('Switch active bot')
|
|
196
|
+
.argument('<bot>', 'Bot ID')
|
|
197
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
198
|
+
.action(async (botId: string, opts: { pretty?: boolean }) => {
|
|
199
|
+
cliOutput(await useAction(botId, opts), opts.pretty)
|
|
200
|
+
}),
|
|
201
|
+
)
|
|
202
|
+
.addCommand(
|
|
203
|
+
new Command('remove')
|
|
204
|
+
.description('Remove a stored bot')
|
|
205
|
+
.argument('<bot>', 'Bot ID')
|
|
206
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
207
|
+
.action(async (botId: string, opts: { pretty?: boolean }) => {
|
|
208
|
+
cliOutput(await removeAction(botId, opts), opts.pretty)
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { basename, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { Command } from 'commander'
|
|
5
|
+
|
|
6
|
+
import { cliOutput } from '@/shared/utils/cli-output'
|
|
7
|
+
|
|
8
|
+
import type { BotOption } from './shared'
|
|
9
|
+
import { getClient } from './shared'
|
|
10
|
+
|
|
11
|
+
interface FileResult {
|
|
12
|
+
id?: string
|
|
13
|
+
roomId?: string
|
|
14
|
+
files?: string[]
|
|
15
|
+
created?: string
|
|
16
|
+
downloaded?: string
|
|
17
|
+
filename?: string
|
|
18
|
+
contentType?: string
|
|
19
|
+
size?: number
|
|
20
|
+
error?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function uploadAction(
|
|
24
|
+
space: string,
|
|
25
|
+
path: string,
|
|
26
|
+
options: BotOption & { text?: string; markdown?: boolean; parent?: string },
|
|
27
|
+
): Promise<FileResult> {
|
|
28
|
+
try {
|
|
29
|
+
const client = await getClient(options)
|
|
30
|
+
const filePath = resolve(path)
|
|
31
|
+
const content = await readFile(filePath)
|
|
32
|
+
const message = await client.uploadFile(
|
|
33
|
+
space,
|
|
34
|
+
{ content: new Blob([content]), filename: basename(filePath) },
|
|
35
|
+
{ text: options.text, markdown: options.markdown, parentId: options.parent },
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
id: message.id,
|
|
40
|
+
roomId: message.roomId,
|
|
41
|
+
files: message.files,
|
|
42
|
+
created: message.created,
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return { error: (error as Error).message }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function downloadAction(
|
|
50
|
+
contentRef: string,
|
|
51
|
+
output: string | undefined,
|
|
52
|
+
options: BotOption,
|
|
53
|
+
): Promise<FileResult> {
|
|
54
|
+
try {
|
|
55
|
+
const client = await getClient(options)
|
|
56
|
+
const { data, filename, contentType } = await client.downloadContent(contentRef)
|
|
57
|
+
// When no explicit output is given, confine the server-provided name to cwd.
|
|
58
|
+
const outputPath = output ? resolve(output) : resolve(process.cwd(), basename(filename))
|
|
59
|
+
await writeFile(outputPath, Buffer.from(data))
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
downloaded: outputPath,
|
|
63
|
+
filename,
|
|
64
|
+
contentType,
|
|
65
|
+
size: data.byteLength,
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return { error: (error as Error).message }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const fileCommand = new Command('file')
|
|
73
|
+
.description('File commands')
|
|
74
|
+
.addCommand(
|
|
75
|
+
new Command('upload')
|
|
76
|
+
.description('Upload a local file to a space')
|
|
77
|
+
.argument('<space>', 'Space/Room ID')
|
|
78
|
+
.argument('<path>', 'Local file path')
|
|
79
|
+
.option('--text <text>', 'Optional message to send with the file')
|
|
80
|
+
.option('--markdown', 'Treat --text as markdown')
|
|
81
|
+
.option('--parent <id>', 'Reply within a thread (parent message ID)')
|
|
82
|
+
.option('--bot <id>', 'Use specific bot')
|
|
83
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
84
|
+
.action(
|
|
85
|
+
async (
|
|
86
|
+
space: string,
|
|
87
|
+
path: string,
|
|
88
|
+
opts: BotOption & { text?: string; markdown?: boolean; parent?: string },
|
|
89
|
+
) => {
|
|
90
|
+
cliOutput(await uploadAction(space, path, opts), opts.pretty)
|
|
91
|
+
},
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
.addCommand(
|
|
95
|
+
new Command('download')
|
|
96
|
+
.description('Download a file attachment by content URL or ID')
|
|
97
|
+
.argument('<content>', 'File content URL (from message.files) or content ID')
|
|
98
|
+
.argument('[output]', 'Output path (defaults to original filename)')
|
|
99
|
+
.option('--bot <id>', 'Use specific bot')
|
|
100
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
101
|
+
.action(async (content: string, output: string | undefined, opts: BotOption) => {
|
|
102
|
+
cliOutput(await downloadAction(content, output, opts), opts.pretty)
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { authCommand } from './auth'
|
|
2
|
+
export { fileCommand } from './file'
|
|
3
|
+
export { listenCommand } from './listen'
|
|
4
|
+
export { memberCommand } from './member'
|
|
5
|
+
export { messageCommand } from './message'
|
|
6
|
+
export { snapshotCommand } from './snapshot'
|
|
7
|
+
export { spaceCommand } from './space'
|
|
8
|
+
export { userCommand } from './user'
|
|
9
|
+
export { whoamiCommand } from './whoami'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { parseEvents } from './listen'
|
|
4
|
+
|
|
5
|
+
describe('webexbot listen parseEvents', () => {
|
|
6
|
+
it('returns defaults when no filter is given', () => {
|
|
7
|
+
const events = parseEvents(undefined)
|
|
8
|
+
expect(events).toContain('message_created')
|
|
9
|
+
expect(events).toContain('error')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('accepts known event names', () => {
|
|
13
|
+
expect(parseEvents('message_created,disconnected')).toEqual(['message_created', 'disconnected'])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('throws on unknown event names and lists supported events', () => {
|
|
17
|
+
expect(() => parseEvents('message_created,mesage_deleted')).toThrow(/Unknown event\(s\): mesage_deleted/)
|
|
18
|
+
expect(() => parseEvents('bogus')).toThrow(/Supported events:/)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { cliOutput } from '@/shared/utils/cli-output'
|
|
4
|
+
|
|
5
|
+
import { WebexBotListener } from '../listener'
|
|
6
|
+
import type { WebexBotListenerEventMap } from '../types'
|
|
7
|
+
import type { BotOption } from './shared'
|
|
8
|
+
import { getClient } from './shared'
|
|
9
|
+
|
|
10
|
+
type ListenEvent = keyof WebexBotListenerEventMap
|
|
11
|
+
|
|
12
|
+
const SUPPORTED_EVENTS = new Set<ListenEvent>([
|
|
13
|
+
'message_created',
|
|
14
|
+
'message_updated',
|
|
15
|
+
'message_deleted',
|
|
16
|
+
'membership_created',
|
|
17
|
+
'attachment_action',
|
|
18
|
+
'room_created',
|
|
19
|
+
'room_updated',
|
|
20
|
+
'webex_event',
|
|
21
|
+
'connected',
|
|
22
|
+
'reconnecting',
|
|
23
|
+
'disconnected',
|
|
24
|
+
'error',
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
const DEFAULT_EVENTS: ListenEvent[] = [
|
|
28
|
+
'message_created',
|
|
29
|
+
'message_updated',
|
|
30
|
+
'message_deleted',
|
|
31
|
+
'membership_created',
|
|
32
|
+
'attachment_action',
|
|
33
|
+
'connected',
|
|
34
|
+
'reconnecting',
|
|
35
|
+
'disconnected',
|
|
36
|
+
'error',
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
interface ListenOptions extends BotOption {
|
|
40
|
+
events?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function printEvent(type: string, payload: unknown, pretty?: boolean): void {
|
|
44
|
+
const line = payload === undefined ? { type } : { type, payload }
|
|
45
|
+
console.log(JSON.stringify(line, null, pretty ? 2 : undefined))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function parseEvents(events?: string): ListenEvent[] {
|
|
49
|
+
if (!events) return DEFAULT_EVENTS
|
|
50
|
+
|
|
51
|
+
const tokens = events
|
|
52
|
+
.split(',')
|
|
53
|
+
.map((event) => event.trim())
|
|
54
|
+
.filter((event) => event.length > 0)
|
|
55
|
+
|
|
56
|
+
const unknown = tokens.filter((event) => !SUPPORTED_EVENTS.has(event as ListenEvent))
|
|
57
|
+
if (unknown.length > 0) {
|
|
58
|
+
const supported = [...SUPPORTED_EVENTS].join(', ')
|
|
59
|
+
throw new Error(`Unknown event(s): ${unknown.join(', ')}. Supported events: ${supported}`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return tokens as ListenEvent[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function listenAction(options: ListenOptions): Promise<void> {
|
|
66
|
+
let events: ListenEvent[]
|
|
67
|
+
try {
|
|
68
|
+
events = parseEvents(options.events)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
cliOutput({ error: (error as Error).message }, options.pretty, true)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const client = await getClient(options)
|
|
75
|
+
const listener = new WebexBotListener(client)
|
|
76
|
+
|
|
77
|
+
for (const event of events) {
|
|
78
|
+
if (event === 'error') {
|
|
79
|
+
listener.on(event, (error) => printEvent(event, { message: error.message, name: error.name }, options.pretty))
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
listener.on(event, (payload) => printEvent(event, payload, options.pretty))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
process.once('SIGINT', () => {
|
|
86
|
+
void listener.stop().finally(() => process.exit(0))
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
await listener.start()
|
|
91
|
+
} catch (error) {
|
|
92
|
+
printEvent('error', { message: (error as Error).message, name: (error as Error).name }, options.pretty)
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const listenCommand = new Command('listen')
|
|
98
|
+
.description('Listen for real-time Webex bot events')
|
|
99
|
+
.option('--events <list>', 'Comma-separated event filter')
|
|
100
|
+
.option('--bot <id>', 'Use specific bot')
|
|
101
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
102
|
+
.action(async (opts: ListenOptions) => {
|
|
103
|
+
await listenAction(opts)
|
|
104
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { cliOutput } from '@/shared/utils/cli-output'
|
|
4
|
+
|
|
5
|
+
import type { BotOption } from './shared'
|
|
6
|
+
import { getClient } from './shared'
|
|
7
|
+
|
|
8
|
+
interface MemberResult {
|
|
9
|
+
members?: Array<{
|
|
10
|
+
id: string
|
|
11
|
+
personId: string
|
|
12
|
+
personEmail: string
|
|
13
|
+
personDisplayName: string
|
|
14
|
+
isModerator: boolean
|
|
15
|
+
created: string
|
|
16
|
+
}>
|
|
17
|
+
error?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function listAction(space: string, options: BotOption & { max?: string }): Promise<MemberResult> {
|
|
21
|
+
try {
|
|
22
|
+
const client = await getClient(options)
|
|
23
|
+
const max = options.max ? parseInt(options.max, 10) : 100
|
|
24
|
+
const members = await client.listMemberships(space, { max })
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
members: members.map((m) => ({
|
|
28
|
+
id: m.id,
|
|
29
|
+
personId: m.personId,
|
|
30
|
+
personEmail: m.personEmail,
|
|
31
|
+
personDisplayName: m.personDisplayName,
|
|
32
|
+
isModerator: m.isModerator,
|
|
33
|
+
created: m.created,
|
|
34
|
+
})),
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return { error: (error as Error).message }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const memberCommand = new Command('member').description('Member commands').addCommand(
|
|
42
|
+
new Command('list')
|
|
43
|
+
.description('List members of a space')
|
|
44
|
+
.argument('<space>', 'Space ID')
|
|
45
|
+
.option('--max <n>', 'Number of members to retrieve', '100')
|
|
46
|
+
.option('--bot <id>', 'Use specific bot')
|
|
47
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
48
|
+
.action(async (space: string, opts: BotOption & { max?: string }) => {
|
|
49
|
+
cliOutput(await listAction(space, opts), opts.pretty)
|
|
50
|
+
}),
|
|
51
|
+
)
|